From 8fcf75fcb1d7b24eeebf3a6f767d5f0bd17c4a35 Mon Sep 17 00:00:00 2001 From: ASDAlexander77 Date: Sat, 11 Jul 2026 13:18:07 +0100 Subject: [PATCH] Replace hand-rolled DI scope rewriter with mlir::AttrTypeReplacer LLVMDebugInfoHelperFixer completes each function's DISubprogramAttr with real signature DI types at lowering time (they need LLVM layout info that does not exist at MLIRGen time), then must remap every reference to the old scope. That remapping was ~450 lines of hand-written recursive location/attribute rewriting; mlir::AttrTypeReplacer does the same job generically with a single DISubprogramAttr replacement rule, rebuilding any attribute or location that contains the old scope transitively. Also fixes real defects the old rewriter had: - CallSiteLoc handling compared the new caller against getCallee() and never rewrote callee scopes, leaking stale empty-signature subprograms into DWARF as duplicate DISubprogram nodes (3-7 duplicates per test file in 00class/00funcs/02numbers --di output; gone after this change, otherwise byte-identical debug info modulo nondeterministic name hashes). - The DbgValueOp branch compared against dbgDeclare (null there), a latent null-op dereference. - Unknown Location kinds hit llvm_unreachable; AttrTypeReplacer handles all location kinds. Also removes the never-referenced LLVMDebugInfoHelperCreator and the unused stripMetadata/removeAllMetadata/walkMetadata/debugPrint helpers. Verified: --di LLVM IR compared before/after on 00funcs, 00class, 19forof_capture, 02numbers (identical except duplicate removal); full test suite 683/683; JIT smoke run with --di. Co-Authored-By: Claude Fable 5 --- .../LowerToLLVM/LLVMDebugInfoFixer.h | 517 +++--------------- 1 file changed, 61 insertions(+), 456 deletions(-) diff --git a/tslang/include/TypeScript/LowerToLLVM/LLVMDebugInfoFixer.h b/tslang/include/TypeScript/LowerToLLVM/LLVMDebugInfoFixer.h index b975e35e..01dc4485 100644 --- a/tslang/include/TypeScript/LowerToLLVM/LLVMDebugInfoFixer.h +++ b/tslang/include/TypeScript/LowerToLLVM/LLVMDebugInfoFixer.h @@ -5,15 +5,12 @@ #include "TypeScript/LowerToLLVM/LLVMDebugInfo.h" #include "TypeScript/LowerToLLVM/LLVMTypeConverterHelper.h" -#include "mlir/IR/PatternMatch.h" +#include "mlir/IR/AttrTypeSubElements.h" #include "mlir/IR/Builders.h" #include "mlir/Dialect/LLVMIR/LLVMDialect.h" -#include "llvm/ADT/TypeSwitch.h" #include "llvm/DebugInfo/DWARF/DWARFUnit.h" -#include - #define DEBUG_TYPE "llvm" using namespace mlir; @@ -22,95 +19,14 @@ namespace mlir_ts = mlir::typescript; namespace typescript { -class LLVMDebugInfoHelperCreator -{ - LLVM::LLVMFuncOp llvmFuncOp; - const LLVMTypeConverter *typeConverter; - MLIRContext *context; - CompileOptions& compileOptions; - -public: - LLVMDebugInfoHelperCreator(LLVM::LLVMFuncOp llvmFuncOp, const LLVMTypeConverter *typeConverter, CompileOptions& compileOptions) - : llvmFuncOp(llvmFuncOp), typeConverter(typeConverter), compileOptions(compileOptions) - { - context = llvmFuncOp.getContext(); - } - - auto createSubProgramAttr() -> void - { - auto location = llvmFuncOp->getLoc(); - if (auto funcLocWithSubprog = dyn_cast>(location)) - { - return; - } - - auto moduleOp = llvmFuncOp->getParentOp(); - auto moduleLocation = moduleOp->getLoc(); - if (auto funcLocWithCompileUnit = dyn_cast>(moduleLocation)) - { - auto compileUnit = funcLocWithCompileUnit.getMetadata(); - - LocationHelper lh(context); - LLVMTypeConverterHelper llvmtch(typeConverter); - LLVMDebugInfoHelper di(context, llvmtch, compileOptions); - - auto funcNameAttr = llvmFuncOp.getNameAttr(); - - // return type - auto [file, lineAndColumn] = lh.getLineAndColumnAndFile(location); - auto [line, column] = lineAndColumn; - - SmallVector resultTypes; - for (auto resType : llvmFuncOp.getResultTypes()) - { - auto diType = di.getDIType(location, {}, resType, file, line, file); - resultTypes.push_back(diType); - } - - if (llvmFuncOp.getArgumentTypes().size() > 0 && llvmFuncOp.getResultTypes().size() == 0) - { - // return type is null - resultTypes.push_back(mlir::LLVM::DINullTypeAttr()); - } - - for (auto argType : llvmFuncOp.getArgumentTypes()) - { - auto diType = di.getDIType(location, {}, argType, file, line, file); - resultTypes.push_back(diType); - } - - auto subroutineTypeAttr = mlir::LLVM::DISubroutineTypeAttr::get(context, llvm::dwarf::DW_CC_normal, resultTypes); - - SmallVector retainedNodes; // TODO: review usage of it - SmallVector annotations; // TODO: review usage of it - auto subprogramAttr = mlir::LLVM::DISubprogramAttr::get( - context, - DistinctAttr::create(mlir::UnitAttr::get(context)), - compileUnit, - compileUnit, - funcNameAttr, - funcNameAttr, - compileUnit.getFile(), - line, - line, - LLVM::DISubprogramFlags::Pure, - subroutineTypeAttr, - retainedNodes, - annotations); - - LLVM_DEBUG(llvm::dbgs() << "\n!! new prog attr: " << subprogramAttr << "\n"); - - // we do not use replaceScope here as we are fixing metadata - auto newLoc = mlir::FusedLoc::get(location.getContext(), {location}, subprogramAttr); - llvmFuncOp->setLoc(newLoc); - } - } - - void clear() { - llvmFuncOp->setLoc(mlir::UnknownLoc::get(llvmFuncOp.getContext())); - } -}; - +// The DISubprogramAttr attached at MLIRGen time carries an empty +// DISubroutineTypeAttr: the DI types of the signature need LLVM layout +// information (LLVMTypeConverter) that only exists here, at lowering time. +// This helper rebuilds the subprogram with the real signature types and remaps +// every reference to the old scope inside the function - locations, block +// arguments, and debug-intrinsic attributes. Attributes that contain the old +// subprogram as a nested scope (lexical blocks, local variables, labels, +// nested subprograms) are rebuilt transitively by AttrTypeReplacer. class LLVMDebugInfoHelperFixer { mlir_ts::FuncOp funcOp; @@ -127,385 +43,74 @@ class LLVMDebugInfoHelperFixer void fix() { auto location = funcOp->getLoc(); - if (auto funcLocWithSubprog = dyn_cast>(location)) + auto funcLocWithSubprog = dyn_cast>(location); + if (!funcLocWithSubprog) { - LocationHelper lh(context); - LLVMTypeConverterHelper llvmtch(typeConverter); - LLVMDebugInfoHelper di(context, llvmtch, compileOptions); - - auto oldMetadata = funcLocWithSubprog.getMetadata(); - auto funcNameAttr = funcOp.getNameAttr(); - - // return type - auto [file, lineAndColumn] = lh.getLineAndColumnAndFile(location); - auto [line, column] = lineAndColumn; - - SmallVector resultTypes; - for (auto resType : funcOp.getResultTypes()) - { - auto diType = di.getDIType(location, {}, resType, file, line, file); - resultTypes.push_back(diType); - } - - if (funcOp.getArgumentTypes().size() > 0 && funcOp.getResultTypes().size() == 0) - { - // return type is null - resultTypes.push_back(mlir::LLVM::DINullTypeAttr()); - } - - for (auto argType : funcOp.getArgumentTypes()) - { - auto diType = di.getDIType(location, {}, argType, file, line, file); - resultTypes.push_back(diType); - } - - auto subroutineTypeAttr = mlir::LLVM::DISubroutineTypeAttr::get(context, llvm::dwarf::DW_CC_normal, resultTypes); - SmallVector retainedNodes; // TODO: review usage of it - SmallVector annotations; // TODO: review usage of it - auto subprogramAttr = mlir::LLVM::DISubprogramAttr::get( - context, - DistinctAttr::create(mlir::UnitAttr::get(context)), - oldMetadata.getCompileUnit(), - oldMetadata.getScope(), - oldMetadata.getName(), - oldMetadata.getLinkageName(), - oldMetadata.getFile(), - oldMetadata.getLine(), - oldMetadata.getScopeLine(), - oldMetadata.getSubprogramFlags(), - subroutineTypeAttr, - retainedNodes, - annotations); - - LLVM_DEBUG(llvm::dbgs() << "\n!! new prog attr: " << subprogramAttr << "\n"); - - // we do not use replaceScope here as we are fixing metadata - funcOp->setLoc(replaceScope(location, subprogramAttr, oldMetadata)); - - fixNestedOpScope(subprogramAttr, oldMetadata); - //debugPrint(funcOp); - } - } - -private: - static void removeAllMetadata(mlir::func::FuncOp newFuncOp) { - newFuncOp->walk([&](Operation *op) { - op->setLoc(stripMetadata(op->getLoc())); - - // Strip block arguments debug info. - for (auto ®ion : op->getRegions()) { - for (auto &block : region.getBlocks()) { - for (auto &arg : block.getArguments()) { - arg.setLoc(stripMetadata(arg.getLoc())); - } - } - } - }); - } - - static mlir::Location stripMetadata(mlir::Location loc) - { - mlir::Location ret = loc; - mlir::TypeSwitch(loc) - .Case([&](mlir::FileLineColLoc loc) { - // nothing todo - }) - .Case([&](mlir::FusedLoc loc) { - SmallVector newLocs; - auto anyNewLoc = false; - for (auto subLoc : loc.getLocations()) - { - auto newSubLoc = stripMetadata(subLoc); - newLocs.push_back(newSubLoc); - anyNewLoc |= newSubLoc != subLoc; - } - - if (loc.getMetadata() || anyNewLoc) { - ret = mlir::FusedLoc::get(loc.getContext(), anyNewLoc ? newLocs : loc.getLocations()); - } - }) - .Case([&](mlir::UnknownLoc loc) { - // nothing todo - }) - .Case([&](mlir::NameLoc loc) { - auto newChildLoc = stripMetadata(loc.getChildLoc()); - if (newChildLoc != loc.getChildLoc()) { - ret = NameLoc::get(loc.getName(), newChildLoc); - } - }) - .Case([&](mlir::OpaqueLoc loc) { - auto newFallbackLoc = stripMetadata(loc.getFallbackLocation()); - if (newFallbackLoc != loc.getFallbackLocation()) { - ret = OpaqueLoc::get(loc.getContext(), newFallbackLoc); - } - }) - .Case([&](mlir::CallSiteLoc loc) { - auto newCallerLoc = stripMetadata(loc.getCaller()); - if (newCallerLoc != loc.getCaller()) { - ret = mlir::CallSiteLoc::get(loc.getCallee(), newCallerLoc); - } - }) - .Default([&](mlir::Location loc) { - llvm_unreachable("not implemented"); - }); - - return ret; - } - - mlir::LLVM::DILexicalBlockAttr recreateLexicalBlockForNewScope(mlir::LLVM::DILexicalBlockAttr lexicalBlockAttr, mlir::Attribute newScope, mlir::Attribute oldScope) { - if (lexicalBlockAttr.getScope() == oldScope) { - auto newLexicalBlockAttr = - mlir::LLVM::DILexicalBlockAttr::get( - lexicalBlockAttr.getContext(), - mlir::cast(newScope), - lexicalBlockAttr.getFile(), - lexicalBlockAttr.getLine(), - lexicalBlockAttr.getColumn()); - - // we need to fix nested scope again - fixNestedOpScope(newLexicalBlockAttr, lexicalBlockAttr); - - return newLexicalBlockAttr; - } - - return lexicalBlockAttr; - } - - static mlir::LLVM::DILocalVariableAttr recreateLocalVariableForNewScope(mlir::LLVM::DILocalVariableAttr localVarScope, mlir::Attribute newScope, mlir::Attribute oldScope) { - if (localVarScope.getScope() == oldScope) { - auto newLocalVar = mlir::LLVM::DILocalVariableAttr::get( - localVarScope.getContext(), - mlir::cast(newScope), - localVarScope.getName(), - localVarScope.getFile(), - localVarScope.getLine(), - localVarScope.getArg(), - localVarScope.getAlignInBits(), - localVarScope.getType(), - localVarScope.getFlags()); - return newLocalVar; - } - - return localVarScope; - } - - mlir::LLVM::DISubprogramAttr recreateSubprogramForNewScope(mlir::LLVM::DISubprogramAttr subprogScope, mlir::Attribute newScope, mlir::Attribute oldScope) { - if (subprogScope.getScope() == oldScope) { - SmallVector retainedNodes; // TODO: review usage of it - SmallVector annotations; // TODO: review usage of it - auto newSubprogramAttr = mlir::LLVM::DISubprogramAttr::get( - subprogScope.getContext(), - DistinctAttr::create(mlir::UnitAttr::get(subprogScope.getContext())), - subprogScope.getCompileUnit(), - mlir::cast(newScope), - subprogScope.getName(), - subprogScope.getLinkageName(), - subprogScope.getFile(), - subprogScope.getLine(), - subprogScope.getScopeLine(), - subprogScope.getSubprogramFlags(), - subprogScope.getType(), - retainedNodes, - annotations); - - // we need to fix nested scope again - fixNestedOpScope(newSubprogramAttr, subprogScope); - - return newSubprogramAttr; + return; } - return subprogScope; - } - - static mlir::LLVM::DILabelAttr recreateLabelForNewScope(mlir::LLVM::DILabelAttr labelScope, mlir::Attribute newScope, mlir::Attribute oldScope) { - if (labelScope.getScope() == oldScope) { - auto newLabel = mlir::LLVM::DILabelAttr::get( - labelScope.getContext(), - mlir::cast(newScope), - labelScope.getName(), - labelScope.getFile(), - labelScope.getLine()); - return newLabel; - } + LocationHelper lh(context); + LLVMTypeConverterHelper llvmtch(typeConverter); + LLVMDebugInfoHelper di(context, llvmtch, compileOptions); - return labelScope; - } + auto oldMetadata = funcLocWithSubprog.getMetadata(); - mlir::Attribute recreateMetadataForNewScope(mlir::Attribute currentMetadata, mlir::Attribute newScope, mlir::Attribute oldScope) { - if (auto lexicalBlockAttr = dyn_cast_or_null(currentMetadata)) { - return recreateLexicalBlockForNewScope(lexicalBlockAttr, newScope, oldScope); - } + auto [file, lineAndColumn] = lh.getLineAndColumnAndFile(location); + auto [line, column] = lineAndColumn; - if (auto localVarScope = dyn_cast_or_null(currentMetadata)) { - return recreateLocalVariableForNewScope(localVarScope, newScope, oldScope); + SmallVector resultTypes; + for (auto resType : funcOp.getResultTypes()) + { + auto diType = di.getDIType(location, {}, resType, file, line, file); + resultTypes.push_back(diType); } - if (auto subprogScope = dyn_cast_or_null(currentMetadata)) { - return recreateSubprogramForNewScope(subprogScope, newScope, oldScope); + if (funcOp.getArgumentTypes().size() > 0 && funcOp.getResultTypes().size() == 0) + { + // return type is null + resultTypes.push_back(mlir::LLVM::DINullTypeAttr()); } - if (auto labelScope = dyn_cast_or_null(currentMetadata)) { - return recreateLabelForNewScope(labelScope, newScope, oldScope); + for (auto argType : funcOp.getArgumentTypes()) + { + auto diType = di.getDIType(location, {}, argType, file, line, file); + resultTypes.push_back(diType); } - return currentMetadata; - } - - mlir::Location replaceScope(mlir::Location loc, mlir::Attribute newScope, mlir::Attribute oldScope) - { - return replaceMetadata(loc, [&](mlir::Attribute currentMetadata) -> mlir::Attribute { - if (currentMetadata == oldScope) + auto subroutineTypeAttr = mlir::LLVM::DISubroutineTypeAttr::get(context, llvm::dwarf::DW_CC_normal, resultTypes); + SmallVector retainedNodes; // TODO: review usage of it + SmallVector annotations; // TODO: review usage of it + // a fresh DistinctAttr id: references to the old subprogram outside this + // function (if any) keep the old identity instead of colliding with the + // rebuilt one at LLVM IR translation time + auto subprogramAttr = mlir::LLVM::DISubprogramAttr::get( + context, + DistinctAttr::create(mlir::UnitAttr::get(context)), + oldMetadata.getCompileUnit(), + oldMetadata.getScope(), + oldMetadata.getName(), + oldMetadata.getLinkageName(), + oldMetadata.getFile(), + oldMetadata.getLine(), + oldMetadata.getScopeLine(), + oldMetadata.getSubprogramFlags(), + subroutineTypeAttr, + retainedNodes, + annotations); + + LLVM_DEBUG(llvm::dbgs() << "\n!! new prog attr: " << subprogramAttr << "\n"); + + mlir::AttrTypeReplacer replacer; + replacer.addReplacement([&](mlir::LLVM::DISubprogramAttr attr) -> std::optional { + if (attr == oldMetadata) { - return newScope; + return subprogramAttr; } - return recreateMetadataForNewScope(currentMetadata, newScope, oldScope); - }); - } - - static mlir::Location replaceMetadata(mlir::Location loc, std::function f) - { - mlir::Location ret = loc; - mlir::TypeSwitch(loc) - .Case([&](mlir::FileLineColLoc loc) { - // nothing todo - }) - .Case([&](mlir::FusedLoc loc) { - SmallVector newLocs; - auto anyNew = false; - for (auto subLoc : loc.getLocations()) - { - auto newSubLoc = replaceMetadata(subLoc, f); - newLocs.push_back(newSubLoc); - anyNew |= newSubLoc != subLoc; - } - - auto newMetadata = f(loc.getMetadata()); - if (loc.getMetadata() != newMetadata || anyNew) - { - ret = mlir::FusedLoc::get(loc.getContext(), anyNew ? newLocs : loc.getLocations(), newMetadata); - } - }) - .Case([&](mlir::UnknownLoc loc) { - // nothing todo - }) - .Case([&](mlir::NameLoc loc) { - auto newChildLoc = replaceMetadata(loc.getChildLoc(), f); - if (newChildLoc != loc.getChildLoc()) { - ret = NameLoc::get(loc.getName(), newChildLoc); - } - }) - .Case([&](mlir::OpaqueLoc loc) { - auto newFallbackLoc = replaceMetadata(loc.getFallbackLocation(), f); - if (newFallbackLoc != loc.getFallbackLocation()) { - ret = OpaqueLoc::get(loc.getContext(), newFallbackLoc); - } - }) - .Case([&](mlir::CallSiteLoc loc) { - auto newCallerLoc = replaceMetadata(loc.getCaller(), f); - if (newCallerLoc != loc.getCallee()) { - ret = mlir::CallSiteLoc::get(loc.getCallee(), newCallerLoc); - } - }) - .Default([&](mlir::Location loc) { - LLVM_DEBUG(llvm::dbgs() << "\n!! location: " << loc << "\n"); - llvm_unreachable("not implemented"); - }); - - return ret; - } - - static void walkMetadata(mlir::Location loc, std::function f) - { - mlir::TypeSwitch(loc) - .Case([&](mlir::FileLineColLoc loc) { - // nothing todo - }) - .Case([&](mlir::FusedLoc loc) { - f(loc.getMetadata()); - for (auto subLoc : loc.getLocations()) - { - walkMetadata(subLoc, f); - } - }) - .Case([&](mlir::UnknownLoc loc) { - // nothing todo - }) - .Case([&](mlir::NameLoc loc) { - walkMetadata(loc.getChildLoc(), f); - }) - .Case([&](mlir::OpaqueLoc loc) { - walkMetadata(loc.getFallbackLocation(), f); - }) - .Case([&](mlir::CallSiteLoc loc) { - walkMetadata(loc.getCaller(), f); - }) - .Default([&](mlir::Location loc) { - llvm_unreachable("not implemented"); - }); - } - - void fixNestedOpScope(mlir::Attribute newMetadata, mlir::Attribute oldMetadata) { - funcOp.walk([&, newMetadata, oldMetadata](Operation *op) { - - LLVM_DEBUG(llvm::dbgs() << "\n!! replacing for: " << *op << "\n"); - - op->setLoc(replaceScope(op->getLoc(), newMetadata, oldMetadata)); - - // Strip block arguments debug info. - for (auto ®ion : op->getRegions()) { - for (auto &block : region.getBlocks()) { - for (auto &arg : block.getArguments()) { - arg.setLoc(replaceScope(arg.getLoc(), newMetadata, oldMetadata)); - } - } - } - - // fix metadata for llvm.dbg.declare & llvm.dbg.value - if (auto dbgDeclare = dyn_cast(op)) { - auto newLocVar = recreateLocalVariableForNewScope(dbgDeclare.getVarInfo(), newMetadata, oldMetadata); - if (newLocVar != dbgDeclare.getVarInfo()) { - dbgDeclare.setVarInfoAttr(newLocVar); - } - } else if (auto dbgValue = dyn_cast(op)) { - auto newLocVar = recreateLocalVariableForNewScope(dbgValue.getVarInfo(), newMetadata, oldMetadata); - if (newLocVar != dbgDeclare.getVarInfo()) { - dbgValue.setVarInfoAttr(newLocVar); - } - } else if (auto dbgLabel = dyn_cast(op)) { - auto newLabel = recreateLabelForNewScope(dbgLabel.getLabel(), newMetadata, oldMetadata); - if (newLabel != dbgLabel.getLabel()) { - dbgLabel.setLabelAttr(newLabel); - } - } - }); - } - - void debugPrint(mlir::func::FuncOp newFuncOp) { - - // debug - walkMetadata(newFuncOp->getLoc(), [&](mlir::Attribute metadata) { - LLVM_DEBUG(llvm::dbgs() << "\n!! metadata: " << metadata << "\n"); - }); - - newFuncOp.walk([&](Operation *op) { - - walkMetadata(op->getLoc(), [&](mlir::Attribute metadata) { - LLVM_DEBUG(llvm::dbgs() << "\n!! metadata: " << metadata << "\n"); - }); - - // Strip block arguments debug info. - for (auto ®ion : op->getRegions()) { - for (auto &block : region.getBlocks()) { - for (auto &arg : block.getArguments()) { - walkMetadata(arg.getLoc(), [&](mlir::Attribute metadata) { - LLVM_DEBUG(llvm::dbgs() << "\n!! metadata: " << metadata << "\n"); - }); - } - } - } + return std::nullopt; }); + replacer.recursivelyReplaceElementsIn(funcOp, /*replaceAttrs=*/true, /*replaceLocs=*/true, /*replaceTypes=*/false); } }; @@ -513,4 +118,4 @@ class LLVMDebugInfoHelperFixer #undef DEBUG_TYPE -#endif // MLIR_TYPESCRIPT_LOWERTOLLVMLOGIC_LLVMDEBUGINFOFIXER_H_ \ No newline at end of file +#endif // MLIR_TYPESCRIPT_LOWERTOLLVMLOGIC_LLVMDEBUGINFOFIXER_H_