[SILOptimizer][DebugInfo] Preliminary support for DIExpression in SROA and Mem2Reg

SROA and Mem2Reg now can leverage DIExpression -- op_fragment, more
specifically -- to generate correct debug info for optimized SIL. Some
important highlights:
 - The new swift::salvageDebugInfo, similar to llvm::salvageDebugInfo,
   tries to restore / transfer debug info from a deleted instruction.
   Currently I only implemented this for store instruction whose
   destination is an alloc_stack value.
 - Since we now have source-variable-specific SIL location inside a
   `debug_value` instruction (and its friends), this patch teaches
   SILCloner and SILInliner to remap the debug scope there in addition
   to debug scope of the instruction.
 - DCE now does not remove `debug_value` instruction whose associating
   with a function argument SSA value that is not used elsewhere. Since
   that SSA value will not disappear so we should keep the debug info.
This commit is contained in:
Min-Yih Hsu
2021-08-04 14:20:50 -07:00
parent e63632fda8
commit 9a8f2ed642
25 changed files with 373 additions and 69 deletions

View File

@@ -308,6 +308,24 @@ struct DebugVarCarryingInst {
}
llvm_unreachable("covered switch");
}
void setDebugVarScope(const SILDebugScope *NewDS) {
switch (kind) {
case Kind::Invalid:
llvm_unreachable("Invalid?!");
case Kind::DebugValue:
cast<DebugValueInst>(inst)->setDebugVarScope(NewDS);
break;
case Kind::DebugValueAddr:
cast<DebugValueAddrInst>(inst)->setDebugVarScope(NewDS);
break;
case Kind::AllocStack:
cast<AllocStackInst>(inst)->setDebugVarScope(NewDS);
break;
case Kind::AllocBox:
llvm_unreachable("Not implemented");
}
}
};
} // end namespace swift

View File

@@ -19,6 +19,7 @@
#include "swift/AST/ProtocolConformance.h"
#include "swift/SIL/BasicBlockUtils.h"
#include "swift/SIL/DebugUtils.h"
#include "swift/SIL/Dominance.h"
#include "swift/SIL/SILBuilder.h"
#include "swift/SIL/SILDebugScope.h"
@@ -245,6 +246,16 @@ public:
registerOpenedExistentialRemapping(archetypeTy, replacementTy);
}
// SILCloner will take care of debug scope on the instruction
// and this helper will remap the auxiliary debug scope too, if there is any.
void remapDebugVarInfo(DebugVarCarryingInst DbgVarInst) {
if (!DbgVarInst)
return;
auto VarInfo = DbgVarInst.getVarInfo();
if (VarInfo && VarInfo->Scope)
DbgVarInst.setDebugVarScope(getOpScope(VarInfo->Scope));
}
ProtocolConformanceRef getOpConformance(Type ty,
ProtocolConformanceRef conformance) {
// If we have open existentials to substitute, do so now.
@@ -748,9 +759,11 @@ SILCloner<ImplClass>::visitAllocStackInst(AllocStackInst *Inst) {
Loc = MandatoryInlinedLocation::getAutoGeneratedLocation();
VarInfo = None;
}
recordClonedInstruction(Inst, getBuilder().createAllocStack(
Loc, getOpType(Inst->getElementType()),
VarInfo, Inst->hasDynamicLifetime()));
auto *NewInst =
getBuilder().createAllocStack(Loc, getOpType(Inst->getElementType()),
VarInfo, Inst->hasDynamicLifetime());
remapDebugVarInfo(DebugVarCarryingInst(NewInst));
recordClonedInstruction(Inst, NewInst);
}
template<typename ImplClass>
@@ -1232,12 +1245,13 @@ SILCloner<ImplClass>::visitDebugValueInst(DebugValueInst *Inst) {
return;
// Since we want the debug info to survive, we do not remap the location here.
SILDebugVariable VarInfo = *Inst->getVarInfo();
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
recordClonedInstruction(
Inst, getBuilder().createDebugValue(Inst->getLoc(),
getOpValue(Inst->getOperand()),
*Inst->getVarInfo(),
Inst->poisonRefs()));
auto *NewInst = getBuilder().createDebugValue(Inst->getLoc(),
getOpValue(Inst->getOperand()),
VarInfo, Inst->poisonRefs());
remapDebugVarInfo(DebugVarCarryingInst(NewInst));
recordClonedInstruction(Inst, NewInst);
}
template<typename ImplClass>
void
@@ -1249,11 +1263,13 @@ SILCloner<ImplClass>::visitDebugValueAddrInst(DebugValueAddrInst *Inst) {
return;
// Do not remap the location for a debug Instruction.
SILDebugVariable VarInfo = *Inst->getVarInfo();
SILValue OpValue = getOpValue(Inst->getOperand());
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
recordClonedInstruction(
Inst, getBuilder().createDebugValueAddr(Inst->getLoc(), OpValue,
*Inst->getVarInfo()));
auto *NewInst = getBuilder().createDebugValueAddr(Inst->getLoc(), OpValue,
*Inst->getVarInfo());
remapDebugVarInfo(DebugVarCarryingInst(NewInst));
recordClonedInstruction(Inst, NewInst);
}
#define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, name, ...) \

View File

@@ -123,6 +123,8 @@ public:
explicit SILDebugInfoExpression(llvm::ArrayRef<SILDIExprElement> EL)
: Elements(EL.begin(), EL.end()) {}
void clear() { Elements.clear(); }
size_t getNumElements() const { return Elements.size(); }
using iterator = typename decltype(Elements)::iterator;
@@ -151,6 +153,15 @@ public:
Elements.push_back(Element);
}
void appendElements(llvm::ArrayRef<SILDIExprElement> NewElements) {
if (NewElements.size())
Elements.append(NewElements.begin(), NewElements.end());
}
void append(const SILDebugInfoExpression &Tail) {
appendElements(Tail.Elements);
}
/// The iterator for SILDIExprOperand
class op_iterator {
friend class SILDebugInfoExpression;
@@ -208,6 +219,9 @@ public:
/// Return true if this expression is not empty
inline operator bool() const { return Elements.size(); }
/// Create a op_fragment expression
static SILDebugInfoExpression createFragment(VarDecl *Field);
};
} // end namespace swift
#endif

View File

@@ -1732,6 +1732,10 @@ struct SILDebugVariable {
const SILDebugScope *Scope;
SILDebugInfoExpression DIExpr;
// Use vanilla copy ctor / operator
SILDebugVariable(const SILDebugVariable &) = default;
SILDebugVariable &operator=(const SILDebugVariable &) = default;
SILDebugVariable()
: ArgNo(0), Constant(false), Implicit(false), Scope(nullptr) {}
SILDebugVariable(bool Constant, uint16_t ArgNo)
@@ -1744,6 +1748,10 @@ struct SILDebugVariable {
: Name(Name), ArgNo(ArgNo), Constant(Constant), Implicit(IsImplicit),
Type(AuxType), Loc(DeclLoc), Scope(DeclScope), DIExpr(ExprElements) {}
/// Created from either AllocStack or AllocBox instruction
static Optional<SILDebugVariable>
createFromAllocation(const AllocationInst *AI);
// We're not comparing DIExpr here because strictly speaking,
// DIExpr is not part of the debug variable. We simply piggyback
// it in this class so that's it's easier to carry DIExpr around.
@@ -1962,6 +1970,11 @@ public:
SILNode::Bits.AllocStackInst.VarInfo = VI.getRawValue();
}
void setDebugVarScope(const SILDebugScope *NewDS) {
if (hasAuxDebugScope())
*getTrailingObjects<const SILDebugScope *>() = NewDS;
}
/// getElementType - Get the type of the allocated memory (as opposed to the
/// type of the instruction itself, which will be an address type).
SILType getElementType() const {
@@ -4681,6 +4694,11 @@ public:
VarDeclLoc, VarDeclScope, DIExprElements);
}
void setDebugVarScope(const SILDebugScope *NewDS) {
if (hasAuxDebugScope())
*getTrailingObjects<const SILDebugScope *>() = NewDS;
}
/// True if all references within this debug value will be overwritten with a
/// poison sentinel at this point in the program. This is used in debug builds
/// when shortening non-trivial value lifetimes to ensure the debugger cannot
@@ -4741,7 +4759,12 @@ public:
return VarInfo.get(getDecl(), getTrailingObjects<char>(), AuxVarType,
VarDeclLoc, VarDeclScope, DIExprElements);
};
}
void setDebugVarScope(const SILDebugScope *NewDS) {
if (hasAuxDebugScope())
*getTrailingObjects<const SILDebugScope *>() = NewDS;
}
};
/// An abstract class representing a load from some kind of reference storage.

View File

@@ -43,6 +43,11 @@ inline void deleteAllDebugUses(SILInstruction *inst,
}
}
/// Transfer debug info associated with (the result of) \p I to a
/// new `debug_value` or `debug_value_addr` instruction before \p I is
/// deleted.
void salvageDebugInfo(SILInstruction *I);
/// Erases the instruction \p I from it's parent block and deletes it, including
/// all debug instructions which use \p I.
/// Precondition: The instruction may only have debug instructions as uses.
@@ -76,6 +81,7 @@ eraseFromParentWithDebugInsts(SILInstruction *inst,
// Just matching what eraseFromParentWithDebugInsts is today.
if (nextII == inst->getIterator())
++nextII;
swift::salvageDebugInfo(inst);
callbacks.deleteInst(inst, false /*do not notify*/);
return nextII;
}

View File

@@ -191,9 +191,11 @@ public:
void emitArtificialFunction(IRBuilder &Builder, llvm::Function *Fn,
SILType SILTy);
void handleFragmentDIExpr(const SILDIExprOperand &CurDIExprOp,
/// Return false if we fail to create the right DW_OP_LLVM_fragment operand.
bool handleFragmentDIExpr(const SILDIExprOperand &CurDIExprOp,
SmallVectorImpl<uint64_t> &Operands);
void buildDebugInfoExpression(const SILDebugVariable &VarInfo,
/// Return false if we fail to create the desired !DIExpression.
bool buildDebugInfoExpression(const SILDebugVariable &VarInfo,
SmallVectorImpl<uint64_t> &Operands);
void emitVariableDeclaration(IRBuilder &Builder,
@@ -2336,9 +2338,8 @@ void IRGenDebugInfoImpl::emitArtificialFunction(IRBuilder &Builder,
setCurrentLoc(Builder, Scope, ALoc);
}
void IRGenDebugInfoImpl::handleFragmentDIExpr(
const SILDIExprOperand &CurDIExprOp,
SmallVectorImpl<uint64_t> &Operands) {
bool IRGenDebugInfoImpl::handleFragmentDIExpr(
const SILDIExprOperand &CurDIExprOp, SmallVectorImpl<uint64_t> &Operands) {
assert(CurDIExprOp.getOperator() == SILDIExprOperator::Fragment);
// Expecting a VarDecl that points to a field in an struct
auto DIExprArgs = CurDIExprOp.args();
@@ -2348,14 +2349,22 @@ void IRGenDebugInfoImpl::handleFragmentDIExpr(
"DIExprOperator::Fragment");
// Translate the based type
DeclContext *ParentDecl = VD->getDeclContext();
assert(ParentDecl && "VarDecl has no parent context?");
SILType ParentSILType =
IGM.getLoweredType(ParentDecl->getDeclaredTypeInContext());
// Retrieve the offset & size of the field
llvm::Constant *Offset =
emitPhysicalStructMemberFixedOffset(IGM, ParentSILType, VD);
llvm::Type *FieldTy =
getPhysicalStructFieldTypeInfo(IGM, ParentSILType, VD)->getStorageType();
assert(Offset && FieldTy && "Non-fixed type?");
auto *FieldTypeInfo = getPhysicalStructFieldTypeInfo(IGM, ParentSILType, VD);
// FIXME: This will only happen if IRGen hasn't processed ParentSILType
// (into its own representation) but we probably should ask IRGen to process
// it right now.
if (!FieldTypeInfo)
return false;
llvm::Type *FieldTy = FieldTypeInfo->getStorageType();
// Doesn't support non-fixed type right now
if (!Offset || !FieldTy)
return false;
uint64_t SizeOfByte = CI.getTargetInfo().getCharWidth();
uint64_t SizeInBits = IGM.DataLayout.getTypeSizeInBits(FieldTy);
@@ -2366,9 +2375,11 @@ void IRGenDebugInfoImpl::handleFragmentDIExpr(
Operands.push_back(llvm::dwarf::DW_OP_LLVM_fragment);
Operands.push_back(OffsetInBits);
Operands.push_back(SizeInBits);
return true;
}
void IRGenDebugInfoImpl::buildDebugInfoExpression(
bool IRGenDebugInfoImpl::buildDebugInfoExpression(
const SILDebugVariable &VarInfo, SmallVectorImpl<uint64_t> &Operands) {
assert(VarInfo.DIExpr && "SIL debug info expression not found");
@@ -2376,12 +2387,14 @@ void IRGenDebugInfoImpl::buildDebugInfoExpression(
for (const SILDIExprOperand &ExprOperand : DIExpr.operands()) {
switch (ExprOperand.getOperator()) {
case SILDIExprOperator::Fragment:
handleFragmentDIExpr(ExprOperand, Operands);
if (!handleFragmentDIExpr(ExprOperand, Operands))
return false;
break;
default:
llvm_unreachable("Unrecoginized operator");
llvm_unreachable("Unrecognized operator");
}
}
return true;
}
void IRGenDebugInfoImpl::emitVariableDeclaration(
@@ -2455,7 +2468,8 @@ void IRGenDebugInfoImpl::emitVariableDeclaration(
[&VarInfo, this](llvm::DIExpression *DIExpr) -> llvm::DIExpression * {
if (VarInfo.DIExpr) {
llvm::SmallVector<uint64_t, 2> Operands;
buildDebugInfoExpression(VarInfo, Operands);
if (!buildDebugInfoExpression(VarInfo, Operands))
return nullptr;
if (Operands.size())
return llvm::DIExpression::append(DIExpr, Operands);
}
@@ -2493,22 +2507,24 @@ void IRGenDebugInfoImpl::emitVariableDeclaration(
Operands.push_back(SizeInBits);
}
llvm::DIExpression *DIExpr = DBuilder.createExpression(Operands);
emitDbgIntrinsic(Builder, Piece, Var,
// DW_OP_LLVM_fragment must be the last part of an DIExpr
// so we can't append more if IsPiece is true.
IsPiece ? DIExpr : appendDIExpression(DIExpr), DInstLine,
DInstLoc.column, Scope, DS,
Indirection == CoroDirectValue ||
Indirection == CoroIndirectValue);
// DW_OP_LLVM_fragment must be the last part of an DIExpr
// so we can't append more if IsPiece is true.
if (!IsPiece)
DIExpr = appendDIExpression(DIExpr);
if (DIExpr)
emitDbgIntrinsic(
Builder, Piece, Var, DIExpr, DInstLine, DInstLoc.column, Scope, DS,
Indirection == CoroDirectValue || Indirection == CoroIndirectValue);
}
// Emit locationless intrinsic for variables that were optimized away.
if (Storage.empty())
emitDbgIntrinsic(Builder, llvm::ConstantInt::get(IGM.Int64Ty, 0), Var,
appendDIExpression(DBuilder.createExpression()), DInstLine,
DInstLoc.column, Scope, DS,
Indirection == CoroDirectValue ||
Indirection == CoroIndirectValue);
if (Storage.empty()) {
if (auto *DIExpr = appendDIExpression(DBuilder.createExpression()))
emitDbgIntrinsic(Builder, llvm::ConstantInt::get(IGM.Int64Ty, 0), Var,
DIExpr, DInstLine, DInstLoc.column, Scope, DS,
Indirection == CoroDirectValue ||
Indirection == CoroIndirectValue);
}
}
void IRGenDebugInfoImpl::emitDbgIntrinsic(
@@ -2554,7 +2570,19 @@ void IRGenDebugInfoImpl::emitDbgIntrinsic(
DBuilder.insertDeclare(Storage, Var, Expr, DL, &EntryBlock);
} else {
// Insert a dbg.value at the current insertion point.
DBuilder.insertDbgValueIntrinsic(Storage, Var, Expr, DL, BB);
if (isa<llvm::Argument>(Storage) && !Var->getArg() &&
BB->getFirstNonPHIOrDbg())
// SelectionDAGISel only generates debug info for a dbg.value
// that is associated with a llvm::Argument if either its !DIVariable
// is marked as argument or there is no non-debug intrinsic instruction
// before it. So In the case of associating a llvm::Argument with a
// non-argument debug variable -- usually via a !DIExpression -- we
// need to make sure that dbg.value is before any non-phi / no-dbg
// instruction.
DBuilder.insertDbgValueIntrinsic(Storage, Var, Expr, DL,
BB->getFirstNonPHIOrDbg());
else
DBuilder.insertDbgValueIntrinsic(Storage, Var, Expr, DL, BB);
}
}

View File

@@ -4752,8 +4752,7 @@ void IRGenSILFunction::visitDebugValueInst(DebugValueInst *i) {
if (VarDecl *Decl = i->getDecl()) {
DbgTy = DebugTypeInfo::getLocalVariable(
Decl, RealTy, getTypeInfo(SILVal->getType()));
} else if (i->getFunction()->isBare() && !SILTy.hasArchetype() &&
!VarInfo->Name.empty()) {
} else if (!SILTy.hasArchetype() && !VarInfo->Name.empty()) {
// Preliminary support for .sil debug information.
DbgTy = DebugTypeInfo::getFromTypeInfo(RealTy, getTypeInfo(SILTy));
} else

View File

@@ -292,8 +292,15 @@ transferNodesFromList(llvm::ilist_traits<SILBasicBlock> &SrcTraits,
First->Parent = Parent;
First->index = -1;
First->lastInitializedBitfieldID = 0;
for (auto &II : *First)
for (auto &II : *First) {
II.setDebugScope(ScopeCloner.getOrCreateClonedScope(II.getDebugScope()));
// Special handling for SILDebugVariable.
if (auto DVI = DebugVarCarryingInst(&II))
if (auto VarInfo = DVI.getVarInfo())
if (VarInfo->Scope)
DVI.setDebugVarScope(
ScopeCloner.getOrCreateClonedScope(VarInfo->Scope));
}
}
}

View File

@@ -58,3 +58,10 @@ void SILDebugInfoExpression::op_iterator::increment() {
Remain = Remain.drop_front(Current.size());
}
}
SILDebugInfoExpression SILDebugInfoExpression::createFragment(VarDecl *Field) {
assert(Field);
return SILDebugInfoExpression(
{SILDIExprElement::createOperator(SILDIExprOperator::Fragment),
SILDIExprElement::createDecl(Field)});
}

View File

@@ -161,6 +161,33 @@ StringRef TailAllocatedDebugVariable::getName(const char *buf) const {
return StringRef();
}
Optional<SILDebugVariable>
SILDebugVariable::createFromAllocation(const AllocationInst *AI) {
Optional<SILDebugVariable> VarInfo;
if (const auto *ASI = dyn_cast_or_null<AllocStackInst>(AI))
VarInfo = ASI->getVarInfo();
// TODO: Support AllocBoxInst
if (!VarInfo)
return VarInfo;
// Copy everything but the DIExpr
VarInfo->DIExpr.clear();
// 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;
}
AllocStackInst::AllocStackInst(SILDebugLocation Loc, SILType elementType,
ArrayRef<SILValue> TypeDependentOperands,
SILFunction &F, Optional<SILDebugVariable> Var,

View File

@@ -4105,7 +4105,12 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B,
SILDebugVariable VarInfo;
if (parseSILDebugVar(VarInfo) || parseSILDebugLocation(InstLoc, B))
return true;
ResultVal = B.createAllocStack(InstLoc, Ty, VarInfo, hasDynamicLifetime);
// It doesn't make sense to attach a debug var info if the name is empty
if (VarInfo.Name.size())
ResultVal =
B.createAllocStack(InstLoc, Ty, VarInfo, hasDynamicLifetime);
else
ResultVal = B.createAllocStack(InstLoc, Ty, {}, hasDynamicLifetime);
} else {
assert(Opcode == SILInstructionKind::MetatypeInst);
if (parseSILDebugLocation(InstLoc, B))

View File

@@ -1244,6 +1244,12 @@ public:
DebugVars[argNum] = varInfo->Name;
}
// Check the (auxiliary) debug variable scope
if (const SILDebugScope *VarDS = varInfo->Scope)
require(VarDS->getInlinedFunction() == debugScope->getInlinedFunction(),
"Scope of the debug variable should have the same parent function"
" as that of instruction.");
// Check debug info expression
if (const auto &DIExpr = varInfo->DIExpr) {
for (auto It = DIExpr.element_begin(), ItEnd = DIExpr.element_end();

View File

@@ -74,6 +74,10 @@ static bool seemsUseful(SILInstruction *I) {
return true;
}
// Is useful if it's associating with a function argument
if (isa<DebugValueInst>(I) || isa<DebugValueAddrInst>(I))
return isa<SILFunctionArgument>(I->getOperand(0));
return false;
}

View File

@@ -195,10 +195,11 @@ SROAMemoryUseAnalyzer::
createAllocas(llvm::SmallVector<AllocStackInst *, 4> &NewAllocations) {
SILBuilderWithScope B(AI);
SILType Type = AI->getType().getObjectType();
// Intentionally dropping the debug info.
// FIXME: VarInfo needs to be extended with fragment info to support this.
SILLocation Loc = RegularLocation::getAutoGeneratedLocation();
if (TT) {
// TODO: Add op_fragment support for tuple type
for (unsigned EltNo : indices(TT->getElementTypes())) {
SILType EltTy = Type.getTupleElementType(EltNo);
NewAllocations.push_back(B.createAllocStack(Loc, EltTy, {}));
@@ -207,10 +208,17 @@ createAllocas(llvm::SmallVector<AllocStackInst *, 4> &NewAllocations) {
assert(SD && "SD should not be null since either it or TT must be set at "
"this point.");
SILModule &M = AI->getModule();
for (auto *D : SD->getStoredProperties())
for (VarDecl *VD : SD->getStoredProperties()) {
Optional<SILDebugVariable> NewDebugVarInfo =
SILDebugVariable::createFromAllocation(AI);
if (NewDebugVarInfo)
// TODO: Handle DIExpr that is already attached
NewDebugVarInfo->DIExpr = SILDebugInfoExpression::createFragment(VD);
NewAllocations.push_back(B.createAllocStack(
Loc, Type.getFieldType(D, M, TypeExpansionContext(B.getFunction())),
{}));
Loc, Type.getFieldType(VD, M, TypeExpansionContext(B.getFunction())),
NewDebugVarInfo));
}
}
}

View File

@@ -461,6 +461,7 @@ void InstructionDeleter::deleteWithUses(SILInstruction *inst, bool fixLifetimes,
SmallVector<SILInstruction *, 4> toDeleteInsts;
toDeleteInsts.push_back(inst);
swift::salvageDebugInfo(inst);
for (unsigned idx = 0; idx < toDeleteInsts.size(); ++idx) {
for (SILValue result : toDeleteInsts[idx]->getResults()) {
// Temporary use vector to avoid iterator invalidation.
@@ -472,6 +473,7 @@ void InstructionDeleter::deleteWithUses(SILInstruction *inst, bool fixLifetimes,
assert(!isa<BranchInst>(user) && "can't delete phis");
toDeleteInsts.push_back(user);
swift::salvageDebugInfo(user);
use->drop();
}
}
@@ -2104,3 +2106,21 @@ void swift::endLifetimeAtLeakingBlocks(SILValue value,
RegularLocation::getAutoGeneratedLocation(), value);
});
}
void swift::salvageDebugInfo(SILInstruction *I) {
if (!I)
return;
if (auto *SI = dyn_cast<StoreInst>(I)) {
if (SILValue DestVal = SI->getDest())
// TODO: Generalize this into "get the attached debug info
// on `DestVal`".
if (auto *ASI = dyn_cast_or_null<AllocStackInst>(
DestVal.getDefiningInstruction())) {
if (auto VarInfo = ASI->getVarInfo())
SILBuilder(SI, ASI->getDebugScope())
.createDebugValue(RegularLocation::getAutoGeneratedLocation(),
SI->getSrc(), *VarInfo);
}
}
}

View File

@@ -0,0 +1,13 @@
// RUN: %target-swift-frontend -primary-file %s -g -O -emit-sil | %FileCheck %s
public enum BenchmarkCategory : String {
case validation
case api, Array, String, Dictionary, Codable, Set, Data, IndexPath, SIMD
}
// Similar to `DebugInfo/verifier_debug_scope.sil`, in this case the cloner
// should correctly map the scope of debug variable.
// CHECK-LABEL: sil {{.*}} @{{.*}}__derived_enum_equalsySbAC_ACtFZTf4nnd_n
// CHECK: debug_value %{{.*}} : $Builtin.Int{{[0-9]+}}, var, (name "index_a"
// CHECK-SAME: , scope [[VAR_SCOPE:[0-9]+]]),
// CHECK-SAME: , scope [[VAR_SCOPE]]

View File

@@ -0,0 +1,80 @@
// RUN: %target-sil-opt -enable-sil-verify-all -sil-print-debuginfo -sroa %s | %FileCheck --check-prefix=CHECK-SROA %s
// RUN: %target-sil-opt -enable-sil-verify-all -sil-print-debuginfo -sroa -mem2reg %s -o %t.sil
// RUN: %FileCheck --check-prefix=CHECK-MEM2REG %s --input-file=%t.sil
// RUN: %target-swiftc_driver -Xfrontend -disable-debugger-shadow-copies -g -emit-ir %t.sil | %FileCheck --check-prefix=CHECK-IR %s
sil_stage canonical
import Builtin
import Swift
struct MyStruct {
@_hasStorage var x: Int64 { get set }
@_hasStorage var y: Int64 { get set }
init(x: Int64, y: Int64)
}
sil_scope 1 { loc "sroa.swift":2:8 parent @MyStructInit : $@convention(method) (Int64, Int64, @thin MyStruct.Type) -> MyStruct }
// MyStruct.init(x:y:)
sil hidden @MyStructInit : $@convention(method) (Int64, Int64, @thin MyStruct.Type) -> MyStruct {
bb0(%0 : $Int64, %1 : $Int64, %2 : $@thin MyStruct.Type):
%3 = struct $MyStruct (%0 : $Int64, %1 : $Int64), loc "sroa.swift":2:8, scope 1
return %3 : $MyStruct, loc "sroa.swift":2:8, scope 1
} // end sil function 'MyStructInit'
sil_scope 2 { loc "sroa.swift":7:6 parent @foo : $@convention(thin) (Int64, Int64) -> Int64 }
// foo(in_x:in_y:)
sil hidden @foo : $@convention(thin) (Int64, Int64) -> Int64 {
bb0(%0 : $Int64, %1 : $Int64):
debug_value %0 : $Int64, let, name "in_x", argno 1, loc "sroa.swift":7:10, scope 2
debug_value %1 : $Int64, let, name "in_y", argno 2, loc "sroa.swift":7:21, scope 2
%4 = alloc_stack $MyStruct, var, name "my_struct", loc "sroa.swift":8:9, scope 2
// Make sure SROA propagate the debug info to the splitted alloc_stack instructions
// CHECK-SROA: alloc_stack $Int64, var
// CHECK-SROA-SAME: (name "my_struct", loc "sroa.swift":8:9
// CHECK-SROA-SAME: type $*MyStruct, expr op_fragment:#MyStruct.x
// CHECK-SROA-SAME: loc "<compiler-generated>":0:0
// CHECK-SROA: alloc_stack $Int64, var
// CHECK-SROA-SAME: (name "my_struct", loc "sroa.swift":8:9
// CHECK-SROA-SAME: type $*MyStruct, expr op_fragment:#MyStruct.y
// CHECK-SROA-SAME: loc "<compiler-generated>":0:0
%5 = metatype $@thin MyStruct.Type, loc "sroa.swift":8:21, scope 2
%6 = integer_literal $Builtin.Int64, 0, loc "sroa.swift":8:33, scope 2
%7 = struct $Int64 (%6 : $Builtin.Int64), loc "sroa.swift":8:33, scope 2
%8 = integer_literal $Builtin.Int64, 0, loc "sroa.swift":8:39, scope 2
%9 = struct $Int64 (%8 : $Builtin.Int64), loc "sroa.swift":8:39, scope 2
// function_ref MyStruct.init(x:y:)
%10 = function_ref @MyStructInit : $@convention(method) (Int64, Int64, @thin MyStruct.Type) -> MyStruct, loc "sroa.swift":8:21, scope 2
%11 = apply %10(%7, %9, %5) : $@convention(method) (Int64, Int64, @thin MyStruct.Type) -> MyStruct, loc "sroa.swift":8:21, scope 2
store %11 to %4 : $*MyStruct, loc "sroa.swift":8:21, scope 2
%13 = struct_element_addr %4 : $*MyStruct, #MyStruct.x, loc "sroa.swift":9:17, scope 2
// CHECK-MEM2REG: %[[FIELD_X:[0-9]+]] = struct_extract %[[STRUCT:[0-9]+]] : $MyStruct, #MyStruct.x, loc "sroa.swift":8:21
store %0 to %13 : $*Int64, loc "sroa.swift":9:17, scope 2
%15 = struct_element_addr %4 : $*MyStruct, #MyStruct.y, loc "sroa.swift":10:17, scope 2
// CHECK-MEM2REG: %[[FIELD_Y:[0-9]+]] = struct_extract %[[STRUCT]] : $MyStruct, #MyStruct.y, loc "sroa.swift":8:21
store %1 to %15 : $*Int64, loc "sroa.swift":10:17, scope 2
// Make sure the struct fields' SSA values are properly connected to the source variables via op_fragment
// CHECK-MEM2REG: debug_value %[[FIELD_X]] : $Int64, var, (name "my_struct", loc "sroa.swift":8:9, scope 2), type $*MyStruct, expr op_fragment:#MyStruct.x
// CHECK-MEM2REG: debug_value %[[FIELD_Y]] : $Int64, var, (name "my_struct", loc "sroa.swift":8:9, scope 2), type $*MyStruct, expr op_fragment:#MyStruct.y
// CHECK-IR: call void @llvm.dbg.value(metadata i64 %0
// CHECK-IR-SAME: metadata ![[STRUCT_MD:[0-9]+]]
// CHECK-IR-SAME: !DIExpression(DW_OP_LLVM_fragment, 0, 64)
// CHECK-IR: call void @llvm.dbg.value(metadata i64 %1
// CHECK-IR-SAME: metadata ![[STRUCT_MD]]
// CHECK-IR-SAME: !DIExpression(DW_OP_LLVM_fragment, 64, 64)
// Make sure function arguments' SSA values are also properly connected to the source variables
// CHECK-MEM2REG: debug_value %0 : $Int64, var, (name "my_struct", loc "sroa.swift":8:9, scope 2), type $*MyStruct, expr op_fragment:#MyStruct.x
// CHECK-MEM2REG: debug_value %1 : $Int64, var, (name "my_struct", loc "sroa.swift":8:9, scope 2), type $*MyStruct, expr op_fragment:#MyStruct.y
// CHECK-IR: call void @llvm.dbg.value(metadata i64 %0, metadata ![[ARG1_MD:[0-9]+]]
// CHECK-IR: call void @llvm.dbg.value(metadata i64 %1, metadata ![[ARG2_MD:[0-9]+]]
dealloc_stack %4 : $*MyStruct, loc "sroa.swift":8:9, scope 2
return %0 : $Int64, loc "sroa.swift":11:5, scope 2
} // end sil function 'foo'
// CHECK-IR: ![[STRUCT_MD]] = !DILocalVariable(name: "my_struct"
// CHECK-IR-SAME: line: 8
// CHECK-IR: ![[ARG1_MD]] = !DILocalVariable(name: "in_x", arg: 1
// CHECK-IR-SAME: line: 7
// CHECK-IR: ![[ARG2_MD]] = !DILocalVariable(name: "in_y", arg: 2
// CHECK-IR-SAME: line: 7

View File

@@ -0,0 +1,17 @@
// RUN: not --crash %target-swift-frontend %s -g -emit-sil -sil-verify-all
import Swift
func foo(_ x: Int)
func bar(_ x: Int)
sil_scope 1 { loc "file.swift":2:6 parent @foo : $@convention(thin) (Int) -> () }
sil_scope 2 { loc "file.swift":6:2 parent @bar : $@convention(thin) (Int) -> () }
sil hidden @foo : $@convention(thin) (Int) -> () {
bb0(%0 : $Int):
// The scope of the debug variable needs to have the same root function as the
// debug scope on instruction
debug_value %0 : $Int, let, (name "x", loc "file.swift":8:7, scope 1), loc "file.swift":9:4, scope 2
%r = tuple()
return %r : $()
}

View File

@@ -12,7 +12,7 @@ public struct MyInt {
// CHECK-ONONE: } // end sil function '$s27constant_propagation_stdlib15isConcrete_trueyBi1_AA5MyIntVF'
// CHECK-O-LABEL: sil @$s27constant_propagation_stdlib15isConcrete_trueyBi1_AA5MyIntVF : $@convention(thin) (MyInt) -> Builtin.Int1 {
// CHECK-O: bb0(
// CHECK-O-NEXT: [[RESULT:%.*]] = integer_literal $Builtin.Int1, -1
// CHECK-O: [[RESULT:%.*]] = integer_literal $Builtin.Int1, -1
// CHECK-O-NEXT: return [[RESULT]]
// CHECK-O-NEXT: } // end sil function '$s27constant_propagation_stdlib15isConcrete_trueyBi1_AA5MyIntVF'
public func isConcrete_true(_ x: MyInt) -> Builtin.Int1 {
@@ -85,8 +85,8 @@ public func isConcrete_concrete_caller(_ x: MyInt) -> Builtin.Int1 {
// CHECK-ONONE: } // end sil function '$s27constant_propagation_stdlib4main1xBi1__Bi1_Bi1_tAA5MyIntV_tF'
// CHECK-O-LABEL: sil @$s27constant_propagation_stdlib4main1xBi1__Bi1_Bi1_tAA5MyIntV_tF : $@convention(thin) (MyInt) -> (Builtin.Int1, Builtin.Int1, Builtin.Int1) {
// CHECK-O: bb0(
// CHECK-O-NEXT: [[VALUE:%.*]] = integer_literal $Builtin.Int1, -1
// CHECK-O-NEXT: [[RESULT:%.*]] = tuple ([[VALUE]] : $Builtin.Int1, [[VALUE]] : $Builtin.Int1, [[VALUE]] : $Builtin.Int1)
// CHECK-O: [[VALUE:%.*]] = integer_literal $Builtin.Int1, -1
// CHECK-O: [[RESULT:%.*]] = tuple ([[VALUE]] : $Builtin.Int1, [[VALUE]] : $Builtin.Int1, [[VALUE]] : $Builtin.Int1)
// CHECK-O-NEXT: return [[RESULT]]
// CHECK-O-NEXT: } // end sil function '$s27constant_propagation_stdlib4main1xBi1__Bi1_Bi1_tAA5MyIntV_tF'
public func main(x: MyInt) -> (Builtin.Int1, Builtin.Int1, Builtin.Int1) {

View File

@@ -21,6 +21,7 @@ func g<T : P>(_ x : T) -> Bool {
// CHECK-LABEL: sil @$s16dead_alloc_stack6testitySbAA1XVF
// CHECK: bb0({{.*}}):
// CHECK-NEXT: debug_value
// CHECK-NEXT: integer_literal
// CHECK-NEXT: struct
// CHECK-NEXT: return

View File

@@ -59,7 +59,9 @@ func testDeadArrayElimWithAddressOnlyValues<T>(x: T, y: T) {
// CHECK-LABEL: sil hidden @$s15dead_array_elim31testDeadArrayAfterOptimizationsySiSSF
// CHECK: bb0(%0 : $String):
// CHECK-NEXT: debug_value
// CHECK-NEXT: integer_literal $Builtin.Int{{[0-9]+}}, 21
// CHECK-NEXT: debug_value
// CHECK-NEXT: struct $Int
// CHECK-NEXT: return
// CHECK: } // end sil function '$s15dead_array_elim31testDeadArrayAfterOptimizationsySiSSF'
@@ -80,6 +82,7 @@ func testDeadArrayAfterOptimizations(_ stringParameter: String) -> Int {
// CHECK-LABEL: sil hidden @$s15dead_array_elim15testNestedArraySiyF
// CHECK: bb0:
// CHECK-NEXT: integer_literal $Builtin.Int{{[0-9]+}}, 3
// CHECK-NEXT: debug_value
// CHECK-NEXT: struct $Int
// CHECK-NEXT: return
// CHECK: } // end sil function '$s15dead_array_elim15testNestedArraySiyF'

View File

@@ -9,7 +9,8 @@ import Foundation
class Myclass : NSObject {
// CHECK-LABEL: sil hidden [thunk] @$s4test7MyclassC3fooyySSFTo
// CHECK-NEXT: bb0(%0 : $NSString, %1 : $Myclass):
// CHECK: bb0(%0 : $NSString, %1 : $Myclass):
// CHECK-NEXT: debug_value
// CHECK-NEXT: tuple ()
// CHECK-NEXT: return
@objc func foo(_ s: String) {

View File

@@ -17,10 +17,11 @@ struct X : Q {
// CHECK-LABEL: sil hidden @$s4test1XV0A2MeSiSgvg : $@convention(method) (X) -> Optional<Int> {
// CHECK: bb0(%0 : $X):
// CHECK-NEXT: debug_value
// CHECK-NEXT: integer_literal ${{.*}}, 0
// CHECK-NEXT: struct $Int
// CHECK-NEXT: enum $Optional<Int>, #Optional.some!enumelt
// CHECK-NEXT: return %3 : $Optional<Int>
// CHECK-NEXT: %[[ENUM:[0-9]+]] = enum $Optional<Int>, #Optional.some!enumelt
// CHECK-NEXT: return %[[ENUM]] : $Optional<Int>
// CHECK-NEXT: } // end sil function '$s4test1XV0A2MeSiSgvg'
var testMe: Int? { z }
}

View File

@@ -185,13 +185,13 @@ bb0(%0 : $*SomeNoClassProtocol):
// CHECK-LABEL: sil hidden [noinline] @$s21existential_transform3ncpyyF : $@convention(thin) () -> () {
// CHECK: bb0:
// CHECK: %0 = alloc_ref $SomeNoClass
// CHECK: debug_value %0 : $SomeNoClass, let, name "self", argno 1
// CHECK: %2 = function_ref @$s21existential_transform12wrap_foo_ncp1as5Int32VAA19SomeNoClassProtocol_p_tFTf4e_n4main0ghI0C_Tg5 : $@convention(thin) (@guaranteed SomeNoClass) -> Int32
// CHECK: %3 = apply %2(%0) : $@convention(thin) (@guaranteed SomeNoClass) -> Int32
// CHECK: strong_release %0 : $SomeNoClass
// CHECK: %5 = tuple ()
// CHECK: return %5 : $()
// CHECK: %[[ALLOCREF:[0-9]+]] = alloc_ref $SomeNoClass
// CHECK: debug_value %[[ALLOCREF]] : $SomeNoClass, let, name "self", argno 1
// CHECK: %[[FUNC:[0-9]+]] = function_ref @$s21existential_transform12wrap_foo_ncp1as5Int32VAA19SomeNoClassProtocol_p_tFTf4e_n4main0ghI0C_Tg5 : $@convention(thin) (@guaranteed SomeNoClass) -> Int32
// CHECK: %[[RES:[0-9]+]] = apply %[[FUNC]](%[[ALLOCREF]]) : $@convention(thin) (@guaranteed SomeNoClass) -> Int32
// CHECK: strong_release %[[ALLOCREF]] : $SomeNoClass
// CHECK: %[[RET:[0-9]+]] = tuple ()
// CHECK: return %[[RET]] : $()
// CHECK-LABEL: } // end sil function '$s21existential_transform3ncpyyF'
sil hidden [noinline] @$s21existential_transform3ncpyyF : $@convention(thin) () -> () {
bb0:
@@ -309,13 +309,13 @@ bb0(%0 : $*SomeNoClassProtocolComp & SomeOtherNoClassProtocolComp):
// CHECK-LABEL: sil hidden [noinline] @$s21existential_transform4ncpcyyF : $@convention(thin) () -> () {
// CHECK: bb0:
// CHECK: %0 = alloc_ref $SomeNoClassComp
// CHECK: debug_value %0 : $SomeNoClassComp, let, name "self", argno 1
// CHECK: %2 = function_ref @$s21existential_transform25wrap_no_foo_bar_comp_ncpc1as5Int32VAA23SomeNoClassProtocolComp_AA0j5OtherklmN0p_tFTf4e_n4main0jklN0C_Tg5 : $@convention(thin) (@guaranteed SomeNoClassComp) -> Int32
// CHECK: %3 = apply %2(%0) : $@convention(thin) (@guaranteed SomeNoClassComp) -> Int32
// CHECK: strong_release %0 : $SomeNoClassComp
// CHECK: %5 = tuple ()
// CHECK: return %5 : $()
// CHECK: %[[ALLOCREF:[0-9]+]] = alloc_ref $SomeNoClassComp
// CHECK: debug_value %[[ALLOCREF]] : $SomeNoClassComp, let, name "self", argno 1
// CHECK: %[[FUNC:[0-9]+]] = function_ref @$s21existential_transform25wrap_no_foo_bar_comp_ncpc1as5Int32VAA23SomeNoClassProtocolComp_AA0j5OtherklmN0p_tFTf4e_n4main0jklN0C_Tg5 : $@convention(thin) (@guaranteed SomeNoClassComp) -> Int32
// CHECK: %[[RES:[0-9]+]] = apply %[[FUNC]](%[[ALLOCREF]]) : $@convention(thin) (@guaranteed SomeNoClassComp) -> Int32
// CHECK: strong_release %[[ALLOCREF]] : $SomeNoClassComp
// CHECK: %[[RET:[0-9]+]] = tuple ()
// CHECK: return %[[RET]] : $()
// CHECK-LABEL: } // end sil function '$s21existential_transform4ncpcyyF'
sil hidden [noinline] @$s21existential_transform4ncpcyyF : $@convention(thin) () -> () {
bb0:

View File

@@ -206,8 +206,8 @@ ConcreteToArchetypeConvertUInt8(t: b, t2: f)
// x -> x where x is not a class.
// CHECK-LABEL: sil shared [noinline] @$s37specialize_unconditional_checked_cast31ConcreteToArchetypeConvertUInt8{{[_0-9a-zA-Z]*}}3Not{{.*}}Tg5 : $@convention(thin) (NotUInt8, NotUInt8) -> NotUInt8 {
// CHECK: bb0(%0 : $NotUInt8, %1 : $NotUInt8):
// CHECK-NEXT: debug_value %0
// CHECK-NEXT: return %0
// CHECK: debug_value %0
// CHECK: return %0
// x -> y where x is not a class but y is a class.
// CHECK-LABEL: sil shared [noinline] @$s37specialize_unconditional_checked_cast31ConcreteToArchetypeConvertUInt8{{[_0-9a-zA-Z]*}}FAA1CC_Tg5 : $@convention(thin) (NotUInt8, @guaranteed C) -> @owned C {
@@ -260,7 +260,7 @@ ConcreteToArchetypeConvertC(t: c, t2: e)
// x -> y where x and y are unrelated classes.
// CHECK-LABEL: sil shared [noinline] @$s37specialize_unconditional_checked_cast27ConcreteToArchetypeConvertC{{[_0-9a-zA-Z]*}}FAA1EC_Tg5 : $@convention(thin) (@guaranteed C, @guaranteed E) -> @owned E {
// CHECK: bb0(%0 : $C, %1 : $E):
// CHECK-NEXT: builtin "int_trap"
// CHECK: builtin "int_trap"
// CHECK-NEXT: unreachable
// CHECK-NEXT: }