From f5ba4a40c7ae6c85a7cf2de58e7c34e2f6e7d206 Mon Sep 17 00:00:00 2001 From: Andrey Penechko Date: Mon, 3 Apr 2023 17:39:37 +0300 Subject: [PATCH] Add pragma(LDC_musttail) --- dmd/expression.d | 4 ++++ dmd/expression.h | 3 +++ dmd/id.d | 1 + dmd/id.h | 1 + dmd/statementsem.d | 35 +++++++++++++++++++++++++++++++++++ gen/aa.cpp | 10 +++++----- gen/arrays.cpp | 20 ++++++++++---------- gen/classes.cpp | 8 ++++---- gen/dpragma.d | 3 ++- gen/funcgenstate.cpp | 13 +++++++++++-- gen/funcgenstate.h | 5 +++-- gen/irstate.cpp | 27 ++++++++++++++------------- gen/irstate.h | 22 ++++++++++++---------- gen/llvmhelpers.cpp | 20 ++++++++++---------- gen/llvmhelpers.h | 3 ++- gen/nested.cpp | 2 +- gen/pragma.cpp | 9 +++++++++ gen/pragma.h | 3 ++- gen/tocall.cpp | 5 +++-- gen/toir.cpp | 8 ++++---- gen/trycatchfinally.cpp | 4 ++-- 21 files changed, 138 insertions(+), 68 deletions(-) diff --git a/dmd/expression.d b/dmd/expression.d index e0c4493ac0c..6c838598c62 100644 --- a/dmd/expression.d +++ b/dmd/expression.d @@ -5077,6 +5077,10 @@ extern (C++) final class CallExp : UnaExp bool directcall; // true if a virtual call is devirtualized bool inDebugStatement; /// true if this was in a debug statement bool ignoreAttributes; /// don't enforce attributes (e.g. call @gc function in @nogc code) +version (IN_LLVM) +{ + bool isMustTail; // If marked with pragma(musttail) +} VarDeclaration vthis2; // container for multi-context extern (D) this(const ref Loc loc, Expression e, Expressions* exps) diff --git a/dmd/expression.h b/dmd/expression.h index 89a3980b222..b46303abcde 100644 --- a/dmd/expression.h +++ b/dmd/expression.h @@ -863,6 +863,9 @@ class CallExp final : public UnaExp bool directcall; // true if a virtual call is devirtualized bool inDebugStatement; // true if this was in a debug statement bool ignoreAttributes; // don't enforce attributes (e.g. call @gc function in @nogc code) +#if IN_LLVM + bool isMustTail; // If marked with pragma(musttail) +#endif VarDeclaration *vthis2; // container for multi-context static CallExp *create(const Loc &loc, Expression *e, Expressions *exps); diff --git a/dmd/id.d b/dmd/id.d index 2b19c5ca72d..d3a0403af63 100644 --- a/dmd/id.d +++ b/dmd/id.d @@ -555,6 +555,7 @@ immutable Msgtable[] msgtable = { "LDC_global_crt_dtor" }, { "LDC_extern_weak" }, { "LDC_profile_instr" }, + { "musttail" }, // IN_LLVM: LDC-specific traits { "targetCPU" }, diff --git a/dmd/id.h b/dmd/id.h index 28255e68572..3683991fb71 100644 --- a/dmd/id.h +++ b/dmd/id.h @@ -76,6 +76,7 @@ struct Id static Identifier *LDC_inline_ir; static Identifier *LDC_extern_weak; static Identifier *LDC_profile_instr; + static Identifier *musttail; static Identifier *dcReflect; static Identifier *opencl; static Identifier *criticalenter; diff --git a/dmd/statementsem.d b/dmd/statementsem.d index ef387ac5640..16943b7745b 100644 --- a/dmd/statementsem.d +++ b/dmd/statementsem.d @@ -2135,6 +2135,10 @@ else return setError(); } } + else if (ps.ident == Id.musttail) + { + pragmaMustTailSemantic(ps); + } else if (!global.params.ignoreUnsupportedPragmas) { ps.error("unrecognized `pragma(%s)`", ps.ident.toChars()); @@ -2153,6 +2157,37 @@ else result = ps._body; } + private void pragmaMustTailSemantic(PragmaStatement ps) + { + if (!ps._body) + { + ps.error("`pragma(musttail)` must be attached to a return statement"); + return setError(); + } + + auto rs = ps._body.isReturnStatement(); + if (!rs) + { + ps.error("`pragma(musttail)` must be attached to a return statement"); + return setError(); + } + + if (!rs.exp) + { + ps.error("`pragma(musttail)` must be attached to a return statement returning result of a function call"); + return setError(); + } + + auto ce = rs.exp.isCallExp(); + if (!ce) + { + ps.error("`pragma(musttail)` must be attached to a return statement returning result of a function call"); + return setError(); + } + + ce.isMustTail = true; + } + override void visit(StaticAssertStatement s) { s.sa.semantic2(sc); diff --git a/gen/aa.cpp b/gen/aa.cpp index 84278e12b7f..485bc5bdbc6 100644 --- a/gen/aa.cpp +++ b/gen/aa.cpp @@ -64,11 +64,11 @@ DLValue *DtoAAIndex(const Loc &loc, Type *type, DValue *aa, DValue *key, DtoTypeInfoOf(loc, aa->type->unSharedOf()->mutableOf(), /*base=*/false); LLValue *castedAATI = DtoBitCast(rawAATI, funcTy->getParamType(1)); LLValue *valsize = DtoConstSize_t(getTypeAllocSize(DtoType(type))); - ret = gIR->CreateCallOrInvoke(func, aaval, castedAATI, valsize, pkey, + ret = gIR->CreateCallOrInvoke(loc, func, aaval, castedAATI, valsize, pkey, "aa.index"); } else { LLValue *keyti = to_keyti(loc, aa, funcTy->getParamType(1)); - ret = gIR->CreateCallOrInvoke(func, aaval, keyti, pkey, "aa.index"); + ret = gIR->CreateCallOrInvoke(loc, func, aaval, keyti, pkey, "aa.index"); } // cast return value @@ -130,7 +130,7 @@ DValue *DtoAAIn(const Loc &loc, Type *type, DValue *aa, DValue *key) { pkey = DtoBitCast(pkey, getVoidPtrType()); // call runtime - LLValue *ret = gIR->CreateCallOrInvoke(func, aaval, keyti, pkey, "aa.in"); + LLValue *ret = gIR->CreateCallOrInvoke(loc, func, aaval, keyti, pkey, "aa.in"); // cast return value LLType *targettype = DtoType(type); @@ -174,7 +174,7 @@ DValue *DtoAARemove(const Loc &loc, DValue *aa, DValue *key) { pkey = DtoBitCast(pkey, funcTy->getParamType(2)); // call runtime - LLValue *res = gIR->CreateCallOrInvoke(func, aaval, keyti, pkey); + LLValue *res = gIR->CreateCallOrInvoke(loc, func, aaval, keyti, pkey); return new DImValue(Type::tbool, res); } @@ -192,7 +192,7 @@ LLValue *DtoAAEquals(const Loc &loc, EXP op, DValue *l, DValue *r) { LLValue *abval = DtoBitCast(DtoRVal(r), funcTy->getParamType(2)); LLValue *aaTypeInfo = DtoTypeInfoOf(loc, t); LLValue *res = - gIR->CreateCallOrInvoke(func, aaTypeInfo, aaval, abval, "aaEqRes"); + gIR->CreateCallOrInvoke(loc, func, aaTypeInfo, aaval, abval, "aaEqRes"); const auto predicate = eqTokToICmpPred(op, /* invert = */ true); res = gIR->ir->CreateICmp(predicate, res, DtoConstInt(0)); diff --git a/gen/arrays.cpp b/gen/arrays.cpp index 14b1b26614d..726a430be45 100644 --- a/gen/arrays.cpp +++ b/gen/arrays.cpp @@ -168,7 +168,7 @@ static void copySlice(const Loc &loc, LLValue *dstarr, LLValue *dstlen, if (checksEnabled && !knownInBounds) { LLFunction *fn = getRuntimeFunction(loc, gIR->module, "_d_array_slice_copy"); gIR->CreateCallOrInvoke( - fn, {dstarr, dstlen, srcarr, srclen, DtoConstSize_t(elementSize)}, "", + loc, fn, {dstarr, dstlen, srcarr, srclen, DtoConstSize_t(elementSize)}, "", /*isNothrow=*/true); } else { // We might have dstarr == srcarr at compile time, but as long as @@ -271,7 +271,7 @@ void DtoArrayAssign(const Loc &loc, DValue *lhs, DValue *rhs, EXP op, loc, gIR->module, !canSkipPostblit ? "_d_arrayassign_l" : "_d_arrayassign_r"); gIR->CreateCallOrInvoke( - fn, DtoTypeInfoOf(loc, elemType), DtoSlice(rhsPtr, rhsLength, getI8Type()), + loc, fn, DtoTypeInfoOf(loc, elemType), DtoSlice(rhsPtr, rhsLength, getI8Type()), DtoSlice(lhsPtr, lhsLength, getI8Type()), DtoBitCast(tmpSwap, getVoidPtrType())); } } else { @@ -305,7 +305,7 @@ void DtoArrayAssign(const Loc &loc, DValue *lhs, DValue *rhs, EXP op, LLFunction *fn = getRuntimeFunction(loc, gIR->module, "_d_arraysetassign"); gIR->CreateCallOrInvoke( - fn, lhsPtr, DtoBitCast(makeLValue(loc, rhs), getVoidPtrType()), + loc, fn, lhsPtr, DtoBitCast(makeLValue(loc, rhs), getVoidPtrType()), gIR->ir->CreateTruncOrBitCast(lhsLength, LLType::getInt32Ty(gIR->context())), DtoTypeInfoOf(loc, stripModifiers(t2))); @@ -672,7 +672,7 @@ DSliceValue *DtoNewDynArray(const Loc &loc, Type *arrayType, DValue *dim, // call allocator LLValue *newArray = - gIR->CreateCallOrInvoke(fn, arrayTypeInfo, arrayLen, ".gc_mem"); + gIR->CreateCallOrInvoke(loc, fn, arrayTypeInfo, arrayLen, ".gc_mem"); // return a DSliceValue with the well-known length for better optimizability auto ptr = @@ -741,7 +741,7 @@ DSliceValue *DtoNewMulDimDynArray(const Loc &loc, Type *arrayType, // call allocator LLValue *newptr = - gIR->CreateCallOrInvoke(fn, arrayTypeInfo, DtoLoad(dtype, darray), ".gc_mem"); + gIR->CreateCallOrInvoke(loc, fn, arrayTypeInfo, DtoLoad(dtype, darray), ".gc_mem"); IF_LOG Logger::cout() << "final ptr = " << *newptr << '\n'; @@ -769,7 +769,7 @@ DSliceValue *DtoResizeDynArray(const Loc &loc, Type *arrayType, DValue *array, : "_d_arraysetlengthiT"); LLValue *newArray = gIR->CreateCallOrInvoke( - fn, DtoTypeInfoOf(loc, arrayType), newdim, + loc, fn, DtoTypeInfoOf(loc, arrayType), newdim, DtoBitCast(DtoLVal(array), fn->getFunctionType()->getParamType(2)), ".gc_mem"); @@ -871,7 +871,7 @@ DSliceValue *DtoCatArrays(const Loc &loc, Type *arrayType, Expression *exp1, args.push_back(loadArray(exp2,2)); } - auto newArray = gIR->CreateCallOrInvoke(fn, args, ".appendedArray"); + auto newArray = gIR->CreateCallOrInvoke(loc, fn, args, ".appendedArray"); return getSlice(arrayType, newArray); } @@ -886,7 +886,7 @@ DSliceValue *DtoAppendDChar(const Loc &loc, DValue *arr, Expression *exp, // Call function (ref string x, dchar c) LLValue *newArray = gIR->CreateCallOrInvoke( - fn, DtoBitCast(DtoLVal(arr), fn->getFunctionType()->getParamType(0)), + loc, fn, DtoBitCast(DtoLVal(arr), fn->getFunctionType()->getParamType(0)), DtoBitCast(valueToAppend, fn->getFunctionType()->getParamType(1)), ".appendedArray"); @@ -942,7 +942,7 @@ LLValue *DtoArrayEqCmp_impl(const Loc &loc, const char *func, DValue *l, args.push_back(DtoBitCast(tival, fn->getFunctionType()->getParamType(2))); } - return gIR->CreateCallOrInvoke(fn, args); + return gIR->CreateCallOrInvoke(loc, fn, args); } /// When `true` is returned, the type can be compared using `memcmp`. @@ -1324,7 +1324,7 @@ static void emitRangeErrorImpl(IRState *irs, const Loc &loc, args.push_back(DtoModuleFileName(module, loc)); args.push_back(DtoConstUint(loc.linnum)); args.insert(args.end(), extraArgs.begin(), extraArgs.end()); - irs->CreateCallOrInvoke(fn, args); + irs->CreateCallOrInvoke(loc, fn, args); irs->ir->CreateUnreachable(); break; } diff --git a/gen/classes.cpp b/gen/classes.cpp index 22f3d20846a..dcb6eafd04d 100644 --- a/gen/classes.cpp +++ b/gen/classes.cpp @@ -90,7 +90,7 @@ DValue *DtoNewClass(const Loc &loc, TypeClass *tc, NewExp *newexp) { LLConstant *ci = DtoBitCast(irClass->getClassInfoSymbol(), DtoType(getClassInfoType())); mem = gIR->CreateCallOrInvoke( - fn, ci, useEHAlloc ? ".newthrowable_alloc" : ".newclass_gc_alloc"); + loc, fn, ci, useEHAlloc ? ".newthrowable_alloc" : ".newclass_gc_alloc"); mem = DtoBitCast(mem, DtoType(tc), useEHAlloc ? ".newthrowable" : ".newclass_gc"); doInit = !useEHAlloc; @@ -183,7 +183,7 @@ void DtoFinalizeClass(const Loc &loc, LLValue *inst) { getRuntimeFunction(loc, gIR->module, "_d_callfinalizer"); gIR->CreateCallOrInvoke( - fn, DtoBitCast(inst, fn->getFunctionType()->getParamType(0)), ""); + loc, fn, DtoBitCast(inst, fn->getFunctionType()->getParamType(0)), ""); } //////////////////////////////////////////////////////////////////////////////// @@ -378,7 +378,7 @@ DValue *DtoDynamicCastObject(const Loc &loc, DValue *val, Type *_to) { assert(funcTy->getParamType(1) == cinfo->getType()); // call it - LLValue *ret = gIR->CreateCallOrInvoke(func, obj, cinfo); + LLValue *ret = gIR->CreateCallOrInvoke(loc, func, obj, cinfo); // cast return value ret = DtoBitCast(ret, DtoType(_to)); @@ -412,7 +412,7 @@ DValue *DtoDynamicCastInterface(const Loc &loc, DValue *val, Type *_to) { cinfo = DtoBitCast(cinfo, funcTy->getParamType(1)); // call it - LLValue *ret = gIR->CreateCallOrInvoke(func, ptr, cinfo); + LLValue *ret = gIR->CreateCallOrInvoke(loc, func, ptr, cinfo); // cast return value ret = DtoBitCast(ret, DtoType(_to)); diff --git a/gen/dpragma.d b/gen/dpragma.d index 36c30bb988c..db69285b3be 100644 --- a/gen/dpragma.d +++ b/gen/dpragma.d @@ -44,7 +44,8 @@ extern (C++) enum LDCPragma : int { LLVMbitop_bts, LLVMbitop_vld, LLVMbitop_vst, - LLVMextern_weak + LLVMextern_weak, + LLVMmusttail, }; extern (C++) LDCPragma DtoGetPragma(Scope* sc, PragmaDeclaration decl, ref const(char)* arg1str); diff --git a/gen/funcgenstate.cpp b/gen/funcgenstate.cpp index fbdb9a34625..54f9bcd66ed 100644 --- a/gen/funcgenstate.cpp +++ b/gen/funcgenstate.cpp @@ -9,6 +9,7 @@ #include "gen/funcgenstate.h" +#include "dmd/errors.h" #include "dmd/identifier.h" #include "gen/llvm.h" #include "gen/llvmhelpers.h" @@ -103,10 +104,10 @@ FuncGenState::FuncGenState(IrFunction &irFunc, IRState &irs) : irFunc(irFunc), scopes(irs), jumpTargets(scopes), switchTargets(), irs(irs) {} -LLCallBasePtr FuncGenState::callOrInvoke(llvm::Value *callee, +LLCallBasePtr FuncGenState::callOrInvoke(const Loc &loc, llvm::Value *callee, llvm::FunctionType *calleeType, llvm::ArrayRef args, - const char *name, bool isNothrow) { + const char *name, bool isNothrow, bool isMustTail) { // If this is a direct call, we might be able to use the callee attributes // to our advantage. llvm::Function *calleeFn = llvm::dyn_cast(callee); @@ -135,9 +136,17 @@ LLCallBasePtr FuncGenState::callOrInvoke(llvm::Value *callee, if (calleeFn) { call->setAttributes(calleeFn->getAttributes()); } + if (isMustTail) { + call->setTailCallKind(llvm::CallInst::TCK_MustTail); + } return call; } + if (isMustTail) { + error(loc, "cannot perform tail-call, there is code after call"); + fatal(); + } + llvm::BasicBlock *landingPad = scopes.getLandingPad(); llvm::BasicBlock *postinvoke = irs.insertBB("postinvoke"); diff --git a/gen/funcgenstate.h b/gen/funcgenstate.h index af719c74b8a..56e1b00105b 100644 --- a/gen/funcgenstate.h +++ b/gen/funcgenstate.h @@ -199,10 +199,11 @@ class FuncGenState { /// Emits a call or invoke to the given callee, depending on whether there /// are catches/cleanups active or not. - LLCallBasePtr callOrInvoke(llvm::Value *callee, + LLCallBasePtr callOrInvoke(const Loc &loc, llvm::Value *callee, llvm::FunctionType *calleeType, llvm::ArrayRef args, - const char *name = "", bool isNothrow = false); + const char *name = "", bool isNothrow = false, + bool isMustTail = false); private: IRState &irs; diff --git a/gen/irstate.cpp b/gen/irstate.cpp index 5894088dbba..55f66c40723 100644 --- a/gen/irstate.cpp +++ b/gen/irstate.cpp @@ -79,43 +79,44 @@ llvm::BasicBlock *IRState::insertBB(const llvm::Twine &name) { return insertBBAfter(scopebb(), name); } -llvm::Instruction *IRState::CreateCallOrInvoke(LLFunction *Callee, +llvm::Instruction *IRState::CreateCallOrInvoke(const Loc &loc, LLFunction *Callee, const char *Name) { - return CreateCallOrInvoke(Callee, {}, Name); + return CreateCallOrInvoke(loc, Callee, {}, Name); } -llvm::Instruction *IRState::CreateCallOrInvoke(LLFunction *Callee, +llvm::Instruction *IRState::CreateCallOrInvoke(const Loc &loc, LLFunction *Callee, llvm::ArrayRef Args, const char *Name, bool isNothrow) { - return funcGen().callOrInvoke(Callee, Callee->getFunctionType(), Args, Name, - isNothrow); + return funcGen().callOrInvoke(loc, Callee, Callee->getFunctionType(), Args, + Name, isNothrow); } -llvm::Instruction *IRState::CreateCallOrInvoke(LLFunction *Callee, +llvm::Instruction *IRState::CreateCallOrInvoke(const Loc &loc, + LLFunction *Callee, LLValue *Arg1, const char *Name) { - return CreateCallOrInvoke(Callee, llvm::ArrayRef(Arg1), Name); + return CreateCallOrInvoke(loc, Callee, llvm::ArrayRef(Arg1), Name); } -llvm::Instruction *IRState::CreateCallOrInvoke(LLFunction *Callee, +llvm::Instruction *IRState::CreateCallOrInvoke(const Loc &loc, LLFunction *Callee, LLValue *Arg1, LLValue *Arg2, const char *Name) { - return CreateCallOrInvoke(Callee, {Arg1, Arg2}, Name); + return CreateCallOrInvoke(loc, Callee, {Arg1, Arg2}, Name); } -llvm::Instruction *IRState::CreateCallOrInvoke(LLFunction *Callee, +llvm::Instruction *IRState::CreateCallOrInvoke(const Loc &loc, LLFunction *Callee, LLValue *Arg1, LLValue *Arg2, LLValue *Arg3, const char *Name) { - return CreateCallOrInvoke(Callee, {Arg1, Arg2, Arg3}, Name); + return CreateCallOrInvoke(loc, Callee, {Arg1, Arg2, Arg3}, Name); } -llvm::Instruction *IRState::CreateCallOrInvoke(LLFunction *Callee, +llvm::Instruction *IRState::CreateCallOrInvoke(const Loc &loc, LLFunction *Callee, LLValue *Arg1, LLValue *Arg2, LLValue *Arg3, LLValue *Arg4, const char *Name) { - return CreateCallOrInvoke(Callee, {Arg1, Arg2, Arg3, Arg4}, Name); + return CreateCallOrInvoke(loc, Callee, {Arg1, Arg2, Arg3, Arg4}, Name); } bool IRState::emitArrayBoundsChecks() { diff --git a/gen/irstate.h b/gen/irstate.h index fbd69cd4a36..6c81c6530ce 100644 --- a/gen/irstate.h +++ b/gen/irstate.h @@ -179,22 +179,24 @@ struct IRState { llvm::BasicBlock *insertBB(const llvm::Twine &name); // create a call or invoke, depending on the landing pad info - llvm::Instruction *CreateCallOrInvoke(LLFunction *Callee, + llvm::Instruction *CreateCallOrInvoke(const Loc &loc, LLFunction *Callee, const char *Name = ""); - llvm::Instruction *CreateCallOrInvoke(LLFunction *Callee, + llvm::Instruction *CreateCallOrInvoke(const Loc &loc, LLFunction *Callee, llvm::ArrayRef Args, const char *Name = "", bool isNothrow = false); - llvm::Instruction *CreateCallOrInvoke(LLFunction *Callee, LLValue *Arg1, + llvm::Instruction *CreateCallOrInvoke(const Loc &loc, LLFunction *Callee, + LLValue *Arg1, const char *Name = ""); + llvm::Instruction *CreateCallOrInvoke(const Loc &loc, LLFunction *Callee, + LLValue *Arg1, LLValue *Arg2, const char *Name = ""); - llvm::Instruction *CreateCallOrInvoke(LLFunction *Callee, LLValue *Arg1, - LLValue *Arg2, const char *Name = ""); - llvm::Instruction *CreateCallOrInvoke(LLFunction *Callee, LLValue *Arg1, - LLValue *Arg2, LLValue *Arg3, + llvm::Instruction *CreateCallOrInvoke(const Loc &loc, LLFunction *Callee, + LLValue *Arg1, LLValue *Arg2, + LLValue *Arg3, const char *Name = ""); + llvm::Instruction *CreateCallOrInvoke(const Loc &loc, LLFunction *Callee, + LLValue *Arg1, LLValue *Arg2, + LLValue *Arg3, LLValue *Arg4, const char *Name = ""); - llvm::Instruction *CreateCallOrInvoke(LLFunction *Callee, LLValue *Arg1, - LLValue *Arg2, LLValue *Arg3, - LLValue *Arg4, const char *Name = ""); // this holds the array being indexed or sliced so $ will work // might be a better way but it works. problem is I only get a diff --git a/gen/llvmhelpers.cpp b/gen/llvmhelpers.cpp index 792f11cc5a7..46b104c17d7 100644 --- a/gen/llvmhelpers.cpp +++ b/gen/llvmhelpers.cpp @@ -86,7 +86,7 @@ LLValue *DtoNew(const Loc &loc, Type *newtype) { LLConstant *ti = DtoTypeInfoOf(loc, newtype); assert(isaPointer(ti)); // call runtime allocator - LLValue *mem = gIR->CreateCallOrInvoke(fn, ti, ".gc_mem"); + LLValue *mem = gIR->CreateCallOrInvoke(loc, fn, ti, ".gc_mem"); // cast return DtoBitCast(mem, DtoPtrToType(newtype), ".gc_mem"); } @@ -96,7 +96,7 @@ LLValue *DtoNewStruct(const Loc &loc, TypeStruct *newtype) { loc, gIR->module, newtype->isZeroInit(newtype->sym->loc) ? "_d_newitemT" : "_d_newitemiT"); LLConstant *ti = DtoTypeInfoOf(loc, newtype); - LLValue *mem = gIR->CreateCallOrInvoke(fn, ti, ".gc_struct"); + LLValue *mem = gIR->CreateCallOrInvoke(loc, fn, ti, ".gc_struct"); return DtoBitCast(mem, DtoPtrToType(newtype), ".gc_struct"); } @@ -104,14 +104,14 @@ void DtoDeleteMemory(const Loc &loc, DValue *ptr) { llvm::Function *fn = getRuntimeFunction(loc, gIR->module, "_d_delmemory"); LLValue *lval = (ptr->isLVal() ? DtoLVal(ptr) : makeLValue(loc, ptr)); gIR->CreateCallOrInvoke( - fn, DtoBitCast(lval, fn->getFunctionType()->getParamType(0))); + loc, fn, DtoBitCast(lval, fn->getFunctionType()->getParamType(0))); } void DtoDeleteStruct(const Loc &loc, DValue *ptr) { llvm::Function *fn = getRuntimeFunction(loc, gIR->module, "_d_delstruct"); LLValue *lval = (ptr->isLVal() ? DtoLVal(ptr) : makeLValue(loc, ptr)); gIR->CreateCallOrInvoke( - fn, DtoBitCast(lval, fn->getFunctionType()->getParamType(0)), + loc, fn, DtoBitCast(lval, fn->getFunctionType()->getParamType(0)), DtoBitCast(DtoTypeInfoOf(loc, ptr->type->nextOf()), fn->getFunctionType()->getParamType(1))); } @@ -120,14 +120,14 @@ void DtoDeleteClass(const Loc &loc, DValue *inst) { llvm::Function *fn = getRuntimeFunction(loc, gIR->module, "_d_delclass"); LLValue *lval = (inst->isLVal() ? DtoLVal(inst) : makeLValue(loc, inst)); gIR->CreateCallOrInvoke( - fn, DtoBitCast(lval, fn->getFunctionType()->getParamType(0))); + loc, fn, DtoBitCast(lval, fn->getFunctionType()->getParamType(0))); } void DtoDeleteInterface(const Loc &loc, DValue *inst) { llvm::Function *fn = getRuntimeFunction(loc, gIR->module, "_d_delinterface"); LLValue *lval = (inst->isLVal() ? DtoLVal(inst) : makeLValue(loc, inst)); gIR->CreateCallOrInvoke( - fn, DtoBitCast(lval, fn->getFunctionType()->getParamType(0))); + loc, fn, DtoBitCast(lval, fn->getFunctionType()->getParamType(0))); } void DtoDeleteArray(const Loc &loc, DValue *arr) { @@ -142,7 +142,7 @@ void DtoDeleteArray(const Loc &loc, DValue *arr) { : DtoTypeInfoOf(loc, elementType); LLValue *lval = (arr->isLVal() ? DtoLVal(arr) : makeLValue(loc, arr)); - gIR->CreateCallOrInvoke(fn, DtoBitCast(lval, fty->getParamType(0)), + gIR->CreateCallOrInvoke(loc, fn, DtoBitCast(lval, fty->getParamType(0)), DtoBitCast(typeInfo, fty->getParamType(1))); } @@ -282,7 +282,7 @@ void DtoAssert(Module *M, const Loc &loc, DValue *msg) { args.push_back(DtoConstUint(loc.linnum)); // call - gIR->CreateCallOrInvoke(fn, args); + gIR->CreateCallOrInvoke(loc, fn, args); // after assert is always unreachable gIR->ir->CreateUnreachable(); @@ -323,7 +323,7 @@ void DtoCAssert(Module *M, const Loc &loc, LLValue *msg) { args.push_back(line); } - gIR->CreateCallOrInvoke(fn, args); + gIR->CreateCallOrInvoke(loc, fn, args); gIR->ir->CreateUnreachable(); } @@ -336,7 +336,7 @@ void DtoThrow(const Loc &loc, DValue *e) { LLFunction *fn = getRuntimeFunction(loc, gIR->module, "_d_throw_exception"); LLValue *arg = DtoBitCast(DtoRVal(e), fn->getFunctionType()->getParamType(0)); - gIR->CreateCallOrInvoke(fn, arg); + gIR->CreateCallOrInvoke(loc, fn, arg); gIR->ir->CreateUnreachable(); llvm::BasicBlock *bb = gIR->insertBB("afterthrow"); diff --git a/gen/llvmhelpers.h b/gen/llvmhelpers.h index 7dbacbefda6..ec0b04c6046 100644 --- a/gen/llvmhelpers.h +++ b/gen/llvmhelpers.h @@ -206,7 +206,8 @@ bool DtoLowerMagicIntrinsic(IRState *p, FuncDeclaration *fndecl, CallExp *e, /// DValue *DtoCallFunction(const Loc &loc, Type *resulttype, DValue *fnval, - Expressions *arguments, LLValue *sretPointer = nullptr); + Expressions *arguments, LLValue *sretPointer = nullptr, + bool isMustTail = false); Type *stripModifiers(Type *type, bool transitive = false); diff --git a/gen/nested.cpp b/gen/nested.cpp index 2e85a1e6745..04254ec06a0 100644 --- a/gen/nested.cpp +++ b/gen/nested.cpp @@ -503,7 +503,7 @@ void DtoCreateNestedContext(FuncGenState &funcGen) { if (frameAlignment > 16) // GC guarantees an alignment of 16 size += frameAlignment - 16; LLValue *mem = - gIR->CreateCallOrInvoke(fn, DtoConstSize_t(size), ".gc_frame"); + gIR->CreateCallOrInvoke(fd->loc, fn, DtoConstSize_t(size), ".gc_frame"); if (frameAlignment <= 16) { frame = DtoBitCast(mem, frameType->getPointerTo(), ".frame"); } else { diff --git a/gen/pragma.cpp b/gen/pragma.cpp index 0702d1c7b1f..08b176e695e 100644 --- a/gen/pragma.cpp +++ b/gen/pragma.cpp @@ -331,6 +331,15 @@ LDCPragma DtoGetPragma(Scope *sc, PragmaDeclaration *decl, return LLVMprofile_instr; } + // pragma(musttail) + if (ident == Id::musttail) { + if (args && args->length > 0) { + decl->error("takes no parameters"); + fatal(); + } + return LLVMmusttail; + } + return LLVMnone; } diff --git a/gen/pragma.h b/gen/pragma.h index 4fae84e5fae..8691b3e301b 100644 --- a/gen/pragma.h +++ b/gen/pragma.h @@ -48,7 +48,8 @@ enum LDCPragma { LLVMbitop_vld, LLVMbitop_vst, LLVMextern_weak, - LLVMprofile_instr + LLVMprofile_instr, + LLVMmusttail, }; LDCPragma DtoGetPragma(Scope *sc, PragmaDeclaration *decl, const char *&arg1str); diff --git a/gen/tocall.cpp b/gen/tocall.cpp index 16da4ba086d..9f53b3ebeb6 100644 --- a/gen/tocall.cpp +++ b/gen/tocall.cpp @@ -841,7 +841,8 @@ static LLValue *DtoCallableValue(llvm::FunctionType * ft,DValue *fn) { // FIXME: this function is a mess ! DValue *DtoCallFunction(const Loc &loc, Type *resulttype, DValue *fnval, - Expressions *arguments, LLValue *sretPointer) { + Expressions *arguments, LLValue *sretPointer, + bool isMustTail) { IF_LOG Logger::println("DtoCallFunction()"); LOG_SCOPE @@ -917,7 +918,7 @@ DValue *DtoCallFunction(const Loc &loc, Type *resulttype, DValue *fnval, // call the function LLCallBasePtr call = gIR->funcGen().callOrInvoke(callable, callableTy, args, - "", tf->isnothrow()); + "", tf->isnothrow(), isMustTail); // PGO: Insert instrumentation or attach profile metadata at indirect call // sites. diff --git a/gen/toir.cpp b/gen/toir.cpp index 6a2c00052e0..5af307e0343 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -777,7 +777,7 @@ class ToElemVisitor : public Visitor { } DValue *result = - DtoCallFunction(e->loc, e->type, fnval, e->arguments, sretPointer); + DtoCallFunction(e->loc, e->type, fnval, e->arguments, sretPointer, e->isMustTail); if (delayedDtorVar) { delayedDtorVar->edtor = delayedDtorExp; @@ -1587,7 +1587,7 @@ class ToElemVisitor : public Visitor { LLValue *aaTypeInfo = DtoBitCast(DtoTypeInfoOf(e->loc, stripModifiers(taa), /*base=*/false), DtoType(getAssociativeArrayTypeInfoType())); - LLValue *aa = gIR->CreateCallOrInvoke(func, aaTypeInfo, "aa"); + LLValue *aa = gIR->CreateCallOrInvoke(e->loc, func, aaTypeInfo, "aa"); result = new DImValue(e->type, aa); } // new basic type @@ -1785,7 +1785,7 @@ class ToElemVisitor : public Visitor { const auto arg = DtoBitCast(DtoRVal(cond), fn->getFunctionType()->getParamType(0)); - gIR->CreateCallOrInvoke(fn, arg); + gIR->CreateCallOrInvoke(e->loc, fn, arg); } else if (condty->ty == TY::Tpointer && condty->nextOf()->ty == TY::Tstruct) { const auto invDecl = @@ -2517,7 +2517,7 @@ class ToElemVisitor : public Visitor { slice = DtoConstSlice(DtoConstSize_t(e->keys->length), slice); LLValue *valuesArray = DtoSlicePaint(slice, funcTy->getParamType(2)); - LLValue *aa = gIR->CreateCallOrInvoke(func, aaTypeInfo, keysArray, + LLValue *aa = gIR->CreateCallOrInvoke(e->loc, func, aaTypeInfo, keysArray, valuesArray, "aa"); if (basetype->ty != TY::Taarray) { LLValue *tmp = DtoAlloca(e->type, "aaliteral"); diff --git a/gen/trycatchfinally.cpp b/gen/trycatchfinally.cpp index f27e42ad656..b624fe15623 100644 --- a/gen/trycatchfinally.cpp +++ b/gen/trycatchfinally.cpp @@ -270,8 +270,8 @@ void emitBeginCatchMSVC(IRState &irs, Catch *ctch, if (!isCPPclass) { auto enterCatchFn = getRuntimeFunction(ctch->loc, irs.module, "_d_eh_enter_catch"); - irs.CreateCallOrInvoke(enterCatchFn, DtoBitCast(exnObj, getVoidPtrType()), - clssInfo); + irs.CreateCallOrInvoke(ctch->loc, enterCatchFn, + DtoBitCast(exnObj, getVoidPtrType()), clssInfo); } } }