Use IndirectByvalRewrite for non-POD args and extern(C++) on Posix (#2728)

Fixing one aspect of issue #2702; not tackling the different destruction
rules yet.
This commit is contained in:
Martin Kinkelin 2018-06-19 20:09:35 +02:00 committed by GitHub
parent f8880c6c38
commit 3ca43c51b8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 61 additions and 30 deletions

View file

@ -32,10 +32,10 @@ struct AArch64TargetABI : TargetABI {
if (!isPOD(rt))
return true;
return passByVal(rt);
return passByVal(tf, rt);
}
bool passByVal(Type *t) override {
bool passByVal(TypeFunction *, Type *t) override {
t = t->toBasetype();
return t->ty == Tsarray ||
(t->ty == Tstruct && t->size() > 16 && !isHFA((TypeStruct *)t));

View file

@ -42,7 +42,7 @@ struct ArmTargetABI : TargetABI {
!isHFA((TypeStruct *)rt)));
}
bool passByVal(Type *t) override {
bool passByVal(TypeFunction *, 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

View file

@ -42,7 +42,7 @@ struct MIPS64TargetABI : TargetABI {
return (rt->ty == Tstruct || rt->ty == Tsarray);
}
bool passByVal(Type *t) override {
bool passByVal(TypeFunction *, Type *t) override {
TY ty = t->toBasetype()->ty;
return ty == Tstruct || ty == Tsarray;
}

View file

@ -24,7 +24,7 @@ struct NVPTXTargetABI : TargetABI {
else
return llvm::CallingConv::PTX_Device;
}
bool passByVal(Type *t) override {
bool passByVal(TypeFunction *, Type *t) override {
t = t->toBasetype();
return ((t->ty == Tsarray || t->ty == Tstruct) && t->size() > 64);
}

View file

@ -49,7 +49,7 @@ struct PPCTargetABI : TargetABI {
return rt->ty == Tsarray || rt->ty == Tstruct;
}
bool passByVal(Type *t) override {
bool passByVal(TypeFunction *, Type *t) override {
// On ppc, aggregates are always passed as an indirect value.
// On ppc64, they are always passed by value. However, clang
// used byval for type > 64 bytes.

View file

@ -38,10 +38,10 @@ struct PPC64LETargetABI : TargetABI {
if (!isPOD(rt))
return true;
return passByVal(rt);
return passByVal(tf, rt);
}
bool passByVal(Type *t) override {
bool passByVal(TypeFunction *, Type *t) override {
t = t->toBasetype();
return t->ty == Tsarray || (t->ty == Tstruct && t->size() > 16 &&
!isHFA((TypeStruct *)t, nullptr, 8));

View file

@ -24,7 +24,7 @@ struct SPIRVTargetABI : TargetABI {
else
return llvm::CallingConv::SPIR_FUNC;
}
bool passByVal(Type *t) override {
bool passByVal(TypeFunction *, Type *t) override {
t = t->toBasetype();
return ((t->ty == Tsarray || t->ty == Tstruct) && t->size() > 64);
}

View file

@ -117,7 +117,7 @@ public:
return passPointerToHiddenCopy(rt, /*isReturnValue=*/true, tf->linkage);
}
bool passByVal(Type *t) override {
bool passByVal(TypeFunction *, Type *) override {
// LLVM's byval attribute is not compatible with the Win64 ABI
return false;
}

View file

@ -217,15 +217,16 @@ struct ImplicitByvalRewrite : ABIRewrite {
struct X86_64TargetABI : TargetABI {
X86_64_C_struct_rewrite struct_rewrite;
ImplicitByvalRewrite byvalRewrite;
IndirectByvalRewrite indirectByvalRewrite;
bool returnInArg(TypeFunction *tf) override;
bool passByVal(Type *t) override;
bool passByVal(TypeFunction *tf, Type *t) override;
void rewriteFunctionType(IrFuncTy &fty) override;
void rewriteVarargs(IrFuncTy &fty, std::vector<IrFuncTyArg *> &args) override;
void rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg) override;
void rewriteArgument(IrFuncTyArg &arg, RegCount &regCount);
void rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg, RegCount &regCount);
LLValue *prepareVaStart(DLValue *ap) override;
@ -252,11 +253,15 @@ bool X86_64TargetABI::returnInArg(TypeFunction *tf) {
return false;
}
Type *rt = tf->next;
return passByVal(rt);
Type *rt = tf->next->toBasetype();
return dmd_abi::passByVal(rt);
}
bool X86_64TargetABI::passByVal(Type *t) {
bool X86_64TargetABI::passByVal(TypeFunction *tf, Type *t) {
// indirectly by-value for extern(C++) functions and non-POD args
if (tf->linkage == LINKcpp && !isPOD(t))
return false;
return dmd_abi::passByVal(t->toBasetype());
}
@ -264,10 +269,21 @@ void X86_64TargetABI::rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg) {
llvm_unreachable("Please use the other overload explicitly.");
}
void X86_64TargetABI::rewriteArgument(IrFuncTyArg &arg, RegCount &regCount) {
void X86_64TargetABI::rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg,
RegCount &regCount) {
LLType *originalLType = arg.ltype;
Type *t = arg.type->toBasetype();
// indirectly by-value for extern(C++) functions and non-POD args
if (fty.type->linkage == LINKcpp && !isPOD(t)) {
indirectByvalRewrite.applyTo(arg);
if (regCount.int_regs > 0) {
regCount.int_regs--;
}
return;
}
LLType *abiTy = getAbiType(t);
if (abiTy && !LLTypeMemoryLayout::typesAreEquivalent(abiTy, originalLType)) {
IF_LOG {
@ -280,11 +296,12 @@ void X86_64TargetABI::rewriteArgument(IrFuncTyArg &arg, RegCount &regCount) {
}
if (regCount.trySubtract(arg) == RegCount::ArgumentWouldFitInPartially) {
// pass LL structs implicitly ByVal, otherwise LLVM passes
// them partially in registers, partially in memory
// pass the LL struct with byval attribute to prevent LLVM from passing it
// partially in registers, partially in memory
assert(originalLType->isStructTy());
IF_LOG Logger::cout() << "Passing implicitly ByVal: " << arg.type->toChars()
<< " (" << *originalLType << ")\n";
IF_LOG Logger::cout() << "Passing byval to prevent register/memory mix: "
<< arg.type->toChars() << " (" << *originalLType
<< ")\n";
byvalRewrite.applyTo(arg);
}
}
@ -298,7 +315,7 @@ void X86_64TargetABI::rewriteFunctionType(IrFuncTy &fty) {
Logger::println("x86-64 ABI: Transforming return type");
LOG_SCOPE;
RegCount dummy;
rewriteArgument(*fty.ret, dummy);
rewriteArgument(fty, *fty.ret, dummy);
}
// IMPLICIT PARAMETERS
@ -336,7 +353,7 @@ void X86_64TargetABI::rewriteFunctionType(IrFuncTy &fty) {
continue;
}
rewriteArgument(arg, regCount);
rewriteArgument(fty, arg, regCount);
}
// regCount (fty.tag) is now in the state after all implicit & formal args,
@ -351,7 +368,7 @@ void X86_64TargetABI::rewriteVarargs(IrFuncTy &fty,
for (auto arg : args) {
if (!arg->byref) { // don't rewrite ByVal arguments
rewriteArgument(*arg, regCount);
rewriteArgument(fty, *arg, regCount);
}
}
}

View file

@ -24,6 +24,7 @@ struct X86TargetABI : TargetABI {
const bool isMSVC;
bool returnStructsInRegs;
IntegerRewrite integerRewrite;
IndirectByvalRewrite indirectByvalRewrite;
X86TargetABI()
: isOSX(global.params.targetTriple->isMacOSX()),
@ -118,7 +119,11 @@ struct X86TargetABI : TargetABI {
return !canRewriteAsInt(rt);
}
bool passByVal(Type *t) override {
bool passByVal(TypeFunction *tf, Type *t) override {
// indirectly by-value for extern(C++) functions and non-POD args on Posix
if (!isMSVC && tf->linkage == LINKcpp && !isPOD(t))
return false;
// pass all structs and static arrays with the LLVM byval attribute
return DtoIsInMemoryOnly(t);
}
@ -182,6 +187,13 @@ struct X86TargetABI : TargetABI {
// all other arguments are passed on the stack, don't rewrite
}
// extern(C++) on Posix: non-POD args are passed indirectly by-value
else if (!isMSVC && fty.type->linkage == LINKcpp) {
for (auto arg : fty.args) {
if (!arg->byref && !isPOD(arg->type))
indirectByvalRewrite.applyTo(*arg);
}
}
workaroundIssue1356(fty.args);

View file

@ -310,7 +310,9 @@ struct UnknownTargetABI : TargetABI {
return (rt->ty == Tstruct || rt->ty == Tsarray);
}
bool passByVal(Type *t) override { return t->toBasetype()->ty == Tstruct; }
bool passByVal(TypeFunction *, Type *t) override {
return t->toBasetype()->ty == Tstruct;
}
void rewriteFunctionType(IrFuncTy &) override {
// why?
@ -361,7 +363,7 @@ struct IntrinsicABI : TargetABI {
bool returnInArg(TypeFunction *tf) override { return false; }
bool passByVal(Type *t) override { return false; }
bool passByVal(TypeFunction *, Type *t) override { return false; }
bool reverseExplicitParams(TypeFunction *) override { return false; }

View file

@ -137,7 +137,7 @@ struct TargetABI {
/// parameter.
/// The LL caller needs to pass a pointer to the original argument (the memcpy
/// source).
virtual bool passByVal(Type *t) = 0;
virtual bool passByVal(TypeFunction *tf, Type *t) = 0;
/// Returns true if the 'this' argument is to be passed before the 'sret'
/// argument.

View file

@ -170,7 +170,7 @@ llvm::FunctionType *DtoFunctionType(Type *type, IrFuncTy &irFty, Type *thistype,
// ref/out
attrs.addDereferenceable(loweredDType->size());
} else {
if (abi->passByVal(loweredDType)) {
if (abi->passByVal(f, loweredDType)) {
// LLVM ByVal parameters are pointers to a copy in the function
// parameters stack. The caller needs to provide a pointer to the
// original argument.

View file

@ -129,7 +129,7 @@ static void addExplicitArguments(std::vector<LLValue *> &args, AttrSet &attrs,
std::vector<IrFuncTyArg *> optionalIrArgs;
for (size_t i = formalDArgCount; i < explicitDArgCount; i++) {
Type *argType = argexps[i]->type;
bool passByVal = gABI->passByVal(argType);
bool passByVal = gABI->passByVal(irFty.type, argType);
AttrBuilder initialAttrs;
if (passByVal) {

@ -1 +1 @@
Subproject commit a0f157abcc2d2779aa44cd9d6dec84aa56950d81
Subproject commit b0b28297a8befba66640c7b4462ecdc12c40c318