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

Follow in footsteps of 5e3785159f
,
allowing opCmp to behave as expected.
This allows runnable/test12.d and test5854.d to pass.
144 lines
5.3 KiB
C++
144 lines
5.3 KiB
C++
//===-- abi-arm.cpp ---------------------------------------------------===//
|
||
//
|
||
// LDC – the LLVM D compiler
|
||
//
|
||
// This file is distributed under the BSD-style LDC license. See the LICENSE
|
||
// file for details.
|
||
//
|
||
//===----------------------------------------------------------------------===//
|
||
|
||
/*
|
||
ARM ABI based on AAPCS (Procedure Call Standard for the ARM Architecture)
|
||
|
||
http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042f/IHI0042F_aapcs.pdf
|
||
*/
|
||
|
||
#include "gen/abi.h"
|
||
#include "gen/abi-generic.h"
|
||
#include "gen/abi-arm.h"
|
||
|
||
namespace {
|
||
struct CompositeToArray32 : ABIRewrite {
|
||
LLValue *get(Type *dty, LLValue *v) override {
|
||
Logger::println("rewriting i32 array -> as %s", dty->toChars());
|
||
LLValue *lval = DtoRawAlloca(v->getType(), 0);
|
||
DtoStore(v, lval);
|
||
|
||
LLType *pTy = getPtrToType(DtoType(dty));
|
||
return DtoLoad(DtoBitCast(lval, pTy), "get-result");
|
||
}
|
||
|
||
LLValue *put(DValue *dv) override {
|
||
Type *dty = dv->getType();
|
||
Logger::println("rewriting %s -> as i32 array", dty->toChars());
|
||
LLType *t = type(dty, nullptr);
|
||
return DtoLoad(DtoBitCast(dv->getRVal(), getPtrToType(t)));
|
||
}
|
||
|
||
LLType *type(Type *t, LLType *) override {
|
||
// An i32 array that will hold Type 't'
|
||
size_t sz = (t->size() + 3) / 4;
|
||
return LLArrayType::get(LLIntegerType::get(gIR->context(), 32), sz);
|
||
}
|
||
};
|
||
}
|
||
|
||
struct ArmTargetABI : TargetABI {
|
||
HFAToArray hfaToArray;
|
||
CompositeToArray32 compositeToArray32;
|
||
IntegerRewrite integerRewrite;
|
||
|
||
bool returnInArg(TypeFunction *tf) override {
|
||
// AAPCS 5.4 wants composites > 4-bytes returned by arg except for
|
||
// Homogeneous Aggregates of up-to 4 float types (6.1.2.1) - an HFA.
|
||
// TODO: see if Tsarray should be candidate for HFA.
|
||
if (tf->isref)
|
||
return false;
|
||
Type *rt = tf->next->toBasetype();
|
||
|
||
// For extern(D), always return structs by arg because of problem with
|
||
// non-POD structs (failure in std.algorithm.move when struct has a ctor).
|
||
// TODO: figure out what the problem is
|
||
if (tf->linkage == LINKd)
|
||
return rt->ty == Tsarray || rt->ty == Tstruct;
|
||
|
||
return rt->ty == Tsarray ||
|
||
(rt->ty == Tstruct && rt->size() > 4 && !isHFA((TypeStruct *)rt));
|
||
}
|
||
|
||
bool passByVal(Type *t) override {
|
||
// AAPCS does not use an indirect arg to pass aggregates, however
|
||
// clang uses byval for types > 64-bytes, then llvm backend
|
||
// converts back to non-byval. Without this special handling the
|
||
// optimzer generates bad code (e.g. std.random unittest crash).
|
||
t = t->toBasetype();
|
||
return ((t->ty == Tsarray || t->ty == Tstruct) && t->size() > 64);
|
||
|
||
// Note: byval can have a codegen problem with -O1 and higher.
|
||
// What happens is that load instructions are being incorrectly
|
||
// reordered before stores. It is a problem in the LLVM backend.
|
||
// The outcome is a program with incorrect results or crashes.
|
||
// It happens in the "top-down list latency scheduler" pass
|
||
//
|
||
// https://forum.dlang.org/post/m2r3u5ac0c.fsf@comcast.net
|
||
//
|
||
// Revist and determine if the byval problem is only for small
|
||
// structs, say 16-bytes or less, that can entirely fit in
|
||
// registers.
|
||
|
||
// Note: the codegen is horrible for Tsarrays passed this way -
|
||
// does a copy without a loop for huge arrays. Could be better if
|
||
// byval was always used for sarrays, and maybe can if above
|
||
// problem is better understood.
|
||
}
|
||
|
||
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;
|
||
} else {
|
||
fty.ret->rewrite = &integerRewrite;
|
||
fty.ret->ltype = integerRewrite.type(fty.ret->type, fty.ret->ltype);
|
||
}
|
||
}
|
||
|
||
for (auto arg : fty.args) {
|
||
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 rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg) override {
|
||
// structs and arrays need rewrite as i32 arrays. This keeps data layout
|
||
// unchanged when passed in registers r0-r3 and is necessary to match C ABI
|
||
// for struct passing. Without out this rewrite, each field or array
|
||
// element is passed in own register. For example: char[4] now all fits in
|
||
// r0, where before it consumed r0-r3.
|
||
Type *ty = arg.type->toBasetype();
|
||
|
||
// TODO: want to also rewrite Tsarray as i32 arrays, but sometimes
|
||
// llvm selects an aligned ldrd instruction even though the ptr is
|
||
// unaligned (e.g. walking through members of array char[5][]).
|
||
// if (ty->ty == Tstruct || ty->ty == Tsarray)
|
||
if (ty->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 *)ty, &arg.ltype)) {
|
||
arg.rewrite = &hfaToArray;
|
||
} else {
|
||
arg.rewrite = &compositeToArray32;
|
||
arg.ltype = compositeToArray32.type(arg.type, arg.ltype);
|
||
}
|
||
}
|
||
}
|
||
};
|
||
|
||
TargetABI *getArmTargetABI() { return new ArmTargetABI; }
|