Thanks to visit codestin.com
Credit goes to llvm.org

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 outputExecutionMode(const Module &M);
82 void outputAnnotations(const Module &M);
83 void outputModuleSections();
84 void outputFPFastMathDefaultInfo();
85 bool isHidden() {
86 return MF->getFunction()
87 .getFnAttribute(SPIRV_BACKEND_SERVICE_FUN_NAME)
88 .isValid();
89 }
90
91 void emitInstruction(const MachineInstr *MI) override;
92 void emitFunctionEntryLabel() override {}
93 void emitFunctionHeader() override;
94 void emitFunctionBodyStart() override {}
95 void emitFunctionBodyEnd() override;
96 void emitBasicBlockStart(const MachineBasicBlock &MBB) override;
97 void emitBasicBlockEnd(const MachineBasicBlock &MBB) override {}
98 void emitGlobalVariable(const GlobalVariable *GV) override {}
99 void emitOpLabel(const MachineBasicBlock &MBB);
100 void emitEndOfAsmFile(Module &M) override;
101 bool doInitialization(Module &M) override;
102
103 void getAnalysisUsage(AnalysisUsage &AU) const override;
105
106protected:
107 void cleanUp(Module &M);
108};
109} // namespace
110
111void SPIRVAsmPrinter::getAnalysisUsage(AnalysisUsage &AU) const {
112 AU.addRequired<SPIRVModuleAnalysis>();
113 AU.addPreserved<SPIRVModuleAnalysis>();
115}
116
117// If the module has no functions, we need output global info anyway.
118void SPIRVAsmPrinter::emitEndOfAsmFile(Module &M) {
119 if (ModuleSectionsEmitted == false) {
120 outputModuleSections();
121 ModuleSectionsEmitted = true;
122 }
123
124 ST = static_cast<const SPIRVTargetMachine &>(TM).getSubtargetImpl();
125 VersionTuple SPIRVVersion = ST->getSPIRVVersion();
126 uint32_t Major = SPIRVVersion.getMajor();
127 uint32_t Minor = SPIRVVersion.getMinor().value_or(0);
128 // Bound is an approximation that accounts for the maximum used register
129 // number and number of generated OpLabels
130 unsigned Bound = 2 * (ST->getBound() + 1) + NLabels;
131 if (MCAssembler *Asm = OutStreamer->getAssemblerPtr())
132 static_cast<SPIRVObjectWriter &>(Asm->getWriter())
133 .setBuildVersion(Major, Minor, Bound);
134
135 cleanUp(M);
136}
137
138// Any cleanup actions with the Module after we don't care about its content
139// anymore.
140void SPIRVAsmPrinter::cleanUp(Module &M) {
141 // Verifier disallows uses of intrinsic global variables.
142 for (StringRef GVName : {"llvm.global_ctors", "llvm.global_dtors",
143 "llvm.used", "llvm.compiler.used"}) {
144 if (GlobalVariable *GV = M.getNamedGlobal(GVName))
145 GV->setName("");
146 }
147}
148
149void SPIRVAsmPrinter::emitFunctionHeader() {
150 if (ModuleSectionsEmitted == false) {
151 outputModuleSections();
152 ModuleSectionsEmitted = true;
153 }
154 // Get the subtarget from the current MachineFunction.
155 ST = &MF->getSubtarget<SPIRVSubtarget>();
156 TII = ST->getInstrInfo();
157 const Function &F = MF->getFunction();
158
159 if (isVerbose() && !isHidden()) {
160 OutStreamer->getCommentOS()
161 << "-- Begin function "
162 << GlobalValue::dropLLVMManglingEscape(F.getName()) << '\n';
163 }
164
165 auto Section = getObjFileLowering().SectionForGlobal(&F, TM);
166 MF->setSection(Section);
167}
168
169void SPIRVAsmPrinter::outputOpFunctionEnd() {
170 MCInst FunctionEndInst;
171 FunctionEndInst.setOpcode(SPIRV::OpFunctionEnd);
172 outputMCInst(FunctionEndInst);
173}
174
175void SPIRVAsmPrinter::emitFunctionBodyEnd() {
176 if (!isHidden())
177 outputOpFunctionEnd();
178}
179
180void SPIRVAsmPrinter::emitOpLabel(const MachineBasicBlock &MBB) {
181 // Do not emit anything if it's an internal service function.
182 if (isHidden())
183 return;
184
185 MCInst LabelInst;
186 LabelInst.setOpcode(SPIRV::OpLabel);
187 LabelInst.addOperand(MCOperand::createReg(MAI->getOrCreateMBBRegister(MBB)));
188 outputMCInst(LabelInst);
189 ++NLabels;
190 LabeledMBB.insert(&MBB);
191}
192
193void SPIRVAsmPrinter::emitBasicBlockStart(const MachineBasicBlock &MBB) {
194 // Do not emit anything if it's an internal service function.
195 if (MBB.empty())
196 return;
197
198 // If it's the first MBB in MF, it has OpFunction and OpFunctionParameter, so
199 // OpLabel should be output after them.
200 if (MBB.getNumber() == MF->front().getNumber()) {
201 for (const MachineInstr &MI : MBB)
202 if (MI.getOpcode() == SPIRV::OpFunction)
203 return;
204 // TODO: this case should be checked by the verifier.
205 report_fatal_error("OpFunction is expected in the front MBB of MF");
206 }
207 emitOpLabel(MBB);
208}
209
210void SPIRVAsmPrinter::printOperand(const MachineInstr *MI, int OpNum,
211 raw_ostream &O) {
212 const MachineOperand &MO = MI->getOperand(OpNum);
213
214 switch (MO.getType()) {
217 break;
218
220 O << MO.getImm();
221 break;
222
224 O << MO.getFPImm();
225 break;
226
228 O << *MO.getMBB()->getSymbol();
229 break;
230
232 O << *getSymbol(MO.getGlobal());
233 break;
234
236 MCSymbol *BA = GetBlockAddressSymbol(MO.getBlockAddress());
237 O << BA->getName();
238 break;
239 }
240
242 O << *GetExternalSymbolSymbol(MO.getSymbolName());
243 break;
244
247 default:
248 llvm_unreachable("<unknown operand type>");
249 }
250}
251
252bool SPIRVAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
253 const char *ExtraCode, raw_ostream &O) {
254 if (ExtraCode && ExtraCode[0])
255 return true; // Invalid instruction - SPIR-V does not have special modifiers
256
257 printOperand(MI, OpNo, O);
258 return false;
259}
260
262 const SPIRVInstrInfo *TII) {
263 return TII->isHeaderInstr(*MI) || MI->getOpcode() == SPIRV::OpFunction ||
264 MI->getOpcode() == SPIRV::OpFunctionParameter;
265}
266
267void SPIRVAsmPrinter::outputMCInst(MCInst &Inst) {
268 OutStreamer->emitInstruction(Inst, *OutContext.getSubtargetInfo());
269}
270
271void SPIRVAsmPrinter::outputInstruction(const MachineInstr *MI) {
272 SPIRVMCInstLower MCInstLowering;
273 MCInst TmpInst;
274 MCInstLowering.lower(MI, TmpInst, MAI);
275 outputMCInst(TmpInst);
276}
277
278void SPIRVAsmPrinter::emitInstruction(const MachineInstr *MI) {
279 SPIRV_MC::verifyInstructionPredicates(MI->getOpcode(),
280 getSubtargetInfo().getFeatureBits());
281
282 if (!MAI->getSkipEmission(MI))
283 outputInstruction(MI);
284
285 // Output OpLabel after OpFunction and OpFunctionParameter in the first MBB.
286 const MachineInstr *NextMI = MI->getNextNode();
287 if (!LabeledMBB.contains(MI->getParent()) && isFuncOrHeaderInstr(MI, TII) &&
288 (!NextMI || !isFuncOrHeaderInstr(NextMI, TII))) {
289 assert(MI->getParent()->getNumber() == MF->front().getNumber() &&
290 "OpFunction is not in the front MBB of MF");
291 emitOpLabel(*MI->getParent());
292 }
293}
294
295void SPIRVAsmPrinter::outputModuleSection(SPIRV::ModuleSectionType MSType) {
296 for (const MachineInstr *MI : MAI->getMSInstrs(MSType))
297 outputInstruction(MI);
298}
299
300void SPIRVAsmPrinter::outputDebugSourceAndStrings(const Module &M) {
301 // Output OpSourceExtensions.
302 for (auto &Str : MAI->SrcExt) {
303 MCInst Inst;
304 Inst.setOpcode(SPIRV::OpSourceExtension);
305 addStringImm(Str.first(), Inst);
306 outputMCInst(Inst);
307 }
308 // Output OpString.
309 outputModuleSection(SPIRV::MB_DebugStrings);
310 // Output OpSource.
311 MCInst Inst;
312 Inst.setOpcode(SPIRV::OpSource);
313 Inst.addOperand(MCOperand::createImm(static_cast<unsigned>(MAI->SrcLang)));
314 Inst.addOperand(
315 MCOperand::createImm(static_cast<unsigned>(MAI->SrcLangVersion)));
316 outputMCInst(Inst);
317}
318
319void SPIRVAsmPrinter::outputOpExtInstImports(const Module &M) {
320 for (auto &CU : MAI->ExtInstSetMap) {
321 unsigned Set = CU.first;
322 MCRegister Reg = CU.second;
323 MCInst Inst;
324 Inst.setOpcode(SPIRV::OpExtInstImport);
327 static_cast<SPIRV::InstructionSet::InstructionSet>(Set)),
328 Inst);
329 outputMCInst(Inst);
330 }
331}
332
333void SPIRVAsmPrinter::outputOpMemoryModel() {
334 MCInst Inst;
335 Inst.setOpcode(SPIRV::OpMemoryModel);
336 Inst.addOperand(MCOperand::createImm(static_cast<unsigned>(MAI->Addr)));
337 Inst.addOperand(MCOperand::createImm(static_cast<unsigned>(MAI->Mem)));
338 outputMCInst(Inst);
339}
340
341// Before the OpEntryPoints' output, we need to add the entry point's
342// interfaces. The interface is a list of IDs of global OpVariable instructions.
343// These declare the set of global variables from a module that form
344// the interface of this entry point.
345void SPIRVAsmPrinter::outputEntryPoints() {
346 // Find all OpVariable IDs with required StorageClass.
347 DenseSet<MCRegister> InterfaceIDs;
348 for (const MachineInstr *MI : MAI->GlobalVarList) {
349 assert(MI->getOpcode() == SPIRV::OpVariable);
350 auto SC = static_cast<SPIRV::StorageClass::StorageClass>(
351 MI->getOperand(2).getImm());
352 // Before version 1.4, the interface's storage classes are limited to
353 // the Input and Output storage classes. Starting with version 1.4,
354 // the interface's storage classes are all storage classes used in
355 // declaring all global variables referenced by the entry point call tree.
356 if (ST->isAtLeastSPIRVVer(VersionTuple(1, 4)) ||
357 SC == SPIRV::StorageClass::Input || SC == SPIRV::StorageClass::Output) {
358 const MachineFunction *MF = MI->getMF();
359 MCRegister Reg = MAI->getRegisterAlias(MF, MI->getOperand(0).getReg());
360 InterfaceIDs.insert(Reg);
361 }
362 }
363
364 // Output OpEntryPoints adding interface args to all of them.
365 for (const MachineInstr *MI : MAI->getMSInstrs(SPIRV::MB_EntryPoints)) {
366 SPIRVMCInstLower MCInstLowering;
367 MCInst TmpInst;
368 MCInstLowering.lower(MI, TmpInst, MAI);
369 for (MCRegister Reg : InterfaceIDs) {
370 assert(Reg.isValid());
372 }
373 outputMCInst(TmpInst);
374 }
375}
376
377// Create global OpCapability instructions for the required capabilities.
378void SPIRVAsmPrinter::outputGlobalRequirements() {
379 // Abort here if not all requirements can be satisfied.
380 MAI->Reqs.checkSatisfiable(*ST);
381
382 for (const auto &Cap : MAI->Reqs.getMinimalCapabilities()) {
383 MCInst Inst;
384 Inst.setOpcode(SPIRV::OpCapability);
386 outputMCInst(Inst);
387 }
388
389 // Generate the final OpExtensions with strings instead of enums.
390 for (const auto &Ext : MAI->Reqs.getExtensions()) {
391 MCInst Inst;
392 Inst.setOpcode(SPIRV::OpExtension);
394 SPIRV::OperandCategory::ExtensionOperand, Ext),
395 Inst);
396 outputMCInst(Inst);
397 }
398 // TODO add a pseudo instr for version number.
399}
400
401void SPIRVAsmPrinter::outputExtFuncDecls() {
402 // Insert OpFunctionEnd after each declaration.
403 auto I = MAI->getMSInstrs(SPIRV::MB_ExtFuncDecls).begin(),
404 E = MAI->getMSInstrs(SPIRV::MB_ExtFuncDecls).end();
405 for (; I != E; ++I) {
406 outputInstruction(*I);
407 if ((I + 1) == E || (*(I + 1))->getOpcode() == SPIRV::OpFunction)
408 outputOpFunctionEnd();
409 }
410}
411
412// Encode LLVM type by SPIR-V execution mode VecTypeHint.
413static unsigned encodeVecTypeHint(Type *Ty) {
414 if (Ty->isHalfTy())
415 return 4;
416 if (Ty->isFloatTy())
417 return 5;
418 if (Ty->isDoubleTy())
419 return 6;
420 if (IntegerType *IntTy = dyn_cast<IntegerType>(Ty)) {
421 switch (IntTy->getIntegerBitWidth()) {
422 case 8:
423 return 0;
424 case 16:
425 return 1;
426 case 32:
427 return 2;
428 case 64:
429 return 3;
430 default:
431 llvm_unreachable("invalid integer type");
432 }
433 }
435 Type *EleTy = VecTy->getElementType();
436 unsigned Size = VecTy->getNumElements();
437 return Size << 16 | encodeVecTypeHint(EleTy);
438 }
439 llvm_unreachable("invalid type");
440}
441
442static void addOpsFromMDNode(MDNode *MDN, MCInst &Inst,
444 for (const MDOperand &MDOp : MDN->operands()) {
445 if (auto *CMeta = dyn_cast<ConstantAsMetadata>(MDOp)) {
446 Constant *C = CMeta->getValue();
447 if (ConstantInt *Const = dyn_cast<ConstantInt>(C)) {
448 Inst.addOperand(MCOperand::createImm(Const->getZExtValue()));
449 } else if (auto *CE = dyn_cast<Function>(C)) {
450 MCRegister FuncReg = MAI->getFuncReg(CE);
451 assert(FuncReg.isValid());
452 Inst.addOperand(MCOperand::createReg(FuncReg));
453 }
454 }
455 }
456}
457
458void SPIRVAsmPrinter::outputExecutionModeFromMDNode(
459 MCRegister Reg, MDNode *Node, SPIRV::ExecutionMode::ExecutionMode EM,
460 unsigned ExpectMDOps, int64_t DefVal) {
461 MCInst Inst;
462 Inst.setOpcode(SPIRV::OpExecutionMode);
464 Inst.addOperand(MCOperand::createImm(static_cast<unsigned>(EM)));
465 addOpsFromMDNode(Node, Inst, MAI);
466 // reqd_work_group_size and work_group_size_hint require 3 operands,
467 // if metadata contains less operands, just add a default value
468 unsigned NodeSz = Node->getNumOperands();
469 if (ExpectMDOps > 0 && NodeSz < ExpectMDOps)
470 for (unsigned i = NodeSz; i < ExpectMDOps; ++i)
471 Inst.addOperand(MCOperand::createImm(DefVal));
472 outputMCInst(Inst);
473}
474
475void SPIRVAsmPrinter::outputExecutionModeFromNumthreadsAttribute(
476 const MCRegister &Reg, const Attribute &Attr,
477 SPIRV::ExecutionMode::ExecutionMode EM) {
478 assert(Attr.isValid() && "Function called with an invalid attribute.");
479
480 MCInst Inst;
481 Inst.setOpcode(SPIRV::OpExecutionMode);
483 Inst.addOperand(MCOperand::createImm(static_cast<unsigned>(EM)));
484
485 SmallVector<StringRef> NumThreads;
486 Attr.getValueAsString().split(NumThreads, ',');
487 assert(NumThreads.size() == 3 && "invalid numthreads");
488 for (uint32_t i = 0; i < 3; ++i) {
489 uint32_t V;
490 [[maybe_unused]] bool Result = NumThreads[i].getAsInteger(10, V);
491 assert(!Result && "Failed to parse numthreads");
493 }
494
495 outputMCInst(Inst);
496}
497
498void SPIRVAsmPrinter::outputExecutionMode(const Module &M) {
499 NamedMDNode *Node = M.getNamedMetadata("spirv.ExecutionMode");
500 if (Node) {
501 for (unsigned i = 0; i < Node->getNumOperands(); i++) {
502 // If SPV_KHR_float_controls2 is enabled and we find any of
503 // FPFastMathDefault, ContractionOff or SignedZeroInfNanPreserve execution
504 // modes, skip it, it'll be done somewhere else.
505 if (ST->canUseExtension(SPIRV::Extension::SPV_KHR_float_controls2)) {
506 const auto EM =
508 cast<ConstantAsMetadata>((Node->getOperand(i))->getOperand(1))
509 ->getValue())
510 ->getZExtValue();
511 if (EM == SPIRV::ExecutionMode::FPFastMathDefault ||
512 EM == SPIRV::ExecutionMode::ContractionOff ||
513 EM == SPIRV::ExecutionMode::SignedZeroInfNanPreserve)
514 continue;
515 }
516
517 MCInst Inst;
518 Inst.setOpcode(SPIRV::OpExecutionMode);
519 addOpsFromMDNode(cast<MDNode>(Node->getOperand(i)), Inst, MAI);
520 outputMCInst(Inst);
521 }
522 outputFPFastMathDefaultInfo();
523 }
524 for (auto FI = M.begin(), E = M.end(); FI != E; ++FI) {
525 const Function &F = *FI;
526 // Only operands of OpEntryPoint instructions are allowed to be
527 // <Entry Point> operands of OpExecutionMode
528 if (F.isDeclaration() || !isEntryPoint(F))
529 continue;
530 MCRegister FReg = MAI->getFuncReg(&F);
531 assert(FReg.isValid());
532
533 if (Attribute Attr = F.getFnAttribute("hlsl.shader"); Attr.isValid()) {
534 // SPIR-V common validation: Fragment requires OriginUpperLeft or
535 // OriginLowerLeft.
536 // VUID-StandaloneSpirv-OriginLowerLeft-04653: Fragment must declare
537 // OriginUpperLeft.
538 if (Attr.getValueAsString() == "pixel") {
539 MCInst Inst;
540 Inst.setOpcode(SPIRV::OpExecutionMode);
542 unsigned EM =
543 static_cast<unsigned>(SPIRV::ExecutionMode::OriginUpperLeft);
545 outputMCInst(Inst);
546 }
547 }
548 if (MDNode *Node = F.getMetadata("reqd_work_group_size"))
549 outputExecutionModeFromMDNode(FReg, Node, SPIRV::ExecutionMode::LocalSize,
550 3, 1);
551 if (Attribute Attr = F.getFnAttribute("hlsl.numthreads"); Attr.isValid())
552 outputExecutionModeFromNumthreadsAttribute(
553 FReg, Attr, SPIRV::ExecutionMode::LocalSize);
554 if (MDNode *Node = F.getMetadata("work_group_size_hint"))
555 outputExecutionModeFromMDNode(FReg, Node,
556 SPIRV::ExecutionMode::LocalSizeHint, 3, 1);
557 if (MDNode *Node = F.getMetadata("intel_reqd_sub_group_size"))
558 outputExecutionModeFromMDNode(FReg, Node,
559 SPIRV::ExecutionMode::SubgroupSize, 0, 0);
560 if (MDNode *Node = F.getMetadata("vec_type_hint")) {
561 MCInst Inst;
562 Inst.setOpcode(SPIRV::OpExecutionMode);
564 unsigned EM = static_cast<unsigned>(SPIRV::ExecutionMode::VecTypeHint);
566 unsigned TypeCode = encodeVecTypeHint(getMDOperandAsType(Node, 0));
567 Inst.addOperand(MCOperand::createImm(TypeCode));
568 outputMCInst(Inst);
569 }
570 if (ST->isKernel() && !M.getNamedMetadata("spirv.ExecutionMode") &&
571 !M.getNamedMetadata("opencl.enable.FP_CONTRACT")) {
572 if (ST->canUseExtension(SPIRV::Extension::SPV_KHR_float_controls2)) {
573 // When SPV_KHR_float_controls2 is enabled, ContractionOff is
574 // deprecated. We need to use FPFastMathDefault with the appropriate
575 // flags instead. Since FPFastMathDefault takes a target type, we need
576 // to emit it for each floating-point type that exists in the module
577 // to match the effect of ContractionOff. As of now, there are 3 FP
578 // types: fp16, fp32 and fp64.
579
580 // We only end up here because there is no "spirv.ExecutionMode"
581 // metadata, so that means no FPFastMathDefault. Therefore, we only
582 // need to make sure AllowContract is set to 0, as the rest of flags.
583 // We still need to emit the OpExecutionMode instruction, otherwise
584 // it's up to the client API to define the flags. Therefore, we need
585 // to find the constant with 0 value.
586
587 // Collect the SPIRVTypes for fp16, fp32, and fp64 and the constant of
588 // type int32 with 0 value to represent the FP Fast Math Mode.
589 std::vector<const MachineInstr *> SPIRVFloatTypes;
590 const MachineInstr *ConstZero = nullptr;
591 for (const MachineInstr *MI :
592 MAI->getMSInstrs(SPIRV::MB_TypeConstVars)) {
593 // Skip if the instruction is not OpTypeFloat or OpConstant.
594 unsigned OpCode = MI->getOpcode();
595 if (OpCode != SPIRV::OpTypeFloat && OpCode != SPIRV::OpConstantNull)
596 continue;
597
598 // Collect the SPIRV type if it's a float.
599 if (OpCode == SPIRV::OpTypeFloat) {
600 // Skip if the target type is not fp16, fp32, fp64.
601 const unsigned OpTypeFloatSize = MI->getOperand(1).getImm();
602 if (OpTypeFloatSize != 16 && OpTypeFloatSize != 32 &&
603 OpTypeFloatSize != 64) {
604 continue;
605 }
606 SPIRVFloatTypes.push_back(MI);
607 } else {
608 // Check if the constant is int32, if not skip it.
609 const MachineRegisterInfo &MRI = MI->getMF()->getRegInfo();
610 MachineInstr *TypeMI = MRI.getVRegDef(MI->getOperand(1).getReg());
611 if (!TypeMI || TypeMI->getOperand(1).getImm() != 32)
612 continue;
613
614 ConstZero = MI;
615 }
616 }
617
618 // When SPV_KHR_float_controls2 is enabled, ContractionOff is
619 // deprecated. We need to use FPFastMathDefault with the appropriate
620 // flags instead. Since FPFastMathDefault takes a target type, we need
621 // to emit it for each floating-point type that exists in the module
622 // to match the effect of ContractionOff. As of now, there are 3 FP
623 // types: fp16, fp32 and fp64.
624 for (const MachineInstr *MI : SPIRVFloatTypes) {
625 MCInst Inst;
626 Inst.setOpcode(SPIRV::OpExecutionModeId);
628 unsigned EM =
629 static_cast<unsigned>(SPIRV::ExecutionMode::FPFastMathDefault);
631 const MachineFunction *MF = MI->getMF();
632 MCRegister TypeReg =
633 MAI->getRegisterAlias(MF, MI->getOperand(0).getReg());
634 Inst.addOperand(MCOperand::createReg(TypeReg));
635 assert(ConstZero && "There should be a constant zero.");
636 MCRegister ConstReg = MAI->getRegisterAlias(
637 ConstZero->getMF(), ConstZero->getOperand(0).getReg());
638 Inst.addOperand(MCOperand::createReg(ConstReg));
639 outputMCInst(Inst);
640 }
641 } else {
642 MCInst Inst;
643 Inst.setOpcode(SPIRV::OpExecutionMode);
645 unsigned EM =
646 static_cast<unsigned>(SPIRV::ExecutionMode::ContractionOff);
648 outputMCInst(Inst);
649 }
650 }
651 }
652}
653
654void SPIRVAsmPrinter::outputAnnotations(const Module &M) {
655 outputModuleSection(SPIRV::MB_Annotations);
656 // Process llvm.global.annotations special global variable.
657 for (auto F = M.global_begin(), E = M.global_end(); F != E; ++F) {
658 if ((*F).getName() != "llvm.global.annotations")
659 continue;
660 const GlobalVariable *V = &(*F);
661 const ConstantArray *CA = cast<ConstantArray>(V->getOperand(0));
662 for (Value *Op : CA->operands()) {
663 ConstantStruct *CS = cast<ConstantStruct>(Op);
664 // The first field of the struct contains a pointer to
665 // the annotated variable.
666 Value *AnnotatedVar = CS->getOperand(0)->stripPointerCasts();
667 if (!isa<Function>(AnnotatedVar))
668 report_fatal_error("Unsupported value in llvm.global.annotations");
669 Function *Func = cast<Function>(AnnotatedVar);
670 MCRegister Reg = MAI->getFuncReg(Func);
671 if (!Reg.isValid()) {
672 std::string DiagMsg;
673 raw_string_ostream OS(DiagMsg);
674 AnnotatedVar->print(OS);
675 DiagMsg = "Unknown function in llvm.global.annotations: " + DiagMsg;
676 report_fatal_error(DiagMsg.c_str());
677 }
678
679 // The second field contains a pointer to a global annotation string.
680 GlobalVariable *GV =
682
683 StringRef AnnotationString;
684 [[maybe_unused]] bool Success =
685 getConstantStringInfo(GV, AnnotationString);
686 assert(Success && "Failed to get annotation string");
687 MCInst Inst;
688 Inst.setOpcode(SPIRV::OpDecorate);
690 unsigned Dec = static_cast<unsigned>(SPIRV::Decoration::UserSemantic);
692 addStringImm(AnnotationString, Inst);
693 outputMCInst(Inst);
694 }
695 }
696}
697
698void SPIRVAsmPrinter::outputFPFastMathDefaultInfo() {
699 // Collect the SPIRVTypes that are OpTypeFloat and the constants of type
700 // int32, that might be used as FP Fast Math Mode.
701 std::vector<const MachineInstr *> SPIRVFloatTypes;
702 // Hashtable to associate immediate values with the constant holding them.
703 std::unordered_map<int, const MachineInstr *> ConstMap;
704 for (const MachineInstr *MI : MAI->getMSInstrs(SPIRV::MB_TypeConstVars)) {
705 // Skip if the instruction is not OpTypeFloat or OpConstant.
706 unsigned OpCode = MI->getOpcode();
707 if (OpCode != SPIRV::OpTypeFloat && OpCode != SPIRV::OpConstantI &&
708 OpCode != SPIRV::OpConstantNull)
709 continue;
710
711 // Collect the SPIRV type if it's a float.
712 if (OpCode == SPIRV::OpTypeFloat) {
713 SPIRVFloatTypes.push_back(MI);
714 } else {
715 // Check if the constant is int32, if not skip it.
716 const MachineRegisterInfo &MRI = MI->getMF()->getRegInfo();
717 MachineInstr *TypeMI = MRI.getVRegDef(MI->getOperand(1).getReg());
718 if (!TypeMI || TypeMI->getOpcode() != SPIRV::OpTypeInt ||
719 TypeMI->getOperand(1).getImm() != 32)
720 continue;
721
722 if (OpCode == SPIRV::OpConstantI)
723 ConstMap[MI->getOperand(2).getImm()] = MI;
724 else
725 ConstMap[0] = MI;
726 }
727 }
728
729 for (const auto &[Func, FPFastMathDefaultInfoVec] :
730 MAI->FPFastMathDefaultInfoMap) {
731 if (FPFastMathDefaultInfoVec.empty())
732 continue;
733
734 for (const MachineInstr *MI : SPIRVFloatTypes) {
735 unsigned OpTypeFloatSize = MI->getOperand(1).getImm();
738 assert(Index < FPFastMathDefaultInfoVec.size() &&
739 "Index out of bounds for FPFastMathDefaultInfoVec");
740 const auto &FPFastMathDefaultInfo = FPFastMathDefaultInfoVec[Index];
741 assert(FPFastMathDefaultInfo.Ty &&
742 "Expected target type for FPFastMathDefaultInfo");
743 assert(FPFastMathDefaultInfo.Ty->getScalarSizeInBits() ==
744 OpTypeFloatSize &&
745 "Mismatched float type size");
746 MCInst Inst;
747 Inst.setOpcode(SPIRV::OpExecutionModeId);
748 MCRegister FuncReg = MAI->getFuncReg(Func);
749 assert(FuncReg.isValid());
750 Inst.addOperand(MCOperand::createReg(FuncReg));
751 Inst.addOperand(
752 MCOperand::createImm(SPIRV::ExecutionMode::FPFastMathDefault));
753 MCRegister TypeReg =
754 MAI->getRegisterAlias(MI->getMF(), MI->getOperand(0).getReg());
755 Inst.addOperand(MCOperand::createReg(TypeReg));
756 unsigned Flags = FPFastMathDefaultInfo.FastMathFlags;
757 if (FPFastMathDefaultInfo.ContractionOff &&
758 (Flags & SPIRV::FPFastMathMode::AllowContract))
760 "Conflicting FPFastMathFlags: ContractionOff and AllowContract");
761
762 if (FPFastMathDefaultInfo.SignedZeroInfNanPreserve &&
763 !(Flags &
764 (SPIRV::FPFastMathMode::NotNaN | SPIRV::FPFastMathMode::NotInf |
765 SPIRV::FPFastMathMode::NSZ))) {
766 if (FPFastMathDefaultInfo.FPFastMathDefault)
767 report_fatal_error("Conflicting FPFastMathFlags: "
768 "SignedZeroInfNanPreserve but at least one of "
769 "NotNaN/NotInf/NSZ is enabled.");
770 }
771
772 // Don't emit if none of the execution modes was used.
773 if (Flags == SPIRV::FPFastMathMode::None &&
774 !FPFastMathDefaultInfo.ContractionOff &&
775 !FPFastMathDefaultInfo.SignedZeroInfNanPreserve &&
776 !FPFastMathDefaultInfo.FPFastMathDefault)
777 continue;
778
779 // Retrieve the constant instruction for the immediate value.
780 auto It = ConstMap.find(Flags);
781 if (It == ConstMap.end())
782 report_fatal_error("Expected constant instruction for FP Fast Math "
783 "Mode operand of FPFastMathDefault execution mode.");
784 const MachineInstr *ConstMI = It->second;
785 MCRegister ConstReg = MAI->getRegisterAlias(
786 ConstMI->getMF(), ConstMI->getOperand(0).getReg());
787 Inst.addOperand(MCOperand::createReg(ConstReg));
788 outputMCInst(Inst);
789 }
790 }
791}
792
793void SPIRVAsmPrinter::outputModuleSections() {
794 const Module *M = MMI->getModule();
795 // Get the global subtarget to output module-level info.
796 ST = static_cast<const SPIRVTargetMachine &>(TM).getSubtargetImpl();
797 TII = ST->getInstrInfo();
799 assert(ST && TII && MAI && M && "Module analysis is required");
800 // Output instructions according to the Logical Layout of a Module:
801 // 1,2. All OpCapability instructions, then optional OpExtension
802 // instructions.
803 outputGlobalRequirements();
804 // 3. Optional OpExtInstImport instructions.
805 outputOpExtInstImports(*M);
806 // 4. The single required OpMemoryModel instruction.
807 outputOpMemoryModel();
808 // 5. All entry point declarations, using OpEntryPoint.
809 outputEntryPoints();
810 // 6. Execution-mode declarations, using OpExecutionMode or
811 // OpExecutionModeId.
812 outputExecutionMode(*M);
813 // 7a. Debug: all OpString, OpSourceExtension, OpSource, and
814 // OpSourceContinued, without forward references.
815 outputDebugSourceAndStrings(*M);
816 // 7b. Debug: all OpName and all OpMemberName.
817 outputModuleSection(SPIRV::MB_DebugNames);
818 // 7c. Debug: all OpModuleProcessed instructions.
819 outputModuleSection(SPIRV::MB_DebugModuleProcessed);
820 // xxx. SPV_INTEL_memory_access_aliasing instructions go before 8.
821 // "All annotation instructions"
822 outputModuleSection(SPIRV::MB_AliasingInsts);
823 // 8. All annotation instructions (all decorations).
824 outputAnnotations(*M);
825 // 9. All type declarations (OpTypeXXX instructions), all constant
826 // instructions, and all global variable declarations. This section is
827 // the first section to allow use of: OpLine and OpNoLine debug information;
828 // non-semantic instructions with OpExtInst.
829 outputModuleSection(SPIRV::MB_TypeConstVars);
830 // 10. All global NonSemantic.Shader.DebugInfo.100 instructions.
831 outputModuleSection(SPIRV::MB_NonSemanticGlobalDI);
832 // 11. All function declarations (functions without a body).
833 outputExtFuncDecls();
834 // 12. All function definitions (functions with a body).
835 // This is done in regular function output.
836}
837
838bool SPIRVAsmPrinter::doInitialization(Module &M) {
839 ModuleSectionsEmitted = false;
840 // We need to call the parent's one explicitly.
842}
843
844char SPIRVAsmPrinter::ID = 0;
845
846INITIALIZE_PASS(SPIRVAsmPrinter, "spirv-asm-printer", "SPIRV Assembly Printer",
847 false, false)
848
849// Force static initialization.
851LLVMInitializeSPIRVAsmPrinter() {
855}
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:500
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:91
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 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
bool canUseExtension(SPIRV::Extension::Extension E) 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)