Skip to content

[DebugInfo] Return complete variable info from getVarInfo by default #73555

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
May 13, 2024
Merged
3 changes: 3 additions & 0 deletions docs/HowToUpdateDebugInfo.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ debug_value %0 : $*T, let, name "address", type $UnsafeRawPointer
The variable will usually have an associated expression yielding the correct
type.

> [!Note]
> As there are no pointers in Swift, the type should never be an address type.

### Variable expressions

A variable can have an associated expression if the value needs computation.
Expand Down
16 changes: 12 additions & 4 deletions include/swift/SIL/DebugUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -437,16 +437,24 @@ struct DebugVarCarryingInst : VarDeclCarryingInst {

Kind getKind() const { return Kind(VarDeclCarryingInst::getKind()); }

std::optional<SILDebugVariable> getVarInfo() const {
/// Returns the debug variable information attached to the instruction.
///
/// \param complete If true, always retrieve the complete variable with
/// location and scope, and the type if possible. If false, only return the
/// values if they are stored (if they are different from the instruction's
/// location, scope, and type). This should only be set to false in
/// SILPrinter. Incomplete var info is unpredictable, as it will sometimes
/// have location and scope and sometimes not.
std::optional<SILDebugVariable> getVarInfo(bool complete = true) const {
switch (getKind()) {
case Kind::Invalid:
llvm_unreachable("Invalid?!");
case Kind::DebugValue:
return cast<DebugValueInst>(**this)->getVarInfo();
return cast<DebugValueInst>(**this)->getVarInfo(complete);
case Kind::AllocStack:
return cast<AllocStackInst>(**this)->getVarInfo();
return cast<AllocStackInst>(**this)->getVarInfo(complete);
case Kind::AllocBox:
return cast<AllocBoxInst>(**this)->getVarInfo();
return cast<AllocBoxInst>(**this)->getVarInfo(complete);
}
llvm_unreachable("covered switch");
}
Expand Down
2 changes: 1 addition & 1 deletion include/swift/SIL/SILBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ class SILBuilder {
#endif
// Don't apply location overrides on variables.
if (Var && !Var->Loc)
Var->Loc = Loc;
Var->Loc = Loc.strippedForDebugVariable();
return insert(AllocStackInst::create(
getSILDebugLocation(Loc, true), elementType, getFunction(),
substituteAnonymousArgs(Name, Var, Loc), dynamic, isLexical,
Expand Down
10 changes: 10 additions & 0 deletions include/swift/SIL/SILDebugInfoExpression.h
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,16 @@ class SILDebugInfoExpression {
return Elements.size() &&
Elements[0].getAsOperator() == SILDIExprOperator::Dereference;
}

/// Return the part of this SILDebugInfoExpression corresponding to fragments
SILDebugInfoExpression getFragmentPart() const {
for (auto it = element_begin(), end = element_end(); it != end; ++it) {
if (it->getAsOperator() == SILDIExprOperator::Fragment
|| it->getAsOperator() == SILDIExprOperator::TupleFragment)
return SILDebugInfoExpression(ArrayRef(it, element_end()));
}
return {};
}
};

/// Returns the hashcode for the di expr element.
Expand Down
57 changes: 48 additions & 9 deletions include/swift/SIL/SILInstruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -1875,9 +1875,9 @@ class TailAllocatedDebugVariable {
bool isLet() const { return Bits.Data.Constant; }

std::optional<SILDebugVariable>
get(VarDecl *VD, const char *buf, std::optional<SILType> AuxVarType = {},
std::optional<SILLocation> DeclLoc = {},
const SILDebugScope *DeclScope = nullptr,
get(VarDecl *VD, const char *buf, std::optional<SILType> AuxVarType,
std::optional<SILLocation> DeclLoc,
const SILDebugScope *DeclScope,
llvm::ArrayRef<SILDIExprElement> DIExprElements = {}) const {
if (!Bits.Data.HasValue)
return std::nullopt;
Expand Down Expand Up @@ -2087,11 +2087,18 @@ class AllocStackInst final
SILLocation getVarLoc() const {
if (hasAuxDebugLocation())
return *getTrailingObjects<SILLocation>();
return getLoc();
return getLoc().strippedForDebugVariable();
}

/// Return the debug variable information attached to this instruction.
std::optional<SILDebugVariable> getVarInfo() const {
///
/// \param complete If true, always retrieve the complete variable with
/// location, scope, and element type. If false, only return the
/// values if they are stored (if they are different from the instruction's
/// location, scope, and type). This should only be set to false in
/// SILPrinter. Incomplete var info is unpredictable, as it will sometimes
/// have location and scope and sometimes not.
std::optional<SILDebugVariable> getVarInfo(bool complete = true) const {
// If we used to have debug info attached but our debug info is now
// invalidated, just bail.
if (sharedUInt8().AllocStackInst.hasInvalidatedVarInfo) {
Expand All @@ -2103,11 +2110,18 @@ class AllocStackInst final
const SILDebugScope *VarDeclScope = nullptr;
if (HasAuxDebugVariableType)
AuxVarType = *getTrailingObjects<SILType>();
else if (complete)
AuxVarType = getElementType();

if (hasAuxDebugLocation())
VarDeclLoc = *getTrailingObjects<SILLocation>();
else if (complete)
VarDeclLoc = getLoc().strippedForDebugVariable();

if (hasAuxDebugScope())
VarDeclScope = *getTrailingObjects<const SILDebugScope *>();
else if (complete)
VarDeclScope = getDebugScope();

llvm::ArrayRef<SILDIExprElement> DIExprElements(
getTrailingObjects<SILDIExprElement>(), NumDIExprOperands);
Expand Down Expand Up @@ -2509,8 +2523,13 @@ class AllocBoxInst final
SILType getAddressType() const;

/// Return the debug variable information attached to this instruction.
std::optional<SILDebugVariable> getVarInfo() const {
return VarInfo.get(getDecl(), getTrailingObjects<char>());
std::optional<SILDebugVariable> getVarInfo(bool complete = true) const {
if (complete)
return VarInfo.get(getDecl(), getTrailingObjects<char>(),
getAddressType().getObjectType(),
getLoc().strippedForDebugVariable(),
getDebugScope());
return VarInfo.get(getDecl(), getTrailingObjects<char>(), {}, {}, nullptr);
};

void setUsesMoveableValueDebugInfo() {
Expand Down Expand Up @@ -5385,21 +5404,41 @@ class DebugValueInst final
SILLocation getVarLoc() const {
if (hasAuxDebugLocation())
return *getTrailingObjects<SILLocation>();
return getLoc();
return getLoc().strippedForDebugVariable();
}

/// Return the debug variable information attached to this instruction.
std::optional<SILDebugVariable> getVarInfo() const {
///
/// \param complete If true, always retrieve the complete variable with
/// location and scope, and the type if possible. If false, only return the
/// values if they are stored (if they are different from the instruction's
/// location, scope, and type). This should only be set to false in
/// SILPrinter. Incomplete var info is unpredictable, as it will sometimes
/// have location and scope and sometimes not.
///
/// \note The type is not included because it can change during a pass.
/// Passes must make sure to not lose the type information.
std::optional<SILDebugVariable> getVarInfo(bool complete = true) const {
std::optional<SILType> AuxVarType;
std::optional<SILLocation> VarDeclLoc;
const SILDebugScope *VarDeclScope = nullptr;

if (HasAuxDebugVariableType)
AuxVarType = *getTrailingObjects<SILType>();
// TODO: passes break if we set the type here, as the type of the operand
// can be changed during a pass.
// else if (complete)
// AuxVarType = getOperand()->getType().getObjectType();

if (hasAuxDebugLocation())
VarDeclLoc = *getTrailingObjects<SILLocation>();
else if (complete)
VarDeclLoc = getLoc().strippedForDebugVariable();

if (hasAuxDebugScope())
VarDeclScope = *getTrailingObjects<const SILDebugScope *>();
else if (complete)
VarDeclScope = getDebugScope();

llvm::ArrayRef<SILDIExprElement> DIExprElements(
getTrailingObjects<SILDIExprElement>(), NumDIExprOperands);
Expand Down
9 changes: 7 additions & 2 deletions include/swift/SIL/SILLocation.h
Original file line number Diff line number Diff line change
Expand Up @@ -520,8 +520,13 @@ class SILLocation {
};

inline llvm::hash_code hash_value(const SILLocation &R) {
return llvm::hash_combine(R.kindAndFlags.packedKindAndFlags,
*R.storage.filePositionLoc);
if (R.isFilenameAndLocation()) {
return llvm::hash_combine(R.kindAndFlags.packedKindAndFlags,
*R.storage.filePositionLoc);
} else {
return llvm::hash_combine(R.kindAndFlags.packedKindAndFlags,
R.storage.filePositionLoc);
}
}

inline llvm::hash_code hash_value(const SILLocation::FilenameAndLocation &R) {
Expand Down
2 changes: 1 addition & 1 deletion lib/IRGen/IRGenSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -679,7 +679,7 @@ class IRGenSILFunction :
if (!VarInfo)
return StringRef();

StringRef Name = i->getVarInfo()->Name;
StringRef Name = VarInfo->Name;
// The $match variables generated by the type checker are not
// guaranteed to be unique within their scope, but they have
// unique VarDecls.
Expand Down
4 changes: 3 additions & 1 deletion lib/SIL/IR/SILBasicBlock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -342,8 +342,10 @@ transferNodesFromList(llvm::ilist_traits<SILBasicBlock> &SrcTraits,

II.setDebugScope(ScopeCloner.getOrCreateClonedScope(II.getDebugScope()));
// Special handling for SILDebugVariable.
// Fetch incomplete var info to avoid calling setDebugVarScope on
// alloc_box, crashing.
if (auto DVI = DebugVarCarryingInst(&II))
if (auto VarInfo = DVI.getVarInfo())
if (auto VarInfo = DVI.getVarInfo(false))
if (VarInfo->Scope)
DVI.setDebugVarScope(
ScopeCloner.getOrCreateClonedScope(VarInfo->Scope));
Expand Down
24 changes: 4 additions & 20 deletions lib/SIL/IR/SILInstructions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,26 +199,10 @@ StringRef TailAllocatedDebugVariable::getName(const char *buf) const {

std::optional<SILDebugVariable>
SILDebugVariable::createFromAllocation(const AllocationInst *AI) {
std::optional<SILDebugVariable> VarInfo;
if (const auto *ASI = dyn_cast_or_null<AllocStackInst>(AI))
VarInfo = ASI->getVarInfo();
return ASI->getVarInfo();
// TODO: Support AllocBoxInst

if (!VarInfo)
return {};

// Coalesce the debug loc attached on AI into VarInfo
SILType Type = AI->getType();
SILLocation InstLoc = AI->getLoc();
const SILDebugScope *InstDS = AI->getDebugScope();
if (!VarInfo->Type)
VarInfo->Type = Type;
if (!VarInfo->Loc)
VarInfo->Loc = InstLoc;
if (!VarInfo->Scope)
VarInfo->Scope = InstDS;

return VarInfo;
return {};
}

AllocStackInst::AllocStackInst(
Expand Down Expand Up @@ -268,7 +252,7 @@ AllocStackInst *AllocStackInst::create(SILDebugLocation Loc,
UsesMoveableValueDebugInfo_t wasMoved) {
// Don't store the same information twice.
if (Var) {
if (Var->Loc == Loc.getLocation())
if (Var->Loc == Loc.getLocation().strippedForDebugVariable())
Var->Loc = {};
if (Var->Scope == Loc.getScope())
Var->Scope = nullptr;
Expand Down Expand Up @@ -473,7 +457,7 @@ DebugValueInst *DebugValueInst::create(SILDebugLocation DebugLoc,
UsesMoveableValueDebugInfo_t wasMoved,
bool trace) {
// Don't store the same information twice.
if (Var.Loc == DebugLoc.getLocation())
if (Var.Loc == DebugLoc.getLocation().strippedForDebugVariable())
Var.Loc = {};
if (Var.Scope == DebugLoc.getScope())
Var.Scope = nullptr;
Expand Down
6 changes: 3 additions & 3 deletions lib/SIL/IR/SILPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1446,7 +1446,7 @@ class SILPrinter : public SILInstructionVisitor<SILPrinter> {
if (AVI->usesMoveableValueDebugInfo() && !AVI->getType().isMoveOnly())
*this << "[moveable_value_debuginfo] ";
*this << AVI->getElementType();
printDebugVar(AVI->getVarInfo(),
printDebugVar(AVI->getVarInfo(false),
&AVI->getModule().getASTContext().SourceMgr);
}
void visitAllocVectorInst(AllocVectorInst *AVI) {
Expand Down Expand Up @@ -1503,7 +1503,7 @@ class SILPrinter : public SILInstructionVisitor<SILPrinter> {
}

*this << ABI->getType();
printDebugVar(ABI->getVarInfo(),
printDebugVar(ABI->getVarInfo(false),
&ABI->getModule().getASTContext().SourceMgr);
}

Expand Down Expand Up @@ -1915,7 +1915,7 @@ class SILPrinter : public SILInstructionVisitor<SILPrinter> {
if (DVI->hasTrace())
*this << "[trace] ";
*this << getIDAndType(DVI->getOperand());
printDebugVar(DVI->getVarInfo(),
printDebugVar(DVI->getVarInfo(false),
&DVI->getModule().getASTContext().SourceMgr);
}

Expand Down
51 changes: 23 additions & 28 deletions lib/SIL/Verifier/SILVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1486,39 +1486,34 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {

void checkDebugVariable(SILInstruction *inst) {
std::optional<SILDebugVariable> varInfo;
if (auto *di = dyn_cast<AllocStackInst>(inst))
varInfo = di->getVarInfo();
else if (auto *di = dyn_cast<AllocBoxInst>(inst))
varInfo = di->getVarInfo();
else if (auto *di = dyn_cast<DebugValueInst>(inst))
varInfo = di->getVarInfo();
if (auto di = DebugVarCarryingInst(inst))
varInfo = di.getVarInfo();

if (!varInfo)
return;

// Retrieve debug variable type
SILType DebugVarTy;
if (varInfo->Type)
DebugVarTy = *varInfo->Type;
else {
// Fetch from related SSA value
switch (inst->getKind()) {
case SILInstructionKind::AllocStackInst:
case SILInstructionKind::AllocBoxInst:
DebugVarTy = inst->getResult(0)->getType();
break;
case SILInstructionKind::DebugValueInst:
DebugVarTy = inst->getOperand(0)->getType();
if (DebugVarTy.isAddress()) {
// FIXME: op_deref could be applied to address types only.
// FIXME: Add this check
if (varInfo->DIExpr.startsWithDeref())
DebugVarTy = DebugVarTy.getObjectType();
}
break;
default:
llvm_unreachable("impossible instruction kind");
}
SILType SSAType;
switch (inst->getKind()) {
case SILInstructionKind::AllocStackInst:
case SILInstructionKind::AllocBoxInst:
// TODO: unwrap box for AllocBox
SSAType = inst->getResult(0)->getType().getObjectType();
break;
case SILInstructionKind::DebugValueInst:
SSAType = inst->getOperand(0)->getType();
break;
default:
llvm_unreachable("impossible instruction kind");
}

SILType DebugVarTy = varInfo->Type ? *varInfo->Type :
SSAType.getObjectType();
if (!varInfo->DIExpr && !isa<AllocBoxInst>(inst)) {
// FIXME: Remove getObjectType() below when we fix create/createAddr
require(DebugVarTy.removingMoveOnlyWrapper()
== SSAType.getObjectType().removingMoveOnlyWrapper(),
"debug type mismatch without a DIExpr");
}

auto *debugScope = inst->getDebugScope();
Expand Down
4 changes: 2 additions & 2 deletions lib/SILOptimizer/Differentiation/Common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -266,12 +266,12 @@ void collectMinimalIndicesForFunctionCall(
std::optional<std::pair<SILDebugLocation, SILDebugVariable>>
findDebugLocationAndVariable(SILValue originalValue) {
if (auto *asi = dyn_cast<AllocStackInst>(originalValue))
return swift::transform(asi->getVarInfo(), [&](SILDebugVariable var) {
return swift::transform(asi->getVarInfo(false), [&](SILDebugVariable var) {
return std::make_pair(asi->getDebugLocation(), var);
});
for (auto *use : originalValue->getUses()) {
if (auto *dvi = dyn_cast<DebugValueInst>(use->getUser()))
return swift::transform(dvi->getVarInfo(), [&](SILDebugVariable var) {
return swift::transform(dvi->getVarInfo(false), [&](SILDebugVariable var) {
// We need to drop `op_deref` here as we're transferring debug info
// location from debug_value instruction (which describes how to get value)
// into alloc_stack (which describes the location)
Expand Down
11 changes: 9 additions & 2 deletions lib/SILOptimizer/Differentiation/JVPCloner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1000,9 +1000,16 @@ class JVPCloner::Implementation final
/// Tangent: tan[y] = alloc_stack $T.Tangent
CLONE_AND_EMIT_TANGENT(AllocStack, asi) {
auto &diffBuilder = getDifferentialBuilder();
auto varInfo = asi->getVarInfo();
if (varInfo) {
// This is a new variable, it shouldn't keep the old scope, type, etc.
varInfo->Type = {};
varInfo->DIExpr = {};
varInfo->Loc = {};
varInfo->Scope = nullptr;
}
auto *mappedAllocStackInst = diffBuilder.createAllocStack(
asi->getLoc(), getRemappedTangentType(asi->getElementType()),
asi->getVarInfo());
asi->getLoc(), getRemappedTangentType(asi->getElementType()), varInfo);
setTangentBuffer(asi->getParent(), asi, mappedAllocStackInst);
}

Expand Down
5 changes: 5 additions & 0 deletions lib/SILOptimizer/Differentiation/PullbackCloner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,11 @@ class PullbackCloner::Implementation final
}
adjNameStream << " (scope #" << origBB->getDebugID() << ")";
dv.Name = adjName;
// We have no meaningful debug location, and the type is different.
dv.Scope = nullptr;
dv.Loc = {};
dv.Type = {};
dv.DIExpr = {};
return dv;
}));
return (insertion.first->getSecond() = newBuf);
Expand Down
Loading