ldc/gen/abi-win64.cpp
Martin Kinkelin 5451a072a8
Refactoring: Free TargetABI from legacy magic C++ structs (#3073)
Which have been superseded by magic enums (in core.stdc.config).
2019-07-01 21:48:01 +02:00

184 lines
5.9 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//===-- 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 "gen/abi-win64.h"
#include "dmd/mtype.h"
#include "dmd/declaration.h"
#include "dmd/aggregate.h"
#include "gen/abi.h"
#include "gen/abi-generic.h"
#include "gen/dvalue.h"
#include "gen/irstate.h"
#include "gen/llvm.h"
#include "gen/llvmhelpers.h"
#include "gen/logger.h"
#include "gen/tollvm.h"
#include "ir/irfunction.h"
#include <cassert>
#include <string>
#include <utility>
struct Win64TargetABI : TargetABI {
private:
const bool isMSVC;
IndirectByvalRewrite byvalRewrite;
IntegerRewrite integerRewrite;
bool isX87(Type *t) const {
return !isMSVC // 64-bit reals for MSVC targets
&& (t->ty == Tfloat80 || t->ty == Timaginary80);
}
bool passPointerToHiddenCopy(Type *t, bool isReturnValue, LINK linkage) const {
// 80-bit real/ireal:
// * returned on the x87 stack (for DMD inline asm compliance and what LLVM
// defaults to)
// * passed by ref to hidden copy
if (isX87(t))
return !isReturnValue;
const bool isMSVCpp = isMSVC && linkage == LINKcpp;
if (isReturnValue) {
// MSVC++ enforces sret for non-PODs, incl. aggregates with ctors
// (which by itself doesn't make it a non-POD for D).
const bool excludeStructsWithCtor = isMSVCpp;
if (!isPOD(t, excludeStructsWithCtor))
return true;
} else {
// Contrary to return values, POD-ness is ignored for arguments.
// MSVC++ seems to enforce by-ref passing only for aggregates with
// copy ctor (incl. `= delete`).
if (isMSVCpp && t->ty == Tstruct) {
StructDeclaration *sd = static_cast<TypeStruct *>(t)->sym;
assert(sd);
if (sd->postblit || sd->hasCopyCtor)
return true;
}
}
// Remaining aggregates which can NOT be rewritten as integers (size > 8
// bytes or not a power of 2) are passed by ref to hidden copy.
return isAggregate(t) && !canRewriteAsInt(t);
}
public:
Win64TargetABI()
: isMSVC(global.params.targetTriple->isWindowsMSVCEnvironment()) {}
llvm::CallingConv::ID callingConv(LINK l, TypeFunction *tf = nullptr,
FuncDeclaration *fd = nullptr) override {
// Use the vector calling convention for extern(D) (except for variadics)
// => let LLVM pass vectors in registers instead of passing a ref to a
// hidden copy (both cases handled by LLVM automatically for LL vectors
// which we don't rewrite).
return l == LINKd && !(tf && tf->parameterList.varargs == VARARGvariadic)
? llvm::CallingConv::X86_VectorCall
: llvm::CallingConv::C;
}
std::string mangleFunctionForLLVM(std::string name, LINK l) override {
if (l == LINKd) {
// Prepend a 0x1 byte to prevent LLVM from applying vectorcall/stdcall
// mangling: _D… => _D…@<paramssize>
name.insert(name.begin(), '\1');
}
return name;
}
bool returnInArg(TypeFunction *tf, bool needsThis) override {
if (tf->isref)
return false;
Type *rt = tf->next->toBasetype();
// for non-static member functions, MSVC++ enforces sret for all structs
if (isMSVC && tf->linkage == LINKcpp && needsThis && rt->ty == Tstruct) {
return true;
}
// * all POD types of a power-of-2 size <= 8 bytes (incl. 2x32-bit cfloat)
// are returned in a register (RAX, or XMM0 for single float/ifloat/
// double/idouble)
// * 80-bit real/ireal are returned on the x87 stack
// * all other types are returned via sret
return passPointerToHiddenCopy(rt, /*isReturnValue=*/true, tf->linkage);
}
bool passByVal(TypeFunction *, Type *) override {
// LLVM's byval attribute is not compatible with the Win64 ABI
return false;
}
bool passThisBeforeSret(TypeFunction *tf) override {
// required by MSVC++
return tf->linkage == LINKcpp;
}
void rewriteFunctionType(IrFuncTy &fty) override {
// return value
const auto rt = fty.ret->type->toBasetype();
if (!fty.ret->byref && rt->ty != Tvoid) {
rewrite(fty, *fty.ret, /*isReturnValue=*/true);
}
// explicit parameters
for (auto arg : fty.args) {
if (!arg->byref) {
rewriteArgument(fty, *arg);
}
}
}
void rewriteVarargs(IrFuncTy &fty,
std::vector<IrFuncTyArg *> &args) override {
for (auto arg : args) {
rewriteArgument(fty, *arg);
if (arg->rewrite == &byvalRewrite) {
// mark the vararg as being passed byref to prevent DtoCall() from
// passing the dereferenced pointer, i.e., just pass the pointer
arg->byref = true;
}
}
}
void rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg) override {
rewrite(fty, arg, /*isReturnValue=*/false);
}
void rewrite(IrFuncTy &fty, IrFuncTyArg &arg, bool isReturnValue) {
Type *t = arg.type->toBasetype();
LLType *originalLType = arg.ltype;
if (passPointerToHiddenCopy(t, isReturnValue, fty.type->linkage)) {
// the caller allocates a hidden copy and passes a pointer to that copy
byvalRewrite.applyTo(arg);
} else if (isAggregate(t) && canRewriteAsInt(t)) {
integerRewrite.applyToIfNotObsolete(arg);
}
if (arg.rewrite) {
IF_LOG {
Logger::println("Rewriting argument type %s", t->toChars());
LOG_SCOPE;
Logger::cout() << *originalLType << " => " << *arg.ltype << '\n';
}
}
}
};
// The public getter for abi.cpp
TargetABI *getWin64TargetABI() { return new Win64TargetABI; }