Run semantic3 on imported modules, and emit new symbols with

`available_externally` linkage. This allows the inliner to inline functions from
other modules while telling the code generator to ignore those functions (treat
them as declarations)
Still generates a few extra `TypeInfo`s and strings...
Disabled when generating debug info because I don't really understand it, and it
doesn't like this.
This commit is contained in:
Frits van Bommel
2009-06-07 16:00:13 +02:00
parent b972dcb92d
commit 96fdcd6347
15 changed files with 201 additions and 9 deletions

View File

@@ -106,6 +106,11 @@ struct AggregateDeclaration : ScopeDsymbol
#endif
AggregateDeclaration *isAggregateDeclaration() { return this; }
#if IN_LLVM
// Aggregates that wouldn't have gotten semantic3'ed if we weren't inlining set this flag.
bool availableExternally;
#endif
};
struct AnonymousAggregateDeclaration : AggregateDeclaration

View File

@@ -635,6 +635,8 @@ VarDeclaration::VarDeclaration(Loc loc, Type *type, Identifier *id, Initializer
offset2 = 0;
nakedUse = false;
availableExternally = true; // assume this unless proven otherwise
#endif
}
@@ -1091,6 +1093,16 @@ void VarDeclaration::semantic2(Scope *sc)
}
}
void VarDeclaration::semantic3(Scope *sc)
{
// LDC
if (!global.params.useAvailableExternally)
availableExternally = false;
// Preserve call chain
Declaration::semantic3(sc);
}
const char *VarDeclaration::kind()
{
return "variable";
@@ -1296,6 +1308,9 @@ Dsymbol *TypeInfoDeclaration::syntaxCopy(Dsymbol *s)
void TypeInfoDeclaration::semantic(Scope *sc)
{
assert(linkage == LINKc);
// LDC
if (!global.params.useAvailableExternally)
availableExternally = false;
}
/***************************** TypeInfoConstDeclaration **********************/

View File

@@ -300,6 +300,11 @@ struct VarDeclaration : Declaration
/// Set during type generation.
unsigned aggrIndex;
/// Variables that wouldn't have gotten semantic3'ed if we weren't inlining set this flag.
bool availableExternally;
/// Override added to set above flag.
void semantic3(Scope *sc);
// FIXME: we're not using these anymore!
AnonDeclaration* anonDecl;
unsigned offset2;
@@ -754,6 +759,9 @@ struct FuncDeclaration : Declaration
// if this is an array operation it gets a little special attention
bool isArrayOp;
// Functions that wouldn't have gotten semantic3'ed if we weren't inlining set this flag.
bool availableExternally;
// true if overridden with the pragma(allow_inline); stmt
bool allowInlining;
#endif

View File

@@ -84,6 +84,8 @@ FuncDeclaration::FuncDeclaration(Loc loc, Loc endloc, Identifier *id, enum STC s
isArrayOp = false;
allowInlining = false;
availableExternally = true; // assume this unless proven otherwise
// function types in ldc don't merge if the context parameter differs
// so we actually don't care about the function declaration, but only
// what kind of context parameter it has.
@@ -674,6 +676,10 @@ void FuncDeclaration::semantic3(Scope *sc)
return;
semanticRun = 3;
// LDC
if (!global.params.useAvailableExternally)
availableExternally = false;
if (!type || type->ty != Tfunction)
return;
f = (TypeFunction *)(type);

View File

@@ -83,14 +83,17 @@ int IfStatement::inlineCost(InlineCostState *ics)
{
int cost;
#if !IN_LLVM
/* Can't declare variables inside ?: expressions, so
* we cannot inline if a variable is declared.
*/
if (arg)
return COST_MAX;
#endif
cost = condition->inlineCost(ics);
#if !IN_LLVM
/* Specifically allow:
* if (condition)
* return exp1;
@@ -108,6 +111,7 @@ int IfStatement::inlineCost(InlineCostState *ics)
//printf("cost = %d\n", cost);
}
else
#endif
{
ics->nested += 1;
if (ifbody)
@@ -121,9 +125,11 @@ int IfStatement::inlineCost(InlineCostState *ics)
int ReturnStatement::inlineCost(InlineCostState *ics)
{
#if !IN_LLVM
// Can't handle return statements nested in if's
if (ics->nested)
return COST_MAX;
#endif
return exp ? exp->inlineCost(ics) : 0;
}
@@ -157,19 +163,23 @@ int VarExp::inlineCost(InlineCostState *ics)
int ThisExp::inlineCost(InlineCostState *ics)
{
#if !IN_LLVM
FuncDeclaration *fd = ics->fd;
if (!ics->hdrscan)
if (fd->isNested() || !ics->hasthis)
return COST_MAX;
#endif
return 1;
}
int SuperExp::inlineCost(InlineCostState *ics)
{
#if !IN_LLVM
FuncDeclaration *fd = ics->fd;
if (!ics->hdrscan)
if (fd->isNested() || !ics->hasthis)
return COST_MAX;
#endif
return 1;
}
@@ -195,12 +205,16 @@ int StructLiteralExp::inlineCost(InlineCostState *ics)
int FuncExp::inlineCost(InlineCostState *ics)
{
// This breaks on LDC too, since nested functions have internal linkage
// and thus can't be referenced from other objects.
// Right now, this makes the function be output to the .obj file twice.
return COST_MAX;
}
int DelegateExp::inlineCost(InlineCostState *ics)
{
// This breaks on LDC too, since nested functions have internal linkage
// and thus can't be referenced from other objects.
return COST_MAX;
}
@@ -229,6 +243,8 @@ int DeclarationExp::inlineCost(InlineCostState *ics)
return td->objects->dim;
#endif
}
// This breaks on LDC too, since nested static variables have internal
// linkage and thus can't be referenced from other objects.
if (!ics->hdrscan && vd->isDataseg())
return COST_MAX;
cost += 1;
@@ -246,6 +262,8 @@ int DeclarationExp::inlineCost(InlineCostState *ics)
}
// These can contain functions, which when copied, get output twice.
// These break on LDC too, since nested static variables and functions have
// internal linkage and thus can't be referenced from other objects.
if (declaration->isStructDeclaration() ||
declaration->isClassDeclaration() ||
declaration->isFuncDeclaration() ||
@@ -1269,6 +1287,10 @@ int FuncDeclaration::canInline(int hasthis, int hdrscan)
if (type)
{ assert(type->ty == Tfunction);
TypeFunction *tf = (TypeFunction *)(type);
#if IN_LLVM
// LDC: Only extern(C) varargs count.
if (tf->linkage != LINKd)
#endif
if (tf->varargs == 1) // no variadic parameter lists
goto Lno;
@@ -1280,12 +1302,15 @@ int FuncDeclaration::canInline(int hasthis, int hdrscan)
!hdrscan)
goto Lno;
}
#if !IN_LLVM
// LDC: Only extern(C) varargs count, and ctors use extern(D).
else
{ CtorDeclaration *ctor = isCtorDeclaration();
if (ctor && ctor->varargs == 1)
goto Lno;
}
#endif
if (
!fbody ||
@@ -1299,17 +1324,20 @@ int FuncDeclaration::canInline(int hasthis, int hdrscan)
#endif
isSynchronized() ||
isImportedSymbol() ||
#if !IN_LLVM
#if DMDV2
closureVars.dim || // no nested references to this frame
#else
nestedFrameRef || // no nested references to this frame
#endif
#endif // !IN_LLVM
(isVirtual() && !isFinal())
))
{
goto Lno;
}
#if !IN_LLVM
/* If any parameters are Tsarray's (which are passed by reference)
* or out parameters (also passed by reference), don't do inlining.
*/
@@ -1322,6 +1350,7 @@ int FuncDeclaration::canInline(int hasthis, int hdrscan)
goto Lno;
}
}
#endif
memset(&ics, 0, sizeof(ics));
ics.hasthis = hasthis;
@@ -1334,8 +1363,10 @@ int FuncDeclaration::canInline(int hasthis, int hdrscan)
if (cost >= COST_MAX)
goto Lno;
#if !IN_LLVM
if (!hdrscan) // Don't scan recursively for header content scan
inlineScan();
#endif
Lyes:
if (!hdrscan) // Don't modify inlineStatus for header content scan

View File

@@ -237,6 +237,7 @@ struct Param
bool llvmAnnotate;
bool useInlineAsm;
bool verbose_cg;
bool useAvailableExternally;
// target stuff
const char* llvmArch;

View File

@@ -52,6 +52,10 @@ AggregateDeclaration::AggregateDeclaration(Loc loc, Identifier *id)
ctor = NULL;
defaultCtor = NULL;
#endif
#if IN_LLVM
availableExternally = true; // assume this unless proven otherwise
#endif
}
enum PROT AggregateDeclaration::prot()
@@ -81,6 +85,10 @@ void AggregateDeclaration::semantic2(Scope *sc)
void AggregateDeclaration::semantic3(Scope *sc)
{ int i;
// LDC
if (!global.params.useAvailableExternally)
availableExternally = false;
//printf("AggregateDeclaration::semantic3(%s)\n", toChars());
if (members)
{

View File

@@ -491,7 +491,9 @@ LLValue* DtoIndexClass(LLValue* src, ClassDeclaration* cd, VarDeclaration* vd)
{
Logger::cout() << "src2: " << *src << '\n';
Logger::cout() << "index: " << field->index << '\n';
#if 0
Logger::cout() << "srctype: " << *src->getType() << '\n';
#endif
}
#endif
LLValue* val = DtoGEPi(src, 0, field->index);

View File

@@ -9,6 +9,5 @@ enum
LLVMva_copy,
LLVMva_end,
LLVMva_arg,
LLVMldc,
LLVMinline_asm
};

View File

@@ -1328,6 +1328,48 @@ bool mustDefineSymbol(Dsymbol* s)
if (fd->isArrayOp)
return true;
if (global.params.useAvailableExternally && 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);
TypeFunction* tf = (TypeFunction*) fd->type;
// * 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))
{
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;
}
}
// Inlining checks may create some variable and class declarations
// we don't need to emit.
if (global.params.useAvailableExternally)
{
if (VarDeclaration* vd = s->isVarDeclaration())
if (vd->availableExternally)
return false;
if (ClassDeclaration* cd = s->isClassDeclaration())
if (cd->availableExternally)
return false;
}
TemplateInstance* tinst = DtoIsTemplateInstance(s);

View File

@@ -35,6 +35,7 @@
#include "gen/logger.h"
#include "gen/linker.h"
#include "gen/irstate.h"
#include "gen/optimizer.h"
#include "gen/toobj.h"
#include "gen/metadata.h"
#include "gen/passes/Passes.h"
@@ -811,6 +812,17 @@ int main(int argc, char** argv)
* not be found at link time.
*/
if (!global.params.useArrayBounds && !global.params.useAssert)
#elif LLVM_REV >= 68940
// This doesn't play nice with debug info at the moment
if (!global.params.symdebug && willInline())
{
global.params.useAvailableExternally = true;
Logger::println("Running some extra semantic3's for inlining purposes");
#else
// IN_LLVM, but available_externally not available yet.
if (false)
{
#endif
{
// Do pass 3 semantic analysis on all imported modules,
// since otherwise functions in them cannot be inlined
@@ -825,6 +837,7 @@ int main(int argc, char** argv)
fatal();
}
#if !IN_LLVM
for (int i = 0; i < modules.dim; i++)
{
m = (Module *)modules.data[i];
@@ -832,10 +845,10 @@ int main(int argc, char** argv)
printf("inline scan %s\n", m->toChars());
m->inlineScan();
}
#endif
}
if (global.errors)
fatal();
#endif
// write module dependencies to file if requested
if (global.params.moduleDepsFile != NULL)

View File

@@ -12,6 +12,7 @@
#include "llvm/Support/PassNameParser.h"
#include "root.h" // error()
#include <cstring> // strcmp();
using namespace llvm;
@@ -78,6 +79,19 @@ bool doInline() {
|| (enableInlining == cl::BOU_UNSET && optimizeLevel >= 3);
}
// Determine whether the inliner will be run.
bool willInline() {
if (doInline())
return true;
// It may also have been specified explicitly on the command line as an explicit pass
typedef cl::list<const PassInfo*, bool, PassNameParser> PL;
for (PL::iterator I = passList.begin(), E = passList.end(); I != E; ++I) {
if (!std::strcmp((*I)->getPassArgument(), "inline"))
return true;
}
return false;
}
// Some extra accessors for the linker: (llvm-ld version only, currently unused?)
int optLevel() {
return optimizeLevel;
@@ -108,7 +122,6 @@ static void addPassesForOptLevel(PassManager& pm) {
else
addPass(pm, createScalarReplAggregatesPass());
addPass(pm, createGlobalOptimizerPass());
addPass(pm, createGlobalDCEPass());
}
// -O2
@@ -208,6 +221,10 @@ static void addPassesForOptLevel(PassManager& pm) {
addPass(pm, createConstantMergePass());
}
if (optimizeLevel >= 1) {
addPass(pm, createGlobalDCEPass());
}
// level -O4 and -O5 are linktime optimizations
}

View File

@@ -5,7 +5,10 @@ namespace llvm { class Module; }
bool ldc_optimize_module(llvm::Module* m);
// Determines whether the inliner will run in the -O<N> list of passes
bool doInline();
// Determines whether the inliner will be run at all.
bool willInline();
int optLevel();

View File

@@ -241,6 +241,13 @@ LLGlobalValue::LinkageTypes DtoLinkage(Dsymbol* sym)
// global variable
if (VarDeclaration* vd = sym->isVarDeclaration())
{
if (mustDefineSymbol(vd))
Logger::println("Variable %savailable externally: %s", (vd->availableExternally ? "" : "not "), vd->toChars());
#if LLVM_REV >= 68940
// generated by inlining semantics run
if (vd->availableExternally && mustDefineSymbol(sym))
return llvm::GlobalValue::AvailableExternallyLinkage;
#endif
// template
if (needsTemplateLinkage(sym))
return TEMPLATE_LINKAGE_TYPE;
@@ -248,15 +255,22 @@ LLGlobalValue::LinkageTypes DtoLinkage(Dsymbol* sym)
// function
else if (FuncDeclaration* fdecl = sym->isFuncDeclaration())
{
if (mustDefineSymbol(fdecl))
Logger::println("Function %savailable externally: %s", (fdecl->availableExternally ? "" : "not "), fdecl->toChars());
assert(fdecl->type->ty == Tfunction);
TypeFunction* ft = (TypeFunction*)fdecl->type;
// array operations are always template linkage
if (fdecl->isArrayOp)
return TEMPLATE_LINKAGE_TYPE;
// intrinsics are always external
if (fdecl->llvmInternal == LLVMintrinsic)
return llvm::GlobalValue::ExternalLinkage;
#if LLVM_REV >= 68940
// generated by inlining semantics run
if (fdecl->availableExternally && mustDefineSymbol(sym))
return llvm::GlobalValue::AvailableExternallyLinkage;
#endif
// array operations are always template linkage
if (fdecl->isArrayOp)
return TEMPLATE_LINKAGE_TYPE;
// template instances should have weak linkage
// but only if there's a body, and it's not naked
// otherwise we make it external
@@ -269,6 +283,13 @@ LLGlobalValue::LinkageTypes DtoLinkage(Dsymbol* sym)
// class
else if (ClassDeclaration* cd = sym->isClassDeclaration())
{
if (mustDefineSymbol(cd))
Logger::println("Class %savailable externally: %s", (cd->availableExternally ? "" : "not "), vd->toChars());
#if LLVM_REV >= 68940
// generated by inlining semantics run
if (cd->availableExternally && mustDefineSymbol(sym))
return llvm::GlobalValue::AvailableExternallyLinkage;
#endif
// template
if (needsTemplateLinkage(cd))
return TEMPLATE_LINKAGE_TYPE;
@@ -278,8 +299,8 @@ LLGlobalValue::LinkageTypes DtoLinkage(Dsymbol* sym)
assert(0 && "not global/function");
}
// The following breaks for nested naked functions, so check for that.
bool skipNestedCheck = false;
// The following breaks for nested naked functions and other declarations, so check for that.
bool skipNestedCheck = !mustDefineSymbol(sym);
if (FuncDeclaration* fd = sym->isFuncDeclaration())
skipNestedCheck = (fd->naked != 0);
@@ -306,16 +327,36 @@ LLGlobalValue::LinkageTypes DtoLinkage(Dsymbol* sym)
return llvm::GlobalValue::ExternalLinkage;
}
static bool isAvailableExternally(Dsymbol* sym)
{
if (VarDeclaration* vd = sym->isVarDeclaration())
return vd->availableExternally;
if (FuncDeclaration* fd = sym->isFuncDeclaration())
return fd->availableExternally;
if (AggregateDeclaration* ad = sym->isAggregateDeclaration())
return ad->availableExternally;
return false;
}
llvm::GlobalValue::LinkageTypes DtoInternalLinkage(Dsymbol* sym)
{
if (needsTemplateLinkage(sym))
if (needsTemplateLinkage(sym)) {
#if LLVM_REV >= 68940
if (isAvailableExternally(sym) && mustDefineSymbol(sym))
return llvm::GlobalValue::AvailableExternallyLinkage;
#endif
return TEMPLATE_LINKAGE_TYPE;
}
else
return llvm::GlobalValue::InternalLinkage;
}
llvm::GlobalValue::LinkageTypes DtoExternalLinkage(Dsymbol* sym)
{
#if LLVM_REV >= 68940
if (isAvailableExternally(sym) && mustDefineSymbol(sym))
return llvm::GlobalValue::AvailableExternallyLinkage;
#endif
if (needsTemplateLinkage(sym))
return TEMPLATE_LINKAGE_TYPE;
else

View File

@@ -42,6 +42,7 @@
#include "gen/linkage.h"
#include "gen/metadata.h"
#include "gen/rttibuilder.h"
#include "gen/llvm-version.h"
#include "ir/irvar.h"
#include "ir/irtype.h"