# HG changeset patch # User Frits van Bommel # Date 1234717462 -3600 # Node ID 7e669954db7d083729e41725440e7f6ff35e5463 # Parent 89729c76b8ffc3eaf09260cb5e96f4f2fa21cb38 Implement implicit return after inline asm on x86_64 diff -r 89729c76b8ff -r 7e669954db7d gen/asmstmt.cpp --- 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 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; diff -r 89729c76b8ff -r 7e669954db7d gen/irstate.h --- 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 diff -r 89729c76b8ff -r 7e669954db7d gen/naked.cpp --- 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 { diff -r 89729c76b8ff -r 7e669954db7d tests/mini/asm8.d --- 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);