Lime Parser Generator 0.1.0
Runtime-extensible LALR(1) parser with SIMD tokenization and LLVM JIT
Loading...
Searching...
No Matches
jit_llvm_compat.h
1/*
2** jit_llvm_compat.h -- LLVM C API compatibility shims for Lime.
3**
4** Lime declares a minimum LLVM version of 14. Three LLVM C APIs that
5** Lime uses changed across that range, so we hide the version split
6** here:
7**
8** * New PassBuilder API (LLVMRunPasses, LLVMPassBuilderOptionsRef)
9** is present from LLVM 16 onward. On LLVM 14-15 we fall back to
10** the legacy LLVMPassManagerBuilder + LLVMPassManager combo.
11** LLVMPassManagerBuilder itself was removed from the C API in
12** LLVM 17, so these are complementary: legacy for 14-15, modern
13** for 16+.
14**
15** * LLVMOrcCreateNewThreadSafeContextFromLLVMContext() is NOT a
16** stable API: it is absent from the public llvm-c/Orc.h on LLVM
17** 14 through 20 (Ubuntu 25.04 / LLVM 20 reproduces the breakage
18** as `call to undeclared function`) and was (re)introduced in
19** LLVM 21. Conversely, LLVMOrcThreadSafeContextGetContext()
20** -- the getter we use to pull the internal LLVMContextRef back
21** out of a no-arg-created ThreadSafeContext -- exists on LLVM
22** 14 through 20 and was REMOVED from the public header in LLVM
23** 21. These two APIs are exact mirrors of each other across
24** the version cutoff. We pick the working pair per version:
25** - LLVM 14-20: LLVMOrcCreateNewThreadSafeContext() (no-arg)
26** + LLVMOrcThreadSafeContextGetContext() to recover
27** the internal LLVMContextRef.
28** - LLVM 21+ : LLVMContextCreate() externally, then
29** LLVMOrcCreateNewThreadSafeContextFromLLVMContext()
30** to wrap it.
31** In both cases the ThreadSafeContext owns the LLVMContext, so
32** teardown is just LLVMOrcDisposeThreadSafeContext().
33**
34** * LLVMOrcLLJITLookup() takes an LLVMOrcJITTargetAddress * in
35** LLVM 14 and an LLVMOrcExecutorAddress * in LLVM 15+. Both are
36** uint64_t, just differently named. LimeJitAddress aliases to
37** whichever name the current LLVM exposes.
38**
39** Any code that runs the O2 pass pipeline, creates a thread-safe
40** context, or calls LLVMOrcLLJITLookup should use the helpers defined
41** here instead of the underlying LLVM symbols directly.
42*/
43
44#ifndef LIME_JIT_LLVM_COMPAT_H
45#define LIME_JIT_LLVM_COMPAT_H
46
47#ifndef LIME_NO_JIT
48
49/* LLVM_VERSION_MAJOR / _MINOR / _PATCH come from here. The header
50** lives under the C++ include path but contains only #defines, so it
51** is usable from C translation units. */
52#include <llvm/Config/llvm-config.h>
53
54#include <stdbool.h>
55#include <stddef.h>
56
57#include <llvm-c/Core.h>
58#include <llvm-c/Orc.h>
59
60#if LLVM_VERSION_MAJOR >= 16
61#include <llvm-c/Transforms/PassBuilder.h>
62#else
63#include <llvm-c/Transforms/PassManagerBuilder.h>
64#endif
65
66/*
67** LIME_JIT_RUN_O2_PASSES(module)
68**
69** Run the -O2 optimisation pipeline over the given LLVM module.
70** Evaluates `module` exactly once. Void-returning; does not signal
71** failure (the legacy path has no status channel, and the new path's
72** LLVMErrorRef return is ignored to keep the call-site symmetrical --
73** revisit if we ever care about pipeline setup errors).
74*/
75#if LLVM_VERSION_MAJOR >= 16
76
77#define LIME_JIT_RUN_O2_PASSES(module) \
78 do { \
79 LLVMModuleRef _lime_mod = (module); \
80 LLVMPassBuilderOptionsRef _lime_opts = LLVMCreatePassBuilderOptions(); \
81 LLVMErrorRef _lime_err = LLVMRunPasses(_lime_mod, "default<O2>", NULL, _lime_opts); \
82 if (_lime_err != LLVMErrorSuccess) { \
83 char *_lime_msg = LLVMGetErrorMessage(_lime_err); \
84 LLVMDisposeErrorMessage(_lime_msg); \
85 } \
86 LLVMDisposePassBuilderOptions(_lime_opts); \
87 } while (0)
88
89#else /* LLVM 14 / 15 -- legacy PassManagerBuilder */
90
91#define LIME_JIT_RUN_O2_PASSES(module) \
92 do { \
93 LLVMModuleRef _lime_mod = (module); \
94 LLVMPassManagerBuilderRef _lime_pmb = LLVMPassManagerBuilderCreate(); \
95 LLVMPassManagerBuilderSetOptLevel(_lime_pmb, 2); \
96 LLVMPassManagerRef _lime_pm = LLVMCreatePassManager(); \
97 LLVMPassManagerBuilderPopulateModulePassManager(_lime_pmb, _lime_pm); \
98 LLVMRunPassManager(_lime_pm, _lime_mod); \
99 LLVMDisposePassManager(_lime_pm); \
100 LLVMPassManagerBuilderDispose(_lime_pmb); \
101 } while (0)
102
103#endif /* LLVM_VERSION_MAJOR >= 16 */
104
105/*
106** LimeJitAddress -- the uint64_t-sized type LLVMOrcLLJITLookup writes
107** its result into. Use this type at every LLVMOrcLLJITLookup call
108** site; it resolves to whichever name the current LLVM headers
109** expose.
110*/
111#if LLVM_VERSION_MAJOR >= 15
112typedef LLVMOrcExecutorAddress LimeJitAddress;
113#else
114typedef LLVMOrcJITTargetAddress LimeJitAddress;
115#endif
116
117/*
118** lime_jit_create_ts_ctx()
119**
120** Create a ThreadSafeContext and return both it and its internal
121** LLVMContextRef. On success, *ts_ctx_out owns the returned
122** llvm_ctx_out -- callers must dispose only *ts_ctx_out during
123** teardown. Returns false on allocation failure, in which case
124** neither out-parameter is touched.
125*/
126static inline bool lime_jit_create_ts_ctx(LLVMOrcThreadSafeContextRef *ts_ctx_out,
127 LLVMContextRef *llvm_ctx_out) {
128#if LLVM_VERSION_MAJOR >= 21
129 /* LLVM 21+: ThreadSafeContextGetContext was removed from the
130 ** public llvm-c/Orc.h, but FromLLVMContext was (re)introduced.
131 ** Create the LLVMContext externally and wrap it. */
132 LLVMContextRef llvm_ctx = LLVMContextCreate();
133 if (llvm_ctx == NULL) {
134 return false;
135 }
136 LLVMOrcThreadSafeContextRef ts_ctx = LLVMOrcCreateNewThreadSafeContextFromLLVMContext(llvm_ctx);
137 if (ts_ctx == NULL) {
138 LLVMContextDispose(llvm_ctx);
139 return false;
140 }
141#else
142 /* LLVM 14-20: FromLLVMContext is not declared in the public
143 ** header; the no-arg ctor is the only entry point. Recover
144 ** the LLVMContextRef via the GetContext getter (still present
145 ** on these releases). */
146 LLVMOrcThreadSafeContextRef ts_ctx = LLVMOrcCreateNewThreadSafeContext();
147 if (ts_ctx == NULL) {
148 return false;
149 }
150 LLVMContextRef llvm_ctx = LLVMOrcThreadSafeContextGetContext(ts_ctx);
151 if (llvm_ctx == NULL) {
152 LLVMOrcDisposeThreadSafeContext(ts_ctx);
153 return false;
154 }
155#endif
156 *ts_ctx_out = ts_ctx;
157 *llvm_ctx_out = llvm_ctx;
158 return true;
159}
160
161#endif /* !LIME_NO_JIT */
162
163#endif /* LIME_JIT_LLVM_COMPAT_H */