ldc/gen/abi.cpp
Frits van Bommel 03ce6604a0 Fix cfloat return on x86_64: only perform ABI transformation for non-extern(D)
functions.
There's no need to waste cycles with extern(D), which we get to define
ourselves. Fixes tests/mini/asm8.d. (Since the asm abiret code already assumed
{xmm0, xmm1} returns)
2009-02-26 23:35:39 +01:00

255 lines
7.3 KiB
C++

#include "gen/llvm.h"
#include "mars.h"
#include "gen/irstate.h"
#include "gen/llvmhelpers.h"
#include "gen/tollvm.h"
#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;
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
///////////////////// X86 ////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// simply swap of real/imag parts for proper x87 complex abi
struct X87_complex_swap : ABIRetRewrite
{
LLValue* get(LLValue* v)
{
return DtoAggrPairSwap(v);
}
LLValue* put(LLValue* v)
{
return DtoAggrPairSwap(v);
}
const LLType* type(const LLType* t)
{
return t;
}
bool test(TypeFunction* tf)
{
return (tf->next->toBasetype()->iscomplex());
}
};
//////////////////////////////////////////////////////////////////////////////
struct X86TargetABI : TargetABI
{
X86TargetABI()
{
retOps.push_back(new X87_complex_swap);
}
bool returnInArg(Type* t)
{
Type* rt = t->toBasetype();
return (rt->ty == Tstruct);
}
bool passByRef(Type* t)
{
t = t->toBasetype();
return (t->ty == Tstruct || t->ty == Tsarray);
}
};
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
/////////////////// X86-64 //////////////////////////
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
struct X86_64_cfloat_rewrite : ABIRetRewrite
{
// {double} -> {float,float}
LLValue* get(LLValue* in)
{
// extract double
LLValue* v = gIR->ir->CreateExtractValue(in, 0);
// cast to i64
v = gIR->ir->CreateBitCast(v, LLType::Int64Ty);
// extract real part
LLValue* rpart = gIR->ir->CreateTrunc(v, LLType::Int32Ty);
rpart = gIR->ir->CreateBitCast(rpart, LLType::FloatTy, ".re");
// extract imag part
LLValue* ipart = gIR->ir->CreateLShr(v, 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} -> {double}
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
v = gIR->ir->CreateOr(r, i);
// cast to double
v = gIR->ir->CreateBitCast(v, LLType::DoubleTy);
// return {double}
const LLType* t = LLStructType::get(LLType::DoubleTy, NULL);
LLValue* undef = llvm::UndefValue::get(t);
return gIR->ir->CreateInsertValue(undef, v, 0);
}
// {float,float} -> {double}
const LLType* 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);
}
bool returnInArg(Type* t)
{
Type* rt = t->toBasetype();
return (rt->ty == Tstruct);
}
bool passByRef(Type* t)
{
t = t->toBasetype();
return (t->ty == Tstruct || t->ty == Tsarray);
}
};
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
/////////////////// Unknown targets //////////////////////////
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// Some reasonable defaults for when we don't know what ABI to use.
struct UnknownTargetABI : TargetABI
{
UnknownTargetABI()
{
// Don't push anything into retOps, assume defaults will be fine.
}
bool returnInArg(Type* t)
{
Type* rt = t->toBasetype();
return (rt->ty == Tstruct);
}
bool passByRef(Type* t)
{
t = t->toBasetype();
return (t->ty == Tstruct || t->ty == Tsarray);
}
};
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
TargetABI * TargetABI::getTarget()
{
switch(global.params.cpu)
{
case ARCHx86:
return new X86TargetABI;
case ARCHx86_64:
return new X86_64TargetABI;
default:
Logger::cout() << "WARNING: Unknown ABI, guessing...\n";
return new UnknownTargetABI;
}
}