mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-12 22:14:54 +03:00
186 lines
5.6 KiB
C++
186 lines
5.6 KiB
C++
//===-- abi-aarch64.cpp ---------------------------------------------------===//
|
||
//
|
||
// LDC – the LLVM D compiler
|
||
//
|
||
// This file is distributed under the BSD-style LDC license. See the LICENSE
|
||
// file for details.
|
||
//
|
||
//===----------------------------------------------------------------------===//
|
||
//
|
||
// The AArch64 Procedure Call Standard (AAPCS64) can be found here:
|
||
// https://github.com/ARM-software/abi-aa/blob/master/aapcs64/aapcs64.rst
|
||
//
|
||
//===----------------------------------------------------------------------===//
|
||
|
||
#include "gen/abi-aarch64.h"
|
||
|
||
#include "dmd/identifier.h"
|
||
#include "dmd/nspace.h"
|
||
#include "gen/abi.h"
|
||
#include "gen/abi-generic.h"
|
||
|
||
/**
|
||
* AAPCS64 uses a special native va_list type, a struct aliased as
|
||
* object.__va_list in druntime. Apple diverges and uses a simple char*
|
||
* pointer.
|
||
* __va_list arguments are never passed by value, only by reference (even though
|
||
* the mangled function name indicates otherwise!). This requires a little bit
|
||
* of compiler magic in the following implementations.
|
||
*/
|
||
struct AArch64TargetABI : TargetABI {
|
||
private:
|
||
const bool isDarwin;
|
||
IndirectByvalRewrite indirectByvalRewrite;
|
||
ArgTypesRewrite argTypesRewrite;
|
||
|
||
bool isAAPCS64VaList(Type *t) {
|
||
if (isDarwin)
|
||
return false;
|
||
|
||
// look for a __va_list struct in a `std` C++ namespace
|
||
if (auto ts = t->isTypeStruct()) {
|
||
auto sd = ts->sym;
|
||
if (strcmp(sd->ident->toChars(), "__va_list") == 0) {
|
||
if (auto ns = sd->parent->isNspace()) {
|
||
return strcmp(ns->toChars(), "std") == 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
public:
|
||
AArch64TargetABI() : isDarwin(global.params.targetTriple->isOSDarwin()) {}
|
||
|
||
bool returnInArg(TypeFunction *tf, bool) override {
|
||
if (tf->isref()) {
|
||
return false;
|
||
}
|
||
|
||
Type *rt = tf->next->toBasetype();
|
||
if (rt->ty == TY::Tstruct || rt->ty == TY::Tsarray) {
|
||
auto argTypes = getArgTypes(rt);
|
||
return !argTypes // FIXME: get rid of sret workaround for 0-sized return
|
||
// values (static arrays with 0 elements)
|
||
|| argTypes->arguments->empty();
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
// Prefer a ref if the POD cannot be passed in registers, i.e., if
|
||
// IndirectByvalRewrite would be applied.
|
||
bool preferPassByRef(Type *t) override {
|
||
t = t->toBasetype();
|
||
|
||
if (!(t->ty == TY::Tstruct || t->ty == TY::Tsarray))
|
||
return false;
|
||
|
||
auto argTypes = getArgTypes(t);
|
||
return argTypes // not 0-sized
|
||
&& argTypes->arguments->empty(); // cannot be passed in registers
|
||
}
|
||
|
||
bool passByVal(TypeFunction *, Type *) override { return false; }
|
||
|
||
void rewriteFunctionType(IrFuncTy &fty) override {
|
||
if (!skipReturnValueRewrite(fty)) {
|
||
rewriteArgument(fty, *fty.ret, /*isReturnVal=*/true);
|
||
}
|
||
|
||
for (auto arg : fty.args) {
|
||
if (!arg->byref)
|
||
rewriteArgument(fty, *arg, /*isReturnVal=*/false);
|
||
}
|
||
|
||
// remove 0-sized args (static arrays with 0 elements) and, for Darwin,
|
||
// empty POD structs too
|
||
size_t i = 0;
|
||
while (i < fty.args.size()) {
|
||
auto arg = fty.args[i];
|
||
if (!arg->byref) {
|
||
auto tb = arg->type->toBasetype();
|
||
|
||
if (tb->size() == 0) {
|
||
fty.args.erase(fty.args.begin() + i);
|
||
continue;
|
||
}
|
||
|
||
// https://developer.apple.com/library/archive/documentation/Xcode/Conceptual/iPhoneOSABIReference/Articles/ARM64FunctionCallingConventions.html#//apple_ref/doc/uid/TP40013702-SW1
|
||
if (isDarwin) {
|
||
if (auto ts = tb->isTypeStruct()) {
|
||
if (ts->sym->fields.empty() && ts->sym->isPOD()) {
|
||
fty.args.erase(fty.args.begin() + i);
|
||
continue;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
++i;
|
||
}
|
||
}
|
||
|
||
void rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg) override {
|
||
return rewriteArgument(fty, arg, /*isReturnVal=*/false);
|
||
}
|
||
|
||
void rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg, bool isReturnVal) {
|
||
Type *t = arg.type->toBasetype();
|
||
|
||
if (!isAggregate(t))
|
||
return;
|
||
|
||
// compiler magic: pass va_list args implicitly by reference
|
||
if (!isReturnVal && isAAPCS64VaList(t)) {
|
||
arg.byref = true;
|
||
arg.ltype = arg.ltype->getPointerTo();
|
||
return;
|
||
}
|
||
|
||
auto argTypes = getArgTypes(t);
|
||
if (!argTypes)
|
||
return; // don't rewrite 0-sized types
|
||
|
||
if (argTypes->arguments->empty()) {
|
||
// non-PODs and larger non-HFVA aggregates are passed as pointer to
|
||
// hidden copy
|
||
indirectByvalRewrite.applyTo(arg);
|
||
return;
|
||
}
|
||
|
||
// LLVM seems to take care of the rest when rewriting as follows, close to
|
||
// what clang emits:
|
||
|
||
auto rewrittenType = getRewrittenArgType(t, argTypes);
|
||
if (!rewrittenType)
|
||
return;
|
||
|
||
if (rewrittenType->isIntegerTy()) {
|
||
argTypesRewrite.applyToIfNotObsolete(arg, rewrittenType);
|
||
} else {
|
||
// in most cases, a LL array of either floats/vectors (HFVAs) or i64
|
||
argTypesRewrite.applyTo(arg, rewrittenType);
|
||
}
|
||
}
|
||
|
||
Type *vaListType() override {
|
||
if (isDarwin)
|
||
return TargetABI::vaListType(); // char*
|
||
|
||
// We need to pass the actual va_list type for correct mangling. Simply
|
||
// using TypeIdentifier here is a bit wonky but works, as long as the name
|
||
// is actually available in the scope (this is what DMD does, so if a
|
||
// better solution is found there, this should be adapted).
|
||
return TypeIdentifier::create(Loc(), Identifier::idPool("__va_list"));
|
||
}
|
||
|
||
const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty) override {
|
||
// see objc/message.h for objc_msgSend selection rules
|
||
return "objc_msgSend";
|
||
}
|
||
};
|
||
|
||
// The public getter for abi.cpp
|
||
TargetABI *getAArch64TargetABI() { return new AArch64TargetABI(); }
|