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:
Adrian Prantl
2017-04-03 15:46:56 -07:00
parent 3e28874bd0
commit 5ea2d13f5e
11 changed files with 196 additions and 180 deletions

View File

@@ -46,82 +46,28 @@ public:
/// An optional chain of inlined call sites. /// An optional chain of inlined call sites.
/// ///
/// If this scope is inlined, this points to a special "scope" that /// 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 /// holds the location of the call site.
/// the scope of the inlined call site.
///
/// Note that compared to the inlinedAt chain in llvm::DILocation
/// SILDebugScope represents an inline tree.
const SILDebugScope *InlinedCallSite; const SILDebugScope *InlinedCallSite;
SILDebugScope(SILLocation Loc, SILFunction *SILFn, SILDebugScope(SILLocation Loc, SILFunction *SILFn,
const SILDebugScope *ParentScope = nullptr, const SILDebugScope *ParentScope = nullptr,
const SILDebugScope *InlinedCallSite = nullptr) const SILDebugScope *InlinedCallSite = nullptr);
: Loc(Loc), InlinedCallSite(InlinedCallSite) {
if (ParentScope)
Parent = ParentScope;
else {
assert(SILFn && "no parent provided");
Parent = SILFn;
}
}
/// Create a scope for an artificial function. /// Create a scope for an artificial function.
SILDebugScope(SILLocation Loc) 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");
}
/// Return the function this scope originated from before being inlined. /// Return the function this scope originated from before being inlined.
SILFunction *getInlinedFunction() const { 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;
}
/// Return the parent function of this scope. If the scope was /// Return the parent function of this scope. If the scope was
/// inlined this recursively returns the function it was inlined /// inlined this recursively returns the function it was inlined
/// into. /// into.
SILFunction *getParentFunction() const { 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;
}
#ifndef NDEBUG
void dump(SourceManager &SM, llvm::raw_ostream &OS = llvm::errs(), void dump(SourceManager &SM, llvm::raw_ostream &OS = llvm::errs(),
unsigned Indent = 0) const; unsigned Indent = 0) const;
#endif
}; };
#ifndef NDEBUG #ifndef NDEBUG

View File

@@ -410,9 +410,12 @@ public:
SourceLoc getSourceLoc() const; SourceLoc getSourceLoc() const;
SourceLoc getStartSourceLoc() const; SourceLoc getStartSourceLoc() const;
SourceLoc getEndSourceLoc() const; SourceLoc getEndSourceLoc() const;
SourceRange getSourceRange() 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. /// Fingerprint a DebugLoc for use in a DenseMap.

View File

@@ -51,7 +51,7 @@ public:
CloneCollector::CallbackType Callback = nullptr) CloneCollector::CallbackType Callback = nullptr)
: TypeSubstCloner<SILInliner>(To, From, ApplySubs, : TypeSubstCloner<SILInliner>(To, From, ApplySubs,
OpenedArchetypesTracker, true), OpenedArchetypesTracker, true),
IKind(IKind), CalleeEntryBB(nullptr), CallSiteScope(nullptr), IKind(IKind), CalleeEntryBB(nullptr),
Callback(Callback) { Callback(Callback) {
} }
@@ -114,7 +114,7 @@ private:
/// Alternatively, it can be the SIL file location of the call site (in case /// Alternatively, it can be the SIL file location of the call site (in case
/// of SIL-to-SIL transformations). /// of SIL-to-SIL transformations).
Optional<SILLocation> Loc; Optional<SILLocation> Loc;
const SILDebugScope *CallSiteScope; const SILDebugScope *CallSiteScope = nullptr;
SILFunction *CalleeFunction; SILFunction *CalleeFunction;
llvm::SmallDenseMap<const SILDebugScope *, llvm::SmallDenseMap<const SILDebugScope *,
const SILDebugScope *> InlinedScopeCache; const SILDebugScope *> InlinedScopeCache;

View File

@@ -190,7 +190,7 @@ SILLocation::DebugLoc getDeserializedLoc(Decl *D) {
/// Use the SM to figure out the actual line/column of a SourceLoc. /// Use the SM to figure out the actual line/column of a SourceLoc.
template <typename WithLoc> template <typename WithLoc>
SILLocation::DebugLoc getDebugLoc(SourceManager &SM, WithLoc *S, SILLocation::DebugLoc getDebugLoc(IRGenDebugInfo &DI, WithLoc *S,
bool End = false) { bool End = false) {
SILLocation::DebugLoc L; SILLocation::DebugLoc L;
if (S == nullptr) if (S == nullptr)
@@ -203,22 +203,37 @@ SILLocation::DebugLoc getDebugLoc(SourceManager &SM, WithLoc *S,
// the module. // the module.
return getDeserializedLoc(S); return getDeserializedLoc(S);
return SILLocation::decode(Loc, SM); return DI.decodeSourceLoc(Loc);
} }
/// Return the start of the location's source range. SILLocation::DebugLoc
static SILLocation::DebugLoc getStartLocation(Optional<SILLocation> OptLoc, IRGenDebugInfo::getStartLocation(Optional<SILLocation> OptLoc) {
SourceManager &SM) { if (!OptLoc)
if (!OptLoc) return {}; return {};
return SILLocation::decode(OptLoc->getStartSourceLoc(), SM); return decodeSourceLoc(OptLoc->getStartSourceLoc());
} }
/// Return the debug location from a SILLocation. SILLocation::DebugLoc
static SILLocation::DebugLoc getDebugLocation(Optional<SILLocation> OptLoc, IRGenDebugInfo::decodeSourceLoc(SourceLoc SL) {
SourceManager &SM) { 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()) if (!OptLoc || OptLoc->isInPrologue())
return {}; 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 *IRGenDebugInfo::createInlinedAt(const SILDebugScope *DS) {
llvm::MDNode *InlinedAt = nullptr; auto *CS = DS->InlinedCallSite;
if (DS) { if (!CS)
// The inlined-at chain, starting with the innermost (noninlined) scope. return nullptr;
auto Scopes = DS->flattenedInlineTree();
// See if we share a common prefix with the last chain of inline scopes. auto CachedInlinedAt = InlinedAtCache.find(CS);
unsigned N = 0; if (CachedInlinedAt != InlinedAtCache.end())
while (N < LastInlineChain.size() && N < Scopes.size() && return cast<llvm::MDNode>(CachedInlinedAt->second);
LastInlineChain[N].first == Scopes[N])
InlinedAt = LastInlineChain[N++].second;
LastInlineChain.resize(N);
// Construct the new suffix. auto L = decodeDebugLoc(CS->Loc);
for (; N < Scopes.size(); ++N) { auto Scope = getOrCreateScope(CS->Parent.dyn_cast<const SILDebugScope *>());
auto *CS = Scopes[N]; auto InlinedAt =
// In SIL the inlined-at information is part of the scopes, in llvm::DebugLoc::get(L.Line, L.Column, Scope, createInlinedAt(CS));
// LLVM IR it is part of the location. Transforming the inlined-at InlinedAtCache.insert({CS, llvm::TrackingMDNodeRef(InlinedAt.getAsMDNode())});
// 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)});
}
}
return InlinedAt; return InlinedAt;
} }
@@ -299,13 +300,11 @@ bool IRGenDebugInfo::lineNumberIsSane(IRBuilder &Builder, unsigned Line) {
void IRGenDebugInfo::setCurrentLoc(IRBuilder &Builder, const SILDebugScope *DS, void IRGenDebugInfo::setCurrentLoc(IRBuilder &Builder, const SILDebugScope *DS,
Optional<SILLocation> Loc) { Optional<SILLocation> Loc) {
assert(DS && "empty scope"); assert(DS && "empty scope");
// Inline info is emitted as part of the location below; extract the auto *Scope = getOrCreateScope(DS);
// original scope here.
auto *Scope = getOrCreateScope(DS->getInlinedScope());
if (!Scope) if (!Scope)
return; return;
auto L = getDebugLocation(Loc, SM); auto L = getDebugLocation(Loc);
auto *File = getOrCreateFile(L.Filename); auto *File = getOrCreateFile(L.Filename);
if (File->getFilename() != Scope->getFilename()) { if (File->getFilename() != Scope->getFilename()) {
// We changed files in the middle of a scope. This happens, for // 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; return MainFile;
// Try to find it in the cache first. // Try to find it in the cache first.
auto CachedScope = ScopeCache.find(DS); auto CachedScope = ScopeCache.find(LocalScope(DS));
if (CachedScope != ScopeCache.end()) if (CachedScope != ScopeCache.end())
return cast<llvm::DIScope>(CachedScope->second); return cast<llvm::DIScope>(CachedScope->second);
@@ -376,7 +375,7 @@ llvm::DIScope *IRGenDebugInfo::getOrCreateScope(const SILDebugScope *DS) {
if (!FnScope) if (!FnScope)
SILFn->setDebugScope(DS); SILFn->setDebugScope(DS);
auto CachedScope = ScopeCache.find(FnScope); auto CachedScope = ScopeCache.find(LocalScope(FnScope));
if (CachedScope != ScopeCache.end()) if (CachedScope != ScopeCache.end())
return cast<llvm::DIScope>(CachedScope->second); return cast<llvm::DIScope>(CachedScope->second);
@@ -388,7 +387,7 @@ llvm::DIScope *IRGenDebugInfo::getOrCreateScope(const SILDebugScope *DS) {
auto *SP = emitFunction(*SILFn, Fn); auto *SP = emitFunction(*SILFn, Fn);
// Cache it. // Cache it.
ScopeCache[DS] = llvm::TrackingMDNodeRef(SP); ScopeCache[LocalScope(DS)] = llvm::TrackingMDNodeRef(SP);
return SP; return SP;
} }
@@ -400,13 +399,12 @@ llvm::DIScope *IRGenDebugInfo::getOrCreateScope(const SILDebugScope *DS) {
return Parent; return Parent;
assert(DS->Parent && "lexical block must have a parent subprogram"); 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); llvm::DIFile *File = getOrCreateFile(L.Filename);
auto *DScope = DBuilder.createLexicalBlock(Parent, File, L.Line, L.Column); auto *DScope = DBuilder.createLexicalBlock(Parent, File, L.Line, L.Column);
// Cache it. // Cache it.
ScopeCache[DS] = llvm::TrackingMDNodeRef(DScope); ScopeCache[LocalScope(DS)] = llvm::TrackingMDNodeRef(DScope);
return DScope; return DScope;
} }
@@ -549,7 +547,7 @@ llvm::DIScope *IRGenDebugInfo::getOrCreateContext(DeclContext *DC) {
return DITy; return DITy;
// Create a Forward-declared type. // Create a Forward-declared type.
auto Loc = getDebugLoc(SM, NTD); auto Loc = getDebugLoc(*this, NTD);
auto File = getOrCreateFile(Loc.Filename); auto File = getOrCreateFile(Loc.Filename);
auto Line = Loc.Line; auto Line = Loc.Line;
auto FwdDecl = DBuilder.createReplaceableCompositeType( auto FwdDecl = DBuilder.createReplaceableCompositeType(
@@ -630,7 +628,7 @@ llvm::DISubprogram *
IRGenDebugInfo::emitFunction(const SILDebugScope *DS, llvm::Function *Fn, IRGenDebugInfo::emitFunction(const SILDebugScope *DS, llvm::Function *Fn,
SILFunctionTypeRepresentation Rep, SILType SILTy, SILFunctionTypeRepresentation Rep, SILType SILTy,
DeclContext *DeclCtx, GenericEnvironment *GE) { DeclContext *DeclCtx, GenericEnvironment *GE) {
auto Cached = ScopeCache.find(DS); auto Cached = ScopeCache.find(LocalScope(DS));
if (Cached != ScopeCache.end()) { if (Cached != ScopeCache.end()) {
auto SP = cast<llvm::DISubprogram>(Cached->second); auto SP = cast<llvm::DISubprogram>(Cached->second);
// If we created the DISubprogram for a forward declaration, // 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 // thunk helpers, where DS->Loc is an arbitrary location of whichever use
// was emitted first. // was emitted first.
if (DS && (!SILFn || (!SILFn->isBare() && !SILFn->isThunk()))) { if (DS && (!SILFn || (!SILFn->isBare() && !SILFn->isThunk()))) {
L = DS->Loc.decodeDebugLoc(SM); L = decodeDebugLoc(DS->Loc);
ScopeLine = L.Line; ScopeLine = L.Line;
if (!DS->Loc.isDebugInfoLoc()) if (!DS->Loc.isDebugInfoLoc())
L = SILLocation::decode(DS->Loc.getSourceLoc(), SM); L = decodeSourceLoc(DS->Loc.getSourceLoc());
} }
auto Line = L.Line; auto Line = L.Line;
@@ -740,7 +738,7 @@ IRGenDebugInfo::emitFunction(const SILDebugScope *DS, llvm::Function *Fn,
if (!DS) if (!DS)
return nullptr; return nullptr;
ScopeCache[DS] = llvm::TrackingMDNodeRef(SP); ScopeCache[LocalScope(DS)] = llvm::TrackingMDNodeRef(SP);
return SP; return SP;
} }
@@ -757,7 +755,7 @@ void IRGenDebugInfo::emitImport(ImportDecl *D) {
return; return;
} }
auto DIMod = getOrCreateModule({D->getModulePath(), M}); auto DIMod = getOrCreateModule({D->getModulePath(), M});
auto L = getDebugLoc(SM, D); auto L = getDebugLoc(*this, D);
DBuilder.createImportedModule(getOrCreateFile(L.Filename), DIMod, L.Line); DBuilder.createImportedModule(getOrCreateFile(L.Filename), DIMod, L.Line);
} }
@@ -896,7 +894,7 @@ void IRGenDebugInfo::emitVariableDeclaration(
auto *Scope = dyn_cast<llvm::DILocalScope>(getOrCreateScope(DS)); auto *Scope = dyn_cast<llvm::DILocalScope>(getOrCreateScope(DS));
assert(Scope && "variable has no local scope"); 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. // FIXME: this should be the scope of the type's declaration.
// If this is an argument, attach it to the current function scope. // 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. // would confuse both the user and LLDB.
return; return;
auto L = getStartLocation(Loc, SM); auto L = getStartLocation(Loc);
auto File = getOrCreateFile(L.Filename); auto File = getOrCreateFile(L.Filename);
// Emit it as global variable of the current module. // Emit it as global variable of the current module.
@@ -1473,7 +1471,7 @@ llvm::DIType *IRGenDebugInfo::createType(DebugTypeInfo DbgTy,
case TypeKind::Struct: { case TypeKind::Struct: {
auto *StructTy = BaseTy->castTo<StructType>(); auto *StructTy = BaseTy->castTo<StructType>();
auto *Decl = StructTy->getDecl(); auto *Decl = StructTy->getDecl();
auto L = getDebugLoc(SM, Decl); auto L = getDebugLoc(*this, Decl);
auto *File = getOrCreateFile(L.Filename); auto *File = getOrCreateFile(L.Filename);
if (Opts.DebugInfoKind > IRGenDebugInfoKind::ASTTypes) if (Opts.DebugInfoKind > IRGenDebugInfoKind::ASTTypes)
return createStructType(DbgTy, Decl, StructTy, Scope, File, L.Line, 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. // used to differentiate them from C++ and ObjC classes.
auto *ClassTy = BaseTy->castTo<ClassType>(); auto *ClassTy = BaseTy->castTo<ClassType>();
auto *Decl = ClassTy->getDecl(); auto *Decl = ClassTy->getDecl();
auto L = getDebugLoc(SM, Decl); auto L = getDebugLoc(*this, Decl);
if (auto *ClangDecl = Decl->getClangDecl()) { if (auto *ClangDecl = Decl->getClangDecl()) {
auto ClangSrcLoc = ClangDecl->getLocStart(); auto ClangSrcLoc = ClangDecl->getLocStart();
clang::SourceManager &ClangSM = clang::SourceManager &ClangSM =
@@ -1531,7 +1529,7 @@ llvm::DIType *IRGenDebugInfo::createType(DebugTypeInfo DbgTy,
auto *ProtocolTy = BaseTy->castTo<ProtocolType>(); auto *ProtocolTy = BaseTy->castTo<ProtocolType>();
auto *Decl = ProtocolTy->getDecl(); auto *Decl = ProtocolTy->getDecl();
// FIXME: (LLVM branch) This should probably be a DW_TAG_interface_type. // 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); auto File = getOrCreateFile(L.Filename);
return createOpaqueStruct(Scope, Decl ? Decl->getNameStr() : MangledName, return createOpaqueStruct(Scope, Decl ? Decl->getNameStr() : MangledName,
File, L.Line, SizeInBits, AlignInBits, Flags, File, L.Line, SizeInBits, AlignInBits, Flags,
@@ -1540,7 +1538,7 @@ llvm::DIType *IRGenDebugInfo::createType(DebugTypeInfo DbgTy,
case TypeKind::ProtocolComposition: { case TypeKind::ProtocolComposition: {
auto *Decl = DbgTy.getDecl(); auto *Decl = DbgTy.getDecl();
auto L = getDebugLoc(SM, Decl); auto L = getDebugLoc(*this, Decl);
auto File = getOrCreateFile(L.Filename); auto File = getOrCreateFile(L.Filename);
// FIXME: emit types // FIXME: emit types
@@ -1553,7 +1551,7 @@ llvm::DIType *IRGenDebugInfo::createType(DebugTypeInfo DbgTy,
case TypeKind::UnboundGeneric: { case TypeKind::UnboundGeneric: {
auto *UnboundTy = BaseTy->castTo<UnboundGenericType>(); auto *UnboundTy = BaseTy->castTo<UnboundGenericType>();
auto *Decl = UnboundTy->getDecl(); auto *Decl = UnboundTy->getDecl();
auto L = getDebugLoc(SM, Decl); auto L = getDebugLoc(*this, Decl);
assert(SizeInBits == CI.getTargetInfo().getPointerWidth(0)); assert(SizeInBits == CI.getTargetInfo().getPointerWidth(0));
return createPointerSizedStruct(Scope, return createPointerSizedStruct(Scope,
Decl ? Decl->getNameStr() : MangledName, Decl ? Decl->getNameStr() : MangledName,
@@ -1563,7 +1561,7 @@ llvm::DIType *IRGenDebugInfo::createType(DebugTypeInfo DbgTy,
case TypeKind::BoundGenericStruct: { case TypeKind::BoundGenericStruct: {
auto *StructTy = BaseTy->castTo<BoundGenericStructType>(); auto *StructTy = BaseTy->castTo<BoundGenericStructType>();
auto *Decl = StructTy->getDecl(); auto *Decl = StructTy->getDecl();
auto L = getDebugLoc(SM, Decl); auto L = getDebugLoc(*this, Decl);
return createOpaqueStruct(Scope, Decl ? Decl->getNameStr() : MangledName, return createOpaqueStruct(Scope, Decl ? Decl->getNameStr() : MangledName,
File, L.Line, SizeInBits, AlignInBits, Flags, File, L.Line, SizeInBits, AlignInBits, Flags,
MangledName); MangledName);
@@ -1572,7 +1570,7 @@ llvm::DIType *IRGenDebugInfo::createType(DebugTypeInfo DbgTy,
case TypeKind::BoundGenericClass: { case TypeKind::BoundGenericClass: {
auto *ClassTy = BaseTy->castTo<BoundGenericClassType>(); auto *ClassTy = BaseTy->castTo<BoundGenericClassType>();
auto *Decl = ClassTy->getDecl(); 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 // TODO: We may want to peek at Decl->isObjC() and set this
// attribute accordingly. // attribute accordingly.
assert(SizeInBits == CI.getTargetInfo().getPointerWidth(0)); assert(SizeInBits == CI.getTargetInfo().getPointerWidth(0));
@@ -1608,7 +1606,7 @@ llvm::DIType *IRGenDebugInfo::createType(DebugTypeInfo DbgTy,
case TypeKind::Archetype: { case TypeKind::Archetype: {
auto *Archetype = BaseTy->castTo<ArchetypeType>(); auto *Archetype = BaseTy->castTo<ArchetypeType>();
auto L = getDebugLoc(SM, Archetype->getAssocType()); auto L = getDebugLoc(*this, Archetype->getAssocType());
auto Superclass = Archetype->getSuperclass(); auto Superclass = Archetype->getSuperclass();
auto DerivedFrom = Superclass.isNull() auto DerivedFrom = Superclass.isNull()
? nullptr ? nullptr
@@ -1645,7 +1643,7 @@ llvm::DIType *IRGenDebugInfo::createType(DebugTypeInfo DbgTy,
case TypeKind::Metatype: { case TypeKind::Metatype: {
// Metatypes are (mostly) singleton type descriptors, often without storage. // Metatypes are (mostly) singleton type descriptors, often without storage.
Flags |= llvm::DINode::FlagArtificial; Flags |= llvm::DINode::FlagArtificial;
auto L = getDebugLoc(SM, DbgTy.getDecl()); auto L = getDebugLoc(*this, DbgTy.getDecl());
auto File = getOrCreateFile(L.Filename); auto File = getOrCreateFile(L.Filename);
return DBuilder.createStructType( return DBuilder.createStructType(
Scope, MangledName, File, L.Line, SizeInBits, AlignInBits, Flags, Scope, MangledName, File, L.Line, SizeInBits, AlignInBits, Flags,
@@ -1667,7 +1665,7 @@ llvm::DIType *IRGenDebugInfo::createType(DebugTypeInfo DbgTy,
case TypeKind::Enum: { case TypeKind::Enum: {
auto *EnumTy = BaseTy->castTo<EnumType>(); auto *EnumTy = BaseTy->castTo<EnumType>();
auto *Decl = EnumTy->getDecl(); auto *Decl = EnumTy->getDecl();
auto L = getDebugLoc(SM, Decl); auto L = getDebugLoc(*this, Decl);
auto *File = getOrCreateFile(L.Filename); auto *File = getOrCreateFile(L.Filename);
if (Opts.DebugInfoKind > IRGenDebugInfoKind::ASTTypes) if (Opts.DebugInfoKind > IRGenDebugInfoKind::ASTTypes)
return createEnumType(DbgTy, Decl, MangledName, Scope, File, L.Line, return createEnumType(DbgTy, Decl, MangledName, Scope, File, L.Line,
@@ -1680,7 +1678,7 @@ llvm::DIType *IRGenDebugInfo::createType(DebugTypeInfo DbgTy,
case TypeKind::BoundGenericEnum: { case TypeKind::BoundGenericEnum: {
auto *EnumTy = BaseTy->castTo<BoundGenericEnumType>(); auto *EnumTy = BaseTy->castTo<BoundGenericEnumType>();
auto *Decl = EnumTy->getDecl(); auto *Decl = EnumTy->getDecl();
auto L = getDebugLoc(SM, Decl); auto L = getDebugLoc(*this, Decl);
auto *File = getOrCreateFile(L.Filename); auto *File = getOrCreateFile(L.Filename);
if (Opts.DebugInfoKind > IRGenDebugInfoKind::ASTTypes) if (Opts.DebugInfoKind > IRGenDebugInfoKind::ASTTypes)
return createEnumType(DbgTy, Decl, MangledName, Scope, File, L.Line, return createEnumType(DbgTy, Decl, MangledName, Scope, File, L.Line,
@@ -1709,7 +1707,7 @@ llvm::DIType *IRGenDebugInfo::createType(DebugTypeInfo DbgTy,
case TypeKind::WeakStorage: { case TypeKind::WeakStorage: {
auto *ReferenceTy = cast<ReferenceStorageType>(BaseTy); auto *ReferenceTy = cast<ReferenceStorageType>(BaseTy);
auto CanTy = ReferenceTy->getReferentType(); auto CanTy = ReferenceTy->getReferentType();
auto L = getDebugLoc(SM, DbgTy.getDecl()); auto L = getDebugLoc(*this, DbgTy.getDecl());
auto File = getOrCreateFile(L.Filename); auto File = getOrCreateFile(L.Filename);
return DBuilder.createTypedef(getOrCreateDesugaredType(CanTy, DbgTy), return DBuilder.createTypedef(getOrCreateDesugaredType(CanTy, DbgTy),
MangledName, File, L.Line, File); MangledName, File, L.Line, File);
@@ -1721,7 +1719,7 @@ llvm::DIType *IRGenDebugInfo::createType(DebugTypeInfo DbgTy,
auto *NameAliasTy = cast<NameAliasType>(BaseTy); auto *NameAliasTy = cast<NameAliasType>(BaseTy);
auto *Decl = NameAliasTy->getDecl(); auto *Decl = NameAliasTy->getDecl();
auto L = getDebugLoc(SM, Decl); auto L = getDebugLoc(*this, Decl);
auto AliasedTy = NameAliasTy->getSinglyDesugaredType(); auto AliasedTy = NameAliasTy->getSinglyDesugaredType();
auto File = getOrCreateFile(L.Filename); auto File = getOrCreateFile(L.Filename);
// For NameAlias types, the DeclContext for the aliasED type is // For NameAlias types, the DeclContext for the aliasED type is

View File

@@ -61,14 +61,24 @@ class IRGenDebugInfo {
llvm::DIBuilder DBuilder; llvm::DIBuilder DBuilder;
IRGenModule &IGM; IRGenModule &IGM;
// Various caches. /// Used for caching SILDebugScopes without inline information.
llvm::DenseMap<const SILDebugScope *, llvm::TrackingMDNodeRef> ScopeCache; 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<llvm::StringRef, llvm::TrackingMDNodeRef> DIFileCache;
llvm::DenseMap<const void *, SILLocation::DebugLoc> DebugLocCache;
llvm::DenseMap<TypeBase *, llvm::TrackingMDNodeRef> DITypeCache; llvm::DenseMap<TypeBase *, llvm::TrackingMDNodeRef> DITypeCache;
llvm::StringMap<llvm::TrackingMDNodeRef> DIModuleCache; llvm::StringMap<llvm::TrackingMDNodeRef> DIModuleCache;
TrackingDIRefMap DIRefMap; TrackingDIRefMap DIRefMap;
std::vector<std::pair<const SILDebugScope *, llvm::TrackingMDNodeRef>> /// @}
LastInlineChain;
/// A list of replaceable fwddecls that need to be RAUWed at the end. /// A list of replaceable fwddecls that need to be RAUWed at the end.
std::vector<std::pair<TypeBase *, llvm::TrackingMDRef>> ReplaceMap; std::vector<std::pair<TypeBase *, llvm::TrackingMDRef>> ReplaceMap;
@@ -207,7 +217,17 @@ public:
/// Return the DIBuilder. /// Return the DIBuilder.
llvm::DIBuilder &getBuilder() { return DBuilder; } llvm::DIBuilder &getBuilder() { return DBuilder; }
/// Decode (and cache) a SourceLoc.
SILLocation::DebugLoc decodeSourceLoc(SourceLoc SL);
private: 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(const char *Data, size_t Length);
StringRef BumpAllocatedString(std::string S); StringRef BumpAllocatedString(std::string S);
StringRef BumpAllocatedString(StringRef S); StringRef BumpAllocatedString(StringRef S);

View File

@@ -13,6 +13,7 @@ add_swift_library(swiftSIL STATIC
SILBasicBlock.cpp SILBasicBlock.cpp
SILBuilder.cpp SILBuilder.cpp
SILCoverageMap.cpp SILCoverageMap.cpp
SILDebugScope.cpp
SILDeclRef.cpp SILDeclRef.cpp
SILDefaultWitnessTable.cpp SILDefaultWitnessTable.cpp
SILFunction.cpp SILFunction.cpp

57
lib/SIL/SILDebugScope.cpp Normal file
View 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 *>();
}

View File

@@ -744,27 +744,24 @@ public:
*this << ":in_prologue"; *this << ":in_prologue";
} }
// Print inlined-at location, if any. if (!DS)
if (DS) { return;
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 << "?";
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()); print(llvm::errs());
} }
void SILDebugScope::flatten(const SILDebugScope *DS, #ifndef NDEBUG
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);
}
}
void SILDebugScope::dump(SourceManager &SM, llvm::raw_ostream &OS, void SILDebugScope::dump(SourceManager &SM, llvm::raw_ostream &OS,
unsigned Indent) const { unsigned Indent) const {
OS << "{\n"; OS << "{\n";
@@ -2478,6 +2465,7 @@ void SILDebugScope::dump(SourceManager &SM, llvm::raw_ostream &OS,
} }
OS << "}\n"; OS << "}\n";
} }
#endif
void SILSpecializeAttr::print(llvm::raw_ostream &OS) const { void SILSpecializeAttr::print(llvm::raw_ostream &OS) const {
SILPrintContext Ctx(OS); SILPrintContext Ctx(OS);

View File

@@ -71,10 +71,10 @@ bool SILInliner::inlineFunction(FullApplySite AI, ArrayRef<SILValue> Args) {
// Performance inlining. Construct a proper inline scope pointing // Performance inlining. Construct a proper inline scope pointing
// back to the call site. // back to the call site.
CallSiteScope = new (F.getModule()) CallSiteScope = new (F.getModule())
SILDebugScope(AI.getLoc(), &F, AIScope); SILDebugScope(AI.getLoc(), nullptr, AIScope, AIScope->InlinedCallSite);
assert(CallSiteScope->getParentFunction() == &F);
} }
assert(CallSiteScope && "call site has no scope"); assert(CallSiteScope && "call site has no scope");
assert(CallSiteScope->getParentFunction() == &F);
// Increment the ref count for the inlined function, so it doesn't // Increment the ref count for the inlined function, so it doesn't
// get deleted before we can emit abstract debug info for it. // get deleted before we can emit abstract debug info for it.
@@ -198,20 +198,22 @@ void SILInliner::visitDebugValueAddrInst(DebugValueAddrInst *Inst) {
const SILDebugScope * const SILDebugScope *
SILInliner::getOrCreateInlineScope(const SILDebugScope *CalleeScope) { SILInliner::getOrCreateInlineScope(const SILDebugScope *CalleeScope) {
assert(CalleeScope); if (!CalleeScope)
return CallSiteScope;
auto it = InlinedScopeCache.find(CalleeScope); auto it = InlinedScopeCache.find(CalleeScope);
if (it != InlinedScopeCache.end()) if (it != InlinedScopeCache.end())
return it->second; return it->second;
auto InlineScope = new (getBuilder().getFunction().getModule()) auto &M = getBuilder().getFunction().getModule();
SILDebugScope(CallSiteScope, CalleeScope); auto InlinedAt =
assert(CallSiteScope->Parent == InlineScope->InlinedCallSite->Parent); getOrCreateInlineScope(CalleeScope->InlinedCallSite);
auto *InlinedScope = new (M) SILDebugScope(
InlinedScopeCache.insert({CalleeScope, InlineScope}); CalleeScope->Loc, CalleeScope->Parent.dyn_cast<SILFunction *>(),
return InlineScope; CalleeScope->Parent.dyn_cast<const SILDebugScope *>(), InlinedAt);
InlinedScopeCache.insert({CalleeScope, InlinedScope});
return InlinedScope;
} }
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// Cost Model // Cost Model
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//

View File

@@ -51,3 +51,4 @@ public func f(_ i : Int) -> Int { // 301
// CHECK-SAME: inlinedAt: ![[L2:.*]]) // CHECK-SAME: inlinedAt: ![[L2:.*]])
// CHECK: ![[L2]] = !DILocation(line: 203, column: 13, scope: ![[G_SCOPE]], // CHECK: ![[L2]] = !DILocation(line: 203, column: 13, scope: ![[G_SCOPE]],
// CHECK-SAME: inlinedAt: ![[L3]]) // CHECK-SAME: inlinedAt: ![[L3]])

View File

@@ -7,10 +7,10 @@ import Swift
import SwiftShims import SwiftShims
//CHECK-LABEL: caller_function //CHECK-LABEL: sil @caller_function
//CHECK-NOT: try_apply //CHECK-NOT: try_apply
//CHECK: throw {{.*}} : $Error //CHECK: throw {{.*}} : $Error
sil @caller_function : $@convention(thin) () -> @error Error { sil @caller_function : $@convention(thin) () -> @error Error {
bb0: bb0:
// function_ref main.inner () throws -> () // function_ref main.inner () throws -> ()
%0 = function_ref @callee_function : $@convention(thin) () -> @error Error // user: %1 %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 throw %5 : $Error // id: %6
} }
//CHECK-LABEL: callee_function //CHECK-LABEL: sil [always_inline] @callee_function
//CHECK: return //CHECK: return
// main.inner () throws -> () // main.inner () throws -> ()