changeset 930:7985bb036db4

Follow the D ABI and pass the last arg in a register if it is a struct that fits.
author Christian Kamm <kamm incasoftware de>
date Tue, 03 Feb 2009 21:46:46 +0100
parents 0749c0757a43
children b6647328d11e
files dmd/mtype.c dmd/mtype.h gen/functions.cpp gen/tocall.cpp
diffstat 4 files changed, 54 insertions(+), 20 deletions(-) [+]
line wrap: on
line diff
--- a/dmd/mtype.c	Tue Feb 03 18:11:39 2009 +0100
+++ b/dmd/mtype.c	Tue Feb 03 21:46:46 2009 +0100
@@ -2691,10 +2691,11 @@
     this->retInPtr = false;
     this->usesThis = false;
     this->usesNest = false;
+    this->structInregArg = false;
     this->retAttrs = 0;
     this->thisAttrs = 0;
     this->reverseParams = false;
-    this->reverseIndex = 0;
+    this->firstRealArg = 0;
 }
 
 Type *TypeFunction::syntaxCopy()
@@ -2708,7 +2709,7 @@
     t->retAttrs = retAttrs;
     t->thisAttrs = thisAttrs;
     t->reverseParams = reverseParams;
-    t->reverseIndex = reverseIndex;
+    t->firstRealArg = firstRealArg;
     return t;
 }
 
--- a/dmd/mtype.h	Tue Feb 03 18:11:39 2009 +0100
+++ b/dmd/mtype.h	Tue Feb 03 21:46:46 2009 +0100
@@ -438,11 +438,13 @@
     bool retInPtr;
     bool usesThis;
     bool usesNest;
+    bool structInregArg;
     unsigned retAttrs;
     unsigned thisAttrs; // also used for nest
+    // parameter index in the llvm function that contains the first not-implicit arg
+    size_t firstRealArg;
 
     bool reverseParams;
-    size_t reverseIndex;
 };
 
 struct TypeDelegate : Type
--- a/gen/functions.cpp	Tue Feb 03 18:11:39 2009 +0100
+++ b/gen/functions.cpp	Tue Feb 03 21:46:46 2009 +0100
@@ -101,6 +101,9 @@
         paramvec.push_back(getVoidPtrType()); // _argptr
     }
 
+    // now that all implicit args are done, store the start of the real args
+    f->firstRealArg = paramvec.size();
+
     // number of formal params
     size_t n = Argument::dim(f->parameters);
 
@@ -114,7 +117,6 @@
         if (n > 1 && f->linkage == LINKd && !dVararg)
         {
             f->reverseParams = true;
-            f->reverseIndex = paramvec.size();
         }
     }
 #endif // X86_REVERSE_PARAMS
@@ -177,24 +179,17 @@
     // reverse params?
     if (f->reverseParams)
     {
-        std::reverse(paramvec.begin() + f->reverseIndex, paramvec.end());
+        std::reverse(paramvec.begin() + f->firstRealArg, paramvec.end());
     }
 
-    // construct function type
-    bool isvararg = !(dVararg || arrayVararg) && f->varargs;
-    llvm::FunctionType* functype = llvm::FunctionType::get(actualRettype, paramvec, isvararg);
-
 #if X86_PASS_IN_EAX
-    // tell first param to be passed in a register if we can
+    // pass first param in EAX if it fits, is not floating point and is not a 3 byte struct.
     // ONLY extern(D) functions !
     if ((n > 0 || usesthis || usesnest) && f->linkage == LINKd)
     {
         // FIXME: Only x86 right now ...
         if (global.params.cpu == ARCHx86)
         {
-            // pass first param in EAX if it fits, is not floating point and is not a 3 byte struct.
-            // FIXME: struct are not passed in EAX yet
-
             int n_inreg = f->reverseParams ? n - 1 : 0;
             Argument* arg = Argument::getNth(f->parameters, n_inreg);
 
@@ -209,25 +204,42 @@
             {
                 Type* t = arg->type->toBasetype();
 
-                // 32bit ints, pointers, classes, static arrays, AAs, ref and out params
+                // 32bit ints, pointers, classes, static arrays, AAs, ref and out params,
+                // and structs with size <= 4 and != 3
                 // are candidate for being passed in EAX
                 if (
                     (arg->storageClass & (STCref|STCout))
                     ||
                     ((arg->storageClass & STCin) &&
                      ((t->isscalar() && !t->isfloating()) ||
-                     t->ty == Tclass || t->ty == Tsarray || t->ty == Taarray) &&
-                     (t->size() <= PTRSIZE))
+                      t->ty == Tclass || t->ty == Tsarray || t->ty == Taarray ||
+                      (t->ty == Tstruct && t->size() != 3)
+                     ) && (t->size() <= PTRSIZE))
                    )
                 {
                     arg->llvmAttrs |= llvm::Attribute::InReg;
                     assert((f->thisAttrs & llvm::Attribute::InReg) == 0 && "can't have two inreg args!");
+
+                    // structs need to go from {...}* byval to {...} inreg
+                    if ((arg->storageClass & STCin) && t->ty == Tstruct)
+                    {
+                        int n_param = f->reverseParams ? f->firstRealArg + n - 1 - n_inreg : f->firstRealArg + n_inreg;
+                        assert(isaPointer(paramvec[n_param]) && (arg->llvmAttrs & llvm::Attribute::ByVal)
+                            && "struct parameter expected to be {...}* byval before inreg is applied");
+                        paramvec[n_param] = paramvec[n_param]->getContainedType(0);
+                        arg->llvmAttrs &= ~llvm::Attribute::ByVal;
+                        f->structInregArg = true;
+                    }
                 }
             }
         }
     }
 #endif // X86_PASS_IN_EAX
 
+    // construct function type
+    bool isvararg = !(dVararg || arrayVararg) && f->varargs;
+    llvm::FunctionType* functype = llvm::FunctionType::get(actualRettype, paramvec, isvararg);
+
     // done
     f->retInPtr = retinptr;
     f->usesThis = usesthis;
@@ -740,6 +752,21 @@
             VarDeclaration* vd = argsym->isVarDeclaration();
             assert(vd);
 
+            IrLocal* irloc = vd->ir.irLocal;
+            assert(irloc);
+
+            // if it's inreg struct arg, allocate storage
+            if (f->structInregArg && i == (f->reverseParams ? n - 1 : 0))
+            {
+                int n_param = f->reverseParams ? f->firstRealArg + n - 1 - i : f->firstRealArg + i;
+                assert(!f->usesNest && !f->usesThis && isaStruct(functype->getParamType(n_param))
+                    && "Preconditions for inreg struct arg not met!");
+
+                LLValue* mem = DtoAlloca(functype->getParamType(n_param), "inregstructarg");
+                DtoStore(irloc->value, mem);
+                irloc->value = mem;
+            }
+
         #if DMDV2
             if (vd->nestedrefs.dim)
         #else
@@ -749,9 +776,6 @@
                 fd->nestedVars.insert(vd);
             }
 
-            IrLocal* irloc = vd->ir.irLocal;
-            assert(irloc);
-
             bool refout = vd->storage_class & (STCref | STCout);
             bool lazy = vd->storage_class & STClazy;
 
--- a/gen/tocall.cpp	Tue Feb 03 18:11:39 2009 +0100
+++ b/gen/tocall.cpp	Tue Feb 03 21:46:46 2009 +0100
@@ -370,6 +370,13 @@
             DValue* argval = DtoArgument(fnarg, (Expression*)arguments->data[i]);
             LLValue* arg = argval->getRVal();
 
+            // if it's a struct inreg arg, load first to pass as first-class value
+            if (tf->structInregArg && i == (tf->reverseParams ? n - 1 : 0))
+            {
+                assert(fnarg->llvmAttrs & llvm::Attribute::InReg);
+                arg = DtoLoad(arg);
+            }
+
             int j = tf->reverseParams ? beg + n - i - 1 : beg + i;
 
             // parameter type mismatch, this is hard to get rid of
@@ -395,7 +402,7 @@
         // reverse the relevant params as well as the param attrs
         if (tf->reverseParams)
         {
-            std::reverse(args.begin() + tf->reverseIndex, args.end());
+            std::reverse(args.begin() + tf->firstRealArg, args.end());
             std::reverse(attrptr.begin(), attrptr.end());
         }