Files
clang-p2996/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp
bwlodarcz 62da359ce7 [SPIRV] Emitting DebugSource, DebugCompileUnit (#97558)
This commit introduces emission of DebugSource, DebugCompileUnit from
NonSemantic.Shader.DebugInfo.100 and required OpString with filename.
NonSemantic.Shader.DebugInfo.100 is divided, following DWARF into two
main concepts – emitting DIE and Line.
In DWARF .debug_abbriev and .debug_info sections are responsible for
emitting tree with information (DEIs) about e.g. types, compilation
unit. Corresponding to that in NonSemantic.Shader.DebugInfo.100 have
instructions like DebugSource, DebugCompileUnit etc. which preforms same
role in SPIR-V file. The difference is in fact that in SPIR-V there are
no sections but logical layout which forces order of the instruction
emission.
The NonSemantic.Shader.DebugInfo.100 requires for this type of global
information to be emitted after OpTypeXXX and OpConstantXXX
instructions.
One of the goals was to minimize changes and interaction with
SPIRVModuleAnalysis as possible which current commit achieves by
emitting it’s instructions directly into MachineFunction.
The possibility of duplicates are mitigated by guard inside pass which
emits the global information only once in one function.
By that method duplicates don’t have chance to be emitted.
From that point, adding new debug global instructions should be
straightforward.
2024-08-22 20:27:36 -07:00

614 lines
22 KiB
C++

//===-- SPIRVAsmPrinter.cpp - SPIR-V LLVM assembly writer ------*- C++ -*--===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file contains a printer that converts from our internal representation
// of machine-dependent LLVM code to the SPIR-V assembly language.
//
//===----------------------------------------------------------------------===//
#include "MCTargetDesc/SPIRVInstPrinter.h"
#include "SPIRV.h"
#include "SPIRVInstrInfo.h"
#include "SPIRVMCInstLower.h"
#include "SPIRVModuleAnalysis.h"
#include "SPIRVSubtarget.h"
#include "SPIRVTargetMachine.h"
#include "SPIRVUtils.h"
#include "TargetInfo/SPIRVTargetInfo.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/CodeGen/AsmPrinter.h"
#include "llvm/CodeGen/MachineConstantPool.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstr.h"
#include "llvm/CodeGen/MachineModuleInfo.h"
#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCAssembler.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCObjectStreamer.h"
#include "llvm/MC/MCSPIRVObjectWriter.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSymbol.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
#define DEBUG_TYPE "asm-printer"
namespace {
class SPIRVAsmPrinter : public AsmPrinter {
unsigned NLabels = 0;
public:
explicit SPIRVAsmPrinter(TargetMachine &TM,
std::unique_ptr<MCStreamer> Streamer)
: AsmPrinter(TM, std::move(Streamer)), ST(nullptr), TII(nullptr) {}
bool ModuleSectionsEmitted;
const SPIRVSubtarget *ST;
const SPIRVInstrInfo *TII;
StringRef getPassName() const override { return "SPIRV Assembly Printer"; }
void printOperand(const MachineInstr *MI, int OpNum, raw_ostream &O);
bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
const char *ExtraCode, raw_ostream &O) override;
void outputMCInst(MCInst &Inst);
void outputInstruction(const MachineInstr *MI);
void outputModuleSection(SPIRV::ModuleSectionType MSType);
void outputGlobalRequirements();
void outputEntryPoints();
void outputDebugSourceAndStrings(const Module &M);
void outputOpExtInstImports(const Module &M);
void outputOpMemoryModel();
void outputOpFunctionEnd();
void outputExtFuncDecls();
void outputExecutionModeFromMDNode(Register Reg, MDNode *Node,
SPIRV::ExecutionMode::ExecutionMode EM,
unsigned ExpectMDOps, int64_t DefVal);
void outputExecutionModeFromNumthreadsAttribute(
const Register &Reg, const Attribute &Attr,
SPIRV::ExecutionMode::ExecutionMode EM);
void outputExecutionMode(const Module &M);
void outputAnnotations(const Module &M);
void outputModuleSections();
void emitInstruction(const MachineInstr *MI) override;
void emitFunctionEntryLabel() override {}
void emitFunctionHeader() override;
void emitFunctionBodyStart() override {}
void emitFunctionBodyEnd() override;
void emitBasicBlockStart(const MachineBasicBlock &MBB) override;
void emitBasicBlockEnd(const MachineBasicBlock &MBB) override {}
void emitGlobalVariable(const GlobalVariable *GV) override {}
void emitOpLabel(const MachineBasicBlock &MBB);
void emitEndOfAsmFile(Module &M) override;
bool doInitialization(Module &M) override;
void getAnalysisUsage(AnalysisUsage &AU) const override;
SPIRV::ModuleAnalysisInfo *MAI;
};
} // namespace
void SPIRVAsmPrinter::getAnalysisUsage(AnalysisUsage &AU) const {
AU.addRequired<SPIRVModuleAnalysis>();
AU.addPreserved<SPIRVModuleAnalysis>();
AsmPrinter::getAnalysisUsage(AU);
}
// If the module has no functions, we need output global info anyway.
void SPIRVAsmPrinter::emitEndOfAsmFile(Module &M) {
if (ModuleSectionsEmitted == false) {
outputModuleSections();
ModuleSectionsEmitted = true;
}
ST = static_cast<const SPIRVTargetMachine &>(TM).getSubtargetImpl();
VersionTuple SPIRVVersion = ST->getSPIRVVersion();
uint32_t Major = SPIRVVersion.getMajor();
uint32_t Minor = SPIRVVersion.getMinor().value_or(0);
// Bound is an approximation that accounts for the maximum used register
// number and number of generated OpLabels
unsigned Bound = 2 * (ST->getBound() + 1) + NLabels;
if (MCAssembler *Asm = OutStreamer->getAssemblerPtr())
static_cast<SPIRVObjectWriter &>(Asm->getWriter())
.setBuildVersion(Major, Minor, Bound);
}
void SPIRVAsmPrinter::emitFunctionHeader() {
if (ModuleSectionsEmitted == false) {
outputModuleSections();
ModuleSectionsEmitted = true;
}
// Get the subtarget from the current MachineFunction.
ST = &MF->getSubtarget<SPIRVSubtarget>();
TII = ST->getInstrInfo();
const Function &F = MF->getFunction();
if (isVerbose()) {
OutStreamer->getCommentOS()
<< "-- Begin function "
<< GlobalValue::dropLLVMManglingEscape(F.getName()) << '\n';
}
auto Section = getObjFileLowering().SectionForGlobal(&F, TM);
MF->setSection(Section);
}
void SPIRVAsmPrinter::outputOpFunctionEnd() {
MCInst FunctionEndInst;
FunctionEndInst.setOpcode(SPIRV::OpFunctionEnd);
outputMCInst(FunctionEndInst);
}
// Emit OpFunctionEnd at the end of MF and clear BBNumToRegMap.
void SPIRVAsmPrinter::emitFunctionBodyEnd() {
outputOpFunctionEnd();
MAI->BBNumToRegMap.clear();
}
void SPIRVAsmPrinter::emitOpLabel(const MachineBasicBlock &MBB) {
MCInst LabelInst;
LabelInst.setOpcode(SPIRV::OpLabel);
LabelInst.addOperand(MCOperand::createReg(MAI->getOrCreateMBBRegister(MBB)));
outputMCInst(LabelInst);
++NLabels;
}
void SPIRVAsmPrinter::emitBasicBlockStart(const MachineBasicBlock &MBB) {
assert(!MBB.empty() && "MBB is empty!");
// If it's the first MBB in MF, it has OpFunction and OpFunctionParameter, so
// OpLabel should be output after them.
if (MBB.getNumber() == MF->front().getNumber()) {
for (const MachineInstr &MI : MBB)
if (MI.getOpcode() == SPIRV::OpFunction)
return;
// TODO: this case should be checked by the verifier.
report_fatal_error("OpFunction is expected in the front MBB of MF");
}
emitOpLabel(MBB);
}
void SPIRVAsmPrinter::printOperand(const MachineInstr *MI, int OpNum,
raw_ostream &O) {
const MachineOperand &MO = MI->getOperand(OpNum);
switch (MO.getType()) {
case MachineOperand::MO_Register:
O << SPIRVInstPrinter::getRegisterName(MO.getReg());
break;
case MachineOperand::MO_Immediate:
O << MO.getImm();
break;
case MachineOperand::MO_FPImmediate:
O << MO.getFPImm();
break;
case MachineOperand::MO_MachineBasicBlock:
O << *MO.getMBB()->getSymbol();
break;
case MachineOperand::MO_GlobalAddress:
O << *getSymbol(MO.getGlobal());
break;
case MachineOperand::MO_BlockAddress: {
MCSymbol *BA = GetBlockAddressSymbol(MO.getBlockAddress());
O << BA->getName();
break;
}
case MachineOperand::MO_ExternalSymbol:
O << *GetExternalSymbolSymbol(MO.getSymbolName());
break;
case MachineOperand::MO_JumpTableIndex:
case MachineOperand::MO_ConstantPoolIndex:
default:
llvm_unreachable("<unknown operand type>");
}
}
bool SPIRVAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
const char *ExtraCode, raw_ostream &O) {
if (ExtraCode && ExtraCode[0])
return true; // Invalid instruction - SPIR-V does not have special modifiers
printOperand(MI, OpNo, O);
return false;
}
static bool isFuncOrHeaderInstr(const MachineInstr *MI,
const SPIRVInstrInfo *TII) {
return TII->isHeaderInstr(*MI) || MI->getOpcode() == SPIRV::OpFunction ||
MI->getOpcode() == SPIRV::OpFunctionParameter;
}
void SPIRVAsmPrinter::outputMCInst(MCInst &Inst) {
OutStreamer->emitInstruction(Inst, *OutContext.getSubtargetInfo());
}
void SPIRVAsmPrinter::outputInstruction(const MachineInstr *MI) {
SPIRVMCInstLower MCInstLowering;
MCInst TmpInst;
MCInstLowering.lower(MI, TmpInst, MAI);
outputMCInst(TmpInst);
}
void SPIRVAsmPrinter::emitInstruction(const MachineInstr *MI) {
SPIRV_MC::verifyInstructionPredicates(MI->getOpcode(),
getSubtargetInfo().getFeatureBits());
if (!MAI->getSkipEmission(MI))
outputInstruction(MI);
// Output OpLabel after OpFunction and OpFunctionParameter in the first MBB.
const MachineInstr *NextMI = MI->getNextNode();
if (!MAI->hasMBBRegister(*MI->getParent()) && isFuncOrHeaderInstr(MI, TII) &&
(!NextMI || !isFuncOrHeaderInstr(NextMI, TII))) {
assert(MI->getParent()->getNumber() == MF->front().getNumber() &&
"OpFunction is not in the front MBB of MF");
emitOpLabel(*MI->getParent());
}
}
void SPIRVAsmPrinter::outputModuleSection(SPIRV::ModuleSectionType MSType) {
for (MachineInstr *MI : MAI->getMSInstrs(MSType))
outputInstruction(MI);
}
void SPIRVAsmPrinter::outputDebugSourceAndStrings(const Module &M) {
// Output OpSourceExtensions.
for (auto &Str : MAI->SrcExt) {
MCInst Inst;
Inst.setOpcode(SPIRV::OpSourceExtension);
addStringImm(Str.first(), Inst);
outputMCInst(Inst);
}
// Output OpString.
outputModuleSection(SPIRV::MB_DebugStrings);
// Output OpSource.
MCInst Inst;
Inst.setOpcode(SPIRV::OpSource);
Inst.addOperand(MCOperand::createImm(static_cast<unsigned>(MAI->SrcLang)));
Inst.addOperand(
MCOperand::createImm(static_cast<unsigned>(MAI->SrcLangVersion)));
outputMCInst(Inst);
}
void SPIRVAsmPrinter::outputOpExtInstImports(const Module &M) {
for (auto &CU : MAI->ExtInstSetMap) {
unsigned Set = CU.first;
Register Reg = CU.second;
MCInst Inst;
Inst.setOpcode(SPIRV::OpExtInstImport);
Inst.addOperand(MCOperand::createReg(Reg));
addStringImm(getExtInstSetName(
static_cast<SPIRV::InstructionSet::InstructionSet>(Set)),
Inst);
outputMCInst(Inst);
}
}
void SPIRVAsmPrinter::outputOpMemoryModel() {
MCInst Inst;
Inst.setOpcode(SPIRV::OpMemoryModel);
Inst.addOperand(MCOperand::createImm(static_cast<unsigned>(MAI->Addr)));
Inst.addOperand(MCOperand::createImm(static_cast<unsigned>(MAI->Mem)));
outputMCInst(Inst);
}
// Before the OpEntryPoints' output, we need to add the entry point's
// interfaces. The interface is a list of IDs of global OpVariable instructions.
// These declare the set of global variables from a module that form
// the interface of this entry point.
void SPIRVAsmPrinter::outputEntryPoints() {
// Find all OpVariable IDs with required StorageClass.
DenseSet<Register> InterfaceIDs;
for (MachineInstr *MI : MAI->GlobalVarList) {
assert(MI->getOpcode() == SPIRV::OpVariable);
auto SC = static_cast<SPIRV::StorageClass::StorageClass>(
MI->getOperand(2).getImm());
// Before version 1.4, the interface's storage classes are limited to
// the Input and Output storage classes. Starting with version 1.4,
// the interface's storage classes are all storage classes used in
// declaring all global variables referenced by the entry point call tree.
if (ST->isAtLeastSPIRVVer(VersionTuple(1, 4)) ||
SC == SPIRV::StorageClass::Input || SC == SPIRV::StorageClass::Output) {
MachineFunction *MF = MI->getMF();
Register Reg = MAI->getRegisterAlias(MF, MI->getOperand(0).getReg());
InterfaceIDs.insert(Reg);
}
}
// Output OpEntryPoints adding interface args to all of them.
for (MachineInstr *MI : MAI->getMSInstrs(SPIRV::MB_EntryPoints)) {
SPIRVMCInstLower MCInstLowering;
MCInst TmpInst;
MCInstLowering.lower(MI, TmpInst, MAI);
for (Register Reg : InterfaceIDs) {
assert(Reg.isValid());
TmpInst.addOperand(MCOperand::createReg(Reg));
}
outputMCInst(TmpInst);
}
}
// Create global OpCapability instructions for the required capabilities.
void SPIRVAsmPrinter::outputGlobalRequirements() {
// Abort here if not all requirements can be satisfied.
MAI->Reqs.checkSatisfiable(*ST);
for (const auto &Cap : MAI->Reqs.getMinimalCapabilities()) {
MCInst Inst;
Inst.setOpcode(SPIRV::OpCapability);
Inst.addOperand(MCOperand::createImm(Cap));
outputMCInst(Inst);
}
// Generate the final OpExtensions with strings instead of enums.
for (const auto &Ext : MAI->Reqs.getExtensions()) {
MCInst Inst;
Inst.setOpcode(SPIRV::OpExtension);
addStringImm(getSymbolicOperandMnemonic(
SPIRV::OperandCategory::ExtensionOperand, Ext),
Inst);
outputMCInst(Inst);
}
// TODO add a pseudo instr for version number.
}
void SPIRVAsmPrinter::outputExtFuncDecls() {
// Insert OpFunctionEnd after each declaration.
SmallVectorImpl<MachineInstr *>::iterator
I = MAI->getMSInstrs(SPIRV::MB_ExtFuncDecls).begin(),
E = MAI->getMSInstrs(SPIRV::MB_ExtFuncDecls).end();
for (; I != E; ++I) {
outputInstruction(*I);
if ((I + 1) == E || (*(I + 1))->getOpcode() == SPIRV::OpFunction)
outputOpFunctionEnd();
}
}
// Encode LLVM type by SPIR-V execution mode VecTypeHint.
static unsigned encodeVecTypeHint(Type *Ty) {
if (Ty->isHalfTy())
return 4;
if (Ty->isFloatTy())
return 5;
if (Ty->isDoubleTy())
return 6;
if (IntegerType *IntTy = dyn_cast<IntegerType>(Ty)) {
switch (IntTy->getIntegerBitWidth()) {
case 8:
return 0;
case 16:
return 1;
case 32:
return 2;
case 64:
return 3;
default:
llvm_unreachable("invalid integer type");
}
}
if (FixedVectorType *VecTy = dyn_cast<FixedVectorType>(Ty)) {
Type *EleTy = VecTy->getElementType();
unsigned Size = VecTy->getNumElements();
return Size << 16 | encodeVecTypeHint(EleTy);
}
llvm_unreachable("invalid type");
}
static void addOpsFromMDNode(MDNode *MDN, MCInst &Inst,
SPIRV::ModuleAnalysisInfo *MAI) {
for (const MDOperand &MDOp : MDN->operands()) {
if (auto *CMeta = dyn_cast<ConstantAsMetadata>(MDOp)) {
Constant *C = CMeta->getValue();
if (ConstantInt *Const = dyn_cast<ConstantInt>(C)) {
Inst.addOperand(MCOperand::createImm(Const->getZExtValue()));
} else if (auto *CE = dyn_cast<Function>(C)) {
Register FuncReg = MAI->getFuncReg(CE);
assert(FuncReg.isValid());
Inst.addOperand(MCOperand::createReg(FuncReg));
}
}
}
}
void SPIRVAsmPrinter::outputExecutionModeFromMDNode(
Register Reg, MDNode *Node, SPIRV::ExecutionMode::ExecutionMode EM,
unsigned ExpectMDOps, int64_t DefVal) {
MCInst Inst;
Inst.setOpcode(SPIRV::OpExecutionMode);
Inst.addOperand(MCOperand::createReg(Reg));
Inst.addOperand(MCOperand::createImm(static_cast<unsigned>(EM)));
addOpsFromMDNode(Node, Inst, MAI);
// reqd_work_group_size and work_group_size_hint require 3 operands,
// if metadata contains less operands, just add a default value
unsigned NodeSz = Node->getNumOperands();
if (ExpectMDOps > 0 && NodeSz < ExpectMDOps)
for (unsigned i = NodeSz; i < ExpectMDOps; ++i)
Inst.addOperand(MCOperand::createImm(DefVal));
outputMCInst(Inst);
}
void SPIRVAsmPrinter::outputExecutionModeFromNumthreadsAttribute(
const Register &Reg, const Attribute &Attr,
SPIRV::ExecutionMode::ExecutionMode EM) {
assert(Attr.isValid() && "Function called with an invalid attribute.");
MCInst Inst;
Inst.setOpcode(SPIRV::OpExecutionMode);
Inst.addOperand(MCOperand::createReg(Reg));
Inst.addOperand(MCOperand::createImm(static_cast<unsigned>(EM)));
SmallVector<StringRef> NumThreads;
Attr.getValueAsString().split(NumThreads, ',');
assert(NumThreads.size() == 3 && "invalid numthreads");
for (uint32_t i = 0; i < 3; ++i) {
uint32_t V;
[[maybe_unused]] bool Result = NumThreads[i].getAsInteger(10, V);
assert(!Result && "Failed to parse numthreads");
Inst.addOperand(MCOperand::createImm(V));
}
outputMCInst(Inst);
}
void SPIRVAsmPrinter::outputExecutionMode(const Module &M) {
NamedMDNode *Node = M.getNamedMetadata("spirv.ExecutionMode");
if (Node) {
for (unsigned i = 0; i < Node->getNumOperands(); i++) {
MCInst Inst;
Inst.setOpcode(SPIRV::OpExecutionMode);
addOpsFromMDNode(cast<MDNode>(Node->getOperand(i)), Inst, MAI);
outputMCInst(Inst);
}
}
for (auto FI = M.begin(), E = M.end(); FI != E; ++FI) {
const Function &F = *FI;
// Only operands of OpEntryPoint instructions are allowed to be
// <Entry Point> operands of OpExecutionMode
if (F.isDeclaration() || !isEntryPoint(F))
continue;
Register FReg = MAI->getFuncReg(&F);
assert(FReg.isValid());
if (MDNode *Node = F.getMetadata("reqd_work_group_size"))
outputExecutionModeFromMDNode(FReg, Node, SPIRV::ExecutionMode::LocalSize,
3, 1);
if (Attribute Attr = F.getFnAttribute("hlsl.numthreads"); Attr.isValid())
outputExecutionModeFromNumthreadsAttribute(
FReg, Attr, SPIRV::ExecutionMode::LocalSize);
if (MDNode *Node = F.getMetadata("work_group_size_hint"))
outputExecutionModeFromMDNode(FReg, Node,
SPIRV::ExecutionMode::LocalSizeHint, 3, 1);
if (MDNode *Node = F.getMetadata("intel_reqd_sub_group_size"))
outputExecutionModeFromMDNode(FReg, Node,
SPIRV::ExecutionMode::SubgroupSize, 0, 0);
if (MDNode *Node = F.getMetadata("vec_type_hint")) {
MCInst Inst;
Inst.setOpcode(SPIRV::OpExecutionMode);
Inst.addOperand(MCOperand::createReg(FReg));
unsigned EM = static_cast<unsigned>(SPIRV::ExecutionMode::VecTypeHint);
Inst.addOperand(MCOperand::createImm(EM));
unsigned TypeCode = encodeVecTypeHint(getMDOperandAsType(Node, 0));
Inst.addOperand(MCOperand::createImm(TypeCode));
outputMCInst(Inst);
}
if (ST->isOpenCLEnv() && !M.getNamedMetadata("spirv.ExecutionMode") &&
!M.getNamedMetadata("opencl.enable.FP_CONTRACT")) {
MCInst Inst;
Inst.setOpcode(SPIRV::OpExecutionMode);
Inst.addOperand(MCOperand::createReg(FReg));
unsigned EM = static_cast<unsigned>(SPIRV::ExecutionMode::ContractionOff);
Inst.addOperand(MCOperand::createImm(EM));
outputMCInst(Inst);
}
}
}
void SPIRVAsmPrinter::outputAnnotations(const Module &M) {
outputModuleSection(SPIRV::MB_Annotations);
// Process llvm.global.annotations special global variable.
for (auto F = M.global_begin(), E = M.global_end(); F != E; ++F) {
if ((*F).getName() != "llvm.global.annotations")
continue;
const GlobalVariable *V = &(*F);
const ConstantArray *CA = cast<ConstantArray>(V->getOperand(0));
for (Value *Op : CA->operands()) {
ConstantStruct *CS = cast<ConstantStruct>(Op);
// The first field of the struct contains a pointer to
// the annotated variable.
Value *AnnotatedVar = CS->getOperand(0)->stripPointerCasts();
if (!isa<Function>(AnnotatedVar))
report_fatal_error("Unsupported value in llvm.global.annotations");
Function *Func = cast<Function>(AnnotatedVar);
Register Reg = MAI->getFuncReg(Func);
if (!Reg.isValid()) {
std::string DiagMsg;
raw_string_ostream OS(DiagMsg);
AnnotatedVar->print(OS);
DiagMsg = "Unknown function in llvm.global.annotations: " + DiagMsg;
report_fatal_error(DiagMsg.c_str());
}
// The second field contains a pointer to a global annotation string.
GlobalVariable *GV =
cast<GlobalVariable>(CS->getOperand(1)->stripPointerCasts());
StringRef AnnotationString;
getConstantStringInfo(GV, AnnotationString);
MCInst Inst;
Inst.setOpcode(SPIRV::OpDecorate);
Inst.addOperand(MCOperand::createReg(Reg));
unsigned Dec = static_cast<unsigned>(SPIRV::Decoration::UserSemantic);
Inst.addOperand(MCOperand::createImm(Dec));
addStringImm(AnnotationString, Inst);
outputMCInst(Inst);
}
}
}
void SPIRVAsmPrinter::outputModuleSections() {
const Module *M = MMI->getModule();
// Get the global subtarget to output module-level info.
ST = static_cast<const SPIRVTargetMachine &>(TM).getSubtargetImpl();
TII = ST->getInstrInfo();
MAI = &SPIRVModuleAnalysis::MAI;
assert(ST && TII && MAI && M && "Module analysis is required");
// Output instructions according to the Logical Layout of a Module:
// 1,2. All OpCapability instructions, then optional OpExtension instructions.
outputGlobalRequirements();
// 3. Optional OpExtInstImport instructions.
outputOpExtInstImports(*M);
// 4. The single required OpMemoryModel instruction.
outputOpMemoryModel();
// 5. All entry point declarations, using OpEntryPoint.
outputEntryPoints();
// 6. Execution-mode declarations, using OpExecutionMode or OpExecutionModeId.
outputExecutionMode(*M);
// 7a. Debug: all OpString, OpSourceExtension, OpSource, and
// OpSourceContinued, without forward references.
outputDebugSourceAndStrings(*M);
// 7b. Debug: all OpName and all OpMemberName.
outputModuleSection(SPIRV::MB_DebugNames);
// 7c. Debug: all OpModuleProcessed instructions.
outputModuleSection(SPIRV::MB_DebugModuleProcessed);
// 8. All annotation instructions (all decorations).
outputAnnotations(*M);
// 9. All type declarations (OpTypeXXX instructions), all constant
// instructions, and all global variable declarations. This section is
// the first section to allow use of: OpLine and OpNoLine debug information;
// non-semantic instructions with OpExtInst.
outputModuleSection(SPIRV::MB_TypeConstVars);
// 10. All global NonSemantic.Shader.DebugInfo.100 instructions.
outputModuleSection(SPIRV::MB_NonSemanticGlobalDI);
// 11. All function declarations (functions without a body).
outputExtFuncDecls();
// 12. All function definitions (functions with a body).
// This is done in regular function output.
}
bool SPIRVAsmPrinter::doInitialization(Module &M) {
ModuleSectionsEmitted = false;
// We need to call the parent's one explicitly.
return AsmPrinter::doInitialization(M);
}
// Force static initialization.
extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeSPIRVAsmPrinter() {
RegisterAsmPrinter<SPIRVAsmPrinter> X(getTheSPIRV32Target());
RegisterAsmPrinter<SPIRVAsmPrinter> Y(getTheSPIRV64Target());
RegisterAsmPrinter<SPIRVAsmPrinter> Z(getTheSPIRVLogicalTarget());
}