Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * jit.c
4 : * Provider independent JIT infrastructure.
5 : *
6 : * Code related to loading JIT providers, redirecting calls into JIT providers
7 : * and error handling. No code specific to a specific JIT implementation
8 : * should end up here.
9 : *
10 : *
11 : * Copyright (c) 2016-2026, PostgreSQL Global Development Group
12 : *
13 : * IDENTIFICATION
14 : * src/backend/jit/jit.c
15 : *
16 : *-------------------------------------------------------------------------
17 : */
18 : #include "postgres.h"
19 :
20 : #include <sys/types.h>
21 : #include <sys/stat.h>
22 : #include <unistd.h>
23 :
24 : #include "fmgr.h"
25 : #include "jit/jit.h"
26 : #include "miscadmin.h"
27 : #include "nodes/execnodes.h"
28 : #include "portability/instr_time.h"
29 : #include "utils/fmgrprotos.h"
30 :
31 : /* GUCs */
32 : bool jit_enabled = true;
33 : char *jit_provider = NULL;
34 : bool jit_debugging_support = false;
35 : bool jit_dump_bitcode = false;
36 : bool jit_expressions = true;
37 : bool jit_profiling_support = false;
38 : bool jit_tuple_deforming = true;
39 : double jit_above_cost = 100000;
40 : double jit_inline_above_cost = 500000;
41 : double jit_optimize_above_cost = 500000;
42 :
43 : static JitProviderCallbacks provider;
44 : static bool provider_successfully_loaded = false;
45 : static bool provider_failed_loading = false;
46 :
47 :
48 : static bool provider_init(void);
49 :
50 :
51 : /*
52 : * SQL level function returning whether JIT is available in the current
53 : * backend. Will attempt to load JIT provider if necessary.
54 : */
55 : Datum
56 0 : pg_jit_available(PG_FUNCTION_ARGS)
57 : {
58 0 : PG_RETURN_BOOL(provider_init());
59 : }
60 :
61 :
62 : /*
63 : * Return whether a JIT provider has successfully been loaded, caching the
64 : * result.
65 : */
66 : static bool
67 3630 : provider_init(void)
68 : {
69 : char path[MAXPGPATH];
70 : JitProviderInit init;
71 :
72 : /* don't even try to load if not enabled */
73 3630 : if (!jit_enabled)
74 0 : return false;
75 :
76 : /*
77 : * Don't retry loading after failing - attempting to load JIT provider
78 : * isn't cheap.
79 : */
80 3630 : if (provider_failed_loading)
81 0 : return false;
82 3630 : if (provider_successfully_loaded)
83 3535 : return true;
84 :
85 : /*
86 : * Check whether shared library exists. We do that check before actually
87 : * attempting to load the shared library (via load_external_function()),
88 : * because that'd error out in case the shlib isn't available.
89 : */
90 95 : snprintf(path, MAXPGPATH, "%s/%s%s", pkglib_path, jit_provider, DLSUFFIX);
91 95 : elog(DEBUG1, "probing availability of JIT provider at %s", path);
92 95 : if (!pg_file_exists(path))
93 : {
94 0 : elog(DEBUG1,
95 : "provider not available, disabling JIT for current session");
96 0 : provider_failed_loading = true;
97 0 : return false;
98 : }
99 :
100 : /*
101 : * If loading functions fails, signal failure. We do so because
102 : * load_external_function() might error out despite the above check if
103 : * e.g. the library's dependencies aren't installed. We want to signal
104 : * ERROR in that case, so the user is notified, but we don't want to
105 : * continually retry.
106 : */
107 95 : provider_failed_loading = true;
108 :
109 : /* and initialize */
110 95 : init = (JitProviderInit)
111 95 : load_external_function(path, "_PG_jit_provider_init", true, NULL);
112 95 : init(&provider);
113 :
114 95 : provider_successfully_loaded = true;
115 95 : provider_failed_loading = false;
116 :
117 95 : elog(DEBUG1, "successfully loaded JIT provider in current session");
118 :
119 95 : return true;
120 : }
121 :
122 : /*
123 : * Reset JIT provider's error handling. This'll be called after an error has
124 : * been thrown and the main-loop has re-established control.
125 : */
126 : void
127 22755 : jit_reset_after_error(void)
128 : {
129 22755 : if (provider_successfully_loaded)
130 2038 : provider.reset_after_error();
131 22755 : }
132 :
133 : /*
134 : * Release resources required by one JIT context.
135 : */
136 : void
137 464 : jit_release_context(JitContext *context)
138 : {
139 464 : if (provider_successfully_loaded)
140 464 : provider.release_context(context);
141 :
142 464 : pfree(context);
143 464 : }
144 :
145 : /*
146 : * Ask provider to JIT compile an expression.
147 : *
148 : * Returns true if successful, false if not.
149 : */
150 : bool
151 1270286 : jit_compile_expr(struct ExprState *state)
152 : {
153 : /*
154 : * We can easily create a one-off context for functions without an
155 : * associated PlanState (and thus EState). But because there's no executor
156 : * shutdown callback that could deallocate the created function, they'd
157 : * live to the end of the transactions, where they'd be cleaned up by the
158 : * resowner machinery. That can lead to a noticeable amount of memory
159 : * usage, and worse, trigger some quadratic behaviour in gdb. Therefore,
160 : * at least for now, don't create a JITed function in those circumstances.
161 : */
162 1270286 : if (!state->parent)
163 347257 : return false;
164 :
165 : /* if no jitting should be performed at all */
166 923029 : if (!(state->parent->state->es_jit_flags & PGJIT_PERFORM))
167 919399 : return false;
168 :
169 : /* or if expressions aren't JITed */
170 3630 : if (!(state->parent->state->es_jit_flags & PGJIT_EXPR))
171 0 : return false;
172 :
173 : /* this also takes !jit_enabled into account */
174 3630 : if (provider_init())
175 3630 : return provider.compile_expr(state);
176 :
177 0 : return false;
178 : }
179 :
180 : /* Aggregate JIT instrumentation information */
181 : void
182 0 : InstrJitAgg(JitInstrumentation *dst, JitInstrumentation *add)
183 : {
184 0 : dst->created_functions += add->created_functions;
185 0 : INSTR_TIME_ADD(dst->generation_counter, add->generation_counter);
186 0 : INSTR_TIME_ADD(dst->deform_counter, add->deform_counter);
187 0 : INSTR_TIME_ADD(dst->inlining_counter, add->inlining_counter);
188 0 : INSTR_TIME_ADD(dst->optimization_counter, add->optimization_counter);
189 0 : INSTR_TIME_ADD(dst->emission_counter, add->emission_counter);
190 0 : }
|