xref: /freebsd-src/contrib/llvm-project/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
181ad6265SDimitry Andric //===-- SPIRVAsmPrinter.cpp - SPIR-V LLVM assembly writer ------*- C++ -*--===//
281ad6265SDimitry Andric //
381ad6265SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
481ad6265SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
581ad6265SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
681ad6265SDimitry Andric //
781ad6265SDimitry Andric //===----------------------------------------------------------------------===//
881ad6265SDimitry Andric //
981ad6265SDimitry Andric // This file contains a printer that converts from our internal representation
1081ad6265SDimitry Andric // of machine-dependent LLVM code to the SPIR-V assembly language.
1181ad6265SDimitry Andric //
1281ad6265SDimitry Andric //===----------------------------------------------------------------------===//
1381ad6265SDimitry Andric 
1481ad6265SDimitry Andric #include "MCTargetDesc/SPIRVInstPrinter.h"
1581ad6265SDimitry Andric #include "SPIRV.h"
1681ad6265SDimitry Andric #include "SPIRVInstrInfo.h"
1781ad6265SDimitry Andric #include "SPIRVMCInstLower.h"
1881ad6265SDimitry Andric #include "SPIRVModuleAnalysis.h"
1981ad6265SDimitry Andric #include "SPIRVSubtarget.h"
2081ad6265SDimitry Andric #include "SPIRVTargetMachine.h"
2181ad6265SDimitry Andric #include "SPIRVUtils.h"
2281ad6265SDimitry Andric #include "TargetInfo/SPIRVTargetInfo.h"
2381ad6265SDimitry Andric #include "llvm/ADT/DenseMap.h"
24fcaf7f86SDimitry Andric #include "llvm/Analysis/ValueTracking.h"
2581ad6265SDimitry Andric #include "llvm/CodeGen/AsmPrinter.h"
2681ad6265SDimitry Andric #include "llvm/CodeGen/MachineConstantPool.h"
2781ad6265SDimitry Andric #include "llvm/CodeGen/MachineFunctionPass.h"
2881ad6265SDimitry Andric #include "llvm/CodeGen/MachineInstr.h"
2981ad6265SDimitry Andric #include "llvm/CodeGen/MachineModuleInfo.h"
3081ad6265SDimitry Andric #include "llvm/CodeGen/TargetLoweringObjectFileImpl.h"
3181ad6265SDimitry Andric #include "llvm/MC/MCAsmInfo.h"
32*0fca6ea1SDimitry Andric #include "llvm/MC/MCAssembler.h"
3381ad6265SDimitry Andric #include "llvm/MC/MCInst.h"
34*0fca6ea1SDimitry Andric #include "llvm/MC/MCObjectStreamer.h"
35*0fca6ea1SDimitry Andric #include "llvm/MC/MCSPIRVObjectWriter.h"
3681ad6265SDimitry Andric #include "llvm/MC/MCStreamer.h"
3781ad6265SDimitry Andric #include "llvm/MC/MCSymbol.h"
3881ad6265SDimitry Andric #include "llvm/MC/TargetRegistry.h"
3981ad6265SDimitry Andric #include "llvm/Support/raw_ostream.h"
4081ad6265SDimitry Andric 
4181ad6265SDimitry Andric using namespace llvm;
4281ad6265SDimitry Andric 
4381ad6265SDimitry Andric #define DEBUG_TYPE "asm-printer"
4481ad6265SDimitry Andric 
4581ad6265SDimitry Andric namespace {
4681ad6265SDimitry Andric class SPIRVAsmPrinter : public AsmPrinter {
47*0fca6ea1SDimitry Andric   unsigned NLabels = 0;
48*0fca6ea1SDimitry Andric 
4981ad6265SDimitry Andric public:
5081ad6265SDimitry Andric   explicit SPIRVAsmPrinter(TargetMachine &TM,
5181ad6265SDimitry Andric                            std::unique_ptr<MCStreamer> Streamer)
5281ad6265SDimitry Andric       : AsmPrinter(TM, std::move(Streamer)), ST(nullptr), TII(nullptr) {}
5381ad6265SDimitry Andric   bool ModuleSectionsEmitted;
5481ad6265SDimitry Andric   const SPIRVSubtarget *ST;
5581ad6265SDimitry Andric   const SPIRVInstrInfo *TII;
5681ad6265SDimitry Andric 
5781ad6265SDimitry Andric   StringRef getPassName() const override { return "SPIRV Assembly Printer"; }
5881ad6265SDimitry Andric   void printOperand(const MachineInstr *MI, int OpNum, raw_ostream &O);
5981ad6265SDimitry Andric   bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
6081ad6265SDimitry Andric                        const char *ExtraCode, raw_ostream &O) override;
6181ad6265SDimitry Andric 
6281ad6265SDimitry Andric   void outputMCInst(MCInst &Inst);
6381ad6265SDimitry Andric   void outputInstruction(const MachineInstr *MI);
6481ad6265SDimitry Andric   void outputModuleSection(SPIRV::ModuleSectionType MSType);
65bdd1243dSDimitry Andric   void outputGlobalRequirements();
6681ad6265SDimitry Andric   void outputEntryPoints();
6781ad6265SDimitry Andric   void outputDebugSourceAndStrings(const Module &M);
68fcaf7f86SDimitry Andric   void outputOpExtInstImports(const Module &M);
6981ad6265SDimitry Andric   void outputOpMemoryModel();
7081ad6265SDimitry Andric   void outputOpFunctionEnd();
7181ad6265SDimitry Andric   void outputExtFuncDecls();
72fcaf7f86SDimitry Andric   void outputExecutionModeFromMDNode(Register Reg, MDNode *Node,
73*0fca6ea1SDimitry Andric                                      SPIRV::ExecutionMode::ExecutionMode EM,
74*0fca6ea1SDimitry Andric                                      unsigned ExpectMDOps, int64_t DefVal);
755f757f3fSDimitry Andric   void outputExecutionModeFromNumthreadsAttribute(
765f757f3fSDimitry Andric       const Register &Reg, const Attribute &Attr,
775f757f3fSDimitry Andric       SPIRV::ExecutionMode::ExecutionMode EM);
78fcaf7f86SDimitry Andric   void outputExecutionMode(const Module &M);
79fcaf7f86SDimitry Andric   void outputAnnotations(const Module &M);
8081ad6265SDimitry Andric   void outputModuleSections();
8181ad6265SDimitry Andric 
8281ad6265SDimitry Andric   void emitInstruction(const MachineInstr *MI) override;
8381ad6265SDimitry Andric   void emitFunctionEntryLabel() override {}
8481ad6265SDimitry Andric   void emitFunctionHeader() override;
8581ad6265SDimitry Andric   void emitFunctionBodyStart() override {}
8681ad6265SDimitry Andric   void emitFunctionBodyEnd() override;
8781ad6265SDimitry Andric   void emitBasicBlockStart(const MachineBasicBlock &MBB) override;
8881ad6265SDimitry Andric   void emitBasicBlockEnd(const MachineBasicBlock &MBB) override {}
8981ad6265SDimitry Andric   void emitGlobalVariable(const GlobalVariable *GV) override {}
9081ad6265SDimitry Andric   void emitOpLabel(const MachineBasicBlock &MBB);
9181ad6265SDimitry Andric   void emitEndOfAsmFile(Module &M) override;
9281ad6265SDimitry Andric   bool doInitialization(Module &M) override;
9381ad6265SDimitry Andric 
9481ad6265SDimitry Andric   void getAnalysisUsage(AnalysisUsage &AU) const override;
9581ad6265SDimitry Andric   SPIRV::ModuleAnalysisInfo *MAI;
9681ad6265SDimitry Andric };
9781ad6265SDimitry Andric } // namespace
9881ad6265SDimitry Andric 
9981ad6265SDimitry Andric void SPIRVAsmPrinter::getAnalysisUsage(AnalysisUsage &AU) const {
10081ad6265SDimitry Andric   AU.addRequired<SPIRVModuleAnalysis>();
10181ad6265SDimitry Andric   AU.addPreserved<SPIRVModuleAnalysis>();
10281ad6265SDimitry Andric   AsmPrinter::getAnalysisUsage(AU);
10381ad6265SDimitry Andric }
10481ad6265SDimitry Andric 
10581ad6265SDimitry Andric // If the module has no functions, we need output global info anyway.
10681ad6265SDimitry Andric void SPIRVAsmPrinter::emitEndOfAsmFile(Module &M) {
10781ad6265SDimitry Andric   if (ModuleSectionsEmitted == false) {
10881ad6265SDimitry Andric     outputModuleSections();
10981ad6265SDimitry Andric     ModuleSectionsEmitted = true;
11081ad6265SDimitry Andric   }
111*0fca6ea1SDimitry Andric 
112*0fca6ea1SDimitry Andric   ST = static_cast<const SPIRVTargetMachine &>(TM).getSubtargetImpl();
113*0fca6ea1SDimitry Andric   VersionTuple SPIRVVersion = ST->getSPIRVVersion();
114*0fca6ea1SDimitry Andric   uint32_t Major = SPIRVVersion.getMajor();
115*0fca6ea1SDimitry Andric   uint32_t Minor = SPIRVVersion.getMinor().value_or(0);
116*0fca6ea1SDimitry Andric   // Bound is an approximation that accounts for the maximum used register
117*0fca6ea1SDimitry Andric   // number and number of generated OpLabels
118*0fca6ea1SDimitry Andric   unsigned Bound = 2 * (ST->getBound() + 1) + NLabels;
119*0fca6ea1SDimitry Andric   if (MCAssembler *Asm = OutStreamer->getAssemblerPtr())
120*0fca6ea1SDimitry Andric     static_cast<SPIRVObjectWriter &>(Asm->getWriter())
121*0fca6ea1SDimitry Andric         .setBuildVersion(Major, Minor, Bound);
12281ad6265SDimitry Andric }
12381ad6265SDimitry Andric 
12481ad6265SDimitry Andric void SPIRVAsmPrinter::emitFunctionHeader() {
12581ad6265SDimitry Andric   if (ModuleSectionsEmitted == false) {
12681ad6265SDimitry Andric     outputModuleSections();
12781ad6265SDimitry Andric     ModuleSectionsEmitted = true;
12881ad6265SDimitry Andric   }
12981ad6265SDimitry Andric   // Get the subtarget from the current MachineFunction.
13081ad6265SDimitry Andric   ST = &MF->getSubtarget<SPIRVSubtarget>();
13181ad6265SDimitry Andric   TII = ST->getInstrInfo();
13281ad6265SDimitry Andric   const Function &F = MF->getFunction();
13381ad6265SDimitry Andric 
13481ad6265SDimitry Andric   if (isVerbose()) {
13581ad6265SDimitry Andric     OutStreamer->getCommentOS()
13681ad6265SDimitry Andric         << "-- Begin function "
13781ad6265SDimitry Andric         << GlobalValue::dropLLVMManglingEscape(F.getName()) << '\n';
13881ad6265SDimitry Andric   }
13981ad6265SDimitry Andric 
14081ad6265SDimitry Andric   auto Section = getObjFileLowering().SectionForGlobal(&F, TM);
14181ad6265SDimitry Andric   MF->setSection(Section);
14281ad6265SDimitry Andric }
14381ad6265SDimitry Andric 
14481ad6265SDimitry Andric void SPIRVAsmPrinter::outputOpFunctionEnd() {
14581ad6265SDimitry Andric   MCInst FunctionEndInst;
14681ad6265SDimitry Andric   FunctionEndInst.setOpcode(SPIRV::OpFunctionEnd);
14781ad6265SDimitry Andric   outputMCInst(FunctionEndInst);
14881ad6265SDimitry Andric }
14981ad6265SDimitry Andric 
15081ad6265SDimitry Andric // Emit OpFunctionEnd at the end of MF and clear BBNumToRegMap.
15181ad6265SDimitry Andric void SPIRVAsmPrinter::emitFunctionBodyEnd() {
15281ad6265SDimitry Andric   outputOpFunctionEnd();
15381ad6265SDimitry Andric   MAI->BBNumToRegMap.clear();
15481ad6265SDimitry Andric }
15581ad6265SDimitry Andric 
15681ad6265SDimitry Andric void SPIRVAsmPrinter::emitOpLabel(const MachineBasicBlock &MBB) {
15781ad6265SDimitry Andric   MCInst LabelInst;
15881ad6265SDimitry Andric   LabelInst.setOpcode(SPIRV::OpLabel);
15981ad6265SDimitry Andric   LabelInst.addOperand(MCOperand::createReg(MAI->getOrCreateMBBRegister(MBB)));
16081ad6265SDimitry Andric   outputMCInst(LabelInst);
161*0fca6ea1SDimitry Andric   ++NLabels;
16281ad6265SDimitry Andric }
16381ad6265SDimitry Andric 
16481ad6265SDimitry Andric void SPIRVAsmPrinter::emitBasicBlockStart(const MachineBasicBlock &MBB) {
16506c3fb27SDimitry Andric   assert(!MBB.empty() && "MBB is empty!");
16606c3fb27SDimitry Andric 
16781ad6265SDimitry Andric   // If it's the first MBB in MF, it has OpFunction and OpFunctionParameter, so
16881ad6265SDimitry Andric   // OpLabel should be output after them.
16981ad6265SDimitry Andric   if (MBB.getNumber() == MF->front().getNumber()) {
17081ad6265SDimitry Andric     for (const MachineInstr &MI : MBB)
17181ad6265SDimitry Andric       if (MI.getOpcode() == SPIRV::OpFunction)
17281ad6265SDimitry Andric         return;
17381ad6265SDimitry Andric     // TODO: this case should be checked by the verifier.
17481ad6265SDimitry Andric     report_fatal_error("OpFunction is expected in the front MBB of MF");
17581ad6265SDimitry Andric   }
17681ad6265SDimitry Andric   emitOpLabel(MBB);
17781ad6265SDimitry Andric }
17881ad6265SDimitry Andric 
17981ad6265SDimitry Andric void SPIRVAsmPrinter::printOperand(const MachineInstr *MI, int OpNum,
18081ad6265SDimitry Andric                                    raw_ostream &O) {
18181ad6265SDimitry Andric   const MachineOperand &MO = MI->getOperand(OpNum);
18281ad6265SDimitry Andric 
18381ad6265SDimitry Andric   switch (MO.getType()) {
18481ad6265SDimitry Andric   case MachineOperand::MO_Register:
18581ad6265SDimitry Andric     O << SPIRVInstPrinter::getRegisterName(MO.getReg());
18681ad6265SDimitry Andric     break;
18781ad6265SDimitry Andric 
18881ad6265SDimitry Andric   case MachineOperand::MO_Immediate:
18981ad6265SDimitry Andric     O << MO.getImm();
19081ad6265SDimitry Andric     break;
19181ad6265SDimitry Andric 
19281ad6265SDimitry Andric   case MachineOperand::MO_FPImmediate:
19381ad6265SDimitry Andric     O << MO.getFPImm();
19481ad6265SDimitry Andric     break;
19581ad6265SDimitry Andric 
19681ad6265SDimitry Andric   case MachineOperand::MO_MachineBasicBlock:
19781ad6265SDimitry Andric     O << *MO.getMBB()->getSymbol();
19881ad6265SDimitry Andric     break;
19981ad6265SDimitry Andric 
20081ad6265SDimitry Andric   case MachineOperand::MO_GlobalAddress:
20181ad6265SDimitry Andric     O << *getSymbol(MO.getGlobal());
20281ad6265SDimitry Andric     break;
20381ad6265SDimitry Andric 
20481ad6265SDimitry Andric   case MachineOperand::MO_BlockAddress: {
20581ad6265SDimitry Andric     MCSymbol *BA = GetBlockAddressSymbol(MO.getBlockAddress());
20681ad6265SDimitry Andric     O << BA->getName();
20781ad6265SDimitry Andric     break;
20881ad6265SDimitry Andric   }
20981ad6265SDimitry Andric 
21081ad6265SDimitry Andric   case MachineOperand::MO_ExternalSymbol:
21181ad6265SDimitry Andric     O << *GetExternalSymbolSymbol(MO.getSymbolName());
21281ad6265SDimitry Andric     break;
21381ad6265SDimitry Andric 
21481ad6265SDimitry Andric   case MachineOperand::MO_JumpTableIndex:
21581ad6265SDimitry Andric   case MachineOperand::MO_ConstantPoolIndex:
21681ad6265SDimitry Andric   default:
21781ad6265SDimitry Andric     llvm_unreachable("<unknown operand type>");
21881ad6265SDimitry Andric   }
21981ad6265SDimitry Andric }
22081ad6265SDimitry Andric 
22181ad6265SDimitry Andric bool SPIRVAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
22281ad6265SDimitry Andric                                       const char *ExtraCode, raw_ostream &O) {
22381ad6265SDimitry Andric   if (ExtraCode && ExtraCode[0])
22481ad6265SDimitry Andric     return true; // Invalid instruction - SPIR-V does not have special modifiers
22581ad6265SDimitry Andric 
22681ad6265SDimitry Andric   printOperand(MI, OpNo, O);
22781ad6265SDimitry Andric   return false;
22881ad6265SDimitry Andric }
22981ad6265SDimitry Andric 
23081ad6265SDimitry Andric static bool isFuncOrHeaderInstr(const MachineInstr *MI,
23181ad6265SDimitry Andric                                 const SPIRVInstrInfo *TII) {
23281ad6265SDimitry Andric   return TII->isHeaderInstr(*MI) || MI->getOpcode() == SPIRV::OpFunction ||
23381ad6265SDimitry Andric          MI->getOpcode() == SPIRV::OpFunctionParameter;
23481ad6265SDimitry Andric }
23581ad6265SDimitry Andric 
23681ad6265SDimitry Andric void SPIRVAsmPrinter::outputMCInst(MCInst &Inst) {
23781ad6265SDimitry Andric   OutStreamer->emitInstruction(Inst, *OutContext.getSubtargetInfo());
23881ad6265SDimitry Andric }
23981ad6265SDimitry Andric 
24081ad6265SDimitry Andric void SPIRVAsmPrinter::outputInstruction(const MachineInstr *MI) {
24181ad6265SDimitry Andric   SPIRVMCInstLower MCInstLowering;
24281ad6265SDimitry Andric   MCInst TmpInst;
24381ad6265SDimitry Andric   MCInstLowering.lower(MI, TmpInst, MAI);
24481ad6265SDimitry Andric   outputMCInst(TmpInst);
24581ad6265SDimitry Andric }
24681ad6265SDimitry Andric 
24781ad6265SDimitry Andric void SPIRVAsmPrinter::emitInstruction(const MachineInstr *MI) {
248753f127fSDimitry Andric   SPIRV_MC::verifyInstructionPredicates(MI->getOpcode(),
249753f127fSDimitry Andric                                         getSubtargetInfo().getFeatureBits());
250753f127fSDimitry Andric 
25181ad6265SDimitry Andric   if (!MAI->getSkipEmission(MI))
25281ad6265SDimitry Andric     outputInstruction(MI);
25381ad6265SDimitry Andric 
25481ad6265SDimitry Andric   // Output OpLabel after OpFunction and OpFunctionParameter in the first MBB.
25581ad6265SDimitry Andric   const MachineInstr *NextMI = MI->getNextNode();
25681ad6265SDimitry Andric   if (!MAI->hasMBBRegister(*MI->getParent()) && isFuncOrHeaderInstr(MI, TII) &&
25781ad6265SDimitry Andric       (!NextMI || !isFuncOrHeaderInstr(NextMI, TII))) {
25881ad6265SDimitry Andric     assert(MI->getParent()->getNumber() == MF->front().getNumber() &&
25981ad6265SDimitry Andric            "OpFunction is not in the front MBB of MF");
26081ad6265SDimitry Andric     emitOpLabel(*MI->getParent());
26181ad6265SDimitry Andric   }
26281ad6265SDimitry Andric }
26381ad6265SDimitry Andric 
26481ad6265SDimitry Andric void SPIRVAsmPrinter::outputModuleSection(SPIRV::ModuleSectionType MSType) {
26581ad6265SDimitry Andric   for (MachineInstr *MI : MAI->getMSInstrs(MSType))
26681ad6265SDimitry Andric     outputInstruction(MI);
26781ad6265SDimitry Andric }
26881ad6265SDimitry Andric 
26981ad6265SDimitry Andric void SPIRVAsmPrinter::outputDebugSourceAndStrings(const Module &M) {
270fcaf7f86SDimitry Andric   // Output OpSourceExtensions.
271fcaf7f86SDimitry Andric   for (auto &Str : MAI->SrcExt) {
272fcaf7f86SDimitry Andric     MCInst Inst;
273fcaf7f86SDimitry Andric     Inst.setOpcode(SPIRV::OpSourceExtension);
274fcaf7f86SDimitry Andric     addStringImm(Str.first(), Inst);
275fcaf7f86SDimitry Andric     outputMCInst(Inst);
276fcaf7f86SDimitry Andric   }
27781ad6265SDimitry Andric   // Output OpSource.
27881ad6265SDimitry Andric   MCInst Inst;
27981ad6265SDimitry Andric   Inst.setOpcode(SPIRV::OpSource);
28081ad6265SDimitry Andric   Inst.addOperand(MCOperand::createImm(static_cast<unsigned>(MAI->SrcLang)));
28181ad6265SDimitry Andric   Inst.addOperand(
28281ad6265SDimitry Andric       MCOperand::createImm(static_cast<unsigned>(MAI->SrcLangVersion)));
28381ad6265SDimitry Andric   outputMCInst(Inst);
28481ad6265SDimitry Andric }
28581ad6265SDimitry Andric 
286fcaf7f86SDimitry Andric void SPIRVAsmPrinter::outputOpExtInstImports(const Module &M) {
287fcaf7f86SDimitry Andric   for (auto &CU : MAI->ExtInstSetMap) {
288fcaf7f86SDimitry Andric     unsigned Set = CU.first;
289fcaf7f86SDimitry Andric     Register Reg = CU.second;
290fcaf7f86SDimitry Andric     MCInst Inst;
291fcaf7f86SDimitry Andric     Inst.setOpcode(SPIRV::OpExtInstImport);
292fcaf7f86SDimitry Andric     Inst.addOperand(MCOperand::createReg(Reg));
293bdd1243dSDimitry Andric     addStringImm(getExtInstSetName(
294bdd1243dSDimitry Andric                      static_cast<SPIRV::InstructionSet::InstructionSet>(Set)),
295fcaf7f86SDimitry Andric                  Inst);
296fcaf7f86SDimitry Andric     outputMCInst(Inst);
297fcaf7f86SDimitry Andric   }
298fcaf7f86SDimitry Andric }
299fcaf7f86SDimitry Andric 
30081ad6265SDimitry Andric void SPIRVAsmPrinter::outputOpMemoryModel() {
30181ad6265SDimitry Andric   MCInst Inst;
30281ad6265SDimitry Andric   Inst.setOpcode(SPIRV::OpMemoryModel);
30381ad6265SDimitry Andric   Inst.addOperand(MCOperand::createImm(static_cast<unsigned>(MAI->Addr)));
30481ad6265SDimitry Andric   Inst.addOperand(MCOperand::createImm(static_cast<unsigned>(MAI->Mem)));
30581ad6265SDimitry Andric   outputMCInst(Inst);
30681ad6265SDimitry Andric }
30781ad6265SDimitry Andric 
30881ad6265SDimitry Andric // Before the OpEntryPoints' output, we need to add the entry point's
30981ad6265SDimitry Andric // interfaces. The interface is a list of IDs of global OpVariable instructions.
31081ad6265SDimitry Andric // These declare the set of global variables from a module that form
31181ad6265SDimitry Andric // the interface of this entry point.
31281ad6265SDimitry Andric void SPIRVAsmPrinter::outputEntryPoints() {
31381ad6265SDimitry Andric   // Find all OpVariable IDs with required StorageClass.
31481ad6265SDimitry Andric   DenseSet<Register> InterfaceIDs;
31581ad6265SDimitry Andric   for (MachineInstr *MI : MAI->GlobalVarList) {
31681ad6265SDimitry Andric     assert(MI->getOpcode() == SPIRV::OpVariable);
317bdd1243dSDimitry Andric     auto SC = static_cast<SPIRV::StorageClass::StorageClass>(
318bdd1243dSDimitry Andric         MI->getOperand(2).getImm());
31981ad6265SDimitry Andric     // Before version 1.4, the interface's storage classes are limited to
32081ad6265SDimitry Andric     // the Input and Output storage classes. Starting with version 1.4,
32181ad6265SDimitry Andric     // the interface's storage classes are all storage classes used in
32281ad6265SDimitry Andric     // declaring all global variables referenced by the entry point call tree.
323*0fca6ea1SDimitry Andric     if (ST->isAtLeastSPIRVVer(VersionTuple(1, 4)) ||
324*0fca6ea1SDimitry Andric         SC == SPIRV::StorageClass::Input || SC == SPIRV::StorageClass::Output) {
32581ad6265SDimitry Andric       MachineFunction *MF = MI->getMF();
32681ad6265SDimitry Andric       Register Reg = MAI->getRegisterAlias(MF, MI->getOperand(0).getReg());
32781ad6265SDimitry Andric       InterfaceIDs.insert(Reg);
32881ad6265SDimitry Andric     }
32981ad6265SDimitry Andric   }
33081ad6265SDimitry Andric 
33181ad6265SDimitry Andric   // Output OpEntryPoints adding interface args to all of them.
33281ad6265SDimitry Andric   for (MachineInstr *MI : MAI->getMSInstrs(SPIRV::MB_EntryPoints)) {
33381ad6265SDimitry Andric     SPIRVMCInstLower MCInstLowering;
33481ad6265SDimitry Andric     MCInst TmpInst;
33581ad6265SDimitry Andric     MCInstLowering.lower(MI, TmpInst, MAI);
33681ad6265SDimitry Andric     for (Register Reg : InterfaceIDs) {
33781ad6265SDimitry Andric       assert(Reg.isValid());
33881ad6265SDimitry Andric       TmpInst.addOperand(MCOperand::createReg(Reg));
33981ad6265SDimitry Andric     }
34081ad6265SDimitry Andric     outputMCInst(TmpInst);
34181ad6265SDimitry Andric   }
34281ad6265SDimitry Andric }
34381ad6265SDimitry Andric 
344bdd1243dSDimitry Andric // Create global OpCapability instructions for the required capabilities.
345bdd1243dSDimitry Andric void SPIRVAsmPrinter::outputGlobalRequirements() {
346bdd1243dSDimitry Andric   // Abort here if not all requirements can be satisfied.
347bdd1243dSDimitry Andric   MAI->Reqs.checkSatisfiable(*ST);
348bdd1243dSDimitry Andric 
349bdd1243dSDimitry Andric   for (const auto &Cap : MAI->Reqs.getMinimalCapabilities()) {
350bdd1243dSDimitry Andric     MCInst Inst;
351bdd1243dSDimitry Andric     Inst.setOpcode(SPIRV::OpCapability);
352bdd1243dSDimitry Andric     Inst.addOperand(MCOperand::createImm(Cap));
353bdd1243dSDimitry Andric     outputMCInst(Inst);
354bdd1243dSDimitry Andric   }
355bdd1243dSDimitry Andric 
356bdd1243dSDimitry Andric   // Generate the final OpExtensions with strings instead of enums.
357bdd1243dSDimitry Andric   for (const auto &Ext : MAI->Reqs.getExtensions()) {
358bdd1243dSDimitry Andric     MCInst Inst;
359bdd1243dSDimitry Andric     Inst.setOpcode(SPIRV::OpExtension);
360bdd1243dSDimitry Andric     addStringImm(getSymbolicOperandMnemonic(
361bdd1243dSDimitry Andric                      SPIRV::OperandCategory::ExtensionOperand, Ext),
362bdd1243dSDimitry Andric                  Inst);
363bdd1243dSDimitry Andric     outputMCInst(Inst);
364bdd1243dSDimitry Andric   }
365bdd1243dSDimitry Andric   // TODO add a pseudo instr for version number.
366bdd1243dSDimitry Andric }
367bdd1243dSDimitry Andric 
36881ad6265SDimitry Andric void SPIRVAsmPrinter::outputExtFuncDecls() {
36981ad6265SDimitry Andric   // Insert OpFunctionEnd after each declaration.
37081ad6265SDimitry Andric   SmallVectorImpl<MachineInstr *>::iterator
37181ad6265SDimitry Andric       I = MAI->getMSInstrs(SPIRV::MB_ExtFuncDecls).begin(),
37281ad6265SDimitry Andric       E = MAI->getMSInstrs(SPIRV::MB_ExtFuncDecls).end();
37381ad6265SDimitry Andric   for (; I != E; ++I) {
37481ad6265SDimitry Andric     outputInstruction(*I);
37581ad6265SDimitry Andric     if ((I + 1) == E || (*(I + 1))->getOpcode() == SPIRV::OpFunction)
37681ad6265SDimitry Andric       outputOpFunctionEnd();
37781ad6265SDimitry Andric   }
37881ad6265SDimitry Andric }
37981ad6265SDimitry Andric 
380fcaf7f86SDimitry Andric // Encode LLVM type by SPIR-V execution mode VecTypeHint.
381fcaf7f86SDimitry Andric static unsigned encodeVecTypeHint(Type *Ty) {
382fcaf7f86SDimitry Andric   if (Ty->isHalfTy())
383fcaf7f86SDimitry Andric     return 4;
384fcaf7f86SDimitry Andric   if (Ty->isFloatTy())
385fcaf7f86SDimitry Andric     return 5;
386fcaf7f86SDimitry Andric   if (Ty->isDoubleTy())
387fcaf7f86SDimitry Andric     return 6;
388fcaf7f86SDimitry Andric   if (IntegerType *IntTy = dyn_cast<IntegerType>(Ty)) {
389fcaf7f86SDimitry Andric     switch (IntTy->getIntegerBitWidth()) {
390fcaf7f86SDimitry Andric     case 8:
391fcaf7f86SDimitry Andric       return 0;
392fcaf7f86SDimitry Andric     case 16:
393fcaf7f86SDimitry Andric       return 1;
394fcaf7f86SDimitry Andric     case 32:
395fcaf7f86SDimitry Andric       return 2;
396fcaf7f86SDimitry Andric     case 64:
397fcaf7f86SDimitry Andric       return 3;
398fcaf7f86SDimitry Andric     default:
399fcaf7f86SDimitry Andric       llvm_unreachable("invalid integer type");
400fcaf7f86SDimitry Andric     }
401fcaf7f86SDimitry Andric   }
402fcaf7f86SDimitry Andric   if (FixedVectorType *VecTy = dyn_cast<FixedVectorType>(Ty)) {
403fcaf7f86SDimitry Andric     Type *EleTy = VecTy->getElementType();
404fcaf7f86SDimitry Andric     unsigned Size = VecTy->getNumElements();
405fcaf7f86SDimitry Andric     return Size << 16 | encodeVecTypeHint(EleTy);
406fcaf7f86SDimitry Andric   }
407fcaf7f86SDimitry Andric   llvm_unreachable("invalid type");
408fcaf7f86SDimitry Andric }
409fcaf7f86SDimitry Andric 
410fcaf7f86SDimitry Andric static void addOpsFromMDNode(MDNode *MDN, MCInst &Inst,
411fcaf7f86SDimitry Andric                              SPIRV::ModuleAnalysisInfo *MAI) {
412fcaf7f86SDimitry Andric   for (const MDOperand &MDOp : MDN->operands()) {
413fcaf7f86SDimitry Andric     if (auto *CMeta = dyn_cast<ConstantAsMetadata>(MDOp)) {
414fcaf7f86SDimitry Andric       Constant *C = CMeta->getValue();
415fcaf7f86SDimitry Andric       if (ConstantInt *Const = dyn_cast<ConstantInt>(C)) {
416fcaf7f86SDimitry Andric         Inst.addOperand(MCOperand::createImm(Const->getZExtValue()));
417fcaf7f86SDimitry Andric       } else if (auto *CE = dyn_cast<Function>(C)) {
418bdd1243dSDimitry Andric         Register FuncReg = MAI->getFuncReg(CE);
419fcaf7f86SDimitry Andric         assert(FuncReg.isValid());
420fcaf7f86SDimitry Andric         Inst.addOperand(MCOperand::createReg(FuncReg));
421fcaf7f86SDimitry Andric       }
422fcaf7f86SDimitry Andric     }
423fcaf7f86SDimitry Andric   }
424fcaf7f86SDimitry Andric }
425fcaf7f86SDimitry Andric 
426bdd1243dSDimitry Andric void SPIRVAsmPrinter::outputExecutionModeFromMDNode(
427*0fca6ea1SDimitry Andric     Register Reg, MDNode *Node, SPIRV::ExecutionMode::ExecutionMode EM,
428*0fca6ea1SDimitry Andric     unsigned ExpectMDOps, int64_t DefVal) {
429fcaf7f86SDimitry Andric   MCInst Inst;
430fcaf7f86SDimitry Andric   Inst.setOpcode(SPIRV::OpExecutionMode);
431fcaf7f86SDimitry Andric   Inst.addOperand(MCOperand::createReg(Reg));
432fcaf7f86SDimitry Andric   Inst.addOperand(MCOperand::createImm(static_cast<unsigned>(EM)));
433fcaf7f86SDimitry Andric   addOpsFromMDNode(Node, Inst, MAI);
434*0fca6ea1SDimitry Andric   // reqd_work_group_size and work_group_size_hint require 3 operands,
435*0fca6ea1SDimitry Andric   // if metadata contains less operands, just add a default value
436*0fca6ea1SDimitry Andric   unsigned NodeSz = Node->getNumOperands();
437*0fca6ea1SDimitry Andric   if (ExpectMDOps > 0 && NodeSz < ExpectMDOps)
438*0fca6ea1SDimitry Andric     for (unsigned i = NodeSz; i < ExpectMDOps; ++i)
439*0fca6ea1SDimitry Andric       Inst.addOperand(MCOperand::createImm(DefVal));
440fcaf7f86SDimitry Andric   outputMCInst(Inst);
441fcaf7f86SDimitry Andric }
442fcaf7f86SDimitry Andric 
4435f757f3fSDimitry Andric void SPIRVAsmPrinter::outputExecutionModeFromNumthreadsAttribute(
4445f757f3fSDimitry Andric     const Register &Reg, const Attribute &Attr,
4455f757f3fSDimitry Andric     SPIRV::ExecutionMode::ExecutionMode EM) {
4465f757f3fSDimitry Andric   assert(Attr.isValid() && "Function called with an invalid attribute.");
4475f757f3fSDimitry Andric 
4485f757f3fSDimitry Andric   MCInst Inst;
4495f757f3fSDimitry Andric   Inst.setOpcode(SPIRV::OpExecutionMode);
4505f757f3fSDimitry Andric   Inst.addOperand(MCOperand::createReg(Reg));
4515f757f3fSDimitry Andric   Inst.addOperand(MCOperand::createImm(static_cast<unsigned>(EM)));
4525f757f3fSDimitry Andric 
4535f757f3fSDimitry Andric   SmallVector<StringRef> NumThreads;
4545f757f3fSDimitry Andric   Attr.getValueAsString().split(NumThreads, ',');
4555f757f3fSDimitry Andric   assert(NumThreads.size() == 3 && "invalid numthreads");
4565f757f3fSDimitry Andric   for (uint32_t i = 0; i < 3; ++i) {
4575f757f3fSDimitry Andric     uint32_t V;
4585f757f3fSDimitry Andric     [[maybe_unused]] bool Result = NumThreads[i].getAsInteger(10, V);
4595f757f3fSDimitry Andric     assert(!Result && "Failed to parse numthreads");
4605f757f3fSDimitry Andric     Inst.addOperand(MCOperand::createImm(V));
4615f757f3fSDimitry Andric   }
4625f757f3fSDimitry Andric 
4635f757f3fSDimitry Andric   outputMCInst(Inst);
4645f757f3fSDimitry Andric }
4655f757f3fSDimitry Andric 
466fcaf7f86SDimitry Andric void SPIRVAsmPrinter::outputExecutionMode(const Module &M) {
467fcaf7f86SDimitry Andric   NamedMDNode *Node = M.getNamedMetadata("spirv.ExecutionMode");
468fcaf7f86SDimitry Andric   if (Node) {
469fcaf7f86SDimitry Andric     for (unsigned i = 0; i < Node->getNumOperands(); i++) {
470fcaf7f86SDimitry Andric       MCInst Inst;
471fcaf7f86SDimitry Andric       Inst.setOpcode(SPIRV::OpExecutionMode);
472fcaf7f86SDimitry Andric       addOpsFromMDNode(cast<MDNode>(Node->getOperand(i)), Inst, MAI);
473fcaf7f86SDimitry Andric       outputMCInst(Inst);
474fcaf7f86SDimitry Andric     }
475fcaf7f86SDimitry Andric   }
476fcaf7f86SDimitry Andric   for (auto FI = M.begin(), E = M.end(); FI != E; ++FI) {
477fcaf7f86SDimitry Andric     const Function &F = *FI;
478*0fca6ea1SDimitry Andric     // Only operands of OpEntryPoint instructions are allowed to be
479*0fca6ea1SDimitry Andric     // <Entry Point> operands of OpExecutionMode
480*0fca6ea1SDimitry Andric     if (F.isDeclaration() || !isEntryPoint(F))
481fcaf7f86SDimitry Andric       continue;
482bdd1243dSDimitry Andric     Register FReg = MAI->getFuncReg(&F);
483fcaf7f86SDimitry Andric     assert(FReg.isValid());
484fcaf7f86SDimitry Andric     if (MDNode *Node = F.getMetadata("reqd_work_group_size"))
485*0fca6ea1SDimitry Andric       outputExecutionModeFromMDNode(FReg, Node, SPIRV::ExecutionMode::LocalSize,
486*0fca6ea1SDimitry Andric                                     3, 1);
4875f757f3fSDimitry Andric     if (Attribute Attr = F.getFnAttribute("hlsl.numthreads"); Attr.isValid())
4885f757f3fSDimitry Andric       outputExecutionModeFromNumthreadsAttribute(
4895f757f3fSDimitry Andric           FReg, Attr, SPIRV::ExecutionMode::LocalSize);
490fcaf7f86SDimitry Andric     if (MDNode *Node = F.getMetadata("work_group_size_hint"))
491fcaf7f86SDimitry Andric       outputExecutionModeFromMDNode(FReg, Node,
492*0fca6ea1SDimitry Andric                                     SPIRV::ExecutionMode::LocalSizeHint, 3, 1);
493fcaf7f86SDimitry Andric     if (MDNode *Node = F.getMetadata("intel_reqd_sub_group_size"))
494fcaf7f86SDimitry Andric       outputExecutionModeFromMDNode(FReg, Node,
495*0fca6ea1SDimitry Andric                                     SPIRV::ExecutionMode::SubgroupSize, 0, 0);
496fcaf7f86SDimitry Andric     if (MDNode *Node = F.getMetadata("vec_type_hint")) {
497fcaf7f86SDimitry Andric       MCInst Inst;
498fcaf7f86SDimitry Andric       Inst.setOpcode(SPIRV::OpExecutionMode);
499fcaf7f86SDimitry Andric       Inst.addOperand(MCOperand::createReg(FReg));
500fcaf7f86SDimitry Andric       unsigned EM = static_cast<unsigned>(SPIRV::ExecutionMode::VecTypeHint);
501fcaf7f86SDimitry Andric       Inst.addOperand(MCOperand::createImm(EM));
502fcaf7f86SDimitry Andric       unsigned TypeCode = encodeVecTypeHint(getMDOperandAsType(Node, 0));
503fcaf7f86SDimitry Andric       Inst.addOperand(MCOperand::createImm(TypeCode));
504fcaf7f86SDimitry Andric       outputMCInst(Inst);
505fcaf7f86SDimitry Andric     }
5065f757f3fSDimitry Andric     if (ST->isOpenCLEnv() && !M.getNamedMetadata("spirv.ExecutionMode") &&
507bdd1243dSDimitry Andric         !M.getNamedMetadata("opencl.enable.FP_CONTRACT")) {
508bdd1243dSDimitry Andric       MCInst Inst;
509bdd1243dSDimitry Andric       Inst.setOpcode(SPIRV::OpExecutionMode);
510bdd1243dSDimitry Andric       Inst.addOperand(MCOperand::createReg(FReg));
511bdd1243dSDimitry Andric       unsigned EM = static_cast<unsigned>(SPIRV::ExecutionMode::ContractionOff);
512bdd1243dSDimitry Andric       Inst.addOperand(MCOperand::createImm(EM));
513bdd1243dSDimitry Andric       outputMCInst(Inst);
514bdd1243dSDimitry Andric     }
515fcaf7f86SDimitry Andric   }
516fcaf7f86SDimitry Andric }
517fcaf7f86SDimitry Andric 
518fcaf7f86SDimitry Andric void SPIRVAsmPrinter::outputAnnotations(const Module &M) {
519fcaf7f86SDimitry Andric   outputModuleSection(SPIRV::MB_Annotations);
520fcaf7f86SDimitry Andric   // Process llvm.global.annotations special global variable.
521fcaf7f86SDimitry Andric   for (auto F = M.global_begin(), E = M.global_end(); F != E; ++F) {
522fcaf7f86SDimitry Andric     if ((*F).getName() != "llvm.global.annotations")
523fcaf7f86SDimitry Andric       continue;
524fcaf7f86SDimitry Andric     const GlobalVariable *V = &(*F);
525fcaf7f86SDimitry Andric     const ConstantArray *CA = cast<ConstantArray>(V->getOperand(0));
526fcaf7f86SDimitry Andric     for (Value *Op : CA->operands()) {
527fcaf7f86SDimitry Andric       ConstantStruct *CS = cast<ConstantStruct>(Op);
528fcaf7f86SDimitry Andric       // The first field of the struct contains a pointer to
529fcaf7f86SDimitry Andric       // the annotated variable.
530fcaf7f86SDimitry Andric       Value *AnnotatedVar = CS->getOperand(0)->stripPointerCasts();
531fcaf7f86SDimitry Andric       if (!isa<Function>(AnnotatedVar))
532bdd1243dSDimitry Andric         report_fatal_error("Unsupported value in llvm.global.annotations");
533fcaf7f86SDimitry Andric       Function *Func = cast<Function>(AnnotatedVar);
534bdd1243dSDimitry Andric       Register Reg = MAI->getFuncReg(Func);
535*0fca6ea1SDimitry Andric       if (!Reg.isValid()) {
536*0fca6ea1SDimitry Andric         std::string DiagMsg;
537*0fca6ea1SDimitry Andric         raw_string_ostream OS(DiagMsg);
538*0fca6ea1SDimitry Andric         AnnotatedVar->print(OS);
539*0fca6ea1SDimitry Andric         DiagMsg = "Unknown function in llvm.global.annotations: " + DiagMsg;
540*0fca6ea1SDimitry Andric         report_fatal_error(DiagMsg.c_str());
541*0fca6ea1SDimitry Andric       }
542fcaf7f86SDimitry Andric 
543fcaf7f86SDimitry Andric       // The second field contains a pointer to a global annotation string.
544fcaf7f86SDimitry Andric       GlobalVariable *GV =
545fcaf7f86SDimitry Andric           cast<GlobalVariable>(CS->getOperand(1)->stripPointerCasts());
546fcaf7f86SDimitry Andric 
547fcaf7f86SDimitry Andric       StringRef AnnotationString;
548fcaf7f86SDimitry Andric       getConstantStringInfo(GV, AnnotationString);
549fcaf7f86SDimitry Andric       MCInst Inst;
550fcaf7f86SDimitry Andric       Inst.setOpcode(SPIRV::OpDecorate);
551fcaf7f86SDimitry Andric       Inst.addOperand(MCOperand::createReg(Reg));
552fcaf7f86SDimitry Andric       unsigned Dec = static_cast<unsigned>(SPIRV::Decoration::UserSemantic);
553fcaf7f86SDimitry Andric       Inst.addOperand(MCOperand::createImm(Dec));
554fcaf7f86SDimitry Andric       addStringImm(AnnotationString, Inst);
555fcaf7f86SDimitry Andric       outputMCInst(Inst);
556fcaf7f86SDimitry Andric     }
557fcaf7f86SDimitry Andric   }
558fcaf7f86SDimitry Andric }
559fcaf7f86SDimitry Andric 
56081ad6265SDimitry Andric void SPIRVAsmPrinter::outputModuleSections() {
56181ad6265SDimitry Andric   const Module *M = MMI->getModule();
56281ad6265SDimitry Andric   // Get the global subtarget to output module-level info.
56381ad6265SDimitry Andric   ST = static_cast<const SPIRVTargetMachine &>(TM).getSubtargetImpl();
56481ad6265SDimitry Andric   TII = ST->getInstrInfo();
56581ad6265SDimitry Andric   MAI = &SPIRVModuleAnalysis::MAI;
56681ad6265SDimitry Andric   assert(ST && TII && MAI && M && "Module analysis is required");
56781ad6265SDimitry Andric   // Output instructions according to the Logical Layout of a Module:
568bdd1243dSDimitry Andric   // 1,2. All OpCapability instructions, then optional OpExtension instructions.
569bdd1243dSDimitry Andric   outputGlobalRequirements();
570fcaf7f86SDimitry Andric   // 3. Optional OpExtInstImport instructions.
571fcaf7f86SDimitry Andric   outputOpExtInstImports(*M);
57281ad6265SDimitry Andric   // 4. The single required OpMemoryModel instruction.
57381ad6265SDimitry Andric   outputOpMemoryModel();
57481ad6265SDimitry Andric   // 5. All entry point declarations, using OpEntryPoint.
57581ad6265SDimitry Andric   outputEntryPoints();
57681ad6265SDimitry Andric   // 6. Execution-mode declarations, using OpExecutionMode or OpExecutionModeId.
577fcaf7f86SDimitry Andric   outputExecutionMode(*M);
57881ad6265SDimitry Andric   // 7a. Debug: all OpString, OpSourceExtension, OpSource, and
57981ad6265SDimitry Andric   // OpSourceContinued, without forward references.
58081ad6265SDimitry Andric   outputDebugSourceAndStrings(*M);
58181ad6265SDimitry Andric   // 7b. Debug: all OpName and all OpMemberName.
58281ad6265SDimitry Andric   outputModuleSection(SPIRV::MB_DebugNames);
58381ad6265SDimitry Andric   // 7c. Debug: all OpModuleProcessed instructions.
58481ad6265SDimitry Andric   outputModuleSection(SPIRV::MB_DebugModuleProcessed);
58581ad6265SDimitry Andric   // 8. All annotation instructions (all decorations).
586fcaf7f86SDimitry Andric   outputAnnotations(*M);
58781ad6265SDimitry Andric   // 9. All type declarations (OpTypeXXX instructions), all constant
58881ad6265SDimitry Andric   // instructions, and all global variable declarations. This section is
58981ad6265SDimitry Andric   // the first section to allow use of: OpLine and OpNoLine debug information;
59081ad6265SDimitry Andric   // non-semantic instructions with OpExtInst.
59181ad6265SDimitry Andric   outputModuleSection(SPIRV::MB_TypeConstVars);
59281ad6265SDimitry Andric   // 10. All function declarations (functions without a body).
59381ad6265SDimitry Andric   outputExtFuncDecls();
59481ad6265SDimitry Andric   // 11. All function definitions (functions with a body).
59581ad6265SDimitry Andric   // This is done in regular function output.
59681ad6265SDimitry Andric }
59781ad6265SDimitry Andric 
59881ad6265SDimitry Andric bool SPIRVAsmPrinter::doInitialization(Module &M) {
59981ad6265SDimitry Andric   ModuleSectionsEmitted = false;
60081ad6265SDimitry Andric   // We need to call the parent's one explicitly.
60181ad6265SDimitry Andric   return AsmPrinter::doInitialization(M);
60281ad6265SDimitry Andric }
60381ad6265SDimitry Andric 
60481ad6265SDimitry Andric // Force static initialization.
60581ad6265SDimitry Andric extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeSPIRVAsmPrinter() {
60681ad6265SDimitry Andric   RegisterAsmPrinter<SPIRVAsmPrinter> X(getTheSPIRV32Target());
60781ad6265SDimitry Andric   RegisterAsmPrinter<SPIRVAsmPrinter> Y(getTheSPIRV64Target());
6085f757f3fSDimitry Andric   RegisterAsmPrinter<SPIRVAsmPrinter> Z(getTheSPIRVLogicalTarget());
60981ad6265SDimitry Andric }
610