diff gen/abi.cpp @ 1024:9167d492cbc2

Abstracted more (most) ABI details out of the normal codegen.
author Tomas Lindquist Olsen <tomas.l.olsen@gmail.com>
date Tue, 03 Mar 2009 02:51:21 +0100
parents e8c6dbcd33d1
children 0477f98d357e
line wrap: on
line diff
--- a/gen/abi.cpp	Sun Mar 01 22:40:15 2009 +0100
+++ b/gen/abi.cpp	Tue Mar 03 02:51:21 2009 +0100
@@ -1,5 +1,7 @@
 #include "gen/llvm.h"
 
+#include <algorithm>
+
 #include "mars.h"
 
 #include "gen/irstate.h"
@@ -8,59 +10,7 @@
 #include "gen/abi.h"
 #include "gen/logger.h"
 
-//////////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////////
-/////////////////////        baseclass            ////////////////////////////
-//////////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////////
-
-// FIXME: Would be nice to come up a better and faster way to do this, right
-// now I'm more worried about actually making this abstraction work at all ...
-// It's definitely way overkill with the amount of return value rewrites we
-// have right now, but I expect this to change with proper x86-64 abi support
-
-TargetABI::TargetABI()
-{
-}
-
-llvm::Value* TargetABI::getRet(TypeFunction* tf, llvm::Value* io)
-{
-    if (ABIRetRewrite* r = findRetRewrite(tf))
-    {
-        return r->get(io);
-    }
-    return io;
-}
-
-llvm::Value* TargetABI::putRet(TypeFunction* tf, llvm::Value* io)
-{
-    if (ABIRetRewrite* r = findRetRewrite(tf))
-    {
-        return r->put(io);
-    }
-    return io;
-}
-
-const llvm::Type* TargetABI::getRetType(TypeFunction* tf, const llvm::Type* t)
-{
-    if (ABIRetRewrite* r = findRetRewrite(tf))
-    {
-        return r->type(t);
-    }
-    return t;
-}
-
-ABIRetRewrite * TargetABI::findRetRewrite(TypeFunction * tf)
-{
-    size_t n = retOps.size();
-    if (n)
-    for (size_t i = 0; i < n; i++)
-    {
-        if (retOps[i]->test(tf))
-            return retOps[i];
-    }
-    return NULL;
-}
+#include "ir/irfunction.h"
 
 //////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////
@@ -69,33 +19,28 @@
 //////////////////////////////////////////////////////////////////////////////
 
 // simply swap of real/imag parts for proper x87 complex abi
-struct X87_complex_swap : ABIRetRewrite
+struct X87_complex_swap : ABIRewrite
 {
-    LLValue* get(LLValue* v)
+    LLValue* get(Type*, LLValue* v)
     {
         return DtoAggrPairSwap(v);
     }
-    LLValue* put(LLValue* v)
+    LLValue* put(Type*, LLValue* v)
     {
         return DtoAggrPairSwap(v);
     }
-    const LLType* type(const LLType* t)
+    const LLType* type(Type*, const LLType* t)
     {
         return t;
     }
-    bool test(TypeFunction* tf)
-    {
-        // extern(D) && is(T:creal)
-        return (tf->linkage == LINKd && tf->next->toBasetype()->iscomplex());
-    }
 };
 
 //////////////////////////////////////////////////////////////////////////////
 
-struct X86_cfloat_rewrite : ABIRetRewrite
+struct X86_cfloat_rewrite : ABIRewrite
 {
     // i64 -> {float,float}
-    LLValue* get(LLValue* in)
+    LLValue* get(Type*, LLValue* in)
     {
         // extract real part
         LLValue* rpart = gIR->ir->CreateTrunc(in, LLType::Int32Ty);
@@ -111,7 +56,7 @@
     }
 
     // {float,float} -> i64
-    LLValue* put(LLValue* v)
+    LLValue* put(Type*, LLValue* v)
     {
         // extract real
         LLValue* r = gIR->ir->CreateExtractValue(v, 0);
@@ -134,16 +79,41 @@
     }
 
     // {float,float} -> i64
-    const LLType* type(const LLType* t)
+    const LLType* type(Type*, const LLType* t)
     {
         return LLType::Int64Ty;
     }
+};
 
-    // test if rewrite applies to function
-    bool test(TypeFunction* tf)
+//////////////////////////////////////////////////////////////////////////////
+
+// FIXME: try into eliminating the alloca or if at least check
+// if it gets optimized away
+
+// convert byval struct
+// when 
+struct X86_struct_to_register : ABIRewrite
+{
+    // int -> struct
+    LLValue* get(Type* dty, LLValue* v)
     {
-        return (tf->linkage != LINKd) 
-            && (tf->next->toBasetype() == Type::tcomplex32);
+        Logger::println("rewriting int -> struct");
+        LLValue* mem = DtoAlloca(DtoType(dty), ".int_to_struct");
+        DtoStore(v, DtoBitCast(mem, getPtrToType(v->getType())));
+        return DtoLoad(mem);
+    }
+    // struct -> int
+    LLValue* put(Type* dty, LLValue* v)
+    {
+        Logger::println("rewriting struct -> int");
+        LLValue* mem = DtoAlloca(v->getType(), ".struct_to_int");
+        DtoStore(v, mem);
+        DtoLoad(DtoBitCast(mem, getPtrToType(type(dty, v->getType()))));
+    }
+    const LLType* type(Type*, const LLType* t)
+    {
+        size_t sz = getTypePaddedSize(t)*8;
+        return LLIntegerType::get(sz);
     }
 };
 
@@ -151,11 +121,9 @@
 
 struct X86TargetABI : TargetABI
 {
-    X86TargetABI()
-    {
-        retOps.push_back(new X87_complex_swap);
-        retOps.push_back(new X86_cfloat_rewrite);
-    }
+    X87_complex_swap swapComplex;
+    X86_cfloat_rewrite cfloatToInt;
+    X86_struct_to_register structToReg;
 
     bool returnInArg(TypeFunction* tf)
     {
@@ -168,6 +136,98 @@
         else
             return (rt->ty == Tstruct || rt->ty == Tcomplex64 || rt->ty == Tcomplex80);
     }
+
+    bool passByVal(Type* t)
+    {
+        return t->toBasetype()->ty == Tstruct;
+    }
+
+    void rewriteFunctionType(TypeFunction* tf)
+    {
+        IrFuncTy* fty = tf->fty;
+        Type* rt = fty->ret->type->toBasetype();
+
+        // extern(D)
+        if (tf->linkage == LINKd)
+        {
+            // RETURN VALUE
+
+            // complex {re,im} -> {im,re}
+            if (rt->iscomplex())
+            {
+                fty->ret->rewrite = &swapComplex;
+            }
+
+            // IMPLICIT PARAMETERS
+
+            // mark this/nested params inreg
+            if (fty->arg_this)
+            {
+                fty->arg_this->attrs = llvm::Attribute::InReg;
+            }
+            else if (fty->arg_nest)
+            {
+                fty->arg_nest->attrs = llvm::Attribute::InReg;
+            }
+            // otherwise try to mark the last param inreg
+            else if (!fty->arg_sret && !fty->args.empty())
+            {
+                // The last parameter is passed in EAX rather than being pushed on the stack if the following conditions are met:
+                //   * It fits in EAX.
+                //   * It is not a 3 byte struct.
+                //   * It is not a floating point type.
+
+                IrFuncTyArg* last = fty->args.back();
+                Type* lastTy = last->type->toBasetype();
+                unsigned sz = lastTy->size();
+
+                if (last->byref && !last->isByVal())
+                {
+                    last->attrs = llvm::Attribute::InReg;
+                }
+                else if (!lastTy->isfloating() && (sz == 1 || sz == 2 || sz == 4)) // right?
+                {
+                    // rewrite the struct into an integer to make inreg work
+                    if (lastTy->ty == Tstruct)
+                    {
+                        last->rewrite = &structToReg;
+                        last->ltype = structToReg.type(last->type, last->ltype);
+                        last->byref = false;
+                    }
+                    last->attrs = llvm::Attribute::InReg;
+                }
+            }
+
+            // FIXME: tf->varargs == 1 need to use C calling convention and vararg mechanism to live up to the spec:
+            // "The caller is expected to clean the stack. _argptr is not passed, it is computed by the callee."
+
+            // EXPLICIT PARAMETERS
+
+            // reverse parameter order
+            // for non variadics
+            if (!fty->args.empty() && tf->varargs != 1)
+            {
+                fty->reverseParams = true;
+            }
+        }
+
+        // extern(C) and all others
+        else
+        {
+            // RETURN VALUE
+
+            // cfloat -> i64
+            if (tf->next->toBasetype() == Type::tcomplex32)
+            {
+                fty->ret->rewrite = &cfloatToInt;
+                fty->ret->ltype = LLType::Int64Ty;
+            }
+
+            // IMPLICIT PARAMETERS
+
+            // EXPLICIT PARAMETERS
+        }
+    }
 };
 
 //////////////////////////////////////////////////////////////////////////////
@@ -176,10 +236,10 @@
 //////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////
 
-struct X86_64_cfloat_rewrite : ABIRetRewrite
+struct X86_64_cfloat_rewrite : ABIRewrite
 {
     // {double} -> {float,float}
-    LLValue* get(LLValue* in)
+    LLValue* get(Type*, LLValue* in)
     {
         // extract double
         LLValue* v = gIR->ir->CreateExtractValue(in, 0);
@@ -200,7 +260,7 @@
     }
 
     // {float,float} -> {double}
-    LLValue* put(LLValue* v)
+    LLValue* put(Type*, LLValue* v)
     {
         // extract real
         LLValue* r = gIR->ir->CreateExtractValue(v, 0);
@@ -231,33 +291,41 @@
     }
 
     // {float,float} -> {double}
-    const LLType* type(const LLType* t)
+    const LLType* type(Type*, const LLType* t)
     {
         return LLStructType::get(LLType::DoubleTy, NULL);
     }
-
-    // test if rewrite applies to function
-    bool test(TypeFunction* tf)
-    {
-        return (tf->linkage != LINKd) 
-            && (tf->next->toBasetype() == Type::tcomplex32);
-    }
 };
 
 //////////////////////////////////////////////////////////////////////////////
 
 struct X86_64TargetABI : TargetABI
 {
-    X86_64TargetABI()
-    {
-        retOps.push_back(new X86_64_cfloat_rewrite);
-    }
+    X86_64_cfloat_rewrite cfloat_rewrite;
 
     bool returnInArg(TypeFunction* tf)
     {
         Type* rt = tf->next->toBasetype();
         return (rt->ty == Tstruct);
     }
+
+    bool passByVal(Type* t)
+    {
+        return t->toBasetype()->ty == Tstruct;
+    }
+
+    void rewriteFunctionType(TypeFunction* tf)
+    {
+        IrFuncTy* fty = tf->fty;
+        Type* rt = fty->ret->type->toBasetype();
+
+        // rewrite cfloat return for !extern(D)
+        if (tf->linkage != LINKd && rt == Type::tcomplex32)
+        {
+            fty->ret->rewrite = &cfloat_rewrite;
+            fty->ret->ltype = cfloat_rewrite.type(fty->ret->type, fty->ret->ltype);
+        }
+    }
 };
 
 //////////////////////////////////////////////////////////////////////////////
@@ -269,15 +337,19 @@
 // Some reasonable defaults for when we don't know what ABI to use.
 struct UnknownTargetABI : TargetABI
 {
-    UnknownTargetABI()
+    bool returnInArg(TypeFunction* tf)
     {
-        // Don't push anything into retOps, assume defaults will be fine.
+        return (tf->next->toBasetype()->ty == Tstruct);
     }
 
-    bool returnInArg(TypeFunction* tf)
+    bool passByVal(Type* t)
     {
-        Type* rt = tf->next->toBasetype();
-        return (rt->ty == Tstruct);
+        return t->toBasetype()->ty == Tstruct;
+    }
+
+    void rewriteFunctionType(TypeFunction* t)
+    {
+        // why?
     }
 };