ldc/gen/abi-arm.cpp
Dan Olson ad1bb14bd6 ARM: Re-reverse parameter order for extern(D)
Follow in footsteps of 5e3785159f,
allowing opCmp to behave as expected.

This allows runnable/test12.d and test5854.d to pass.
2016-02-25 19:17:02 +00:00

144 lines
5.3 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-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; }