From fc6c340661f5846c695f8f52340c2f6b9d53fc3b Mon Sep 17 00:00:00 2001 From: Martin Date: Sat, 28 May 2016 16:08:44 +0200 Subject: [PATCH 1/5] Optimize ABIRewrite system for lvalues Allow ABIRewrites to return the D parameter's LL value directly. Most rewrites store to memory anyway, so let the D parameter point directly to that memory instead of a dedicated alloca bitcopy. --- gen/abi-generic.h | 106 ++++++++++++++++---------------------------- gen/abi-x86-64.cpp | 24 +++------- gen/abi.cpp | 6 +-- gen/abi.h | 16 +++---- gen/functions.cpp | 6 +-- gen/llvmhelpers.cpp | 28 +++++------- gen/tocall.cpp | 6 +-- ir/irfuncty.cpp | 25 +++++------ ir/irfuncty.h | 6 +-- 9 files changed, 83 insertions(+), 140 deletions(-) diff --git a/gen/abi-generic.h b/gen/abi-generic.h index bdac57ba34a..4edc194cf93 100644 --- a/gen/abi-generic.h +++ b/gen/abi-generic.h @@ -84,25 +84,17 @@ struct LLTypeMemoryLayout { /// Removes padding fields for (non-union-containing!) structs struct RemoveStructPadding : ABIRewrite { - /// get a rewritten value back to its original form - LLValue *get(Type *dty, LLValue *v) override { - LLValue *lval = DtoAlloca(dty, ".rewritetmp"); - getL(dty, v, lval); - return lval; + LLValue *put(DValue *v) override { + return DtoUnpaddedStruct(v->getType()->toBasetype(), v->getRVal()); } - /// get a rewritten value back to its original form and store result in - /// provided lvalue - void getL(Type *dty, LLValue *v, LLValue *lval) override { + LLValue *getLVal(Type *dty, LLValue *v) override { + LLValue *lval = DtoAlloca(dty, ".RemoveStructPadding_dump"); // Make sure the padding is zero, so struct comparisons work. // TODO: Only do this if there's padding, and/or only initialize padding. DtoMemSetZero(lval, DtoConstSize_t(getTypeAllocSize(DtoType(dty)))); DtoPaddedStruct(dty->toBasetype(), v, lval); - } - - /// put out rewritten value - LLValue *put(DValue *v) override { - return DtoUnpaddedStruct(v->getType()->toBasetype(), v->getRVal()); + return lval; } /// return the transformed type for this rewrite @@ -164,20 +156,14 @@ struct IntegerRewrite : ABIRewrite { return LLTypeMemoryLayout::typesAreEquivalent(llType, integerType); } - LLValue *get(Type *dty, LLValue *v) override { - LLValue *integerDump = DtoAllocaDump(v, dty, ".IntegerRewrite_dump"); - LLType *type = DtoType(dty); - return loadFromMemory(integerDump, type, ".IntegerRewrite_getResult"); - } - - void getL(Type *dty, LLValue *v, LLValue *lval) override { - storeToMemory(v, lval); - } - LLValue *put(DValue *dv) override { LLValue *address = getAddressOf(dv); LLType *integerType = getIntegerType(dv->getType()->size()); - return loadFromMemory(address, integerType, ".IntegerRewrite_putResult"); + return loadFromMemory(address, integerType); + } + + LLValue *getLVal(Type *dty, LLValue *v) override { + return DtoAllocaDump(v, dty, ".IntegerRewrite_dump"); } LLType *type(Type *t, LLType *) override { return getIntegerType(t->size()); } @@ -204,29 +190,25 @@ struct ExplicitByvalRewrite : ABIRewrite { explicit ExplicitByvalRewrite(unsigned minAlignment = 16) : minAlignment(minAlignment) {} - LLValue *get(Type *dty, LLValue *v) override { - return DtoLoad(v, ".ExplicitByvalRewrite_getResult"); - } - - void getL(Type *dty, LLValue *v, LLValue *lval) override { - DtoMemCpy(lval, v); - } - LLValue *put(DValue *v) override { Type *dty = v->getType(); const unsigned align = alignment(dty); - if (DtoIsInMemoryOnly(dty)) { - LLValue *originalPointer = v->getRVal(); - LLType *type = originalPointer->getType()->getPointerElementType(); - LLValue *copyForCallee = - DtoRawAlloca(type, align, ".ExplicitByvalRewrite_putResult"); - DtoMemCpy(copyForCallee, originalPointer); - return copyForCallee; + if (!DtoIsInMemoryOnly(dty)) { + return DtoAllocaDump(v->getRVal(), align, + ".ExplicitByvalRewrite_dump"); } - return DtoAllocaDump(v->getRVal(), align, - ".ExplicitByvalRewrite_putResult"); + LLValue *originalPointer = v->getRVal(); + LLType *type = originalPointer->getType()->getPointerElementType(); + LLValue *copyForCallee = + DtoRawAlloca(type, align, ".ExplicitByvalRewrite_dump"); + DtoMemCpy(copyForCallee, originalPointer); + return copyForCallee; + } + + LLValue *getLVal(Type *dty, LLValue *v) override { + return DtoBitCast(v, DtoPtrToType(dty)); } LLType *type(Type *dty, LLType *t) override { return DtoPtrToType(dty); } @@ -245,15 +227,6 @@ struct HFAToArray : ABIRewrite { HFAToArray(const int max = 4) : maxFloats(max) {} - LLValue *get(Type *dty, LLValue *v) override { - Logger::println("rewriting array -> as HFA %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 HFA %s -> as array", dty->toChars()); @@ -261,6 +234,11 @@ struct HFAToArray : ABIRewrite { return DtoLoad(DtoBitCast(dv->getRVal(), getPtrToType(t))); } + LLValue *getLVal(Type *dty, LLValue *v) override { + Logger::println("rewriting array -> as HFA %s", dty->toChars()); + return DtoAllocaDump(v, dty, ".HFAToArray_dump"); + } + LLType *type(Type *dty, LLType *) override { assert(dty->ty == Tstruct); LLType *floatArrayType = nullptr; @@ -274,15 +252,6 @@ struct HFAToArray : ABIRewrite { * Rewrite a composite as array of i64. */ struct CompositeToArray64 : ABIRewrite { - LLValue *get(Type *dty, LLValue *v) override { - Logger::println("rewriting i64 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 i64 array", dty->toChars()); @@ -290,6 +259,11 @@ struct CompositeToArray64 : ABIRewrite { return DtoLoad(DtoBitCast(dv->getRVal(), getPtrToType(t))); } + LLValue *getLVal(Type *dty, LLValue *v) override { + Logger::println("rewriting i64 array -> as %s", dty->toChars()); + return DtoAllocaDump(v, dty, ".CompositeToArray64_dump"); + } + LLType *type(Type *t, LLType *) override { // An i64 array that will hold Type 't' size_t sz = (t->size() + 7) / 8; @@ -301,15 +275,6 @@ struct CompositeToArray64 : ABIRewrite { * Rewrite a composite as array of i32. */ 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()); @@ -317,6 +282,11 @@ struct CompositeToArray32 : ABIRewrite { return DtoLoad(DtoBitCast(dv->getRVal(), getPtrToType(t))); } + LLValue *getLVal(Type *dty, LLValue *v) override { + Logger::println("rewriting i32 array -> as %s", dty->toChars()); + return DtoAllocaDump(v, dty, ".CompositeToArray32_dump"); + } + LLType *type(Type *t, LLType *) override { // An i32 array that will hold Type 't' size_t sz = (t->size() + 3) / 4; diff --git a/gen/abi-x86-64.cpp b/gen/abi-x86-64.cpp index 04859fbfec5..906d8628a61 100644 --- a/gen/abi-x86-64.cpp +++ b/gen/abi-x86-64.cpp @@ -169,16 +169,6 @@ struct RegCount { * memory so that it's then readable as the other type (i.e., bit-casting). */ struct X86_64_C_struct_rewrite : ABIRewrite { - LLValue *get(Type *dty, LLValue *v) override { - LLValue *address = DtoAllocaDump(v, dty, ".X86_64_C_struct_rewrite_dump"); - LLType *type = DtoType(dty); - return loadFromMemory(address, type, ".X86_64_C_struct_rewrite_getResult"); - } - - void getL(Type *dty, LLValue *v, LLValue *lval) override { - storeToMemory(v, lval); - } - LLValue *put(DValue *v) override { LLValue *address = getAddressOf(v); @@ -188,6 +178,10 @@ struct X86_64_C_struct_rewrite : ABIRewrite { return loadFromMemory(address, abiTy, ".X86_64_C_struct_rewrite_putResult"); } + LLValue *getLVal(Type *dty, LLValue *v) override { + return DtoAllocaDump(v, dty, ".X86_64_C_struct_rewrite_dump"); + } + LLType *type(Type *dty, LLType *t) override { return getAbiType(dty); } }; @@ -200,16 +194,10 @@ struct X86_64_C_struct_rewrite : ABIRewrite { * the ByVal LLVM attribute. */ struct ImplicitByvalRewrite : ABIRewrite { - LLValue *get(Type *dty, LLValue *v) override { - return DtoLoad(v, ".ImplicitByvalRewrite_getResult"); - } - - void getL(Type *dty, LLValue *v, LLValue *lval) override { - DtoMemCpy(lval, v); - } - LLValue *put(DValue *v) override { return getAddressOf(v); } + LLValue *getLVal(Type *dty, LLValue *v) override { return v; } + LLType *type(Type *dty, LLType *t) override { return DtoPtrToType(dty); } }; diff --git a/gen/abi.cpp b/gen/abi.cpp index 87f29300b79..eee59c55077 100644 --- a/gen/abi.cpp +++ b/gen/abi.cpp @@ -31,10 +31,8 @@ ////////////////////////////////////////////////////////////////////////////// -void ABIRewrite::getL(Type *dty, LLValue *v, LLValue *lval) { - LLValue *rval = get(dty, v); - assert(rval->getType() == lval->getType()->getContainedType(0)); - DtoStore(rval, lval); +llvm::Value *ABIRewrite::getRVal(Type *dty, LLValue *v) { + return DtoLoad(DtoBitCast(getLVal(dty, v), DtoType(dty)->getPointerTo())); } ////////////////////////////////////////////////////////////////////////////// diff --git a/gen/abi.h b/gen/abi.h index f9cbe930aa4..ebbf452ac39 100644 --- a/gen/abi.h +++ b/gen/abi.h @@ -39,16 +39,16 @@ class FunctionType; struct ABIRewrite { virtual ~ABIRewrite() = default; - /// get a rewritten value back to its original form - virtual llvm::Value *get(Type *dty, llvm::Value *v) = 0; + /// Transforms the D argument to a suitable LL argument. + virtual llvm::Value *put(DValue *v) = 0; - /// get a rewritten value back to its original form and store result in - /// provided lvalue - /// this one is optional and defaults to calling the one above - virtual void getL(Type *dty, llvm::Value *v, llvm::Value *lval); + /// Transforms the LL parameter back and returns the address for the D + /// parameter. + virtual llvm::Value *getLVal(Type *dty, llvm::Value *v) = 0; - /// put out rewritten value - virtual llvm::Value *put(DValue *v) = 0; + /// Transforms the LL parameter back and returns the value for the D + /// parameter. + virtual llvm::Value *getRVal(Type *dty, llvm::Value *v); /// should return the transformed type for this rewrite virtual llvm::Type *type(Type *dty, llvm::Type *t) = 0; diff --git a/gen/functions.cpp b/gen/functions.cpp index cbe13989ee8..351f0f1dc71 100644 --- a/gen/functions.cpp +++ b/gen/functions.cpp @@ -917,11 +917,9 @@ void DtoDefineFunction(FuncDeclaration *fd) { irparam->value = DtoAlloca(vd, vd->ident->toChars()); } else { if (!irparam->arg->byref) { - // alloca a stack slot for this first class value arg - LLValue *mem = DtoAlloca(irparam->arg->type, vd->ident->toChars()); - // let the abi transform the argument back first - irFty.getParam(vd->type, llArgIdx, irparam->value, mem); + LLValue *mem = irFty.getParamLVal(irparam->arg->type, llArgIdx, irparam->value); + mem->setName(vd->ident->toChars()); // set the arg var value to the alloca irparam->value = mem; diff --git a/gen/llvmhelpers.cpp b/gen/llvmhelpers.cpp index 6b8abfdea75..e9c6ec2fb85 100644 --- a/gen/llvmhelpers.cpp +++ b/gen/llvmhelpers.cpp @@ -227,14 +227,14 @@ LLValue *DtoAllocaDump(LLValue *val, Type *asType, const char *name) { LLValue *DtoAllocaDump(LLValue *val, LLType *asType, int alignment, const char *name) { - LLType *valType = i1ToI8(voidToI8(val->getType())); - asType = i1ToI8(voidToI8(asType)); + LLType *memType = i1ToI8(voidToI8(val->getType())); + LLType *asMemType = i1ToI8(voidToI8(asType)); LLType *allocaType = - (getTypeStoreSize(valType) <= getTypeAllocSize(asType) ? asType - : valType); + (getTypeStoreSize(memType) <= getTypeAllocSize(asMemType) ? asMemType + : memType); LLValue *mem = DtoRawAlloca(allocaType, alignment, name); - DtoStoreZextI8(val, DtoBitCast(mem, valType->getPointerTo())); - return DtoBitCast(mem, asType->getPointerTo()); + DtoStoreZextI8(val, DtoBitCast(mem, memType->getPointerTo())); + return DtoBitCast(mem, asMemType->getPointerTo()); } /****************************************************************************** @@ -1602,18 +1602,10 @@ DValue *DtoSymbolAddress(Loc &loc, Type *type, Declaration *decl) { assert(type->ty == Tdelegate); return new DVarValue(type, getIrValue(vd)); } - if (vd->isRef() || vd->isOut() || DtoIsInMemoryOnly(vd->type) || - llvm::isa(getIrValue(vd))) { - assert(!isSpecialRefVar(vd) && "Code not expected to handle special " - "ref vars, although it can easily be " - "made to."); - return new DVarValue(type, getIrValue(vd)); - } - if (llvm::isa(getIrValue(vd))) { - return new DImValue(type, getIrValue(vd)); - } - llvm_unreachable("Unexpected parameter value."); - + assert(!isSpecialRefVar(vd) && "Code not expected to handle special " + "ref vars, although it can easily be " + "made to."); + return new DVarValue(type, getIrValue(vd)); } else { Logger::println("a normal variable"); diff --git a/gen/tocall.cpp b/gen/tocall.cpp index 0c2642a8b70..21b4bb52961 100644 --- a/gen/tocall.cpp +++ b/gen/tocall.cpp @@ -900,13 +900,11 @@ DValue *DtoCallFunction(Loc &loc, Type *resulttype, DValue *fnval, // do ABI specific return value fixups if (storeReturnValueOnStack) { Logger::println("Storing return value to stack slot"); - LLValue *mem = DtoAlloca(returntype); - irFty.getRet(returntype, retllval, mem); - retllval = mem; + retllval = irFty.getRetLVal(returntype, retllval); retValIsAlloca = true; storeReturnValueOnStack = false; } else { - retllval = irFty.getRet(returntype, retllval); + retllval = irFty.getRetRVal(returntype, retllval); storeReturnValueOnStack = (returnTy == Tstruct && !isaPointer(retllval)) || (returnTy == Tsarray && isaArray(retllval)); diff --git a/ir/irfuncty.cpp b/ir/irfuncty.cpp index bcc97c548c9..4983ac9faac 100644 --- a/ir/irfuncty.cpp +++ b/ir/irfuncty.cpp @@ -12,6 +12,7 @@ #include "gen/abi.h" #include "gen/dvalue.h" #include "gen/llvm.h" +#include "gen/llvmhelpers.h" #include "gen/logger.h" #include "gen/tollvm.h" @@ -38,29 +39,28 @@ llvm::Value *IrFuncTy::putRet(DValue *dval) { return dval->getRVal(); } -llvm::Value *IrFuncTy::getRet(Type *dty, LLValue *val) { +llvm::Value *IrFuncTy::getRetRVal(Type *dty, LLValue *val) { assert(!arg_sret); if (ret->rewrite) { - Logger::println("Rewrite: getRet"); + Logger::println("Rewrite: getRetRVal"); LOG_SCOPE - return ret->rewrite->get(dty, val); + return ret->rewrite->getRVal(dty, val); } return val; } -void IrFuncTy::getRet(Type *dty, LLValue *val, LLValue *address) { +llvm::Value *IrFuncTy::getRetLVal(Type *dty, LLValue *val) { assert(!arg_sret); if (ret->rewrite) { - Logger::println("Rewrite: getRet (getL)"); + Logger::println("Rewrite: getRetLVal"); LOG_SCOPE - ret->rewrite->getL(dty, val, address); - return; + return ret->rewrite->getLVal(dty, val); } - DtoStoreZextI8(val, address); + return DtoAllocaDump(val, dty); } llvm::Value *IrFuncTy::putParam(size_t idx, DValue *dval) { @@ -78,17 +78,16 @@ llvm::Value *IrFuncTy::putParam(const IrFuncTyArg &arg, DValue *dval) { return dval->getRVal(); } -void IrFuncTy::getParam(Type *dty, size_t idx, LLValue *val, LLValue *address) { +LLValue *IrFuncTy::getParamLVal(Type *dty, size_t idx, LLValue *val) { assert(idx < args.size() && "invalid getParam"); if (args[idx]->rewrite) { - Logger::println("Rewrite: getParam (getL)"); + Logger::println("Rewrite: getParamLVal"); LOG_SCOPE - args[idx]->rewrite->getL(dty, val, address); - return; + return args[idx]->rewrite->getLVal(dty, val); } - DtoStoreZextI8(val, address); + return DtoAllocaDump(val, dty); } AttrSet IrFuncTy::getParamAttrs(bool passThisBeforeSret) { diff --git a/ir/irfuncty.h b/ir/irfuncty.h index ffdd30b4b7b..24a4636de56 100644 --- a/ir/irfuncty.h +++ b/ir/irfuncty.h @@ -111,12 +111,12 @@ struct IrFuncTy { void *tag = nullptr; llvm::Value *putRet(DValue *dval); - llvm::Value *getRet(Type *dty, llvm::Value *val); - void getRet(Type *dty, llvm::Value *val, llvm::Value *address); + llvm::Value *getRetRVal(Type *dty, llvm::Value *val); + llvm::Value *getRetLVal(Type *dty, llvm::Value *val); llvm::Value *putParam(size_t idx, DValue *dval); llvm::Value *putParam(const IrFuncTyArg &arg, DValue *dval); - void getParam(Type *dty, size_t idx, llvm::Value *val, llvm::Value *address); + llvm::Value *getParamLVal(Type *dty, size_t idx, llvm::Value *val); AttrSet getParamAttrs(bool passThisBeforeSret); }; From 769dac1b1f15da003bdb699d32bf613182b1f757 Mon Sep 17 00:00:00 2001 From: Martin Date: Sun, 29 May 2016 10:41:23 +0200 Subject: [PATCH 2/5] Remove obsolete helper ABIRewrite::storeToMemory() --- gen/abi.cpp | 21 --------------------- gen/abi.h | 5 ----- 2 files changed, 26 deletions(-) diff --git a/gen/abi.cpp b/gen/abi.cpp index eee59c55077..df6851782f0 100644 --- a/gen/abi.cpp +++ b/gen/abi.cpp @@ -51,27 +51,6 @@ LLValue *ABIRewrite::getAddressOf(DValue *v) { return DtoAllocaDump(v, ".getAddressOf_dump"); } -void ABIRewrite::storeToMemory(LLValue *rval, LLValue *address) { - LLType *pointerType = address->getType(); - assert(pointerType->isPointerTy()); - LLType *pointeeType = pointerType->getPointerElementType(); - - LLType *rvalType = rval->getType(); - if (rvalType != pointeeType) { - if (getTypeStoreSize(rvalType) > getTypeAllocSize(pointeeType)) { - // not enough allocated memory - LLValue *paddedDump = DtoAllocaDump(rval, 0, ".storeToMemory_paddedDump"); - DtoMemCpy(address, paddedDump); - return; - } - - address = DtoBitCast(address, getPtrToType(rvalType), - ".storeToMemory_bitCastAddress"); - } - - DtoStore(rval, address); -} - LLValue *ABIRewrite::loadFromMemory(LLValue *address, LLType *asType, const char *name) { LLType *pointerType = address->getType(); diff --git a/gen/abi.h b/gen/abi.h index ebbf452ac39..9fca2ac0c92 100644 --- a/gen/abi.h +++ b/gen/abi.h @@ -59,11 +59,6 @@ struct ABIRewrite { /// Returns the address of a D value, storing it to memory first if need be. static llvm::Value *getAddressOf(DValue *v); - /// Stores a LL value to a specified memory address. The element type of the - /// provided pointer doesn't need to match the value type (=> suited for - /// bit-casting). - static void storeToMemory(llvm::Value *rval, llvm::Value *address); - /// Loads a LL value of a specified type from memory. The element type of the /// provided pointer doesn't need to match the value type (=> suited for /// bit-casting). From dd21a805ee0b0661c203a7d41885ef72697a8e74 Mon Sep 17 00:00:00 2001 From: Martin Date: Sun, 29 May 2016 11:04:29 +0200 Subject: [PATCH 3/5] Trim signature of method ABIRewrite::type() --- gen/abi-aarch64.cpp | 8 ++++---- gen/abi-arm.cpp | 6 +++--- gen/abi-generic.h | 27 +++++++++++++-------------- gen/abi-ppc.cpp | 4 ++-- gen/abi-ppc64le.cpp | 12 ++++++------ gen/abi-win64.cpp | 2 +- gen/abi-x86-64.cpp | 4 ++-- gen/abi-x86.cpp | 4 ++-- gen/abi.h | 12 ++++++++---- 9 files changed, 41 insertions(+), 38 deletions(-) diff --git a/gen/abi-aarch64.cpp b/gen/abi-aarch64.cpp index b652d8c6dc9..3adf1149e83 100644 --- a/gen/abi-aarch64.cpp +++ b/gen/abi-aarch64.cpp @@ -50,11 +50,11 @@ struct AArch64TargetABI : TargetABI { // non-HFA and messes up register selection if (isHFA((TypeStruct *)retTy, &fty.ret->ltype)) { fty.ret->rewrite = &hfaToArray; - fty.ret->ltype = hfaToArray.type(fty.ret->type, fty.ret->ltype); + fty.ret->ltype = hfaToArray.type(fty.ret->type); } else { fty.ret->rewrite = &integerRewrite; - fty.ret->ltype = integerRewrite.type(fty.ret->type, fty.ret->ltype); + fty.ret->ltype = integerRewrite.type(fty.ret->type); } } @@ -75,11 +75,11 @@ struct AArch64TargetABI : TargetABI { // non-HFA and messes up register selection if (ty->ty == Tstruct && isHFA((TypeStruct *)ty, &arg.ltype)) { arg.rewrite = &hfaToArray; - arg.ltype = hfaToArray.type(arg.type, arg.ltype); + arg.ltype = hfaToArray.type(arg.type); } else { arg.rewrite = &compositeToArray64; - arg.ltype = compositeToArray64.type(arg.type, arg.ltype); + arg.ltype = compositeToArray64.type(arg.type); } } } diff --git a/gen/abi-arm.cpp b/gen/abi-arm.cpp index 095e761b566..fe1264f2ee3 100644 --- a/gen/abi-arm.cpp +++ b/gen/abi-arm.cpp @@ -77,7 +77,7 @@ struct ArmTargetABI : TargetABI { fty.ret->rewrite = &hfaToArray; } else { fty.ret->rewrite = &integerRewrite; - fty.ret->ltype = integerRewrite.type(fty.ret->type, fty.ret->ltype); + fty.ret->ltype = integerRewrite.type(fty.ret->type); } } @@ -111,10 +111,10 @@ struct ArmTargetABI : TargetABI { arg.rewrite = &hfaToArray; } else if (DtoAlignment(ty) <= 4) { arg.rewrite = &compositeToArray32; - arg.ltype = compositeToArray32.type(arg.type, arg.ltype); + arg.ltype = compositeToArray32.type(arg.type); } else { arg.rewrite = &compositeToArray64; - arg.ltype = compositeToArray64.type(arg.type, arg.ltype); + arg.ltype = compositeToArray64.type(arg.type); } } } diff --git a/gen/abi-generic.h b/gen/abi-generic.h index 4edc194cf93..a3e66e0a52c 100644 --- a/gen/abi-generic.h +++ b/gen/abi-generic.h @@ -97,9 +97,8 @@ struct RemoveStructPadding : ABIRewrite { return lval; } - /// return the transformed type for this rewrite - LLType *type(Type *dty, LLType *t) override { - return DtoUnpaddedStructType(dty->toBasetype()); + LLType *type(Type *t) override { + return DtoUnpaddedStructType(t->toBasetype()); } }; @@ -166,7 +165,7 @@ struct IntegerRewrite : ABIRewrite { return DtoAllocaDump(v, dty, ".IntegerRewrite_dump"); } - LLType *type(Type *t, LLType *) override { return getIntegerType(t->size()); } + LLType *type(Type *t) override { return getIntegerType(t->size()); } }; ////////////////////////////////////////////////////////////////////////////// @@ -211,7 +210,7 @@ struct ExplicitByvalRewrite : ABIRewrite { return DtoBitCast(v, DtoPtrToType(dty)); } - LLType *type(Type *dty, LLType *t) override { return DtoPtrToType(dty); } + LLType *type(Type *t) override { return DtoPtrToType(t); } unsigned alignment(Type *dty) const { return std::max(minAlignment, DtoAlignment(dty)); @@ -230,7 +229,7 @@ struct HFAToArray : ABIRewrite { LLValue *put(DValue *dv) override { Type *dty = dv->getType(); Logger::println("rewriting HFA %s -> as array", dty->toChars()); - LLType *t = type(dty, nullptr); + LLType *t = type(dty); return DtoLoad(DtoBitCast(dv->getRVal(), getPtrToType(t))); } @@ -239,12 +238,12 @@ struct HFAToArray : ABIRewrite { return DtoAllocaDump(v, dty, ".HFAToArray_dump"); } - LLType *type(Type *dty, LLType *) override { - assert(dty->ty == Tstruct); + LLType *type(Type *t) override { + assert(t->ty == Tstruct); LLType *floatArrayType = nullptr; - if (TargetABI::isHFA((TypeStruct *)dty, &floatArrayType, maxFloats)) + if (TargetABI::isHFA((TypeStruct *)t, &floatArrayType, maxFloats)) return floatArrayType; - llvm_unreachable("Type dty should be an HFA"); + llvm_unreachable("Type t should be an HFA"); } }; @@ -255,7 +254,7 @@ struct CompositeToArray64 : ABIRewrite { LLValue *put(DValue *dv) override { Type *dty = dv->getType(); Logger::println("rewriting %s -> as i64 array", dty->toChars()); - LLType *t = type(dty, nullptr); + LLType *t = type(dty); return DtoLoad(DtoBitCast(dv->getRVal(), getPtrToType(t))); } @@ -264,7 +263,7 @@ struct CompositeToArray64 : ABIRewrite { return DtoAllocaDump(v, dty, ".CompositeToArray64_dump"); } - LLType *type(Type *t, LLType *) override { + LLType *type(Type *t) override { // An i64 array that will hold Type 't' size_t sz = (t->size() + 7) / 8; return LLArrayType::get(LLIntegerType::get(gIR->context(), 64), sz); @@ -278,7 +277,7 @@ struct CompositeToArray32 : ABIRewrite { LLValue *put(DValue *dv) override { Type *dty = dv->getType(); Logger::println("rewriting %s -> as i32 array", dty->toChars()); - LLType *t = type(dty, nullptr); + LLType *t = type(dty); return DtoLoad(DtoBitCast(dv->getRVal(), getPtrToType(t))); } @@ -287,7 +286,7 @@ struct CompositeToArray32 : ABIRewrite { return DtoAllocaDump(v, dty, ".CompositeToArray32_dump"); } - LLType *type(Type *t, LLType *) override { + LLType *type(Type *t) 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); diff --git a/gen/abi-ppc.cpp b/gen/abi-ppc.cpp index b3748cf7031..f21398182d6 100644 --- a/gen/abi-ppc.cpp +++ b/gen/abi-ppc.cpp @@ -68,13 +68,13 @@ struct PPCTargetABI : TargetABI { if (canRewriteAsInt(ty, Is64Bit)) { if (!IntegerRewrite::isObsoleteFor(arg.ltype)) { arg.rewrite = &integerRewrite; - arg.ltype = integerRewrite.type(arg.type, arg.ltype); + arg.ltype = integerRewrite.type(arg.type); } } else { // these types are passed byval: // the caller allocates a copy and then passes a pointer to the copy arg.rewrite = &byvalRewrite; - arg.ltype = byvalRewrite.type(arg.type, arg.ltype); + arg.ltype = byvalRewrite.type(arg.type); // the copy is treated as a local variable of the callee // hence add the NoAlias and NoCapture attributes diff --git a/gen/abi-ppc64le.cpp b/gen/abi-ppc64le.cpp index bc268d49217..2e6c498ee4d 100644 --- a/gen/abi-ppc64le.cpp +++ b/gen/abi-ppc64le.cpp @@ -63,14 +63,14 @@ struct PPC64LETargetABI : TargetABI { if (retTy->ty == Tstruct && isHFA((TypeStruct *)retTy, &fty.ret->ltype, 8)) { fty.ret->rewrite = &hfaToArray; - fty.ret->ltype = hfaToArray.type(fty.ret->type, fty.ret->ltype); + fty.ret->ltype = hfaToArray.type(fty.ret->type); } else if (canRewriteAsInt(retTy, true)) { fty.ret->rewrite = &integerRewrite; - fty.ret->ltype = integerRewrite.type(fty.ret->type, fty.ret->ltype); + fty.ret->ltype = integerRewrite.type(fty.ret->type); } else { fty.ret->rewrite = &compositeToArray64; fty.ret->ltype = - compositeToArray64.type(fty.ret->type, fty.ret->ltype); + compositeToArray64.type(fty.ret->type); } } else if (retTy->isintegral()) fty.ret->attrs.add(retTy->isunsigned() ? LLAttribute::ZExt @@ -90,13 +90,13 @@ struct PPC64LETargetABI : TargetABI { if (ty->ty == Tstruct || ty->ty == Tsarray) { if (ty->ty == Tstruct && isHFA((TypeStruct *)ty, &arg.ltype, 8)) { arg.rewrite = &hfaToArray; - arg.ltype = hfaToArray.type(arg.type, arg.ltype); + arg.ltype = hfaToArray.type(arg.type); } else if (canRewriteAsInt(ty, true)) { arg.rewrite = &integerRewrite; - arg.ltype = integerRewrite.type(arg.type, arg.ltype); + arg.ltype = integerRewrite.type(arg.type); } else { arg.rewrite = &compositeToArray64; - arg.ltype = compositeToArray64.type(arg.type, arg.ltype); + arg.ltype = compositeToArray64.type(arg.type); } } else if (ty->isintegral()) arg.attrs.add(ty->isunsigned() ? LLAttribute::ZExt : LLAttribute::SExt); diff --git a/gen/abi-win64.cpp b/gen/abi-win64.cpp index 729e9bfe78f..ed97cfa3ed2 100644 --- a/gen/abi-win64.cpp +++ b/gen/abi-win64.cpp @@ -145,7 +145,7 @@ struct Win64TargetABI : TargetABI { if (arg.rewrite) { LLType *originalLType = arg.ltype; - arg.ltype = arg.rewrite->type(arg.type, arg.ltype); + arg.ltype = arg.rewrite->type(arg.type); IF_LOG { Logger::println("Rewriting argument type %s", t->toChars()); diff --git a/gen/abi-x86-64.cpp b/gen/abi-x86-64.cpp index 906d8628a61..cd999ba44fa 100644 --- a/gen/abi-x86-64.cpp +++ b/gen/abi-x86-64.cpp @@ -182,7 +182,7 @@ struct X86_64_C_struct_rewrite : ABIRewrite { return DtoAllocaDump(v, dty, ".X86_64_C_struct_rewrite_dump"); } - LLType *type(Type *dty, LLType *t) override { return getAbiType(dty); } + LLType *type(Type *t) override { return getAbiType(t); } }; /** @@ -198,7 +198,7 @@ struct ImplicitByvalRewrite : ABIRewrite { LLValue *getLVal(Type *dty, LLValue *v) override { return v; } - LLType *type(Type *dty, LLType *t) override { return DtoPtrToType(dty); } + LLType *type(Type *t) override { return DtoPtrToType(t); } }; struct X86_64TargetABI : TargetABI { diff --git a/gen/abi-x86.cpp b/gen/abi-x86.cpp index 7515b11acd8..7202536a621 100644 --- a/gen/abi-x86.cpp +++ b/gen/abi-x86.cpp @@ -126,7 +126,7 @@ struct X86TargetABI : TargetABI { !(externD && rt->ty == Tcomplex32) && !integerRewrite.isObsoleteFor(fty.ret->ltype)) { fty.ret->rewrite = &integerRewrite; - fty.ret->ltype = integerRewrite.type(fty.ret->type, fty.ret->ltype); + fty.ret->ltype = integerRewrite.type(fty.ret->type); } } @@ -166,7 +166,7 @@ struct X86TargetABI : TargetABI { // rewrite aggregates as integers to make inreg work if (lastTy->ty == Tstruct || lastTy->ty == Tsarray) { last->rewrite = &integerRewrite; - last->ltype = integerRewrite.type(last->type, last->ltype); + last->ltype = integerRewrite.type(last->type); // undo byval semantics applied via passByVal() returning true last->byref = false; last->attrs.clear(); diff --git a/gen/abi.h b/gen/abi.h index 9fca2ac0c92..d7c8cc4b8ea 100644 --- a/gen/abi.h +++ b/gen/abi.h @@ -35,7 +35,9 @@ class Value; class FunctionType; } -// return rewrite rule +/// Transforms function arguments and return values. +/// This is needed to implement certain ABI aspects which LLVM doesn't get +/// right by default. struct ABIRewrite { virtual ~ABIRewrite() = default; @@ -48,10 +50,12 @@ struct ABIRewrite { /// Transforms the LL parameter back and returns the value for the D /// parameter. + /// Defaults to loading the lvalue returned by getLVal(). virtual llvm::Value *getRVal(Type *dty, llvm::Value *v); - /// should return the transformed type for this rewrite - virtual llvm::Type *type(Type *dty, llvm::Type *t) = 0; + /// Returns the resulting LL type when transforming an argument of the + /// specified D type. + virtual llvm::Type *type(Type *t) = 0; protected: /***** Static Helpers *****/ @@ -60,7 +64,7 @@ struct ABIRewrite { static llvm::Value *getAddressOf(DValue *v); /// Loads a LL value of a specified type from memory. The element type of the - /// provided pointer doesn't need to match the value type (=> suited for + /// provided pointer doesn't need to match the value type (=> suitable for /// bit-casting). static llvm::Value *loadFromMemory(llvm::Value *address, llvm::Type *asType, const char *name = ".bitcast_result"); From 0f41c0c681ff492fd4a54cd7c6d81d3e9c94abfa Mon Sep 17 00:00:00 2001 From: Martin Date: Sun, 29 May 2016 11:54:30 +0200 Subject: [PATCH 4/5] Refactor defining a function's explicit parameters --- gen/functions.cpp | 93 ++++++++++++++++++++++++++++------------------- 1 file changed, 56 insertions(+), 37 deletions(-) diff --git a/gen/functions.cpp b/gen/functions.cpp index 351f0f1dc71..e78a8aa1d9d 100644 --- a/gen/functions.cpp +++ b/gen/functions.cpp @@ -660,6 +660,57 @@ void verifyScopedDestructionInClosure(FuncDeclaration* fd) { } } +namespace { + +// Gives all explicit parameters storage and debug info. +// All explicit D parameters are lvalues, just like regular local variables. +void defineParameters(IrFuncTy &irFty, VarDeclarations ¶meters) { + // Not all arguments are necessarily passed on the LLVM level + // (e.g. zero-member structs), so we need to keep track of the + // index in the IrFuncTy args array separately. + size_t llArgIdx = 0; + + for (size_t i = 0; i < parameters.dim; ++i) { + auto *const vd = parameters[i]; + IrParameter *irparam = getIrParameter(vd); + + // vd->type (parameter) and irparam->arg->type (argument) don't always + // match. + // E.g., for a lazy parameter of type T, vd->type is T (with lazy storage + // class) while irparam->arg->type is the delegate type. + Type *const paramType = (irparam ? irparam->arg->type : vd->type); + + if (!irparam) { + // This is a parameter that is not passed on the LLVM level. + // Create the param here and set it to a "dummy" alloca that + // we do not store to here. + irparam = getIrParameter(vd, true); + irparam->value = DtoAlloca(vd, vd->ident->toChars()); + } else { + assert(irparam->value); + + if (irparam->arg->byref) { + // The argument is an appropriate lvalue passed by reference. + // Use the passed pointer as parameter storage. + assert(irparam->value->getType() == DtoPtrToType(paramType)); + } else { + // Let the ABI transform the parameter back to an lvalue. + irparam->value = + irFty.getParamLVal(paramType, llArgIdx, irparam->value); + } + + irparam->value->setName(vd->ident->toChars()); + + ++llArgIdx; + } + + if (global.params.symdebug) + gIR->DBuilder.EmitLocalVariable(irparam->value, vd, paramType); + } +} + +} // anonymous namespace + void DtoDefineFunction(FuncDeclaration *fd) { IF_LOG Logger::println("DtoDefineFunction(%s): %s", fd->toPrettyChars(), fd->loc.toChars()); @@ -868,7 +919,7 @@ void DtoDefineFunction(FuncDeclaration *fd) { #endif } - // give the 'this' argument storage and debug info + // give the 'this' parameter (an lvalue) storage and debug info if (irFty.arg_this) { LLValue *thisvar = irFunc->thisArg; assert(thisvar); @@ -893,46 +944,14 @@ void DtoDefineFunction(FuncDeclaration *fd) { gIR->DBuilder.EmitLocalVariable(thismem, fd->vthis, nullptr, true); } - // give the 'nestArg' storage + // give the 'nestArg' parameter (an lvalue) storage if (irFty.arg_nest) { irFunc->nestArg = DtoAllocaDump(irFunc->nestArg, 0, "nestedFrame"); } - // give arguments storage and debug info - if (fd->parameters) { - // Not all arguments are necessarily passed on the LLVM level - // (e.g. zero-member structs), so we need to keep track of the - // index in the IrFuncTy args array separately. - size_t llArgIdx = 0; - for (size_t i = 0; i < fd->parameters->dim; ++i) { - auto *const vd = (*fd->parameters)[i]; - - IrParameter *irparam = getIrParameter(vd); - Type *debugInfoType = vd->type; - if (!irparam) { - // This is a parameter that is not passed on the LLVM level. - // Create the param here and set it to a "dummy" alloca that - // we do not store to here. - irparam = getIrParameter(vd, true); - irparam->value = DtoAlloca(vd, vd->ident->toChars()); - } else { - if (!irparam->arg->byref) { - // let the abi transform the argument back first - LLValue *mem = irFty.getParamLVal(irparam->arg->type, llArgIdx, irparam->value); - mem->setName(vd->ident->toChars()); - - // set the arg var value to the alloca - irparam->value = mem; - - debugInfoType = irparam->arg->type; - } - ++llArgIdx; - } - - if (global.params.symdebug) - gIR->DBuilder.EmitLocalVariable(irparam->value, vd, debugInfoType); - } - } + // define all explicit parameters + if (fd->parameters) + defineParameters(irFty, *fd->parameters); { ScopeStack scopeStack(gIR); From 7778db00a86ebafbec63206b50d26d025c07070a Mon Sep 17 00:00:00 2001 From: Martin Date: Sun, 29 May 2016 13:47:39 +0200 Subject: [PATCH 5/5] Simplify generation of a call's return value --- gen/llvmhelpers.cpp | 1 - gen/structs.cpp | 2 +- gen/tocall.cpp | 41 +++++++---------------------------------- 3 files changed, 8 insertions(+), 36 deletions(-) diff --git a/gen/llvmhelpers.cpp b/gen/llvmhelpers.cpp index e9c6ec2fb85..dacd08969f2 100644 --- a/gen/llvmhelpers.cpp +++ b/gen/llvmhelpers.cpp @@ -1600,7 +1600,6 @@ DValue *DtoSymbolAddress(Loc &loc, Type *type, Declaration *decl) { if (vd->storage_class & STClazy) { Logger::println("lazy parameter"); assert(type->ty == Tdelegate); - return new DVarValue(type, getIrValue(vd)); } assert(!isSpecialRefVar(vd) && "Code not expected to handle special " "ref vars, although it can easily be " diff --git a/gen/structs.cpp b/gen/structs.cpp index 2a938ebd555..62ff8b50f5e 100644 --- a/gen/structs.cpp +++ b/gen/structs.cpp @@ -168,7 +168,7 @@ void DtoPaddedStruct(Type *dty, LLValue *v, LLValue *lval) { // Nested structs are the only members that can contain padding DtoPaddedStruct(fields[i]->type, fieldval, fieldptr); } else { - DtoStore(fieldval, fieldptr); + DtoStoreZextI8(fieldval, fieldptr); } } } diff --git a/gen/tocall.cpp b/gen/tocall.cpp index 21b4bb52961..bb2d2d45153 100644 --- a/gen/tocall.cpp +++ b/gen/tocall.cpp @@ -882,45 +882,21 @@ DValue *DtoCallFunction(Loc &loc, Type *resulttype, DValue *fnval, : 0); LLValue *retllval = (irFty.arg_sret ? args[sretArgIndex] : call.getInstruction()); + bool retValIsLVal = (tf->isref && returnTy != Tvoid) || (irFty.arg_sret != nullptr); - // Hack around LDC assuming structs and static arrays are in memory: - // If the function returns a struct or a static array, and the return - // value is not a pointer to a struct or a static array, store it to - // a stack slot before continuing. - bool storeReturnValueOnStack = - (returnTy == Tstruct && !isaPointer(retllval)) || - (returnTy == Tsarray && isaArray(retllval)); - - bool retValIsAlloca = false; - - // ignore ABI for intrinsics - const bool intrinsic = - (dfnval && dfnval->func && DtoIsIntrinsic(dfnval->func)); - if (!intrinsic && !irFty.arg_sret) { - // do ABI specific return value fixups - if (storeReturnValueOnStack) { - Logger::println("Storing return value to stack slot"); + if (!retValIsLVal) { + // let the ABI transform the return value back + if (DtoIsInMemoryOnly(returntype)) { retllval = irFty.getRetLVal(returntype, retllval); - retValIsAlloca = true; - storeReturnValueOnStack = false; + retValIsLVal = true; } else { retllval = irFty.getRetRVal(returntype, retllval); - storeReturnValueOnStack = - (returnTy == Tstruct && !isaPointer(retllval)) || - (returnTy == Tsarray && isaArray(retllval)); } } - if (storeReturnValueOnStack) { - Logger::println("Storing return value to stack slot"); - retllval = DtoAllocaDump(retllval, returntype); - retValIsAlloca = true; - } - // repaint the type if necessary Type *rbase = stripModifiers(resulttype->toBasetype(), true); Type *nextbase = stripModifiers(returntype->toBasetype(), true); - bool retinptr = irFty.arg_sret; if (!rbase->equals(nextbase)) { IF_LOG Logger::println("repainting return value from '%s' to '%s'", returntype->toChars(), rbase->toChars()); @@ -958,7 +934,7 @@ DValue *DtoCallFunction(Loc &loc, Type *resulttype, DValue *fnval, LLValue *val = DtoInsertValue(llvm::UndefValue::get(DtoType(rbase)), retllval, 0); retllval = DtoAllocaDump(val, rbase, ".aalvaluetmp"); - retinptr = true; + retValIsLVal = true; break; } // Fall through. @@ -1033,10 +1009,7 @@ DValue *DtoCallFunction(Loc &loc, Type *resulttype, DValue *fnval, return new DVarValue(resulttype, dfnval->vthis); } - // if we are returning through a pointer arg - // or if we are returning a reference - // make sure we provide a lvalue back! - if (retinptr || (tf->isref && returnTy != Tvoid) || retValIsAlloca) { + if (retValIsLVal) { return new DVarValue(resulttype, retllval); }