changeset 1482:d9c5f5a43403

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.
author Frits van Bommel <fvbommel wxs.nl>
date Sun, 07 Jun 2009 16:00:13 +0200
parents e0f03e11cdf8
children defafbabbe32
files dmd/aggregate.h dmd/declaration.c dmd/declaration.h dmd/func.c dmd/inline.c dmd/mars.h dmd/struct.c gen/classes.cpp gen/enums.h gen/llvmhelpers.cpp gen/main.cpp gen/optimizer.cpp gen/optimizer.h gen/tollvm.cpp gen/typinf.cpp
diffstat 15 files changed, 201 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- a/dmd/aggregate.h	Sun Jun 07 15:07:29 2009 +0200
+++ b/dmd/aggregate.h	Sun Jun 07 16:00:13 2009 +0200
@@ -106,6 +106,11 @@
 #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
--- a/dmd/declaration.c	Sun Jun 07 15:07:29 2009 +0200
+++ b/dmd/declaration.c	Sun Jun 07 16:00:13 2009 +0200
@@ -635,6 +635,8 @@
     offset2 = 0;
 
     nakedUse = false;
+
+    availableExternally = true; // assume this unless proven otherwise
 #endif
 }
 
@@ -1091,6 +1093,16 @@
     }
 }
 
+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 @@
 void TypeInfoDeclaration::semantic(Scope *sc)
 {
     assert(linkage == LINKc);
+    // LDC
+    if (!global.params.useAvailableExternally)
+        availableExternally = false;
 }
 
 /***************************** TypeInfoConstDeclaration **********************/
--- a/dmd/declaration.h	Sun Jun 07 15:07:29 2009 +0200
+++ b/dmd/declaration.h	Sun Jun 07 16:00:13 2009 +0200
@@ -300,6 +300,11 @@
     /// 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 @@
     // 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
--- a/dmd/func.c	Sun Jun 07 15:07:29 2009 +0200
+++ b/dmd/func.c	Sun Jun 07 16:00:13 2009 +0200
@@ -84,6 +84,8 @@
     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 @@
 	return;
     semanticRun = 3;
 
+    // LDC
+    if (!global.params.useAvailableExternally)
+        availableExternally = false;
+
     if (!type || type->ty != Tfunction)
 	return;
     f = (TypeFunction *)(type);
--- a/dmd/inline.c	Sun Jun 07 15:07:29 2009 +0200
+++ b/dmd/inline.c	Sun Jun 07 16:00:13 2009 +0200
@@ -83,14 +83,17 @@
 {
     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 @@
 	//printf("cost = %d\n", cost);
     }
     else
+#endif
     {
 	ics->nested += 1;
 	if (ifbody)
@@ -121,9 +125,11 @@
 
 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 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 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 @@
 	    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 @@
     }
 
     // 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 @@
     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 @@
 	    !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 @@
 #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 @@
 		goto Lno;
 	}
     }
+#endif
 
     memset(&ics, 0, sizeof(ics));
     ics.hasthis = hasthis;
@@ -1334,8 +1363,10 @@
     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
--- a/dmd/mars.h	Sun Jun 07 15:07:29 2009 +0200
+++ b/dmd/mars.h	Sun Jun 07 16:00:13 2009 +0200
@@ -237,6 +237,7 @@
     bool llvmAnnotate;
     bool useInlineAsm;
     bool verbose_cg;
+    bool useAvailableExternally;
 
     // target stuff
     const char* llvmArch;
--- a/dmd/struct.c	Sun Jun 07 15:07:29 2009 +0200
+++ b/dmd/struct.c	Sun Jun 07 16:00:13 2009 +0200
@@ -52,6 +52,10 @@
     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::semantic3(Scope *sc)
 {   int i;
 
+    // LDC
+    if (!global.params.useAvailableExternally)
+        availableExternally = false;
+
     //printf("AggregateDeclaration::semantic3(%s)\n", toChars());
     if (members)
     {
--- a/gen/classes.cpp	Sun Jun 07 15:07:29 2009 +0200
+++ b/gen/classes.cpp	Sun Jun 07 16:00:13 2009 +0200
@@ -491,7 +491,9 @@
     {
         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);
--- a/gen/enums.h	Sun Jun 07 15:07:29 2009 +0200
+++ b/gen/enums.h	Sun Jun 07 16:00:13 2009 +0200
@@ -9,6 +9,5 @@
     LLVMva_copy,
     LLVMva_end,
     LLVMva_arg,
-    LLVMldc,
     LLVMinline_asm
 };
--- a/gen/llvmhelpers.cpp	Sun Jun 07 15:07:29 2009 +0200
+++ b/gen/llvmhelpers.cpp	Sun Jun 07 16:00:13 2009 +0200
@@ -1328,6 +1328,48 @@
 
 	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);
--- a/gen/main.cpp	Sun Jun 07 15:07:29 2009 +0200
+++ b/gen/main.cpp	Sun Jun 07 16:00:13 2009 +0200
@@ -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 @@
          * 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 @@
                 fatal();
         }
 
+#if !IN_LLVM
         for (int i = 0; i < modules.dim; i++)
         {
             m = (Module *)modules.data[i];
@@ -832,10 +845,10 @@
                 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) 
--- a/gen/optimizer.cpp	Sun Jun 07 15:07:29 2009 +0200
+++ b/gen/optimizer.cpp	Sun Jun 07 16:00:13 2009 +0200
@@ -12,6 +12,7 @@
 #include "llvm/Support/PassNameParser.h"
 
 #include "root.h"       // error()
+#include <cstring>      // strcmp();
 
 using namespace llvm;
 
@@ -78,6 +79,19 @@
         || (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 @@
         else
             addPass(pm, createScalarReplAggregatesPass());
         addPass(pm, createGlobalOptimizerPass());
-        addPass(pm, createGlobalDCEPass());
     }
 
     // -O2
@@ -208,6 +221,10 @@
         addPass(pm, createConstantMergePass());
     }
 
+    if (optimizeLevel >= 1) {
+        addPass(pm, createGlobalDCEPass());
+    }
+
     // level -O4 and -O5 are linktime optimizations
 }
 
--- a/gen/optimizer.h	Sun Jun 07 15:07:29 2009 +0200
+++ b/gen/optimizer.h	Sun Jun 07 16:00:13 2009 +0200
@@ -5,7 +5,10 @@
 
 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();
 
--- a/gen/tollvm.cpp	Sun Jun 07 15:07:29 2009 +0200
+++ b/gen/tollvm.cpp	Sun Jun 07 16:00:13 2009 +0200
@@ -241,6 +241,13 @@
     // 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 @@
     // 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;
 
+        // 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;
-        // intrinsics are always external
-        if (fdecl->llvmInternal == LLVMintrinsic)
-            return llvm::GlobalValue::ExternalLinkage;
         // 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 @@
     // 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 @@
         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 @@
     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
--- a/gen/typinf.cpp	Sun Jun 07 15:07:29 2009 +0200
+++ b/gen/typinf.cpp	Sun Jun 07 16:00:13 2009 +0200
@@ -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"