Skip to content

Commit 188bfbb

Browse files
authored
Merge pull request #214 from CodaFi/handmaids-tail
Add bindings to tail call markers
2 parents c1b01e2 + 368ff5b commit 188bfbb

File tree

4 files changed

+154
-1
lines changed

4 files changed

+154
-1
lines changed

Sources/LLVM/Call.swift

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#if SWIFT_PACKAGE
22
import cllvm
3+
import llvmshims
34
#endif
45

56
/// Represents a simple function call.
@@ -33,6 +34,22 @@ public struct Call: IRInstruction {
3334
set { LLVMSetTailCall(self.llvm, newValue.llvm) }
3435
}
3536

37+
/// Accesses the tail call marker associated with this call.
38+
///
39+
/// The presence of the tail call marker affects the optimizer's decisions
40+
/// around tail call optimizations. The presence of a `tail` or `mustTail`
41+
/// marker, either inserted by the user or by the optimizer, is a strong
42+
/// hint (or a requirement) that tail call optimizations occur. The presence
43+
/// of `noTail` acts to block any tail call optimization.
44+
///
45+
/// Tail call optimization is finicky and requires a number of complex
46+
/// invariants hold before a call is eligible for the optimization.
47+
/// Even then, it is not necessarily guaranteed by LLVM in all cases.
48+
public var tailCallKind: TailCallKind {
49+
get { return TailCallKind(llvm: LLVMGetTailCallKind(self.llvm)) }
50+
set { return LLVMSetTailCallKind(self.llvm, newValue.llvm) }
51+
}
52+
3653
/// Retrieves the alignment of the parameter at the given index.
3754
///
3855
/// This property is currently set-only due to limitations of the LLVM C API.
@@ -66,3 +83,84 @@ public struct Invoke: IRInstruction {
6683
set { LLVMSetUnwindDest(self.llvm, newValue.asLLVM()) }
6784
}
6885
}
86+
87+
extension Call {
88+
/// Optimization markers for tail call elimination.
89+
public enum TailCallKind {
90+
/// No optimization tail call marker is present. The optimizer is free to
91+
/// infer one of the other tail call markers.
92+
case none
93+
/// Suggests that tail call optimization should be performed on this
94+
/// function. Note that this is not a guarantee.
95+
///
96+
/// `tail` calls may not access caller-provided allocas, and may not
97+
/// access varargs.
98+
///
99+
/// Tail call optimization for calls marked tail is guaranteed to occur if
100+
/// the following conditions are met:
101+
///
102+
/// - Caller and callee both have the calling convention `fastcc`.
103+
/// - The call is in tail position (ret immediately follows `call` and
104+
/// `ret` uses value of call or is void).
105+
/// - Tail call elimination is enabled in the target machine's
106+
/// `TargetOptions` or is globally enabled in LLVM.
107+
/// - Platform-specific constraints are met.
108+
case tail
109+
/// Requires the tail call optimization be performed in order for this call
110+
/// to proceed correctly.
111+
///
112+
/// Tail calls guarantee the following invariants:
113+
///
114+
/// - The call will not cause unbounded stack growth if it is part of a
115+
/// recursive cycle in the call graph.
116+
/// - Arguments with the `inalloca` attribute are forwarded in place.
117+
/// - If the musttail call appears in a function with the `thunk`
118+
/// attribute and the caller and callee both have varargs, than any
119+
/// unprototyped arguments in register or memory are forwarded to the
120+
/// callee. Similarly, the return value of the callee is returned the
121+
/// the caller’s caller, even if a `void` return type is in use.
122+
///
123+
/// `mustTail` calls may not access caller-provided allocas, and may not
124+
/// access varargs. Unlike `tail`s, they are also subject to the following
125+
/// restrictions:
126+
///
127+
/// - The call must immediately precede a `ret` instruction, or a pointer
128+
/// `bitcast` followed by a `ret` instruction.
129+
/// - The `ret` instruction must return the (possibly `bitcast`ed) value
130+
/// produced by the `call`, or return `void`.
131+
/// - The caller and callee prototypes must match. Pointer types of
132+
/// parameters or return types may differ in pointee type, but not in
133+
/// address space.
134+
/// - The calling conventions of the caller and callee must match.
135+
/// - All ABI-impacting function attributes, such as `sret`, `byval`,
136+
/// `inreg`, `returned`, and `inalloca`, must match.
137+
/// - The callee must be varargs iff the caller is varargs. Bitcasting a
138+
/// non-varargs function to the appropriate varargs type is legal so
139+
/// long as the non-varargs prefixes obey the other rules.
140+
case mustTail
141+
/// Prevents tail call optimizations from being performed or inferred.
142+
case noTail
143+
144+
internal init(llvm: LLVMTailCallKind) {
145+
switch llvm {
146+
case LLVMTailCallKindNone: self = .none
147+
case LLVMTailCallKindTail: self = .tail
148+
case LLVMTailCallKindMustTail: self = .mustTail
149+
case LLVMTailCallKindNoTail: self = .noTail
150+
default: fatalError("unknown tail call kind \(llvm)")
151+
}
152+
}
153+
154+
private static let tailCallKindMapping: [TailCallKind: LLVMTailCallKind] = [
155+
.none: LLVMTailCallKindNone,
156+
.tail: LLVMTailCallKindTail,
157+
.mustTail: LLVMTailCallKindMustTail,
158+
.noTail: LLVMTailCallKindNoTail,
159+
]
160+
161+
/// Retrieves the corresponding `LLVMTailCallKind`.
162+
public var llvm: LLVMTailCallKind {
163+
return TailCallKind.tailCallKindMapping[self]!
164+
}
165+
}
166+
}

Sources/LLVM/CallingConvention.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,7 @@ public enum CallingConvention {
419419
.amdGPULS : LLVMAMDGPULSCallConv, .amdGPUES : LLVMAMDGPUESCallConv,
420420
]
421421

422-
/// Retrieves the corresponding `LLVMDWARFSourceLanguage`.
422+
/// Retrieves the corresponding `LLVMCallConv`.
423423
public var llvm: LLVMCallConv {
424424
return CallingConvention.conventionMapping[self]!
425425
}

Sources/llvmshims/include/shim.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,4 +104,14 @@ void LLVMAddInternalizePassWithMustPreservePredicate(
104104
LLVMPassManagerRef PM, void *Context,
105105
LLVMBool (*MustPreserve)(LLVMValueRef, void *));
106106

107+
typedef enum {
108+
LLVMTailCallKindNone,
109+
LLVMTailCallKindTail,
110+
LLVMTailCallKindMustTail,
111+
LLVMTailCallKindNoTail
112+
} LLVMTailCallKind;
113+
114+
LLVMTailCallKind LLVMGetTailCallKind(LLVMValueRef CallInst);
115+
void LLVMSetTailCallKind(LLVMValueRef CallInst, LLVMTailCallKind TCK);
116+
107117
#endif /* LLVMSWIFT_LLVM_SHIM_H */

Sources/llvmshims/src/shim.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,17 @@ extern "C" {
135135
void LLVMAddInternalizePassWithMustPreservePredicate(
136136
LLVMPassManagerRef PM, void *Context,
137137
LLVMBool (*MustPreserve)(LLVMValueRef, void *));
138+
139+
// https://reviews.llvm.org/D66061
140+
typedef enum {
141+
LLVMTailCallKindNone,
142+
LLVMTailCallKindTail,
143+
LLVMTailCallKindMustTail,
144+
LLVMTailCallKindNoTail
145+
} LLVMTailCallKind;
146+
147+
LLVMTailCallKind LLVMGetTailCallKind(LLVMValueRef CallInst);
148+
void LLVMSetTailCallKind(LLVMValueRef CallInst, LLVMTailCallKind TCK);
138149
}
139150

140151
using namespace llvm;
@@ -385,3 +396,37 @@ void LLVMAddInternalizePassWithMustPreservePredicate(
385396
void LLVMAddGlobalsAAWrapperPass(LLVMPassManagerRef PM) {
386397
unwrap(PM)->add(createGlobalsAAWrapperPass());
387398
}
399+
400+
LLVMTailCallKind LLVMGetTailCallKind(LLVMValueRef Call) {
401+
switch (unwrap<CallInst>(Call)->getTailCallKind()) {
402+
case CallInst::TailCallKind::TCK_None:
403+
return LLVMTailCallKindNone;
404+
case CallInst::TailCallKind::TCK_Tail:
405+
return LLVMTailCallKindTail;
406+
case CallInst::TailCallKind::TCK_MustTail:
407+
return LLVMTailCallKindMustTail;
408+
case CallInst::TailCallKind::TCK_NoTail:
409+
return LLVMTailCallKindNoTail;
410+
}
411+
}
412+
413+
void LLVMSetTailCallKind(LLVMValueRef Call, LLVMTailCallKind TCK) {
414+
CallInst::TailCallKind kind;
415+
switch (TCK) {
416+
case LLVMTailCallKindNone:
417+
kind = CallInst::TailCallKind::TCK_None;
418+
break;
419+
case LLVMTailCallKindTail:
420+
kind = CallInst::TailCallKind::TCK_Tail;
421+
break;
422+
case LLVMTailCallKindMustTail:
423+
kind = CallInst::TailCallKind::TCK_MustTail;
424+
break;
425+
case LLVMTailCallKindNoTail:
426+
kind = CallInst::TailCallKind::TCK_NoTail;
427+
break;
428+
}
429+
430+
unwrap<CallInst>(Call)->setTailCallKind(kind);
431+
}
432+

0 commit comments

Comments
 (0)