changeset 959:7e669954db7d

Implement implicit return after inline asm on x86_64
author Frits van Bommel <fvbommel wxs.nl>
date Sun, 15 Feb 2009 18:04:22 +0100
parents 89729c76b8ff
children 0b38b64f62c7
files gen/asmstmt.cpp gen/irstate.h gen/naked.cpp tests/mini/asm8.d
diffstat 4 files changed, 262 insertions(+), 15 deletions(-) [+]
line wrap: on
line diff
--- a/gen/asmstmt.cpp	Sun Feb 15 14:54:36 2009 +0100
+++ b/gen/asmstmt.cpp	Sun Feb 15 18:04:22 2009 +0100
@@ -670,15 +670,19 @@
     std::vector<LLValue*> args;
     args.insert(args.end(), outargs.begin(), outargs.end());
     args.insert(args.end(), inargs.begin(), inargs.end());
-    llvm::CallInst* call = p->ir->CreateCall(ia, args.begin(), args.end(), "");
+    llvm::CallInst* call = p->ir->CreateCall(ia, args.begin(), args.end(),
+        retty == LLType::VoidTy ? "" : "asm");
 
     // capture abi return value
     if (useabiret)
     {
-        if (p->asmBlock->retemu)
-        p->asmBlock->asmBlock->abiret = DtoLoad(p->asmBlock->asmBlock->abiret);
+        IRAsmBlock* block = p->asmBlock;
+        if (block->retfixup)
+            block->asmBlock->abiret = (*block->retfixup)(p->ir, call);
+        else if (p->asmBlock->retemu)
+            block->asmBlock->abiret = DtoLoad(block->asmBlock->abiret);
         else
-        p->asmBlock->asmBlock->abiret = call;
+            block->asmBlock->abiret = call;
     }
 
     p->asmBlock = NULL;
--- a/gen/irstate.h	Sun Feb 15 14:54:36 2009 +0100
+++ b/gen/irstate.h	Sun Feb 15 18:04:22 2009 +0100
@@ -89,8 +89,10 @@
     const LLType* retty;
     unsigned retn;
     bool retemu; // emulate abi ret with a temporary
+    LLValue* (*retfixup)(IRBuilderHelper b, LLValue* orig); // Modifies retval
 
-    IRAsmBlock(AsmBlockStatement* b) : asmBlock(b), retty(NULL), retn(0), retemu(false) {}
+    IRAsmBlock(AsmBlockStatement* b)
+        : asmBlock(b), retty(NULL), retn(0), retemu(false), retfixup(NULL) {}
 };
 
 // llvm::CallInst and llvm::InvokeInst don't share a common base
--- a/gen/naked.cpp	Sun Feb 15 14:54:36 2009 +0100
+++ b/gen/naked.cpp	Sun Feb 15 18:04:22 2009 +0100
@@ -168,6 +168,13 @@
 
 //////////////////////////////////////////////////////////////////////////////////////////
 
+static LLValue* x86_64_cfloatRetFixup(IRBuilderHelper b, LLValue* orig) {
+    assert(orig->getType() == LLType::DoubleTy);
+    LLType* retty = LLStructType::get(LLType::DoubleTy, NULL);
+    LLValue* undef = llvm::UndefValue::get(retty);
+    return b->CreateInsertValue(undef, orig, 0, "asm.ret");
+}
+
 void emitABIReturnAsmStmt(IRAsmBlock* asmblock, Loc loc, FuncDeclaration* fdecl)
 {
     Logger::println("emitABIReturnAsmStmt(%s)", fdecl->mangle());
@@ -179,8 +186,8 @@
     asmblock->retty = llretTy;
     asmblock->retn = 1;
 
-    // x86 or x86_64
-    if (global.params.cpu == ARCHx86 || global.params.cpu == ARCHx86_64)
+    // x86
+    if (global.params.cpu == ARCHx86)
     {
         LINK l = fdecl->linkage;
         assert((l == LINKd || l == LINKc || l == LINKwindows) && "invalid linkage for asm implicit return");
@@ -238,6 +245,69 @@
         }
     }
 
+    // x86_64
+    else if (global.params.cpu == ARCHx86_64)
+    {
+        LINK l = fdecl->linkage;
+        /* TODO: Check if this works with extern(Windows), completely untested.
+         *       In particular, returning cdouble may not work with
+         *       extern(Windows) since according to X86CallingConv.td it
+         *       doesn't allow XMM1 to be used.
+         * (So is extern(C), but that should be fine as the calling convention
+         * is identical to that of extern(D))
+         */
+        assert((l == LINKd || l == LINKc || l == LINKwindows) && "invalid linkage for asm implicit return");
+
+        Type* rt = fdecl->type->nextOf()->toBasetype();
+        if (rt->isintegral() || rt->ty == Tpointer || rt->ty == Tclass || rt->ty == Taarray)
+        {
+            as->out_c = "={ax},";
+        }
+        else if (rt->isfloating())
+        {
+            if (rt == Type::tcomplex80) {
+                // On x87 stack, re=st, im=st(1)
+                as->out_c = "={st},={st(1)},";
+                asmblock->retn = 2;
+            } else if (rt == Type::tfloat80 || rt == Type::timaginary80) {
+                // On x87 stack
+                as->out_c = "={st},";
+            } else if (l != LINKd && rt == Type::tcomplex32) {
+                // LLVM and GCC disagree on how to return {float, float}.
+                // For compatibility, use the GCC/LLVM-GCC way for extern(C/Windows)
+                // extern(C) cfloat -> %xmm0 (extract two floats)
+            #if 0
+                // Disabled because "regular" extern(C) functions aren't
+                // ABI-compatible with GCC yet.
+                // TODO: enable when "extern(C) cfloat foo();" compiles to "declare { double } @foo();"
+                as->out_c = "={xmm0},";
+                asmblock->retty = LLStructType::get(LLType::DoubleTy, NULL);;
+                asmblock->retfixup = &x86_64_cfloatRetFixup;
+            #else
+                error(loc, "unimplemented return type '%s' for implicit abi return", rt->toChars());
+                fatal();
+            #endif
+            } else if (rt->iscomplex()) {
+                // cdouble and extern(D) cfloat -> re=%xmm0, im=%xmm1
+                as->out_c = "={xmm0},={xmm1},";
+                asmblock->retn = 2;
+            } else {
+                // Plain float/double/ifloat/idouble
+                as->out_c = "={xmm0},";
+            }
+        }
+        else if (rt->ty == Tarray || rt->ty == Tdelegate)
+        {
+            as->out_c = "={ax},={dx},";
+            asmblock->retn = 2;
+        }
+        else
+        {
+            error(loc, "unimplemented return type '%s' for implicit abi return", rt->toChars());
+            fatal();
+        }
+    }
+
     // unsupported
     else
     {
--- a/tests/mini/asm8.d	Sun Feb 15 14:54:36 2009 +0100
+++ b/tests/mini/asm8.d	Sun Feb 15 18:04:22 2009 +0100
@@ -1,3 +1,7 @@
+const float  one_f = 1;
+const double one_d = 1;
+const real   one_r = 1;
+
 int foo()
 {
     version(X86)
@@ -24,6 +28,7 @@
     else static assert(0, "todo");
 }
 
+
 float onef()
 {
     version(X86)
@@ -32,7 +37,7 @@
     }
     else version (X86_64)
     {
-        asm { fld1; }
+        asm { movss XMM0, [one_f]; }
     }
     else static assert(0, "todo");
 }
@@ -45,7 +50,7 @@
     }
     else version (X86_64)
     {
-        asm { fld1; }
+        asm { movsd XMM0, [one_d]; }
     }
     else static assert(0, "todo");
 }
@@ -63,18 +68,172 @@
     else static assert(0, "todo");
 }
 
+ifloat oneif()
+{
+    version(X86)
+    {
+        asm { fld1; }
+    }
+    else version (X86_64)
+    {
+        asm { movss XMM0, [one_f]; }
+    }
+    else static assert(0, "todo");
+}
 
-real two = 2.0;
+idouble oneid()
+{
+    version(X86)
+    {
+        asm { fld1; }
+    }
+    else version (X86_64)
+    {
+        asm { movsd XMM0, [one_d]; }
+    }
+    else static assert(0, "todo");
+}
+
+ireal oneir()
+{
+    version(X86)
+    {
+        asm { fld1; }
+    }
+    else version (X86_64)
+    {
+        asm { fld1; }
+    }
+    else static assert(0, "todo");
+}
+
+
+const float two_f = 2;
+
+cfloat cf()
+{
+    version(X86)
+    {
+        asm { fld1; flds two_f; }
+    }
+    else version (X86_64)
+    {
+        asm
+        {
+            movss XMM1, [two_f];
+            movss XMM0, [one_f];
+            movd ECX, XMM1;
+            movd EAX, XMM0;
+            
+            // invalid operand size :(
+            //shl RCX, 32;
+            //or RAX, RCX;
+            
+            pushq RAX;
+            mov [RSP + 4], EAX;
+            popq RAX;
+            
+            movd XMM0, RAX;
+        }
+    }
+    else static assert(0, "todo");
+}
+
+cfloat cf2()
+{
+    version(X86)
+    {
+        asm
+        {
+            naked;
+            fld1;
+            flds two_f;
+            ret;
+        }
+    }
+    else version (X86_64)
+    {
+        asm
+        {
+            naked;
+            movss XMM1, [two_f];
+            movss XMM0, [one_f];
+            movd ECX, XMM1;
+            movd EAX, XMM0;
+            
+            // invalid operand size :(
+            //shl RCX, 32;
+            //or RAX, RCX;
+            
+            pushq RAX;
+            mov [RSP + 4], EAX;
+            popq RAX;
+            
+            movd RAX, XMM0;
+            ret;
+        }
+    }
+    else static assert(0, "todo");
+}
+
+
+const double two_d = 2;
+
+cdouble cd()
+{
+    version(X86)
+    {
+        asm { fld1; fld two_d }
+    }
+    else version (X86_64)
+    {
+        asm
+        {
+            leaq RAX, [one_d];
+            leaq RCX, [two_d];
+            movsd XMM0, [RAX];
+            movsd XMM1, [RCX];
+        }
+    }
+    else static assert(0, "todo");
+}
+
+cdouble cd2()
+{
+    version(X86)
+    {
+        asm
+        {
+            naked;
+            fld1;
+            fld two_d;
+            ret;
+        }
+    }
+    else version (X86_64)
+    {
+        asm
+        {
+            naked;
+            movsd XMM0, [one_d];
+            movsd XMM1, [two_d];
+        }
+    }
+    else static assert(0, "todo");
+}
+
+
+const real two_r = 2.0;
 
 creal cr()
 {
     version(X86)
     {
-        asm { fld1; fld two; }
+        asm { fld1; fld two_r; }
     }
     else version (X86_64)
     {
-        asm { fld1; fld two; }
+        asm { fld two_r; fld1; }
     }
     else static assert(0, "todo");
 }
@@ -87,17 +246,17 @@
         {
             naked;
             fld1;
-            fld two;
+            fld two_r;
             ret;
         }
     }
     else version (X86_64)
     {
-    asm
+        asm
         {
             naked;
+            fld two_r;
             fld1;
-            fld two;
             ret;
         }
     }
@@ -193,8 +352,20 @@
     assert(onef() == 1);
     assert(oned() == 1);
     assert(oner() == 1);
+    
+    assert(oneif() == 1i);
+    assert(oneid() == 1i);
+    assert(oneir() == 1i);
+    
+    assert(cf() == 1+2i);
+    assert(cf2() == 1+2i);
+    
+    assert(cd() == 1+2i);
+    assert(cd2() == 1+2i);
+    
     assert(cr() == 1+2i);
     assert(cr2() == 1+2i);
+    
     assert(vp() == cast(void*)0x80);
     assert(aa() is gaa);
     assert(ob() is gobj);