Age Owner TLA 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-2023, 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 "executor/execExpr.h"
25 : #include "fmgr.h"
26 : #include "jit/jit.h"
27 : #include "miscadmin.h"
28 : #include "utils/fmgrprotos.h"
29 : #include "utils/resowner_private.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 : static bool file_exists(const char *name);
50 :
51 :
52 : /*
53 : * SQL level function returning whether JIT is available in the current
54 : * backend. Will attempt to load JIT provider if necessary.
55 : */
56 : Datum
1845 andres 57 UBC 0 : pg_jit_available(PG_FUNCTION_ARGS)
58 : {
59 0 : PG_RETURN_BOOL(provider_init());
60 : }
61 :
62 :
63 : /*
64 : * Return whether a JIT provider has successfully been loaded, caching the
65 : * result.
66 : */
67 : static bool
1845 andres 68 CBC 5176 : provider_init(void)
69 : {
70 : char path[MAXPGPATH];
71 : JitProviderInit init;
72 :
73 : /* don't even try to load if not enabled */
74 5176 : if (!jit_enabled)
1845 andres 75 UBC 0 : return false;
76 :
77 : /*
78 : * Don't retry loading after failing - attempting to load JIT provider
79 : * isn't cheap.
80 : */
1845 andres 81 CBC 5176 : if (provider_failed_loading)
1845 andres 82 UBC 0 : return false;
1845 andres 83 CBC 5176 : if (provider_successfully_loaded)
84 4783 : return true;
85 :
86 : /*
87 : * Check whether shared library exists. We do that check before actually
88 : * attempting to load the shared library (via load_external_function()),
89 : * because that'd error out in case the shlib isn't available.
90 : */
91 393 : snprintf(path, MAXPGPATH, "%s/%s%s", pkglib_path, jit_provider, DLSUFFIX);
92 393 : elog(DEBUG1, "probing availability of JIT provider at %s", path);
93 393 : if (!file_exists(path))
94 : {
1845 andres 95 UBC 0 : elog(DEBUG1,
96 : "provider not available, disabling JIT for current session");
97 0 : provider_failed_loading = true;
98 0 : return false;
99 : }
100 :
101 : /*
102 : * If loading functions fails, signal failure. We do so because
103 : * load_external_function() might error out despite the above check if
104 : * e.g. the library's dependencies aren't installed. We want to signal
105 : * ERROR in that case, so the user is notified, but we don't want to
106 : * continually retry.
107 : */
1845 andres 108 CBC 393 : provider_failed_loading = true;
109 :
110 : /* and initialize */
111 393 : init = (JitProviderInit)
112 393 : load_external_function(path, "_PG_jit_provider_init", true, NULL);
113 393 : init(&provider);
114 :
115 393 : provider_successfully_loaded = true;
116 393 : provider_failed_loading = false;
117 :
118 393 : elog(DEBUG1, "successfully loaded JIT provider in current session");
119 :
120 393 : return true;
121 : }
122 :
123 : /*
124 : * Reset JIT provider's error handling. This'll be called after an error has
125 : * been thrown and the main-loop has re-established control.
126 : */
127 : void
128 17749 : jit_reset_after_error(void)
129 : {
130 17749 : if (provider_successfully_loaded)
131 1453 : provider.reset_after_error();
132 17749 : }
133 :
134 : /*
135 : * Release resources required by one JIT context.
136 : */
137 : void
138 839 : jit_release_context(JitContext *context)
139 : {
140 839 : if (provider_successfully_loaded)
141 839 : provider.release_context(context);
142 :
143 839 : ResourceOwnerForgetJIT(context->resowner, PointerGetDatum(context));
144 839 : pfree(context);
145 839 : }
146 :
147 : /*
148 : * Ask provider to JIT compile an expression.
149 : *
150 : * Returns true if successful, false if not.
151 : */
152 : bool
1846 153 1095510 : jit_compile_expr(struct ExprState *state)
154 : {
155 : /*
156 : * We can easily create a one-off context for functions without an
157 : * associated PlanState (and thus EState). But because there's no executor
158 : * shutdown callback that could deallocate the created function, they'd
159 : * live to the end of the transactions, where they'd be cleaned up by the
160 : * resowner machinery. That can lead to a noticeable amount of memory
161 : * usage, and worse, trigger some quadratic behaviour in gdb. Therefore,
162 : * at least for now, don't create a JITed function in those circumstances.
163 : */
164 1095510 : if (!state->parent)
165 388031 : return false;
166 :
167 : /* if no jitting should be performed at all */
168 707479 : if (!(state->parent->state->es_jit_flags & PGJIT_PERFORM))
169 702303 : return false;
170 :
171 : /* or if expressions aren't JITed */
172 5176 : if (!(state->parent->state->es_jit_flags & PGJIT_EXPR))
1846 andres 173 UBC 0 : return false;
174 :
175 : /* this also takes !jit_enabled into account */
1846 andres 176 CBC 5176 : if (provider_init())
177 5176 : return provider.compile_expr(state);
178 :
1846 andres 179 UBC 0 : return false;
180 : }
181 :
182 : /* Aggregate JIT instrumentation information */
183 : void
1657 andres 184 CBC 48 : InstrJitAgg(JitInstrumentation *dst, JitInstrumentation *add)
185 : {
186 48 : dst->created_functions += add->created_functions;
187 48 : INSTR_TIME_ADD(dst->generation_counter, add->generation_counter);
188 48 : INSTR_TIME_ADD(dst->inlining_counter, add->inlining_counter);
189 48 : INSTR_TIME_ADD(dst->optimization_counter, add->optimization_counter);
190 48 : INSTR_TIME_ADD(dst->emission_counter, add->emission_counter);
191 48 : }
192 :
193 : static bool
1845 194 393 : file_exists(const char *name)
195 : {
196 : struct stat st;
197 :
163 peter 198 GNC 393 : Assert(name != NULL);
199 :
1845 andres 200 CBC 393 : if (stat(name, &st) == 0)
578 michael 201 393 : return !S_ISDIR(st.st_mode);
1845 andres 202 UBC 0 : else if (!(errno == ENOENT || errno == ENOTDIR))
203 0 : ereport(ERROR,
204 : (errcode_for_file_access(),
205 : errmsg("could not access file \"%s\": %m", name)));
206 :
207 0 : return false;
208 : }
|