Support for Windows x64 ABI.

This commit is contained in:
Martin
2013-02-01 03:16:28 +01:00
parent 0d0608a309
commit 51b45675cc
5 changed files with 309 additions and 54 deletions

View File

@@ -71,6 +71,62 @@ struct X87_complex_swap : ABIRewrite
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// Rewrites a cfloat (2x32 bits) as 64-bit integer.
// Assumes a little-endian byte order.
struct CfloatToInt : ABIRewrite
{
// i64 -> {float,float}
LLValue* get(Type*, DValue* dv)
{
LLValue* in = dv->getRVal();
// extract real part
LLValue* rpart = gIR->ir->CreateTrunc(in, LLType::getInt32Ty(gIR->context()));
rpart = gIR->ir->CreateBitCast(rpart, LLType::getFloatTy(gIR->context()), ".re");
// extract imag part
LLValue* ipart = gIR->ir->CreateLShr(in, LLConstantInt::get(LLType::getInt64Ty(gIR->context()), 32, false));
ipart = gIR->ir->CreateTrunc(ipart, LLType::getInt32Ty(gIR->context()));
ipart = gIR->ir->CreateBitCast(ipart, LLType::getFloatTy(gIR->context()), ".im");
// return {float,float} aggr pair with same bits
return DtoAggrPair(rpart, ipart, ".final_cfloat");
}
// {float,float} -> i64
LLValue* put(Type*, DValue* dv)
{
LLValue* v = dv->getRVal();
// extract real
LLValue* r = gIR->ir->CreateExtractValue(v, 0);
// cast to i32
r = gIR->ir->CreateBitCast(r, LLType::getInt32Ty(gIR->context()));
// zext to i64
r = gIR->ir->CreateZExt(r, LLType::getInt64Ty(gIR->context()));
// extract imag
LLValue* i = gIR->ir->CreateExtractValue(v, 1);
// cast to i32
i = gIR->ir->CreateBitCast(i, LLType::getInt32Ty(gIR->context()));
// zext to i64
i = gIR->ir->CreateZExt(i, LLType::getInt64Ty(gIR->context()));
// shift up
i = gIR->ir->CreateShl(i, LLConstantInt::get(LLType::getInt64Ty(gIR->context()), 32, false));
// combine and return
return v = gIR->ir->CreateOr(r, i);
}
// {float,float} -> i64
LLType* type(Type*, LLType* t)
{
return LLType::getInt64Ty(gIR->context());
}
};
//////////////////////////////////////////////////////////////////////////////
/** /**
* Rewrites a composite type parameter to an integer of the same size. * Rewrites a composite type parameter to an integer of the same size.
* *

223
gen/abi-win64.cpp Normal file
View File

@@ -0,0 +1,223 @@
//===-- abi-win64.cpp -----------------------------------------------------===//
//
// LDC the LLVM D compiler
//
// This file is distributed under the BSD-style LDC license. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//
//
// extern(C) implements the C calling convention for x86-64 on Windows, see
// http://msdn.microsoft.com/en-us/library/7kcdt6fy%28v=vs.110%29.aspx
//
//===----------------------------------------------------------------------===//
#include "mtype.h"
#include "declaration.h"
#include "aggregate.h"
#include "gen/irstate.h"
#include "gen/llvm.h"
#include "gen/tollvm.h"
#include "gen/logger.h"
#include "gen/dvalue.h"
#include "gen/llvmhelpers.h"
#include "gen/abi.h"
#include "gen/abi-win64.h"
#include "gen/abi-generic.h"
#include "ir/irfunction.h"
#include <cassert>
#include <string>
#include <utility>
// Returns true if the D type can be bit-cast to an integer of the same size.
static bool canRewriteAsInt(Type* t)
{
unsigned size = t->size();
return size <= 8 &&
(size == 1 || size == 2 || size == 4 || size == 8) &&
(t->ty == Tstruct || t->ty == Tsarray);
}
// FIXME: This should actually be handled by LLVM and the ByVal arg attribute.
// Implements byval argument passing for scalar non-struct types.
struct Win64_byval_rewrite : ABIRewrite
{
// Get instance from pointer.
LLValue* get(Type* dty, DValue* v)
{
LLValue* ptr = v->getRVal();
return DtoLoad(ptr); // *ptr
}
// Get instance from pointer, and store in the provided location.
void getL(Type* dty, DValue* v, llvm::Value* lval)
{
LLValue* ptr = v->getRVal();
DtoStore(DtoLoad(ptr), lval); // *lval = *ptr
}
// Turn an instance into a pointer (to a private copy for the callee,
// allocated by the caller).
LLValue* put(Type* dty, DValue* v)
{
/* NOTE: probably not safe
// optimization: do not copy if parameter is not mutable
if (!dty->isMutable() && v->isLVal())
return v->getLVal();
*/
LLValue* original = v->getRVal();
LLValue* copy = DtoRawAlloca(original->getType(), 16, "copy_for_callee");
DtoStore(original, copy); // *copy = *original
return copy;
}
/// should return the transformed type for this rewrite
LLType* type(Type* dty, LLType* t)
{
return getPtrToType(DtoType(dty));
}
};
struct Win64TargetABI : TargetABI
{
Win64_byval_rewrite byval_rewrite;
CompositeToInt compositeToInt;
CfloatToInt cfloatToInt;
X87_complex_swap swapComplex;
bool returnInArg(TypeFunction* tf);
bool passByVal(Type* t);
void rewriteFunctionType(TypeFunction* tf);
};
// The public getter for abi.cpp
TargetABI* getWin64TargetABI()
{
return new Win64TargetABI;
}
bool Win64TargetABI::returnInArg(TypeFunction* tf)
{
#if DMDV2
if (tf->isref)
return false;
#endif
Type* rt = tf->next->toBasetype();
// everything <= 64 bits and of a size that is a power of 2
// is returned in a register (RAX, or XMM0 for single float/
// double) - except for cfloat
// real/ireal is returned on top of the x87 stack: ST(0)
// complex numbers are returned in XMM0 & XMM1 (cfloat, cdouble)
// or ST(1) & ST(0) (creal)
// all other structs and static arrays are returned by struct-return (sret)
return (rt->ty == Tstruct
#if SARRAYVALUE
|| rt->ty == Tsarray
#endif
) && !canRewriteAsInt(rt);
}
bool Win64TargetABI::passByVal(Type* t)
{
t = t->toBasetype();
// structs and static arrays are passed byval unless they can be
// rewritten as integers
return (t->ty == Tstruct || t->ty == Tsarray) && !canRewriteAsInt(t);
}
void Win64TargetABI::rewriteFunctionType(TypeFunction* tf)
{
IrFuncTy& fty = tf->fty;
Type* rt = fty.ret->type->toBasetype();
// RETURN VALUE
#if DMDV2
if (!tf->isref)
#endif
{
if (rt->ty == Tcomplex80)
{
// LLVM returns a '{real re, ireal im}' via ST(0) = re and ST(1) = im
// DMD does it the other way around: ST(0) = im and ST(1) = re
// therefore swap the real/imaginary parts
// the other complex number types are returned via XMM0 = re and XMM1 = im
fty.ret->rewrite = &swapComplex;
}
else if (canRewriteAsInt(rt))
{
fty.ret->rewrite = &compositeToInt;
fty.ret->ltype = compositeToInt.type(fty.ret->type, fty.ret->ltype);
}
}
// IMPLICIT PARAMETERS
// EXPLICIT PARAMETERS
for (IrFuncTy::ArgRIter I = fty.args.rbegin(), E = fty.args.rend(); I != E; ++I)
{
IrFuncTyArg& arg = **I;
if (arg.byref)
continue;
Type* ty = arg.type->toBasetype();
unsigned sz = ty->size();
if (ty->ty == Tcomplex32)
{
// {float,float} cannot be bit-cast to int64 (using CompositeToInt)
// FIXME: is there a way to force a bit-cast?
arg.rewrite = &cfloatToInt;
arg.ltype = cfloatToInt.type(arg.type, arg.ltype);
}
else if (canRewriteAsInt(ty))
{
arg.rewrite = &compositeToInt;
arg.ltype = compositeToInt.type(arg.type, arg.ltype);
}
else if (ty->iscomplex() || ty->ty == Tfloat80 || ty->ty == Timaginary80)
{
// these types are passed byval:
// the caller allocates a copy and then passes a pointer to the copy
// FIXME: use tightly packed struct for creal like DMD?
arg.rewrite = &byval_rewrite;
arg.ltype = byval_rewrite.type(arg.type, arg.ltype);
// the copy is treated as a local variable of the callee
// hence add the NoAlias and NoCapture attributes
#if LDC_LLVM_VER >= 303
arg.attrs.clear();
arg.attrs.addAttribute(llvm::Attribute::NoAlias)
.addAttribute(llvm::Attribute::NoCapture);
#elif LDC_LLVM_VER == 302
arg.attrs = llvm::Attributes::get(gIR->context(), llvm::AttrBuilder().addAttribute(llvm::Attributes::NoAlias)
.addAttribute(llvm::Attributes::NoCapture));
#else
arg.attrs = llvm::Attribute::NoAlias | llvm::Attribute::NoCapture;
#endif
}
}
if (tf->linkage == LINKd)
{
// reverse parameter order
// for non variadics
if (fty.args.size() > 1 && tf->varargs != 1)
fty.reverseParams = true;
}
}

24
gen/abi-win64.h Normal file
View File

@@ -0,0 +1,24 @@
//===-- gen/abi-win64.h - Windows x86_64 ABI description --------*- C++ -*-===//
//
// LDC the LLVM D compiler
//
// This file is distributed under the BSD-style LDC license. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//
//
// The ABI implementation used for 64 bit x86 (i.e. x86_64/AMD64/x64) targets
// on Windows.
//
//===----------------------------------------------------------------------===//
#ifndef __LDC_GEN_ABI_WIN64_H__
#define __LDC_GEN_ABI_WIN64_H__
#include "gen/abi.h"
TargetABI* getWin64TargetABI();
#endif

View File

@@ -19,63 +19,11 @@
#include "ir/irfunction.h" #include "ir/irfunction.h"
#include "ir/irfuncty.h" #include "ir/irfuncty.h"
struct X86_cfloat_rewrite : ABIRewrite
{
// i64 -> {float,float}
LLValue* get(Type*, DValue* dv)
{
LLValue* in = dv->getRVal();
// extract real part
LLValue* rpart = gIR->ir->CreateTrunc(in, LLType::getInt32Ty(gIR->context()));
rpart = gIR->ir->CreateBitCast(rpart, LLType::getFloatTy(gIR->context()), ".re");
// extract imag part
LLValue* ipart = gIR->ir->CreateLShr(in, LLConstantInt::get(LLType::getInt64Ty(gIR->context()), 32, false));
ipart = gIR->ir->CreateTrunc(ipart, LLType::getInt32Ty(gIR->context()));
ipart = gIR->ir->CreateBitCast(ipart, LLType::getFloatTy(gIR->context()), ".im");
// return {float,float} aggr pair with same bits
return DtoAggrPair(rpart, ipart, ".final_cfloat");
}
// {float,float} -> i64
LLValue* put(Type*, DValue* dv)
{
LLValue* v = dv->getRVal();
// extract real
LLValue* r = gIR->ir->CreateExtractValue(v, 0);
// cast to i32
r = gIR->ir->CreateBitCast(r, LLType::getInt32Ty(gIR->context()));
// zext to i64
r = gIR->ir->CreateZExt(r, LLType::getInt64Ty(gIR->context()));
// extract imag
LLValue* i = gIR->ir->CreateExtractValue(v, 1);
// cast to i32
i = gIR->ir->CreateBitCast(i, LLType::getInt32Ty(gIR->context()));
// zext to i64
i = gIR->ir->CreateZExt(i, LLType::getInt64Ty(gIR->context()));
// shift up
i = gIR->ir->CreateShl(i, LLConstantInt::get(LLType::getInt64Ty(gIR->context()), 32, false));
// combine and return
return v = gIR->ir->CreateOr(r, i);
}
// {float,float} -> i64
LLType* type(Type*, LLType* t)
{
return LLType::getInt64Ty(gIR->context());
}
};
struct X86TargetABI : TargetABI struct X86TargetABI : TargetABI
{ {
X87_complex_swap swapComplex; X87_complex_swap swapComplex;
X86_cfloat_rewrite cfloatToInt; CfloatToInt cfloatToInt;
CompositeToInt compositeToInt; CompositeToInt compositeToInt;
bool returnInArg(TypeFunction* tf) bool returnInArg(TypeFunction* tf)

View File

@@ -11,6 +11,7 @@
#include "mars.h" #include "mars.h"
#include "gen/abi-generic.h" #include "gen/abi-generic.h"
#include "gen/abi-ppc64.h" #include "gen/abi-ppc64.h"
#include "gen/abi-win64.h"
#include "gen/abi-x86-64.h" #include "gen/abi-x86-64.h"
#include "gen/abi-x86.h" #include "gen/abi-x86.h"
#include "gen/dvalue.h" #include "gen/dvalue.h"
@@ -71,7 +72,10 @@ TargetABI * TargetABI::getTarget()
case llvm::Triple::x86: case llvm::Triple::x86:
return getX86TargetABI(); return getX86TargetABI();
case llvm::Triple::x86_64: case llvm::Triple::x86_64:
return getX86_64TargetABI(); if (global.params.targetTriple.isOSWindows())
return getWin64TargetABI();
else
return getX86_64TargetABI();
case llvm::Triple::ppc64: case llvm::Triple::ppc64:
return getPPC64TargetABI(); return getPPC64TargetABI();
default: default: