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:
David Nadlinger
2013-10-13 04:31:41 +02:00
parent b556ad9996
commit 787c147986
22 changed files with 439 additions and 517 deletions

View File

@@ -14,11 +14,14 @@
#include "init.h"
#include "rmem.h"
#include "template.h"
#include "gen/classes.h"
#include "gen/functions.h"
#include "gen/irstate.h"
#include "gen/llvm.h"
#include "gen/llvmhelpers.h"
#include "gen/logger.h"
#include "gen/tollvm.h"
#include "gen/utils.h"
#include "ir/ir.h"
#include "ir/irtype.h"
#include "ir/irvar.h"
@@ -27,53 +30,129 @@
void Dsymbol::codegen(Ir*)
{
Logger::println("Ignoring Dsymbol::codegen for %s", toPrettyChars());
IF_LOG Logger::println("Ignoring Dsymbol::codegen for %s", toPrettyChars());
}
/* ================================================================== */
void InterfaceDeclaration::codegen(Ir*)
void InterfaceDeclaration::codegen(Ir* p)
{
IF_LOG Logger::println("InterfaceDeclaration::codegen: '%s'", toPrettyChars());
LOG_SCOPE
if (ir.defined) return;
ir.defined = true;
if (type->ty == Terror)
{ error("had semantic errors when compiling");
return;
}
if (members && symtab)
{
DtoResolveDsymbol(this);
// Emit any members (e.g. final functions).
for (ArrayIter<Dsymbol> it(members); !it.done(); it.next())
{
it->codegen(p);
}
// Emit TypeInfo.
DtoTypeInfoOf(type);
// Define __InterfaceZ.
llvm::GlobalVariable *interfaceZ = ir.irAggr->getClassInfoSymbol();
interfaceZ->setInitializer(ir.irAggr->getClassInfoInit());
interfaceZ->setLinkage(DtoExternalLinkage(this));
}
}
/* ================================================================== */
void StructDeclaration::codegen(Ir*)
void StructDeclaration::codegen(Ir* p)
{
IF_LOG Logger::println("StructDeclaration::codegen: '%s'", toPrettyChars());
LOG_SCOPE
if (ir.defined) return;
ir.defined = true;
if (type->ty == Terror)
{ error("had semantic errors when compiling");
return;
}
if (members && symtab)
DtoResolveDsymbol(this);
{
DtoResolveStruct(this);
for (ArrayIter<Dsymbol> it(members); !it.done(); it.next())
{
it->codegen(p);
}
// Define the __initZ symbol.
llvm::GlobalVariable *initZ = ir.irAggr->getInitSymbol();
initZ->setInitializer(ir.irAggr->getDefaultInit());
initZ->setLinkage(DtoExternalLinkage(this));
// emit typeinfo
DtoTypeInfoOf(type);
}
}
/* ================================================================== */
void ClassDeclaration::codegen(Ir*)
void ClassDeclaration::codegen(Ir* p)
{
IF_LOG Logger::println("ClassDeclaration::codegen: '%s'", toPrettyChars());
LOG_SCOPE
if (ir.defined) return;
ir.defined = true;
if (type->ty == Terror)
{ error("had semantic errors when compiling");
return;
}
if (members && symtab)
DtoResolveDsymbol(this);
{
DtoResolveClass(this);
for (ArrayIter<Dsymbol> it(members); !it.done(); it.next())
{
it->codegen(p);
}
llvm::GlobalValue::LinkageTypes const linkage = DtoExternalLinkage(this);
llvm::GlobalVariable *initZ = ir.irAggr->getInitSymbol();
initZ->setInitializer(ir.irAggr->getDefaultInit());
initZ->setLinkage(linkage);
llvm::GlobalVariable *vtbl = ir.irAggr->getVtblSymbol();
vtbl->setInitializer(ir.irAggr->getVtblInit());
vtbl->setLinkage(linkage);
llvm::GlobalVariable *classZ = ir.irAggr->getClassInfoSymbol();
classZ->setInitializer(ir.irAggr->getClassInfoInit());
classZ->setLinkage(linkage);
// No need to do TypeInfo here, it is <name>__classZ for classes in D2.
}
}
/* ================================================================== */
void TupleDeclaration::codegen(Ir* p)
{
Logger::println("TupleDeclaration::codegen(): %s", toChars());
IF_LOG Logger::println("TupleDeclaration::codegen(): '%s'", toPrettyChars());
LOG_SCOPE
if (ir.defined) return;
ir.defined = true;
assert(isexp);
assert(objects);
@@ -92,14 +171,19 @@ void TupleDeclaration::codegen(Ir* p)
void VarDeclaration::codegen(Ir* p)
{
Logger::print("VarDeclaration::codegen(): %s | %s\n", toChars(), type->toChars());
IF_LOG Logger::println("VarDeclaration::codegen(): '%s'", toPrettyChars());
LOG_SCOPE;
if (ir.defined) return;
ir.defined = true;
if (type->ty == Terror)
{ error("had semantic errors when compiling");
return;
}
DtoResolveVariable(this);
// just forward aliases
if (aliassym)
{
@@ -108,10 +192,6 @@ void VarDeclaration::codegen(Ir* p)
return;
}
// output the parent aggregate first
if (AggregateDeclaration* ad = isMember())
ad->codegen(p);
// global variable
if (isDataseg() || (storage_class & (STCconst | STCimmutable) && init))
{
@@ -122,37 +202,14 @@ void VarDeclaration::codegen(Ir* p)
"manifest constant being codegen'd!");
#endif
// don't duplicate work
if (this->ir.resolved) return;
this->ir.resolved = true;
this->ir.declared = true;
llvm::GlobalVariable *gvar = llvm::cast<llvm::GlobalVariable>(
this->ir.irGlobal->value);
assert(gvar && "DtoResolveVariable should have created value");
this->ir.irGlobal = new IrGlobal(this);
Logger::println("parent: %s (%s)", parent->toChars(), parent->kind());
const bool isLLConst = isConst() && init;
const llvm::GlobalValue::LinkageTypes llLinkage = DtoLinkage(this);
assert(!ir.initialized);
ir.initialized = gIR->dmodule;
std::string llName(mangle());
// Since the type of a global must exactly match the type of its
// initializer, we cannot know the type until after we have emitted the
// latter (e.g. in case of unions, …). However, it is legal for the
// initializer to refer to the address of the variable. Thus, we first
// create a global with the generic type (note the assignment to
// this->ir.irGlobal->value!), and in case we also do an initializer
// with a different type later, swap it out and replace any existing
// uses with bitcasts to the previous type.
llvm::GlobalVariable* gvar = getOrCreateGlobal(loc, *gIR->module,
i1ToI8(DtoType(type)), isLLConst, llLinkage, 0, llName,
isThreadlocal());
this->ir.irGlobal->value = gvar;
// Check if we are defining or just declaring the global in this module.
if (!(storage_class & STCextern) && mustDefineSymbol(this))
if (!(storage_class & STCextern))
{
// Build the initializer. Might use this->ir.irGlobal->value!
LLConstant *initVal = DtoConstInitializer(loc, type, init);
@@ -161,10 +218,12 @@ void VarDeclaration::codegen(Ir* p)
if (initVal->getType() != gvar->getType()->getElementType())
{
llvm::GlobalVariable* newGvar = getOrCreateGlobal(loc,
*gIR->module, initVal->getType(), isLLConst, llLinkage, 0,
*gIR->module, initVal->getType(), gvar->isConstant(),
llLinkage, 0,
"", // We take on the name of the old global below.
isThreadlocal());
gvar->isThreadLocal());
newGvar->setAlignment(gvar->getAlignment());
newGvar->takeName(gvar);
llvm::Constant* newValue =
@@ -180,17 +239,12 @@ void VarDeclaration::codegen(Ir* p)
assert(!ir.irGlobal->constInit);
ir.irGlobal->constInit = initVal;
gvar->setInitializer(initVal);
gvar->setLinkage(llLinkage);
// Also set up the edbug info.
gIR->DBuilder.EmitGlobalVariable(gvar, this);
}
// Set the alignment (it is important not to use type->alignsize because
// VarDeclarations can have an align() attribute independent of the type
// as well).
if (alignment != STRUCTALIGN_DEFAULT)
gvar->setAlignment(alignment);
// If this global is used from a naked function, we need to create an
// artificial "use" for it, or it could be removed by the optimizer if
// the only reference to it is in inline asm.
@@ -206,9 +260,12 @@ void VarDeclaration::codegen(Ir* p)
void TypedefDeclaration::codegen(Ir*)
{
Logger::print("TypedefDeclaration::codegen: %s\n", toChars());
IF_LOG Logger::println("TypedefDeclaration::codegen: '%s'", toPrettyChars());
LOG_SCOPE;
if (ir.defined) return;
ir.defined = true;
if (type->ty == Terror)
{ error("had semantic errors when compiling");
return;
@@ -222,7 +279,7 @@ void TypedefDeclaration::codegen(Ir*)
void EnumDeclaration::codegen(Ir*)
{
Logger::println("Ignoring EnumDeclaration::codegen for %s", toChars());
IF_LOG Logger::println("Ignoring EnumDeclaration::codegen: '%s'", toPrettyChars());
if (type->ty == Terror)
{ error("had semantic errors when compiling");
@@ -237,7 +294,7 @@ void FuncDeclaration::codegen(Ir* p)
// don't touch function aliases, they don't contribute any new symbols
if (!isFuncAliasDeclaration())
{
DtoResolveDsymbol(this);
DtoDefineFunction(this);
}
}
@@ -245,18 +302,17 @@ void FuncDeclaration::codegen(Ir* p)
void TemplateInstance::codegen(Ir* p)
{
#if LOG
printf("TemplateInstance::codegen('%s', this = %p)\n", toChars(), this);
#endif
if (ignore)
return;
IF_LOG Logger::println("TemplateInstance::codegen: '%s'", toPrettyChars());
LOG_SCOPE
if (ir.defined) return;
ir.defined = true;
if (!errors && members)
{
for (unsigned i = 0; i < members->dim; i++)
{
Dsymbol *s = static_cast<Dsymbol *>(members->data[i]);
s->codegen(p);
(*members)[i]->codegen(p);
}
}
}
@@ -265,14 +321,17 @@ void TemplateInstance::codegen(Ir* p)
void TemplateMixin::codegen(Ir* p)
{
IF_LOG Logger::println("TemplateInstance::codegen: '%s'", toPrettyChars());
LOG_SCOPE
if (ir.defined) return;
ir.defined = true;
if (!errors && members)
{
for (unsigned i = 0; i < members->dim; i++)
{
Dsymbol *s = static_cast<Dsymbol *>(members->data[i]);
if (s->isVarDeclaration())
continue;
s->codegen(p);
(*members)[i]->codegen(p);
}
}
}