mirror of
https://github.com/xomboverlord/ldc.git
synced 2026-03-02 02:23:13 +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:
@@ -1005,8 +1005,86 @@ void DtoResolveDsymbol(Dsymbol* dsym)
|
||||
else if (TypeInfoDeclaration* fd = dsym->isTypeInfoDeclaration()) {
|
||||
DtoResolveTypeInfo(fd);
|
||||
}
|
||||
else {
|
||||
llvm_unreachable("Unsupported DSymbol for DtoResolveDsymbol.");
|
||||
else if (VarDeclaration* vd = dsym->isVarDeclaration()) {
|
||||
DtoResolveVariable(vd);
|
||||
}
|
||||
}
|
||||
|
||||
void DtoResolveVariable(VarDeclaration* vd)
|
||||
{
|
||||
if (vd->isTypeInfoDeclaration())
|
||||
return DtoResolveTypeInfo(static_cast<TypeInfoDeclaration *>(vd));
|
||||
|
||||
IF_LOG Logger::println("DtoResolveVariable(%s)", vd->toPrettyChars());
|
||||
LOG_SCOPE;
|
||||
|
||||
// just forward aliases
|
||||
// TODO: Is this required here or is the check in VarDeclaration::codegen
|
||||
// sufficient?
|
||||
if (vd->aliassym)
|
||||
{
|
||||
Logger::println("alias sym");
|
||||
DtoResolveDsymbol(vd->aliassym);
|
||||
return;
|
||||
}
|
||||
|
||||
if (AggregateDeclaration* ad = vd->isMember())
|
||||
DtoResolveDsymbol(ad);
|
||||
|
||||
// global variable
|
||||
if (vd->isDataseg() || (vd->storage_class & (STCconst | STCimmutable) && vd->init))
|
||||
{
|
||||
Logger::println("data segment");
|
||||
|
||||
#if 0 // TODO:
|
||||
assert(!(storage_class & STCmanifest) &&
|
||||
"manifest constant being codegen'd!");
|
||||
#endif
|
||||
|
||||
// don't duplicate work
|
||||
if (vd->ir.resolved) return;
|
||||
vd->ir.resolved = true;
|
||||
vd->ir.declared = true;
|
||||
|
||||
vd->ir.irGlobal = new IrGlobal(vd);
|
||||
|
||||
IF_LOG {
|
||||
if (vd->parent)
|
||||
Logger::println("parent: %s (%s)", vd->parent->toChars(), vd->parent->kind());
|
||||
else
|
||||
Logger::println("parent: null");
|
||||
}
|
||||
|
||||
const bool isLLConst = vd->isConst() && vd->init;
|
||||
|
||||
assert(!vd->ir.initialized);
|
||||
vd->ir.initialized = gIR->dmodule;
|
||||
std::string llName(vd->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
|
||||
// vd->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.
|
||||
//
|
||||
// We always start out with external linkage; any other type is set
|
||||
// when actually defining it in VarDeclaration::codegen.
|
||||
llvm::GlobalVariable* gvar = getOrCreateGlobal(vd->loc, *gIR->module,
|
||||
i1ToI8(DtoType(vd->type)), isLLConst, llvm::GlobalValue::ExternalLinkage,
|
||||
0, llName, vd->isThreadlocal());
|
||||
vd->ir.irGlobal->value = gvar;
|
||||
|
||||
// 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 (vd->alignment != STRUCTALIGN_DEFAULT)
|
||||
gvar->setAlignment(vd->alignment);
|
||||
|
||||
if (Logger::enabled())
|
||||
Logger::cout() << *gvar << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1293,7 +1371,7 @@ LLConstant* DtoConstInitializer(Loc loc, Type* type, Initializer* init)
|
||||
else if (StructInitializer* si = init->isStructInitializer())
|
||||
{
|
||||
Logger::println("const struct initializer");
|
||||
si->ad->codegen(Type::sir);
|
||||
DtoResolveDsymbol(si->ad);
|
||||
return si->ad->ir.irAggr->createStructInitializer(si);
|
||||
}
|
||||
else if (ArrayInitializer* ai = init->isArrayInitializer())
|
||||
@@ -1502,98 +1580,6 @@ void DtoOverloadedIntrinsicName(TemplateInstance* ti, TemplateDeclaration* td, s
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool mustDefineSymbol(Dsymbol* s)
|
||||
{
|
||||
if (FuncDeclaration* fd = s->isFuncDeclaration())
|
||||
{
|
||||
// we can't (and probably shouldn't?) define functions
|
||||
// that weren't semantic3'ed
|
||||
if (fd->semanticRun < PASSsemantic3)
|
||||
return false;
|
||||
|
||||
// If a function has no body, we cannot possibly emit it (and so it
|
||||
// cannot be available_externally either).
|
||||
if (!fd->fbody)
|
||||
return false;
|
||||
|
||||
if (fd->isArrayOp == 1)
|
||||
return true;
|
||||
|
||||
if (global.inExtraInliningSemantic && fd->availableExternally) {
|
||||
// Emit extra functions if we're inlining.
|
||||
// These will get available_externally linkage,
|
||||
// so they shouldn't end up in object code.
|
||||
|
||||
assert(fd->type->ty == Tfunction);
|
||||
// * If we define extra static constructors, static destructors
|
||||
// and unittests they'll get registered to run, and we won't
|
||||
// be calling them directly anyway.
|
||||
// * If it's a large function, don't emit it unnecessarily.
|
||||
// Use DMD's canInline() to determine whether it's large.
|
||||
// inlineCost() members have been changed to pay less attention
|
||||
// to DMDs limitations, but still have some issues. The most glaring
|
||||
// offenders are any kind of control flow statements other than
|
||||
// 'if' and 'return'.
|
||||
if ( !fd->isStaticCtorDeclaration()
|
||||
&& !fd->isStaticDtorDeclaration()
|
||||
&& !fd->isUnitTestDeclaration()
|
||||
&& fd->canInline(true, false, false))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// This was only semantic'ed for inlining checks.
|
||||
// We won't be inlining this, so we only need to emit a declaration.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (VarDeclaration* vd = s->isVarDeclaration())
|
||||
{
|
||||
// Never define 'extern' variables.
|
||||
if (vd->storage_class & STCextern)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Inlining checks may create some variable and class declarations
|
||||
// we don't need to emit.
|
||||
if (global.inExtraInliningSemantic)
|
||||
{
|
||||
if (VarDeclaration* vd = s->isVarDeclaration())
|
||||
if (vd->availableExternally)
|
||||
return false;
|
||||
|
||||
if (ClassDeclaration* cd = s->isClassDeclaration())
|
||||
if (cd->availableExternally)
|
||||
return false;
|
||||
}
|
||||
|
||||
TemplateInstance* tinst = DtoIsTemplateInstance(s, true);
|
||||
if (tinst)
|
||||
{
|
||||
if (!global.params.singleObj)
|
||||
return true;
|
||||
|
||||
if (!tinst->emittedInModule)
|
||||
{
|
||||
gIR->seenTemplateInstances.insert(tinst);
|
||||
tinst->emittedInModule = gIR->dmodule;
|
||||
}
|
||||
return tinst->emittedInModule == gIR->dmodule;
|
||||
}
|
||||
|
||||
return s->getModule() == gIR->dmodule;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool needsTemplateLinkage(Dsymbol* s)
|
||||
{
|
||||
return DtoIsTemplateInstance(s) && mustDefineSymbol(s);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool hasUnalignedFields(Type* t)
|
||||
{
|
||||
t = t->toBasetype();
|
||||
@@ -1734,7 +1720,7 @@ void callPostblit(Loc &loc, Expression *exp, LLValue *val)
|
||||
FuncDeclaration *fd = sd->postblit;
|
||||
if (fd->storage_class & STCdisable)
|
||||
fd->toParent()->error(loc, "is not copyable because it is annotated with @disable");
|
||||
fd->codegen(Type::sir);
|
||||
DtoResolveFunction(fd);
|
||||
Expressions args;
|
||||
DFuncValue dfn(fd, fd->ir.irFunc->func, val);
|
||||
DtoCallFunction(loc, Type::basic[Tvoid], &dfn, &args);
|
||||
@@ -1867,14 +1853,14 @@ DValue* DtoSymbolAddress(const Loc& loc, Type* type, Declaration* decl)
|
||||
else if (ClassInfoDeclaration* cid = vd->isClassInfoDeclaration())
|
||||
{
|
||||
Logger::println("ClassInfoDeclaration: %s", cid->cd->toChars());
|
||||
cid->cd->codegen(Type::sir);;
|
||||
DtoResolveClass(cid->cd);
|
||||
return new DVarValue(type, vd, cid->cd->ir.irAggr->getClassInfoSymbol());
|
||||
}
|
||||
// typeinfo
|
||||
else if (TypeInfoDeclaration* tid = vd->isTypeInfoDeclaration())
|
||||
{
|
||||
Logger::println("TypeInfoDeclaration");
|
||||
tid->codegen(Type::sir);
|
||||
DtoResolveTypeInfo(tid);
|
||||
assert(tid->ir.getIrValue());
|
||||
LLType* vartype = DtoType(type);
|
||||
LLValue* m = tid->ir.getIrValue();
|
||||
@@ -1923,7 +1909,7 @@ DValue* DtoSymbolAddress(const Loc& loc, Type* type, Declaration* decl)
|
||||
// take care of forward references of global variables
|
||||
const bool isGlobal = vd->isDataseg() || (vd->storage_class & STCextern);
|
||||
if (isGlobal)
|
||||
vd->codegen(Type::sir);
|
||||
DtoResolveDsymbol(vd);
|
||||
|
||||
assert(vd->ir.isSet() && "Variable not resolved.");
|
||||
|
||||
@@ -1952,19 +1938,15 @@ DValue* DtoSymbolAddress(const Loc& loc, Type* type, Declaration* decl)
|
||||
if (FuncDeclaration* fdecl = decl->isFuncDeclaration())
|
||||
{
|
||||
Logger::println("FuncDeclaration");
|
||||
LLValue* func = 0;
|
||||
fdecl = fdecl->toAliasFunc();
|
||||
if (fdecl->llvmInternal == LLVMinline_asm)
|
||||
{
|
||||
// TODO: Is this needed? If so, what about other intrinsics?
|
||||
error("special ldc inline asm is not a normal function");
|
||||
fatal();
|
||||
}
|
||||
else if (fdecl->llvmInternal != LLVMva_arg)
|
||||
{
|
||||
fdecl->codegen(Type::sir);
|
||||
func = fdecl->ir.irFunc->func;
|
||||
}
|
||||
return new DFuncValue(fdecl, func);
|
||||
DtoResolveFunction(fdecl);
|
||||
return new DFuncValue(fdecl, fdecl->ir.irFunc->func);
|
||||
}
|
||||
|
||||
if (SymbolDeclaration* sdecl = decl->isSymbolDeclaration())
|
||||
@@ -1975,7 +1957,7 @@ DValue* DtoSymbolAddress(const Loc& loc, Type* type, Declaration* decl)
|
||||
assert(sdecltype->ty == Tstruct);
|
||||
TypeStruct* ts = static_cast<TypeStruct*>(sdecltype);
|
||||
assert(ts->sym);
|
||||
ts->sym->codegen(Type::sir);
|
||||
DtoResolveStruct(ts->sym);
|
||||
|
||||
LLValue* initsym = ts->sym->ir.irAggr->getInitSymbol();
|
||||
initsym = DtoBitCast(initsym, DtoType(ts->pointerTo()));
|
||||
@@ -2011,7 +1993,7 @@ llvm::Constant* DtoConstSymbolAddress(const Loc& loc, Declaration* decl)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
vd->codegen(Type::sir);
|
||||
DtoResolveVariable(vd);
|
||||
LLConstant* llc = llvm::dyn_cast<LLConstant>(vd->ir.getIrValue());
|
||||
assert(llc);
|
||||
return llc;
|
||||
@@ -2019,7 +2001,7 @@ llvm::Constant* DtoConstSymbolAddress(const Loc& loc, Declaration* decl)
|
||||
// static function
|
||||
else if (FuncDeclaration* fd = decl->isFuncDeclaration())
|
||||
{
|
||||
fd->codegen(Type::sir);
|
||||
DtoResolveFunction(fd);
|
||||
IrFunction* irfunc = fd->ir.irFunc;
|
||||
return irfunc->func;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user