mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-04 09:00:33 +03:00
163 lines
5.8 KiB
C++
163 lines
5.8 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 Procedure Call Standard can be found here:
|
||
// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf
|
||
//
|
||
//===----------------------------------------------------------------------===//
|
||
|
||
#include "ldcbindings.h"
|
||
#include "gen/abi.h"
|
||
#include "gen/abi-generic.h"
|
||
#include "gen/abi-aarch64.h"
|
||
|
||
struct AArch64TargetABI : TargetABI {
|
||
HFAToArray hfaToArray;
|
||
CompositeToArray64 compositeToArray64;
|
||
IntegerRewrite integerRewrite;
|
||
|
||
bool returnInArg(TypeFunction *tf) override {
|
||
if (tf->isref) {
|
||
return false;
|
||
}
|
||
|
||
Type *rt = tf->next->toBasetype();
|
||
|
||
// FIXME
|
||
if (tf->linkage == LINKd)
|
||
return rt->ty == Tsarray || rt->ty == Tstruct;
|
||
|
||
return rt->ty == Tsarray ||
|
||
(rt->ty == Tstruct && rt->size() > 16 && !isHFA((TypeStruct *)rt));
|
||
}
|
||
|
||
bool passByVal(Type *t) override {
|
||
t = t->toBasetype();
|
||
return t->ty == Tsarray ||
|
||
(t->ty == Tstruct && t->size() > 16 && !isHFA((TypeStruct *)t));
|
||
}
|
||
|
||
void rewriteFunctionType(TypeFunction *tf, IrFuncTy &fty) override {
|
||
Type *retTy = fty.ret->type->toBasetype();
|
||
if (!fty.ret->byref && retTy->ty == Tstruct) {
|
||
// Rewrite HFAs only because union HFAs are turned into IR types that are
|
||
// non-HFA and messes up register selection
|
||
if (isHFA((TypeStruct *)retTy, &fty.ret->ltype)) {
|
||
fty.ret->rewrite = &hfaToArray;
|
||
fty.ret->ltype = hfaToArray.type(fty.ret->type);
|
||
}
|
||
else {
|
||
fty.ret->rewrite = &integerRewrite;
|
||
fty.ret->ltype = integerRewrite.type(fty.ret->type);
|
||
}
|
||
}
|
||
|
||
for (auto arg : fty.args) {
|
||
if (!arg->byref)
|
||
rewriteArgument(fty, *arg);
|
||
else if (passByVal(arg->type))
|
||
arg->attrs.remove(LLAttribute::ByVal);
|
||
}
|
||
}
|
||
|
||
void rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg) override {
|
||
// FIXME
|
||
Type *ty = arg.type->toBasetype();
|
||
|
||
if (ty->ty == Tstruct || ty->ty == Tsarray) {
|
||
// Rewrite HFAs only because union HFAs are turned into IR types that are
|
||
// non-HFA and messes up register selection
|
||
if (ty->ty == Tstruct && isHFA((TypeStruct *)ty, &arg.ltype)) {
|
||
arg.rewrite = &hfaToArray;
|
||
arg.ltype = hfaToArray.type(arg.type);
|
||
}
|
||
else {
|
||
arg.rewrite = &compositeToArray64;
|
||
arg.ltype = compositeToArray64.type(arg.type);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* The AACPS64 uses a special native va_list type:
|
||
*
|
||
* typedef struct __va_list {
|
||
* void *__stack; // next stack param
|
||
* void *__gr_top; // end of GP arg reg save area
|
||
* void *__vr_top; // end of FP/SIMD arg reg save area
|
||
* int __gr_offs; // offset from __gr_top to next GP register arg
|
||
* int __vr_offs; // offset from __vr_top to next FP/SIMD register arg
|
||
* } va_list;
|
||
*
|
||
* In druntime, the struct is defined as core.stdc.stdarg.__va_list; the
|
||
* actually used core.stdc.stdarg.va_list type is a raw char* pointer though to
|
||
* achieve byref semantics.
|
||
* This requires a little bit of compiler magic in the following
|
||
* implementations.
|
||
*/
|
||
|
||
LLType *getValistType() {
|
||
LLType *intType = LLType::getInt32Ty(gIR->context());
|
||
LLType *voidPointerType = getVoidPtrType();
|
||
|
||
std::vector<LLType *> parts; // struct __va_list {
|
||
parts.push_back(voidPointerType); // void *__stack;
|
||
parts.push_back(voidPointerType); // void *__gr_top;
|
||
parts.push_back(voidPointerType); // void *__vr_top;
|
||
parts.push_back(intType); // int __gr_offs;
|
||
parts.push_back(intType); // int __vr_offs; };
|
||
|
||
return LLStructType::get(gIR->context(), parts);
|
||
}
|
||
|
||
LLValue *prepareVaStart(DLValue *ap) override {
|
||
// Since the user only created a char* pointer (ap) on the stack before
|
||
// invoking va_start, we first need to allocate the actual __va_list struct
|
||
// and set `ap` to its address.
|
||
LLValue *valistmem = DtoRawAlloca(getValistType(), 0, "__va_list_mem");
|
||
DtoStore(valistmem,
|
||
DtoBitCast(DtoLVal(ap), getPtrToType(valistmem->getType())));
|
||
// Pass a i8* pointer to the actual struct to LLVM's va_start intrinsic.
|
||
return DtoBitCast(valistmem, getVoidPtrType());
|
||
}
|
||
|
||
void vaCopy(DLValue *dest, DValue *src) override {
|
||
// Analog to va_start, we first need to allocate a new __va_list struct on
|
||
// the stack and set `dest` to its address.
|
||
LLValue *valistmem = DtoRawAlloca(getValistType(), 0, "__va_list_mem");
|
||
DtoStore(valistmem,
|
||
DtoBitCast(DtoLVal(dest), getPtrToType(valistmem->getType())));
|
||
// Then fill the new struct with a bitcopy of the source struct.
|
||
// `src` is a char* pointer to the source struct.
|
||
DtoMemCpy(valistmem, DtoRVal(src));
|
||
}
|
||
|
||
LLValue *prepareVaArg(DLValue *ap) override {
|
||
// Pass a i8* pointer to the actual __va_list struct to LLVM's va_arg
|
||
// intrinsic.
|
||
return DtoRVal(ap);
|
||
}
|
||
|
||
Type *vaListType() override {
|
||
// 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).
|
||
static const llvm::StringRef ident = "__va_list";
|
||
return (createTypeIdentifier(Loc(), Identifier::idPool(ident.data(), ident.size())));
|
||
}
|
||
|
||
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(); }
|