[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
This commit is contained in:
parent
322cf744b9
commit
913b561c0a
|
@ -37,6 +37,7 @@ namespace llvm {
|
||||||
class LLVMContext;
|
class LLVMContext;
|
||||||
class Module;
|
class Module;
|
||||||
class Value;
|
class Value;
|
||||||
|
class DbgAssignIntrinsic;
|
||||||
|
|
||||||
class DIBuilder {
|
class DIBuilder {
|
||||||
Module &M;
|
Module &M;
|
||||||
|
@ -47,6 +48,7 @@ namespace llvm {
|
||||||
Function *ValueFn; ///< llvm.dbg.value
|
Function *ValueFn; ///< llvm.dbg.value
|
||||||
Function *LabelFn; ///< llvm.dbg.label
|
Function *LabelFn; ///< llvm.dbg.label
|
||||||
Function *AddrFn; ///< llvm.dbg.addr
|
Function *AddrFn; ///< llvm.dbg.addr
|
||||||
|
Function *AssignFn; ///< llvm.dbg.assign
|
||||||
|
|
||||||
SmallVector<TrackingMDNodeRef, 4> AllEnumTypes;
|
SmallVector<TrackingMDNodeRef, 4> AllEnumTypes;
|
||||||
/// Track the RetainTypes, since they can be updated later on.
|
/// Track the RetainTypes, since they can be updated later on.
|
||||||
|
@ -917,6 +919,26 @@ namespace llvm {
|
||||||
DIExpression *Expr, const DILocation *DL,
|
DIExpression *Expr, const DILocation *DL,
|
||||||
BasicBlock *InsertAtEnd);
|
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.
|
/// Insert a new llvm.dbg.declare intrinsic call.
|
||||||
/// \param Storage llvm::Value of the variable
|
/// \param Storage llvm::Value of the variable
|
||||||
/// \param VarInfo Variable's debug info descriptor.
|
/// \param VarInfo Variable's debug info descriptor.
|
||||||
|
|
|
@ -18,10 +18,13 @@
|
||||||
|
|
||||||
#include "llvm/ADT/STLExtras.h"
|
#include "llvm/ADT/STLExtras.h"
|
||||||
#include "llvm/ADT/SmallPtrSet.h"
|
#include "llvm/ADT/SmallPtrSet.h"
|
||||||
|
#include "llvm/ADT/SmallSet.h"
|
||||||
#include "llvm/ADT/SmallVector.h"
|
#include "llvm/ADT/SmallVector.h"
|
||||||
#include "llvm/ADT/TinyPtrVector.h"
|
#include "llvm/ADT/TinyPtrVector.h"
|
||||||
#include "llvm/ADT/iterator_range.h"
|
#include "llvm/ADT/iterator_range.h"
|
||||||
|
#include "llvm/IR/DataLayout.h"
|
||||||
#include "llvm/IR/IntrinsicInst.h"
|
#include "llvm/IR/IntrinsicInst.h"
|
||||||
|
#include "llvm/IR/PassManager.h"
|
||||||
|
|
||||||
namespace llvm {
|
namespace llvm {
|
||||||
|
|
||||||
|
@ -225,8 +228,75 @@ void RAUW(DIAssignID *Old, DIAssignID *New);
|
||||||
/// Remove all Assignment Tracking related intrinsics and metadata from \p F.
|
/// Remove all Assignment Tracking related intrinsics and metadata from \p F.
|
||||||
void deleteAll(Function *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
|
} // 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.
|
/// Return true if assignment tracking is enabled.
|
||||||
bool getEnableAssignmentTracking();
|
bool getEnableAssignmentTracking();
|
||||||
} // end namespace llvm
|
} // end namespace llvm
|
||||||
|
|
|
@ -30,7 +30,7 @@ static cl::opt<bool>
|
||||||
|
|
||||||
DIBuilder::DIBuilder(Module &m, bool AllowUnresolvedNodes, DICompileUnit *CU)
|
DIBuilder::DIBuilder(Module &m, bool AllowUnresolvedNodes, DICompileUnit *CU)
|
||||||
: M(m), VMContext(M.getContext()), CUNode(CU), DeclareFn(nullptr),
|
: 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) {
|
AllowUnresolvedNodes(AllowUnresolvedNodes) {
|
||||||
if (CUNode) {
|
if (CUNode) {
|
||||||
if (const auto &ETs = CUNode->getEnumTypes())
|
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);
|
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 *DIBuilder::insertLabel(DILabel *LabelInfo, const DILocation *DL,
|
||||||
Instruction *InsertBefore) {
|
Instruction *InsertBefore) {
|
||||||
return insertLabel(LabelInfo, DL,
|
return insertLabel(LabelInfo, DL,
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include "llvm/IR/LLVMContext.h"
|
#include "llvm/IR/LLVMContext.h"
|
||||||
#include "llvm/IR/Metadata.h"
|
#include "llvm/IR/Metadata.h"
|
||||||
#include "llvm/IR/Module.h"
|
#include "llvm/IR/Module.h"
|
||||||
|
#include "llvm/IR/PassManager.h"
|
||||||
#include "llvm/Support/Casting.h"
|
#include "llvm/Support/Casting.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
@ -1713,3 +1714,237 @@ void at::deleteAll(Function *F) {
|
||||||
for (auto *DAI : ToDelete)
|
for (auto *DAI : ToDelete)
|
||||||
DAI->eraseFromParent();
|
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
|
||||||
|
|
|
@ -73,6 +73,7 @@
|
||||||
#include "llvm/Analysis/TargetLibraryInfo.h"
|
#include "llvm/Analysis/TargetLibraryInfo.h"
|
||||||
#include "llvm/Analysis/TargetTransformInfo.h"
|
#include "llvm/Analysis/TargetTransformInfo.h"
|
||||||
#include "llvm/Analysis/TypeBasedAliasAnalysis.h"
|
#include "llvm/Analysis/TypeBasedAliasAnalysis.h"
|
||||||
|
#include "llvm/IR/DebugInfo.h"
|
||||||
#include "llvm/IR/Dominators.h"
|
#include "llvm/IR/Dominators.h"
|
||||||
#include "llvm/IR/IRPrintingPasses.h"
|
#include "llvm/IR/IRPrintingPasses.h"
|
||||||
#include "llvm/IR/PassManager.h"
|
#include "llvm/IR/PassManager.h"
|
||||||
|
|
|
@ -394,6 +394,7 @@ FUNCTION_PASS("tlshoist", TLSVariableHoistPass())
|
||||||
FUNCTION_PASS("transform-warning", WarnMissedTransformationsPass())
|
FUNCTION_PASS("transform-warning", WarnMissedTransformationsPass())
|
||||||
FUNCTION_PASS("tsan", ThreadSanitizerPass())
|
FUNCTION_PASS("tsan", ThreadSanitizerPass())
|
||||||
FUNCTION_PASS("memprof", MemProfilerPass())
|
FUNCTION_PASS("memprof", MemProfilerPass())
|
||||||
|
FUNCTION_PASS("declare-to-assign", llvm::AssignmentTrackingPass())
|
||||||
#undef FUNCTION_PASS
|
#undef FUNCTION_PASS
|
||||||
|
|
||||||
#ifndef FUNCTION_PASS_WITH_PARAMS
|
#ifndef FUNCTION_PASS_WITH_PARAMS
|
||||||
|
|
|
@ -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)
|
Loading…
Reference in New Issue