882 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// This file contains a printer that converts from our internal representation10// 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/MachineInstr.h"28#include "llvm/CodeGen/MachineModuleInfo.h"29#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h"30#include "llvm/MC/MCAsmInfo.h"31#include "llvm/MC/MCAssembler.h"32#include "llvm/MC/MCInst.h"33#include "llvm/MC/MCObjectStreamer.h"34#include "llvm/MC/MCSPIRVObjectWriter.h"35#include "llvm/MC/MCStreamer.h"36#include "llvm/MC/MCSymbol.h"37#include "llvm/MC/TargetRegistry.h"38#include "llvm/Support/Compiler.h"39#include "llvm/Support/raw_ostream.h"40 41using namespace llvm;42 43#define DEBUG_TYPE "asm-printer"44 45namespace {46class SPIRVAsmPrinter : public AsmPrinter {47 unsigned NLabels = 0;48 SmallPtrSet<const MachineBasicBlock *, 8> LabeledMBB;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;106 SPIRV::ModuleAnalysisInfo *MAI;107 108protected:109 void cleanUp(Module &M);110};111} // namespace112 113void SPIRVAsmPrinter::getAnalysisUsage(AnalysisUsage &AU) const {114 AU.addRequired<SPIRVModuleAnalysis>();115 AU.addPreserved<SPIRVModuleAnalysis>();116 AsmPrinter::getAnalysisUsage(AU);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 register131 // number and number of generated OpLabels132 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 content141// 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, so201 // 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()) {217 case MachineOperand::MO_Register:218 O << SPIRVInstPrinter::getRegisterName(MO.getReg());219 break;220 221 case MachineOperand::MO_Immediate:222 O << MO.getImm();223 break;224 225 case MachineOperand::MO_FPImmediate:226 O << MO.getFPImm();227 break;228 229 case MachineOperand::MO_MachineBasicBlock:230 O << *MO.getMBB()->getSymbol();231 break;232 233 case MachineOperand::MO_GlobalAddress:234 O << *getSymbol(MO.getGlobal());235 break;236 237 case MachineOperand::MO_BlockAddress: {238 MCSymbol *BA = GetBlockAddressSymbol(MO.getBlockAddress());239 O << BA->getName();240 break;241 }242 243 case MachineOperand::MO_ExternalSymbol:244 O << *GetExternalSymbolSymbol(MO.getSymbolName());245 break;246 247 case MachineOperand::MO_JumpTableIndex:248 case MachineOperand::MO_ConstantPoolIndex: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 modifiers258 259 printOperand(MI, OpNo, O);260 return false;261}262 263static bool isFuncOrHeaderInstr(const MachineInstr *MI,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);327 Inst.addOperand(MCOperand::createReg(Reg));328 addStringImm(getExtInstSetName(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's344// interfaces. The interface is a list of IDs of global OpVariable instructions.345// These declare the set of global variables from a module that form346// 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 to355 // the Input and Output storage classes. Starting with version 1.4,356 // the interface's storage classes are all storage classes used in357 // 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());373 TmpInst.addOperand(MCOperand::createReg(Reg));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);387 Inst.addOperand(MCOperand::createImm(Cap));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);395 addStringImm(getSymbolicOperandMnemonic(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 }436 if (FixedVectorType *VecTy = dyn_cast<FixedVectorType>(Ty)) {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,445 SPIRV::ModuleAnalysisInfo *MAI) {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);465 Inst.addOperand(MCOperand::createReg(Reg));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 value470 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);484 Inst.addOperand(MCOperand::createReg(Reg));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");494 Inst.addOperand(MCOperand::createImm(V));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);507 Inst.addOperand(MCOperand::createReg(Reg));508 unsigned EM =509 static_cast<unsigned>(SPIRV::ExecutionMode::MaximallyReconvergesKHR);510 Inst.addOperand(MCOperand::createImm(EM));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 of519 // FPFastMathDefault, ContractionOff or SignedZeroInfNanPreserve execution520 // modes, skip it, it'll be done somewhere else.521 if (ST->canUseExtension(SPIRV::Extension::SPV_KHR_float_controls2)) {522 const auto EM =523 cast<ConstantInt>(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 be543 // <Entry Point> operands of OpExecutionMode544 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 or551 // OriginLowerLeft.552 // VUID-StandaloneSpirv-OriginLowerLeft-04653: Fragment must declare553 // OriginUpperLeft.554 if (Attr.getValueAsString() == "pixel") {555 MCInst Inst;556 Inst.setOpcode(SPIRV::OpExecutionMode);557 Inst.addOperand(MCOperand::createReg(FReg));558 unsigned EM =559 static_cast<unsigned>(SPIRV::ExecutionMode::OriginUpperLeft);560 Inst.addOperand(MCOperand::createImm(EM));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("max_work_group_size")) {581 if (ST->canUseExtension(SPIRV::Extension::SPV_INTEL_kernel_attributes))582 outputExecutionModeFromMDNode(583 FReg, Node, SPIRV::ExecutionMode::MaxWorkgroupSizeINTEL, 3, 1);584 }585 if (MDNode *Node = F.getMetadata("vec_type_hint")) {586 MCInst Inst;587 Inst.setOpcode(SPIRV::OpExecutionMode);588 Inst.addOperand(MCOperand::createReg(FReg));589 unsigned EM = static_cast<unsigned>(SPIRV::ExecutionMode::VecTypeHint);590 Inst.addOperand(MCOperand::createImm(EM));591 unsigned TypeCode = encodeVecTypeHint(getMDOperandAsType(Node, 0));592 Inst.addOperand(MCOperand::createImm(TypeCode));593 outputMCInst(Inst);594 }595 if (ST->isKernel() && !M.getNamedMetadata("spirv.ExecutionMode") &&596 !M.getNamedMetadata("opencl.enable.FP_CONTRACT")) {597 if (ST->canUseExtension(SPIRV::Extension::SPV_KHR_float_controls2)) {598 // When SPV_KHR_float_controls2 is enabled, ContractionOff is599 // deprecated. We need to use FPFastMathDefault with the appropriate600 // flags instead. Since FPFastMathDefault takes a target type, we need601 // to emit it for each floating-point type that exists in the module602 // to match the effect of ContractionOff. As of now, there are 3 FP603 // types: fp16, fp32 and fp64.604 605 // We only end up here because there is no "spirv.ExecutionMode"606 // metadata, so that means no FPFastMathDefault. Therefore, we only607 // need to make sure AllowContract is set to 0, as the rest of flags.608 // We still need to emit the OpExecutionMode instruction, otherwise609 // it's up to the client API to define the flags. Therefore, we need610 // to find the constant with 0 value.611 612 // Collect the SPIRVTypes for fp16, fp32, and fp64 and the constant of613 // type int32 with 0 value to represent the FP Fast Math Mode.614 std::vector<const MachineInstr *> SPIRVFloatTypes;615 const MachineInstr *ConstZeroInt32 = nullptr;616 for (const MachineInstr *MI :617 MAI->getMSInstrs(SPIRV::MB_TypeConstVars)) {618 unsigned OpCode = MI->getOpcode();619 620 // Collect the SPIRV type if it's a float.621 if (OpCode == SPIRV::OpTypeFloat) {622 // Skip if the target type is not fp16, fp32, fp64.623 const unsigned OpTypeFloatSize = MI->getOperand(1).getImm();624 if (OpTypeFloatSize != 16 && OpTypeFloatSize != 32 &&625 OpTypeFloatSize != 64) {626 continue;627 }628 SPIRVFloatTypes.push_back(MI);629 continue;630 }631 632 if (OpCode == SPIRV::OpConstantNull) {633 // Check if the constant is int32, if not skip it.634 const MachineRegisterInfo &MRI = MI->getMF()->getRegInfo();635 MachineInstr *TypeMI = MRI.getVRegDef(MI->getOperand(1).getReg());636 bool IsInt32Ty = TypeMI &&637 TypeMI->getOpcode() == SPIRV::OpTypeInt &&638 TypeMI->getOperand(1).getImm() == 32;639 if (IsInt32Ty)640 ConstZeroInt32 = MI;641 }642 }643 644 // When SPV_KHR_float_controls2 is enabled, ContractionOff is645 // deprecated. We need to use FPFastMathDefault with the appropriate646 // flags instead. Since FPFastMathDefault takes a target type, we need647 // to emit it for each floating-point type that exists in the module648 // to match the effect of ContractionOff. As of now, there are 3 FP649 // types: fp16, fp32 and fp64.650 for (const MachineInstr *MI : SPIRVFloatTypes) {651 MCInst Inst;652 Inst.setOpcode(SPIRV::OpExecutionModeId);653 Inst.addOperand(MCOperand::createReg(FReg));654 unsigned EM =655 static_cast<unsigned>(SPIRV::ExecutionMode::FPFastMathDefault);656 Inst.addOperand(MCOperand::createImm(EM));657 const MachineFunction *MF = MI->getMF();658 MCRegister TypeReg =659 MAI->getRegisterAlias(MF, MI->getOperand(0).getReg());660 Inst.addOperand(MCOperand::createReg(TypeReg));661 assert(ConstZeroInt32 && "There should be a constant zero.");662 MCRegister ConstReg = MAI->getRegisterAlias(663 ConstZeroInt32->getMF(), ConstZeroInt32->getOperand(0).getReg());664 Inst.addOperand(MCOperand::createReg(ConstReg));665 outputMCInst(Inst);666 }667 } else {668 MCInst Inst;669 Inst.setOpcode(SPIRV::OpExecutionMode);670 Inst.addOperand(MCOperand::createReg(FReg));671 unsigned EM =672 static_cast<unsigned>(SPIRV::ExecutionMode::ContractionOff);673 Inst.addOperand(MCOperand::createImm(EM));674 outputMCInst(Inst);675 }676 }677 }678}679 680void SPIRVAsmPrinter::outputAnnotations(const Module &M) {681 outputModuleSection(SPIRV::MB_Annotations);682 // Process llvm.global.annotations special global variable.683 for (auto F = M.global_begin(), E = M.global_end(); F != E; ++F) {684 if ((*F).getName() != "llvm.global.annotations")685 continue;686 const GlobalVariable *V = &(*F);687 const ConstantArray *CA = cast<ConstantArray>(V->getOperand(0));688 for (Value *Op : CA->operands()) {689 ConstantStruct *CS = cast<ConstantStruct>(Op);690 // The first field of the struct contains a pointer to691 // the annotated variable.692 Value *AnnotatedVar = CS->getOperand(0)->stripPointerCasts();693 if (!isa<Function>(AnnotatedVar))694 report_fatal_error("Unsupported value in llvm.global.annotations");695 Function *Func = cast<Function>(AnnotatedVar);696 MCRegister Reg = MAI->getFuncReg(Func);697 if (!Reg.isValid()) {698 std::string DiagMsg;699 raw_string_ostream OS(DiagMsg);700 AnnotatedVar->print(OS);701 DiagMsg = "Unknown function in llvm.global.annotations: " + DiagMsg;702 report_fatal_error(DiagMsg.c_str());703 }704 705 // The second field contains a pointer to a global annotation string.706 GlobalVariable *GV =707 cast<GlobalVariable>(CS->getOperand(1)->stripPointerCasts());708 709 StringRef AnnotationString;710 [[maybe_unused]] bool Success =711 getConstantStringInfo(GV, AnnotationString);712 assert(Success && "Failed to get annotation string");713 MCInst Inst;714 Inst.setOpcode(SPIRV::OpDecorate);715 Inst.addOperand(MCOperand::createReg(Reg));716 unsigned Dec = static_cast<unsigned>(SPIRV::Decoration::UserSemantic);717 Inst.addOperand(MCOperand::createImm(Dec));718 addStringImm(AnnotationString, Inst);719 outputMCInst(Inst);720 }721 }722}723 724void SPIRVAsmPrinter::outputFPFastMathDefaultInfo() {725 // Collect the SPIRVTypes that are OpTypeFloat and the constants of type726 // int32, that might be used as FP Fast Math Mode.727 std::vector<const MachineInstr *> SPIRVFloatTypes;728 // Hashtable to associate immediate values with the constant holding them.729 std::unordered_map<int, const MachineInstr *> ConstMap;730 for (const MachineInstr *MI : MAI->getMSInstrs(SPIRV::MB_TypeConstVars)) {731 // Skip if the instruction is not OpTypeFloat or OpConstant.732 unsigned OpCode = MI->getOpcode();733 if (OpCode != SPIRV::OpTypeFloat && OpCode != SPIRV::OpConstantI &&734 OpCode != SPIRV::OpConstantNull)735 continue;736 737 // Collect the SPIRV type if it's a float.738 if (OpCode == SPIRV::OpTypeFloat) {739 SPIRVFloatTypes.push_back(MI);740 } else {741 // Check if the constant is int32, if not skip it.742 const MachineRegisterInfo &MRI = MI->getMF()->getRegInfo();743 MachineInstr *TypeMI = MRI.getVRegDef(MI->getOperand(1).getReg());744 if (!TypeMI || TypeMI->getOpcode() != SPIRV::OpTypeInt ||745 TypeMI->getOperand(1).getImm() != 32)746 continue;747 748 if (OpCode == SPIRV::OpConstantI)749 ConstMap[MI->getOperand(2).getImm()] = MI;750 else751 ConstMap[0] = MI;752 }753 }754 755 for (const auto &[Func, FPFastMathDefaultInfoVec] :756 MAI->FPFastMathDefaultInfoMap) {757 if (FPFastMathDefaultInfoVec.empty())758 continue;759 760 for (const MachineInstr *MI : SPIRVFloatTypes) {761 unsigned OpTypeFloatSize = MI->getOperand(1).getImm();762 unsigned Index = SPIRV::FPFastMathDefaultInfoVector::763 computeFPFastMathDefaultInfoVecIndex(OpTypeFloatSize);764 assert(Index < FPFastMathDefaultInfoVec.size() &&765 "Index out of bounds for FPFastMathDefaultInfoVec");766 const auto &FPFastMathDefaultInfo = FPFastMathDefaultInfoVec[Index];767 assert(FPFastMathDefaultInfo.Ty &&768 "Expected target type for FPFastMathDefaultInfo");769 assert(FPFastMathDefaultInfo.Ty->getScalarSizeInBits() ==770 OpTypeFloatSize &&771 "Mismatched float type size");772 MCInst Inst;773 Inst.setOpcode(SPIRV::OpExecutionModeId);774 MCRegister FuncReg = MAI->getFuncReg(Func);775 assert(FuncReg.isValid());776 Inst.addOperand(MCOperand::createReg(FuncReg));777 Inst.addOperand(778 MCOperand::createImm(SPIRV::ExecutionMode::FPFastMathDefault));779 MCRegister TypeReg =780 MAI->getRegisterAlias(MI->getMF(), MI->getOperand(0).getReg());781 Inst.addOperand(MCOperand::createReg(TypeReg));782 unsigned Flags = FPFastMathDefaultInfo.FastMathFlags;783 if (FPFastMathDefaultInfo.ContractionOff &&784 (Flags & SPIRV::FPFastMathMode::AllowContract))785 report_fatal_error(786 "Conflicting FPFastMathFlags: ContractionOff and AllowContract");787 788 if (FPFastMathDefaultInfo.SignedZeroInfNanPreserve &&789 !(Flags &790 (SPIRV::FPFastMathMode::NotNaN | SPIRV::FPFastMathMode::NotInf |791 SPIRV::FPFastMathMode::NSZ))) {792 if (FPFastMathDefaultInfo.FPFastMathDefault)793 report_fatal_error("Conflicting FPFastMathFlags: "794 "SignedZeroInfNanPreserve but at least one of "795 "NotNaN/NotInf/NSZ is enabled.");796 }797 798 // Don't emit if none of the execution modes was used.799 if (Flags == SPIRV::FPFastMathMode::None &&800 !FPFastMathDefaultInfo.ContractionOff &&801 !FPFastMathDefaultInfo.SignedZeroInfNanPreserve &&802 !FPFastMathDefaultInfo.FPFastMathDefault)803 continue;804 805 // Retrieve the constant instruction for the immediate value.806 auto It = ConstMap.find(Flags);807 if (It == ConstMap.end())808 report_fatal_error("Expected constant instruction for FP Fast Math "809 "Mode operand of FPFastMathDefault execution mode.");810 const MachineInstr *ConstMI = It->second;811 MCRegister ConstReg = MAI->getRegisterAlias(812 ConstMI->getMF(), ConstMI->getOperand(0).getReg());813 Inst.addOperand(MCOperand::createReg(ConstReg));814 outputMCInst(Inst);815 }816 }817}818 819void SPIRVAsmPrinter::outputModuleSections() {820 const Module *M = MMI->getModule();821 // Get the global subtarget to output module-level info.822 ST = static_cast<const SPIRVTargetMachine &>(TM).getSubtargetImpl();823 TII = ST->getInstrInfo();824 MAI = &SPIRVModuleAnalysis::MAI;825 assert(ST && TII && MAI && M && "Module analysis is required");826 // Output instructions according to the Logical Layout of a Module:827 // 1,2. All OpCapability instructions, then optional OpExtension828 // instructions.829 outputGlobalRequirements();830 // 3. Optional OpExtInstImport instructions.831 outputOpExtInstImports(*M);832 // 4. The single required OpMemoryModel instruction.833 outputOpMemoryModel();834 // 5. All entry point declarations, using OpEntryPoint.835 outputEntryPoints();836 // 6. Execution-mode declarations, using OpExecutionMode or837 // OpExecutionModeId.838 outputExecutionMode(*M);839 // 7a. Debug: all OpString, OpSourceExtension, OpSource, and840 // OpSourceContinued, without forward references.841 outputDebugSourceAndStrings(*M);842 // 7b. Debug: all OpName and all OpMemberName.843 outputModuleSection(SPIRV::MB_DebugNames);844 // 7c. Debug: all OpModuleProcessed instructions.845 outputModuleSection(SPIRV::MB_DebugModuleProcessed);846 // xxx. SPV_INTEL_memory_access_aliasing instructions go before 8.847 // "All annotation instructions"848 outputModuleSection(SPIRV::MB_AliasingInsts);849 // 8. All annotation instructions (all decorations).850 outputAnnotations(*M);851 // 9. All type declarations (OpTypeXXX instructions), all constant852 // instructions, and all global variable declarations. This section is853 // the first section to allow use of: OpLine and OpNoLine debug information;854 // non-semantic instructions with OpExtInst.855 outputModuleSection(SPIRV::MB_TypeConstVars);856 // 10. All global NonSemantic.Shader.DebugInfo.100 instructions.857 outputModuleSection(SPIRV::MB_NonSemanticGlobalDI);858 // 11. All function declarations (functions without a body).859 outputExtFuncDecls();860 // 12. All function definitions (functions with a body).861 // This is done in regular function output.862}863 864bool SPIRVAsmPrinter::doInitialization(Module &M) {865 ModuleSectionsEmitted = false;866 // We need to call the parent's one explicitly.867 return AsmPrinter::doInitialization(M);868}869 870char SPIRVAsmPrinter::ID = 0;871 872INITIALIZE_PASS(SPIRVAsmPrinter, "spirv-asm-printer", "SPIRV Assembly Printer",873 false, false)874 875// Force static initialization.876extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void877LLVMInitializeSPIRVAsmPrinter() {878 RegisterAsmPrinter<SPIRVAsmPrinter> X(getTheSPIRV32Target());879 RegisterAsmPrinter<SPIRVAsmPrinter> Y(getTheSPIRV64Target());880 RegisterAsmPrinter<SPIRVAsmPrinter> Z(getTheSPIRVLogicalTarget());881}882