LLVM 22.0.0git
SPIRVAsmPrinter.cpp
Go to the documentation of this file.
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
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"
23#include "llvm/ADT/DenseMap.h"
30#include "llvm/MC/MCAsmInfo.h"
31#include "llvm/MC/MCAssembler.h"
32#include "llvm/MC/MCInst.h"
35#include "llvm/MC/MCStreamer.h"
36#include "llvm/MC/MCSymbol.h"
40
41using namespace llvm;
42
43#define DEBUG_TYPE "asm-printer"
44
45namespace {
46class SPIRVAsmPrinter : public AsmPrinter {
47 unsigned NLabels = 0;
49
50public:
51 explicit SPIRVAsmPrinter(TargetMachine &TM,
52 std::unique_ptr<MCStreamer> Streamer)
53 : AsmPrinter(TM, std::move(Streamer), ID), ModuleSectionsEmitted(false),
54 ST(nullptr), TII(nullptr), MAI(nullptr) {}
55 static char ID;
56 bool ModuleSectionsEmitted;
57 const SPIRVSubtarget *ST;
58 const SPIRVInstrInfo *TII;
59
60 StringRef getPassName() const override { return "SPIRV Assembly Printer"; }
61 void printOperand(const MachineInstr *MI, int OpNum, raw_ostream &O);
62 bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
63 const char *ExtraCode, raw_ostream &O) override;
64
65 void outputMCInst(MCInst &Inst);
66 void outputInstruction(const MachineInstr *MI);
67 void outputModuleSection(SPIRV::ModuleSectionType MSType);
68 void outputGlobalRequirements();
69 void outputEntryPoints();
70 void outputDebugSourceAndStrings(const Module &M);
71 void outputOpExtInstImports(const Module &M);
72 void outputOpMemoryModel();
73 void outputOpFunctionEnd();
74 void outputExtFuncDecls();
75 void outputExecutionModeFromMDNode(MCRegister Reg, MDNode *Node,
76 SPIRV::ExecutionMode::ExecutionMode EM,
77 unsigned ExpectMDOps, int64_t DefVal);
78 void outputExecutionModeFromNumthreadsAttribute(
79 const MCRegister &Reg, const Attribute &Attr,
80 SPIRV::ExecutionMode::ExecutionMode EM);
81 void outputExecutionModeFromEnableMaximalReconvergenceAttr(
82 const MCRegister &Reg, const SPIRVSubtarget &ST);
83 void outputExecutionMode(const Module &M);
84 void outputAnnotations(const Module &M);
85 void outputModuleSections();
86 void outputFPFastMathDefaultInfo();
87 bool isHidden() {
88 return MF->getFunction()
89 .getFnAttribute(SPIRV_BACKEND_SERVICE_FUN_NAME)
90 .isValid();
91 }
92
93 void emitInstruction(const MachineInstr *MI) override;
94 void emitFunctionEntryLabel() override {}
95 void emitFunctionHeader() override;
96 void emitFunctionBodyStart() override {}
97 void emitFunctionBodyEnd() override;
98 void emitBasicBlockStart(const MachineBasicBlock &MBB) override;
99 void emitBasicBlockEnd(const MachineBasicBlock &MBB) override {}
100 void emitGlobalVariable(const GlobalVariable *GV) override {}
101 void emitOpLabel(const MachineBasicBlock &MBB);
102 void emitEndOfAsmFile(Module &M) override;
103 bool doInitialization(Module &M) override;
104
105 void getAnalysisUsage(AnalysisUsage &AU) const override;
107
108protected:
109 void cleanUp(Module &M);
110};
111} // namespace
112
113void SPIRVAsmPrinter::getAnalysisUsage(AnalysisUsage &AU) const {
114 AU.addRequired<SPIRVModuleAnalysis>();
115 AU.addPreserved<SPIRVModuleAnalysis>();
117}
118
119// If the module has no functions, we need output global info anyway.
120void SPIRVAsmPrinter::emitEndOfAsmFile(Module &M) {
121 if (ModuleSectionsEmitted == false) {
122 outputModuleSections();
123 ModuleSectionsEmitted = true;
124 }
125
126 ST = static_cast<const SPIRVTargetMachine &>(TM).getSubtargetImpl();
127 VersionTuple SPIRVVersion = ST->getSPIRVVersion();
128 uint32_t Major = SPIRVVersion.getMajor();
129 uint32_t Minor = SPIRVVersion.getMinor().value_or(0);
130 // Bound is an approximation that accounts for the maximum used register
131 // number and number of generated OpLabels
132 unsigned Bound = 2 * (ST->getBound() + 1) + NLabels;
133 if (MCAssembler *Asm = OutStreamer->getAssemblerPtr())
134 static_cast<SPIRVObjectWriter &>(Asm->getWriter())
135 .setBuildVersion(Major, Minor, Bound);
136
137 cleanUp(M);
138}
139
140// Any cleanup actions with the Module after we don't care about its content
141// anymore.
142void SPIRVAsmPrinter::cleanUp(Module &M) {
143 // Verifier disallows uses of intrinsic global variables.
144 for (StringRef GVName :
145 {"llvm.global_ctors", "llvm.global_dtors", "llvm.used"}) {
146 if (GlobalVariable *GV = M.getNamedGlobal(GVName))
147 GV->setName("");
148 }
149}
150
151void SPIRVAsmPrinter::emitFunctionHeader() {
152 if (ModuleSectionsEmitted == false) {
153 outputModuleSections();
154 ModuleSectionsEmitted = true;
155 }
156 // Get the subtarget from the current MachineFunction.
157 ST = &MF->getSubtarget<SPIRVSubtarget>();
158 TII = ST->getInstrInfo();
159 const Function &F = MF->getFunction();
160
161 if (isVerbose() && !isHidden()) {
162 OutStreamer->getCommentOS()
163 << "-- Begin function "
164 << GlobalValue::dropLLVMManglingEscape(F.getName()) << '\n';
165 }
166
167 auto Section = getObjFileLowering().SectionForGlobal(&F, TM);
168 MF->setSection(Section);
169}
170
171void SPIRVAsmPrinter::outputOpFunctionEnd() {
172 MCInst FunctionEndInst;
173 FunctionEndInst.setOpcode(SPIRV::OpFunctionEnd);
174 outputMCInst(FunctionEndInst);
175}
176
177void SPIRVAsmPrinter::emitFunctionBodyEnd() {
178 if (!isHidden())
179 outputOpFunctionEnd();
180}
181
182void SPIRVAsmPrinter::emitOpLabel(const MachineBasicBlock &MBB) {
183 // Do not emit anything if it's an internal service function.
184 if (isHidden())
185 return;
186
187 MCInst LabelInst;
188 LabelInst.setOpcode(SPIRV::OpLabel);
189 LabelInst.addOperand(MCOperand::createReg(MAI->getOrCreateMBBRegister(MBB)));
190 outputMCInst(LabelInst);
191 ++NLabels;
192 LabeledMBB.insert(&MBB);
193}
194
195void SPIRVAsmPrinter::emitBasicBlockStart(const MachineBasicBlock &MBB) {
196 // Do not emit anything if it's an internal service function.
197 if (MBB.empty())
198 return;
199
200 // If it's the first MBB in MF, it has OpFunction and OpFunctionParameter, so
201 // OpLabel should be output after them.
202 if (MBB.getNumber() == MF->front().getNumber()) {
203 for (const MachineInstr &MI : MBB)
204 if (MI.getOpcode() == SPIRV::OpFunction)
205 return;
206 // TODO: this case should be checked by the verifier.
207 report_fatal_error("OpFunction is expected in the front MBB of MF");
208 }
209 emitOpLabel(MBB);
210}
211
212void SPIRVAsmPrinter::printOperand(const MachineInstr *MI, int OpNum,
213 raw_ostream &O) {
214 const MachineOperand &MO = MI->getOperand(OpNum);
215
216 switch (MO.getType()) {
219 break;
220
222 O << MO.getImm();
223 break;
224
226 O << MO.getFPImm();
227 break;
228
230 O << *MO.getMBB()->getSymbol();
231 break;
232
234 O << *getSymbol(MO.getGlobal());
235 break;
236
238 MCSymbol *BA = GetBlockAddressSymbol(MO.getBlockAddress());
239 O << BA->getName();
240 break;
241 }
242
244 O << *GetExternalSymbolSymbol(MO.getSymbolName());
245 break;
246
249 default:
250 llvm_unreachable("<unknown operand type>");
251 }
252}
253
254bool SPIRVAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
255 const char *ExtraCode, raw_ostream &O) {
256 if (ExtraCode && ExtraCode[0])
257 return true; // Invalid instruction - SPIR-V does not have special modifiers
258
259 printOperand(MI, OpNo, O);
260 return false;
261}
262
264 const SPIRVInstrInfo *TII) {
265 return TII->isHeaderInstr(*MI) || MI->getOpcode() == SPIRV::OpFunction ||
266 MI->getOpcode() == SPIRV::OpFunctionParameter;
267}
268
269void SPIRVAsmPrinter::outputMCInst(MCInst &Inst) {
270 OutStreamer->emitInstruction(Inst, *OutContext.getSubtargetInfo());
271}
272
273void SPIRVAsmPrinter::outputInstruction(const MachineInstr *MI) {
274 SPIRVMCInstLower MCInstLowering;
275 MCInst TmpInst;
276 MCInstLowering.lower(MI, TmpInst, MAI);
277 outputMCInst(TmpInst);
278}
279
280void SPIRVAsmPrinter::emitInstruction(const MachineInstr *MI) {
281 SPIRV_MC::verifyInstructionPredicates(MI->getOpcode(),
282 getSubtargetInfo().getFeatureBits());
283
284 if (!MAI->getSkipEmission(MI))
285 outputInstruction(MI);
286
287 // Output OpLabel after OpFunction and OpFunctionParameter in the first MBB.
288 const MachineInstr *NextMI = MI->getNextNode();
289 if (!LabeledMBB.contains(MI->getParent()) && isFuncOrHeaderInstr(MI, TII) &&
290 (!NextMI || !isFuncOrHeaderInstr(NextMI, TII))) {
291 assert(MI->getParent()->getNumber() == MF->front().getNumber() &&
292 "OpFunction is not in the front MBB of MF");
293 emitOpLabel(*MI->getParent());
294 }
295}
296
297void SPIRVAsmPrinter::outputModuleSection(SPIRV::ModuleSectionType MSType) {
298 for (const MachineInstr *MI : MAI->getMSInstrs(MSType))
299 outputInstruction(MI);
300}
301
302void SPIRVAsmPrinter::outputDebugSourceAndStrings(const Module &M) {
303 // Output OpSourceExtensions.
304 for (auto &Str : MAI->SrcExt) {
305 MCInst Inst;
306 Inst.setOpcode(SPIRV::OpSourceExtension);
307 addStringImm(Str.first(), Inst);
308 outputMCInst(Inst);
309 }
310 // Output OpString.
311 outputModuleSection(SPIRV::MB_DebugStrings);
312 // Output OpSource.
313 MCInst Inst;
314 Inst.setOpcode(SPIRV::OpSource);
315 Inst.addOperand(MCOperand::createImm(static_cast<unsigned>(MAI->SrcLang)));
316 Inst.addOperand(
317 MCOperand::createImm(static_cast<unsigned>(MAI->SrcLangVersion)));
318 outputMCInst(Inst);
319}
320
321void SPIRVAsmPrinter::outputOpExtInstImports(const Module &M) {
322 for (auto &CU : MAI->ExtInstSetMap) {
323 unsigned Set = CU.first;
324 MCRegister Reg = CU.second;
325 MCInst Inst;
326 Inst.setOpcode(SPIRV::OpExtInstImport);
329 static_cast<SPIRV::InstructionSet::InstructionSet>(Set)),
330 Inst);
331 outputMCInst(Inst);
332 }
333}
334
335void SPIRVAsmPrinter::outputOpMemoryModel() {
336 MCInst Inst;
337 Inst.setOpcode(SPIRV::OpMemoryModel);
338 Inst.addOperand(MCOperand::createImm(static_cast<unsigned>(MAI->Addr)));
339 Inst.addOperand(MCOperand::createImm(static_cast<unsigned>(MAI->Mem)));
340 outputMCInst(Inst);
341}
342
343// Before the OpEntryPoints' output, we need to add the entry point's
344// interfaces. The interface is a list of IDs of global OpVariable instructions.
345// These declare the set of global variables from a module that form
346// the interface of this entry point.
347void SPIRVAsmPrinter::outputEntryPoints() {
348 // Find all OpVariable IDs with required StorageClass.
349 DenseSet<MCRegister> InterfaceIDs;
350 for (const MachineInstr *MI : MAI->GlobalVarList) {
351 assert(MI->getOpcode() == SPIRV::OpVariable);
352 auto SC = static_cast<SPIRV::StorageClass::StorageClass>(
353 MI->getOperand(2).getImm());
354 // Before version 1.4, the interface's storage classes are limited to
355 // the Input and Output storage classes. Starting with version 1.4,
356 // the interface's storage classes are all storage classes used in
357 // declaring all global variables referenced by the entry point call tree.
358 if (ST->isAtLeastSPIRVVer(VersionTuple(1, 4)) ||
359 SC == SPIRV::StorageClass::Input || SC == SPIRV::StorageClass::Output) {
360 const MachineFunction *MF = MI->getMF();
361 MCRegister Reg = MAI->getRegisterAlias(MF, MI->getOperand(0).getReg());
362 InterfaceIDs.insert(Reg);
363 }
364 }
365
366 // Output OpEntryPoints adding interface args to all of them.
367 for (const MachineInstr *MI : MAI->getMSInstrs(SPIRV::MB_EntryPoints)) {
368 SPIRVMCInstLower MCInstLowering;
369 MCInst TmpInst;
370 MCInstLowering.lower(MI, TmpInst, MAI);
371 for (MCRegister Reg : InterfaceIDs) {
372 assert(Reg.isValid());
374 }
375 outputMCInst(TmpInst);
376 }
377}
378
379// Create global OpCapability instructions for the required capabilities.
380void SPIRVAsmPrinter::outputGlobalRequirements() {
381 // Abort here if not all requirements can be satisfied.
382 MAI->Reqs.checkSatisfiable(*ST);
383
384 for (const auto &Cap : MAI->Reqs.getMinimalCapabilities()) {
385 MCInst Inst;
386 Inst.setOpcode(SPIRV::OpCapability);
388 outputMCInst(Inst);
389 }
390
391 // Generate the final OpExtensions with strings instead of enums.
392 for (const auto &Ext : MAI->Reqs.getExtensions()) {
393 MCInst Inst;
394 Inst.setOpcode(SPIRV::OpExtension);
396 SPIRV::OperandCategory::ExtensionOperand, Ext),
397 Inst);
398 outputMCInst(Inst);
399 }
400 // TODO add a pseudo instr for version number.
401}
402
403void SPIRVAsmPrinter::outputExtFuncDecls() {
404 // Insert OpFunctionEnd after each declaration.
405 auto I = MAI->getMSInstrs(SPIRV::MB_ExtFuncDecls).begin(),
406 E = MAI->getMSInstrs(SPIRV::MB_ExtFuncDecls).end();
407 for (; I != E; ++I) {
408 outputInstruction(*I);
409 if ((I + 1) == E || (*(I + 1))->getOpcode() == SPIRV::OpFunction)
410 outputOpFunctionEnd();
411 }
412}
413
414// Encode LLVM type by SPIR-V execution mode VecTypeHint.
415static unsigned encodeVecTypeHint(Type *Ty) {
416 if (Ty->isHalfTy())
417 return 4;
418 if (Ty->isFloatTy())
419 return 5;
420 if (Ty->isDoubleTy())
421 return 6;
422 if (IntegerType *IntTy = dyn_cast<IntegerType>(Ty)) {
423 switch (IntTy->getIntegerBitWidth()) {
424 case 8:
425 return 0;
426 case 16:
427 return 1;
428 case 32:
429 return 2;
430 case 64:
431 return 3;
432 default:
433 llvm_unreachable("invalid integer type");
434 }
435 }
437 Type *EleTy = VecTy->getElementType();
438 unsigned Size = VecTy->getNumElements();
439 return Size << 16 | encodeVecTypeHint(EleTy);
440 }
441 llvm_unreachable("invalid type");
442}
443
444static void addOpsFromMDNode(MDNode *MDN, MCInst &Inst,
446 for (const MDOperand &MDOp : MDN->operands()) {
447 if (auto *CMeta = dyn_cast<ConstantAsMetadata>(MDOp)) {
448 Constant *C = CMeta->getValue();
449 if (ConstantInt *Const = dyn_cast<ConstantInt>(C)) {
450 Inst.addOperand(MCOperand::createImm(Const->getZExtValue()));
451 } else if (auto *CE = dyn_cast<Function>(C)) {
452 MCRegister FuncReg = MAI->getFuncReg(CE);
453 assert(FuncReg.isValid());
454 Inst.addOperand(MCOperand::createReg(FuncReg));
455 }
456 }
457 }
458}
459
460void SPIRVAsmPrinter::outputExecutionModeFromMDNode(
461 MCRegister Reg, MDNode *Node, SPIRV::ExecutionMode::ExecutionMode EM,
462 unsigned ExpectMDOps, int64_t DefVal) {
463 MCInst Inst;
464 Inst.setOpcode(SPIRV::OpExecutionMode);
466 Inst.addOperand(MCOperand::createImm(static_cast<unsigned>(EM)));
467 addOpsFromMDNode(Node, Inst, MAI);
468 // reqd_work_group_size and work_group_size_hint require 3 operands,
469 // if metadata contains less operands, just add a default value
470 unsigned NodeSz = Node->getNumOperands();
471 if (ExpectMDOps > 0 && NodeSz < ExpectMDOps)
472 for (unsigned i = NodeSz; i < ExpectMDOps; ++i)
473 Inst.addOperand(MCOperand::createImm(DefVal));
474 outputMCInst(Inst);
475}
476
477void SPIRVAsmPrinter::outputExecutionModeFromNumthreadsAttribute(
478 const MCRegister &Reg, const Attribute &Attr,
479 SPIRV::ExecutionMode::ExecutionMode EM) {
480 assert(Attr.isValid() && "Function called with an invalid attribute.");
481
482 MCInst Inst;
483 Inst.setOpcode(SPIRV::OpExecutionMode);
485 Inst.addOperand(MCOperand::createImm(static_cast<unsigned>(EM)));
486
487 SmallVector<StringRef> NumThreads;
488 Attr.getValueAsString().split(NumThreads, ',');
489 assert(NumThreads.size() == 3 && "invalid numthreads");
490 for (uint32_t i = 0; i < 3; ++i) {
491 uint32_t V;
492 [[maybe_unused]] bool Result = NumThreads[i].getAsInteger(10, V);
493 assert(!Result && "Failed to parse numthreads");
495 }
496
497 outputMCInst(Inst);
498}
499
500void SPIRVAsmPrinter::outputExecutionModeFromEnableMaximalReconvergenceAttr(
501 const MCRegister &Reg, const SPIRVSubtarget &ST) {
502 assert(ST.canUseExtension(SPIRV::Extension::SPV_KHR_maximal_reconvergence) &&
503 "Function called when SPV_KHR_maximal_reconvergence is not enabled.");
504
505 MCInst Inst;
506 Inst.setOpcode(SPIRV::OpExecutionMode);
508 unsigned EM =
509 static_cast<unsigned>(SPIRV::ExecutionMode::MaximallyReconvergesKHR);
511 outputMCInst(Inst);
512}
513
514void SPIRVAsmPrinter::outputExecutionMode(const Module &M) {
515 NamedMDNode *Node = M.getNamedMetadata("spirv.ExecutionMode");
516 if (Node) {
517 for (unsigned i = 0; i < Node->getNumOperands(); i++) {
518 // If SPV_KHR_float_controls2 is enabled and we find any of
519 // FPFastMathDefault, ContractionOff or SignedZeroInfNanPreserve execution
520 // modes, skip it, it'll be done somewhere else.
521 if (ST->canUseExtension(SPIRV::Extension::SPV_KHR_float_controls2)) {
522 const auto EM =
524 cast<ConstantAsMetadata>((Node->getOperand(i))->getOperand(1))
525 ->getValue())
526 ->getZExtValue();
527 if (EM == SPIRV::ExecutionMode::FPFastMathDefault ||
528 EM == SPIRV::ExecutionMode::ContractionOff ||
529 EM == SPIRV::ExecutionMode::SignedZeroInfNanPreserve)
530 continue;
531 }
532
533 MCInst Inst;
534 Inst.setOpcode(SPIRV::OpExecutionMode);
535 addOpsFromMDNode(cast<MDNode>(Node->getOperand(i)), Inst, MAI);
536 outputMCInst(Inst);
537 }
538 outputFPFastMathDefaultInfo();
539 }
540 for (auto FI = M.begin(), E = M.end(); FI != E; ++FI) {
541 const Function &F = *FI;
542 // Only operands of OpEntryPoint instructions are allowed to be
543 // <Entry Point> operands of OpExecutionMode
544 if (F.isDeclaration() || !isEntryPoint(F))
545 continue;
546 MCRegister FReg = MAI->getFuncReg(&F);
547 assert(FReg.isValid());
548
549 if (Attribute Attr = F.getFnAttribute("hlsl.shader"); Attr.isValid()) {
550 // SPIR-V common validation: Fragment requires OriginUpperLeft or
551 // OriginLowerLeft.
552 // VUID-StandaloneSpirv-OriginLowerLeft-04653: Fragment must declare
553 // OriginUpperLeft.
554 if (Attr.getValueAsString() == "pixel") {
555 MCInst Inst;
556 Inst.setOpcode(SPIRV::OpExecutionMode);
558 unsigned EM =
559 static_cast<unsigned>(SPIRV::ExecutionMode::OriginUpperLeft);
561 outputMCInst(Inst);
562 }
563 }
564 if (MDNode *Node = F.getMetadata("reqd_work_group_size"))
565 outputExecutionModeFromMDNode(FReg, Node, SPIRV::ExecutionMode::LocalSize,
566 3, 1);
567 if (Attribute Attr = F.getFnAttribute("hlsl.numthreads"); Attr.isValid())
568 outputExecutionModeFromNumthreadsAttribute(
569 FReg, Attr, SPIRV::ExecutionMode::LocalSize);
570 if (Attribute Attr = F.getFnAttribute("enable-maximal-reconvergence");
571 Attr.getValueAsBool()) {
572 outputExecutionModeFromEnableMaximalReconvergenceAttr(FReg, *ST);
573 }
574 if (MDNode *Node = F.getMetadata("work_group_size_hint"))
575 outputExecutionModeFromMDNode(FReg, Node,
576 SPIRV::ExecutionMode::LocalSizeHint, 3, 1);
577 if (MDNode *Node = F.getMetadata("intel_reqd_sub_group_size"))
578 outputExecutionModeFromMDNode(FReg, Node,
579 SPIRV::ExecutionMode::SubgroupSize, 0, 0);
580 if (MDNode *Node = F.getMetadata("vec_type_hint")) {
581 MCInst Inst;
582 Inst.setOpcode(SPIRV::OpExecutionMode);
584 unsigned EM = static_cast<unsigned>(SPIRV::ExecutionMode::VecTypeHint);
586 unsigned TypeCode = encodeVecTypeHint(getMDOperandAsType(Node, 0));
587 Inst.addOperand(MCOperand::createImm(TypeCode));
588 outputMCInst(Inst);
589 }
590 if (ST->isKernel() && !M.getNamedMetadata("spirv.ExecutionMode") &&
591 !M.getNamedMetadata("opencl.enable.FP_CONTRACT")) {
592 if (ST->canUseExtension(SPIRV::Extension::SPV_KHR_float_controls2)) {
593 // When SPV_KHR_float_controls2 is enabled, ContractionOff is
594 // deprecated. We need to use FPFastMathDefault with the appropriate
595 // flags instead. Since FPFastMathDefault takes a target type, we need
596 // to emit it for each floating-point type that exists in the module
597 // to match the effect of ContractionOff. As of now, there are 3 FP
598 // types: fp16, fp32 and fp64.
599
600 // We only end up here because there is no "spirv.ExecutionMode"
601 // metadata, so that means no FPFastMathDefault. Therefore, we only
602 // need to make sure AllowContract is set to 0, as the rest of flags.
603 // We still need to emit the OpExecutionMode instruction, otherwise
604 // it's up to the client API to define the flags. Therefore, we need
605 // to find the constant with 0 value.
606
607 // Collect the SPIRVTypes for fp16, fp32, and fp64 and the constant of
608 // type int32 with 0 value to represent the FP Fast Math Mode.
609 std::vector<const MachineInstr *> SPIRVFloatTypes;
610 const MachineInstr *ConstZero = nullptr;
611 for (const MachineInstr *MI :
612 MAI->getMSInstrs(SPIRV::MB_TypeConstVars)) {
613 // Skip if the instruction is not OpTypeFloat or OpConstant.
614 unsigned OpCode = MI->getOpcode();
615 if (OpCode != SPIRV::OpTypeFloat && OpCode != SPIRV::OpConstantNull)
616 continue;
617
618 // Collect the SPIRV type if it's a float.
619 if (OpCode == SPIRV::OpTypeFloat) {
620 // Skip if the target type is not fp16, fp32, fp64.
621 const unsigned OpTypeFloatSize = MI->getOperand(1).getImm();
622 if (OpTypeFloatSize != 16 && OpTypeFloatSize != 32 &&
623 OpTypeFloatSize != 64) {
624 continue;
625 }
626 SPIRVFloatTypes.push_back(MI);
627 } else {
628 // Check if the constant is int32, if not skip it.
629 const MachineRegisterInfo &MRI = MI->getMF()->getRegInfo();
630 MachineInstr *TypeMI = MRI.getVRegDef(MI->getOperand(1).getReg());
631 if (!TypeMI || TypeMI->getOperand(1).getImm() != 32)
632 continue;
633
634 ConstZero = MI;
635 }
636 }
637
638 // When SPV_KHR_float_controls2 is enabled, ContractionOff is
639 // deprecated. We need to use FPFastMathDefault with the appropriate
640 // flags instead. Since FPFastMathDefault takes a target type, we need
641 // to emit it for each floating-point type that exists in the module
642 // to match the effect of ContractionOff. As of now, there are 3 FP
643 // types: fp16, fp32 and fp64.
644 for (const MachineInstr *MI : SPIRVFloatTypes) {
645 MCInst Inst;
646 Inst.setOpcode(SPIRV::OpExecutionModeId);
648 unsigned EM =
649 static_cast<unsigned>(SPIRV::ExecutionMode::FPFastMathDefault);
651 const MachineFunction *MF = MI->getMF();
652 MCRegister TypeReg =
653 MAI->getRegisterAlias(MF, MI->getOperand(0).getReg());
654 Inst.addOperand(MCOperand::createReg(TypeReg));
655 assert(ConstZero && "There should be a constant zero.");
656 MCRegister ConstReg = MAI->getRegisterAlias(
657 ConstZero->getMF(), ConstZero->getOperand(0).getReg());
658 Inst.addOperand(MCOperand::createReg(ConstReg));
659 outputMCInst(Inst);
660 }
661 } else {
662 MCInst Inst;
663 Inst.setOpcode(SPIRV::OpExecutionMode);
665 unsigned EM =
666 static_cast<unsigned>(SPIRV::ExecutionMode::ContractionOff);
668 outputMCInst(Inst);
669 }
670 }
671 }
672}
673
674void SPIRVAsmPrinter::outputAnnotations(const Module &M) {
675 outputModuleSection(SPIRV::MB_Annotations);
676 // Process llvm.global.annotations special global variable.
677 for (auto F = M.global_begin(), E = M.global_end(); F != E; ++F) {
678 if ((*F).getName() != "llvm.global.annotations")
679 continue;
680 const GlobalVariable *V = &(*F);
681 const ConstantArray *CA = cast<ConstantArray>(V->getOperand(0));
682 for (Value *Op : CA->operands()) {
683 ConstantStruct *CS = cast<ConstantStruct>(Op);
684 // The first field of the struct contains a pointer to
685 // the annotated variable.
686 Value *AnnotatedVar = CS->getOperand(0)->stripPointerCasts();
687 if (!isa<Function>(AnnotatedVar))
688 report_fatal_error("Unsupported value in llvm.global.annotations");
689 Function *Func = cast<Function>(AnnotatedVar);
690 MCRegister Reg = MAI->getFuncReg(Func);
691 if (!Reg.isValid()) {
692 std::string DiagMsg;
693 raw_string_ostream OS(DiagMsg);
694 AnnotatedVar->print(OS);
695 DiagMsg = "Unknown function in llvm.global.annotations: " + DiagMsg;
696 report_fatal_error(DiagMsg.c_str());
697 }
698
699 // The second field contains a pointer to a global annotation string.
700 GlobalVariable *GV =
702
703 StringRef AnnotationString;
704 [[maybe_unused]] bool Success =
705 getConstantStringInfo(GV, AnnotationString);
706 assert(Success && "Failed to get annotation string");
707 MCInst Inst;
708 Inst.setOpcode(SPIRV::OpDecorate);
710 unsigned Dec = static_cast<unsigned>(SPIRV::Decoration::UserSemantic);
712 addStringImm(AnnotationString, Inst);
713 outputMCInst(Inst);
714 }
715 }
716}
717
718void SPIRVAsmPrinter::outputFPFastMathDefaultInfo() {
719 // Collect the SPIRVTypes that are OpTypeFloat and the constants of type
720 // int32, that might be used as FP Fast Math Mode.
721 std::vector<const MachineInstr *> SPIRVFloatTypes;
722 // Hashtable to associate immediate values with the constant holding them.
723 std::unordered_map<int, const MachineInstr *> ConstMap;
724 for (const MachineInstr *MI : MAI->getMSInstrs(SPIRV::MB_TypeConstVars)) {
725 // Skip if the instruction is not OpTypeFloat or OpConstant.
726 unsigned OpCode = MI->getOpcode();
727 if (OpCode != SPIRV::OpTypeFloat && OpCode != SPIRV::OpConstantI &&
728 OpCode != SPIRV::OpConstantNull)
729 continue;
730
731 // Collect the SPIRV type if it's a float.
732 if (OpCode == SPIRV::OpTypeFloat) {
733 SPIRVFloatTypes.push_back(MI);
734 } else {
735 // Check if the constant is int32, if not skip it.
736 const MachineRegisterInfo &MRI = MI->getMF()->getRegInfo();
737 MachineInstr *TypeMI = MRI.getVRegDef(MI->getOperand(1).getReg());
738 if (!TypeMI || TypeMI->getOpcode() != SPIRV::OpTypeInt ||
739 TypeMI->getOperand(1).getImm() != 32)
740 continue;
741
742 if (OpCode == SPIRV::OpConstantI)
743 ConstMap[MI->getOperand(2).getImm()] = MI;
744 else
745 ConstMap[0] = MI;
746 }
747 }
748
749 for (const auto &[Func, FPFastMathDefaultInfoVec] :
750 MAI->FPFastMathDefaultInfoMap) {
751 if (FPFastMathDefaultInfoVec.empty())
752 continue;
753
754 for (const MachineInstr *MI : SPIRVFloatTypes) {
755 unsigned OpTypeFloatSize = MI->getOperand(1).getImm();
758 assert(Index < FPFastMathDefaultInfoVec.size() &&
759 "Index out of bounds for FPFastMathDefaultInfoVec");
760 const auto &FPFastMathDefaultInfo = FPFastMathDefaultInfoVec[Index];
761 assert(FPFastMathDefaultInfo.Ty &&
762 "Expected target type for FPFastMathDefaultInfo");
763 assert(FPFastMathDefaultInfo.Ty->getScalarSizeInBits() ==
764 OpTypeFloatSize &&
765 "Mismatched float type size");
766 MCInst Inst;
767 Inst.setOpcode(SPIRV::OpExecutionModeId);
768 MCRegister FuncReg = MAI->getFuncReg(Func);
769 assert(FuncReg.isValid());
770 Inst.addOperand(MCOperand::createReg(FuncReg));
771 Inst.addOperand(
772 MCOperand::createImm(SPIRV::ExecutionMode::FPFastMathDefault));
773 MCRegister TypeReg =
774 MAI->getRegisterAlias(MI->getMF(), MI->getOperand(0).getReg());
775 Inst.addOperand(MCOperand::createReg(TypeReg));
776 unsigned Flags = FPFastMathDefaultInfo.FastMathFlags;
777 if (FPFastMathDefaultInfo.ContractionOff &&
778 (Flags & SPIRV::FPFastMathMode::AllowContract))
780 "Conflicting FPFastMathFlags: ContractionOff and AllowContract");
781
782 if (FPFastMathDefaultInfo.SignedZeroInfNanPreserve &&
783 !(Flags &
784 (SPIRV::FPFastMathMode::NotNaN | SPIRV::FPFastMathMode::NotInf |
785 SPIRV::FPFastMathMode::NSZ))) {
786 if (FPFastMathDefaultInfo.FPFastMathDefault)
787 report_fatal_error("Conflicting FPFastMathFlags: "
788 "SignedZeroInfNanPreserve but at least one of "
789 "NotNaN/NotInf/NSZ is enabled.");
790 }
791
792 // Don't emit if none of the execution modes was used.
793 if (Flags == SPIRV::FPFastMathMode::None &&
794 !FPFastMathDefaultInfo.ContractionOff &&
795 !FPFastMathDefaultInfo.SignedZeroInfNanPreserve &&
796 !FPFastMathDefaultInfo.FPFastMathDefault)
797 continue;
798
799 // Retrieve the constant instruction for the immediate value.
800 auto It = ConstMap.find(Flags);
801 if (It == ConstMap.end())
802 report_fatal_error("Expected constant instruction for FP Fast Math "
803 "Mode operand of FPFastMathDefault execution mode.");
804 const MachineInstr *ConstMI = It->second;
805 MCRegister ConstReg = MAI->getRegisterAlias(
806 ConstMI->getMF(), ConstMI->getOperand(0).getReg());
807 Inst.addOperand(MCOperand::createReg(ConstReg));
808 outputMCInst(Inst);
809 }
810 }
811}
812
813void SPIRVAsmPrinter::outputModuleSections() {
814 const Module *M = MMI->getModule();
815 // Get the global subtarget to output module-level info.
816 ST = static_cast<const SPIRVTargetMachine &>(TM).getSubtargetImpl();
817 TII = ST->getInstrInfo();
819 assert(ST && TII && MAI && M && "Module analysis is required");
820 // Output instructions according to the Logical Layout of a Module:
821 // 1,2. All OpCapability instructions, then optional OpExtension
822 // instructions.
823 outputGlobalRequirements();
824 // 3. Optional OpExtInstImport instructions.
825 outputOpExtInstImports(*M);
826 // 4. The single required OpMemoryModel instruction.
827 outputOpMemoryModel();
828 // 5. All entry point declarations, using OpEntryPoint.
829 outputEntryPoints();
830 // 6. Execution-mode declarations, using OpExecutionMode or
831 // OpExecutionModeId.
832 outputExecutionMode(*M);
833 // 7a. Debug: all OpString, OpSourceExtension, OpSource, and
834 // OpSourceContinued, without forward references.
835 outputDebugSourceAndStrings(*M);
836 // 7b. Debug: all OpName and all OpMemberName.
837 outputModuleSection(SPIRV::MB_DebugNames);
838 // 7c. Debug: all OpModuleProcessed instructions.
839 outputModuleSection(SPIRV::MB_DebugModuleProcessed);
840 // xxx. SPV_INTEL_memory_access_aliasing instructions go before 8.
841 // "All annotation instructions"
842 outputModuleSection(SPIRV::MB_AliasingInsts);
843 // 8. All annotation instructions (all decorations).
844 outputAnnotations(*M);
845 // 9. All type declarations (OpTypeXXX instructions), all constant
846 // instructions, and all global variable declarations. This section is
847 // the first section to allow use of: OpLine and OpNoLine debug information;
848 // non-semantic instructions with OpExtInst.
849 outputModuleSection(SPIRV::MB_TypeConstVars);
850 // 10. All global NonSemantic.Shader.DebugInfo.100 instructions.
851 outputModuleSection(SPIRV::MB_NonSemanticGlobalDI);
852 // 11. All function declarations (functions without a body).
853 outputExtFuncDecls();
854 // 12. All function definitions (functions with a body).
855 // This is done in regular function output.
856}
857
858bool SPIRVAsmPrinter::doInitialization(Module &M) {
859 ModuleSectionsEmitted = false;
860 // We need to call the parent's one explicitly.
862}
863
864char SPIRVAsmPrinter::ID = 0;
865
866INITIALIZE_PASS(SPIRVAsmPrinter, "spirv-asm-printer", "SPIRV Assembly Printer",
867 false, false)
868
869// Force static initialization.
871LLVMInitializeSPIRVAsmPrinter() {
875}
unsigned const MachineRegisterInfo * MRI
#define Success
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
MachineBasicBlock & MBB
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
#define LLVM_ABI
Definition Compiler.h:213
#define LLVM_EXTERNAL_VISIBILITY
Definition Compiler.h:132
This file defines the DenseMap class.
const HexagonInstrInfo * TII
IRTranslator LLVM IR MI
#define F(x, y, z)
Definition MD5.cpp:55
#define I(x, y, z)
Definition MD5.cpp:58
Machine Check Debug Module
This file declares the MachineConstantPool class which is an abstract constant pool to keep track of ...
Register Reg
#define INITIALIZE_PASS(passName, arg, name, cfg, analysis)
Definition PassSupport.h:56
static void addOpsFromMDNode(MDNode *MDN, MCInst &Inst, SPIRV::ModuleAnalysisInfo *MAI)
static bool isFuncOrHeaderInstr(const MachineInstr *MI, const SPIRVInstrInfo *TII)
static unsigned encodeVecTypeHint(Type *Ty)
#define SPIRV_BACKEND_SERVICE_FUN_NAME
Definition SPIRVUtils.h:503
static bool printOperand(raw_ostream &OS, const SelectionDAG *G, const SDValue Value)
static TableGen::Emitter::Opt Y("gen-skeleton-entry", EmitSkeleton, "Generate example skeleton entry")
static TableGen::Emitter::OptClass< SkeletonEmitter > X("gen-skeleton-class", "Generate example skeleton class")
static std::optional< unsigned > getOpcode(ArrayRef< VPValue * > Values)
Returns the opcode of Values or ~0 if they do not all agree.
Definition VPlanSLP.cpp:247
Represent the analysis usage information of a pass.
AnalysisUsage & addRequired()
AnalysisUsage & addPreserved()
Add the specified Pass class to the set of analyses preserved by this pass.
This class is intended to be used as a driving class for all asm writers.
Definition AsmPrinter.h:96
bool doInitialization(Module &M) override
Set up the AsmPrinter when we are working on a new module.
void getAnalysisUsage(AnalysisUsage &AU) const override
Record analysis usage.
Functions, function parameters, and return types can have attributes to indicate how they should be t...
Definition Attributes.h:69
LLVM_ABI bool getValueAsBool() const
Return the attribute's value as a boolean.
LLVM_ABI StringRef getValueAsString() const
Return the attribute's value as a string.
bool isValid() const
Return true if the attribute is any kind of attribute.
Definition Attributes.h:223
This is the shared class of boolean and integer constants.
Definition Constants.h:87
This is an important base class in LLVM.
Definition Constant.h:43
iterator find(const_arg_type_t< KeyT > Val)
Definition DenseMap.h:167
iterator end()
Definition DenseMap.h:81
Class to represent fixed width SIMD vectors.
static StringRef dropLLVMManglingEscape(StringRef Name)
If the given string begins with the GlobalValue name mangling escape character '\1',...
Class to represent integer types.
Instances of this class represent a single low-level machine instruction.
Definition MCInst.h:188
void addOperand(const MCOperand Op)
Definition MCInst.h:215
void setOpcode(unsigned Op)
Definition MCInst.h:201
static MCOperand createReg(MCRegister Reg)
Definition MCInst.h:138
static MCOperand createImm(int64_t Val)
Definition MCInst.h:145
Wrapper class representing physical registers. Should be passed by value.
Definition MCRegister.h:33
constexpr bool isValid() const
Definition MCRegister.h:76
StringRef getName() const
getName - Get the symbol name.
Definition MCSymbol.h:188
Metadata node.
Definition Metadata.h:1078
ArrayRef< MDOperand > operands() const
Definition Metadata.h:1440
Tracking metadata reference owned by Metadata.
Definition Metadata.h:900
LLVM_ABI MCSymbol * getSymbol() const
Return the MCSymbol for this basic block.
int getNumber() const
MachineBasicBlocks are uniquely numbered at the function level, unless they're not in a MachineFuncti...
Representation of each machine instruction.
unsigned getOpcode() const
Returns the opcode of this MachineInstr.
LLVM_ABI const MachineFunction * getMF() const
Return the function that contains the basic block that this instruction belongs to.
const MachineOperand & getOperand(unsigned i) const
const GlobalValue * getGlobal() const
int64_t getImm() const
MachineBasicBlock * getMBB() const
const BlockAddress * getBlockAddress() const
MachineOperandType getType() const
getType - Returns the MachineOperandType for this operand.
const char * getSymbolName() const
Register getReg() const
getReg - Returns the register number.
const ConstantFP * getFPImm() const
@ MO_Immediate
Immediate operand.
@ MO_ConstantPoolIndex
Address of indexed Constant in Constant Pool.
@ MO_GlobalAddress
Address of a global value.
@ MO_BlockAddress
Address of a basic block.
@ MO_MachineBasicBlock
MachineBasicBlock reference.
@ MO_Register
Register operand.
@ MO_ExternalSymbol
Name of external global symbol.
@ MO_JumpTableIndex
Address of indexed Jump Table for switch.
@ MO_FPImmediate
Floating-point immediate operand.
A Module instance is used to store all the information related to an LLVM module.
Definition Module.h:67
constexpr bool isValid() const
Definition Register.h:107
static const char * getRegisterName(MCRegister Reg)
void lower(const MachineInstr *MI, MCInst &OutMI, SPIRV::ModuleAnalysisInfo *MAI) const
const SPIRVInstrInfo * getInstrInfo() const override
bool isAtLeastSPIRVVer(VersionTuple VerToCompareTo) const
VersionTuple getSPIRVVersion() const
unsigned getBound() const
std::pair< iterator, bool > insert(PtrType Ptr)
Inserts Ptr if and only if there is no element in the container equal to Ptr.
bool contains(ConstPtrType Ptr) const
SmallPtrSet - This class implements a set which is optimized for holding SmallSize or less elements.
StringRef - Represent a constant reference to a string, i.e.
Definition StringRef.h:55
std::pair< StringRef, StringRef > split(char Separator) const
Split into two substrings around the first occurrence of a separator character.
Definition StringRef.h:702
Primary interface to the complete machine description for the target machine.
The instances of the Type class are immutable: once they are created, they are never changed.
Definition Type.h:45
op_range operands()
Definition User.h:292
Value * getOperand(unsigned i) const
Definition User.h:232
LLVM_ABI void print(raw_ostream &O, bool IsForDebug=false) const
Implement operator<< on Value.
LLVM_ABI const Value * stripPointerCasts() const
Strip off pointer casts, all-zero GEPs and address space casts.
Definition Value.cpp:701
unsigned getMajor() const
Retrieve the major version number.
std::optional< unsigned > getMinor() const
Retrieve the minor version number, if provided.
std::pair< iterator, bool > insert(const ValueT &V)
Definition DenseSet.h:202
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition raw_ostream.h:53
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
Definition CallingConv.h:24
@ C
The default llvm calling convention, compatible with C.
Definition CallingConv.h:34
NodeAddr< NodeBase * > Node
Definition RDFGraph.h:381
NodeAddr< FuncNode * > Func
Definition RDFGraph.h:393
This is an optimization pass for GlobalISel generic memory operations.
FunctionAddr VTableAddr Value
Definition InstrProf.h:137
Target & getTheSPIRV32Target()
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:644
DenseMap< Value *, Constant * > ConstMap
LLVM_ABI bool getConstantStringInfo(const Value *V, StringRef &Str, bool TrimAtNul=true)
This function computes the length of a null-terminated C string pointed to by V.
std::string getExtInstSetName(SPIRV::InstructionSet::InstructionSet Set)
MachineInstr * getImm(const MachineOperand &MO, const MachineRegisterInfo *MRI)
LLVM_ABI void report_fatal_error(Error Err, bool gen_crash_diag=true)
Definition Error.cpp:167
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
Definition Casting.h:548
std::string getSymbolicOperandMnemonic(SPIRV::OperandCategory::OperandCategory Category, int32_t Value)
bool isEntryPoint(const Function &F)
Target & getTheSPIRV64Target()
Target & getTheSPIRVLogicalTarget()
DWARFExpression::Operation Op
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:560
Type * getMDOperandAsType(const MDNode *N, unsigned I)
void addStringImm(const StringRef &Str, MCInst &Inst)
RegisterAsmPrinter - Helper template for registering a target specific assembly printer,...
static struct SPIRV::ModuleAnalysisInfo MAI
static size_t computeFPFastMathDefaultInfoVecIndex(size_t BitWidth)
Definition SPIRVUtils.h:146
MCRegister getFuncReg(const Function *F)