mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[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:
@@ -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
|
||||
|
||||
@@ -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, ...) \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)});
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
13
test/DebugInfo/debug_scope_propagate.swift
Normal file
13
test/DebugInfo/debug_scope_propagate.swift
Normal 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]]
|
||||
80
test/DebugInfo/sroa_mem2reg.sil
Normal file
80
test/DebugInfo/sroa_mem2reg.sil
Normal 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
|
||||
17
test/DebugInfo/verifier_debug_scope.sil
Normal file
17
test/DebugInfo/verifier_debug_scope.sil
Normal 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 : $()
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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: }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user