mirror of
https://github.com/xomboverlord/ldc.git
synced 2026-03-02 18:43:14 +01:00
Use Module::members -> Dsymbol::codegen to define symbols.
This commit fundamentally changes the way symbol emission in LDC works: Previously, whenever a declaration was used in some way, the compiler would check whether it actually needs to be defined in the currently processed module, based only on the symbol itself. This lack of contextual information proved to be a major problem in correctly handling emission of templates (see e.g. #454). Now, the DtoResolve…() family of functions and similar only ever declare the symbols, and definition is handled by doing a single pass over Module::members for the root module. This is the same strategy that DMD uses as well, which should also reduce the maintainance burden down the road (which is important as during the last few releases, there was pretty much always a symbol emission related problem slowing us down). Our old approach might have been a bit better tuned w.r.t. avoiding emission of unneeded template instances, but 2.064 will bring improvements here (DMD: FuncDeclaration::toObjFile). Barring such issues, the change shoud also marginally improve compile times because of declarations no longer being emitted when they are not needed. In the future, we should also consider refactoring the code so that it no longer directly accesses Dsymbol::ir but uses wrapper functions that ensure that the appropriate DtoResolve…() function has been called. GitHub: Fixes #454.
This commit is contained in:
103
gen/tollvm.cpp
103
gen/tollvm.cpp
@@ -294,22 +294,17 @@ LLValue* DtoDelegateEquals(TOK op, LLValue* lhs, LLValue* rhs)
|
||||
|
||||
LLGlobalValue::LinkageTypes DtoLinkage(Dsymbol* sym)
|
||||
{
|
||||
const bool mustDefine = mustDefineSymbol(sym);
|
||||
|
||||
// global/static variable
|
||||
if (VarDeclaration* vd = sym->isVarDeclaration())
|
||||
{
|
||||
if (mustDefine)
|
||||
{
|
||||
IF_LOG Logger::println("Variable %savailable externally: %s",
|
||||
(vd->availableExternally ? "" : "not "), vd->toChars());
|
||||
}
|
||||
IF_LOG Logger::println("Variable %savailable externally: %s",
|
||||
(vd->availableExternally ? "" : "not "), vd->toChars());
|
||||
|
||||
// generated by inlining semantics run
|
||||
if (vd->availableExternally && mustDefine)
|
||||
if (vd->availableExternally)
|
||||
return llvm::GlobalValue::AvailableExternallyLinkage;
|
||||
// template
|
||||
if (needsTemplateLinkage(sym))
|
||||
if (DtoIsTemplateInstance(sym))
|
||||
return templateLinkage;
|
||||
|
||||
// Currently, we have to consider all variables, even function-local
|
||||
@@ -330,11 +325,8 @@ LLGlobalValue::LinkageTypes DtoLinkage(Dsymbol* sym)
|
||||
}
|
||||
else if (FuncDeclaration* fdecl = sym->isFuncDeclaration())
|
||||
{
|
||||
if (mustDefine)
|
||||
{
|
||||
IF_LOG Logger::println("Function %savailable externally: %s",
|
||||
(fdecl->availableExternally ? "" : "not "), fdecl->toChars());
|
||||
}
|
||||
IF_LOG Logger::println("Function %savailable externally: %s",
|
||||
(fdecl->availableExternally ? "" : "not "), fdecl->toChars());
|
||||
|
||||
assert(fdecl->type->ty == Tfunction);
|
||||
TypeFunction* ft = static_cast<TypeFunction*>(fdecl->type);
|
||||
@@ -347,7 +339,7 @@ LLGlobalValue::LinkageTypes DtoLinkage(Dsymbol* sym)
|
||||
// available_externally. Naked functions are turned into module-level
|
||||
// inline asm and are thus declaration-only as far as the LLVM IR level
|
||||
// is concerned.
|
||||
if (fdecl->availableExternally && mustDefine && !fdecl->naked)
|
||||
if (fdecl->availableExternally && !fdecl->naked)
|
||||
return llvm::GlobalValue::AvailableExternallyLinkage;
|
||||
|
||||
// array operations are always template linkage
|
||||
@@ -357,7 +349,7 @@ LLGlobalValue::LinkageTypes DtoLinkage(Dsymbol* sym)
|
||||
// template instances should have weak linkage
|
||||
// but only if there's a body, and it's not naked
|
||||
// otherwise we make it external
|
||||
if (needsTemplateLinkage(fdecl) && fdecl->fbody && !fdecl->naked)
|
||||
if (DtoIsTemplateInstance(fdecl) && fdecl->fbody && !fdecl->naked)
|
||||
return templateLinkage;
|
||||
|
||||
// extern(C) functions are always external
|
||||
@@ -375,16 +367,14 @@ LLGlobalValue::LinkageTypes DtoLinkage(Dsymbol* sym)
|
||||
// class
|
||||
else if (ClassDeclaration* cd = sym->isClassDeclaration())
|
||||
{
|
||||
if (mustDefine)
|
||||
{
|
||||
IF_LOG Logger::println("Class %savailable externally: %s",
|
||||
(cd->availableExternally ? "" : "not "), vd->toChars());
|
||||
}
|
||||
IF_LOG Logger::println("Class %savailable externally: %s",
|
||||
(cd->availableExternally ? "" : "not "), vd->toChars());
|
||||
|
||||
// generated by inlining semantics run
|
||||
if (cd->availableExternally && mustDefine)
|
||||
if (cd->availableExternally)
|
||||
return llvm::GlobalValue::AvailableExternallyLinkage;
|
||||
// template
|
||||
if (needsTemplateLinkage(cd))
|
||||
if (DtoIsTemplateInstance(cd))
|
||||
return templateLinkage;
|
||||
}
|
||||
else
|
||||
@@ -392,10 +382,8 @@ LLGlobalValue::LinkageTypes DtoLinkage(Dsymbol* sym)
|
||||
llvm_unreachable("not global/function");
|
||||
}
|
||||
|
||||
// If the function needs to be defined in the current module, check if it
|
||||
// is a nested function and we can declare it as internal.
|
||||
bool canInternalize = mustDefine;
|
||||
|
||||
// Check if sym is a nested function and we can declare it as internal.
|
||||
//
|
||||
// Nested naked functions and the implicitly generated __require/__ensure
|
||||
// functions for in/out contracts cannot be internalized. The reason
|
||||
// for the latter is that contract functions, despite being nested, can be
|
||||
@@ -403,39 +391,28 @@ LLGlobalValue::LinkageTypes DtoLinkage(Dsymbol* sym)
|
||||
// interface methods (where __require/__ensure are emitted to the module
|
||||
// where the interface is declared, but an actual interface implementation
|
||||
// can be in a completely different place).
|
||||
if (canInternalize)
|
||||
{
|
||||
if (FuncDeclaration* fd = sym->isFuncDeclaration())
|
||||
{
|
||||
if ((fd->naked != 0) ||
|
||||
(fd->ident == Id::require) || (fd->ident == Id::ensure))
|
||||
{
|
||||
canInternalize = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Any symbol nested in a function that cannot be inlined can't be
|
||||
// referenced directly from outside that function, so we can give
|
||||
// such symbols internal linkage. This holds even if nested indirectly,
|
||||
// such as member functions of aggregates nested in functions.
|
||||
//
|
||||
// Note: This must be checked after things like template member-ness or
|
||||
// symbols nested in templates would get duplicated for each module,
|
||||
// breaking things like
|
||||
// ---
|
||||
// int counter(T)() { static int i; return i++; }"
|
||||
// ---
|
||||
// if instances get emitted in multiple object files because they'd use
|
||||
// different instances of 'i'.
|
||||
// TODO: Check if we are giving away too much inlining potential due to
|
||||
// canInline being overly conservative here.
|
||||
if (canInternalize)
|
||||
FuncDeclaration* fd = sym->isFuncDeclaration();
|
||||
if (!fd || (!fd->naked && fd->ident != Id::require && fd->ident != Id::ensure))
|
||||
{
|
||||
// Any symbol nested in a function that cannot be inlined can't be
|
||||
// referenced directly from outside that function, so we can give
|
||||
// such symbols internal linkage. This holds even if nested indirectly,
|
||||
// such as member functions of aggregates nested in functions.
|
||||
//
|
||||
// Note: This must be checked after things like template member-ness or
|
||||
// symbols nested in templates would get duplicated for each module,
|
||||
// breaking things like
|
||||
// ---
|
||||
// int counter(T)() { static int i; return i++; }"
|
||||
// ---
|
||||
// if instances get emitted in multiple object files because they'd use
|
||||
// different instances of 'i'.
|
||||
// TODO: Check if we are giving away too much inlining potential due to
|
||||
// canInline being overly conservative here.
|
||||
for (Dsymbol* parent = sym->parent; parent ; parent = parent->parent)
|
||||
{
|
||||
FuncDeclaration *fd = parent->isFuncDeclaration();
|
||||
if (fd && !fd->canInline(fd->needThis(), false, false))
|
||||
FuncDeclaration *parentFd = parent->isFuncDeclaration();
|
||||
if (parentFd && !parentFd->canInline(parentFd->needThis(), false, false))
|
||||
{
|
||||
// We also cannot internalize nested functions which are
|
||||
// leaked to the outside via a templated return type, because
|
||||
@@ -444,8 +421,8 @@ LLGlobalValue::LinkageTypes DtoLinkage(Dsymbol* sym)
|
||||
// Since we can't easily determine if this is really the case
|
||||
// here, just don't internalize it if the parent returns a
|
||||
// template at all, to be safe.
|
||||
TypeFunction* tf = static_cast<TypeFunction*>(fd->type);
|
||||
if (!DtoIsTemplateInstance(tf->next->toDsymbol(fd->scope)))
|
||||
TypeFunction* tf = static_cast<TypeFunction*>(parentFd->type);
|
||||
if (!DtoIsTemplateInstance(tf->next->toDsymbol(parentFd->scope)))
|
||||
return llvm::GlobalValue::InternalLinkage;
|
||||
}
|
||||
}
|
||||
@@ -468,8 +445,8 @@ static bool isAvailableExternally(Dsymbol* sym)
|
||||
|
||||
llvm::GlobalValue::LinkageTypes DtoInternalLinkage(Dsymbol* sym)
|
||||
{
|
||||
if (needsTemplateLinkage(sym)) {
|
||||
if (isAvailableExternally(sym) && mustDefineSymbol(sym))
|
||||
if (DtoIsTemplateInstance(sym)) {
|
||||
if (isAvailableExternally(sym))
|
||||
return llvm::GlobalValue::AvailableExternallyLinkage;
|
||||
return templateLinkage;
|
||||
}
|
||||
@@ -479,9 +456,9 @@ llvm::GlobalValue::LinkageTypes DtoInternalLinkage(Dsymbol* sym)
|
||||
|
||||
llvm::GlobalValue::LinkageTypes DtoExternalLinkage(Dsymbol* sym)
|
||||
{
|
||||
if (needsTemplateLinkage(sym))
|
||||
if (DtoIsTemplateInstance(sym))
|
||||
return templateLinkage;
|
||||
else if (isAvailableExternally(sym) && mustDefineSymbol(sym))
|
||||
else if (isAvailableExternally(sym))
|
||||
return llvm::GlobalValue::AvailableExternallyLinkage;
|
||||
else
|
||||
return llvm::GlobalValue::ExternalLinkage;
|
||||
|
||||
Reference in New Issue
Block a user