-
Notifications
You must be signed in to change notification settings - Fork 12.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[SPIRV] Emitting DebugSource, DebugCompileUnit #97558
Conversation
@llvm/pr-subscribers-backend-spir-v Author: None (bwlodarcz) ChangesThis commit introduces emission of DebugSource, DebugCompileUnit from NonSemantic.Shader.DebugInfo.100 and required OpString with filename. Full diff: https://github.com/llvm/llvm-project/pull/97558.diff 7 Files Affected:
diff --git a/llvm/lib/Target/SPIRV/CMakeLists.txt b/llvm/lib/Target/SPIRV/CMakeLists.txt
index 14647e92f5d08..5f8aea5fc8d84 100644
--- a/llvm/lib/Target/SPIRV/CMakeLists.txt
+++ b/llvm/lib/Target/SPIRV/CMakeLists.txt
@@ -40,6 +40,7 @@ add_llvm_target(SPIRVCodeGen
SPIRVSubtarget.cpp
SPIRVTargetMachine.cpp
SPIRVUtils.cpp
+ SPIRVEmitNonSemanticDI.cpp
LINK_COMPONENTS
Analysis
diff --git a/llvm/lib/Target/SPIRV/SPIRV.h b/llvm/lib/Target/SPIRV/SPIRV.h
index e597a1dc8dc06..6c35a467f53be 100644
--- a/llvm/lib/Target/SPIRV/SPIRV.h
+++ b/llvm/lib/Target/SPIRV/SPIRV.h
@@ -26,6 +26,7 @@ FunctionPass *createSPIRVRegularizerPass();
FunctionPass *createSPIRVPreLegalizerPass();
FunctionPass *createSPIRVPostLegalizerPass();
ModulePass *createSPIRVEmitIntrinsicsPass(SPIRVTargetMachine *TM);
+MachineFunctionPass *createSPIRVEmitNonSemanticDIPass(SPIRVTargetMachine *TM);
InstructionSelector *
createSPIRVInstructionSelector(const SPIRVTargetMachine &TM,
const SPIRVSubtarget &Subtarget,
@@ -36,6 +37,7 @@ void initializeSPIRVConvergenceRegionAnalysisWrapperPassPass(PassRegistry &);
void initializeSPIRVPreLegalizerPass(PassRegistry &);
void initializeSPIRVPostLegalizerPass(PassRegistry &);
void initializeSPIRVEmitIntrinsicsPass(PassRegistry &);
+void initializeSPIRVEmitNonSemanticDIPass(PassRegistry &);
} // namespace llvm
#endif // LLVM_LIB_TARGET_SPIRV_SPIRV_H
diff --git a/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp b/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp
index 3206c264f99d3..f9f36cc25eec5 100644
--- a/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp
@@ -273,6 +273,8 @@ void SPIRVAsmPrinter::outputDebugSourceAndStrings(const Module &M) {
addStringImm(Str.first(), Inst);
outputMCInst(Inst);
}
+ // Output OpString.
+ outputModuleSection(SPIRV::MB_DebugStrings);
// Output OpSource.
MCInst Inst;
Inst.setOpcode(SPIRV::OpSource);
@@ -588,9 +590,11 @@ void SPIRVAsmPrinter::outputModuleSections() {
// the first section to allow use of: OpLine and OpNoLine debug information;
// non-semantic instructions with OpExtInst.
outputModuleSection(SPIRV::MB_TypeConstVars);
- // 10. All function declarations (functions without a body).
+ // 10. All global NonSemantic.Shader.DebugInfo.100 instructions.
+ outputModuleSection(SPIRV::MB_NonSemanticGlobalDI);
+ // 11. All function declarations (functions without a body).
outputExtFuncDecls();
- // 11. All function definitions (functions with a body).
+ // 12. All function definitions (functions with a body).
// This is done in regular function output.
}
diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitNonSemanticDI.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitNonSemanticDI.cpp
new file mode 100644
index 0000000000000..f630685d98ac8
--- /dev/null
+++ b/llvm/lib/Target/SPIRV/SPIRVEmitNonSemanticDI.cpp
@@ -0,0 +1,148 @@
+#include "MCTargetDesc/SPIRVBaseInfo.h"
+#include "SPIRVGlobalRegistry.h"
+#include "SPIRVRegisterInfo.h"
+#include "SPIRVTargetMachine.h"
+#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
+#include "llvm/CodeGen/MachineFunction.h"
+#include "llvm/CodeGen/MachineFunctionPass.h"
+#include "llvm/CodeGen/MachineInstr.h"
+#include "llvm/CodeGen/MachineInstrBuilder.h"
+#include "llvm/CodeGen/MachineModuleInfo.h"
+#include "llvm/CodeGen/MachineOperand.h"
+#include "llvm/IR/DebugInfoMetadata.h"
+#include "llvm/IR/Metadata.h"
+#include "llvm/PassRegistry.h"
+#include "llvm/Support/Casting.h"
+
+namespace llvm {
+struct SPIRVEmitNonSemanticDI : public MachineFunctionPass {
+ static char ID;
+ SPIRVTargetMachine *TM;
+ SPIRVEmitNonSemanticDI(SPIRVTargetMachine *TM);
+ SPIRVEmitNonSemanticDI();
+
+ bool runOnMachineFunction(MachineFunction &MF) override;
+
+private:
+ bool IsGlobalDIEmitted = false;
+ bool emitGlobalDI(MachineFunction &MF);
+};
+
+void initializeSPIRVEmitNonSemanticDIPass(PassRegistry &);
+
+FunctionPass *createSPIRVEmitNonSemanticDIPass(SPIRVTargetMachine *TM) {
+ return new SPIRVEmitNonSemanticDI(TM);
+}
+} // namespace llvm
+
+using namespace llvm;
+
+INITIALIZE_PASS(SPIRVEmitNonSemanticDI, "spirv-nonsemantic-debug-info",
+ "SPIRV NonSemantic.Shader.DebugInfo.100 emitter", false, false)
+
+char SPIRVEmitNonSemanticDI::ID = 0;
+
+SPIRVEmitNonSemanticDI::SPIRVEmitNonSemanticDI(SPIRVTargetMachine *TM)
+ : MachineFunctionPass(ID), TM(TM) {
+ initializeSPIRVEmitNonSemanticDIPass(*PassRegistry::getPassRegistry());
+}
+
+SPIRVEmitNonSemanticDI::SPIRVEmitNonSemanticDI() : MachineFunctionPass(ID) {
+ initializeSPIRVEmitNonSemanticDIPass(*PassRegistry::getPassRegistry());
+}
+
+bool SPIRVEmitNonSemanticDI::emitGlobalDI(MachineFunction &MF) {
+ MachineModuleInfo &MMI = MF.getMMI();
+ const Module *M = MMI.getModule();
+ NamedMDNode *DbgCu = M->getNamedMetadata("llvm.dbg.cu");
+ if (!DbgCu) {
+ return false;
+ }
+ std::string FilePath;
+ unsigned SourceLanguage;
+ unsigned NumOp = DbgCu->getNumOperands();
+ if (NumOp) {
+ if (const auto *CompileUnit =
+ dyn_cast<DICompileUnit>(DbgCu->getOperand(0))) {
+ DIFile *File = CompileUnit->getFile();
+ FilePath = ((File->getDirectory() + "/" + File->getFilename())).str();
+ SourceLanguage = CompileUnit->getSourceLanguage();
+ }
+ }
+ NamedMDNode *ModuleFlags = M->getNamedMetadata("llvm.module.flags");
+ int64_t DwarfVersion = 0;
+ int64_t DebugInfoVersion = 0;
+ for (auto *Op : ModuleFlags->operands()) {
+ const MDOperand &StrOp = Op->getOperand(1);
+ if (StrOp.equalsStr("Dwarf Version")) {
+ DwarfVersion =
+ cast<ConstantInt>(
+ cast<ConstantAsMetadata>(Op->getOperand(2))->getValue())
+ ->getSExtValue();
+ } else if (StrOp.equalsStr("Debug Info Version")) {
+ DebugInfoVersion =
+ cast<ConstantInt>(
+ cast<ConstantAsMetadata>(Op->getOperand(2))->getValue())
+ ->getSExtValue();
+ }
+ }
+ const SPIRVInstrInfo *TII = TM->getSubtargetImpl()->getInstrInfo();
+ const SPIRVRegisterInfo *TRI = TM->getSubtargetImpl()->getRegisterInfo();
+ const RegisterBankInfo *RBI = TM->getSubtargetImpl()->getRegBankInfo();
+ SPIRVGlobalRegistry *GR = TM->getSubtargetImpl()->getSPIRVGlobalRegistry();
+ MachineRegisterInfo &MRI = MF.getRegInfo();
+ for (MachineBasicBlock &MBB : MF) {
+ MachineIRBuilder MIRBuilder(MBB, MBB.begin());
+
+ MachineInstrBuilder MIB = MIRBuilder.buildInstr(SPIRV::OpString);
+ Register StrReg = MRI.createVirtualRegister(&SPIRV::IDRegClass);
+ MachineOperand StrRegOp = MachineOperand::CreateReg(StrReg, true);
+ MIB.add(StrRegOp);
+ addStringImm(FilePath, MIB);
+
+ const MachineInstr *VoidTyMI =
+ GR->getOrCreateSPIRVType(Type::getVoidTy(M->getContext()), MIRBuilder);
+
+ MIB = MIRBuilder.buildInstr(SPIRV::OpExtInst);
+ Register DebugSourceResIdReg =
+ MRI.createVirtualRegister(&SPIRV::IDRegClass);
+ MIB.addDef(DebugSourceResIdReg); // Result ID
+ MIB.addUse(VoidTyMI->getOperand(0).getReg()); // Result Type
+ MIB.addImm(static_cast<int64_t>(
+ SPIRV::InstructionSet::NonSemantic_Shader_DebugInfo_100)); // Set ID
+ MIB.addImm(SPIRV::NonSemanticExtInst::DebugSource); //
+ MIB.addUse(StrReg);
+ MIB.constrainAllUses(*TII, *TRI, *RBI);
+
+ Register DwarfVersionReg = GR->buildConstantInt(DwarfVersion, MIRBuilder);
+ Register DebugInfoVersionReg =
+ GR->buildConstantInt(DebugInfoVersion, MIRBuilder);
+ Register SourceLanguageReg =
+ GR->buildConstantInt(SourceLanguage, MIRBuilder);
+
+ MIB = MIRBuilder.buildInstr(SPIRV::OpExtInst);
+ Register DebugCompUnitResIdReg =
+ MRI.createVirtualRegister(&SPIRV::IDRegClass);
+ MIB.addDef(DebugCompUnitResIdReg); // Result ID
+ MIB.addUse(VoidTyMI->getOperand(0).getReg()); // Result Type
+ MIB.addImm(static_cast<int64_t>(
+ SPIRV::InstructionSet::NonSemantic_Shader_DebugInfo_100)); // Set ID
+ MIB.addImm(SPIRV::NonSemanticExtInst::DebugCompilationUnit);
+ MIB.addUse(DebugInfoVersionReg);
+ MIB.addUse(DwarfVersionReg);
+ MIB.addUse(DebugSourceResIdReg);
+ MIB.addUse(SourceLanguageReg);
+ MIB.constrainAllUses(*TII, *TRI, *RBI);
+ }
+
+ return true;
+}
+
+bool SPIRVEmitNonSemanticDI::runOnMachineFunction(MachineFunction &MF) {
+ bool Res = false;
+ if (!IsGlobalDIEmitted) {
+ Res = emitGlobalDI(MF);
+ IsGlobalDIEmitted = true;
+ }
+ return Res;
+}
diff --git a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
index ac0aa682ea4be..c69a82db14bb8 100644
--- a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
@@ -21,7 +21,6 @@
#include "SPIRVSubtarget.h"
#include "SPIRVTargetMachine.h"
#include "SPIRVUtils.h"
-#include "TargetInfo/SPIRVTargetInfo.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/CodeGen/MachineModuleInfo.h"
#include "llvm/CodeGen/TargetPassConfig.h"
@@ -427,7 +426,21 @@ void SPIRVModuleAnalysis::processOtherInstrs(const Module &M) {
if (MAI.getSkipEmission(&MI))
continue;
const unsigned OpCode = MI.getOpcode();
- if (OpCode == SPIRV::OpName || OpCode == SPIRV::OpMemberName) {
+ if (OpCode == SPIRV::OpString) {
+ collectOtherInstr(MI, MAI, SPIRV::MB_DebugStrings, IS);
+ } else if (OpCode == SPIRV::OpExtInst) {
+ MachineOperand Ins = MI.getOperand(3);
+ namespace NS = SPIRV::NonSemanticExtInst;
+ static constexpr int64_t GlobalNonSemanticDITy[] = {
+ NS::DebugSource, NS::DebugCompilationUnit};
+ bool IsGlobalDI = false;
+ for (unsigned Idx = 0; Idx < std::size(GlobalNonSemanticDITy); ++Idx) {
+ IsGlobalDI |= Ins.getImm() == GlobalNonSemanticDITy[Idx];
+ }
+ if (IsGlobalDI) {
+ collectOtherInstr(MI, MAI, SPIRV::MB_NonSemanticGlobalDI, IS);
+ }
+ } else if (OpCode == SPIRV::OpName || OpCode == SPIRV::OpMemberName) {
collectOtherInstr(MI, MAI, SPIRV::MB_DebugNames, IS);
} else if (OpCode == SPIRV::OpEntryPoint) {
collectOtherInstr(MI, MAI, SPIRV::MB_EntryPoints, IS);
diff --git a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.h b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.h
index 79226d6d93efb..024728c347e8a 100644
--- a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.h
+++ b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.h
@@ -20,7 +20,6 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/SmallVector.h"
-#include "llvm/ADT/StringMap.h"
namespace llvm {
class SPIRVSubtarget;
@@ -34,9 +33,11 @@ enum ModuleSectionType {
MB_EntryPoints, // All OpEntryPoint instructions (if any).
// MB_ExecutionModes, MB_DebugSourceAndStrings,
MB_DebugNames, // All OpName and OpMemberName intrs.
+ MB_DebugStrings, // All OpString intrs.
MB_DebugModuleProcessed, // All OpModuleProcessed instructions.
MB_Annotations, // OpDecorate, OpMemberDecorate etc.
MB_TypeConstVars, // OpTypeXXX, OpConstantXXX, and global OpVariables.
+ MB_NonSemanticGlobalDI, // OpExtInst with e.g. DebugSource, DebugTypeBasic.
MB_ExtFuncDecls, // OpFunction etc. to declare for external funcs.
NUM_MODULE_SECTIONS // Total number of sections requiring basic blocks.
};
diff --git a/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp b/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
index 52fc6f33b4ef1..bffc079981bc1 100644
--- a/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
@@ -199,6 +199,7 @@ void SPIRVPassConfig::addPreLegalizeMachineIR() {
bool SPIRVPassConfig::addLegalizeMachineIR() {
addPass(new Legalizer());
addPass(createSPIRVPostLegalizerPass());
+ addPass(createSPIRVEmitNonSemanticDIPass(&getTM<SPIRVTargetMachine>()));
return false;
}
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
c263d71
to
12854b3
Compare
@bwlodarcz Would it be possible to add a couple LIT tests demonstrating the currently expected behavior? You could create a new directory for the DebugInfo tests. Also, if you think it would be beneficial for the development of this pass and to definitely make debugging easier, you could follow the tests for the SPIRVEmitIntrinsics and create some LITs which would verify just the output of this pass using |
I also regard a comprehensive test suite as a very important part of this and following PRs, and probably we have one possible initial approach that is both easier to start with and has the benefit of preserving stability (and in ideal case certain compatibility with Khronos Translator). I mean DebugInfo section of Khronos Translator's test suite: https://github.com/KhronosGroup/SPIRV-LLVM-Translator/tree/main/test/DebugInfo @bwlodarcz Would it be possible to take some tests from that test suite on this early stage of development of the extension? Do you see LIT tests cases from the Khronos Translator's test suite relevant to this initial PR? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bwlodarcz Thank you for this PR that introduces the "SPIR-V NonSemantic Shader DebugInfo Instructions" extension.
ea8b9cb
to
f162159
Compare
@michalpaszkowski @VyacheslavLevytskyy Lit test is added. The @VyacheslavLevytskyy A lot of tests inside the Khronos Translator is using SPT (internal, textual, used only for tests) format and/or are mostly covering OpenCL.DebugInfo.100 standard which we aren't. Some mixing NonSemantic.DI and OpenCL.DI in the same test file (by separate RUN). Finding which tests are useful for us, translating them, is maybe worth an effort but the risk (of time investment) to reward (finding useful tests) ratio have, in my opinion, question mark when we compare it to just creating new set of tests. In addition to that - especially at the beginning of development there is a lot of cases which aren't covered and with broken tests (inevitable if we port them) - PR's won't be merged - so we would need to disable a lot of them. At some point it's likely that the risk-reward ratio will change. Nevertheless if someone want's to do it, go through them and add to the Backend useful ones - I'm fully supporting it as a optional side quest for everyone who want to participate in DebugInfo development. |
a6cafc5
to
3443716
Compare
Thank you @bwlodarcz for working on this! The change is now merged. |
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/159/builds/4482 Here is the relevant piece of the build log for the reference:
|
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.
…#105889) The declaration in SPIRV.h had this returning a `MachineFunctionPass *`, but the implementation returned a `FunctionPass *`. This showed up as a build error on windows, but it was clearly a mistake regardless. I also updated the pass to include SPIRV.h rather than using its own declarations for pass initialization, as this results in better errors for this kind of typo. Fixes a build break after #97558
…#105889) The declaration in SPIRV.h had this returning a `MachineFunctionPass *`, but the implementation returned a `FunctionPass *`. This showed up as a build error on windows, but it was clearly a mistake regardless. I also updated the pass to include SPIRV.h rather than using its own declarations for pass initialization, as this results in better errors for this kind of typo. Fixes a build break after #97558
…#105965) I don't know if this is the right thing to do or if the emitter should be normalizing path separators, but this gets this test to pass on windows where `sys::path::append` will use "\" instead of "/". This is another follow up to #97558, as #105889 managed to fix the build, but not all of the tests.
…llvm#105965) I don't know if this is the right thing to do or if the emitter should be normalizing path separators, but this gets this test to pass on windows where `sys::path::append` will use "\" instead of "/". This is another follow up to llvm#97558, as llvm#105889 managed to fix the build, but not all of the tests.
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.
…llvm#105889) The declaration in SPIRV.h had this returning a `MachineFunctionPass *`, but the implementation returned a `FunctionPass *`. This showed up as a build error on windows, but it was clearly a mistake regardless. I also updated the pass to include SPIRV.h rather than using its own declarations for pass initialization, as this results in better errors for this kind of typo. Fixes a build break after llvm#97558
…llvm#105965) I don't know if this is the right thing to do or if the emitter should be normalizing path separators, but this gets this test to pass on windows where `sys::path::append` will use "\" instead of "/". This is another follow up to llvm#97558, as llvm#105889 managed to fix the build, but not all of the tests.
…Shader_DebugInfo_100 are not mixed up with other OpExtInst instructions (#107007) This PR is to ensure that OpExtInst instructions generated by NonSemantic_Shader_DebugInfo_100 are not mixed up with other OpExtInst instructions. Original implementation (#97558) has introduced an issue by moving OpExtInst instruction with the 3rd operand equal to DebugSource (value 35) or DebugCompilationUnit (value 1) even if OpExtInst is not generated by NonSemantic_Shader_DebugInfo_100 implementation code. The reproducer is attached as a new test case. The code of the test case reproduces the issue, because "lgamma" has the same code (35) inside OpenCL_std as DebugSource inside NonSemantic_Shader_DebugInfo_100.
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.