1 | //===-- SPIRVAsmPrinter.cpp - SPIR-V LLVM assembly writer ------*- C++ -*--===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This file contains a printer that converts from our internal representation |
10 | // of machine-dependent LLVM code to the SPIR-V assembly language. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "MCTargetDesc/SPIRVInstPrinter.h" |
15 | #include "SPIRV.h" |
16 | #include "SPIRVInstrInfo.h" |
17 | #include "SPIRVMCInstLower.h" |
18 | #include "SPIRVModuleAnalysis.h" |
19 | #include "SPIRVSubtarget.h" |
20 | #include "SPIRVTargetMachine.h" |
21 | #include "SPIRVUtils.h" |
22 | #include "TargetInfo/SPIRVTargetInfo.h" |
23 | #include "llvm/ADT/DenseMap.h" |
24 | #include "llvm/Analysis/ValueTracking.h" |
25 | #include "llvm/CodeGen/AsmPrinter.h" |
26 | #include "llvm/CodeGen/MachineConstantPool.h" |
27 | #include "llvm/CodeGen/MachineFunctionPass.h" |
28 | #include "llvm/CodeGen/MachineInstr.h" |
29 | #include "llvm/CodeGen/MachineModuleInfo.h" |
30 | #include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" |
31 | #include "llvm/MC/MCAsmInfo.h" |
32 | #include "llvm/MC/MCAssembler.h" |
33 | #include "llvm/MC/MCInst.h" |
34 | #include "llvm/MC/MCObjectStreamer.h" |
35 | #include "llvm/MC/MCStreamer.h" |
36 | #include "llvm/MC/MCSymbol.h" |
37 | #include "llvm/MC/TargetRegistry.h" |
38 | #include "llvm/Support/raw_ostream.h" |
39 | |
40 | using namespace llvm; |
41 | |
42 | #define DEBUG_TYPE "asm-printer" |
43 | |
44 | namespace { |
45 | class SPIRVAsmPrinter : public AsmPrinter { |
46 | unsigned NLabels = 0; |
47 | |
48 | public: |
49 | explicit SPIRVAsmPrinter(TargetMachine &TM, |
50 | std::unique_ptr<MCStreamer> Streamer) |
51 | : AsmPrinter(TM, std::move(Streamer)), ST(nullptr), TII(nullptr) {} |
52 | bool ModuleSectionsEmitted; |
53 | const SPIRVSubtarget *ST; |
54 | const SPIRVInstrInfo *TII; |
55 | |
56 | StringRef getPassName() const override { return "SPIRV Assembly Printer" ; } |
57 | void printOperand(const MachineInstr *MI, int OpNum, raw_ostream &O); |
58 | bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, |
59 | const char *, raw_ostream &O) override; |
60 | |
61 | void outputMCInst(MCInst &Inst); |
62 | void outputInstruction(const MachineInstr *MI); |
63 | void outputModuleSection(SPIRV::ModuleSectionType MSType); |
64 | void outputGlobalRequirements(); |
65 | void outputEntryPoints(); |
66 | void outputDebugSourceAndStrings(const Module &M); |
67 | void outputOpExtInstImports(const Module &M); |
68 | void outputOpMemoryModel(); |
69 | void outputOpFunctionEnd(); |
70 | void outputExtFuncDecls(); |
71 | void outputExecutionModeFromMDNode(Register Reg, MDNode *Node, |
72 | SPIRV::ExecutionMode::ExecutionMode EM); |
73 | void outputExecutionModeFromNumthreadsAttribute( |
74 | const Register &Reg, const Attribute &Attr, |
75 | SPIRV::ExecutionMode::ExecutionMode EM); |
76 | void outputExecutionMode(const Module &M); |
77 | void outputAnnotations(const Module &M); |
78 | void outputModuleSections(); |
79 | |
80 | void emitInstruction(const MachineInstr *MI) override; |
81 | void emitFunctionEntryLabel() override {} |
82 | void emitFunctionHeader() override; |
83 | void emitFunctionBodyStart() override {} |
84 | void emitFunctionBodyEnd() override; |
85 | void emitBasicBlockStart(const MachineBasicBlock &MBB) override; |
86 | void emitBasicBlockEnd(const MachineBasicBlock &MBB) override {} |
87 | void emitGlobalVariable(const GlobalVariable *GV) override {} |
88 | void emitOpLabel(const MachineBasicBlock &MBB); |
89 | void emitEndOfAsmFile(Module &M) override; |
90 | bool doInitialization(Module &M) override; |
91 | |
92 | void getAnalysisUsage(AnalysisUsage &AU) const override; |
93 | SPIRV::ModuleAnalysisInfo *MAI; |
94 | }; |
95 | } // namespace |
96 | |
97 | void SPIRVAsmPrinter::getAnalysisUsage(AnalysisUsage &AU) const { |
98 | AU.addRequired<SPIRVModuleAnalysis>(); |
99 | AU.addPreserved<SPIRVModuleAnalysis>(); |
100 | AsmPrinter::getAnalysisUsage(AU); |
101 | } |
102 | |
103 | // If the module has no functions, we need output global info anyway. |
104 | void SPIRVAsmPrinter::emitEndOfAsmFile(Module &M) { |
105 | if (ModuleSectionsEmitted == false) { |
106 | outputModuleSections(); |
107 | ModuleSectionsEmitted = true; |
108 | } |
109 | |
110 | ST = static_cast<const SPIRVTargetMachine &>(TM).getSubtargetImpl(); |
111 | VersionTuple SPIRVVersion = ST->getSPIRVVersion(); |
112 | uint32_t Major = SPIRVVersion.getMajor(); |
113 | uint32_t Minor = SPIRVVersion.getMinor().value_or(u: 0); |
114 | // Bound is an approximation that accounts for the maximum used register |
115 | // number and number of generated OpLabels |
116 | unsigned Bound = 2 * (ST->getBound() + 1) + NLabels; |
117 | bool FlagToRestore = OutStreamer->getUseAssemblerInfoForParsing(); |
118 | OutStreamer->setUseAssemblerInfoForParsing(true); |
119 | if (MCAssembler *Asm = OutStreamer->getAssemblerPtr()) |
120 | Asm->setBuildVersion(Platform: static_cast<MachO::PlatformType>(0), Major, Minor, |
121 | Update: Bound, SDKVersion: VersionTuple(Major, Minor, 0, Bound)); |
122 | OutStreamer->setUseAssemblerInfoForParsing(FlagToRestore); |
123 | } |
124 | |
125 | void SPIRVAsmPrinter::() { |
126 | if (ModuleSectionsEmitted == false) { |
127 | outputModuleSections(); |
128 | ModuleSectionsEmitted = true; |
129 | } |
130 | // Get the subtarget from the current MachineFunction. |
131 | ST = &MF->getSubtarget<SPIRVSubtarget>(); |
132 | TII = ST->getInstrInfo(); |
133 | const Function &F = MF->getFunction(); |
134 | |
135 | if (isVerbose()) { |
136 | OutStreamer->getCommentOS() |
137 | << "-- Begin function " |
138 | << GlobalValue::dropLLVMManglingEscape(Name: F.getName()) << '\n'; |
139 | } |
140 | |
141 | auto Section = getObjFileLowering().SectionForGlobal(GO: &F, TM); |
142 | MF->setSection(Section); |
143 | } |
144 | |
145 | void SPIRVAsmPrinter::outputOpFunctionEnd() { |
146 | MCInst FunctionEndInst; |
147 | FunctionEndInst.setOpcode(SPIRV::OpFunctionEnd); |
148 | outputMCInst(Inst&: FunctionEndInst); |
149 | } |
150 | |
151 | // Emit OpFunctionEnd at the end of MF and clear BBNumToRegMap. |
152 | void SPIRVAsmPrinter::emitFunctionBodyEnd() { |
153 | outputOpFunctionEnd(); |
154 | MAI->BBNumToRegMap.clear(); |
155 | } |
156 | |
157 | void SPIRVAsmPrinter::emitOpLabel(const MachineBasicBlock &MBB) { |
158 | MCInst LabelInst; |
159 | LabelInst.setOpcode(SPIRV::OpLabel); |
160 | LabelInst.addOperand(Op: MCOperand::createReg(Reg: MAI->getOrCreateMBBRegister(MBB))); |
161 | outputMCInst(Inst&: LabelInst); |
162 | ++NLabels; |
163 | } |
164 | |
165 | void SPIRVAsmPrinter::emitBasicBlockStart(const MachineBasicBlock &MBB) { |
166 | assert(!MBB.empty() && "MBB is empty!" ); |
167 | |
168 | // If it's the first MBB in MF, it has OpFunction and OpFunctionParameter, so |
169 | // OpLabel should be output after them. |
170 | if (MBB.getNumber() == MF->front().getNumber()) { |
171 | for (const MachineInstr &MI : MBB) |
172 | if (MI.getOpcode() == SPIRV::OpFunction) |
173 | return; |
174 | // TODO: this case should be checked by the verifier. |
175 | report_fatal_error(reason: "OpFunction is expected in the front MBB of MF" ); |
176 | } |
177 | emitOpLabel(MBB); |
178 | } |
179 | |
180 | void SPIRVAsmPrinter::printOperand(const MachineInstr *MI, int OpNum, |
181 | raw_ostream &O) { |
182 | const MachineOperand &MO = MI->getOperand(i: OpNum); |
183 | |
184 | switch (MO.getType()) { |
185 | case MachineOperand::MO_Register: |
186 | O << SPIRVInstPrinter::getRegisterName(Reg: MO.getReg()); |
187 | break; |
188 | |
189 | case MachineOperand::MO_Immediate: |
190 | O << MO.getImm(); |
191 | break; |
192 | |
193 | case MachineOperand::MO_FPImmediate: |
194 | O << MO.getFPImm(); |
195 | break; |
196 | |
197 | case MachineOperand::MO_MachineBasicBlock: |
198 | O << *MO.getMBB()->getSymbol(); |
199 | break; |
200 | |
201 | case MachineOperand::MO_GlobalAddress: |
202 | O << *getSymbol(GV: MO.getGlobal()); |
203 | break; |
204 | |
205 | case MachineOperand::MO_BlockAddress: { |
206 | MCSymbol *BA = GetBlockAddressSymbol(BA: MO.getBlockAddress()); |
207 | O << BA->getName(); |
208 | break; |
209 | } |
210 | |
211 | case MachineOperand::MO_ExternalSymbol: |
212 | O << *GetExternalSymbolSymbol(Sym: MO.getSymbolName()); |
213 | break; |
214 | |
215 | case MachineOperand::MO_JumpTableIndex: |
216 | case MachineOperand::MO_ConstantPoolIndex: |
217 | default: |
218 | llvm_unreachable("<unknown operand type>" ); |
219 | } |
220 | } |
221 | |
222 | bool SPIRVAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, |
223 | const char *, raw_ostream &O) { |
224 | if (ExtraCode && ExtraCode[0]) |
225 | return true; // Invalid instruction - SPIR-V does not have special modifiers |
226 | |
227 | printOperand(MI, OpNum: OpNo, O); |
228 | return false; |
229 | } |
230 | |
231 | static bool (const MachineInstr *MI, |
232 | const SPIRVInstrInfo *TII) { |
233 | return TII->isHeaderInstr(*MI) || MI->getOpcode() == SPIRV::OpFunction || |
234 | MI->getOpcode() == SPIRV::OpFunctionParameter; |
235 | } |
236 | |
237 | void SPIRVAsmPrinter::outputMCInst(MCInst &Inst) { |
238 | OutStreamer->emitInstruction(Inst, STI: *OutContext.getSubtargetInfo()); |
239 | } |
240 | |
241 | void SPIRVAsmPrinter::outputInstruction(const MachineInstr *MI) { |
242 | SPIRVMCInstLower MCInstLowering; |
243 | MCInst TmpInst; |
244 | MCInstLowering.lower(MI, OutMI&: TmpInst, MAI); |
245 | outputMCInst(Inst&: TmpInst); |
246 | } |
247 | |
248 | void SPIRVAsmPrinter::emitInstruction(const MachineInstr *MI) { |
249 | SPIRV_MC::verifyInstructionPredicates(MI->getOpcode(), |
250 | getSubtargetInfo().getFeatureBits()); |
251 | |
252 | if (!MAI->getSkipEmission(MI)) |
253 | outputInstruction(MI); |
254 | |
255 | // Output OpLabel after OpFunction and OpFunctionParameter in the first MBB. |
256 | const MachineInstr *NextMI = MI->getNextNode(); |
257 | if (!MAI->hasMBBRegister(MBB: *MI->getParent()) && isFuncOrHeaderInstr(MI, TII) && |
258 | (!NextMI || !isFuncOrHeaderInstr(MI: NextMI, TII))) { |
259 | assert(MI->getParent()->getNumber() == MF->front().getNumber() && |
260 | "OpFunction is not in the front MBB of MF" ); |
261 | emitOpLabel(MBB: *MI->getParent()); |
262 | } |
263 | } |
264 | |
265 | void SPIRVAsmPrinter::outputModuleSection(SPIRV::ModuleSectionType MSType) { |
266 | for (MachineInstr *MI : MAI->getMSInstrs(MSType)) |
267 | outputInstruction(MI); |
268 | } |
269 | |
270 | void SPIRVAsmPrinter::outputDebugSourceAndStrings(const Module &M) { |
271 | // Output OpSourceExtensions. |
272 | for (auto &Str : MAI->SrcExt) { |
273 | MCInst Inst; |
274 | Inst.setOpcode(SPIRV::OpSourceExtension); |
275 | addStringImm(Str: Str.first(), Inst); |
276 | outputMCInst(Inst); |
277 | } |
278 | // Output OpSource. |
279 | MCInst Inst; |
280 | Inst.setOpcode(SPIRV::OpSource); |
281 | Inst.addOperand(Op: MCOperand::createImm(Val: static_cast<unsigned>(MAI->SrcLang))); |
282 | Inst.addOperand( |
283 | Op: MCOperand::createImm(Val: static_cast<unsigned>(MAI->SrcLangVersion))); |
284 | outputMCInst(Inst); |
285 | } |
286 | |
287 | void SPIRVAsmPrinter::outputOpExtInstImports(const Module &M) { |
288 | for (auto &CU : MAI->ExtInstSetMap) { |
289 | unsigned Set = CU.first; |
290 | Register Reg = CU.second; |
291 | MCInst Inst; |
292 | Inst.setOpcode(SPIRV::OpExtInstImport); |
293 | Inst.addOperand(Op: MCOperand::createReg(Reg)); |
294 | addStringImm(getExtInstSetName( |
295 | static_cast<SPIRV::InstructionSet::InstructionSet>(Set)), |
296 | Inst); |
297 | outputMCInst(Inst); |
298 | } |
299 | } |
300 | |
301 | void SPIRVAsmPrinter::outputOpMemoryModel() { |
302 | MCInst Inst; |
303 | Inst.setOpcode(SPIRV::OpMemoryModel); |
304 | Inst.addOperand(Op: MCOperand::createImm(Val: static_cast<unsigned>(MAI->Addr))); |
305 | Inst.addOperand(Op: MCOperand::createImm(Val: static_cast<unsigned>(MAI->Mem))); |
306 | outputMCInst(Inst); |
307 | } |
308 | |
309 | // Before the OpEntryPoints' output, we need to add the entry point's |
310 | // interfaces. The interface is a list of IDs of global OpVariable instructions. |
311 | // These declare the set of global variables from a module that form |
312 | // the interface of this entry point. |
313 | void SPIRVAsmPrinter::outputEntryPoints() { |
314 | // Find all OpVariable IDs with required StorageClass. |
315 | DenseSet<Register> InterfaceIDs; |
316 | for (MachineInstr *MI : MAI->GlobalVarList) { |
317 | assert(MI->getOpcode() == SPIRV::OpVariable); |
318 | auto SC = static_cast<SPIRV::StorageClass::StorageClass>( |
319 | MI->getOperand(i: 2).getImm()); |
320 | // Before version 1.4, the interface's storage classes are limited to |
321 | // the Input and Output storage classes. Starting with version 1.4, |
322 | // the interface's storage classes are all storage classes used in |
323 | // declaring all global variables referenced by the entry point call tree. |
324 | if (ST->isAtLeastSPIRVVer(VersionTuple(1, 4)) || |
325 | SC == SPIRV::StorageClass::Input || SC == SPIRV::StorageClass::Output) { |
326 | MachineFunction *MF = MI->getMF(); |
327 | Register Reg = MAI->getRegisterAlias(MF, Reg: MI->getOperand(i: 0).getReg()); |
328 | InterfaceIDs.insert(V: Reg); |
329 | } |
330 | } |
331 | |
332 | // Output OpEntryPoints adding interface args to all of them. |
333 | for (MachineInstr *MI : MAI->getMSInstrs(MSType: SPIRV::MB_EntryPoints)) { |
334 | SPIRVMCInstLower MCInstLowering; |
335 | MCInst TmpInst; |
336 | MCInstLowering.lower(MI, OutMI&: TmpInst, MAI); |
337 | for (Register Reg : InterfaceIDs) { |
338 | assert(Reg.isValid()); |
339 | TmpInst.addOperand(Op: MCOperand::createReg(Reg)); |
340 | } |
341 | outputMCInst(Inst&: TmpInst); |
342 | } |
343 | } |
344 | |
345 | // Create global OpCapability instructions for the required capabilities. |
346 | void SPIRVAsmPrinter::outputGlobalRequirements() { |
347 | // Abort here if not all requirements can be satisfied. |
348 | MAI->Reqs.checkSatisfiable(*ST); |
349 | |
350 | for (const auto &Cap : MAI->Reqs.getMinimalCapabilities()) { |
351 | MCInst Inst; |
352 | Inst.setOpcode(SPIRV::OpCapability); |
353 | Inst.addOperand(MCOperand::createImm(Cap)); |
354 | outputMCInst(Inst); |
355 | } |
356 | |
357 | // Generate the final OpExtensions with strings instead of enums. |
358 | for (const auto &Ext : MAI->Reqs.getExtensions()) { |
359 | MCInst Inst; |
360 | Inst.setOpcode(SPIRV::OpExtension); |
361 | addStringImm(getSymbolicOperandMnemonic( |
362 | SPIRV::OperandCategory::ExtensionOperand, Ext), |
363 | Inst); |
364 | outputMCInst(Inst); |
365 | } |
366 | // TODO add a pseudo instr for version number. |
367 | } |
368 | |
369 | void SPIRVAsmPrinter::outputExtFuncDecls() { |
370 | // Insert OpFunctionEnd after each declaration. |
371 | SmallVectorImpl<MachineInstr *>::iterator |
372 | I = MAI->getMSInstrs(MSType: SPIRV::MB_ExtFuncDecls).begin(), |
373 | E = MAI->getMSInstrs(MSType: SPIRV::MB_ExtFuncDecls).end(); |
374 | for (; I != E; ++I) { |
375 | outputInstruction(MI: *I); |
376 | if ((I + 1) == E || (*(I + 1))->getOpcode() == SPIRV::OpFunction) |
377 | outputOpFunctionEnd(); |
378 | } |
379 | } |
380 | |
381 | // Encode LLVM type by SPIR-V execution mode VecTypeHint. |
382 | static unsigned encodeVecTypeHint(Type *Ty) { |
383 | if (Ty->isHalfTy()) |
384 | return 4; |
385 | if (Ty->isFloatTy()) |
386 | return 5; |
387 | if (Ty->isDoubleTy()) |
388 | return 6; |
389 | if (IntegerType *IntTy = dyn_cast<IntegerType>(Val: Ty)) { |
390 | switch (IntTy->getIntegerBitWidth()) { |
391 | case 8: |
392 | return 0; |
393 | case 16: |
394 | return 1; |
395 | case 32: |
396 | return 2; |
397 | case 64: |
398 | return 3; |
399 | default: |
400 | llvm_unreachable("invalid integer type" ); |
401 | } |
402 | } |
403 | if (FixedVectorType *VecTy = dyn_cast<FixedVectorType>(Val: Ty)) { |
404 | Type *EleTy = VecTy->getElementType(); |
405 | unsigned Size = VecTy->getNumElements(); |
406 | return Size << 16 | encodeVecTypeHint(Ty: EleTy); |
407 | } |
408 | llvm_unreachable("invalid type" ); |
409 | } |
410 | |
411 | static void addOpsFromMDNode(MDNode *MDN, MCInst &Inst, |
412 | SPIRV::ModuleAnalysisInfo *MAI) { |
413 | for (const MDOperand &MDOp : MDN->operands()) { |
414 | if (auto *CMeta = dyn_cast<ConstantAsMetadata>(Val: MDOp)) { |
415 | Constant *C = CMeta->getValue(); |
416 | if (ConstantInt *Const = dyn_cast<ConstantInt>(Val: C)) { |
417 | Inst.addOperand(Op: MCOperand::createImm(Val: Const->getZExtValue())); |
418 | } else if (auto *CE = dyn_cast<Function>(Val: C)) { |
419 | Register FuncReg = MAI->getFuncReg(F: CE); |
420 | assert(FuncReg.isValid()); |
421 | Inst.addOperand(Op: MCOperand::createReg(Reg: FuncReg)); |
422 | } |
423 | } |
424 | } |
425 | } |
426 | |
427 | void SPIRVAsmPrinter::outputExecutionModeFromMDNode( |
428 | Register Reg, MDNode *Node, SPIRV::ExecutionMode::ExecutionMode EM) { |
429 | MCInst Inst; |
430 | Inst.setOpcode(SPIRV::OpExecutionMode); |
431 | Inst.addOperand(Op: MCOperand::createReg(Reg)); |
432 | Inst.addOperand(Op: MCOperand::createImm(Val: static_cast<unsigned>(EM))); |
433 | addOpsFromMDNode(MDN: Node, Inst, MAI); |
434 | outputMCInst(Inst); |
435 | } |
436 | |
437 | void SPIRVAsmPrinter::outputExecutionModeFromNumthreadsAttribute( |
438 | const Register &Reg, const Attribute &Attr, |
439 | SPIRV::ExecutionMode::ExecutionMode EM) { |
440 | assert(Attr.isValid() && "Function called with an invalid attribute." ); |
441 | |
442 | MCInst Inst; |
443 | Inst.setOpcode(SPIRV::OpExecutionMode); |
444 | Inst.addOperand(Op: MCOperand::createReg(Reg)); |
445 | Inst.addOperand(Op: MCOperand::createImm(Val: static_cast<unsigned>(EM))); |
446 | |
447 | SmallVector<StringRef> NumThreads; |
448 | Attr.getValueAsString().split(A&: NumThreads, Separator: ','); |
449 | assert(NumThreads.size() == 3 && "invalid numthreads" ); |
450 | for (uint32_t i = 0; i < 3; ++i) { |
451 | uint32_t V; |
452 | [[maybe_unused]] bool Result = NumThreads[i].getAsInteger(Radix: 10, Result&: V); |
453 | assert(!Result && "Failed to parse numthreads" ); |
454 | Inst.addOperand(Op: MCOperand::createImm(Val: V)); |
455 | } |
456 | |
457 | outputMCInst(Inst); |
458 | } |
459 | |
460 | void SPIRVAsmPrinter::outputExecutionMode(const Module &M) { |
461 | NamedMDNode *Node = M.getNamedMetadata(Name: "spirv.ExecutionMode" ); |
462 | if (Node) { |
463 | for (unsigned i = 0; i < Node->getNumOperands(); i++) { |
464 | MCInst Inst; |
465 | Inst.setOpcode(SPIRV::OpExecutionMode); |
466 | addOpsFromMDNode(MDN: cast<MDNode>(Val: Node->getOperand(i)), Inst, MAI); |
467 | outputMCInst(Inst); |
468 | } |
469 | } |
470 | for (auto FI = M.begin(), E = M.end(); FI != E; ++FI) { |
471 | const Function &F = *FI; |
472 | // Only operands of OpEntryPoint instructions are allowed to be |
473 | // <Entry Point> operands of OpExecutionMode |
474 | if (F.isDeclaration() || !isEntryPoint(F)) |
475 | continue; |
476 | Register FReg = MAI->getFuncReg(F: &F); |
477 | assert(FReg.isValid()); |
478 | if (MDNode *Node = F.getMetadata("reqd_work_group_size" )) |
479 | outputExecutionModeFromMDNode(FReg, Node, |
480 | SPIRV::ExecutionMode::LocalSize); |
481 | if (Attribute Attr = F.getFnAttribute("hlsl.numthreads" ); Attr.isValid()) |
482 | outputExecutionModeFromNumthreadsAttribute( |
483 | FReg, Attr, SPIRV::ExecutionMode::LocalSize); |
484 | if (MDNode *Node = F.getMetadata("work_group_size_hint" )) |
485 | outputExecutionModeFromMDNode(FReg, Node, |
486 | SPIRV::ExecutionMode::LocalSizeHint); |
487 | if (MDNode *Node = F.getMetadata("intel_reqd_sub_group_size" )) |
488 | outputExecutionModeFromMDNode(FReg, Node, |
489 | SPIRV::ExecutionMode::SubgroupSize); |
490 | if (MDNode *Node = F.getMetadata(Kind: "vec_type_hint" )) { |
491 | MCInst Inst; |
492 | Inst.setOpcode(SPIRV::OpExecutionMode); |
493 | Inst.addOperand(Op: MCOperand::createReg(Reg: FReg)); |
494 | unsigned EM = static_cast<unsigned>(SPIRV::ExecutionMode::VecTypeHint); |
495 | Inst.addOperand(Op: MCOperand::createImm(Val: EM)); |
496 | unsigned TypeCode = encodeVecTypeHint(Ty: getMDOperandAsType(N: Node, I: 0)); |
497 | Inst.addOperand(Op: MCOperand::createImm(Val: TypeCode)); |
498 | outputMCInst(Inst); |
499 | } |
500 | if (ST->isOpenCLEnv() && !M.getNamedMetadata(Name: "spirv.ExecutionMode" ) && |
501 | !M.getNamedMetadata(Name: "opencl.enable.FP_CONTRACT" )) { |
502 | MCInst Inst; |
503 | Inst.setOpcode(SPIRV::OpExecutionMode); |
504 | Inst.addOperand(Op: MCOperand::createReg(Reg: FReg)); |
505 | unsigned EM = static_cast<unsigned>(SPIRV::ExecutionMode::ContractionOff); |
506 | Inst.addOperand(Op: MCOperand::createImm(Val: EM)); |
507 | outputMCInst(Inst); |
508 | } |
509 | } |
510 | } |
511 | |
512 | void SPIRVAsmPrinter::outputAnnotations(const Module &M) { |
513 | outputModuleSection(MSType: SPIRV::MB_Annotations); |
514 | // Process llvm.global.annotations special global variable. |
515 | for (auto F = M.global_begin(), E = M.global_end(); F != E; ++F) { |
516 | if ((*F).getName() != "llvm.global.annotations" ) |
517 | continue; |
518 | const GlobalVariable *V = &(*F); |
519 | const ConstantArray *CA = cast<ConstantArray>(Val: V->getOperand(i_nocapture: 0)); |
520 | for (Value *Op : CA->operands()) { |
521 | ConstantStruct *CS = cast<ConstantStruct>(Val: Op); |
522 | // The first field of the struct contains a pointer to |
523 | // the annotated variable. |
524 | Value *AnnotatedVar = CS->getOperand(i_nocapture: 0)->stripPointerCasts(); |
525 | if (!isa<Function>(Val: AnnotatedVar)) |
526 | report_fatal_error(reason: "Unsupported value in llvm.global.annotations" ); |
527 | Function *Func = cast<Function>(Val: AnnotatedVar); |
528 | Register Reg = MAI->getFuncReg(F: Func); |
529 | if (!Reg.isValid()) { |
530 | std::string DiagMsg; |
531 | raw_string_ostream OS(DiagMsg); |
532 | AnnotatedVar->print(O&: OS); |
533 | DiagMsg = "Unknown function in llvm.global.annotations: " + DiagMsg; |
534 | report_fatal_error(reason: DiagMsg.c_str()); |
535 | } |
536 | |
537 | // The second field contains a pointer to a global annotation string. |
538 | GlobalVariable *GV = |
539 | cast<GlobalVariable>(Val: CS->getOperand(i_nocapture: 1)->stripPointerCasts()); |
540 | |
541 | StringRef AnnotationString; |
542 | getConstantStringInfo(V: GV, Str&: AnnotationString); |
543 | MCInst Inst; |
544 | Inst.setOpcode(SPIRV::OpDecorate); |
545 | Inst.addOperand(Op: MCOperand::createReg(Reg)); |
546 | unsigned Dec = static_cast<unsigned>(SPIRV::Decoration::UserSemantic); |
547 | Inst.addOperand(Op: MCOperand::createImm(Val: Dec)); |
548 | addStringImm(Str: AnnotationString, Inst); |
549 | outputMCInst(Inst); |
550 | } |
551 | } |
552 | } |
553 | |
554 | void SPIRVAsmPrinter::outputModuleSections() { |
555 | const Module *M = MMI->getModule(); |
556 | // Get the global subtarget to output module-level info. |
557 | ST = static_cast<const SPIRVTargetMachine &>(TM).getSubtargetImpl(); |
558 | TII = ST->getInstrInfo(); |
559 | MAI = &SPIRVModuleAnalysis::MAI; |
560 | assert(ST && TII && MAI && M && "Module analysis is required" ); |
561 | // Output instructions according to the Logical Layout of a Module: |
562 | // 1,2. All OpCapability instructions, then optional OpExtension instructions. |
563 | outputGlobalRequirements(); |
564 | // 3. Optional OpExtInstImport instructions. |
565 | outputOpExtInstImports(M: *M); |
566 | // 4. The single required OpMemoryModel instruction. |
567 | outputOpMemoryModel(); |
568 | // 5. All entry point declarations, using OpEntryPoint. |
569 | outputEntryPoints(); |
570 | // 6. Execution-mode declarations, using OpExecutionMode or OpExecutionModeId. |
571 | outputExecutionMode(M: *M); |
572 | // 7a. Debug: all OpString, OpSourceExtension, OpSource, and |
573 | // OpSourceContinued, without forward references. |
574 | outputDebugSourceAndStrings(M: *M); |
575 | // 7b. Debug: all OpName and all OpMemberName. |
576 | outputModuleSection(MSType: SPIRV::MB_DebugNames); |
577 | // 7c. Debug: all OpModuleProcessed instructions. |
578 | outputModuleSection(MSType: SPIRV::MB_DebugModuleProcessed); |
579 | // 8. All annotation instructions (all decorations). |
580 | outputAnnotations(M: *M); |
581 | // 9. All type declarations (OpTypeXXX instructions), all constant |
582 | // instructions, and all global variable declarations. This section is |
583 | // the first section to allow use of: OpLine and OpNoLine debug information; |
584 | // non-semantic instructions with OpExtInst. |
585 | outputModuleSection(MSType: SPIRV::MB_TypeConstVars); |
586 | // 10. All function declarations (functions without a body). |
587 | outputExtFuncDecls(); |
588 | // 11. All function definitions (functions with a body). |
589 | // This is done in regular function output. |
590 | } |
591 | |
592 | bool SPIRVAsmPrinter::doInitialization(Module &M) { |
593 | ModuleSectionsEmitted = false; |
594 | // We need to call the parent's one explicitly. |
595 | return AsmPrinter::doInitialization(M); |
596 | } |
597 | |
598 | // Force static initialization. |
599 | extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeSPIRVAsmPrinter() { |
600 | RegisterAsmPrinter<SPIRVAsmPrinter> X(getTheSPIRV32Target()); |
601 | RegisterAsmPrinter<SPIRVAsmPrinter> Y(getTheSPIRV64Target()); |
602 | RegisterAsmPrinter<SPIRVAsmPrinter> Z(getTheSPIRVLogicalTarget()); |
603 | } |
604 | |