diff --git a/llvm/include/llvm/Transforms/Utils/EmitChangedFuncDebugInfo.h b/llvm/include/llvm/Transforms/Utils/EmitChangedFuncDebugInfo.h new file mode 100644 index 0000000000000..8d569cd95d7f7 --- /dev/null +++ b/llvm/include/llvm/Transforms/Utils/EmitChangedFuncDebugInfo.h @@ -0,0 +1,33 @@ +//===- EmitChangedFuncDebugInfo.h - Emit Additional Debug Info -*- 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 +// +//===----------------------------------------------------------------------===// +// +/// \file +/// Emit debug info for changed or new funcs. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TRANSFORMS_UTILS_EMITCHANGEDFUNCDEBUGINFO_H +#define LLVM_TRANSFORMS_UTILS_EMITCHANGEDFUNCDEBUGINFO_H + +#include "llvm/IR/PassManager.h" + +namespace llvm { + +class Module; + +// Pass that emits late dwarf. +class EmitChangedFuncDebugInfoPass + : public PassInfoMixin { +public: + EmitChangedFuncDebugInfoPass() = default; + + PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); +}; + +} // end namespace llvm + +#endif // LLVM_TRANSFORMS_UTILS_EMITCHANGEDFUNCDEBUGINFO_H diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp index fde48a30a8203..c5daa32553766 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -1280,11 +1280,77 @@ void DwarfDebug::finishSubprogramDefinitions() { } } +void DwarfDebug::addChangedSubprograms() { + // Generate additional dwarf for functions with signature changed. + DICompileUnit *ExtraCU = nullptr; + for (DICompileUnit *CUNode : MMI->getModule()->debug_compile_units()) { + if (CUNode->getFile()->getFilename() == "") { + ExtraCU = CUNode; + break; + } + } + if (!ExtraCU) + return; + + llvm::DebugInfoFinder DIF; + DIF.processModule(*MMI->getModule()); + for (auto *ExtraSP : DIF.subprograms()) { + if (ExtraSP->getUnit() != ExtraCU) + continue; + + DISubprogram *SP = cast(ExtraSP->getScope()); + DwarfCompileUnit &Cu = getOrCreateDwarfCompileUnit(SP->getUnit()); + DIE *ScopeDIE = + DIE::get(DIEValueAllocator, dwarf::DW_TAG_inlined_subroutine); + Cu.getUnitDie().addChild(ScopeDIE); + + Cu.addString(*ScopeDIE, dwarf::DW_AT_name, ExtraSP->getName()); + if (!ExtraSP->getLinkageName().empty()) + Cu.addString(*ScopeDIE, dwarf::DW_AT_linkage_name, + ExtraSP->getLinkageName()); + + DITypeRefArray Args = ExtraSP->getType()->getTypeArray(); + + if (Args[0]) + Cu.addType(*ScopeDIE, Args[0]); + + if (ExtraSP->getType()->getCC() == llvm::dwarf::DW_CC_nocall) { + Cu.addUInt(*ScopeDIE, dwarf::DW_AT_calling_convention, + dwarf::DW_FORM_data1, llvm::dwarf::DW_CC_nocall); + } + + Cu.addFlag(*ScopeDIE, dwarf::DW_AT_artificial); + + // dereference the DIE* for DIEEntry + DIE *OriginDIE = Cu.getOrCreateSubprogramDIE(SP, nullptr); + Cu.addDIEEntry(*ScopeDIE, dwarf::DW_AT_specification, DIEEntry(*OriginDIE)); + + SmallVector ArgVars(Args.size()); + for (const DINode *DN : ExtraSP->getRetainedNodes()) { + if (const auto *DV = dyn_cast(DN)) { + uint32_t Arg = DV->getArg(); + if (Arg) + ArgVars[Arg - 1] = DV; + } + } + + for (unsigned i = 1, N = Args.size(); i < N; ++i) { + const DIType *Ty = Args[i]; + DIE &Arg = Cu.createAndAddDIE(dwarf::DW_TAG_formal_parameter, *ScopeDIE); + const DILocalVariable *DV = ArgVars[i - 1]; + Cu.addString(Arg, dwarf::DW_AT_name, DV->getName()); + Cu.addType(Arg, Ty); + } + } +} + void DwarfDebug::finalizeModuleInfo() { const TargetLoweringObjectFile &TLOF = Asm->getObjFileLowering(); finishSubprogramDefinitions(); + addChangedSubprograms(); + finishEntityDefinitions(); bool HasEmittedSplitCU = false; diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h index 1a1b28a6fc035..414abd4c7b8cf 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h @@ -565,6 +565,8 @@ class DwarfDebug : public DebugHandlerBase { void finishSubprogramDefinitions(); + void addChangedSubprograms(); + /// Finish off debug information after all functions have been /// processed. void finalizeModuleInfo(); diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp index 0d190ea448931..025a4117f5dc4 100644 --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -351,6 +351,7 @@ #include "llvm/Transforms/Utils/DXILUpgrade.h" #include "llvm/Transforms/Utils/Debugify.h" #include "llvm/Transforms/Utils/DeclareRuntimeLibcalls.h" +#include "llvm/Transforms/Utils/EmitChangedFuncDebugInfo.h" #include "llvm/Transforms/Utils/EntryExitInstrumenter.h" #include "llvm/Transforms/Utils/FixIrreducible.h" #include "llvm/Transforms/Utils/HelloWorld.h" diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp index dd73c04959732..14ec065d8aa53 100644 --- a/llvm/lib/Passes/PassBuilderPipelines.cpp +++ b/llvm/lib/Passes/PassBuilderPipelines.cpp @@ -135,6 +135,7 @@ #include "llvm/Transforms/Utils/AssumeBundleBuilder.h" #include "llvm/Transforms/Utils/CanonicalizeAliases.h" #include "llvm/Transforms/Utils/CountVisits.h" +#include "llvm/Transforms/Utils/EmitChangedFuncDebugInfo.h" #include "llvm/Transforms/Utils/EntryExitInstrumenter.h" #include "llvm/Transforms/Utils/ExtraPassManager.h" #include "llvm/Transforms/Utils/InjectTLIMappings.h" @@ -1645,9 +1646,12 @@ PassBuilder::buildModuleOptimizationPipeline(OptimizationLevel Level, if (PTO.CallGraphProfile && !LTOPreLink) MPM.addPass(CGProfilePass(isLTOPostLink(LTOPhase))); - // RelLookupTableConverterPass runs later in LTO post-link pipeline. - if (!LTOPreLink) + // RelLookupTableConverterPass and EmitChangedFuncDebugInfoPass run later in + // LTO post-link pipeline. + if (!LTOPreLink) { MPM.addPass(RelLookupTableConverterPass()); + MPM.addPass(EmitChangedFuncDebugInfoPass()); + } return MPM; } diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def index 074c328ef0931..a81cb13ace357 100644 --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -76,6 +76,7 @@ MODULE_PASS("dfsan", DataFlowSanitizerPass()) MODULE_PASS("dot-callgraph", CallGraphDOTPrinterPass()) MODULE_PASS("dxil-upgrade", DXILUpgradePass()) MODULE_PASS("elim-avail-extern", EliminateAvailableExternallyPass()) +MODULE_PASS("emit-changed-func-debuginfo", EmitChangedFuncDebugInfoPass()) MODULE_PASS("extract-blocks", BlockExtractorPass({}, false)) MODULE_PASS("expand-variadics", ExpandVariadicsPass(ExpandVariadicsMode::Disable)) diff --git a/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp b/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp index 262c902d40d2d..87b0d069ec04e 100644 --- a/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp +++ b/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp @@ -50,6 +50,7 @@ #include "llvm/IR/BasicBlock.h" #include "llvm/IR/CFG.h" #include "llvm/IR/Constants.h" +#include "llvm/IR/DIBuilder.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Dominators.h" @@ -432,6 +433,16 @@ doPromotion(Function *F, FunctionAnalysisManager &FAM, PromoteMemToReg(Allocas, DT, &AC); } + // If argument(s) are dead (hence removed) or promoted, probably the function + // does not follow standard calling convention anymore. Add DW_CC_nocall to + // DISubroutineType to inform debugger that it may not be safe to call this + // function. + DISubprogram *SP = NF->getSubprogram(); + if (SP) { + auto Temp = SP->getType()->cloneWithCC(llvm::dwarf::DW_CC_nocall); + SP->replaceType(MDNode::replaceWithPermanent(std::move(Temp))); + } + return NF; } diff --git a/llvm/lib/Transforms/Utils/CMakeLists.txt b/llvm/lib/Transforms/Utils/CMakeLists.txt index f367ca2fdf56b..72291a0c7d8b0 100644 --- a/llvm/lib/Transforms/Utils/CMakeLists.txt +++ b/llvm/lib/Transforms/Utils/CMakeLists.txt @@ -23,6 +23,7 @@ add_llvm_component_library(LLVMTransformUtils DebugSSAUpdater.cpp DeclareRuntimeLibcalls.cpp DemoteRegToStack.cpp + EmitChangedFuncDebugInfo.cpp DXILUpgrade.cpp EntryExitInstrumenter.cpp EscapeEnumerator.cpp diff --git a/llvm/lib/Transforms/Utils/EmitChangedFuncDebugInfo.cpp b/llvm/lib/Transforms/Utils/EmitChangedFuncDebugInfo.cpp new file mode 100644 index 0000000000000..ddf77e9d91d7f --- /dev/null +++ b/llvm/lib/Transforms/Utils/EmitChangedFuncDebugInfo.cpp @@ -0,0 +1,567 @@ +//==- EmitChangedFuncDebugInfoPass - Emit Additional Debug Info -*- 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 pass synthesizes a "shadow" DISubprogram carrying a changed signature +// for certain optimized functions or a compiler generated function e.g. +// foo.llvm.. The new subprogram lives in a dedicated DICompileUnit whose +// file name is "", and is attached to a dummy +// AvailableExternally function. +// +// The changed signatures rely on dbg records in the entry block. In certain +// case, e.g., not able to get proper argument type, the function will be +// skipped. Remarks provides additional information about what functions to be +// skipped and useful information. +// +// Only C-family source language is supported. Functions with varargs are +// skipped, and functions whose return type is a large by-value aggregate +// is skipped, etc. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/Utils/EmitChangedFuncDebugInfo.h" + +#include "llvm/Analysis/OptimizationRemarkEmitter.h" +#include "llvm/IR/DIBuilder.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Module.h" +#include "llvm/TargetParser/Triple.h" + +using namespace llvm; + +#define DEBUG_TYPE "emit-changed-func-debuginfo" + +// Disable/Enable switch. +static cl::opt EnableChangedFuncDBInfo( + "enable-changed-func-dbinfo", cl::Hidden, cl::init(false), + cl::desc("Enable debuginfo emission for changed func signatures")); + +static cl::opt SkipDottedFunc("skip-dotted-func", cl::Hidden, + cl::init(false), + cl::desc("Skip functions with dot")); + +// Replace all '.' with "__" (stable with opaque-lifetime inputs). +static std::string sanitizeDots(StringRef S) { + std::string Out = S.str(); + for (size_t pos = 0; (pos = Out.find('.', pos)) != std::string::npos; + pos += 2) + Out.replace(pos, 1, "__"); + return Out; +} + +// Ensure a variable name is unique among previously recorded parameters. +// If collision, append "__". +static std::string uniquifyParamName(StringRef Candidate, + ArrayRef Existing, + unsigned Idx) { + for (unsigned i = 0; i < Existing.size(); ++i) + if (auto *LV = dyn_cast(Existing[i])) + if (LV->getName() == Candidate) + return (Twine(Candidate) + "__" + Twine(Idx)).str(); + return Candidate.str(); +} + +// Walk backward in the current block to see whether LocV matches one of +// previous insn or some operand of those insns. +static bool comesFromArgViaCast(Value *LocV, Argument *Arg, Instruction &At) { + if (!LocV) + return false; + for (Instruction *Prev = At.getPrevNode(); Prev; Prev = Prev->getPrevNode()) { + // FIXME: maybe some other insns need check as well. + if (auto *Z = dyn_cast(Prev)) + if (Z->getOperand(0) == Arg && LocV == Prev) + return true; + if (auto *T = dyn_cast(Prev)) + if (T->getOperand(0) == Arg && LocV == Prev) + return true; + if (auto *T = dyn_cast(Prev)) + if (T->getOperand(0) == Arg && LocV == T->getOperand(1)) + return true; + } + return false; +} + +// Strip qualifiers/typedefs until the first pointer-type (which we keep), or +// to the base non-derived type if no pointer is found. +static DIType *stripToBaseOrFirstPointer(DIType *T) { + while (auto *DT = dyn_cast_or_null(T)) { + if (DT->getTag() == dwarf::DW_TAG_pointer_type) + return DT; + T = DT->getBaseType(); + } + return T; +} + +static DIType *createBasicType(DIBuilder &DIB, uint64_t SizeInBits) { + switch (SizeInBits) { + case 8: + return DIB.createBasicType("signed char", 8, dwarf::DW_ATE_signed_char); + case 16: + return DIB.createBasicType("short", 16, dwarf::DW_ATE_signed); + case 32: + return DIB.createBasicType("int", 32, dwarf::DW_ATE_signed); + case 64: + return DIB.createBasicType("long", 64, dwarf::DW_ATE_signed); + default: + return DIB.createBasicType("__int128", SizeInBits, dwarf::DW_ATE_signed); + } +} + +static DIType *getIntTypeFromExpr(DIBuilder &DIB, DIExpression *Expr, + DICompositeType *DTy, unsigned W, + unsigned PointerBitWidth, Function *F, + int &CoerceVal, Argument *Arg, unsigned Idx) { + for (auto Op : Expr->expr_ops()) { + if (Op.getOp() != dwarf::DW_OP_LLVM_fragment) + break; + + uint64_t BitOffset = Op.getArg(0); + uint64_t BitSize = Op.getArg(1); + uint64_t BitUpLimit = BitOffset + BitSize; + + DINodeArray Elems = DTy->getElements(); + unsigned N = Elems.size(); + + for (unsigned i = 0; i < N; ++i) { + if (auto *Elem = dyn_cast(Elems[i])) { + if (N >= 2 && i < N - 1) { + if (Elem->getOffsetInBits() <= BitOffset && + BitUpLimit <= (Elem->getOffsetInBits() + Elem->getSizeInBits())) { + CoerceVal = BitOffset >= 0 && BitOffset < PointerBitWidth ? 0 : 1; + return Elem->getBaseType(); + } + } else { + if (Elem->getOffsetInBits() <= BitOffset && + BitUpLimit <= DTy->getSizeInBits()) { + CoerceVal = BitOffset >= 0 && BitOffset < PointerBitWidth ? 0 : 1; + return Elem->getBaseType(); + } + } + } + } + } + + OptimizationRemarkEmitter ORE(F); + + ORE.emit([&]() { + return OptimizationRemark(DEBUG_TYPE, "ParamDITypeWithNocallCC", F) + << "create an int type " << ore::NV("ArgName", Arg->getName()) << "(" + << ore::NV("ArgIndex", Idx) << ")"; + }); + + return createBasicType(DIB, W); +} + +static DIType *computeParamDIType(DIBuilder &DIB, Type *Ty, DIType *Orig, + unsigned PointerBitWidth, DIExpression *Expr, + bool ByVal, Function *F, int &CoerceVal, + Argument *Arg, unsigned Idx) { + DIType *Stripped = stripToBaseOrFirstPointer(Orig); + unsigned TyBitSize = Stripped->getSizeInBits(); + + if (TyBitSize <= PointerBitWidth || ByVal) + return Orig; + + auto *Comp = cast(Stripped); + if (Comp->getTag() == dwarf::DW_TAG_union_type) { + OptimizationRemarkEmitter ORE(F); + + ORE.emit([&]() { + return OptimizationRemark(DEBUG_TYPE, "ParamDITypeWithNocallCC", F) + << "cannot handle union type " + << ore::NV("ArgName", Arg->getName()) << "(" + << ore::NV("ArgIndex", Idx) << ")"; + }); + + return nullptr; + } + + unsigned W = cast(Ty)->getBitWidth(); + return getIntTypeFromExpr(DIB, Expr, Comp, W, PointerBitWidth, F, CoerceVal, + Arg, Idx); +} + +static void pushParam(DIBuilder &DIB, DISubprogram *OldSP, DISubprogram *NewSP, + SmallVectorImpl &TypeList, + SmallVectorImpl &ArgList, DIType *Ty, + StringRef VarName, unsigned Idx) { + TypeList.push_back(Ty); + ArgList.push_back(DIB.createParameterVariable( + NewSP, VarName, Idx + 1, OldSP->getFile(), OldSP->getLine(), Ty)); +} + +// Argument collection. +static bool getOneArgDI(unsigned Idx, BasicBlock &Entry, DIBuilder &DIB, + Function *F, DISubprogram *OldSP, DISubprogram *NewSP, + SmallVectorImpl &TypeList, + SmallVectorImpl &ArgList, + unsigned PointerBitWidth) { + Argument *Arg = F->getArg(Idx); + StringRef ArgName = Arg->getName(); + Type *ArgTy = Arg->getType(); + + // If byval struct, remember its identified-name and kind to match via dbg. + StringRef ByValTypeName; + bool IsByValStruct = true; + if (ArgTy->isPointerTy() && Arg->hasByValAttr()) { + if (Type *ByValTy = F->getParamByValType(Idx)) + if (auto *ST = dyn_cast(ByValTy)) { + auto [Kind, Name] = ST->getName().split('.'); + ByValTypeName = Name; + IsByValStruct = (Kind == "struct"); + } + } + + DILocalVariable *DIVar = nullptr; + DIExpression *DIExpr = nullptr; + + // Scan the entry block for dbg records. + for (Instruction &I : Entry) { + bool Final = false; + + for (DbgRecord &DR : I.getDbgRecordRange()) { + auto *DVR = dyn_cast(&DR); + if (!DVR) + continue; + + auto *VAM = dyn_cast_or_null(DVR->getRawLocation()); + if (!VAM) + continue; + + Value *LocV = VAM->getValue(); + auto *Var = DVR->getVariable(); + if (!Var || !Var->getArg()) + continue; + + // Canonicalize through derived types stopping at first pointer. + DIType *DITy = Var->getType(); + while (auto *DTy = dyn_cast(DITy)) { + if (DTy->getTag() == dwarf::DW_TAG_pointer_type) { + DITy = DTy; + break; + } + DITy = DTy->getBaseType(); + } + + if (LocV == Arg) { + DIVar = Var; + DIExpr = DVR->getExpression(); + Final = true; + break; + } + + if (!ByValTypeName.empty()) { + // Match by byval struct/union DI type’s name/kind. + DIType *Stripped = stripToBaseOrFirstPointer(Var->getType()); + auto *Comp = dyn_cast(Stripped); + if (!Comp) + continue; + bool IsStruct = Comp->getTag() == dwarf::DW_TAG_structure_type; + if (Comp->getName() != ByValTypeName || IsStruct != IsByValStruct) + continue; + DIVar = Var; + DIExpr = DVR->getExpression(); + // Possibly we may find a true match later. + } + + if (ArgName.empty()) { + if (isa(LocV)) + continue; + + if (comesFromArgViaCast(LocV, Arg, I)) { + DIVar = Var; + DIExpr = DVR->getExpression(); + Final = true; + break; + } + + if (isa(LocV)) + continue; + } else { + // We do have an IR arg name. + // Compare base names (before dot). + StringRef ArgBase = ArgName.take_front(ArgName.find('.')); + StringRef VarBase = Var->getName(); + + if (isa(LocV)) { + if (Var->getName() != ArgBase) + continue; + DIVar = Var; + DIExpr = DVR->getExpression(); + // Possibly we may find a non poison value later. + } else if (isa(LocV)) { + if (Var->getName() != ArgName) + continue; + DIVar = Var; + DIExpr = DVR->getExpression(); + Final = true; + break; + } else if (ArgBase == VarBase) { + DIVar = Var; + DIExpr = DVR->getExpression(); + Final = true; + break; + } else if (comesFromArgViaCast(LocV, Arg, I)) { + DIVar = Var; + DIExpr = DVR->getExpression(); + Final = true; + break; + } + } + } + + if (Final) + break; + } + + if (!DIVar) { + OptimizationRemarkEmitter ORE(F); + + if (ArgTy->isIntegerTy()) { + // Probably due to argument promotion, e.g., a pointer argument becomes + // an integer. The dbg does not give a clear mapping to the argument. + // So create an int type. + auto *Ty = createBasicType(DIB, cast(ArgTy)->getBitWidth()); + pushParam(DIB, OldSP, NewSP, TypeList, ArgList, Ty, + (Twine("__") + Twine(Idx)).str(), Idx); + + ORE.emit([&]() { + return OptimizationRemark(DEBUG_TYPE, "FindNoDIVariable", F) + << "create a new int type " << ore::NV("ArgName", Arg->getName()) + << "(" << ore::NV("ArgIndex", Idx) << ")"; + }); + + return true; + } + + ORE.emit([&]() { + return OptimizationRemark(DEBUG_TYPE, "FindNoDIVariable", F) + << "cannot find pointee type " + << ore::NV("ArgName", Arg->getName()) << "(" + << ore::NV("ArgIndex", Idx) << ")"; + }); + + return false; + } + + // Compute parameter DI type from IR type + original debug type. + int CoerceVal = -1; // 0: suffix __coerce0; 1: suffix __coerce1 + DIType *ParamType = + computeParamDIType(DIB, ArgTy, DIVar->getType(), PointerBitWidth, DIExpr, + !ByValTypeName.empty(), F, CoerceVal, Arg, Idx); + if (!ParamType) + return false; + + // Decide the parameter name (sanitize + uniquify). + std::string VarName; + if (ArgName.empty()) { + VarName = DIVar->getName(); + if (CoerceVal == 0 || CoerceVal == 1) + VarName += "__coerce" + std::to_string(CoerceVal); + VarName = uniquifyParamName(VarName, ArgList, Idx); + } else { + VarName = sanitizeDots(ArgName); + } + + pushParam(DIB, OldSP, NewSP, TypeList, ArgList, ParamType, VarName, Idx); + return true; +} + +// Collect return and parameter DI information. +static bool collectReturnAndArgs(DIBuilder &DIB, Function *F, + DISubprogram *OldSP, DISubprogram *NewSP, + SmallVectorImpl &TypeList, + SmallVectorImpl &ArgList, + unsigned PointerBitWidth) { + FunctionType *FTy = F->getFunctionType(); + Type *RetTy = FTy->getReturnType(); + + if (RetTy->isVoidTy()) + TypeList.push_back(nullptr); + else + TypeList.push_back(OldSP->getType()->getTypeArray()[0]); + + BasicBlock &Entry = F->getEntryBlock(); + for (unsigned i = 0, n = FTy->getNumParams(); i < n; ++i) + if (!getOneArgDI(i, Entry, DIB, F, OldSP, NewSP, TypeList, ArgList, + PointerBitWidth)) + return false; + return true; +} + +static DICompileUnit *findChangedSigCU(Module &M) { + if (NamedMDNode *CUs = M.getNamedMetadata("llvm.dbg.cu")) + for (MDNode *Node : CUs->operands()) { + auto *CU = cast(Node); + if (CU->getFile()->getFilename() == "") + return CU; + } + return nullptr; +} + +static void generateDebugInfo(Module &M, Function *F, + unsigned PointerBitWidth) { + DICompileUnit *NewCU = findChangedSigCU(M); + DIBuilder DIB(M, /*AllowUnresolved=*/false, NewCU); + + DISubprogram *OldSP = F->getSubprogram(); + DIFile *NewFile; + + if (NewCU) { + NewFile = NewCU->getFile(); + } else { + DICompileUnit *OldCU = OldSP->getUnit(); + DIFile *OldFile = OldCU->getFile(); + NewFile = DIB.createFile("", OldFile->getDirectory()); + NewCU = DIB.createCompileUnit( + OldCU->getSourceLanguage(), NewFile, OldCU->getProducer(), + OldCU->isOptimized(), OldCU->getFlags(), OldCU->getRuntimeVersion()); + } + + SmallVector TypeList, ArgList; + DISubroutineType *SubTy = nullptr; + StringRef FuncName, LinkageName; + DINodeArray ArgArray; + DISubprogram *NewSP; + + uint8_t cc = OldSP->getType()->getCC(); + if (cc != dwarf::DW_CC_nocall) { + FuncName = F->getName(); + SubTy = OldSP->getType(); + NewSP = + DIB.createFunction(OldSP, FuncName, LinkageName, NewFile, + OldSP->getLine(), SubTy, OldSP->getScopeLine(), + DINode::FlagZero, DISubprogram::SPFlagDefinition); + for (DINode *DN : OldSP->getRetainedNodes()) { + if (auto *DV = dyn_cast(DN)) { + ArgList.push_back(DIB.createParameterVariable( + NewSP, DV->getName(), DV->getArg(), OldSP->getFile(), + OldSP->getLine(), DV->getType())); + } + } + } else { + // If the function is generated with FunctionSpecializer, its signature + // will be different from the source function. So this special function + // name is encoded for dwarf func name directly. + if (F->getName().find(".specialized.")) { + FuncName = F->getName(); + } else { + FuncName = OldSP->getName(); + if (FuncName != F->getName()) + LinkageName = F->getName(); + } + + NewSP = + DIB.createFunction(OldSP, FuncName, LinkageName, NewFile, + OldSP->getLine(), SubTy, OldSP->getScopeLine(), + DINode::FlagZero, DISubprogram::SPFlagDefinition); + + bool Success = collectReturnAndArgs(DIB, F, OldSP, NewSP, TypeList, ArgList, + PointerBitWidth); + if (!Success) { + DIB.finalize(); + return; + } + + DITypeRefArray DITypeArray = + DIB.getOrCreateTypeArray(ArrayRef{TypeList}); + SubTy = DIB.createSubroutineType(DITypeArray); + NewSP->replaceType(SubTy); + } + ArgArray = DIB.getOrCreateArray(ArgList); + + // Dummy anchor function + Function *DummyF = Function::Create(F->getFunctionType(), + GlobalValue::AvailableExternallyLinkage, + F->getName() + ".newsig", &M); + + // Provide a trivial body so the SP is marked as "defined". + BasicBlock *BB = BasicBlock::Create(M.getContext(), "entry", DummyF); + IRBuilder<> IRB(BB); + IRB.CreateUnreachable(); + DummyF->setSubprogram(NewSP); + + DIB.finalize(); + + // Add retained nodes after DIB.finalize(). Otherwise, DIB.finalize() + // might make un-intented change. + NewSP->replaceRetainedNodes(ArgArray); +} + +PreservedAnalyses EmitChangedFuncDebugInfoPass::run(Module &M, + ModuleAnalysisManager &AM) { + if (!EnableChangedFuncDBInfo) + return PreservedAnalyses::all(); + + // Only C-family + for (DICompileUnit *CU : M.debug_compile_units()) { + auto L = CU->getSourceLanguage().getUnversionedName(); + if (L != dwarf::DW_LANG_C && L != dwarf::DW_LANG_C89 && + L != dwarf::DW_LANG_C99 && L != dwarf::DW_LANG_C11 && + L != dwarf::DW_LANG_C17) + return PreservedAnalyses::all(); + } + + Triple T(M.getTargetTriple()); + + // Skip BPF for now. + if (T.isBPF()) + return PreservedAnalyses::all(); + + unsigned PointerBitWidth = T.getArchPointerBitWidth(); + + SmallVector ChangedFuncs; + for (Function &F : M) { + if (F.isIntrinsic() || F.isDeclaration()) + continue; + + DISubprogram *SP = F.getSubprogram(); + if (!SP) + continue; + + DITypeRefArray TyArray = SP->getType()->getTypeArray(); + if (TyArray.size() == 0) + continue; + + // Skip varargs + if (TyArray.size() > 1 && TyArray[TyArray.size() - 1] == nullptr) + continue; + + // For C language, only supports int/ptr types, no support for + // floating_point/vector. + unsigned i = 0; + unsigned n = F.getFunctionType()->getNumParams(); + for (i = 0; i < n; ++i) { + Type *ArgTy = F.getArg(i)->getType(); + if (ArgTy->isVectorTy() || ArgTy->isFloatingPointTy()) + break; + } + if (i != n) + continue; + + // Skip if large by-value return + DIType *RetDI = stripToBaseOrFirstPointer(TyArray[0]); + if (auto *Comp = dyn_cast_or_null(RetDI)) + if (Comp->getSizeInBits() > PointerBitWidth) + continue; + + bool FuncHasDot = F.getName().contains('.'); + if (SkipDottedFunc && FuncHasDot) + continue; + + if (!FuncHasDot && SP->getType()->getCC() != dwarf::DW_CC_nocall) + continue; + + ChangedFuncs.push_back(&F); + } + + for (Function *F : ChangedFuncs) + generateDebugInfo(M, F, PointerBitWidth); + + return ChangedFuncs.empty() ? PreservedAnalyses::all() + : PreservedAnalyses::none(); +} diff --git a/llvm/test/Other/new-pm-defaults.ll b/llvm/test/Other/new-pm-defaults.ll index 1f437a662cc96..747bb7126fd5b 100644 --- a/llvm/test/Other/new-pm-defaults.ll +++ b/llvm/test/Other/new-pm-defaults.ll @@ -295,6 +295,8 @@ ; CHECK-DEFAULT-NEXT: Running pass: CGProfilePass ; CHECK-DEFAULT-NEXT: Running pass: RelLookupTableConverterPass ; CHECK-LTO-NOT: Running pass: RelLookupTableConverterPass +; CHECK-DEFAULT-NEXT: Running pass: EmitChangedFuncDebugInfoPass +; CHECK-LTO-NOT: Running pass: EmitChangedFuncDebugInfoPass ; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on foo ; CHECK-LTO-NEXT: Running pass: CanonicalizeAliasesPass ; CHECK-LTO-NEXT: Running pass: NameAnonGlobalPass diff --git a/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll b/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll index 2d8b8f1b22091..84274926ded69 100644 --- a/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll +++ b/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll @@ -208,6 +208,7 @@ ; CHECK-POSTLINK-O-NEXT: Running pass: ConstantMergePass ; CHECK-POSTLINK-O-NEXT: Running pass: CGProfilePass ; CHECK-POSTLINK-O-NEXT: Running pass: RelLookupTableConverterPass +; CHECK-POSTLINK-O-NEXT: Running pass: EmitChangedFuncDebugInfoPass ; CHECK-EP-OPT-EARLY-NEXT: Running pass: NoOpModulePass ; CHECK-EP-OPT-LAST-NEXT: Running pass: NoOpModulePass ; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on foo diff --git a/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll b/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll index 7cacc17c7ab9a..9bdaf09deda72 100644 --- a/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll +++ b/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll @@ -192,6 +192,7 @@ ; CHECK-O-NEXT: Running pass: ConstantMergePass ; CHECK-O-NEXT: Running pass: CGProfilePass ; CHECK-O-NEXT: Running pass: RelLookupTableConverterPass +; CHECK-O-NEXT: Running pass: EmitChangedFuncDebugInfoPass ; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on foo ; CHECK-O-NEXT: Running pass: PrintModulePass diff --git a/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll b/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll index ef6cd8354ae3d..9ae9b7558dc48 100644 --- a/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll +++ b/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll @@ -201,6 +201,7 @@ ; CHECK-O-NEXT: Running pass: ConstantMergePass ; CHECK-O-NEXT: Running pass: CGProfilePass ; CHECK-O-NEXT: Running pass: RelLookupTableConverterPass +; CHECK-O-NEXT: Running pass: EmitChangedFuncDebugInfoPass ; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on foo ; CHECK-O-NEXT: Running pass: PrintModulePass diff --git a/llvm/test/Transforms/ArgumentPromotion/dbg.ll b/llvm/test/Transforms/ArgumentPromotion/dbg.ll index 6a14facfb36a2..ce86aaa3884de 100644 --- a/llvm/test/Transforms/ArgumentPromotion/dbg.ll +++ b/llvm/test/Transforms/ArgumentPromotion/dbg.ll @@ -53,7 +53,11 @@ define void @caller(ptr %Y, ptr %P) { !0 = !{i32 2, !"Debug Info Version", i32 3} !1 = !DILocation(line: 8, scope: !2) -!2 = distinct !DISubprogram(name: "test", file: !5, line: 3, isLocal: true, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: false, unit: !3, scopeLine: 3, scope: null) +!2 = distinct !DISubprogram(name: "test", file: !5, line: 3, type: !7, isLocal: true, isDefinition: true, flags: DIFlagPrototyped, isOptimized: false, unit: !3, scopeLine: 3, scope: null) !3 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, producer: "clang version 3.5.0 ", isOptimized: false, emissionKind: LineTablesOnly, file: !5) !5 = !DIFile(filename: "test.c", directory: "") !6 = !DILocation(line: 9, scope: !2) +!7 = !DISubroutineType(types: !8) +!8 = !{null, !9} +!9 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !10) +!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) diff --git a/llvm/test/Transforms/Util/changed-func-dbg-argpromotion-dwarf.ll b/llvm/test/Transforms/Util/changed-func-dbg-argpromotion-dwarf.ll new file mode 100644 index 0000000000000..7c8adcf3a4fd6 --- /dev/null +++ b/llvm/test/Transforms/Util/changed-func-dbg-argpromotion-dwarf.ll @@ -0,0 +1,81 @@ +; Check the generated DWARF debug info: +; RUN: opt -S -mtriple=x86_64-unknown-unknown -passes=emit-changed-func-debuginfo -enable-changed-func-dbinfo < %s \ +; RUN: | %llc_dwarf -filetype=obj -o - \ +; RUN: | llvm-dwarfdump - | FileCheck %s --check-prefix=DWARF +; +; REQUIRES: debug_frame +; REQUIRES: object-emission + +; Source code: +; // clang -S -emit-llvm -O3 -g test.c +; __attribute__((noinline)) static int callee(const int *p) { return *p + 42; } +; int caller(void) { +; int x = 100; +; return callee(&x); +; } + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) uwtable +define dso_local range(i32 -2147483606, -2147483648) i32 @caller() local_unnamed_addr #0 !dbg !10 { + #dbg_value(i32 100, !15, !DIExpression(), !16) + %1 = tail call fastcc i32 @callee(i32 100), !dbg !17 + ret i32 %1, !dbg !18 +} + +; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(none) uwtable +define internal fastcc range(i32 -2147483606, -2147483648) i32 @callee(i32 %0) unnamed_addr #1 !dbg !19 { + #dbg_value(ptr poison, !25, !DIExpression(), !26) + %2 = add nsw i32 %0, 42, !dbg !27 + ret i32 %2, !dbg !28 +} + +attributes #0 = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #1 = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(none) uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8} +!llvm.ident = !{!9} + +!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 22.0.0git (git@github.com:yonghong-song/llvm-project.git 8e5d24efc7dac78e8ba568dfe2fc6cfbe9663b13)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/tests/sig-change/prom", checksumkind: CSK_MD5, checksum: "f42f3fd1477418a2e17b444f656351ff") +!2 = !{i32 7, !"Dwarf Version", i32 5} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = !{i32 8, !"PIC Level", i32 2} +!6 = !{i32 7, !"PIE Level", i32 2} +!7 = !{i32 7, !"uwtable", i32 2} +!8 = !{i32 7, !"debug-info-assignment-tracking", i1 true} +!9 = !{!"clang version 22.0.0git (git@github.com:yonghong-song/llvm-project.git 8e5d24efc7dac78e8ba568dfe2fc6cfbe9663b13)"} +!10 = distinct !DISubprogram(name: "caller", scope: !1, file: !1, line: 2, type: !11, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !14, keyInstructions: true) +!11 = !DISubroutineType(types: !12) +!12 = !{!13} +!13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!14 = !{!15} +!15 = !DILocalVariable(name: "x", scope: !10, file: !1, line: 3, type: !13) +!16 = !DILocation(line: 0, scope: !10) +!17 = !DILocation(line: 4, column: 10, scope: !10, atomGroup: 3, atomRank: 2) +!18 = !DILocation(line: 4, column: 3, scope: !10, atomGroup: 3, atomRank: 1) +!19 = distinct !DISubprogram(name: "callee", scope: !1, file: !1, line: 1, type: !20, scopeLine: 1, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !24, keyInstructions: true) +!20 = !DISubroutineType(cc: DW_CC_nocall, types: !21) +!21 = !{!13, !22} +!22 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !23, size: 64) +!23 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !13) +!24 = !{!25} +!25 = !DILocalVariable(name: "p", arg: 1, scope: !19, file: !1, line: 1, type: !22) +!26 = !DILocation(line: 0, scope: !19) +!27 = !DILocation(line: 1, column: 71, scope: !19, atomGroup: 1, atomRank: 2) +!28 = !DILocation(line: 1, column: 61, scope: !19, atomGroup: 1, atomRank: 1) + +; DWARF: DW_TAG_inlined_subroutine +; DWARF-NEXT: DW_AT_name ("callee") +; DWARF-NEXT: DW_AT_type +; DWARF-SAME: "int" +; DWARF-NEXT: DW_AT_artificial (true) +; DWARF-NEXT: DW_AT_specification +; DWARF-SAME: "callee" +; DWARF-NEXT: {{^$}} +; DWARF-NEXT: DW_TAG_formal_parameter +; DWARF-NEXT: DW_AT_name ("__0") +; DWARF-NEXT: DW_AT_type +; DWARF-SAME: "int" +; DWARF-NEXT: {{^$}} +; DWARF-NEXT: NULL diff --git a/llvm/test/Transforms/Util/changed-func-dbg-argpromotion.ll b/llvm/test/Transforms/Util/changed-func-dbg-argpromotion.ll new file mode 100644 index 0000000000000..9d68a3103b056 --- /dev/null +++ b/llvm/test/Transforms/Util/changed-func-dbg-argpromotion.ll @@ -0,0 +1,96 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 +; RUN: opt -S -mtriple=x86_64-unknown-unknown -passes=emit-changed-func-debuginfo -enable-changed-func-dbinfo < %s | FileCheck %s + +; Source code: +; // clang -S -emit-llvm -O3 -g test.c +; __attribute__((noinline)) static int callee(const int *p) { return *p + 42; } +; int caller(void) { +; int x = 100; +; return callee(&x); +; } + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) uwtable +define dso_local range(i32 -2147483606, -2147483648) i32 @caller() local_unnamed_addr #0 !dbg !10 { +; CHECK-LABEL: define dso_local range(i32 -2147483606, -2147483648) i32 @caller( +; CHECK-SAME: ) local_unnamed_addr #[[ATTR0:[0-9]+]] !dbg [[DBG12:![0-9]+]] { +; CHECK-NEXT: #dbg_value(i32 100, [[META17:![0-9]+]], !DIExpression(), [[META18:![0-9]+]]) +; CHECK-NEXT: [[TMP1:%.*]] = tail call fastcc i32 @callee(i32 100), !dbg [[DBG19:![0-9]+]] +; CHECK-NEXT: ret i32 [[TMP1]], !dbg [[DBG20:![0-9]+]] +; + #dbg_value(i32 100, !15, !DIExpression(), !16) + %1 = tail call fastcc i32 @callee(i32 100), !dbg !17 + ret i32 %1, !dbg !18 +} + +; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(none) uwtable +define internal fastcc range(i32 -2147483606, -2147483648) i32 @callee(i32 %0) unnamed_addr #1 !dbg !19 { +; CHECK-LABEL: define internal fastcc range(i32 -2147483606, -2147483648) i32 @callee( +; CHECK-SAME: i32 [[TMP0:%.*]]) unnamed_addr #[[ATTR1:[0-9]+]] !dbg [[DBG21:![0-9]+]] { +; CHECK-NEXT: #dbg_value(ptr poison, [[META27:![0-9]+]], !DIExpression(), [[META28:![0-9]+]]) +; CHECK-NEXT: [[TMP2:%.*]] = add nsw i32 [[TMP0]], 42, !dbg [[DBG29:![0-9]+]] +; CHECK-NEXT: ret i32 [[TMP2]], !dbg [[DBG30:![0-9]+]] +; + #dbg_value(ptr poison, !25, !DIExpression(), !26) + %2 = add nsw i32 %0, 42, !dbg !27 + ret i32 %2, !dbg !28 +} + +attributes #0 = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #1 = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(none) uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8} +!llvm.ident = !{!9} + +!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 22.0.0git (git@github.com:yonghong-song/llvm-project.git 8e5d24efc7dac78e8ba568dfe2fc6cfbe9663b13)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/tests/sig-change/prom", checksumkind: CSK_MD5, checksum: "f42f3fd1477418a2e17b444f656351ff") +!2 = !{i32 7, !"Dwarf Version", i32 5} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = !{i32 8, !"PIC Level", i32 2} +!6 = !{i32 7, !"PIE Level", i32 2} +!7 = !{i32 7, !"uwtable", i32 2} +!8 = !{i32 7, !"debug-info-assignment-tracking", i1 true} +!9 = !{!"clang version 22.0.0git (git@github.com:yonghong-song/llvm-project.git 8e5d24efc7dac78e8ba568dfe2fc6cfbe9663b13)"} +!10 = distinct !DISubprogram(name: "caller", scope: !1, file: !1, line: 2, type: !11, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !14, keyInstructions: true) +!11 = !DISubroutineType(types: !12) +!12 = !{!13} +!13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!14 = !{!15} +!15 = !DILocalVariable(name: "x", scope: !10, file: !1, line: 3, type: !13) +!16 = !DILocation(line: 0, scope: !10) +!17 = !DILocation(line: 4, column: 10, scope: !10, atomGroup: 3, atomRank: 2) +!18 = !DILocation(line: 4, column: 3, scope: !10, atomGroup: 3, atomRank: 1) +!19 = distinct !DISubprogram(name: "callee", scope: !1, file: !1, line: 1, type: !20, scopeLine: 1, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !24, keyInstructions: true) +!20 = !DISubroutineType(cc: DW_CC_nocall, types: !21) +!21 = !{!13, !22} +!22 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !23, size: 64) +!23 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !13) +!24 = !{!25} +!25 = !DILocalVariable(name: "p", arg: 1, scope: !19, file: !1, line: 1, type: !22) +!26 = !DILocation(line: 0, scope: !19) +!27 = !DILocation(line: 1, column: 71, scope: !19, atomGroup: 1, atomRank: 2) +!28 = !DILocation(line: 1, column: 61, scope: !19, atomGroup: 1, atomRank: 1) +;. +; CHECK: [[META0:![0-9]+]] = distinct !DICompileUnit(language: DW_LANG_C11, file: [[META1:![0-9]+]], producer: "{{.*}}clang version {{.*}}", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +; CHECK: [[META1]] = !DIFile(filename: "{{.*}}test.c", directory: {{.*}}) +; CHECK: [[DBG12]] = distinct !DISubprogram(name: "caller", scope: [[META1]], file: [[META1]], line: 2, type: [[META13:![0-9]+]], scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: [[META0]], retainedNodes: [[META16:![0-9]+]], keyInstructions: true) +; CHECK: [[META13]] = !DISubroutineType(types: [[META14:![0-9]+]]) +; CHECK: [[META14]] = !{[[META15:![0-9]+]]} +; CHECK: [[META15]] = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +; CHECK: [[META16]] = !{[[META17]]} +; CHECK: [[META17]] = !DILocalVariable(name: "x", scope: [[DBG12]], file: [[META1]], line: 3, type: [[META15]]) +; CHECK: [[META18]] = !DILocation(line: 0, scope: [[DBG12]]) +; CHECK: [[DBG19]] = !DILocation(line: 4, column: 10, scope: [[DBG12]], atomGroup: 3, atomRank: 2) +; CHECK: [[DBG20]] = !DILocation(line: 4, column: 3, scope: [[DBG12]], atomGroup: 3, atomRank: 1) +; CHECK: [[DBG21]] = distinct !DISubprogram(name: "callee", scope: [[META1]], file: [[META1]], line: 1, type: [[META22:![0-9]+]], scopeLine: 1, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: [[META0]], retainedNodes: [[META26:![0-9]+]], keyInstructions: true) +; CHECK: [[META22]] = !DISubroutineType(cc: DW_CC_nocall, types: [[META23:![0-9]+]]) +; CHECK: [[META23]] = !{[[META15]], [[META24:![0-9]+]]} +; CHECK: [[META24]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: [[META25:![0-9]+]], size: 64) +; CHECK: [[META25]] = !DIDerivedType(tag: DW_TAG_const_type, baseType: [[META15]]) +; CHECK: [[META26]] = !{[[META27]]} +; CHECK: [[META27]] = !DILocalVariable(name: "p", arg: 1, scope: [[DBG21]], file: [[META1]], line: 1, type: [[META24]]) +; CHECK: [[META28]] = !DILocation(line: 0, scope: [[DBG21]]) +; CHECK: [[DBG29]] = !DILocation(line: 1, column: 71, scope: [[DBG21]], atomGroup: 1, atomRank: 2) +; CHECK: [[DBG30]] = !DILocation(line: 1, column: 61, scope: [[DBG21]], atomGroup: 1, atomRank: 1) +;. diff --git a/llvm/test/Transforms/Util/changed-func-dbg-deadarg-dwarf.ll b/llvm/test/Transforms/Util/changed-func-dbg-deadarg-dwarf.ll new file mode 100644 index 0000000000000..1fab37e36d0c7 --- /dev/null +++ b/llvm/test/Transforms/Util/changed-func-dbg-deadarg-dwarf.ll @@ -0,0 +1,106 @@ +; Check the generated DWARF debug info: +; RUN: opt -S -mtriple=x86_64-unknown-unknown -passes=emit-changed-func-debuginfo -enable-changed-func-dbinfo < %s \ +; RUN: | %llc_dwarf -filetype=obj -o - \ +; RUN: | llvm-dwarfdump - | FileCheck %s --check-prefix=DWARF +; +; REQUIRES: debug_frame +; REQUIRES: object-emission + +; Source code: +; // clang -O2 -S -emit-llvm -g test.c +; struct t { int a; }; +; char *tar(struct t *a, struct t *d); +; __attribute__((noinline)) static char * foo(struct t *a, struct t *d, int b) +; { +; return tar(a, d); +; } +; char *bar(struct t *a, struct t *d) +; { +; return foo(a, d, 1); +; } + +; Function Attrs: nounwind uwtable +define dso_local ptr @bar(ptr noundef %0, ptr noundef %1) local_unnamed_addr #0 !dbg !10 { + #dbg_value(ptr %0, !21, !DIExpression(), !23) + #dbg_value(ptr %1, !22, !DIExpression(), !23) + %3 = tail call fastcc ptr @foo(ptr noundef %0, ptr noundef %1), !dbg !24 + ret ptr %3, !dbg !25 +} + +; Function Attrs: noinline nounwind uwtable +define internal fastcc ptr @foo(ptr noundef %0, ptr noundef %1) unnamed_addr #1 !dbg !26 { + #dbg_value(ptr %0, !30, !DIExpression(), !33) + #dbg_value(ptr %1, !31, !DIExpression(), !33) + #dbg_value(i32 poison, !32, !DIExpression(), !33) + %3 = tail call ptr @tar(ptr noundef %0, ptr noundef %1) #3, !dbg !34 + ret ptr %3, !dbg !35 +} + +declare !dbg !36 ptr @tar(ptr noundef, ptr noundef) local_unnamed_addr #2 + +attributes #0 = { nounwind uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #1 = { noinline nounwind uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #2 = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #3 = { nounwind } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8} +!llvm.ident = !{!9} + +!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 22.0.0git (git@github.com:yonghong-song/llvm-project.git 8e5d24efc7dac78e8ba568dfe2fc6cfbe9663b13)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/tests/sig-change/deadarg", checksumkind: CSK_MD5, checksum: "54bc89245cb23f69a8eb94fe2fb50a09") +!2 = !{i32 7, !"Dwarf Version", i32 5} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = !{i32 8, !"PIC Level", i32 2} +!6 = !{i32 7, !"PIE Level", i32 2} +!7 = !{i32 7, !"uwtable", i32 2} +!8 = !{i32 7, !"debug-info-assignment-tracking", i1 true} +!9 = !{!"clang version 22.0.0git (git@github.com:yonghong-song/llvm-project.git 8e5d24efc7dac78e8ba568dfe2fc6cfbe9663b13)"} +!10 = distinct !DISubprogram(name: "bar", scope: !1, file: !1, line: 7, type: !11, scopeLine: 8, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !20, keyInstructions: true) +!11 = !DISubroutineType(types: !12) +!12 = !{!13, !15, !15} +!13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) +!14 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char) +!15 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !16, size: 64) +!16 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t", file: !1, line: 1, size: 32, elements: !17) +!17 = !{!18} +!18 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !16, file: !1, line: 1, baseType: !19, size: 32) +!19 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!20 = !{!21, !22} +!21 = !DILocalVariable(name: "a", arg: 1, scope: !10, file: !1, line: 7, type: !15) +!22 = !DILocalVariable(name: "d", arg: 2, scope: !10, file: !1, line: 7, type: !15) +!23 = !DILocation(line: 0, scope: !10) +!24 = !DILocation(line: 9, column: 10, scope: !10, atomGroup: 1, atomRank: 2) +!25 = !DILocation(line: 9, column: 3, scope: !10, atomGroup: 1, atomRank: 1) +!26 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 3, type: !27, scopeLine: 4, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !29, keyInstructions: true) +!27 = !DISubroutineType(cc: DW_CC_nocall, types: !28) +!28 = !{!13, !15, !15, !19} +!29 = !{!30, !31, !32} +!30 = !DILocalVariable(name: "a", arg: 1, scope: !26, file: !1, line: 3, type: !15) +!31 = !DILocalVariable(name: "d", arg: 2, scope: !26, file: !1, line: 3, type: !15) +!32 = !DILocalVariable(name: "b", arg: 3, scope: !26, file: !1, line: 3, type: !19) +!33 = !DILocation(line: 0, scope: !26) +!34 = !DILocation(line: 5, column: 10, scope: !26, atomGroup: 1, atomRank: 2) +!35 = !DILocation(line: 5, column: 3, scope: !26, atomGroup: 1, atomRank: 1) +!36 = !DISubprogram(name: "tar", scope: !1, file: !1, line: 2, type: !11, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) + +; DWARF: DW_TAG_inlined_subroutine +; DWARF-NEXT: DW_AT_name ("foo") +; DWARF-NEXT: DW_AT_type +; DWARF-SAME: "char *" +; DWARF-NEXT: DW_AT_artificial (true) +; DWARF-NEXT: DW_AT_specification +; DWARF-SAME: "foo" +; DWARF-NEXT: {{^$}} +; DWARF-NEXT: DW_TAG_formal_parameter +; DWARF-NEXT: DW_AT_name ("a") +; DWARF-NEXT: DW_AT_type +; DWARF-SAME: "t *" +; DWARF-NEXT: {{^$}} +; DWARF-NEXT: DW_TAG_formal_parameter +; DWARF-NEXT: DW_AT_name ("d") +; DWARF-NEXT: DW_AT_type +; DWARF-SAME: "t *" +; DWARF-NEXT: {{^$}} +; DWARF-NEXT: NULL diff --git a/llvm/test/Transforms/Util/changed-func-dbg-deadarg.ll b/llvm/test/Transforms/Util/changed-func-dbg-deadarg.ll new file mode 100644 index 0000000000000..f92b0f128a1db --- /dev/null +++ b/llvm/test/Transforms/Util/changed-func-dbg-deadarg.ll @@ -0,0 +1,126 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 +; RUN: opt -S -mtriple=x86_64-unknown-unknown -passes=emit-changed-func-debuginfo -enable-changed-func-dbinfo < %s | FileCheck %s + +; Source code: +; // clang -O2 -S -emit-llvm -g test.c +; struct t { int a; }; +; char *tar(struct t *a, struct t *d); +; __attribute__((noinline)) static char * foo(struct t *a, struct t *d, int b) +; { +; return tar(a, d); +; } +; char *bar(struct t *a, struct t *d) +; { +; return foo(a, d, 1); +; } + +; Function Attrs: nounwind uwtable +define dso_local ptr @bar(ptr noundef %0, ptr noundef %1) local_unnamed_addr #0 !dbg !10 { +; CHECK-LABEL: define dso_local ptr @bar( +; CHECK-SAME: ptr noundef [[TMP0:%.*]], ptr noundef [[TMP1:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] !dbg [[DBG12:![0-9]+]] { +; CHECK-NEXT: #dbg_value(ptr [[TMP0]], [[META23:![0-9]+]], !DIExpression(), [[META25:![0-9]+]]) +; CHECK-NEXT: #dbg_value(ptr [[TMP1]], [[META24:![0-9]+]], !DIExpression(), [[META25]]) +; CHECK-NEXT: [[TMP3:%.*]] = tail call fastcc ptr @foo(ptr noundef [[TMP0]], ptr noundef [[TMP1]]), !dbg [[DBG26:![0-9]+]] +; CHECK-NEXT: ret ptr [[TMP3]], !dbg [[DBG27:![0-9]+]] +; + #dbg_value(ptr %0, !21, !DIExpression(), !23) + #dbg_value(ptr %1, !22, !DIExpression(), !23) + %3 = tail call fastcc ptr @foo(ptr noundef %0, ptr noundef %1), !dbg !24 + ret ptr %3, !dbg !25 +} + +; Function Attrs: noinline nounwind uwtable +define internal fastcc ptr @foo(ptr noundef %0, ptr noundef %1) unnamed_addr #1 !dbg !26 { +; CHECK-LABEL: define internal fastcc ptr @foo( +; CHECK-SAME: ptr noundef [[TMP0:%.*]], ptr noundef [[TMP1:%.*]]) unnamed_addr #[[ATTR1:[0-9]+]] !dbg [[DBG28:![0-9]+]] { +; CHECK-NEXT: #dbg_value(ptr [[TMP0]], [[META32:![0-9]+]], !DIExpression(), [[META35:![0-9]+]]) +; CHECK-NEXT: #dbg_value(ptr [[TMP1]], [[META33:![0-9]+]], !DIExpression(), [[META35]]) +; CHECK-NEXT: #dbg_value(i32 poison, [[META34:![0-9]+]], !DIExpression(), [[META35]]) +; CHECK-NEXT: [[TMP3:%.*]] = tail call ptr @tar(ptr noundef [[TMP0]], ptr noundef [[TMP1]]) #[[ATTR3:[0-9]+]], !dbg [[DBG36:![0-9]+]] +; CHECK-NEXT: ret ptr [[TMP3]], !dbg [[DBG37:![0-9]+]] +; + #dbg_value(ptr %0, !30, !DIExpression(), !33) + #dbg_value(ptr %1, !31, !DIExpression(), !33) + #dbg_value(i32 poison, !32, !DIExpression(), !33) + %3 = tail call ptr @tar(ptr noundef %0, ptr noundef %1) #3, !dbg !34 + ret ptr %3, !dbg !35 +} + +declare !dbg !36 ptr @tar(ptr noundef, ptr noundef) local_unnamed_addr #2 + +attributes #0 = { nounwind uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #1 = { noinline nounwind uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #2 = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #3 = { nounwind } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8} +!llvm.ident = !{!9} + +!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 22.0.0git (git@github.com:yonghong-song/llvm-project.git 8e5d24efc7dac78e8ba568dfe2fc6cfbe9663b13)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/tests/sig-change/deadarg", checksumkind: CSK_MD5, checksum: "54bc89245cb23f69a8eb94fe2fb50a09") +!2 = !{i32 7, !"Dwarf Version", i32 5} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = !{i32 8, !"PIC Level", i32 2} +!6 = !{i32 7, !"PIE Level", i32 2} +!7 = !{i32 7, !"uwtable", i32 2} +!8 = !{i32 7, !"debug-info-assignment-tracking", i1 true} +!9 = !{!"clang version 22.0.0git (git@github.com:yonghong-song/llvm-project.git 8e5d24efc7dac78e8ba568dfe2fc6cfbe9663b13)"} +!10 = distinct !DISubprogram(name: "bar", scope: !1, file: !1, line: 7, type: !11, scopeLine: 8, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !20, keyInstructions: true) +!11 = !DISubroutineType(types: !12) +!12 = !{!13, !15, !15} +!13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) +!14 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char) +!15 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !16, size: 64) +!16 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t", file: !1, line: 1, size: 32, elements: !17) +!17 = !{!18} +!18 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !16, file: !1, line: 1, baseType: !19, size: 32) +!19 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!20 = !{!21, !22} +!21 = !DILocalVariable(name: "a", arg: 1, scope: !10, file: !1, line: 7, type: !15) +!22 = !DILocalVariable(name: "d", arg: 2, scope: !10, file: !1, line: 7, type: !15) +!23 = !DILocation(line: 0, scope: !10) +!24 = !DILocation(line: 9, column: 10, scope: !10, atomGroup: 1, atomRank: 2) +!25 = !DILocation(line: 9, column: 3, scope: !10, atomGroup: 1, atomRank: 1) +!26 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 3, type: !27, scopeLine: 4, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !29, keyInstructions: true) +!27 = !DISubroutineType(cc: DW_CC_nocall, types: !28) +!28 = !{!13, !15, !15, !19} +!29 = !{!30, !31, !32} +!30 = !DILocalVariable(name: "a", arg: 1, scope: !26, file: !1, line: 3, type: !15) +!31 = !DILocalVariable(name: "d", arg: 2, scope: !26, file: !1, line: 3, type: !15) +!32 = !DILocalVariable(name: "b", arg: 3, scope: !26, file: !1, line: 3, type: !19) +!33 = !DILocation(line: 0, scope: !26) +!34 = !DILocation(line: 5, column: 10, scope: !26, atomGroup: 1, atomRank: 2) +!35 = !DILocation(line: 5, column: 3, scope: !26, atomGroup: 1, atomRank: 1) +!36 = !DISubprogram(name: "tar", scope: !1, file: !1, line: 2, type: !11, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) +;. +; CHECK: [[META0:![0-9]+]] = distinct !DICompileUnit(language: DW_LANG_C11, file: [[META1:![0-9]+]], producer: "{{.*}}clang version {{.*}}", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +; CHECK: [[META1]] = !DIFile(filename: "{{.*}}test.c", directory: {{.*}}) +; CHECK: [[DBG12]] = distinct !DISubprogram(name: "bar", scope: [[META1]], file: [[META1]], line: 7, type: [[META13:![0-9]+]], scopeLine: 8, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: [[META0]], retainedNodes: [[META22:![0-9]+]], keyInstructions: true) +; CHECK: [[META13]] = !DISubroutineType(types: [[META14:![0-9]+]]) +; CHECK: [[META14]] = !{[[META15:![0-9]+]], [[META17:![0-9]+]], [[META17]]} +; CHECK: [[META15]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: [[META16:![0-9]+]], size: 64) +; CHECK: [[META16]] = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char) +; CHECK: [[META17]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: [[META18:![0-9]+]], size: 64) +; CHECK: [[META18]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t", file: [[META1]], line: 1, size: 32, elements: [[META19:![0-9]+]]) +; CHECK: [[META19]] = !{[[META20:![0-9]+]]} +; CHECK: [[META20]] = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: [[META18]], file: [[META1]], line: 1, baseType: [[META21:![0-9]+]], size: 32) +; CHECK: [[META21]] = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +; CHECK: [[META22]] = !{[[META23]], [[META24]]} +; CHECK: [[META23]] = !DILocalVariable(name: "a", arg: 1, scope: [[DBG12]], file: [[META1]], line: 7, type: [[META17]]) +; CHECK: [[META24]] = !DILocalVariable(name: "d", arg: 2, scope: [[DBG12]], file: [[META1]], line: 7, type: [[META17]]) +; CHECK: [[META25]] = !DILocation(line: 0, scope: [[DBG12]]) +; CHECK: [[DBG26]] = !DILocation(line: 9, column: 10, scope: [[DBG12]], atomGroup: 1, atomRank: 2) +; CHECK: [[DBG27]] = !DILocation(line: 9, column: 3, scope: [[DBG12]], atomGroup: 1, atomRank: 1) +; CHECK: [[DBG28]] = distinct !DISubprogram(name: "foo", scope: [[META1]], file: [[META1]], line: 3, type: [[META29:![0-9]+]], scopeLine: 4, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: [[META0]], retainedNodes: [[META31:![0-9]+]], keyInstructions: true) +; CHECK: [[META29]] = !DISubroutineType(cc: DW_CC_nocall, types: [[META30:![0-9]+]]) +; CHECK: [[META30]] = !{[[META15]], [[META17]], [[META17]], [[META21]]} +; CHECK: [[META31]] = !{[[META32]], [[META33]], [[META34]]} +; CHECK: [[META32]] = !DILocalVariable(name: "a", arg: 1, scope: [[DBG28]], file: [[META1]], line: 3, type: [[META17]]) +; CHECK: [[META33]] = !DILocalVariable(name: "d", arg: 2, scope: [[DBG28]], file: [[META1]], line: 3, type: [[META17]]) +; CHECK: [[META34]] = !DILocalVariable(name: "b", arg: 3, scope: [[DBG28]], file: [[META1]], line: 3, type: [[META21]]) +; CHECK: [[META35]] = !DILocation(line: 0, scope: [[DBG28]]) +; CHECK: [[DBG36]] = !DILocation(line: 5, column: 10, scope: [[DBG28]], atomGroup: 1, atomRank: 2) +; CHECK: [[DBG37]] = !DILocation(line: 5, column: 3, scope: [[DBG28]], atomGroup: 1, atomRank: 1) +;. diff --git a/llvm/test/Transforms/Util/changed-func-dbg-struct-16B-dwarf.ll b/llvm/test/Transforms/Util/changed-func-dbg-struct-16B-dwarf.ll new file mode 100644 index 0000000000000..c12c358a196f5 --- /dev/null +++ b/llvm/test/Transforms/Util/changed-func-dbg-struct-16B-dwarf.ll @@ -0,0 +1,100 @@ +; Check the generated DWARF debug info: +; RUN: opt -S -mtriple=x86_64-unknown-unknown -passes=emit-changed-func-debuginfo -enable-changed-func-dbinfo < %s \ +; RUN: | %llc_dwarf -filetype=obj -o - \ +; RUN: | llvm-dwarfdump - | FileCheck %s --check-prefix=DWARF +; +; REQUIRES: debug_frame +; REQUIRES: object-emission + +; Source code: +; // clang -O2 -S -emit-llvm -g test1.c +; struct t { long a; long b; }; +; __attribute__((noinline)) static int foo(struct t arg, int a) { return arg.a * arg.b; } +; int bar(struct t arg) { +; return foo(arg, 1); +; } + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) uwtable +define dso_local i32 @bar(i64 %0, i64 %1) local_unnamed_addr #0 !dbg !14 { + #dbg_value(i64 %0, !24, !DIExpression(DW_OP_LLVM_fragment, 0, 64), !25) + #dbg_value(i64 %1, !24, !DIExpression(DW_OP_LLVM_fragment, 64, 64), !25) + %3 = tail call fastcc i32 @foo(i64 %0, i64 %1), !dbg !26 + ret i32 %3, !dbg !27 +} + +; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(none) uwtable +define internal fastcc i32 @foo(i64 %0, i64 %1) unnamed_addr #1 !dbg !28 { + #dbg_value(i64 %0, !32, !DIExpression(DW_OP_LLVM_fragment, 0, 64), !34) + #dbg_value(i64 %1, !32, !DIExpression(DW_OP_LLVM_fragment, 64, 64), !34) + #dbg_value(i32 poison, !33, !DIExpression(), !34) + %3 = mul nsw i64 %1, %0, !dbg !35 + %4 = trunc i64 %3 to i32, !dbg !36 + ret i32 %4, !dbg !37 +} + +attributes #0 = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #1 = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(none) uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8} +!llvm.ident = !{!9} +!llvm.errno.tbaa = !{!10} + +!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 22.0.0git (git@github.com:yonghong-song/llvm-project.git 2bb68bb783927bdc2b54e64aea1b78ba598a3349)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test1.c", directory: "/home/yhs/tests/sig-change/struct16B", checksumkind: CSK_MD5, checksum: "c01b6b6ca539b790114bf7472cfb761a") +!2 = !{i32 7, !"Dwarf Version", i32 5} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = !{i32 8, !"PIC Level", i32 2} +!6 = !{i32 7, !"PIE Level", i32 2} +!7 = !{i32 7, !"uwtable", i32 2} +!8 = !{i32 7, !"debug-info-assignment-tracking", i1 true} +!9 = !{!"clang version 22.0.0git (git@github.com:yonghong-song/llvm-project.git 2bb68bb783927bdc2b54e64aea1b78ba598a3349)"} +!10 = !{!11, !11, i64 0} +!11 = !{!"int", !12, i64 0} +!12 = !{!"omnipotent char", !13, i64 0} +!13 = !{!"Simple C/C++ TBAA"} +!14 = distinct !DISubprogram(name: "bar", scope: !1, file: !1, line: 3, type: !15, scopeLine: 3, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !23, keyInstructions: true) +!15 = !DISubroutineType(types: !16) +!16 = !{!17, !18} +!17 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!18 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t", file: !1, line: 1, size: 128, elements: !19) +!19 = !{!20, !22} +!20 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !18, file: !1, line: 1, baseType: !21, size: 64) +!21 = !DIBasicType(name: "long", size: 64, encoding: DW_ATE_signed) +!22 = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: !18, file: !1, line: 1, baseType: !21, size: 64, offset: 64) +!23 = !{!24} +!24 = !DILocalVariable(name: "arg", arg: 1, scope: !14, file: !1, line: 3, type: !18) +!25 = !DILocation(line: 0, scope: !14) +!26 = !DILocation(line: 4, column: 10, scope: !14, atomGroup: 1, atomRank: 2) +!27 = !DILocation(line: 4, column: 3, scope: !14, atomGroup: 1, atomRank: 1) +!28 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 2, type: !29, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !31, keyInstructions: true) +!29 = !DISubroutineType(cc: DW_CC_nocall, types: !30) +!30 = !{!17, !18, !17} +!31 = !{!32, !33} +!32 = !DILocalVariable(name: "arg", arg: 1, scope: !28, file: !1, line: 2, type: !18) +!33 = !DILocalVariable(name: "a", arg: 2, scope: !28, file: !1, line: 2, type: !17) +!34 = !DILocation(line: 0, scope: !28) +!35 = !DILocation(line: 2, column: 78, scope: !28, atomGroup: 1, atomRank: 3) +!36 = !DILocation(line: 2, column: 72, scope: !28, atomGroup: 1, atomRank: 2) +!37 = !DILocation(line: 2, column: 65, scope: !28, atomGroup: 1, atomRank: 1) + +; DWARF: DW_TAG_inlined_subroutine +; DWARF-NEXT: DW_AT_name ("foo") +; DWARF-NEXT: DW_AT_type +; DWARF-SAME: "int" +; DWARF-NEXT: DW_AT_artificial (true) +; DWARF-NEXT: DW_AT_specification +; DWARF-SAME: "foo" +; DWARF-NEXT: {{^$}} +; DWARF-NEXT: DW_TAG_formal_parameter +; DWARF-NEXT: DW_AT_name ("arg__coerce0") +; DWARF-NEXT: DW_AT_type +; DWARF-SAME: "long" +; DWARF-NEXT: {{^$}} +; DWARF-NEXT: DW_TAG_formal_parameter +; DWARF-NEXT: DW_AT_name ("arg__coerce1") +; DWARF-NEXT: DW_AT_type +; DWARF-SAME: "long" +; DWARF-NEXT: {{^$}} +; DWARF-NEXT: NULL diff --git a/llvm/test/Transforms/Util/changed-func-dbg-struct-16B.ll b/llvm/test/Transforms/Util/changed-func-dbg-struct-16B.ll new file mode 100644 index 0000000000000..5db1a1bbbd5b7 --- /dev/null +++ b/llvm/test/Transforms/Util/changed-func-dbg-struct-16B.ll @@ -0,0 +1,71 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 +; RUN: opt -S -mtriple=x86_64-unknown-unknown -passes=emit-changed-func-debuginfo -enable-changed-func-dbinfo < %s | FileCheck %s + +; Source code: +; // clang -O2 -S -emit-llvm -g test1.c +; struct t { long a; long b; }; +; long foo(struct t arg) { +; return arg.a * arg.b; +; } + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) uwtable +define dso_local i64 @foo(i64 %0, i64 %1) local_unnamed_addr #0 !dbg !10 { +; CHECK-LABEL: define dso_local i64 @foo( +; CHECK-SAME: i64 [[TMP0:%.*]], i64 [[TMP1:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] !dbg [[DBG12:![0-9]+]] { +; CHECK-NEXT: #dbg_value(i64 [[TMP0]], [[META21:![0-9]+]], !DIExpression(DW_OP_LLVM_fragment, 0, 64), [[META22:![0-9]+]]) +; CHECK-NEXT: #dbg_value(i64 [[TMP1]], [[META21]], !DIExpression(DW_OP_LLVM_fragment, 64, 64), [[META22]]) +; CHECK-NEXT: [[TMP3:%.*]] = mul nsw i64 [[TMP1]], [[TMP0]], !dbg [[DBG23:![0-9]+]] +; CHECK-NEXT: ret i64 [[TMP3]], !dbg [[DBG24:![0-9]+]] +; + #dbg_value(i64 %0, !19, !DIExpression(DW_OP_LLVM_fragment, 0, 64), !20) + #dbg_value(i64 %1, !19, !DIExpression(DW_OP_LLVM_fragment, 64, 64), !20) + %3 = mul nsw i64 %1, %0, !dbg !21 + ret i64 %3, !dbg !22 +} + +attributes #0 = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8} +!llvm.ident = !{!9} + +!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 22.0.0git (git@github.com:yonghong-song/llvm-project.git 8e5d24efc7dac78e8ba568dfe2fc6cfbe9663b13)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test1.c", directory: "/tmp/home/yhs/tests/sig-change/struct", checksumkind: CSK_MD5, checksum: "bd0d0ce5cc67e004962a79d888a27468") +!2 = !{i32 7, !"Dwarf Version", i32 5} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = !{i32 8, !"PIC Level", i32 2} +!6 = !{i32 7, !"PIE Level", i32 2} +!7 = !{i32 7, !"uwtable", i32 2} +!8 = !{i32 7, !"debug-info-assignment-tracking", i1 true} +!9 = !{!"clang version 22.0.0git (git@github.com:yonghong-song/llvm-project.git 8e5d24efc7dac78e8ba568dfe2fc6cfbe9663b13)"} +!10 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 2, type: !11, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !18, keyInstructions: true) +!11 = !DISubroutineType(types: !12) +!12 = !{!13, !14} +!13 = !DIBasicType(name: "long", size: 64, encoding: DW_ATE_signed) +!14 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t", file: !1, line: 1, size: 128, elements: !15) +!15 = !{!16, !17} +!16 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !14, file: !1, line: 1, baseType: !13, size: 64) +!17 = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: !14, file: !1, line: 1, baseType: !13, size: 64, offset: 64) +!18 = !{!19} +!19 = !DILocalVariable(name: "arg", arg: 1, scope: !10, file: !1, line: 2, type: !14) +!20 = !DILocation(line: 0, scope: !10) +!21 = !DILocation(line: 3, column: 16, scope: !10, atomGroup: 1, atomRank: 2) +!22 = !DILocation(line: 3, column: 3, scope: !10, atomGroup: 1, atomRank: 1) +;. +; CHECK: [[META0:![0-9]+]] = distinct !DICompileUnit(language: DW_LANG_C11, file: [[META1:![0-9]+]], producer: "{{.*}}clang version {{.*}}", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +; CHECK: [[META1]] = !DIFile(filename: "{{.*}}test1.c", directory: {{.*}}) +; CHECK: [[DBG12]] = distinct !DISubprogram(name: "foo", scope: [[META1]], file: [[META1]], line: 2, type: [[META13:![0-9]+]], scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: [[META0]], retainedNodes: [[META20:![0-9]+]], keyInstructions: true) +; CHECK: [[META13]] = !DISubroutineType(types: [[META14:![0-9]+]]) +; CHECK: [[META14]] = !{[[META15:![0-9]+]], [[META16:![0-9]+]]} +; CHECK: [[META15]] = !DIBasicType(name: "long", size: 64, encoding: DW_ATE_signed) +; CHECK: [[META16]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t", file: [[META1]], line: 1, size: 128, elements: [[META17:![0-9]+]]) +; CHECK: [[META17]] = !{[[META18:![0-9]+]], [[META19:![0-9]+]]} +; CHECK: [[META18]] = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: [[META16]], file: [[META1]], line: 1, baseType: [[META15]], size: 64) +; CHECK: [[META19]] = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: [[META16]], file: [[META1]], line: 1, baseType: [[META15]], size: 64, offset: 64) +; CHECK: [[META20]] = !{[[META21]]} +; CHECK: [[META21]] = !DILocalVariable(name: "arg", arg: 1, scope: [[DBG12]], file: [[META1]], line: 2, type: [[META16]]) +; CHECK: [[META22]] = !DILocation(line: 0, scope: [[DBG12]]) +; CHECK: [[DBG23]] = !DILocation(line: 3, column: 16, scope: [[DBG12]], atomGroup: 1, atomRank: 2) +; CHECK: [[DBG24]] = !DILocation(line: 3, column: 3, scope: [[DBG12]], atomGroup: 1, atomRank: 1) +;. diff --git a/llvm/test/Transforms/Util/changed-func-dbg-struct-large-dwarf.ll b/llvm/test/Transforms/Util/changed-func-dbg-struct-large-dwarf.ll new file mode 100644 index 0000000000000..19b356ebbcf11 --- /dev/null +++ b/llvm/test/Transforms/Util/changed-func-dbg-struct-large-dwarf.ll @@ -0,0 +1,112 @@ +; Check the generated DWARF debug info: +; RUN: opt -S -mtriple=x86_64-unknown-unknown -passes=emit-changed-func-debuginfo -enable-changed-func-dbinfo < %s \ +; RUN: | %llc_dwarf -filetype=obj -o - \ +; RUN: | llvm-dwarfdump - | FileCheck %s --check-prefix=DWARF +; +; REQUIRES: debug_frame +; REQUIRES: object-emission + +; Source code: +; // clang -O2 -S -emit-llvm -g test2.c +; struct t { long a; long b; long c; }; +; __attribute__((noinline)) static int foo(struct t arg, int a) { return arg.a * arg.b * arg.c; } +; int bar(struct t arg) { +; return foo(arg, 1); +; } + +%struct.t = type { i64, i64, i64 } + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) uwtable +define dso_local i32 @bar(ptr noundef readonly byval(%struct.t) align 8 captures(none) %0) local_unnamed_addr #0 !dbg !14 { + #dbg_declare(ptr %0, !25, !DIExpression(), !26) + %2 = tail call fastcc i32 @foo(ptr noundef nonnull byval(%struct.t) align 8 %0), !dbg !27 + ret i32 %2, !dbg !28 +} + +; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(argmem: read) uwtable +define internal fastcc i32 @foo(ptr noundef readonly byval(%struct.t) align 8 captures(none) %0) unnamed_addr #1 !dbg !29 { + #dbg_declare(ptr %0, !33, !DIExpression(), !35) + #dbg_value(i32 poison, !34, !DIExpression(), !36) + %2 = load i64, ptr %0, align 8, !dbg !37, !tbaa !38 + %3 = getelementptr inbounds nuw i8, ptr %0, i64 8, !dbg !41 + %4 = load i64, ptr %3, align 8, !dbg !41, !tbaa !42 + %5 = mul nsw i64 %4, %2, !dbg !43 + %6 = getelementptr inbounds nuw i8, ptr %0, i64 16, !dbg !44 + %7 = load i64, ptr %6, align 8, !dbg !44, !tbaa !45 + %8 = mul nsw i64 %5, %7, !dbg !46 + %9 = trunc i64 %8 to i32, !dbg !47 + ret i32 %9, !dbg !48 +} + +attributes #0 = { mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #1 = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(argmem: read) uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8} +!llvm.ident = !{!9} +!llvm.errno.tbaa = !{!10} + +!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 22.0.0git (git@github.com:yonghong-song/llvm-project.git 2bb68bb783927bdc2b54e64aea1b78ba598a3349)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test2.c", directory: "/home/yhs/tests/sig-change/struct16B", checksumkind: CSK_MD5, checksum: "70265912cbcea4a3b09ee4c21d26b33e") +!2 = !{i32 7, !"Dwarf Version", i32 5} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = !{i32 8, !"PIC Level", i32 2} +!6 = !{i32 7, !"PIE Level", i32 2} +!7 = !{i32 7, !"uwtable", i32 2} +!8 = !{i32 7, !"debug-info-assignment-tracking", i1 true} +!9 = !{!"clang version 22.0.0git (git@github.com:yonghong-song/llvm-project.git 2bb68bb783927bdc2b54e64aea1b78ba598a3349)"} +!10 = !{!11, !11, i64 0} +!11 = !{!"int", !12, i64 0} +!12 = !{!"omnipotent char", !13, i64 0} +!13 = !{!"Simple C/C++ TBAA"} +!14 = distinct !DISubprogram(name: "bar", scope: !1, file: !1, line: 3, type: !15, scopeLine: 3, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !24, keyInstructions: true) +!15 = !DISubroutineType(types: !16) +!16 = !{!17, !18} +!17 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!18 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t", file: !1, line: 1, size: 192, elements: !19) +!19 = !{!20, !22, !23} +!20 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !18, file: !1, line: 1, baseType: !21, size: 64) +!21 = !DIBasicType(name: "long", size: 64, encoding: DW_ATE_signed) +!22 = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: !18, file: !1, line: 1, baseType: !21, size: 64, offset: 64) +!23 = !DIDerivedType(tag: DW_TAG_member, name: "c", scope: !18, file: !1, line: 1, baseType: !21, size: 64, offset: 128) +!24 = !{!25} +!25 = !DILocalVariable(name: "arg", arg: 1, scope: !14, file: !1, line: 3, type: !18) +!26 = !DILocation(line: 3, column: 18, scope: !14) +!27 = !DILocation(line: 4, column: 10, scope: !14, atomGroup: 1, atomRank: 2) +!28 = !DILocation(line: 4, column: 3, scope: !14, atomGroup: 1, atomRank: 1) +!29 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 2, type: !30, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !32, keyInstructions: true) +!30 = !DISubroutineType(cc: DW_CC_nocall, types: !31) +!31 = !{!17, !18, !17} +!32 = !{!33, !34} +!33 = !DILocalVariable(name: "arg", arg: 1, scope: !29, file: !1, line: 2, type: !18) +!34 = !DILocalVariable(name: "a", arg: 2, scope: !29, file: !1, line: 2, type: !17) +!35 = !DILocation(line: 2, column: 51, scope: !29) +!36 = !DILocation(line: 0, scope: !29) +!37 = !DILocation(line: 2, column: 76, scope: !29) +!38 = !{!39, !40, i64 0} +!39 = !{!"t", !40, i64 0, !40, i64 8, !40, i64 16} +!40 = !{!"long", !12, i64 0} +!41 = !DILocation(line: 2, column: 84, scope: !29) +!42 = !{!39, !40, i64 8} +!43 = !DILocation(line: 2, column: 78, scope: !29) +!44 = !DILocation(line: 2, column: 92, scope: !29) +!45 = !{!39, !40, i64 16} +!46 = !DILocation(line: 2, column: 86, scope: !29, atomGroup: 1, atomRank: 3) +!47 = !DILocation(line: 2, column: 72, scope: !29, atomGroup: 1, atomRank: 2) +!48 = !DILocation(line: 2, column: 65, scope: !29, atomGroup: 1, atomRank: 1) + +; DWARF: DW_TAG_inlined_subroutine +; DWARF-NEXT: DW_AT_name ("foo") +; DWARF-NEXT: DW_AT_type +; DWARF-SAME: "int" +; DWARF-NEXT: DW_AT_artificial (true) +; DWARF-NEXT: DW_AT_specification +; DWARF-SAME: "foo" +; DWARF-NEXT: {{^$}} +; DWARF-NEXT: DW_TAG_formal_parameter +; DWARF-NEXT: DW_AT_name ("arg") +; DWARF-NEXT: DW_AT_type +; DWARF-SAME: "t" +; DWARF-NEXT: {{^$}} +; DWARF-NEXT: NULL diff --git a/llvm/test/Transforms/Util/changed-func-dbg-struct-large.ll b/llvm/test/Transforms/Util/changed-func-dbg-struct-large.ll new file mode 100644 index 0000000000000..9a1f16db7a907 --- /dev/null +++ b/llvm/test/Transforms/Util/changed-func-dbg-struct-large.ll @@ -0,0 +1,94 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 +; RUN: opt -S -mtriple=x86_64-unknown-unknown -passes=emit-changed-func-debuginfo -enable-changed-func-dbinfo < %s | FileCheck %s + +; Source code: +; // clang -O2 -S -emit-llvm -g test2.c +; struct t { long a; long b; long c;}; +; long foo(struct t arg) { +; return arg.a * arg.c; +; } + +%struct.t = type { i64, i64, i64 } + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) uwtable +define dso_local i64 @foo(ptr noundef readonly byval(%struct.t) align 8 captures(none) %0) local_unnamed_addr #0 !dbg !9 { +; CHECK-LABEL: define dso_local i64 @foo( +; CHECK-SAME: ptr noundef readonly byval([[STRUCT_T:%.*]]) align 8 captures(none) [[TMP0:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] !dbg [[DBG11:![0-9]+]] { +; CHECK-NEXT: #dbg_declare(ptr [[TMP0]], [[META21:![0-9]+]], !DIExpression(), [[META22:![0-9]+]]) +; CHECK-NEXT: [[TMP2:%.*]] = load i64, ptr [[TMP0]], align 8, !dbg [[DBG23:![0-9]+]], !tbaa [[LONG_TBAA24:![0-9]+]] +; CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds nuw i8, ptr [[TMP0]], i64 16, !dbg [[DBG29:![0-9]+]] +; CHECK-NEXT: [[TMP4:%.*]] = load i64, ptr [[TMP3]], align 8, !dbg [[DBG29]], !tbaa [[LONG_TBAA30:![0-9]+]] +; CHECK-NEXT: [[TMP5:%.*]] = mul nsw i64 [[TMP4]], [[TMP2]], !dbg [[DBG31:![0-9]+]] +; CHECK-NEXT: ret i64 [[TMP5]], !dbg [[DBG32:![0-9]+]] +; + #dbg_declare(ptr %0, !19, !DIExpression(), !20) + %2 = load i64, ptr %0, align 8, !dbg !21, !tbaa !22 + %3 = getelementptr inbounds nuw i8, ptr %0, i64 16, !dbg !27 + %4 = load i64, ptr %3, align 8, !dbg !27, !tbaa !28 + %5 = mul nsw i64 %4, %2, !dbg !29 + ret i64 %5, !dbg !30 +} + +attributes #0 = { mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5, !6, !7} +!llvm.ident = !{!8} + +!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 22.0.0git (git@github.com:yonghong-song/llvm-project.git 8e5d24efc7dac78e8ba568dfe2fc6cfbe9663b13)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test2.c", directory: "/tmp/home/yhs/tests/sig-change/struct", checksumkind: CSK_MD5, checksum: "d58648f18d3fa35e3d95b364b4a95c4c") +!2 = !{i32 7, !"Dwarf Version", i32 5} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = !{i32 8, !"PIC Level", i32 2} +!6 = !{i32 7, !"PIE Level", i32 2} +!7 = !{i32 7, !"uwtable", i32 2} +!8 = !{!"clang version 22.0.0git (git@github.com:yonghong-song/llvm-project.git 8e5d24efc7dac78e8ba568dfe2fc6cfbe9663b13)"} +!9 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 2, type: !10, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !18, keyInstructions: true) +!10 = !DISubroutineType(types: !11) +!11 = !{!12, !13} +!12 = !DIBasicType(name: "long", size: 64, encoding: DW_ATE_signed) +!13 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t", file: !1, line: 1, size: 192, elements: !14) +!14 = !{!15, !16, !17} +!15 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !13, file: !1, line: 1, baseType: !12, size: 64) +!16 = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: !13, file: !1, line: 1, baseType: !12, size: 64, offset: 64) +!17 = !DIDerivedType(tag: DW_TAG_member, name: "c", scope: !13, file: !1, line: 1, baseType: !12, size: 64, offset: 128) +!18 = !{!19} +!19 = !DILocalVariable(name: "arg", arg: 1, scope: !9, file: !1, line: 2, type: !13) +!20 = !DILocation(line: 2, column: 19, scope: !9) +!21 = !DILocation(line: 3, column: 14, scope: !9) +!22 = !{!23, !24, i64 0} +!23 = !{!"t", !24, i64 0, !24, i64 8, !24, i64 16} +!24 = !{!"long", !25, i64 0} +!25 = !{!"omnipotent char", !26, i64 0} +!26 = !{!"Simple C/C++ TBAA"} +!27 = !DILocation(line: 3, column: 22, scope: !9) +!28 = !{!23, !24, i64 16} +!29 = !DILocation(line: 3, column: 16, scope: !9, atomGroup: 1, atomRank: 2) +!30 = !DILocation(line: 3, column: 3, scope: !9, atomGroup: 1, atomRank: 1) +;. +; CHECK: [[META0:![0-9]+]] = distinct !DICompileUnit(language: DW_LANG_C11, file: [[META1:![0-9]+]], producer: "{{.*}}clang version {{.*}}", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +; CHECK: [[META1]] = !DIFile(filename: "{{.*}}test2.c", directory: {{.*}}) +; CHECK: [[DBG11]] = distinct !DISubprogram(name: "foo", scope: [[META1]], file: [[META1]], line: 2, type: [[META12:![0-9]+]], scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: [[META0]], retainedNodes: [[META20:![0-9]+]], keyInstructions: true) +; CHECK: [[META12]] = !DISubroutineType(types: [[META13:![0-9]+]]) +; CHECK: [[META13]] = !{[[META14:![0-9]+]], [[META15:![0-9]+]]} +; CHECK: [[META14]] = !DIBasicType(name: "long", size: 64, encoding: DW_ATE_signed) +; CHECK: [[META15]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t", file: [[META1]], line: 1, size: 192, elements: [[META16:![0-9]+]]) +; CHECK: [[META16]] = !{[[META17:![0-9]+]], [[META18:![0-9]+]], [[META19:![0-9]+]]} +; CHECK: [[META17]] = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: [[META15]], file: [[META1]], line: 1, baseType: [[META14]], size: 64) +; CHECK: [[META18]] = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: [[META15]], file: [[META1]], line: 1, baseType: [[META14]], size: 64, offset: 64) +; CHECK: [[META19]] = !DIDerivedType(tag: DW_TAG_member, name: "c", scope: [[META15]], file: [[META1]], line: 1, baseType: [[META14]], size: 64, offset: 128) +; CHECK: [[META20]] = !{[[META21]]} +; CHECK: [[META21]] = !DILocalVariable(name: "arg", arg: 1, scope: [[DBG11]], file: [[META1]], line: 2, type: [[META15]]) +; CHECK: [[META22]] = !DILocation(line: 2, column: 19, scope: [[DBG11]]) +; CHECK: [[DBG23]] = !DILocation(line: 3, column: 14, scope: [[DBG11]]) +; CHECK: [[LONG_TBAA24]] = !{[[META25:![0-9]+]], [[META26:![0-9]+]], i64 0} +; CHECK: [[META25]] = !{!"t", [[META26]], i64 0, [[META26]], i64 8, [[META26]], i64 16} +; CHECK: [[META26]] = !{!"long", [[META27:![0-9]+]], i64 0} +; CHECK: [[META27]] = !{!"omnipotent char", [[META28:![0-9]+]], i64 0} +; CHECK: [[META28]] = !{!"Simple C/C++ TBAA"} +; CHECK: [[DBG29]] = !DILocation(line: 3, column: 22, scope: [[DBG11]]) +; CHECK: [[LONG_TBAA30]] = !{[[META25]], [[META26]], i64 16} +; CHECK: [[DBG31]] = !DILocation(line: 3, column: 16, scope: [[DBG11]], atomGroup: 1, atomRank: 2) +; CHECK: [[DBG32]] = !DILocation(line: 3, column: 3, scope: [[DBG11]], atomGroup: 1, atomRank: 1) +;.