mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-03 08:30:47 +03:00

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)
255 lines
7.3 KiB
C++
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;
|
|
}
|
|
}
|