[llvm] 913b561 - [Assignment Tracking][6/*] Add trackAssignments function
via llvm-commits
llvm-commits at lists.llvm.org
Tue Nov 8 08:52:27 PST 2022
Author: OCHyams
Date: 2022-11-08T16:52:11Z
New Revision: 913b561c0a1d5817be7e2bc811a42bd4b9b6ff1e
URL: https://github.com/llvm/llvm-project/commit/913b561c0a1d5817be7e2bc811a42bd4b9b6ff1e
DIFF: https://github.com/llvm/llvm-project/commit/913b561c0a1d5817be7e2bc811a42bd4b9b6ff1e.diff
LOG: [Assignment Tracking][6/*] Add trackAssignments function
The Assignment Tracking debug-info feature is outlined in this RFC:
https://discourse.llvm.org/t/
rfc-assignment-tracking-a-better-way-of-specifying-variable-locations-in-ir
Add trackAssignments which adds assignment tracking metadata to a function for
a specified set of variables. The intended callers are the inliner and the
front end - those calls will be added in separate patches.
I've added a pass called declare-to-assign (AssignmentTrackingPass) that
converts dbg.declare intrinsics to dbg.assigns using trackAssignments so that
the function can be easily tested (see
llvm/test/DebugInfo/Generic/track-assignments.ll). The pass could also be used
by front ends to easily test out enabling assignment tracking.
Reviewed By: jmorse
Differential Revision: https://reviews.llvm.org/D132225
Added:
llvm/test/DebugInfo/Generic/assignment-tracking/track-assignments.ll
Modified:
llvm/include/llvm/IR/DIBuilder.h
llvm/include/llvm/IR/DebugInfo.h
llvm/lib/IR/DIBuilder.cpp
llvm/lib/IR/DebugInfo.cpp
llvm/lib/Passes/PassBuilder.cpp
llvm/lib/Passes/PassRegistry.def
Removed:
################################################################################
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<TrackingMDNodeRef, 4> 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<const AllocaInst *, SmallSet<VarRecord, 2>>;
+
+/// 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<AssignmentInfo> getAssignmentInfo(const DataLayout &DL,
+ const MemIntrinsic *I);
+Optional<AssignmentInfo> getAssignmentInfo(const DataLayout &DL,
+ const StoreInst *SI);
+Optional<AssignmentInfo> 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<AssignmentTrackingPass> {
+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<bool>
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<Value *, 6> 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<DbgAssignIntrinsic>(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 <algorithm>
#include <cassert>
@@ -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<AssignmentInfo> 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<AllocaInst>(Base))
+ return AssignmentInfo(DL, Alloca, OffsetInBytes * 8, SizeInBits);
+ return None;
+}
+
+Optional<AssignmentInfo> at::getAssignmentInfo(const DataLayout &DL,
+ const MemIntrinsic *I) {
+ const Value *StoreDest = I->getRawDest();
+ // Assume 8 bit bytes.
+ auto *ConstLengthInBytes = dyn_cast<ConstantInt>(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<AssignmentInfo> 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<AssignmentInfo> 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<AssignmentInfo> Info = None;
+ Value *ValueComponent = nullptr;
+ Value *DestComponent = nullptr;
+ if (auto *AI = dyn_cast<AllocaInst>(&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<StoreInst>(&I)) {
+ Info = getAssignmentInfo(DL, SI);
+ ValueComponent = SI->getValueOperand();
+ DestComponent = SI->getPointerOperand();
+ } else if (auto *MI = dyn_cast<MemTransferInst>(&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<MemSetInst>(&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<ConstantInt>(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<DIAssignID>(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<const AllocaInst *, SmallPtrSet<DbgDeclareInst *, 2>> 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<DbgDeclareInst>(&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<AllocaInst>(DDI->getAddress()->stripPointerCasts())) {
+ DbgDeclares[Alloca].insert(DDI);
+ Vars[Alloca].insert(VarRecord(DDI));
+ }
+ }
+ }
+
+ auto DL = std::make_unique<DataLayout>(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<CFGAnalyses>();
+ 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<CFGAnalyses>();
+ 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] }
+
+ at Value = dso_local global i32 0, align 4, !dbg !0
+ at Index = dso_local global i32 0, align 4, !dbg !5
+ at Cond = dso_local global i32 0, align 4, !dbg !8
+ at InnerA = dso_local global %struct.Inner zeroinitializer, align 4, !dbg !10
+ at InnerB = dso_local global %struct.Inner zeroinitializer, align 4, !dbg !16
+ at L = dso_local global %struct.Large zeroinitializer, align 4, !dbg !18
+ at __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)
More information about the llvm-commits
mailing list