diff --git a/llvm/include/llvm/IR/DIBuilder.h b/llvm/include/llvm/IR/DIBuilder.h index 61fa4d8f3b9fd..ef64fcd8cb924 100644 --- a/llvm/include/llvm/IR/DIBuilder.h +++ b/llvm/include/llvm/IR/DIBuilder.h @@ -37,6 +37,7 @@ namespace llvm { class LLVMContext; class Module; class Value; + class DbgAssignIntrinsic; class DIBuilder { Module &M; @@ -47,6 +48,7 @@ namespace llvm { Function *ValueFn; ///< llvm.dbg.value Function *LabelFn; ///< llvm.dbg.label Function *AddrFn; ///< llvm.dbg.addr + Function *AssignFn; ///< llvm.dbg.assign SmallVector AllEnumTypes; /// Track the RetainTypes, since they can be updated later on. @@ -917,6 +919,26 @@ namespace llvm { DIExpression *Expr, const DILocation *DL, BasicBlock *InsertAtEnd); + /// Insert a new llvm.dbg.assign intrinsic call. + /// \param LinkedInstr Instruction with a DIAssignID to link with the new + /// intrinsic. The intrinsic will be inserted after + /// this instruction. + /// \param Val The value component of this dbg.assign. + /// \param SrcVar Variable's debug info descriptor. + /// \param ValExpr A complex location expression to modify \p Val. + /// \param Addr The address component (store destination). + /// \param AddrExpr A complex location expression to modify \p Addr. + /// NOTE: \p ValExpr carries the FragInfo for the + /// variable. + /// \param DL Debug info location, usually: (line: 0, + /// column: 0, scope: var-decl-scope). See + /// getDebugValueLoc. + DbgAssignIntrinsic *insertDbgAssign(Instruction *LinkedInstr, Value *Val, + DILocalVariable *SrcVar, + DIExpression *ValExpr, Value *Addr, + DIExpression *AddrExpr, + const DILocation *DL); + /// Insert a new llvm.dbg.declare intrinsic call. /// \param Storage llvm::Value of the variable /// \param VarInfo Variable's debug info descriptor. diff --git a/llvm/include/llvm/IR/DebugInfo.h b/llvm/include/llvm/IR/DebugInfo.h index ab6a36946af6a..7e0a68b7b9383 100644 --- a/llvm/include/llvm/IR/DebugInfo.h +++ b/llvm/include/llvm/IR/DebugInfo.h @@ -18,10 +18,13 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/TinyPtrVector.h" #include "llvm/ADT/iterator_range.h" +#include "llvm/IR/DataLayout.h" #include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/PassManager.h" namespace llvm { @@ -225,8 +228,75 @@ void RAUW(DIAssignID *Old, DIAssignID *New); /// Remove all Assignment Tracking related intrinsics and metadata from \p F. void deleteAll(Function *F); +/// Helper struct for trackAssignments, below. We don't use the similar +/// DebugVariable class because trackAssignments doesn't (yet?) understand +/// partial variables (fragment info) as input and want to make that clear and +/// explicit using types. In addition, eventually we will want to understand +/// expressions that modify the base address too, which a DebugVariable doesn't +/// capture. +struct VarRecord { + DILocalVariable *Var; + DILocation *DL; + + VarRecord(DbgVariableIntrinsic *DVI) + : Var(DVI->getVariable()), DL(getDebugValueLoc(DVI)) {} + VarRecord(DILocalVariable *Var, DILocation *DL) : Var(Var), DL(DL) {} + friend bool operator<(const VarRecord &LHS, const VarRecord &RHS) { + return std::tie(LHS.Var, LHS.DL) < std::tie(RHS.Var, RHS.DL); + } + friend bool operator==(const VarRecord &LHS, const VarRecord &RHS) { + return std::tie(LHS.Var, LHS.DL) == std::tie(RHS.Var, RHS.DL); + } +}; + +/// Map of backing storage to a set of variables that are stored to it. +/// TODO: Backing storage shouldn't be limited to allocas only. Some local +/// variables have their storage allocated by the calling function (addresses +/// passed in with sret & byval parameters). +using StorageToVarsMap = DenseMap>; + +/// Track assignments to \p Vars between \p Start and \p End. + +void trackAssignments(Function::iterator Start, Function::iterator End, + const StorageToVarsMap &Vars, const DataLayout &DL, + bool DebugPrints = false); + +/// Describes properties of a store that has a static size and offset into a +/// some base storage. Used by the getAssignmentInfo functions. +struct AssignmentInfo { + AllocaInst const *Base; ///< Base storage. + uint64_t OffsetInBits; ///< Offset into Base. + uint64_t SizeInBits; ///< Number of bits stored. + bool StoreToWholeAlloca; ///< SizeInBits equals the size of the base storage. + + AssignmentInfo(const DataLayout &DL, AllocaInst const *Base, + uint64_t OffsetInBits, uint64_t SizeInBits) + : Base(Base), OffsetInBits(OffsetInBits), SizeInBits(SizeInBits), + StoreToWholeAlloca( + OffsetInBits == 0 && + SizeInBits == DL.getTypeSizeInBits(Base->getAllocatedType())) {} +}; + +Optional getAssignmentInfo(const DataLayout &DL, + const MemIntrinsic *I); +Optional getAssignmentInfo(const DataLayout &DL, + const StoreInst *SI); +Optional getAssignmentInfo(const DataLayout &DL, + const AllocaInst *AI); + } // end namespace at +/// Convert @llvm.dbg.declare intrinsics into sets of @llvm.dbg.assign +/// intrinsics by treating stores to the dbg.declare'd address as assignments +/// to the variable. Not all kinds of variables are supported yet; those will +/// be left with their dbg.declare intrinsics. +class AssignmentTrackingPass : public PassInfoMixin { +public: + void runOnFunction(Function &F); + PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); + PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); +}; + /// Return true if assignment tracking is enabled. bool getEnableAssignmentTracking(); } // end namespace llvm diff --git a/llvm/lib/IR/DIBuilder.cpp b/llvm/lib/IR/DIBuilder.cpp index 76d7ade09a88c..989b55463b4dc 100644 --- a/llvm/lib/IR/DIBuilder.cpp +++ b/llvm/lib/IR/DIBuilder.cpp @@ -30,7 +30,7 @@ static cl::opt DIBuilder::DIBuilder(Module &m, bool AllowUnresolvedNodes, DICompileUnit *CU) : M(m), VMContext(M.getContext()), CUNode(CU), DeclareFn(nullptr), - ValueFn(nullptr), LabelFn(nullptr), AddrFn(nullptr), + ValueFn(nullptr), LabelFn(nullptr), AddrFn(nullptr), AssignFn(nullptr), AllowUnresolvedNodes(AllowUnresolvedNodes) { if (CUNode) { if (const auto &ETs = CUNode->getEnumTypes()) @@ -960,6 +960,36 @@ Instruction *DIBuilder::insertDeclare(Value *Storage, DILocalVariable *VarInfo, return insertDeclare(Storage, VarInfo, Expr, DL, InsertAtEnd, InsertBefore); } +DbgAssignIntrinsic * +DIBuilder::insertDbgAssign(Instruction *LinkedInstr, Value *Val, + DILocalVariable *SrcVar, DIExpression *ValExpr, + Value *Addr, DIExpression *AddrExpr, + const DILocation *DL) { + LLVMContext &Ctx = LinkedInstr->getContext(); + Module *M = LinkedInstr->getModule(); + if (!AssignFn) + AssignFn = Intrinsic::getDeclaration(M, Intrinsic::dbg_assign); + + auto *Link = LinkedInstr->getMetadata(LLVMContext::MD_DIAssignID); + assert(Link && "Linked instruction must have DIAssign metadata attached"); + + std::array Args = { + MetadataAsValue::get(Ctx, ValueAsMetadata::get(Val)), + MetadataAsValue::get(Ctx, SrcVar), + MetadataAsValue::get(Ctx, ValExpr), + MetadataAsValue::get(Ctx, Link), + MetadataAsValue::get(Ctx, ValueAsMetadata::get(Addr)), + MetadataAsValue::get(Ctx, AddrExpr), + }; + + IRBuilder<> B(Ctx); + B.SetCurrentDebugLocation(DL); + + auto *DVI = cast(B.CreateCall(AssignFn, Args)); + DVI->insertAfter(LinkedInstr); + return DVI; +} + Instruction *DIBuilder::insertLabel(DILabel *LabelInfo, const DILocation *DL, Instruction *InsertBefore) { return insertLabel(LabelInfo, DL, diff --git a/llvm/lib/IR/DebugInfo.cpp b/llvm/lib/IR/DebugInfo.cpp index 64d606ec15a60..8c9ef9839eb1d 100644 --- a/llvm/lib/IR/DebugInfo.cpp +++ b/llvm/lib/IR/DebugInfo.cpp @@ -32,6 +32,7 @@ #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Metadata.h" #include "llvm/IR/Module.h" +#include "llvm/IR/PassManager.h" #include "llvm/Support/Casting.h" #include #include @@ -1713,3 +1714,237 @@ void at::deleteAll(Function *F) { for (auto *DAI : ToDelete) DAI->eraseFromParent(); } + +/// Collect constant properies (base, size, offset) of \p StoreDest. +/// Return None if any properties are not constants. +static Optional getAssignmentInfoImpl(const DataLayout &DL, + const Value *StoreDest, + uint64_t SizeInBits) { + APInt GEPOffset(DL.getIndexTypeSizeInBits(StoreDest->getType()), 0); + const Value *Base = StoreDest->stripAndAccumulateConstantOffsets( + DL, GEPOffset, /*AllowNonInbounds*/ true); + uint64_t OffsetInBytes = GEPOffset.getLimitedValue(); + // Check for overflow. + if (OffsetInBytes == UINT64_MAX) + return None; + if (const auto *Alloca = dyn_cast(Base)) + return AssignmentInfo(DL, Alloca, OffsetInBytes * 8, SizeInBits); + return None; +} + +Optional at::getAssignmentInfo(const DataLayout &DL, + const MemIntrinsic *I) { + const Value *StoreDest = I->getRawDest(); + // Assume 8 bit bytes. + auto *ConstLengthInBytes = dyn_cast(I->getLength()); + if (!ConstLengthInBytes) + // We can't use a non-const size, bail. + return None; + uint64_t SizeInBits = 8 * ConstLengthInBytes->getZExtValue(); + return getAssignmentInfoImpl(DL, StoreDest, SizeInBits); +} + +Optional at::getAssignmentInfo(const DataLayout &DL, + const StoreInst *SI) { + const Value *StoreDest = SI->getPointerOperand(); + uint64_t SizeInBits = DL.getTypeSizeInBits(SI->getValueOperand()->getType()); + return getAssignmentInfoImpl(DL, StoreDest, SizeInBits); +} + +Optional at::getAssignmentInfo(const DataLayout &DL, + const AllocaInst *AI) { + uint64_t SizeInBits = DL.getTypeSizeInBits(AI->getAllocatedType()); + return getAssignmentInfoImpl(DL, AI, SizeInBits); +} + +static CallInst *emitDbgAssign(AssignmentInfo Info, Value *Val, Value *Dest, + Instruction &StoreLikeInst, + const VarRecord &VarRec, DIBuilder &DIB) { + auto *ID = StoreLikeInst.getMetadata(LLVMContext::MD_DIAssignID); + assert(ID && "Store instruction must have DIAssignID metadata"); + + DIExpression *Expr = DIExpression::get(StoreLikeInst.getContext(), None); + if (!Info.StoreToWholeAlloca) { + auto R = DIExpression::createFragmentExpression(Expr, Info.OffsetInBits, + Info.SizeInBits); + assert(R.has_value() && "failed to create fragment expression"); + Expr = R.value(); + } + DIExpression *AddrExpr = DIExpression::get(StoreLikeInst.getContext(), None); + return DIB.insertDbgAssign(&StoreLikeInst, Val, VarRec.Var, Expr, Dest, + AddrExpr, VarRec.DL); +} + +#undef DEBUG_TYPE // Silence redefinition warning (from ConstantsContext.h). +#define DEBUG_TYPE "assignment-tracking" + +void at::trackAssignments(Function::iterator Start, Function::iterator End, + const StorageToVarsMap &Vars, const DataLayout &DL, + bool DebugPrints) { + // Early-exit if there are no interesting variables. + if (Vars.empty()) + return; + + auto &Ctx = Start->getContext(); + auto &Module = *Start->getModule(); + + // Undef type doesn't matter, so long as it isn't void. Let's just use i1. + auto *Undef = UndefValue::get(Type::getInt1Ty(Ctx)); + DIBuilder DIB(Module, /*AllowUnresolved*/ false); + + // Scan the instructions looking for stores to local variables' storage. + LLVM_DEBUG(errs() << "# Scanning instructions\n"); + for (auto BBI = Start; BBI != End; ++BBI) { + for (Instruction &I : *BBI) { + + Optional Info = None; + Value *ValueComponent = nullptr; + Value *DestComponent = nullptr; + if (auto *AI = dyn_cast(&I)) { + // We want to track the variable's stack home from its alloca's + // position onwards so we treat it as an assignment (where the stored + // value is Undef). + Info = getAssignmentInfo(DL, AI); + ValueComponent = Undef; + DestComponent = AI; + } else if (auto *SI = dyn_cast(&I)) { + Info = getAssignmentInfo(DL, SI); + ValueComponent = SI->getValueOperand(); + DestComponent = SI->getPointerOperand(); + } else if (auto *MI = dyn_cast(&I)) { + Info = getAssignmentInfo(DL, MI); + // May not be able to represent this value easily. + ValueComponent = Undef; + DestComponent = MI->getOperand(0); + } else if (auto *MI = dyn_cast(&I)) { + Info = getAssignmentInfo(DL, MI); + // If we're zero-initing we can state the assigned value is zero, + // otherwise use undef. + auto *ConstValue = dyn_cast(MI->getOperand(1)); + if (ConstValue && ConstValue->isZero()) + ValueComponent = ConstValue; + else + ValueComponent = Undef; + DestComponent = MI->getOperand(0); + } else { + // Not a store-like instruction. + continue; + } + + assert(ValueComponent && DestComponent); + LLVM_DEBUG(errs() << "SCAN: Found store-like: " << I << "\n"); + + // Check if getAssignmentInfo failed to understand this store. + if (!Info.has_value()) { + LLVM_DEBUG( + errs() + << " | SKIP: Untrackable store (e.g. through non-const gep)\n"); + continue; + } + LLVM_DEBUG(errs() << " | BASE: " << *Info->Base << "\n"); + + // Check if the store destination is a local variable with debug info. + auto LocalIt = Vars.find(Info->Base); + if (LocalIt == Vars.end()) { + LLVM_DEBUG( + errs() + << " | SKIP: Base address not associated with local variable\n"); + continue; + } + + DIAssignID *ID = + cast_or_null(I.getMetadata(LLVMContext::MD_DIAssignID)); + if (!ID) { + ID = DIAssignID::getDistinct(Ctx); + I.setMetadata(LLVMContext::MD_DIAssignID, ID); + } + + for (const VarRecord &R : LocalIt->second) { + auto *Assign = + emitDbgAssign(*Info, ValueComponent, DestComponent, I, R, DIB); + (void)Assign; + LLVM_DEBUG(errs() << " > INSERT: " << *Assign << "\n"); + } + } + } +} + +void AssignmentTrackingPass::runOnFunction(Function &F) { + // Collect a map of {backing storage : dbg.declares} (currently "backing + // storage" is limited to Allocas). We'll use this to find dbg.declares to + // delete after running `trackAssignments`. + DenseMap> DbgDeclares; + // Create another similar map of {storage : variables} that we'll pass to + // trackAssignments. + StorageToVarsMap Vars; + for (auto &BB : F) { + for (auto &I : BB) { + DbgDeclareInst *DDI = dyn_cast(&I); + if (!DDI) + continue; + // FIXME: trackAssignments doesn't let you specify any modifiers to the + // variable (e.g. fragment) or location (e.g. offset), so we have to + // leave dbg.declares with non-empty expressions in place. + if (DDI->getExpression()->getNumElements() != 0) + continue; + if (AllocaInst *Alloca = + dyn_cast(DDI->getAddress()->stripPointerCasts())) { + DbgDeclares[Alloca].insert(DDI); + Vars[Alloca].insert(VarRecord(DDI)); + } + } + } + + auto DL = std::make_unique(F.getParent()); + // FIXME: Locals can be backed by caller allocas (sret, byval). + // Note: trackAssignments doesn't respect dbg.declare's IR positions (as it + // doesn't "understand" dbg.declares). However, this doesn't appear to break + // any rules given this description of dbg.declare from + // llvm/docs/SourceLevelDebugging.rst: + // + // It is not control-dependent, meaning that if a call to llvm.dbg.declare + // exists and has a valid location argument, that address is considered to + // be the true home of the variable across its entire lifetime. + trackAssignments(F.begin(), F.end(), Vars, *DL); + + // Delete dbg.declares for variables now tracked with assignment tracking. + for (auto &P : DbgDeclares) { + const AllocaInst *Alloca = P.first; + auto Markers = at::getAssignmentMarkers(Alloca); + for (DbgDeclareInst *DDI : P.second) { + // Assert that the alloca that DDI uses is now linked to a dbg.assign + // describing the same variable (i.e. check that this dbg.declare + // has been replaced by a dbg.assign). + assert(std::find_if(Markers.begin(), Markers.end(), + [DDI](DbgAssignIntrinsic *DAI) { + return DebugVariable(DAI) == DebugVariable(DDI); + }) != Markers.end()); + // Delete DDI because the variable location is now tracked using + // assignment tracking. + DDI->eraseFromParent(); + } + } +} + +PreservedAnalyses AssignmentTrackingPass::run(Function &F, + FunctionAnalysisManager &AM) { + runOnFunction(F); + // Q: Can we return a less conservative set than just CFGAnalyses? Can we + // return PreservedAnalyses::all()? + PreservedAnalyses PA; + PA.preserveSet(); + return PA; +} + +PreservedAnalyses AssignmentTrackingPass::run(Module &M, + ModuleAnalysisManager &AM) { + for (auto &F : M) + runOnFunction(F); + // Q: Can we return a less conservative set than just CFGAnalyses? Can we + // return PreservedAnalyses::all()? + PreservedAnalyses PA; + PA.preserveSet(); + return PA; +} + +#undef DEBUG_TYPE diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp index 46e8b52ac7e86..b84f0db188da6 100644 --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -73,6 +73,7 @@ #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/Analysis/TypeBasedAliasAnalysis.h" +#include "llvm/IR/DebugInfo.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/IRPrintingPasses.h" #include "llvm/IR/PassManager.h" diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def index e7a65ae7b7000..7d8656ee41d7f 100644 --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -394,6 +394,7 @@ FUNCTION_PASS("tlshoist", TLSVariableHoistPass()) FUNCTION_PASS("transform-warning", WarnMissedTransformationsPass()) FUNCTION_PASS("tsan", ThreadSanitizerPass()) FUNCTION_PASS("memprof", MemProfilerPass()) +FUNCTION_PASS("declare-to-assign", llvm::AssignmentTrackingPass()) #undef FUNCTION_PASS #ifndef FUNCTION_PASS_WITH_PARAMS diff --git a/llvm/test/DebugInfo/Generic/assignment-tracking/track-assignments.ll b/llvm/test/DebugInfo/Generic/assignment-tracking/track-assignments.ll new file mode 100644 index 0000000000000..2dafda04a8247 --- /dev/null +++ b/llvm/test/DebugInfo/Generic/assignment-tracking/track-assignments.ll @@ -0,0 +1,505 @@ +; RUN: opt -passes='declare-to-assign,verify' %s -S -o - -experimental-assignment-tracking \ +; RUN: | FileCheck %s --implicit-check-not="call void @llvm.dbg" + +;; This test checks that `trackAssignments` is working correctly by using the +;; pass-wrapper `declare-to-assign`. Each function checks some specific +;; functionality and has its checks inline. + +;; $ cat test.cpp +;; struct Inner { int A, B; }; +;; struct Outer { Inner A, B; }; +;; struct Large { int A[10]; }; +;; struct LCopyCtor { int A[4]; LCopyCtor(); LCopyCtor(LCopyCtor const &); }; +;; int Value, Index, Cond; +;; Inner InnerA, InnerB; +;; Large L; +;; +;; void zeroInit() { int Z[3] = {0, 0, 0}; } +;; +;; void memcpyInit() { int A[4] = {0, 1, 2, 3}; } +;; +;; void setField() { +;; Outer O; +;; O.A.B = Value; +;; } +;; +;; void unknownOffset() { +;; int A[2]; +;; A[Index] = Value; +;; } +;; +;; Inner sharedAlloca() { +;; if (Cond) { +;; Inner A = InnerA; +;; return A; +;; } else { +;; Inner B = InnerB; +;; return B; +;; } +;; } +;; +;; Large sret() { +;; Large X = L; +;; return X; +;; } +;; +;; void byval(Large X) {} +;; +;; LCopyCtor indirectReturn() { +;; LCopyCtor R; +;; return R; +;; } +;; +;; $ clang++ -g test.cpp -o - -emit-llvm -S -Xclang -disable-llvm-passes -O2 +;; +;; Then these functions are added manually to the IR using +;; $ clang++ -g test.cpp -o - -emit-llvm -S -O0 +;; and removing the optnone attributes: +;; +;; __attribute__((always_inline)) +;; int sqr(int Y) { return Y * Y; } +;; int fun(int X) { return sqr(X); } + + +source_filename = "test.cpp" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +%struct.Inner = type { i32, i32 } +%struct.Large = type { [10 x i32] } +%struct.Outer = type { %struct.Inner, %struct.Inner } +%struct.LCopyCtor = type { [4 x i32] } + +@Value = dso_local global i32 0, align 4, !dbg !0 +@Index = dso_local global i32 0, align 4, !dbg !5 +@Cond = dso_local global i32 0, align 4, !dbg !8 +@InnerA = dso_local global %struct.Inner zeroinitializer, align 4, !dbg !10 +@InnerB = dso_local global %struct.Inner zeroinitializer, align 4, !dbg !16 +@L = dso_local global %struct.Large zeroinitializer, align 4, !dbg !18 +@__const._Z10memcpyInitv.A = private unnamed_addr constant [4 x i32] [i32 0, i32 1, i32 2, i32 3], align 16 + +;; Zero init with a memset. +;; +;; void zeroInit() { int Z[3] = {0, 0, 0}; } +;; +;; Check that we get two dbg.assign intrinsics. The first linked to the alloca +;; and the second linked to the zero-init-memset, which should have a constant 0 +;; for the value component. +define dso_local void @_Z8zeroInitv() #0 !dbg !31 { +; CHECK-LABEL: define dso_local void @_Z8zeroInitv +entry: + %Z = alloca [3 x i32], align 4 +; CHECK: %Z = alloca [3 x i32], align 4, !DIAssignID ![[ID_0:[0-9]+]] +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR_0:[0-9]+]], metadata !DIExpression(), metadata ![[ID_0]], metadata [3 x i32]* %Z, metadata !DIExpression()) + %0 = bitcast [3 x i32]* %Z to i8*, !dbg !39 + call void @llvm.lifetime.start.p0i8(i64 12, i8* %0) #5, !dbg !39 + call void @llvm.dbg.declare(metadata [3 x i32]* %Z, metadata !35, metadata !DIExpression()), !dbg !40 + %1 = bitcast [3 x i32]* %Z to i8*, !dbg !40 + call void @llvm.memset.p0i8.i64(i8* align 4 %1, i8 0, i64 12, i1 false), !dbg !40 +; CHECK: @llvm.memset.p0i8.i64{{.*}}, !DIAssignID ![[ID_1:[0-9]+]] +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i8 0, metadata ![[VAR_0]], metadata !DIExpression(), metadata ![[ID_1]], metadata i8* %1, metadata !DIExpression()) + %2 = bitcast [3 x i32]* %Z to i8*, !dbg !41 + call void @llvm.lifetime.end.p0i8(i64 12, i8* %2) #5, !dbg !41 + ret void, !dbg !41 +} + +;; Init with a memcpy (from a global). +;; +;; void memcpyInit() { int A[4] = {0, 1, 2, 3}; } +;; +;; Check that we get two dbg.assign intrinsics. The first linked to the alloca +;; and the second linked to the initialising memcpy, which should have an Undef +;; value component. +define dso_local void @_Z10memcpyInitv() #0 !dbg !42 { +; CHECK-LABEL: define dso_local void @_Z10memcpyInitv +entry: + %A = alloca [4 x i32], align 16 +; CHECK: %A = alloca [4 x i32], align 16, !DIAssignID ![[ID_2:[0-9]+]] +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR_1:[0-9]+]], metadata !DIExpression(), metadata ![[ID_2]], metadata [4 x i32]* %A, metadata !DIExpression()) + %0 = bitcast [4 x i32]* %A to i8*, !dbg !48 + call void @llvm.lifetime.start.p0i8(i64 16, i8* %0) #5, !dbg !48 + call void @llvm.dbg.declare(metadata [4 x i32]* %A, metadata !44, metadata !DIExpression()), !dbg !49 + %1 = bitcast [4 x i32]* %A to i8*, !dbg !49 + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 %1, i8* align 16 bitcast ([4 x i32]* @__const._Z10memcpyInitv.A to i8*), i64 16, i1 false), !dbg !49 +; CHECK: @llvm.memcpy.p0i8.p0i8.i64{{.*}}, !DIAssignID ![[ID_3:[0-9]+]] +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR_1]], metadata !DIExpression(), metadata ![[ID_3]], metadata i8* %1, metadata !DIExpression()) + %2 = bitcast [4 x i32]* %A to i8*, !dbg !50 + call void @llvm.lifetime.end.p0i8(i64 16, i8* %2) #5, !dbg !50 + ret void, !dbg !50 +} + +;; Assign to field of local variable. +;; +;; void setField() { +;; Outer O; +;; O.A.B = Value; +;; } +;; +;; Check that we get two dbg.assign intrinsics. The first linked to the alloca +;; and the second linked to the store to O.A.B, which should include the +;; appropriate fragment info (SizeInBits: 32, OffsetInBits: 32) with respect to +;; the base variable. +define dso_local void @_Z8setFieldv() #0 !dbg !51 { +; CHECK-LABEL: define dso_local void @_Z8setFieldv +entry: + %O = alloca %struct.Outer, align 4 +; CHECK: %O = alloca %struct.Outer, align 4, !DIAssignID ![[ID_4:[0-9]+]] +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR_2:[0-9]+]], metadata !DIExpression(), metadata ![[ID_4]], metadata %struct.Outer* %O, metadata !DIExpression()) + %0 = bitcast %struct.Outer* %O to i8*, !dbg !58 + call void @llvm.lifetime.start.p0i8(i64 16, i8* %0) #5, !dbg !58 + call void @llvm.dbg.declare(metadata %struct.Outer* %O, metadata !53, metadata !DIExpression()), !dbg !59 + %1 = load i32, i32* @Value, align 4, !dbg !60, !tbaa !61 + %A = getelementptr inbounds %struct.Outer, %struct.Outer* %O, i32 0, i32 0, !dbg !65 + %B = getelementptr inbounds %struct.Inner, %struct.Inner* %A, i32 0, i32 1, !dbg !66 + store i32 %1, i32* %B, align 4, !dbg !67, !tbaa !68 +; CHECK: store i32 %1, i32* %B, align 4,{{.*}}!DIAssignID ![[ID_5:[0-9]+]] +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i32 %1, metadata ![[VAR_2]], metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32), metadata ![[ID_5]], metadata i32* %B, metadata !DIExpression()) + %2 = bitcast %struct.Outer* %O to i8*, !dbg !71 + call void @llvm.lifetime.end.p0i8(i64 16, i8* %2) #5, !dbg !71 + ret void, !dbg !71 +} + +;; Assign to (statically) unknown offset into a local variable. +;; +;; void unknownOffset() { +;; int A[2]; +;; A[Index] = Value; +;; } +;; +;; Check that we get only one dbg.assign intrinsic and that it is linked to the +;; alloca. The assignment doesn't get a dbg.assign intrinsic because we cannot +;; encode the fragment info for the assignment. +;; Note: This doesn't mean we lose the assignment entirely: the backend will +;; interpret the store (as it sees it) as an assignment. +define dso_local void @_Z13unknownOffsetv() #0 !dbg !72 { +; CHECK-LABEL: define dso_local void @_Z13unknownOffsetv +entry: + %A = alloca [2 x i32], align 4 +; CHECK: %A = alloca [2 x i32], align 4, !DIAssignID ![[ID_6:[0-9]+]] +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR_3:[0-9]+]], metadata !DIExpression(), metadata ![[ID_6]], metadata [2 x i32]* %A, metadata !DIExpression()) + %0 = bitcast [2 x i32]* %A to i8*, !dbg !78 + call void @llvm.lifetime.start.p0i8(i64 8, i8* %0) #5, !dbg !78 + call void @llvm.dbg.declare(metadata [2 x i32]* %A, metadata !74, metadata !DIExpression()), !dbg !79 + %1 = load i32, i32* @Value, align 4, !dbg !80, !tbaa !61 + %2 = load i32, i32* @Index, align 4, !dbg !81, !tbaa !61 + %idxprom = sext i32 %2 to i64, !dbg !82 + %arrayidx = getelementptr inbounds [2 x i32], [2 x i32]* %A, i64 0, i64 %idxprom, !dbg !82 + store i32 %1, i32* %arrayidx, align 4, !dbg !83, !tbaa !61 + %3 = bitcast [2 x i32]* %A to i8*, !dbg !84 + call void @llvm.lifetime.end.p0i8(i64 8, i8* %3) #5, !dbg !84 + ret void, !dbg !84 +} + +;; Assignments to variables which share the same backing storage. +;; +;; Inner sharedAlloca() { +;; if (Cond) { +;; Inner A = InnerA; +;; return A; +;; } else { +;; Inner B = InnerB; +;; return B; +;; } +;; } +;; +define dso_local i64 @_Z12sharedAllocav() #0 !dbg !85 { +; CHECK-LABEL: define dso_local i64 @_Z12sharedAllocav +entry: + %retval = alloca %struct.Inner, align 4 +; CHECK: %retval = alloca %struct.Inner, align 4, !DIAssignID ![[ID_7:[0-9]+]] +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR_4:[0-9]+]], metadata !DIExpression(), metadata ![[ID_7]], metadata %struct.Inner* %retval, metadata !DIExpression()) +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR_5:[0-9]+]], metadata !DIExpression(), metadata ![[ID_7]], metadata %struct.Inner* %retval, metadata !DIExpression()) + %0 = load i32, i32* @Cond, align 4, !dbg !94, !tbaa !61 + %tobool = icmp ne i32 %0, 0, !dbg !94 + br i1 %tobool, label %if.then, label %if.else, !dbg !95 + +if.then: ; preds = %entry +; CHECK: if.then: + call void @llvm.dbg.declare(metadata %struct.Inner* %retval, metadata !89, metadata !DIExpression()), !dbg !96 + %1 = bitcast %struct.Inner* %retval to i8*, !dbg !97 + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %1, i8* align 4 bitcast (%struct.Inner* @InnerA to i8*), i64 8, i1 false), !dbg !97, !tbaa.struct !98 +; CHECK: call void @llvm.memcpy{{.*}}, !DIAssignID ![[ID_8:[0-9]+]] +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR_4]], metadata !DIExpression(), metadata ![[ID_8]], metadata i8* %1, metadata !DIExpression()) +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR_5]], metadata !DIExpression(), metadata ![[ID_8]], metadata i8* %1, metadata !DIExpression()) + br label %return, !dbg !99 + +if.else: ; preds = %entry +; CHECK: if.else: + call void @llvm.dbg.declare(metadata %struct.Inner* %retval, metadata !92, metadata !DIExpression()), !dbg !100 + %2 = bitcast %struct.Inner* %retval to i8*, !dbg !101 + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %2, i8* align 4 bitcast (%struct.Inner* @InnerB to i8*), i64 8, i1 false), !dbg !101, !tbaa.struct !98 +; CHECK: call void @llvm.memcpy{{.*}}, !DIAssignID ![[ID_9:[0-9]+]] +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR_4]], metadata !DIExpression(), metadata ![[ID_9]], metadata i8* %2, metadata !DIExpression()) +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR_5]], metadata !DIExpression(), metadata ![[ID_9]], metadata i8* %2, metadata !DIExpression()) + br label %return, !dbg !102 + +return: ; preds = %if.else, %if.then + %3 = bitcast %struct.Inner* %retval to i64*, !dbg !103 + %4 = load i64, i64* %3, align 4, !dbg !103 + ret i64 %4, !dbg !103 +} + +;; Caller-allocated memory for a local (sret parameter). +;; +;; Large sret() { +;; Large X = L; +;; return X; +;; } +;; +;; TODO: Currently not supported by `trackAssignments` (or the rest of the +;; assignment tracking pipeline). In lieu of being able to xfail a part of a +;; test, check that the dbg.declare is preserved so the test fails (and can be +;; updated) when this is fixed. +define dso_local void @_Z4sretv(%struct.Large* noalias sret(%struct.Large) align 4 %agg.result) #0 !dbg !104 { +; CHECK-LABEL: define dso_local void @_Z4sretv +entry: +; CHECK: call void @llvm.dbg.declare + call void @llvm.dbg.declare(metadata %struct.Large* %agg.result, metadata !108, metadata !DIExpression()), !dbg !109 + %0 = bitcast %struct.Large* %agg.result to i8*, !dbg !110 + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 4 %0, i8* align 4 bitcast (%struct.Large* @L to i8*), i64 40, i1 false), !dbg !110, !tbaa.struct !111 + ret void, !dbg !113 +} + +;; Caller-allocated memory for a local (byval parameter). +;; +;; void byval(Large X) {} +;; +;; TODO: See comment for sret parameters above. +define dso_local void @_Z5byval5Large(%struct.Large* noundef byval(%struct.Large) align 8 %X) #0 !dbg !114 { +; CHECK-LABEL: define dso_local void @_Z5byval5Large +entry: +; CHECK: llvm.dbg.declare + call void @llvm.dbg.declare(metadata %struct.Large* %X, metadata !118, metadata !DIExpression()), !dbg !119 + ret void, !dbg !120 +} + +;; Caller-allocated memory for a local (sret parameter) with address stored to +;; local alloca. +;; +;; LCopyCtor indirectReturn() { +;; LCopyCtor R; +;; return R; +;; } +;; +;; A sret parameter is used here also, but in this case clang emits an alloca +;; to store the passed-in address for the storage for the variable. The +;; dbg.declare for the local R therefore requires a DW_OP_deref expression. +;; TODO: This isn't supported yet, so check the dbg.declare remains. +define dso_local void @_Z14indirectReturnv(%struct.LCopyCtor* noalias sret(%struct.LCopyCtor) align 4 %agg.result) #0 !dbg !121 { +; CHECK-LABEL: define dso_local void @_Z14indirectReturnv +entry: + %result.ptr = alloca i8*, align 8 + %0 = bitcast %struct.LCopyCtor* %agg.result to i8* + store i8* %0, i8** %result.ptr, align 8 + call void @llvm.dbg.declare(metadata i8** %result.ptr, metadata !126, metadata !DIExpression(DW_OP_deref)), !dbg !127 +; CHECK: call void @llvm.dbg.declare + call void @_ZN9LCopyCtorC1Ev(%struct.LCopyCtor* noundef nonnull align 4 dereferenceable(16) %agg.result), !dbg !127 + ret void, !dbg !128 +} + +;; Inlined variable. +;; +;; __attribute__((always_inline)) +;; int sqr(int Y) { return Y * Y; } +;; int fun(int X) { return sqr(X); } +;; +;; Check that dbg.assign intrinsics correctly inherit the !dbg attachment from +;; the dbg.declre. +define dso_local noundef i32 @_Z3funi(i32 noundef %X) !dbg !139 { +; CHECK-LABEL: define dso_local noundef i32 @_Z3funi +entry: + %Y.addr.i = alloca i32, align 4 +; CHECK: %Y.addr.i = alloca i32, align 4, !DIAssignID ![[ID_10:[0-9]+]] +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR_6:[0-9]+]], metadata !DIExpression(), metadata ![[ID_10]], metadata i32* %Y.addr.i, metadata !DIExpression()), !dbg ![[DBG_0:[0-9]+]] + %X.addr = alloca i32, align 4 +; CHECK-NEXT: %X.addr = alloca i32, align 4, !DIAssignID ![[ID_11:[0-9]+]] +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[VAR_7:[0-9]+]], metadata !DIExpression(), metadata ![[ID_11]], metadata i32* %X.addr, metadata !DIExpression()), !dbg ![[DBG_1:[0-9]+]] + store i32 %X, i32* %X.addr, align 4 +; CHECK-NEXT: store i32 %X, i32* %X.addr, align 4, !DIAssignID ![[ID_12:[0-9]+]] +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i32 %X, metadata ![[VAR_7]], metadata !DIExpression(), metadata ![[ID_12]], metadata i32* %X.addr, metadata !DIExpression()), !dbg ![[DBG_1]] + call void @llvm.dbg.declare(metadata i32* %X.addr, metadata !140, metadata !DIExpression()), !dbg !141 + %0 = load i32, i32* %X.addr, align 4, !dbg !142 + store i32 %0, i32* %Y.addr.i, align 4 +; CHECK: store i32 %0, i32* %Y.addr.i, align 4, !DIAssignID ![[ID_13:[0-9]+]] +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i32 %0, metadata ![[VAR_6]], metadata !DIExpression(), metadata ![[ID_13]], metadata i32* %Y.addr.i, metadata !DIExpression()), !dbg ![[DBG_0]] + call void @llvm.dbg.declare(metadata i32* %Y.addr.i, metadata !133, metadata !DIExpression()), !dbg !143 + %1 = load i32, i32* %Y.addr.i, align 4, !dbg !145 + %2 = load i32, i32* %Y.addr.i, align 4, !dbg !146 + %mul.i = mul nsw i32 %1, %2, !dbg !147 + ret i32 %mul.i, !dbg !148 +} + +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1 +declare void @llvm.dbg.declare(metadata, metadata, metadata) #2 +declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #3 +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) #4 +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1 +declare dso_local void @_ZN9LCopyCtorC1Ev(%struct.LCopyCtor* noundef nonnull align 4 dereferenceable(16)) unnamed_addr + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!26, !27, !28, !29} +!llvm.ident = !{!30} + +; CHECK-DAG: ![[VAR_0]] = !DILocalVariable(name: "Z", +; CHECK-DAG: ![[VAR_1]] = !DILocalVariable(name: "A", +; CHECK-DAG: ![[VAR_2]] = !DILocalVariable(name: "O", +; CHECK-DAG: ![[VAR_3]] = !DILocalVariable(name: "A", +; CHECK-DAG: ![[VAR_4]] = !DILocalVariable(name: "B", +; CHECK-DAG: ![[VAR_5]] = !DILocalVariable(name: "A", +; CHECK-DAG: ![[VAR_6]] = !DILocalVariable(name: "Y", arg: 1, scope: ![[SQR:[0-9]+]], +; CHECK-DAG: ![[VAR_7]] = !DILocalVariable(name: "X", arg: 1, scope: ![[FUN:[0-9]+]], +; CHECK-DAG: ![[SQR]] = distinct !DISubprogram(name: "sqr", +; CHECK-DAG: ![[FUN]] = distinct !DISubprogram(name: "fun", +; CHECK-DAG: ![[DBG_0]] = !DILocation(line: 0, scope: ![[SQR]], inlinedAt: ![[SQR_INLINE_SITE:[0-9]+]]) +; CHECK-DAG: [[SQR_INLINE_SITE]] = distinct !DILocation(line: 3, column: 25, scope: ![[FUN]]) +; CHECK-DAG: ![[DBG_1]] = !DILocation(line: 0, scope: ![[FUN]]) + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "Value", scope: !2, file: !3, line: 5, type: !7, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !3, producer: "clang version 14.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None) +!3 = !DIFile(filename: "test.cpp", directory: "/") +!4 = !{!0, !5, !8, !10, !16, !18} +!5 = !DIGlobalVariableExpression(var: !6, expr: !DIExpression()) +!6 = distinct !DIGlobalVariable(name: "Index", scope: !2, file: !3, line: 5, type: !7, isLocal: false, isDefinition: true) +!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!8 = !DIGlobalVariableExpression(var: !9, expr: !DIExpression()) +!9 = distinct !DIGlobalVariable(name: "Cond", scope: !2, file: !3, line: 5, type: !7, isLocal: false, isDefinition: true) +!10 = !DIGlobalVariableExpression(var: !11, expr: !DIExpression()) +!11 = distinct !DIGlobalVariable(name: "InnerA", scope: !2, file: !3, line: 6, type: !12, isLocal: false, isDefinition: true) +!12 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Inner", file: !3, line: 1, size: 64, flags: DIFlagTypePassByValue, elements: !13, identifier: "_ZTS5Inner") +!13 = !{!14, !15} +!14 = !DIDerivedType(tag: DW_TAG_member, name: "A", scope: !12, file: !3, line: 1, baseType: !7, size: 32) +!15 = !DIDerivedType(tag: DW_TAG_member, name: "B", scope: !12, file: !3, line: 1, baseType: !7, size: 32, offset: 32) +!16 = !DIGlobalVariableExpression(var: !17, expr: !DIExpression()) +!17 = distinct !DIGlobalVariable(name: "InnerB", scope: !2, file: !3, line: 6, type: !12, isLocal: false, isDefinition: true) +!18 = !DIGlobalVariableExpression(var: !19, expr: !DIExpression()) +!19 = distinct !DIGlobalVariable(name: "L", scope: !2, file: !3, line: 7, type: !20, isLocal: false, isDefinition: true) +!20 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Large", file: !3, line: 3, size: 320, flags: DIFlagTypePassByValue, elements: !21, identifier: "_ZTS5Large") +!21 = !{!22} +!22 = !DIDerivedType(tag: DW_TAG_member, name: "A", scope: !20, file: !3, line: 3, baseType: !23, size: 320) +!23 = !DICompositeType(tag: DW_TAG_array_type, baseType: !7, size: 320, elements: !24) +!24 = !{!25} +!25 = !DISubrange(count: 10) +!26 = !{i32 7, !"Dwarf Version", i32 5} +!27 = !{i32 2, !"Debug Info Version", i32 3} +!28 = !{i32 1, !"wchar_size", i32 4} +!29 = !{i32 7, !"uwtable", i32 1} +!30 = !{!"clang version 14.0.0"} +!31 = distinct !DISubprogram(name: "zeroInit", linkageName: "_Z8zeroInitv", scope: !3, file: !3, line: 9, type: !32, scopeLine: 9, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !34) +!32 = !DISubroutineType(types: !33) +!33 = !{null} +!34 = !{!35} +!35 = !DILocalVariable(name: "Z", scope: !31, file: !3, line: 9, type: !36) +!36 = !DICompositeType(tag: DW_TAG_array_type, baseType: !7, size: 96, elements: !37) +!37 = !{!38} +!38 = !DISubrange(count: 3) +!39 = !DILocation(line: 9, column: 19, scope: !31) +!40 = !DILocation(line: 9, column: 23, scope: !31) +!41 = !DILocation(line: 9, column: 41, scope: !31) +!42 = distinct !DISubprogram(name: "memcpyInit", linkageName: "_Z10memcpyInitv", scope: !3, file: !3, line: 11, type: !32, scopeLine: 11, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !43) +!43 = !{!44} +!44 = !DILocalVariable(name: "A", scope: !42, file: !3, line: 11, type: !45) +!45 = !DICompositeType(tag: DW_TAG_array_type, baseType: !7, size: 128, elements: !46) +!46 = !{!47} +!47 = !DISubrange(count: 4) +!48 = !DILocation(line: 11, column: 21, scope: !42) +!49 = !DILocation(line: 11, column: 25, scope: !42) +!50 = !DILocation(line: 11, column: 46, scope: !42) +!51 = distinct !DISubprogram(name: "setField", linkageName: "_Z8setFieldv", scope: !3, file: !3, line: 13, type: !32, scopeLine: 13, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !52) +!52 = !{!53} +!53 = !DILocalVariable(name: "O", scope: !51, file: !3, line: 14, type: !54) +!54 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Outer", file: !3, line: 2, size: 128, flags: DIFlagTypePassByValue, elements: !55, identifier: "_ZTS5Outer") +!55 = !{!56, !57} +!56 = !DIDerivedType(tag: DW_TAG_member, name: "A", scope: !54, file: !3, line: 2, baseType: !12, size: 64) +!57 = !DIDerivedType(tag: DW_TAG_member, name: "B", scope: !54, file: !3, line: 2, baseType: !12, size: 64, offset: 64) +!58 = !DILocation(line: 14, column: 3, scope: !51) +!59 = !DILocation(line: 14, column: 9, scope: !51) +!60 = !DILocation(line: 15, column: 11, scope: !51) +!61 = !{!62, !62, i64 0} +!62 = !{!"int", !63, i64 0} +!63 = !{!"omnipotent char", !64, i64 0} +!64 = !{!"Simple C++ TBAA"} +!65 = !DILocation(line: 15, column: 5, scope: !51) +!66 = !DILocation(line: 15, column: 7, scope: !51) +!67 = !DILocation(line: 15, column: 9, scope: !51) +!68 = !{!69, !62, i64 4} +!69 = !{!"_ZTS5Outer", !70, i64 0, !70, i64 8} +!70 = !{!"_ZTS5Inner", !62, i64 0, !62, i64 4} +!71 = !DILocation(line: 16, column: 1, scope: !51) +!72 = distinct !DISubprogram(name: "unknownOffset", linkageName: "_Z13unknownOffsetv", scope: !3, file: !3, line: 18, type: !32, scopeLine: 18, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !73) +!73 = !{!74} +!74 = !DILocalVariable(name: "A", scope: !72, file: !3, line: 19, type: !75) +!75 = !DICompositeType(tag: DW_TAG_array_type, baseType: !7, size: 64, elements: !76) +!76 = !{!77} +!77 = !DISubrange(count: 2) +!78 = !DILocation(line: 19, column: 3, scope: !72) +!79 = !DILocation(line: 19, column: 7, scope: !72) +!80 = !DILocation(line: 20, column: 14, scope: !72) +!81 = !DILocation(line: 20, column: 5, scope: !72) +!82 = !DILocation(line: 20, column: 3, scope: !72) +!83 = !DILocation(line: 20, column: 12, scope: !72) +!84 = !DILocation(line: 21, column: 1, scope: !72) +!85 = distinct !DISubprogram(name: "sharedAlloca", linkageName: "_Z12sharedAllocav", scope: !3, file: !3, line: 23, type: !86, scopeLine: 23, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !88) +!86 = !DISubroutineType(types: !87) +!87 = !{!12} +!88 = !{!89, !92} +!89 = !DILocalVariable(name: "A", scope: !90, file: !3, line: 25, type: !12) +!90 = distinct !DILexicalBlock(scope: !91, file: !3, line: 24, column: 13) +!91 = distinct !DILexicalBlock(scope: !85, file: !3, line: 24, column: 7) +!92 = !DILocalVariable(name: "B", scope: !93, file: !3, line: 28, type: !12) +!93 = distinct !DILexicalBlock(scope: !91, file: !3, line: 27, column: 10) +!94 = !DILocation(line: 24, column: 7, scope: !91) +!95 = !DILocation(line: 24, column: 7, scope: !85) +!96 = !DILocation(line: 25, column: 11, scope: !90) +!97 = !DILocation(line: 25, column: 15, scope: !90) +!98 = !{i64 0, i64 4, !61, i64 4, i64 4, !61} +!99 = !DILocation(line: 26, column: 5, scope: !90) +!100 = !DILocation(line: 28, column: 11, scope: !93) +!101 = !DILocation(line: 28, column: 15, scope: !93) +!102 = !DILocation(line: 29, column: 5, scope: !93) +!103 = !DILocation(line: 31, column: 1, scope: !85) +!104 = distinct !DISubprogram(name: "sret", linkageName: "_Z4sretv", scope: !3, file: !3, line: 33, type: !105, scopeLine: 33, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !107) +!105 = !DISubroutineType(types: !106) +!106 = !{!20} +!107 = !{!108} +!108 = !DILocalVariable(name: "X", scope: !104, file: !3, line: 34, type: !20) +!109 = !DILocation(line: 34, column: 9, scope: !104) +!110 = !DILocation(line: 34, column: 13, scope: !104) +!111 = !{i64 0, i64 40, !112} +!112 = !{!63, !63, i64 0} +!113 = !DILocation(line: 35, column: 3, scope: !104) +!114 = distinct !DISubprogram(name: "byval", linkageName: "_Z5byval5Large", scope: !3, file: !3, line: 38, type: !115, scopeLine: 38, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !117) +!115 = !DISubroutineType(types: !116) +!116 = !{null, !20} +!117 = !{!118} +!118 = !DILocalVariable(name: "X", arg: 1, scope: !114, file: !3, line: 38, type: !20) +!119 = !DILocation(line: 38, column: 18, scope: !114) +!120 = !DILocation(line: 38, column: 22, scope: !114) +!121 = distinct !DISubprogram(name: "indirectReturn", linkageName: "_Z14indirectReturnv", scope: !2, file: !3, line: 41, type: !122, scopeLine: 41, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !125) +!122 = !DISubroutineType(types: !123) +!123 = !{!124} +!124 = !DICompositeType(tag: DW_TAG_structure_type, name: "LCopyCtor", file: !3, line: 4, size: 128, flags: DIFlagFwdDecl | DIFlagNonTrivial, identifier: "_ZTS9LCopyCtor") +!125 = !{} +!126 = !DILocalVariable(name: "R", scope: !121, file: !3, line: 42, type: !124) +!127 = !DILocation(line: 42, column: 13, scope: !121) +!128 = !DILocation(line: 43, column: 3, scope: !121) +!129 = distinct !DISubprogram(name: "sqr", linkageName: "_Z3sqri", scope: !2, file: !3, line: 2, type: !130, scopeLine: 2, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !132) +!130 = !DISubroutineType(types: !131) +!131 = !{!7, !7} +!132 = !{} +!133 = !DILocalVariable(name: "Y", arg: 1, scope: !129, file: !3, line: 2, type: !7) +!134 = !DILocation(line: 2, column: 13, scope: !129) +!135 = !DILocation(line: 2, column: 25, scope: !129) +!136 = !DILocation(line: 2, column: 29, scope: !129) +!137 = !DILocation(line: 2, column: 27, scope: !129) +!138 = !DILocation(line: 2, column: 18, scope: !129) +!139 = distinct !DISubprogram(name: "fun", linkageName: "_Z3funi", scope: !2, file: !3, line: 3, type: !130, scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !132) +!140 = !DILocalVariable(name: "X", arg: 1, scope: !139, file: !3, line: 3, type: !7) +!141 = !DILocation(line: 3, column: 13, scope: !139) +!142 = !DILocation(line: 3, column: 29, scope: !139) +!143 = !DILocation(line: 2, column: 13, scope: !129, inlinedAt: !144) +!144 = distinct !DILocation(line: 3, column: 25, scope: !139) +!145 = !DILocation(line: 2, column: 25, scope: !129, inlinedAt: !144) +!146 = !DILocation(line: 2, column: 29, scope: !129, inlinedAt: !144) +!147 = !DILocation(line: 2, column: 27, scope: !129, inlinedAt: !144) +!148 = !DILocation(line: 3, column: 18, scope: !139)