mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-02 08:01:11 +03:00

0.15.1 used stdcall convention everywhere. But the ABI change in 0.15.2 requires use of the C calling convention for variadic calls on x86 because the stdcall convention does not support vararg functions. Solution is to check the llvm::FunctionType if the function has varargs. This commit also makes the C calling convention the global default because according to the LLVM documentation the fastcc convention used for D linkage does not support varargs. This fixes issue #1000.
170 lines
4.9 KiB
C++
170 lines
4.9 KiB
C++
//===-- 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>
|
||
|
||
struct Win64TargetABI : TargetABI
|
||
{
|
||
ExplicitByvalRewrite byvalRewrite;
|
||
IntegerRewrite integerRewrite;
|
||
|
||
bool returnInArg(TypeFunction* tf);
|
||
|
||
bool passByVal(Type* t);
|
||
|
||
void rewriteFunctionType(TypeFunction* tf, IrFuncTy &fty);
|
||
|
||
void rewriteArgument(IrFuncTy& fty, IrFuncTyArg& arg);
|
||
|
||
private:
|
||
// Returns true if the D type is an aggregate:
|
||
// * struct
|
||
// * static/dynamic array
|
||
// * delegate
|
||
// * complex number
|
||
bool isAggregate(Type* t)
|
||
{
|
||
TY ty = t->ty;
|
||
return ty == Tstruct || ty == Tsarray || /*ty == Tarray ||*/ ty == Tdelegate
|
||
|| t->iscomplex();
|
||
}
|
||
|
||
// Returns true if the D type can be bit-cast to an integer of the same size.
|
||
bool canRewriteAsInt(Type* t)
|
||
{
|
||
unsigned size = t->size();
|
||
return size == 1 || size == 2 || size == 4 || size == 8;
|
||
}
|
||
|
||
bool realIs80bits()
|
||
{
|
||
#if LDC_LLVM_VER >= 305
|
||
return !global.params.targetTriple.isWindowsMSVCEnvironment();
|
||
#else
|
||
return true;
|
||
#endif
|
||
}
|
||
|
||
// Returns true if the D type is passed byval (the callee getting a pointer
|
||
// to a dedicated hidden copy).
|
||
bool isPassedWithByvalSemantics(Type* t)
|
||
{
|
||
return
|
||
// * aggregates which can NOT be rewritten as integers
|
||
// (size > 64 bits or not a power of 2)
|
||
(isAggregate(t) && !canRewriteAsInt(t)) ||
|
||
// * 80-bit real and ireal
|
||
(realIs80bits() && (t->ty == Tfloat80 || t->ty == Timaginary80));
|
||
}
|
||
};
|
||
|
||
|
||
// The public getter for abi.cpp
|
||
TargetABI* getWin64TargetABI()
|
||
{
|
||
return new Win64TargetABI;
|
||
}
|
||
|
||
bool Win64TargetABI::returnInArg(TypeFunction* tf)
|
||
{
|
||
if (tf->isref)
|
||
return false;
|
||
|
||
Type* rt = tf->next->toBasetype();
|
||
|
||
// * let LLVM return 80-bit real/ireal on the x87 stack, for DMD compliance
|
||
if (realIs80bits() && (rt->ty == Tfloat80 || rt->ty == Timaginary80))
|
||
return false;
|
||
|
||
// * all POD types <= 64 bits and of a size that is a power of 2
|
||
// (incl. 2x32-bit cfloat) are returned in a register (RAX, or
|
||
// XMM0 for single float/ifloat/double/idouble)
|
||
// * all other types are returned via struct-return (sret)
|
||
return (rt->ty == Tstruct && !((TypeStruct*)rt)->sym->isPOD())
|
||
|| isPassedWithByvalSemantics(rt);
|
||
}
|
||
|
||
bool Win64TargetABI::passByVal(Type* t)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
void Win64TargetABI::rewriteFunctionType(TypeFunction* tf, IrFuncTy &fty)
|
||
{
|
||
// RETURN VALUE
|
||
if (!fty.ret->byref && fty.ret->type->toBasetype()->ty != Tvoid)
|
||
rewriteArgument(fty, *fty.ret);
|
||
|
||
// EXPLICIT PARAMETERS
|
||
for (IrFuncTy::ArgIter I = fty.args.begin(), E = fty.args.end(); I != E; ++I)
|
||
{
|
||
IrFuncTyArg& arg = **I;
|
||
|
||
if (!arg.byref)
|
||
rewriteArgument(fty, arg);
|
||
}
|
||
|
||
// extern(D): reverse parameter order for non variadics, for DMD-compliance
|
||
if (tf->linkage == LINKd && tf->varargs != 1 && fty.args.size() > 1)
|
||
fty.reverseParams = true;
|
||
}
|
||
|
||
void Win64TargetABI::rewriteArgument(IrFuncTy& fty, IrFuncTyArg& arg)
|
||
{
|
||
LLType* originalLType = arg.ltype;
|
||
Type* t = arg.type->toBasetype();
|
||
|
||
if (isPassedWithByvalSemantics(t))
|
||
{
|
||
// these types are passed byval:
|
||
// the caller allocates a copy and then passes a pointer to the copy
|
||
arg.rewrite = &byvalRewrite;
|
||
arg.ltype = byvalRewrite.type(arg.type, arg.ltype);
|
||
|
||
// the copy is treated as a local variable of the callee
|
||
// hence add the NoAlias and NoCapture attributes
|
||
arg.attrs.clear()
|
||
.add(LDC_ATTRIBUTE(NoAlias))
|
||
.add(LDC_ATTRIBUTE(NoCapture));
|
||
}
|
||
else if (isAggregate(t) && canRewriteAsInt(t) && !IntegerRewrite::isObsoleteFor(originalLType))
|
||
{
|
||
arg.rewrite = &integerRewrite;
|
||
arg.ltype = integerRewrite.type(arg.type, arg.ltype);
|
||
}
|
||
|
||
IF_LOG if (arg.rewrite)
|
||
{
|
||
Logger::println("Rewriting argument type %s", t->toChars());
|
||
LOG_SCOPE;
|
||
Logger::cout() << *originalLType << " => " << *arg.ltype << '\n';
|
||
}
|
||
}
|