1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
|
//===-- 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;
HFVAToArray hfvaToArray;
bool isX87(Type *t) const {
return !isMSVC // 64-bit reals for MSVC targets
&& (t->ty == TY::Tfloat80 || t->ty == TY::Timaginary80);
}
bool passPointerToHiddenCopy(Type *t, bool isReturnValue,
TypeFunction *tf) const {
return passPointerToHiddenCopy(t, isReturnValue, tf->linkage, tf);
}
bool passPointerToHiddenCopy(Type *t, bool isReturnValue, LINK linkage,
TypeFunction *tf = nullptr) 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 == LINK::cpp;
// Handle non-PODs:
if (isReturnValue) {
// Enforce sret for non-PODs.
// MSVC++ additionally enforces it for all structs with ctors.
if (!isPOD(t, isMSVCpp))
return true;
} else {
// MSVC++ seems to enforce by-ref passing only for structs with
// copy ctor (incl. `= delete`).
if (isMSVCpp) {
if (t->ty == TY::Tstruct) {
StructDeclaration *sd = static_cast<TypeStruct *>(t)->sym;
assert(sd);
if (sd->postblit || sd->hasCopyCtor)
return true;
}
}
// non-MSVC++: pass all non-PODs by ref to hidden copy
else if (!isPOD(t)) {
return true;
}
}
// __vectorcall: Homogeneous Vector Aggregates are passed in registers
const bool isD = tf ? isExternD(tf) : linkage == LINK::d;
if (isD && isHVA(t, hfvaToArray.maxElements))
return false;
// 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.
// LDC-specific exceptions: slices and delegates are left alone (as non-
// rewritten IR structs) and passed/returned as 2 separate args => passed in
// up to 2 GP registers and returned in RAX & RDX.
return isAggregate(t) && !canRewriteAsInt(t) && t->ty != TY::Tarray &&
t->ty != TY::Tdelegate;
}
public:
Win64TargetABI()
: isMSVC(global.params.targetTriple->isWindowsMSVCEnvironment()),
hfvaToArray(4) {}
// 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).
llvm::CallingConv::ID callingConv(LINK l) override {
return l == LINK::d ? llvm::CallingConv::X86_VectorCall
: llvm::CallingConv::C;
}
std::string mangleFunctionForLLVM(std::string name, LINK l) override {
if (l == LINK::d) {
// 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 == LINK::cpp && needsThis &&
rt->ty == 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
// * LDC-specific: slices and delegates are returned in RAX & RDX
// * for extern(D), vectors and Homogeneous Vector Aggregates are returned
// in SIMD register(s)
// * all other types are returned via sret
return passPointerToHiddenCopy(rt, /*isReturnValue=*/true, tf);
}
// Prefer a ref if the POD cannot be passed in a register, i.e., if
// IndirectByvalRewrite would be applied.
bool preferPassByRef(Type *t) override {
return passPointerToHiddenCopy(t->toBasetype(), false, LINK::d);
}
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 == LINK::cpp;
}
void rewriteFunctionType(IrFuncTy &fty) override {
// return value
if (!skipReturnValueRewrite(fty)) {
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)) {
// the caller allocates a hidden copy and passes a pointer to that copy
byvalRewrite.applyTo(arg);
} else if (isExternD(fty.type) && isHVA(t, hfvaToArray.maxElements, &arg.ltype)) {
// rewrite Homogeneous Vector Aggregates as static array of vectors
hfvaToArray.applyTo(arg, arg.ltype);
} 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; }
|