mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Improve the performance of IRGenDebugInfo
This commit changes how inline information is stored in SILDebugScope from a tree to a linear chain of inlined call sites (similar to what LLVM is using). This makes creating inlined SILDebugScopes slightly more expensive, but makes lowering SILDebugScopes into LLVM metadata much faster because entire inlined-at chains can now be cached. This means that SIL is no longer preserve the inlining history (i.e., ((a was inlined into b) was inlined into c) is represented the same as (a was inlined into (b was inlined into c)), but this information was not used by anyone. On my late 2012 i7 iMac, this saves about 4 seconds when compiling the RelWithDebInfo x86_64 swift standard library — or 40% of IRGen time. rdar://problem/28311051
This commit is contained in:
@@ -46,82 +46,28 @@ public:
|
||||
/// An optional chain of inlined call sites.
|
||||
///
|
||||
/// If this scope is inlined, this points to a special "scope" that
|
||||
/// holds only the location of the call site. The parent scope will be
|
||||
/// the scope of the inlined call site.
|
||||
///
|
||||
/// Note that compared to the inlinedAt chain in llvm::DILocation
|
||||
/// SILDebugScope represents an inline tree.
|
||||
/// holds the location of the call site.
|
||||
const SILDebugScope *InlinedCallSite;
|
||||
|
||||
SILDebugScope(SILLocation Loc, SILFunction *SILFn,
|
||||
const SILDebugScope *ParentScope = nullptr,
|
||||
const SILDebugScope *InlinedCallSite = nullptr)
|
||||
: Loc(Loc), InlinedCallSite(InlinedCallSite) {
|
||||
if (ParentScope)
|
||||
Parent = ParentScope;
|
||||
else {
|
||||
assert(SILFn && "no parent provided");
|
||||
Parent = SILFn;
|
||||
}
|
||||
}
|
||||
const SILDebugScope *InlinedCallSite = nullptr);
|
||||
|
||||
/// Create a scope for an artificial function.
|
||||
SILDebugScope(SILLocation Loc)
|
||||
: Loc(Loc), InlinedCallSite(nullptr) {}
|
||||
|
||||
/// Create an inlined version of CalleeScope.
|
||||
SILDebugScope(const SILDebugScope *CallSiteScope,
|
||||
const SILDebugScope *CalleeScope)
|
||||
: Loc(CalleeScope->Loc), Parent(CalleeScope),
|
||||
InlinedCallSite(CallSiteScope) {
|
||||
assert(CallSiteScope && CalleeScope);
|
||||
if (InlinedCallSite)
|
||||
assert(!InlinedCallSite->InlinedCallSite &&
|
||||
"a call site scope cannot have an inlined call site");
|
||||
}
|
||||
SILDebugScope(SILLocation Loc);
|
||||
|
||||
/// Return the function this scope originated from before being inlined.
|
||||
SILFunction *getInlinedFunction() const {
|
||||
if (Parent.isNull())
|
||||
return nullptr;
|
||||
|
||||
const SILDebugScope *Scope = this;
|
||||
while (Scope->Parent.is<const SILDebugScope *>())
|
||||
Scope = Scope->Parent.get<const SILDebugScope *>();
|
||||
assert(Scope->Parent.is<SILFunction *>() && "orphaned scope");
|
||||
return Scope->Parent.get<SILFunction *>();
|
||||
}
|
||||
|
||||
/// Return this scope without inline information.
|
||||
const SILDebugScope *getInlinedScope() const {
|
||||
return InlinedCallSite ? Parent.get<const SILDebugScope*>() : this;
|
||||
}
|
||||
SILFunction *getInlinedFunction() const;
|
||||
|
||||
/// Return the parent function of this scope. If the scope was
|
||||
/// inlined this recursively returns the function it was inlined
|
||||
/// into.
|
||||
SILFunction *getParentFunction() const {
|
||||
if (InlinedCallSite)
|
||||
return InlinedCallSite->Parent.get<const SILDebugScope *>()
|
||||
->getParentFunction();
|
||||
if (auto *ParentScope = Parent.dyn_cast<const SILDebugScope *>())
|
||||
return ParentScope->getParentFunction();
|
||||
return Parent.get<SILFunction *>();
|
||||
}
|
||||
|
||||
typedef SmallVector<const SILDebugScope *, 8> InlineScopeList;
|
||||
static void flatten(const SILDebugScope *DS, InlineScopeList &List);
|
||||
|
||||
// Return a flattened representation of the inline scope tree that
|
||||
// is equivalent to the reversed inlined-at chain.
|
||||
InlineScopeList flattenedInlineTree() const {
|
||||
InlineScopeList List;
|
||||
flatten(this, List);
|
||||
return List;
|
||||
}
|
||||
SILFunction *getParentFunction() const;
|
||||
|
||||
#ifndef NDEBUG
|
||||
void dump(SourceManager &SM, llvm::raw_ostream &OS = llvm::errs(),
|
||||
unsigned Indent = 0) const;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
@@ -410,9 +410,12 @@ public:
|
||||
SourceLoc getSourceLoc() const;
|
||||
SourceLoc getStartSourceLoc() const;
|
||||
SourceLoc getEndSourceLoc() const;
|
||||
|
||||
SourceRange getSourceRange() const {
|
||||
return { getStartSourceLoc(), getEndSourceLoc() };
|
||||
return {getStartSourceLoc(), getEndSourceLoc()};
|
||||
}
|
||||
DebugLoc getDebugInfoLoc() const {
|
||||
assert(isDebugInfoLoc());
|
||||
return Loc.DebugInfoLoc;
|
||||
}
|
||||
|
||||
/// Fingerprint a DebugLoc for use in a DenseMap.
|
||||
|
||||
@@ -51,7 +51,7 @@ public:
|
||||
CloneCollector::CallbackType Callback = nullptr)
|
||||
: TypeSubstCloner<SILInliner>(To, From, ApplySubs,
|
||||
OpenedArchetypesTracker, true),
|
||||
IKind(IKind), CalleeEntryBB(nullptr), CallSiteScope(nullptr),
|
||||
IKind(IKind), CalleeEntryBB(nullptr),
|
||||
Callback(Callback) {
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ private:
|
||||
/// Alternatively, it can be the SIL file location of the call site (in case
|
||||
/// of SIL-to-SIL transformations).
|
||||
Optional<SILLocation> Loc;
|
||||
const SILDebugScope *CallSiteScope;
|
||||
const SILDebugScope *CallSiteScope = nullptr;
|
||||
SILFunction *CalleeFunction;
|
||||
llvm::SmallDenseMap<const SILDebugScope *,
|
||||
const SILDebugScope *> InlinedScopeCache;
|
||||
|
||||
@@ -190,7 +190,7 @@ SILLocation::DebugLoc getDeserializedLoc(Decl *D) {
|
||||
|
||||
/// Use the SM to figure out the actual line/column of a SourceLoc.
|
||||
template <typename WithLoc>
|
||||
SILLocation::DebugLoc getDebugLoc(SourceManager &SM, WithLoc *S,
|
||||
SILLocation::DebugLoc getDebugLoc(IRGenDebugInfo &DI, WithLoc *S,
|
||||
bool End = false) {
|
||||
SILLocation::DebugLoc L;
|
||||
if (S == nullptr)
|
||||
@@ -203,22 +203,37 @@ SILLocation::DebugLoc getDebugLoc(SourceManager &SM, WithLoc *S,
|
||||
// the module.
|
||||
return getDeserializedLoc(S);
|
||||
|
||||
return SILLocation::decode(Loc, SM);
|
||||
return DI.decodeSourceLoc(Loc);
|
||||
}
|
||||
|
||||
/// Return the start of the location's source range.
|
||||
static SILLocation::DebugLoc getStartLocation(Optional<SILLocation> OptLoc,
|
||||
SourceManager &SM) {
|
||||
if (!OptLoc) return {};
|
||||
return SILLocation::decode(OptLoc->getStartSourceLoc(), SM);
|
||||
SILLocation::DebugLoc
|
||||
IRGenDebugInfo::getStartLocation(Optional<SILLocation> OptLoc) {
|
||||
if (!OptLoc)
|
||||
return {};
|
||||
return decodeSourceLoc(OptLoc->getStartSourceLoc());
|
||||
}
|
||||
|
||||
/// Return the debug location from a SILLocation.
|
||||
static SILLocation::DebugLoc getDebugLocation(Optional<SILLocation> OptLoc,
|
||||
SourceManager &SM) {
|
||||
SILLocation::DebugLoc
|
||||
IRGenDebugInfo::decodeSourceLoc(SourceLoc SL) {
|
||||
auto &Cached = DebugLocCache[SL.getOpaquePointerValue()];
|
||||
if (Cached.Filename.empty())
|
||||
Cached = SILLocation::decode(SL, SM);
|
||||
return Cached;
|
||||
}
|
||||
|
||||
SILLocation::DebugLoc
|
||||
IRGenDebugInfo::decodeDebugLoc(SILLocation Loc) {
|
||||
if (Loc.isDebugInfoLoc())
|
||||
return Loc.getDebugInfoLoc();
|
||||
return decodeSourceLoc(Loc.getDebugSourceLoc());
|
||||
}
|
||||
|
||||
SILLocation::DebugLoc
|
||||
IRGenDebugInfo::getDebugLocation(Optional<SILLocation> OptLoc) {
|
||||
if (!OptLoc || OptLoc->isInPrologue())
|
||||
return {};
|
||||
return OptLoc->decodeDebugLoc(SM);
|
||||
|
||||
return decodeDebugLoc(*OptLoc);
|
||||
}
|
||||
|
||||
|
||||
@@ -240,33 +255,19 @@ static bool isAbstractClosure(const SILLocation &Loc) {
|
||||
}
|
||||
|
||||
llvm::MDNode *IRGenDebugInfo::createInlinedAt(const SILDebugScope *DS) {
|
||||
llvm::MDNode *InlinedAt = nullptr;
|
||||
if (DS) {
|
||||
// The inlined-at chain, starting with the innermost (noninlined) scope.
|
||||
auto Scopes = DS->flattenedInlineTree();
|
||||
auto *CS = DS->InlinedCallSite;
|
||||
if (!CS)
|
||||
return nullptr;
|
||||
|
||||
// See if we share a common prefix with the last chain of inline scopes.
|
||||
unsigned N = 0;
|
||||
while (N < LastInlineChain.size() && N < Scopes.size() &&
|
||||
LastInlineChain[N].first == Scopes[N])
|
||||
InlinedAt = LastInlineChain[N++].second;
|
||||
LastInlineChain.resize(N);
|
||||
auto CachedInlinedAt = InlinedAtCache.find(CS);
|
||||
if (CachedInlinedAt != InlinedAtCache.end())
|
||||
return cast<llvm::MDNode>(CachedInlinedAt->second);
|
||||
|
||||
// Construct the new suffix.
|
||||
for (; N < Scopes.size(); ++N) {
|
||||
auto *CS = Scopes[N];
|
||||
// In SIL the inlined-at information is part of the scopes, in
|
||||
// LLVM IR it is part of the location. Transforming the inlined-at
|
||||
// SIL scope to a location means skipping the inlined-at scope.
|
||||
auto *Parent = CS->Parent.get<const SILDebugScope *>();
|
||||
auto *ParentScope = getOrCreateScope(Parent);
|
||||
auto L = CS->Loc.decodeDebugLoc(SM);
|
||||
InlinedAt = llvm::DebugLoc::get(L.Line, L.Column, ParentScope, InlinedAt);
|
||||
|
||||
// Cache the suffix.
|
||||
LastInlineChain.push_back({CS, llvm::TrackingMDNodeRef(InlinedAt)});
|
||||
}
|
||||
}
|
||||
auto L = decodeDebugLoc(CS->Loc);
|
||||
auto Scope = getOrCreateScope(CS->Parent.dyn_cast<const SILDebugScope *>());
|
||||
auto InlinedAt =
|
||||
llvm::DebugLoc::get(L.Line, L.Column, Scope, createInlinedAt(CS));
|
||||
InlinedAtCache.insert({CS, llvm::TrackingMDNodeRef(InlinedAt.getAsMDNode())});
|
||||
return InlinedAt;
|
||||
}
|
||||
|
||||
@@ -299,13 +300,11 @@ bool IRGenDebugInfo::lineNumberIsSane(IRBuilder &Builder, unsigned Line) {
|
||||
void IRGenDebugInfo::setCurrentLoc(IRBuilder &Builder, const SILDebugScope *DS,
|
||||
Optional<SILLocation> Loc) {
|
||||
assert(DS && "empty scope");
|
||||
// Inline info is emitted as part of the location below; extract the
|
||||
// original scope here.
|
||||
auto *Scope = getOrCreateScope(DS->getInlinedScope());
|
||||
auto *Scope = getOrCreateScope(DS);
|
||||
if (!Scope)
|
||||
return;
|
||||
|
||||
auto L = getDebugLocation(Loc, SM);
|
||||
auto L = getDebugLocation(Loc);
|
||||
auto *File = getOrCreateFile(L.Filename);
|
||||
if (File->getFilename() != Scope->getFilename()) {
|
||||
// We changed files in the middle of a scope. This happens, for
|
||||
@@ -364,7 +363,7 @@ llvm::DIScope *IRGenDebugInfo::getOrCreateScope(const SILDebugScope *DS) {
|
||||
return MainFile;
|
||||
|
||||
// Try to find it in the cache first.
|
||||
auto CachedScope = ScopeCache.find(DS);
|
||||
auto CachedScope = ScopeCache.find(LocalScope(DS));
|
||||
if (CachedScope != ScopeCache.end())
|
||||
return cast<llvm::DIScope>(CachedScope->second);
|
||||
|
||||
@@ -376,7 +375,7 @@ llvm::DIScope *IRGenDebugInfo::getOrCreateScope(const SILDebugScope *DS) {
|
||||
if (!FnScope)
|
||||
SILFn->setDebugScope(DS);
|
||||
|
||||
auto CachedScope = ScopeCache.find(FnScope);
|
||||
auto CachedScope = ScopeCache.find(LocalScope(FnScope));
|
||||
if (CachedScope != ScopeCache.end())
|
||||
return cast<llvm::DIScope>(CachedScope->second);
|
||||
|
||||
@@ -388,7 +387,7 @@ llvm::DIScope *IRGenDebugInfo::getOrCreateScope(const SILDebugScope *DS) {
|
||||
auto *SP = emitFunction(*SILFn, Fn);
|
||||
|
||||
// Cache it.
|
||||
ScopeCache[DS] = llvm::TrackingMDNodeRef(SP);
|
||||
ScopeCache[LocalScope(DS)] = llvm::TrackingMDNodeRef(SP);
|
||||
return SP;
|
||||
}
|
||||
|
||||
@@ -400,13 +399,12 @@ llvm::DIScope *IRGenDebugInfo::getOrCreateScope(const SILDebugScope *DS) {
|
||||
return Parent;
|
||||
|
||||
assert(DS->Parent && "lexical block must have a parent subprogram");
|
||||
auto L = getStartLocation(DS->Loc, SM);
|
||||
auto L = getStartLocation(DS->Loc);
|
||||
llvm::DIFile *File = getOrCreateFile(L.Filename);
|
||||
auto *DScope = DBuilder.createLexicalBlock(Parent, File, L.Line, L.Column);
|
||||
|
||||
// Cache it.
|
||||
ScopeCache[DS] = llvm::TrackingMDNodeRef(DScope);
|
||||
|
||||
ScopeCache[LocalScope(DS)] = llvm::TrackingMDNodeRef(DScope);
|
||||
return DScope;
|
||||
}
|
||||
|
||||
@@ -549,7 +547,7 @@ llvm::DIScope *IRGenDebugInfo::getOrCreateContext(DeclContext *DC) {
|
||||
return DITy;
|
||||
|
||||
// Create a Forward-declared type.
|
||||
auto Loc = getDebugLoc(SM, NTD);
|
||||
auto Loc = getDebugLoc(*this, NTD);
|
||||
auto File = getOrCreateFile(Loc.Filename);
|
||||
auto Line = Loc.Line;
|
||||
auto FwdDecl = DBuilder.createReplaceableCompositeType(
|
||||
@@ -630,7 +628,7 @@ llvm::DISubprogram *
|
||||
IRGenDebugInfo::emitFunction(const SILDebugScope *DS, llvm::Function *Fn,
|
||||
SILFunctionTypeRepresentation Rep, SILType SILTy,
|
||||
DeclContext *DeclCtx, GenericEnvironment *GE) {
|
||||
auto Cached = ScopeCache.find(DS);
|
||||
auto Cached = ScopeCache.find(LocalScope(DS));
|
||||
if (Cached != ScopeCache.end()) {
|
||||
auto SP = cast<llvm::DISubprogram>(Cached->second);
|
||||
// If we created the DISubprogram for a forward declaration,
|
||||
@@ -666,10 +664,10 @@ IRGenDebugInfo::emitFunction(const SILDebugScope *DS, llvm::Function *Fn,
|
||||
// thunk helpers, where DS->Loc is an arbitrary location of whichever use
|
||||
// was emitted first.
|
||||
if (DS && (!SILFn || (!SILFn->isBare() && !SILFn->isThunk()))) {
|
||||
L = DS->Loc.decodeDebugLoc(SM);
|
||||
L = decodeDebugLoc(DS->Loc);
|
||||
ScopeLine = L.Line;
|
||||
if (!DS->Loc.isDebugInfoLoc())
|
||||
L = SILLocation::decode(DS->Loc.getSourceLoc(), SM);
|
||||
L = decodeSourceLoc(DS->Loc.getSourceLoc());
|
||||
}
|
||||
|
||||
auto Line = L.Line;
|
||||
@@ -740,7 +738,7 @@ IRGenDebugInfo::emitFunction(const SILDebugScope *DS, llvm::Function *Fn,
|
||||
if (!DS)
|
||||
return nullptr;
|
||||
|
||||
ScopeCache[DS] = llvm::TrackingMDNodeRef(SP);
|
||||
ScopeCache[LocalScope(DS)] = llvm::TrackingMDNodeRef(SP);
|
||||
return SP;
|
||||
}
|
||||
|
||||
@@ -757,7 +755,7 @@ void IRGenDebugInfo::emitImport(ImportDecl *D) {
|
||||
return;
|
||||
}
|
||||
auto DIMod = getOrCreateModule({D->getModulePath(), M});
|
||||
auto L = getDebugLoc(SM, D);
|
||||
auto L = getDebugLoc(*this, D);
|
||||
DBuilder.createImportedModule(getOrCreateFile(L.Filename), DIMod, L.Line);
|
||||
}
|
||||
|
||||
@@ -896,7 +894,7 @@ void IRGenDebugInfo::emitVariableDeclaration(
|
||||
|
||||
auto *Scope = dyn_cast<llvm::DILocalScope>(getOrCreateScope(DS));
|
||||
assert(Scope && "variable has no local scope");
|
||||
auto Loc = getDebugLoc(SM, VarDecl);
|
||||
auto Loc = getDebugLoc(*this, VarDecl);
|
||||
|
||||
// FIXME: this should be the scope of the type's declaration.
|
||||
// If this is an argument, attach it to the current function scope.
|
||||
@@ -1022,7 +1020,7 @@ void IRGenDebugInfo::emitGlobalVariableDeclaration(
|
||||
// would confuse both the user and LLDB.
|
||||
return;
|
||||
|
||||
auto L = getStartLocation(Loc, SM);
|
||||
auto L = getStartLocation(Loc);
|
||||
auto File = getOrCreateFile(L.Filename);
|
||||
|
||||
// Emit it as global variable of the current module.
|
||||
@@ -1473,7 +1471,7 @@ llvm::DIType *IRGenDebugInfo::createType(DebugTypeInfo DbgTy,
|
||||
case TypeKind::Struct: {
|
||||
auto *StructTy = BaseTy->castTo<StructType>();
|
||||
auto *Decl = StructTy->getDecl();
|
||||
auto L = getDebugLoc(SM, Decl);
|
||||
auto L = getDebugLoc(*this, Decl);
|
||||
auto *File = getOrCreateFile(L.Filename);
|
||||
if (Opts.DebugInfoKind > IRGenDebugInfoKind::ASTTypes)
|
||||
return createStructType(DbgTy, Decl, StructTy, Scope, File, L.Line,
|
||||
@@ -1491,7 +1489,7 @@ llvm::DIType *IRGenDebugInfo::createType(DebugTypeInfo DbgTy,
|
||||
// used to differentiate them from C++ and ObjC classes.
|
||||
auto *ClassTy = BaseTy->castTo<ClassType>();
|
||||
auto *Decl = ClassTy->getDecl();
|
||||
auto L = getDebugLoc(SM, Decl);
|
||||
auto L = getDebugLoc(*this, Decl);
|
||||
if (auto *ClangDecl = Decl->getClangDecl()) {
|
||||
auto ClangSrcLoc = ClangDecl->getLocStart();
|
||||
clang::SourceManager &ClangSM =
|
||||
@@ -1531,7 +1529,7 @@ llvm::DIType *IRGenDebugInfo::createType(DebugTypeInfo DbgTy,
|
||||
auto *ProtocolTy = BaseTy->castTo<ProtocolType>();
|
||||
auto *Decl = ProtocolTy->getDecl();
|
||||
// FIXME: (LLVM branch) This should probably be a DW_TAG_interface_type.
|
||||
auto L = getDebugLoc(SM, Decl);
|
||||
auto L = getDebugLoc(*this, Decl);
|
||||
auto File = getOrCreateFile(L.Filename);
|
||||
return createOpaqueStruct(Scope, Decl ? Decl->getNameStr() : MangledName,
|
||||
File, L.Line, SizeInBits, AlignInBits, Flags,
|
||||
@@ -1540,7 +1538,7 @@ llvm::DIType *IRGenDebugInfo::createType(DebugTypeInfo DbgTy,
|
||||
|
||||
case TypeKind::ProtocolComposition: {
|
||||
auto *Decl = DbgTy.getDecl();
|
||||
auto L = getDebugLoc(SM, Decl);
|
||||
auto L = getDebugLoc(*this, Decl);
|
||||
auto File = getOrCreateFile(L.Filename);
|
||||
|
||||
// FIXME: emit types
|
||||
@@ -1553,7 +1551,7 @@ llvm::DIType *IRGenDebugInfo::createType(DebugTypeInfo DbgTy,
|
||||
case TypeKind::UnboundGeneric: {
|
||||
auto *UnboundTy = BaseTy->castTo<UnboundGenericType>();
|
||||
auto *Decl = UnboundTy->getDecl();
|
||||
auto L = getDebugLoc(SM, Decl);
|
||||
auto L = getDebugLoc(*this, Decl);
|
||||
assert(SizeInBits == CI.getTargetInfo().getPointerWidth(0));
|
||||
return createPointerSizedStruct(Scope,
|
||||
Decl ? Decl->getNameStr() : MangledName,
|
||||
@@ -1563,7 +1561,7 @@ llvm::DIType *IRGenDebugInfo::createType(DebugTypeInfo DbgTy,
|
||||
case TypeKind::BoundGenericStruct: {
|
||||
auto *StructTy = BaseTy->castTo<BoundGenericStructType>();
|
||||
auto *Decl = StructTy->getDecl();
|
||||
auto L = getDebugLoc(SM, Decl);
|
||||
auto L = getDebugLoc(*this, Decl);
|
||||
return createOpaqueStruct(Scope, Decl ? Decl->getNameStr() : MangledName,
|
||||
File, L.Line, SizeInBits, AlignInBits, Flags,
|
||||
MangledName);
|
||||
@@ -1572,7 +1570,7 @@ llvm::DIType *IRGenDebugInfo::createType(DebugTypeInfo DbgTy,
|
||||
case TypeKind::BoundGenericClass: {
|
||||
auto *ClassTy = BaseTy->castTo<BoundGenericClassType>();
|
||||
auto *Decl = ClassTy->getDecl();
|
||||
auto L = getDebugLoc(SM, Decl);
|
||||
auto L = getDebugLoc(*this, Decl);
|
||||
// TODO: We may want to peek at Decl->isObjC() and set this
|
||||
// attribute accordingly.
|
||||
assert(SizeInBits == CI.getTargetInfo().getPointerWidth(0));
|
||||
@@ -1608,7 +1606,7 @@ llvm::DIType *IRGenDebugInfo::createType(DebugTypeInfo DbgTy,
|
||||
|
||||
case TypeKind::Archetype: {
|
||||
auto *Archetype = BaseTy->castTo<ArchetypeType>();
|
||||
auto L = getDebugLoc(SM, Archetype->getAssocType());
|
||||
auto L = getDebugLoc(*this, Archetype->getAssocType());
|
||||
auto Superclass = Archetype->getSuperclass();
|
||||
auto DerivedFrom = Superclass.isNull()
|
||||
? nullptr
|
||||
@@ -1645,7 +1643,7 @@ llvm::DIType *IRGenDebugInfo::createType(DebugTypeInfo DbgTy,
|
||||
case TypeKind::Metatype: {
|
||||
// Metatypes are (mostly) singleton type descriptors, often without storage.
|
||||
Flags |= llvm::DINode::FlagArtificial;
|
||||
auto L = getDebugLoc(SM, DbgTy.getDecl());
|
||||
auto L = getDebugLoc(*this, DbgTy.getDecl());
|
||||
auto File = getOrCreateFile(L.Filename);
|
||||
return DBuilder.createStructType(
|
||||
Scope, MangledName, File, L.Line, SizeInBits, AlignInBits, Flags,
|
||||
@@ -1667,7 +1665,7 @@ llvm::DIType *IRGenDebugInfo::createType(DebugTypeInfo DbgTy,
|
||||
case TypeKind::Enum: {
|
||||
auto *EnumTy = BaseTy->castTo<EnumType>();
|
||||
auto *Decl = EnumTy->getDecl();
|
||||
auto L = getDebugLoc(SM, Decl);
|
||||
auto L = getDebugLoc(*this, Decl);
|
||||
auto *File = getOrCreateFile(L.Filename);
|
||||
if (Opts.DebugInfoKind > IRGenDebugInfoKind::ASTTypes)
|
||||
return createEnumType(DbgTy, Decl, MangledName, Scope, File, L.Line,
|
||||
@@ -1680,7 +1678,7 @@ llvm::DIType *IRGenDebugInfo::createType(DebugTypeInfo DbgTy,
|
||||
case TypeKind::BoundGenericEnum: {
|
||||
auto *EnumTy = BaseTy->castTo<BoundGenericEnumType>();
|
||||
auto *Decl = EnumTy->getDecl();
|
||||
auto L = getDebugLoc(SM, Decl);
|
||||
auto L = getDebugLoc(*this, Decl);
|
||||
auto *File = getOrCreateFile(L.Filename);
|
||||
if (Opts.DebugInfoKind > IRGenDebugInfoKind::ASTTypes)
|
||||
return createEnumType(DbgTy, Decl, MangledName, Scope, File, L.Line,
|
||||
@@ -1709,7 +1707,7 @@ llvm::DIType *IRGenDebugInfo::createType(DebugTypeInfo DbgTy,
|
||||
case TypeKind::WeakStorage: {
|
||||
auto *ReferenceTy = cast<ReferenceStorageType>(BaseTy);
|
||||
auto CanTy = ReferenceTy->getReferentType();
|
||||
auto L = getDebugLoc(SM, DbgTy.getDecl());
|
||||
auto L = getDebugLoc(*this, DbgTy.getDecl());
|
||||
auto File = getOrCreateFile(L.Filename);
|
||||
return DBuilder.createTypedef(getOrCreateDesugaredType(CanTy, DbgTy),
|
||||
MangledName, File, L.Line, File);
|
||||
@@ -1721,7 +1719,7 @@ llvm::DIType *IRGenDebugInfo::createType(DebugTypeInfo DbgTy,
|
||||
|
||||
auto *NameAliasTy = cast<NameAliasType>(BaseTy);
|
||||
auto *Decl = NameAliasTy->getDecl();
|
||||
auto L = getDebugLoc(SM, Decl);
|
||||
auto L = getDebugLoc(*this, Decl);
|
||||
auto AliasedTy = NameAliasTy->getSinglyDesugaredType();
|
||||
auto File = getOrCreateFile(L.Filename);
|
||||
// For NameAlias types, the DeclContext for the aliasED type is
|
||||
|
||||
@@ -61,14 +61,24 @@ class IRGenDebugInfo {
|
||||
llvm::DIBuilder DBuilder;
|
||||
IRGenModule &IGM;
|
||||
|
||||
// Various caches.
|
||||
llvm::DenseMap<const SILDebugScope *, llvm::TrackingMDNodeRef> ScopeCache;
|
||||
/// Used for caching SILDebugScopes without inline information.
|
||||
typedef std::pair<const void *, void *> LocalScopeHash;
|
||||
struct LocalScope : public LocalScopeHash {
|
||||
LocalScope(const SILDebugScope *DS)
|
||||
: LocalScopeHash({DS->Loc.getOpaquePointerValue(),
|
||||
DS->Parent.getOpaqueValue()}) {}
|
||||
};
|
||||
|
||||
/// Various caches.
|
||||
/// @{
|
||||
llvm::DenseMap<LocalScopeHash, llvm::TrackingMDNodeRef> ScopeCache;
|
||||
llvm::DenseMap<const SILDebugScope *, llvm::TrackingMDNodeRef> InlinedAtCache;
|
||||
llvm::DenseMap<llvm::StringRef, llvm::TrackingMDNodeRef> DIFileCache;
|
||||
llvm::DenseMap<const void *, SILLocation::DebugLoc> DebugLocCache;
|
||||
llvm::DenseMap<TypeBase *, llvm::TrackingMDNodeRef> DITypeCache;
|
||||
llvm::StringMap<llvm::TrackingMDNodeRef> DIModuleCache;
|
||||
TrackingDIRefMap DIRefMap;
|
||||
std::vector<std::pair<const SILDebugScope *, llvm::TrackingMDNodeRef>>
|
||||
LastInlineChain;
|
||||
/// @}
|
||||
|
||||
/// A list of replaceable fwddecls that need to be RAUWed at the end.
|
||||
std::vector<std::pair<TypeBase *, llvm::TrackingMDRef>> ReplaceMap;
|
||||
@@ -207,7 +217,17 @@ public:
|
||||
/// Return the DIBuilder.
|
||||
llvm::DIBuilder &getBuilder() { return DBuilder; }
|
||||
|
||||
/// Decode (and cache) a SourceLoc.
|
||||
SILLocation::DebugLoc decodeSourceLoc(SourceLoc SL);
|
||||
private:
|
||||
/// Decode (and cache) a SILLocation.
|
||||
SILLocation::DebugLoc decodeDebugLoc(SILLocation Loc);
|
||||
/// Return the debug location from a SILLocation.
|
||||
SILLocation::DebugLoc getDebugLocation(Optional<SILLocation> OptLoc);
|
||||
/// Return the start of the location's source range.
|
||||
SILLocation::DebugLoc getStartLocation(Optional<SILLocation> OptLoc);
|
||||
|
||||
|
||||
StringRef BumpAllocatedString(const char *Data, size_t Length);
|
||||
StringRef BumpAllocatedString(std::string S);
|
||||
StringRef BumpAllocatedString(StringRef S);
|
||||
|
||||
@@ -13,6 +13,7 @@ add_swift_library(swiftSIL STATIC
|
||||
SILBasicBlock.cpp
|
||||
SILBuilder.cpp
|
||||
SILCoverageMap.cpp
|
||||
SILDebugScope.cpp
|
||||
SILDeclRef.cpp
|
||||
SILDefaultWitnessTable.cpp
|
||||
SILFunction.cpp
|
||||
|
||||
57
lib/SIL/SILDebugScope.cpp
Normal file
57
lib/SIL/SILDebugScope.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
//===--- SILDebugScope.h - DebugScopes for SIL code -------------*- C++ -*-===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
///
|
||||
/// This file defines a container for scope information used to
|
||||
/// generate debug info.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "swift/SIL/SILDebugScope.h"
|
||||
#include "swift/SIL/SILFunction.h"
|
||||
|
||||
using namespace swift;
|
||||
|
||||
SILDebugScope::SILDebugScope(SILLocation Loc, SILFunction *SILFn,
|
||||
const SILDebugScope *ParentScope ,
|
||||
const SILDebugScope *InlinedCallSite)
|
||||
: Loc(Loc), InlinedCallSite(InlinedCallSite) {
|
||||
if (ParentScope)
|
||||
Parent = ParentScope;
|
||||
else {
|
||||
assert(SILFn && "no parent provided");
|
||||
Parent = SILFn;
|
||||
}
|
||||
}
|
||||
|
||||
SILDebugScope::SILDebugScope(SILLocation Loc)
|
||||
: Loc(Loc), InlinedCallSite(nullptr) {}
|
||||
|
||||
SILFunction *SILDebugScope::getInlinedFunction() const {
|
||||
if (Parent.isNull())
|
||||
return nullptr;
|
||||
|
||||
const SILDebugScope *Scope = this;
|
||||
while (Scope->Parent.is<const SILDebugScope *>())
|
||||
Scope = Scope->Parent.get<const SILDebugScope *>();
|
||||
assert(Scope->Parent.is<SILFunction *>() && "orphaned scope");
|
||||
return Scope->Parent.get<SILFunction *>();
|
||||
}
|
||||
|
||||
SILFunction *SILDebugScope::getParentFunction() const {
|
||||
if (InlinedCallSite)
|
||||
return InlinedCallSite->getParentFunction();
|
||||
if (auto *ParentScope = Parent.dyn_cast<const SILDebugScope *>())
|
||||
return ParentScope->getParentFunction();
|
||||
return Parent.get<SILFunction *>();
|
||||
}
|
||||
@@ -744,27 +744,24 @@ public:
|
||||
*this << ":in_prologue";
|
||||
}
|
||||
|
||||
// Print inlined-at location, if any.
|
||||
if (DS) {
|
||||
SILFunction *InlinedF = DS->getInlinedFunction();
|
||||
auto InlineScopes = DS->flattenedInlineTree();
|
||||
for (auto *CS : reversed(InlineScopes)) {
|
||||
*this << ": ";
|
||||
if (InlinedF) {
|
||||
*this << demangleSymbol(InlinedF->getName());
|
||||
} else {
|
||||
*this << '?';
|
||||
}
|
||||
*this << " perf_inlined_at ";
|
||||
auto CallSite = CS->Loc;
|
||||
if (!CallSite.isNull() && CallSite.isASTNode())
|
||||
CallSite.getSourceLoc().print(
|
||||
PrintState.OS, M.getASTContext().SourceMgr, LastBufferID);
|
||||
else
|
||||
*this << "?";
|
||||
if (!DS)
|
||||
return;
|
||||
|
||||
InlinedF = CS->getInlinedFunction();
|
||||
}
|
||||
// Print inlined-at location, if any.
|
||||
const SILDebugScope *CS = DS;
|
||||
while ((CS = CS->InlinedCallSite)) {
|
||||
*this << ": ";
|
||||
if (auto *InlinedF = CS->getInlinedFunction())
|
||||
*this << demangleSymbol(InlinedF->getName());
|
||||
else
|
||||
*this << '?';
|
||||
*this << " perf_inlined_at ";
|
||||
auto CallSite = CS->Loc;
|
||||
if (!CallSite.isNull() && CallSite.isASTNode())
|
||||
CallSite.getSourceLoc().print(
|
||||
PrintState.OS, M.getASTContext().SourceMgr, LastBufferID);
|
||||
else
|
||||
*this << "?";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2440,17 +2437,7 @@ void SILCoverageMap::dump() const {
|
||||
print(llvm::errs());
|
||||
}
|
||||
|
||||
void SILDebugScope::flatten(const SILDebugScope *DS,
|
||||
SILDebugScope::InlineScopeList &List) {
|
||||
if (DS) {
|
||||
if (auto *CS = DS->InlinedCallSite) {
|
||||
flatten(CS->Parent.dyn_cast<const SILDebugScope *>(), List);
|
||||
List.push_back(CS);
|
||||
}
|
||||
flatten(DS->Parent.dyn_cast<const SILDebugScope *>(), List);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
void SILDebugScope::dump(SourceManager &SM, llvm::raw_ostream &OS,
|
||||
unsigned Indent) const {
|
||||
OS << "{\n";
|
||||
@@ -2478,6 +2465,7 @@ void SILDebugScope::dump(SourceManager &SM, llvm::raw_ostream &OS,
|
||||
}
|
||||
OS << "}\n";
|
||||
}
|
||||
#endif
|
||||
|
||||
void SILSpecializeAttr::print(llvm::raw_ostream &OS) const {
|
||||
SILPrintContext Ctx(OS);
|
||||
|
||||
@@ -71,10 +71,10 @@ bool SILInliner::inlineFunction(FullApplySite AI, ArrayRef<SILValue> Args) {
|
||||
// Performance inlining. Construct a proper inline scope pointing
|
||||
// back to the call site.
|
||||
CallSiteScope = new (F.getModule())
|
||||
SILDebugScope(AI.getLoc(), &F, AIScope);
|
||||
assert(CallSiteScope->getParentFunction() == &F);
|
||||
SILDebugScope(AI.getLoc(), nullptr, AIScope, AIScope->InlinedCallSite);
|
||||
}
|
||||
assert(CallSiteScope && "call site has no scope");
|
||||
assert(CallSiteScope->getParentFunction() == &F);
|
||||
|
||||
// Increment the ref count for the inlined function, so it doesn't
|
||||
// get deleted before we can emit abstract debug info for it.
|
||||
@@ -198,20 +198,22 @@ void SILInliner::visitDebugValueAddrInst(DebugValueAddrInst *Inst) {
|
||||
|
||||
const SILDebugScope *
|
||||
SILInliner::getOrCreateInlineScope(const SILDebugScope *CalleeScope) {
|
||||
assert(CalleeScope);
|
||||
if (!CalleeScope)
|
||||
return CallSiteScope;
|
||||
auto it = InlinedScopeCache.find(CalleeScope);
|
||||
if (it != InlinedScopeCache.end())
|
||||
return it->second;
|
||||
|
||||
auto InlineScope = new (getBuilder().getFunction().getModule())
|
||||
SILDebugScope(CallSiteScope, CalleeScope);
|
||||
assert(CallSiteScope->Parent == InlineScope->InlinedCallSite->Parent);
|
||||
|
||||
InlinedScopeCache.insert({CalleeScope, InlineScope});
|
||||
return InlineScope;
|
||||
auto &M = getBuilder().getFunction().getModule();
|
||||
auto InlinedAt =
|
||||
getOrCreateInlineScope(CalleeScope->InlinedCallSite);
|
||||
auto *InlinedScope = new (M) SILDebugScope(
|
||||
CalleeScope->Loc, CalleeScope->Parent.dyn_cast<SILFunction *>(),
|
||||
CalleeScope->Parent.dyn_cast<const SILDebugScope *>(), InlinedAt);
|
||||
InlinedScopeCache.insert({CalleeScope, InlinedScope});
|
||||
return InlinedScope;
|
||||
}
|
||||
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Cost Model
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
@@ -51,3 +51,4 @@ public func f(_ i : Int) -> Int { // 301
|
||||
// CHECK-SAME: inlinedAt: ![[L2:.*]])
|
||||
// CHECK: ![[L2]] = !DILocation(line: 203, column: 13, scope: ![[G_SCOPE]],
|
||||
// CHECK-SAME: inlinedAt: ![[L3]])
|
||||
|
||||
|
||||
@@ -7,10 +7,10 @@ import Swift
|
||||
import SwiftShims
|
||||
|
||||
|
||||
//CHECK-LABEL: caller_function
|
||||
//CHECK-LABEL: sil @caller_function
|
||||
//CHECK-NOT: try_apply
|
||||
//CHECK: throw {{.*}} : $Error
|
||||
sil @caller_function : $@convention(thin) () -> @error Error {
|
||||
sil @caller_function : $@convention(thin) () -> @error Error {
|
||||
bb0:
|
||||
// function_ref main.inner () throws -> ()
|
||||
%0 = function_ref @callee_function : $@convention(thin) () -> @error Error // user: %1
|
||||
@@ -24,7 +24,7 @@ bb2(%5 : $Error): // Preds: bb0
|
||||
throw %5 : $Error // id: %6
|
||||
}
|
||||
|
||||
//CHECK-LABEL: callee_function
|
||||
//CHECK-LABEL: sil [always_inline] @callee_function
|
||||
//CHECK: return
|
||||
|
||||
// main.inner () throws -> ()
|
||||
|
||||
Reference in New Issue
Block a user