From 61dfb11bc5208d939222cfd8bb1ba32ae783f2fd Mon Sep 17 00:00:00 2001 From: Tomas Lindquist Olsen Date: Sun, 1 Mar 2009 19:01:05 +0100 Subject: [PATCH] - Fixed x86-32 C ABI for complex number return values. - Removed unused code from the ABI class. --- gen/abi.cpp | 99 +++++++++++++++++++++++++++++++++------------ gen/abi.h | 3 +- gen/functions.cpp | 2 +- gen/llvmhelpers.cpp | 6 ++- gen/naked.cpp | 22 +++++++++- gen/tocall.cpp | 5 +++ tests/mini/asm8.d | 9 +++-- 7 files changed, 110 insertions(+), 36 deletions(-) diff --git a/gen/abi.cpp b/gen/abi.cpp index ccd07489..e20192d5 100644 --- a/gen/abi.cpp +++ b/gen/abi.cpp @@ -85,7 +85,65 @@ struct X87_complex_swap : ABIRetRewrite } bool test(TypeFunction* tf) { - return (tf->next->toBasetype()->iscomplex()); + // extern(D) && is(T:creal) + return (tf->linkage == LINKd && tf->next->toBasetype()->iscomplex()); + } +}; + +////////////////////////////////////////////////////////////////////////////// + +struct X86_cfloat_rewrite : ABIRetRewrite +{ + // i64 -> {float,float} + LLValue* get(LLValue* in) + { + // extract real part + LLValue* rpart = gIR->ir->CreateTrunc(in, LLType::Int32Ty); + rpart = gIR->ir->CreateBitCast(rpart, LLType::FloatTy, ".re"); + + // extract imag part + LLValue* ipart = gIR->ir->CreateLShr(in, LLConstantInt::get(LLType::Int64Ty, 32, false)); + ipart = gIR->ir->CreateTrunc(ipart, LLType::Int32Ty); + ipart = gIR->ir->CreateBitCast(ipart, LLType::FloatTy, ".im"); + + // return {float,float} aggr pair with same bits + return DtoAggrPair(rpart, ipart, ".final_cfloat"); + } + + // {float,float} -> i64 + LLValue* put(LLValue* v) + { + // extract real + LLValue* r = gIR->ir->CreateExtractValue(v, 0); + // cast to i32 + r = gIR->ir->CreateBitCast(r, LLType::Int32Ty); + // zext to i64 + r = gIR->ir->CreateZExt(r, LLType::Int64Ty); + + // extract imag + LLValue* i = gIR->ir->CreateExtractValue(v, 1); + // cast to i32 + i = gIR->ir->CreateBitCast(i, LLType::Int32Ty); + // zext to i64 + i = gIR->ir->CreateZExt(i, LLType::Int64Ty); + // shift up + i = gIR->ir->CreateShl(i, LLConstantInt::get(LLType::Int64Ty, 32, false)); + + // combine and return + return v = gIR->ir->CreateOr(r, i); + } + + // {float,float} -> i64 + const LLType* type(const LLType* t) + { + return LLType::Int64Ty; + } + + // test if rewrite applies to function + bool test(TypeFunction* tf) + { + return (tf->linkage != LINKd) + && (tf->next->toBasetype() == Type::tcomplex32); } }; @@ -96,18 +154,19 @@ struct X86TargetABI : TargetABI X86TargetABI() { retOps.push_back(new X87_complex_swap); + retOps.push_back(new X86_cfloat_rewrite); } - bool returnInArg(Type* t) + bool returnInArg(TypeFunction* tf) { - Type* rt = t->toBasetype(); - return (rt->ty == Tstruct); - } - - bool passByRef(Type* t) - { - t = t->toBasetype(); - return (t->ty == Tstruct || t->ty == Tsarray); + Type* rt = tf->next->toBasetype(); + // D only returns structs on the stack + if (tf->linkage == LINKd) + return (rt->ty == Tstruct); + // other ABI's follow C, which is cdouble and creal returned on the stack + // as well as structs + else + return (rt->ty == Tstruct || rt->ty == Tcomplex64 || rt->ty == Tcomplex80); } }; @@ -194,17 +253,11 @@ struct X86_64TargetABI : TargetABI retOps.push_back(new X86_64_cfloat_rewrite); } - bool returnInArg(Type* t) + bool returnInArg(TypeFunction* tf) { - Type* rt = t->toBasetype(); + Type* rt = tf->next->toBasetype(); return (rt->ty == Tstruct); } - - bool passByRef(Type* t) - { - t = t->toBasetype(); - return (t->ty == Tstruct || t->ty == Tsarray); - } }; ////////////////////////////////////////////////////////////////////////////// @@ -221,17 +274,11 @@ struct UnknownTargetABI : TargetABI // Don't push anything into retOps, assume defaults will be fine. } - bool returnInArg(Type* t) + bool returnInArg(TypeFunction* tf) { - Type* rt = t->toBasetype(); + Type* rt = tf->next->toBasetype(); return (rt->ty == Tstruct); } - - bool passByRef(Type* t) - { - t = t->toBasetype(); - return (t->ty == Tstruct || t->ty == Tsarray); - } }; ////////////////////////////////////////////////////////////////////////////// diff --git a/gen/abi.h b/gen/abi.h index 52ded695..6d654cb6 100644 --- a/gen/abi.h +++ b/gen/abi.h @@ -38,8 +38,7 @@ struct TargetABI llvm::Value* getRet(TypeFunction* tf, llvm::Value* v); llvm::Value* putRet(TypeFunction* tf, llvm::Value* v); - virtual bool returnInArg(Type* t) = 0; - virtual bool passByRef(Type* t) = 0; + virtual bool returnInArg(TypeFunction* t) = 0; protected: std::vector retOps; diff --git a/gen/functions.cpp b/gen/functions.cpp index 1b439e16..acf56e8a 100644 --- a/gen/functions.cpp +++ b/gen/functions.cpp @@ -79,7 +79,7 @@ const llvm::FunctionType* DtoFunctionType(Type* type, const LLType* thistype, co } else { - if (gABI->returnInArg(rt)) + if (gABI->returnInArg(f)) { rettype = getPtrToType(DtoType(rt)); actualRettype = LLType::VoidTy; diff --git a/gen/llvmhelpers.cpp b/gen/llvmhelpers.cpp index 4cca2de9..b82821d5 100644 --- a/gen/llvmhelpers.cpp +++ b/gen/llvmhelpers.cpp @@ -392,6 +392,8 @@ LLValue* DtoNestedContext(Loc loc, Dsymbol* sym) // ASSIGNMENT HELPER (store this in that) ////////////////////////////////////////////////////////////////////////////////////////*/ +// is this a good approach at all ? + void DtoAssign(Loc& loc, DValue* lhs, DValue* rhs) { Logger::println("DtoAssign(...);\n"); @@ -402,7 +404,7 @@ void DtoAssign(Loc& loc, DValue* lhs, DValue* rhs) if (t->ty == Tstruct) { if (!t->equals(t2)) { - // TODO: fix this, use 'rhs' for something + // FIXME: use 'rhs' for something !?! DtoAggrZeroInit(lhs->getLVal()); } else { @@ -476,7 +478,7 @@ void DtoAssign(Loc& loc, DValue* lhs, DValue* rhs) DtoStore(r, l); } else if (t->iscomplex()) { - LLValue* dst; + LLValue *dst, *src; if (DLRValue* lr = lhs->isLRValue()) { dst = lr->getLVal(); rhs = DtoCastComplex(loc, rhs, lr->getLType()); diff --git a/gen/naked.cpp b/gen/naked.cpp index de030a03..51c18035 100644 --- a/gen/naked.cpp +++ b/gen/naked.cpp @@ -175,6 +175,13 @@ static LLValue* x86_64_cfloatRetFixup(IRBuilderHelper b, LLValue* orig) { return b->CreateInsertValue(undef, orig, 0, "asm.ret"); } +static LLValue* x86_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()); @@ -204,8 +211,19 @@ void emitABIReturnAsmStmt(IRAsmBlock* asmblock, Loc loc, FuncDeclaration* fdecl) else if (rt->isfloating()) { if (rt->iscomplex()) { - as->out_c = "={st},={st(1)},"; - asmblock->retn = 2; + if (fdecl->linkage == LINKd) { + // extern(D) always returns on the FPU stack + as->out_c = "={st},={st(1)},"; + asmblock->retn = 2; + } else if (rt->ty == Tcomplex32) { + // extern(C) cfloat is return as i64 + as->out_c = "=A,"; + asmblock->retty = LLType::Int64Ty; + } else { + // cdouble and creal extern(C) are returned in pointer + // don't add anything! + return; + } } else { as->out_c = "={st},"; } diff --git a/gen/tocall.cpp b/gen/tocall.cpp index 20445448..1d90a21a 100644 --- a/gen/tocall.cpp +++ b/gen/tocall.cpp @@ -527,5 +527,10 @@ DValue* DtoCallFunction(Loc& loc, Type* resulttype, DValue* fnval, Expressions* call.setCallingConv(callconv); call.setAttributes(attrlist); + // if we are returning through a pointer arg + // make sure we provide a lvalue back! + if (retinptr) + return new DVarValue(resulttype, retllval); + return new DImValue(resulttype, retllval); } diff --git a/tests/mini/asm8.d b/tests/mini/asm8.d index aab13cf7..c10e14e2 100644 --- a/tests/mini/asm8.d +++ b/tests/mini/asm8.d @@ -131,7 +131,10 @@ extern(C) cfloat cf_C() { version(X86) { - asm { fld1; fld two_f; } + asm { + mov EAX, [one_f]; + mov EDX, [two_f]; + } } else version (X86_64) { @@ -180,8 +183,8 @@ extern(C) cfloat cf2_C() asm { naked; - fld1; - fld two_f; + mov EAX, [one_f]; + mov EDX, [two_f]; ret; } }