Skip to content

Commit 628cb3b

Browse files
committed
Add pragma(musttail)
1 parent 0d4d711 commit 628cb3b

12 files changed

+92
-5
lines changed

dmd/expression.d

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5077,6 +5077,10 @@ extern (C++) final class CallExp : UnaExp
50775077
bool directcall; // true if a virtual call is devirtualized
50785078
bool inDebugStatement; /// true if this was in a debug statement
50795079
bool ignoreAttributes; /// don't enforce attributes (e.g. call @gc function in @nogc code)
5080+
version (IN_LLVM)
5081+
{
5082+
bool isMustTail; // If marked with pragma(musttail)
5083+
}
50805084
VarDeclaration vthis2; // container for multi-context
50815085

50825086
extern (D) this(const ref Loc loc, Expression e, Expressions* exps)

dmd/expression.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -863,6 +863,9 @@ class CallExp final : public UnaExp
863863
bool directcall; // true if a virtual call is devirtualized
864864
bool inDebugStatement; // true if this was in a debug statement
865865
bool ignoreAttributes; // don't enforce attributes (e.g. call @gc function in @nogc code)
866+
#if IN_LLVM
867+
bool isMustTail; // If marked with pragma(musttail)
868+
#endif
866869
VarDeclaration *vthis2; // container for multi-context
867870

868871
static CallExp *create(const Loc &loc, Expression *e, Expressions *exps);

dmd/id.d

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,7 @@ immutable Msgtable[] msgtable =
555555
{ "LDC_global_crt_dtor" },
556556
{ "LDC_extern_weak" },
557557
{ "LDC_profile_instr" },
558+
{ "musttail" },
558559

559560
// IN_LLVM: LDC-specific traits
560561
{ "targetCPU" },

dmd/id.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ struct Id
7676
static Identifier *LDC_inline_ir;
7777
static Identifier *LDC_extern_weak;
7878
static Identifier *LDC_profile_instr;
79+
static Identifier *musttail;
7980
static Identifier *dcReflect;
8081
static Identifier *opencl;
8182
static Identifier *criticalenter;

dmd/statementsem.d

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2135,6 +2135,13 @@ else
21352135
return setError();
21362136
}
21372137
}
2138+
else if (ps.ident == Id.musttail)
2139+
{
2140+
version (IN_LLVM)
2141+
{
2142+
pragmaMustTailSemantic(ps);
2143+
}
2144+
}
21382145
else if (!global.params.ignoreUnsupportedPragmas)
21392146
{
21402147
ps.error("unrecognized `pragma(%s)`", ps.ident.toChars());
@@ -2153,6 +2160,38 @@ else
21532160
result = ps._body;
21542161
}
21552162

2163+
version (IN_LLVM)
2164+
private void pragmaMustTailSemantic(PragmaStatement ps)
2165+
{
2166+
if (!ps._body)
2167+
{
2168+
ps.error("`pragma(musttail)` must be attached to a return statement");
2169+
return setError();
2170+
}
2171+
2172+
auto rs = ps._body.isReturnStatement();
2173+
if (!rs)
2174+
{
2175+
ps.error("`pragma(musttail)` must be attached to a return statement");
2176+
return setError();
2177+
}
2178+
2179+
if (!rs.exp)
2180+
{
2181+
ps.error("`pragma(musttail)` must be attached to a return statement returning result of a function call");
2182+
return setError();
2183+
}
2184+
2185+
auto ce = rs.exp.isCallExp();
2186+
if (!ce)
2187+
{
2188+
ps.error("`pragma(musttail)` must be attached to a return statement returning result of a function call");
2189+
return setError();
2190+
}
2191+
2192+
ce.isMustTail = true;
2193+
}
2194+
21562195
override void visit(StaticAssertStatement s)
21572196
{
21582197
s.sa.semantic2(sc);

gen/dpragma.d

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ extern (C++) enum LDCPragma : int {
4444
LLVMbitop_bts,
4545
LLVMbitop_vld,
4646
LLVMbitop_vst,
47-
LLVMextern_weak
47+
LLVMextern_weak,
48+
LLVMmusttail,
4849
};
4950

5051
extern (C++) LDCPragma DtoGetPragma(Scope* sc, PragmaDeclaration decl, ref const(char)* arg1str);

gen/llvmhelpers.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,8 @@ bool DtoLowerMagicIntrinsic(IRState *p, FuncDeclaration *fndecl, CallExp *e,
206206

207207
///
208208
DValue *DtoCallFunction(const Loc &loc, Type *resulttype, DValue *fnval,
209-
Expressions *arguments, LLValue *sretPointer = nullptr);
209+
Expressions *arguments, LLValue *sretPointer = nullptr,
210+
bool isMustTail = false);
210211

211212
Type *stripModifiers(Type *type, bool transitive = false);
212213

gen/pragma.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,15 @@ LDCPragma DtoGetPragma(Scope *sc, PragmaDeclaration *decl,
331331
return LLVMprofile_instr;
332332
}
333333

334+
// pragma(musttail)
335+
if (ident == Id::musttail) {
336+
if (args && args->length > 0) {
337+
decl->error("takes no parameters");
338+
fatal();
339+
}
340+
return LLVMmusttail;
341+
}
342+
334343
return LLVMnone;
335344
}
336345

gen/pragma.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ enum LDCPragma {
4848
LLVMbitop_vld,
4949
LLVMbitop_vst,
5050
LLVMextern_weak,
51-
LLVMprofile_instr
51+
LLVMprofile_instr,
52+
LLVMmusttail,
5253
};
5354

5455
LDCPragma DtoGetPragma(Scope *sc, PragmaDeclaration *decl, const char *&arg1str);

gen/tocall.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -841,7 +841,8 @@ static LLValue *DtoCallableValue(llvm::FunctionType * ft,DValue *fn) {
841841

842842
// FIXME: this function is a mess !
843843
DValue *DtoCallFunction(const Loc &loc, Type *resulttype, DValue *fnval,
844-
Expressions *arguments, LLValue *sretPointer) {
844+
Expressions *arguments, LLValue *sretPointer,
845+
bool isMustTail) {
845846
IF_LOG Logger::println("DtoCallFunction()");
846847
LOG_SCOPE
847848

@@ -1065,6 +1066,18 @@ DValue *DtoCallFunction(const Loc &loc, Type *resulttype, DValue *fnval,
10651066
llvm::AttrBuilder(call->getAttributes(), LLAttributeList::FunctionIndex));
10661067
#endif
10671068
call->setAttributes(attrlist);
1069+
if (isMustTail) {
1070+
if (auto ci = llvm::dyn_cast<llvm::CallInst>(call)) {
1071+
ci->setTailCallKind(llvm::CallInst::TCK_MustTail);
1072+
} else {
1073+
if (!tf->isnothrow()) {
1074+
error(loc, "cannot perform tail-call - callee must be nothrow");
1075+
} else {
1076+
error(loc, "cannot perform tail-call - no code like destructors or scope(exit) should run after the call");
1077+
}
1078+
fatal();
1079+
}
1080+
}
10681081

10691082
// Special case for struct constructor calls: For temporaries, using the
10701083
// this pointer value returned from the constructor instead of the alloca

gen/toir.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -777,7 +777,7 @@ class ToElemVisitor : public Visitor {
777777
}
778778

779779
DValue *result =
780-
DtoCallFunction(e->loc, e->type, fnval, e->arguments, sretPointer);
780+
DtoCallFunction(e->loc, e->type, fnval, e->arguments, sretPointer, e->isMustTail);
781781

782782
if (delayedDtorVar) {
783783
delayedDtorVar->edtor = delayedDtorExp;

tests/codegen/musttail_1.d

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Tests successfull musttail application
2+
3+
// RUN: %ldc -output-ll -of=%t.ll %s && FileCheck %s < %t.ll
4+
5+
// CHECK-LABEL: define{{.*}} @{{.*}}foo
6+
int foo(int x) nothrow
7+
{
8+
// CHECK-NEXT: %1 = musttail call{{.*}} @{{.*}}bar
9+
// CHECK-NEXT: ret i32 %1
10+
pragma(musttail) return bar(x);
11+
}
12+
13+
// CHECK-LABEL: define{{.*}} @{{.*}}bar
14+
int bar(int x) nothrow { return x; }

0 commit comments

Comments
 (0)