mirror of
https://github.com/xomboverlord/ldc.git
synced 2026-01-16 12:53:14 +01:00
Implement implicit return after inline asm on x86_64
This commit is contained in:
@@ -670,15 +670,19 @@ void AsmBlockStatement::toIR(IRState* p)
|
||||
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;
|
||||
|
||||
@@ -89,8 +89,10 @@ struct IRAsmBlock
|
||||
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
|
||||
|
||||
@@ -168,6 +168,13 @@ void DtoDefineNakedFunction(FuncDeclaration* fd)
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
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 @@ void emitABIReturnAsmStmt(IRAsmBlock* asmblock, Loc loc, FuncDeclaration* fdecl)
|
||||
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 @@ void emitABIReturnAsmStmt(IRAsmBlock* asmblock, Loc loc, FuncDeclaration* fdecl)
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
{
|
||||
|
||||
@@ -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 @@ ulong bar()
|
||||
else static assert(0, "todo");
|
||||
}
|
||||
|
||||
|
||||
float onef()
|
||||
{
|
||||
version(X86)
|
||||
@@ -32,7 +37,7 @@ float onef()
|
||||
}
|
||||
else version (X86_64)
|
||||
{
|
||||
asm { fld1; }
|
||||
asm { movss XMM0, [one_f]; }
|
||||
}
|
||||
else static assert(0, "todo");
|
||||
}
|
||||
@@ -45,7 +50,7 @@ double oned()
|
||||
}
|
||||
else version (X86_64)
|
||||
{
|
||||
asm { fld1; }
|
||||
asm { movsd XMM0, [one_d]; }
|
||||
}
|
||||
else static assert(0, "todo");
|
||||
}
|
||||
@@ -63,18 +68,172 @@ real oner()
|
||||
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 @@ creal cr2()
|
||||
{
|
||||
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 @@ void main()
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user