mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-10 21:06:33 +03:00
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:
parent
f8880c6c38
commit
3ca43c51b8
15 changed files with 61 additions and 30 deletions
|
@ -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));
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 ®Count);
|
void rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg, RegCount ®Count);
|
||||||
|
|
||||||
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 ®Count) {
|
void X86_64TargetABI::rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg,
|
||||||
|
RegCount ®Count) {
|
||||||
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 ®Count) {
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
Loading…
Add table
Add a link
Reference in a new issue