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)) if (!isPOD(rt))
return true; return true;
return passByVal(rt); return passByVal(tf, rt);
} }
bool passByVal(Type *t) override { bool passByVal(TypeFunction *, Type *t) override {
t = t->toBasetype(); t = t->toBasetype();
return t->ty == Tsarray || return t->ty == Tsarray ||
(t->ty == Tstruct && t->size() > 16 && !isHFA((TypeStruct *)t)); (t->ty == Tstruct && t->size() > 16 && !isHFA((TypeStruct *)t));

View file

@ -42,7 +42,7 @@ struct ArmTargetABI : TargetABI {
!isHFA((TypeStruct *)rt))); !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 // AAPCS does not use an indirect arg to pass aggregates, however
// clang uses byval for types > 64-bytes, then llvm backend // clang uses byval for types > 64-bytes, then llvm backend
// converts back to non-byval. Without this special handling the // 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); return (rt->ty == Tstruct || rt->ty == Tsarray);
} }
bool passByVal(Type *t) override { bool passByVal(TypeFunction *, Type *t) override {
TY ty = t->toBasetype()->ty; TY ty = t->toBasetype()->ty;
return ty == Tstruct || ty == Tsarray; return ty == Tstruct || ty == Tsarray;
} }

View file

@ -24,7 +24,7 @@ struct NVPTXTargetABI : TargetABI {
else else
return llvm::CallingConv::PTX_Device; return llvm::CallingConv::PTX_Device;
} }
bool passByVal(Type *t) override { bool passByVal(TypeFunction *, Type *t) override {
t = t->toBasetype(); t = t->toBasetype();
return ((t->ty == Tsarray || t->ty == Tstruct) && t->size() > 64); 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; 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 ppc, aggregates are always passed as an indirect value.
// On ppc64, they are always passed by value. However, clang // On ppc64, they are always passed by value. However, clang
// used byval for type > 64 bytes. // used byval for type > 64 bytes.

View file

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

View file

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

View file

@ -117,7 +117,7 @@ public:
return passPointerToHiddenCopy(rt, /*isReturnValue=*/true, tf->linkage); 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 // LLVM's byval attribute is not compatible with the Win64 ABI
return false; return false;
} }

View file

@ -217,15 +217,16 @@ struct ImplicitByvalRewrite : ABIRewrite {
struct X86_64TargetABI : TargetABI { struct X86_64TargetABI : TargetABI {
X86_64_C_struct_rewrite struct_rewrite; X86_64_C_struct_rewrite struct_rewrite;
ImplicitByvalRewrite byvalRewrite; ImplicitByvalRewrite byvalRewrite;
IndirectByvalRewrite indirectByvalRewrite;
bool returnInArg(TypeFunction *tf) override; bool returnInArg(TypeFunction *tf) override;
bool passByVal(Type *t) override; bool passByVal(TypeFunction *tf, Type *t) override;
void rewriteFunctionType(IrFuncTy &fty) override; void rewriteFunctionType(IrFuncTy &fty) override;
void rewriteVarargs(IrFuncTy &fty, std::vector<IrFuncTyArg *> &args) override; void rewriteVarargs(IrFuncTy &fty, std::vector<IrFuncTyArg *> &args) override;
void rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg) 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; LLValue *prepareVaStart(DLValue *ap) override;
@ -252,11 +253,15 @@ bool X86_64TargetABI::returnInArg(TypeFunction *tf) {
return false; return false;
} }
Type *rt = tf->next; Type *rt = tf->next->toBasetype();
return passByVal(rt); 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()); 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."); 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; LLType *originalLType = arg.ltype;
Type *t = arg.type->toBasetype(); 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); LLType *abiTy = getAbiType(t);
if (abiTy && !LLTypeMemoryLayout::typesAreEquivalent(abiTy, originalLType)) { if (abiTy && !LLTypeMemoryLayout::typesAreEquivalent(abiTy, originalLType)) {
IF_LOG { IF_LOG {
@ -280,11 +296,12 @@ void X86_64TargetABI::rewriteArgument(IrFuncTyArg &arg, RegCount &regCount) {
} }
if (regCount.trySubtract(arg) == RegCount::ArgumentWouldFitInPartially) { if (regCount.trySubtract(arg) == RegCount::ArgumentWouldFitInPartially) {
// pass LL structs implicitly ByVal, otherwise LLVM passes // pass the LL struct with byval attribute to prevent LLVM from passing it
// them partially in registers, partially in memory // partially in registers, partially in memory
assert(originalLType->isStructTy()); assert(originalLType->isStructTy());
IF_LOG Logger::cout() << "Passing implicitly ByVal: " << arg.type->toChars() IF_LOG Logger::cout() << "Passing byval to prevent register/memory mix: "
<< " (" << *originalLType << ")\n"; << arg.type->toChars() << " (" << *originalLType
<< ")\n";
byvalRewrite.applyTo(arg); byvalRewrite.applyTo(arg);
} }
} }
@ -298,7 +315,7 @@ void X86_64TargetABI::rewriteFunctionType(IrFuncTy &fty) {
Logger::println("x86-64 ABI: Transforming return type"); Logger::println("x86-64 ABI: Transforming return type");
LOG_SCOPE; LOG_SCOPE;
RegCount dummy; RegCount dummy;
rewriteArgument(*fty.ret, dummy); rewriteArgument(fty, *fty.ret, dummy);
} }
// IMPLICIT PARAMETERS // IMPLICIT PARAMETERS
@ -336,7 +353,7 @@ void X86_64TargetABI::rewriteFunctionType(IrFuncTy &fty) {
continue; continue;
} }
rewriteArgument(arg, regCount); rewriteArgument(fty, arg, regCount);
} }
// regCount (fty.tag) is now in the state after all implicit & formal args, // 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) { for (auto arg : args) {
if (!arg->byref) { // don't rewrite ByVal arguments 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; const bool isMSVC;
bool returnStructsInRegs; bool returnStructsInRegs;
IntegerRewrite integerRewrite; IntegerRewrite integerRewrite;
IndirectByvalRewrite indirectByvalRewrite;
X86TargetABI() X86TargetABI()
: isOSX(global.params.targetTriple->isMacOSX()), : isOSX(global.params.targetTriple->isMacOSX()),
@ -118,7 +119,11 @@ struct X86TargetABI : TargetABI {
return !canRewriteAsInt(rt); 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 // pass all structs and static arrays with the LLVM byval attribute
return DtoIsInMemoryOnly(t); return DtoIsInMemoryOnly(t);
} }
@ -182,6 +187,13 @@ struct X86TargetABI : TargetABI {
// all other arguments are passed on the stack, don't rewrite // 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); workaroundIssue1356(fty.args);

View file

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

View file

@ -137,7 +137,7 @@ struct TargetABI {
/// parameter. /// parameter.
/// The LL caller needs to pass a pointer to the original argument (the memcpy /// The LL caller needs to pass a pointer to the original argument (the memcpy
/// source). /// 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' /// Returns true if the 'this' argument is to be passed before the 'sret'
/// argument. /// argument.

View file

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

View file

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

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