From 368ff5b114a8211a842e22144893efe5ffdcbb17 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 14 Aug 2019 14:15:52 -0700 Subject: [PATCH] Add bindings to tail call markers --- Sources/LLVM/Call.swift | 98 ++++++++++++++++++++++++++++ Sources/LLVM/CallingConvention.swift | 2 +- Sources/llvmshims/include/shim.h | 10 +++ Sources/llvmshims/src/shim.cpp | 45 +++++++++++++ 4 files changed, 154 insertions(+), 1 deletion(-) diff --git a/Sources/LLVM/Call.swift b/Sources/LLVM/Call.swift index dd9c7fd9..459c22c8 100644 --- a/Sources/LLVM/Call.swift +++ b/Sources/LLVM/Call.swift @@ -1,5 +1,6 @@ #if SWIFT_PACKAGE import cllvm +import llvmshims #endif /// Represents a simple function call. @@ -33,6 +34,22 @@ public struct Call: IRInstruction { set { LLVMSetTailCall(self.llvm, newValue.llvm) } } + /// Accesses the tail call marker associated with this call. + /// + /// The presence of the tail call marker affects the optimizer's decisions + /// around tail call optimizations. The presence of a `tail` or `mustTail` + /// marker, either inserted by the user or by the optimizer, is a strong + /// hint (or a requirement) that tail call optimizations occur. The presence + /// of `noTail` acts to block any tail call optimization. + /// + /// Tail call optimization is finicky and requires a number of complex + /// invariants hold before a call is eligible for the optimization. + /// Even then, it is not necessarily guaranteed by LLVM in all cases. + public var tailCallKind: TailCallKind { + get { return TailCallKind(llvm: LLVMGetTailCallKind(self.llvm)) } + set { return LLVMSetTailCallKind(self.llvm, newValue.llvm) } + } + /// Retrieves the alignment of the parameter at the given index. /// /// This property is currently set-only due to limitations of the LLVM C API. @@ -66,3 +83,84 @@ public struct Invoke: IRInstruction { set { LLVMSetUnwindDest(self.llvm, newValue.asLLVM()) } } } + +extension Call { + /// Optimization markers for tail call elimination. + public enum TailCallKind { + /// No optimization tail call marker is present. The optimizer is free to + /// infer one of the other tail call markers. + case none + /// Suggests that tail call optimization should be performed on this + /// function. Note that this is not a guarantee. + /// + /// `tail` calls may not access caller-provided allocas, and may not + /// access varargs. + /// + /// Tail call optimization for calls marked tail is guaranteed to occur if + /// the following conditions are met: + /// + /// - Caller and callee both have the calling convention `fastcc`. + /// - The call is in tail position (ret immediately follows `call` and + /// `ret` uses value of call or is void). + /// - Tail call elimination is enabled in the target machine's + /// `TargetOptions` or is globally enabled in LLVM. + /// - Platform-specific constraints are met. + case tail + /// Requires the tail call optimization be performed in order for this call + /// to proceed correctly. + /// + /// Tail calls guarantee the following invariants: + /// + /// - The call will not cause unbounded stack growth if it is part of a + /// recursive cycle in the call graph. + /// - Arguments with the `inalloca` attribute are forwarded in place. + /// - If the musttail call appears in a function with the `thunk` + /// attribute and the caller and callee both have varargs, than any + /// unprototyped arguments in register or memory are forwarded to the + /// callee. Similarly, the return value of the callee is returned the + /// the caller’s caller, even if a `void` return type is in use. + /// + /// `mustTail` calls may not access caller-provided allocas, and may not + /// access varargs. Unlike `tail`s, they are also subject to the following + /// restrictions: + /// + /// - The call must immediately precede a `ret` instruction, or a pointer + /// `bitcast` followed by a `ret` instruction. + /// - The `ret` instruction must return the (possibly `bitcast`ed) value + /// produced by the `call`, or return `void`. + /// - The caller and callee prototypes must match. Pointer types of + /// parameters or return types may differ in pointee type, but not in + /// address space. + /// - The calling conventions of the caller and callee must match. + /// - All ABI-impacting function attributes, such as `sret`, `byval`, + /// `inreg`, `returned`, and `inalloca`, must match. + /// - The callee must be varargs iff the caller is varargs. Bitcasting a + /// non-varargs function to the appropriate varargs type is legal so + /// long as the non-varargs prefixes obey the other rules. + case mustTail + /// Prevents tail call optimizations from being performed or inferred. + case noTail + + internal init(llvm: LLVMTailCallKind) { + switch llvm { + case LLVMTailCallKindNone: self = .none + case LLVMTailCallKindTail: self = .tail + case LLVMTailCallKindMustTail: self = .mustTail + case LLVMTailCallKindNoTail: self = .noTail + default: fatalError("unknown tail call kind \(llvm)") + } + } + + private static let tailCallKindMapping: [TailCallKind: LLVMTailCallKind] = [ + .none: LLVMTailCallKindNone, + .tail: LLVMTailCallKindTail, + .mustTail: LLVMTailCallKindMustTail, + .noTail: LLVMTailCallKindNoTail, + ] + + /// Retrieves the corresponding `LLVMTailCallKind`. + public var llvm: LLVMTailCallKind { + return TailCallKind.tailCallKindMapping[self]! + } + } +} diff --git a/Sources/LLVM/CallingConvention.swift b/Sources/LLVM/CallingConvention.swift index 05a44014..0643b1ac 100644 --- a/Sources/LLVM/CallingConvention.swift +++ b/Sources/LLVM/CallingConvention.swift @@ -419,7 +419,7 @@ public enum CallingConvention { .amdGPULS : LLVMAMDGPULSCallConv, .amdGPUES : LLVMAMDGPUESCallConv, ] - /// Retrieves the corresponding `LLVMDWARFSourceLanguage`. + /// Retrieves the corresponding `LLVMCallConv`. public var llvm: LLVMCallConv { return CallingConvention.conventionMapping[self]! } diff --git a/Sources/llvmshims/include/shim.h b/Sources/llvmshims/include/shim.h index 9767e95f..4b0a69c5 100644 --- a/Sources/llvmshims/include/shim.h +++ b/Sources/llvmshims/include/shim.h @@ -104,4 +104,14 @@ void LLVMAddInternalizePassWithMustPreservePredicate( LLVMPassManagerRef PM, void *Context, LLVMBool (*MustPreserve)(LLVMValueRef, void *)); +typedef enum { + LLVMTailCallKindNone, + LLVMTailCallKindTail, + LLVMTailCallKindMustTail, + LLVMTailCallKindNoTail +} LLVMTailCallKind; + +LLVMTailCallKind LLVMGetTailCallKind(LLVMValueRef CallInst); +void LLVMSetTailCallKind(LLVMValueRef CallInst, LLVMTailCallKind TCK); + #endif /* LLVMSWIFT_LLVM_SHIM_H */ diff --git a/Sources/llvmshims/src/shim.cpp b/Sources/llvmshims/src/shim.cpp index 85cb156e..b5fc7b34 100644 --- a/Sources/llvmshims/src/shim.cpp +++ b/Sources/llvmshims/src/shim.cpp @@ -135,6 +135,17 @@ extern "C" { void LLVMAddInternalizePassWithMustPreservePredicate( LLVMPassManagerRef PM, void *Context, LLVMBool (*MustPreserve)(LLVMValueRef, void *)); + + // https://reviews.llvm.org/D66061 + typedef enum { + LLVMTailCallKindNone, + LLVMTailCallKindTail, + LLVMTailCallKindMustTail, + LLVMTailCallKindNoTail + } LLVMTailCallKind; + + LLVMTailCallKind LLVMGetTailCallKind(LLVMValueRef CallInst); + void LLVMSetTailCallKind(LLVMValueRef CallInst, LLVMTailCallKind TCK); } using namespace llvm; @@ -385,3 +396,37 @@ void LLVMAddInternalizePassWithMustPreservePredicate( void LLVMAddGlobalsAAWrapperPass(LLVMPassManagerRef PM) { unwrap(PM)->add(createGlobalsAAWrapperPass()); } + +LLVMTailCallKind LLVMGetTailCallKind(LLVMValueRef Call) { + switch (unwrap(Call)->getTailCallKind()) { + case CallInst::TailCallKind::TCK_None: + return LLVMTailCallKindNone; + case CallInst::TailCallKind::TCK_Tail: + return LLVMTailCallKindTail; + case CallInst::TailCallKind::TCK_MustTail: + return LLVMTailCallKindMustTail; + case CallInst::TailCallKind::TCK_NoTail: + return LLVMTailCallKindNoTail; + } +} + +void LLVMSetTailCallKind(LLVMValueRef Call, LLVMTailCallKind TCK) { + CallInst::TailCallKind kind; + switch (TCK) { + case LLVMTailCallKindNone: + kind = CallInst::TailCallKind::TCK_None; + break; + case LLVMTailCallKindTail: + kind = CallInst::TailCallKind::TCK_Tail; + break; + case LLVMTailCallKindMustTail: + kind = CallInst::TailCallKind::TCK_MustTail; + break; + case LLVMTailCallKindNoTail: + kind = CallInst::TailCallKind::TCK_NoTail; + break; + } + + unwrap(Call)->setTailCallKind(kind); +} +