Browse Source

Support for optimizing and emitting code in LLVM JIT provider.

This commit introduces the ability to actually generate code using
LLVM. In particular, this adds:

- Ability to emit code both in heavily optimized and largely
  unoptimized fashion
- Batching facility to allow functions to be defined in small
  increments, but optimized and emitted in executable form in larger
  batches (for performance and memory efficiency)
- Type and function declaration synchronization between runtime
  generated code and normal postgres code. This is critical to be able
  to access struct fields etc.
- Developer oriented jit_dump_bitcode GUC, for inspecting / debugging
  the generated code.
- per JitContext statistics of number of functions, time spent
  generating code, optimizing, and emitting it.  This will later be
  employed for EXPLAIN support.

This commit doesn't yet contain any code actually generating
functions. That'll follow in later commits.

Documentation for GUCs added, and for JIT in general, will be added in
later commits.

Author: Andres Freund, with contributions by Pierre Ducroquet
Testing-By: Thomas Munro, Peter Eisentraut
Discussion: https://postgr.es/m/20170901064131.tazjxwus3k2w3ybh@alap3.anarazel.de
tags/REL_11_BETA1
Andres Freund 1 year ago
parent
commit
b96d550eb0

+ 1
- 0
.gitignore View File

@@ -1,6 +1,7 @@
1 1
 # Global excludes across all subdirectories
2 2
 *.o
3 3
 *.obj
4
+*.bc
4 5
 *.so
5 6
 *.so.[0-9]
6 7
 *.so.[0-9].[0-9]

+ 21
- 0
src/Makefile.global.in View File

@@ -951,3 +951,24 @@ coverage-clean:
951 951
 	rm -f `find . -name '*.gcda' -print`
952 952
 
953 953
 endif # enable_coverage
954
+
955
+##########################################################################
956
+#
957
+# LLVM support
958
+#
959
+
960
+ifndef COMPILE.c.bc
961
+# -Wno-ignored-attributes added so gnu_printf doesn't trigger
962
+# warnings, when the main binary is compiled with C.
963
+COMPILE.c.bc = $(CLANG) -Wno-ignored-attributes $(BITCODE_CFLAGS) $(CPPFLAGS) -flto=thin -emit-llvm -c
964
+endif
965
+
966
+ifndef COMPILE.cxx.bc
967
+COMPILE.cxx.bc = $(CLANG) -xc++ -Wno-ignored-attributes $(BITCODE_CXXFLAGS) $(CPPFLAGS) -flto=thin -emit-llvm -c
968
+endif
969
+
970
+%.bc : %.c
971
+	$(COMPILE.c.bc) -o $@ $<
972
+
973
+%.bc : %.cpp
974
+	$(COMPILE.cxx.bc) -o $@ $<

+ 1
- 0
src/backend/common.mk View File

@@ -46,3 +46,4 @@ clean-local:
46 46
 	rm -f $(subsysfilename) $(OBJS)
47 47
 
48 48
 $(call recurse,coverage)
49
+$(call recurse,install)

+ 1
- 0
src/backend/jit/jit.c View File

@@ -33,6 +33,7 @@
33 33
 /* GUCs */
34 34
 bool		jit_enabled = true;
35 35
 char	   *jit_provider = "llvmjit";
36
+bool		jit_dump_bitcode = false;
36 37
 
37 38
 static JitProviderCallbacks provider;
38 39
 static bool provider_successfully_loaded = false;

+ 11
- 3
src/backend/jit/llvm/Makefile View File

@@ -41,15 +41,23 @@ OBJS += llvmjit.o llvmjit_error.o llvmjit_wrap.o
41 41
 # Code generation
42 42
 OBJS +=
43 43
 
44
-all: all-shared-lib
44
+all: all-shared-lib llvmjit_types.bc
45 45
 
46
-install: all installdirs install-lib
46
+install: all installdirs install-lib install-types
47 47
 
48 48
 installdirs: installdirs-lib
49 49
 
50
-uninstall: uninstall-lib
50
+uninstall: uninstall-lib uninstall-types
51
+
52
+# Note this is intentionally not in bitcodedir, as it's not for inlining */
53
+install-types: llvmjit_types.bc
54
+	$(INSTALL_DATA) llvmjit_types.bc '$(DESTDIR)$(pkglibdir)'
55
+
56
+uninstall-types:
57
+	rm -f '$(DESTDIR)$(pkglibdir)/llvmjit_types.bc'
51 58
 
52 59
 include $(top_srcdir)/src/Makefile.shlib
53 60
 
54 61
 clean distclean maintainer-clean: clean-lib
55 62
 	rm -f $(OBJS)
63
+	rm -f llvmjit_types.bc

+ 588
- 0
src/backend/jit/llvm/llvmjit.c View File

@@ -19,18 +19,59 @@
19 19
 
20 20
 #include "utils/memutils.h"
21 21
 #include "utils/resowner_private.h"
22
+#include "portability/instr_time.h"
22 23
 #include "storage/ipc.h"
23 24
 
24 25
 
26
+#include <llvm-c/Analysis.h>
27
+#include <llvm-c/BitReader.h>
28
+#include <llvm-c/BitWriter.h>
29
+#include <llvm-c/Core.h>
30
+#include <llvm-c/OrcBindings.h>
31
+#include <llvm-c/Support.h>
25 32
 #include <llvm-c/Target.h>
33
+#include <llvm-c/Transforms/IPO.h>
34
+#include <llvm-c/Transforms/PassManagerBuilder.h>
35
+#include <llvm-c/Transforms/Scalar.h>
36
+
37
+
38
+/* Handle of a module emitted via ORC JIT */
39
+typedef struct LLVMJitHandle
40
+{
41
+	LLVMOrcJITStackRef stack;
42
+	LLVMOrcModuleHandle orc_handle;
43
+} LLVMJitHandle;
44
+
45
+
46
+/* types & functions commonly needed for JITing */
47
+LLVMTypeRef TypeSizeT;
48
+
49
+LLVMValueRef AttributeTemplate;
50
+LLVMValueRef FuncStrlen;
26 51
 
27 52
 
28 53
 static bool llvm_session_initialized = false;
54
+static size_t llvm_generation = 0;
55
+static const char *llvm_triple = NULL;
56
+static const char *llvm_layout = NULL;
57
+
58
+
59
+static LLVMTargetMachineRef llvm_opt0_targetmachine;
60
+static LLVMTargetMachineRef llvm_opt3_targetmachine;
61
+
62
+static LLVMTargetRef llvm_targetref;
63
+static LLVMOrcJITStackRef llvm_opt0_orc;
64
+static LLVMOrcJITStackRef llvm_opt3_orc;
29 65
 
30 66
 
31 67
 static void llvm_release_context(JitContext *context);
32 68
 static void llvm_session_initialize(void);
33 69
 static void llvm_shutdown(int code, Datum arg);
70
+static void llvm_compile_module(LLVMJitContext *context);
71
+static void llvm_optimize_module(LLVMJitContext *context, LLVMModuleRef module);
72
+
73
+static void llvm_create_types(void);
74
+static uint64_t llvm_resolve_symbol(const char *name, void *ctx);
34 75
 
35 76
 
36 77
 PG_MODULE_MAGIC;
@@ -81,6 +122,359 @@ llvm_create_context(int jitFlags)
81 122
 static void
82 123
 llvm_release_context(JitContext *context)
83 124
 {
125
+	LLVMJitContext *llvm_context = (LLVMJitContext *) context;
126
+
127
+	llvm_enter_fatal_on_oom();
128
+
129
+	/*
130
+	 * When this backend is exiting, don't clean up LLVM. As an error might
131
+	 * have occurred from within LLVM, we do not want to risk reentering. All
132
+	 * resource cleanup is going to happen through process exit.
133
+	 */
134
+	if (!proc_exit_inprogress)
135
+	{
136
+		if (llvm_context->module)
137
+		{
138
+			LLVMDisposeModule(llvm_context->module);
139
+			llvm_context->module = NULL;
140
+		}
141
+
142
+		while (llvm_context->handles != NIL)
143
+		{
144
+			LLVMJitHandle *jit_handle;
145
+
146
+			jit_handle = (LLVMJitHandle *) linitial(llvm_context->handles);
147
+			llvm_context->handles = list_delete_first(llvm_context->handles);
148
+
149
+			LLVMOrcRemoveModule(jit_handle->stack, jit_handle->orc_handle);
150
+			pfree(jit_handle);
151
+		}
152
+	}
153
+}
154
+
155
+/*
156
+ * Return module which may be modified, e.g. by creating new functions.
157
+ */
158
+LLVMModuleRef
159
+llvm_mutable_module(LLVMJitContext *context)
160
+{
161
+	llvm_assert_in_fatal_section();
162
+
163
+	/*
164
+	 * If there's no in-progress module, create a new one.
165
+	 */
166
+	if (!context->module)
167
+	{
168
+		context->compiled = false;
169
+		context->module_generation = llvm_generation++;
170
+		context->module = LLVMModuleCreateWithName("pg");
171
+		LLVMSetTarget(context->module, llvm_triple);
172
+		LLVMSetDataLayout(context->module, llvm_layout);
173
+	}
174
+
175
+	return context->module;
176
+}
177
+
178
+/*
179
+ * Expand function name to be non-conflicting. This should be used by code
180
+ * generating code, when adding new externally visible function definitions to
181
+ * a Module.
182
+ */
183
+char *
184
+llvm_expand_funcname(struct LLVMJitContext *context, const char *basename)
185
+{
186
+	Assert(context->module != NULL);
187
+
188
+	context->base.created_functions++;
189
+
190
+	/*
191
+	 * Previously we used dots to separate, but turns out some tools, e.g.
192
+	 * GDB, don't like that and truncate name.
193
+	 */
194
+	return psprintf("%s_%zu_%d",
195
+					basename,
196
+					context->module_generation,
197
+					context->counter++);
198
+}
199
+
200
+/*
201
+ * Return pointer to function funcname, which has to exist. If there's pending
202
+ * code to be optimized and emitted, do so first.
203
+ */
204
+void *
205
+llvm_get_function(LLVMJitContext *context, const char *funcname)
206
+{
207
+	LLVMOrcTargetAddress addr = 0;
208
+#if defined(HAVE_DECL_LLVMORCGETSYMBOLADDRESSIN) && HAVE_DECL_LLVMORCGETSYMBOLADDRESSIN
209
+	ListCell   *lc;
210
+#endif
211
+
212
+	llvm_assert_in_fatal_section();
213
+
214
+	/*
215
+	 * If there is a pending / not emitted module, compile and emit now.
216
+	 * Otherwise we migh not find the [correct] function.
217
+	 */
218
+	if (!context->compiled)
219
+	{
220
+		llvm_compile_module(context);
221
+	}
222
+
223
+	/*
224
+	 * ORC's symbol table is of *unmangled* symbols. Therefore we don't need
225
+	 * to mangle here.
226
+	 */
227
+
228
+#if defined(HAVE_DECL_LLVMORCGETSYMBOLADDRESSIN) && HAVE_DECL_LLVMORCGETSYMBOLADDRESSIN
229
+	foreach(lc, context->handles)
230
+	{
231
+		LLVMJitHandle *handle = (LLVMJitHandle *) lfirst(lc);
232
+
233
+		addr = 0;
234
+		if (LLVMOrcGetSymbolAddressIn(handle->stack, &addr, handle->orc_handle, funcname))
235
+			elog(ERROR, "failed to lookup symbol \"%s\"", funcname);
236
+		if (addr)
237
+			return (void *) (uintptr_t) addr;
238
+	}
239
+
240
+#else
241
+
242
+#if LLVM_VERSION_MAJOR < 5
243
+	if ((addr = LLVMOrcGetSymbolAddress(llvm_opt0_orc, funcname)))
244
+		return (void *) (uintptr_t) addr;
245
+	if ((addr = LLVMOrcGetSymbolAddress(llvm_opt3_orc, funcname)))
246
+		return (void *) (uintptr_t) addr;
247
+#else
248
+	if (LLVMOrcGetSymbolAddress(llvm_opt0_orc, &addr, funcname))
249
+		elog(ERROR, "failed to lookup symbol \"%s\"", funcname);
250
+	if (addr)
251
+		return (void *) (uintptr_t) addr;
252
+	if (LLVMOrcGetSymbolAddress(llvm_opt3_orc, &addr, funcname))
253
+		elog(ERROR, "failed to lookup symbol \"%s\"", funcname);
254
+	if (addr)
255
+		return (void *) (uintptr_t) addr;
256
+#endif							/* LLVM_VERSION_MAJOR */
257
+
258
+#endif							/* HAVE_DECL_LLVMORCGETSYMBOLADDRESSIN */
259
+
260
+	elog(ERROR, "failed to JIT: %s", funcname);
261
+
262
+	return NULL;
263
+}
264
+
265
+/*
266
+ * Return declaration for passed function, adding it to the module if
267
+ * necessary.
268
+ *
269
+ * This is used to make functions imported by llvm_create_types() known to the
270
+ * module that's currently being worked on.
271
+ */
272
+LLVMValueRef
273
+llvm_get_decl(LLVMModuleRef mod, LLVMValueRef v_src)
274
+{
275
+	LLVMValueRef v_fn;
276
+
277
+	/* don't repeatedly add function */
278
+	v_fn = LLVMGetNamedFunction(mod, LLVMGetValueName(v_src));
279
+	if (v_fn)
280
+		return v_fn;
281
+
282
+	v_fn = LLVMAddFunction(mod,
283
+						   LLVMGetValueName(v_src),
284
+						   LLVMGetElementType(LLVMTypeOf(v_src)));
285
+	llvm_copy_attributes(v_src, v_fn);
286
+
287
+	return v_fn;
288
+}
289
+
290
+/*
291
+ * Copy attributes from one function to another.
292
+ */
293
+void
294
+llvm_copy_attributes(LLVMValueRef v_from, LLVMValueRef v_to)
295
+{
296
+	int			num_attributes;
297
+	int			attno;
298
+	LLVMAttributeRef *attrs;
299
+
300
+	num_attributes =
301
+		LLVMGetAttributeCountAtIndex(v_from, LLVMAttributeFunctionIndex);
302
+
303
+	attrs = palloc(sizeof(LLVMAttributeRef) * num_attributes);
304
+	LLVMGetAttributesAtIndex(v_from, LLVMAttributeFunctionIndex, attrs);
305
+
306
+	for (attno = 0; attno < num_attributes; attno++)
307
+	{
308
+		LLVMAddAttributeAtIndex(v_to, LLVMAttributeFunctionIndex,
309
+								attrs[attno]);
310
+	}
311
+}
312
+
313
+/*
314
+ * Optimize code in module using the flags set in context.
315
+ */
316
+static void
317
+llvm_optimize_module(LLVMJitContext *context, LLVMModuleRef module)
318
+{
319
+	LLVMPassManagerBuilderRef llvm_pmb;
320
+	LLVMPassManagerRef llvm_mpm;
321
+	LLVMPassManagerRef llvm_fpm;
322
+	LLVMValueRef func;
323
+	int			compile_optlevel;
324
+
325
+	if (context->base.flags & PGJIT_OPT3)
326
+		compile_optlevel = 3;
327
+	else
328
+		compile_optlevel = 0;
329
+
330
+	/*
331
+	 * Have to create a new pass manager builder every pass through, as the
332
+	 * inliner has some per-builder state. Otherwise one ends up only inlining
333
+	 * a function the first time though.
334
+	 */
335
+	llvm_pmb = LLVMPassManagerBuilderCreate();
336
+	LLVMPassManagerBuilderSetOptLevel(llvm_pmb, compile_optlevel);
337
+	llvm_fpm = LLVMCreateFunctionPassManagerForModule(module);
338
+
339
+	if (context->base.flags & PGJIT_OPT3)
340
+	{
341
+		/* TODO: Unscientifically determined threshhold */
342
+		LLVMPassManagerBuilderUseInlinerWithThreshold(llvm_pmb, 512);
343
+	}
344
+	else
345
+	{
346
+		/* we rely on mem2reg heavily, so emit even in the O0 case */
347
+		LLVMAddPromoteMemoryToRegisterPass(llvm_fpm);
348
+	}
349
+
350
+	LLVMPassManagerBuilderPopulateFunctionPassManager(llvm_pmb, llvm_fpm);
351
+
352
+	/*
353
+	 * Do function level optimization. This could be moved to the point where
354
+	 * functions are emitted, to reduce memory usage a bit.
355
+	 */
356
+	LLVMInitializeFunctionPassManager(llvm_fpm);
357
+	for (func = LLVMGetFirstFunction(context->module);
358
+		 func != NULL;
359
+		 func = LLVMGetNextFunction(func))
360
+		LLVMRunFunctionPassManager(llvm_fpm, func);
361
+	LLVMFinalizeFunctionPassManager(llvm_fpm);
362
+	LLVMDisposePassManager(llvm_fpm);
363
+
364
+	/*
365
+	 * Perform module level optimization. We do so even in the non-optimized
366
+	 * case, so always-inline functions etc get inlined. It's cheap enough.
367
+	 */
368
+	llvm_mpm = LLVMCreatePassManager();
369
+	LLVMPassManagerBuilderPopulateModulePassManager(llvm_pmb,
370
+													llvm_mpm);
371
+	/* always use always-inliner pass */
372
+	if (!(context->base.flags & PGJIT_OPT3))
373
+		LLVMAddAlwaysInlinerPass(llvm_mpm);
374
+	LLVMRunPassManager(llvm_mpm, context->module);
375
+	LLVMDisposePassManager(llvm_mpm);
376
+
377
+	LLVMPassManagerBuilderDispose(llvm_pmb);
378
+}
379
+
380
+/*
381
+ * Emit code for the currently pending module.
382
+ */
383
+static void
384
+llvm_compile_module(LLVMJitContext *context)
385
+{
386
+	LLVMOrcModuleHandle orc_handle;
387
+	MemoryContext oldcontext;
388
+	static LLVMOrcJITStackRef compile_orc;
389
+	instr_time	starttime;
390
+	instr_time	endtime;
391
+
392
+	if (context->base.flags & PGJIT_OPT3)
393
+		compile_orc = llvm_opt3_orc;
394
+	else
395
+		compile_orc = llvm_opt0_orc;
396
+
397
+	if (jit_dump_bitcode)
398
+	{
399
+		char	   *filename;
400
+
401
+		filename = psprintf("%u.%zu.bc",
402
+							MyProcPid,
403
+							context->module_generation);
404
+		LLVMWriteBitcodeToFile(context->module, filename);
405
+		pfree(filename);
406
+	}
407
+
408
+
409
+	/* optimize according to the chosen optimization settings */
410
+	INSTR_TIME_SET_CURRENT(starttime);
411
+	llvm_optimize_module(context, context->module);
412
+	INSTR_TIME_SET_CURRENT(endtime);
413
+	INSTR_TIME_ACCUM_DIFF(context->base.optimization_counter,
414
+						  endtime, starttime);
415
+
416
+	if (jit_dump_bitcode)
417
+	{
418
+		char	   *filename;
419
+
420
+		filename = psprintf("%u.%zu.optimized.bc",
421
+							MyProcPid,
422
+							context->module_generation);
423
+		LLVMWriteBitcodeToFile(context->module, filename);
424
+		pfree(filename);
425
+	}
426
+
427
+	/*
428
+	 * Emit the code. Note that this can, depending on the optimization
429
+	 * settings, take noticeable resources as code emission executes low-level
430
+	 * instruction combining/selection passes etc. Without optimization a
431
+	 * faster instruction selection mechanism is used.
432
+	 */
433
+	INSTR_TIME_SET_CURRENT(starttime);
434
+#if LLVM_VERSION_MAJOR < 5
435
+	{
436
+		orc_handle = LLVMOrcAddEagerlyCompiledIR(compile_orc, context->module,
437
+												 llvm_resolve_symbol, NULL);
438
+	}
439
+#else
440
+	{
441
+		LLVMSharedModuleRef smod;
442
+
443
+		smod = LLVMOrcMakeSharedModule(context->module);
444
+		if (LLVMOrcAddEagerlyCompiledIR(compile_orc, &orc_handle, smod,
445
+										llvm_resolve_symbol, NULL))
446
+		{
447
+			elog(ERROR, "failed to jit module");
448
+		}
449
+		LLVMOrcDisposeSharedModuleRef(smod);
450
+	}
451
+#endif
452
+	INSTR_TIME_SET_CURRENT(endtime);
453
+	INSTR_TIME_ACCUM_DIFF(context->base.emission_counter,
454
+						  endtime, starttime);
455
+
456
+	context->module = NULL;
457
+	context->compiled = true;
458
+
459
+	/* remember emitted code for cleanup and lookups */
460
+	oldcontext = MemoryContextSwitchTo(TopMemoryContext);
461
+	{
462
+		LLVMJitHandle *handle;
463
+
464
+		handle = (LLVMJitHandle *) palloc(sizeof(LLVMJitHandle));
465
+		handle->stack = compile_orc;
466
+		handle->orc_handle = orc_handle;
467
+
468
+		context->handles = lappend(context->handles, handle);
469
+	}
470
+	MemoryContextSwitchTo(oldcontext);
471
+
472
+	ereport(DEBUG1,
473
+			(errmsg("time to opt: %.3fs, emit: %.3fs",
474
+					INSTR_TIME_GET_DOUBLE(context->base.optimization_counter),
475
+					INSTR_TIME_GET_DOUBLE(context->base.emission_counter)),
476
+			 errhidestmt(true),
477
+			 errhidecontext(true)));
84 478
 }
85 479
 
86 480
 /*
@@ -90,6 +484,9 @@ static void
90 484
 llvm_session_initialize(void)
91 485
 {
92 486
 	MemoryContext oldcontext;
487
+	char	   *error = NULL;
488
+	char	   *cpu = NULL;
489
+	char	   *features = NULL;
93 490
 
94 491
 	if (llvm_session_initialized)
95 492
 		return;
@@ -100,6 +497,50 @@ llvm_session_initialize(void)
100 497
 	LLVMInitializeNativeAsmPrinter();
101 498
 	LLVMInitializeNativeAsmParser();
102 499
 
500
+	/*
501
+	 * Synchronize types early, as that also includes inferring the target
502
+	 * triple.
503
+	 */
504
+	llvm_create_types();
505
+
506
+	if (LLVMGetTargetFromTriple(llvm_triple, &llvm_targetref, &error) != 0)
507
+	{
508
+		elog(FATAL, "failed to query triple %s\n", error);
509
+	}
510
+
511
+	/*
512
+	 * We want the generated code to use all available features. Therefore
513
+	 * grab the host CPU string and detect features of the current CPU. The
514
+	 * latter is needed because some CPU architectures default to enabling
515
+	 * features not all CPUs have (weird, huh).
516
+	 */
517
+	cpu = LLVMGetHostCPUName();
518
+	features = LLVMGetHostCPUFeatures();
519
+	elog(DEBUG2, "LLVMJIT detected CPU \"%s\", with features \"%s\"",
520
+		 cpu, features);
521
+
522
+	llvm_opt0_targetmachine =
523
+		LLVMCreateTargetMachine(llvm_targetref, llvm_triple, cpu, features,
524
+								LLVMCodeGenLevelNone,
525
+								LLVMRelocDefault,
526
+								LLVMCodeModelJITDefault);
527
+	llvm_opt3_targetmachine =
528
+		LLVMCreateTargetMachine(llvm_targetref, llvm_triple, cpu, features,
529
+								LLVMCodeGenLevelAggressive,
530
+								LLVMRelocDefault,
531
+								LLVMCodeModelJITDefault);
532
+
533
+	LLVMDisposeMessage(cpu);
534
+	cpu = NULL;
535
+	LLVMDisposeMessage(features);
536
+	features = NULL;
537
+
538
+	/* force symbols in main binary to be loaded */
539
+	LLVMLoadLibraryPermanently(NULL);
540
+
541
+	llvm_opt0_orc = LLVMOrcCreateInstance(llvm_opt0_targetmachine);
542
+	llvm_opt3_orc = LLVMOrcCreateInstance(llvm_opt3_targetmachine);
543
+
103 544
 	before_shmem_exit(llvm_shutdown, 0);
104 545
 
105 546
 	llvm_session_initialized = true;
@@ -111,3 +552,150 @@ static void
111 552
 llvm_shutdown(int code, Datum arg)
112 553
 {
113 554
 }
555
+
556
+/* helper for llvm_create_types */
557
+static LLVMTypeRef
558
+load_type(LLVMModuleRef mod, const char *name)
559
+{
560
+	LLVMValueRef value;
561
+	LLVMTypeRef typ;
562
+
563
+	/* this'll return a *pointer* to the global */
564
+	value = LLVMGetNamedGlobal(mod, name);
565
+	if (!value)
566
+		elog(ERROR, "type %s is unknown", name);
567
+
568
+	/* therefore look at the contained type and return that */
569
+	typ = LLVMTypeOf(value);
570
+	Assert(typ != NULL);
571
+	typ = LLVMGetElementType(typ);
572
+	Assert(typ != NULL);
573
+	return typ;
574
+}
575
+
576
+/*
577
+ * Load required information, types, function signatures from llvmjit_types.c
578
+ * and make them available in global variables.
579
+ *
580
+ * Those global variables are then used while emitting code.
581
+ */
582
+static void
583
+llvm_create_types(void)
584
+{
585
+	char		path[MAXPGPATH];
586
+	LLVMMemoryBufferRef buf;
587
+	char	   *msg;
588
+	LLVMModuleRef mod = NULL;
589
+
590
+	snprintf(path, MAXPGPATH, "%s/%s", pkglib_path, "llvmjit_types.bc");
591
+
592
+	/* open file */
593
+	if (LLVMCreateMemoryBufferWithContentsOfFile(path, &buf, &msg))
594
+	{
595
+		elog(ERROR, "LLVMCreateMemoryBufferWithContentsOfFile(%s) failed: %s",
596
+			 path, msg);
597
+	}
598
+
599
+	/* eagerly load contents, going to need it all */
600
+	if (LLVMParseBitcode2(buf, &mod))
601
+	{
602
+		elog(ERROR, "LLVMParseBitcode2 of %s failed", path);
603
+	}
604
+	LLVMDisposeMemoryBuffer(buf);
605
+
606
+	/*
607
+	 * Load triple & layout from clang emitted file so we're guaranteed to be
608
+	 * compatible.
609
+	 */
610
+	llvm_triple = pstrdup(LLVMGetTarget(mod));
611
+	llvm_layout = pstrdup(LLVMGetDataLayoutStr(mod));
612
+
613
+	TypeSizeT = load_type(mod, "TypeSizeT");
614
+
615
+	AttributeTemplate = LLVMGetNamedFunction(mod, "AttributeTemplate");
616
+	FuncStrlen = LLVMGetNamedFunction(mod, "strlen");
617
+
618
+	/*
619
+	 * Leave the module alive, otherwise references to function would be
620
+	 * dangling.
621
+	 */
622
+
623
+	return;
624
+}
625
+
626
+/*
627
+ * Split a symbol into module / function parts.  If the function is in the
628
+ * main binary (or an external library) *modname will be NULL.
629
+ */
630
+void
631
+llvm_split_symbol_name(const char *name, char **modname, char **funcname)
632
+{
633
+	*modname = NULL;
634
+	*funcname = NULL;
635
+
636
+	/*
637
+	 * Module function names are pgextern.$module.$funcname
638
+	 */
639
+	if (strncmp(name, "pgextern.", strlen("pgextern.")) == 0)
640
+	{
641
+		/*
642
+		 * Symbol names cannot contain a ., therefore we can split based on
643
+		 * first and last occurance of one.
644
+		 */
645
+		*funcname = rindex(name, '.');
646
+		(*funcname)++;			/* jump over . */
647
+
648
+		*modname = pnstrdup(name + strlen("pgextern."),
649
+							*funcname - name - strlen("pgextern.") - 1);
650
+		Assert(funcname);
651
+
652
+		*funcname = pstrdup(*funcname);
653
+	}
654
+	else
655
+	{
656
+		*modname = NULL;
657
+		*funcname = pstrdup(name);
658
+	}
659
+}
660
+
661
+/*
662
+ * Attempt to resolve symbol, so LLVM can emit a reference to it.
663
+ */
664
+static uint64_t
665
+llvm_resolve_symbol(const char *symname, void *ctx)
666
+{
667
+	uintptr_t	addr;
668
+	char	   *funcname;
669
+	char	   *modname;
670
+
671
+	/*
672
+	 * OSX prefixes all object level symbols with an underscore. But neither
673
+	 * dlsym() nor PG's inliner expect that. So undo.
674
+	 */
675
+#if defined(__darwin__)
676
+	if (symname[0] != '_')
677
+		elog(ERROR, "expected prefixed symbol name, but got \"%s\"", symname);
678
+	symname++;
679
+#endif
680
+
681
+	llvm_split_symbol_name(symname, &modname, &funcname);
682
+
683
+	/* functions that aren't resolved to names shouldn't ever get here */
684
+	Assert(funcname);
685
+
686
+	if (modname)
687
+		addr = (uintptr_t) load_external_function(modname, funcname,
688
+												  true, NULL);
689
+	else
690
+		addr = (uintptr_t) LLVMSearchForAddressOfSymbol(symname);
691
+
692
+	pfree(funcname);
693
+	if (modname)
694
+		pfree(modname);
695
+
696
+	/* let LLVM will error out - should never happen */
697
+	if (!addr)
698
+		elog(WARNING, "failed to resolve name %s", symname);
699
+
700
+	return (uint64_t) addr;
701
+}

+ 60
- 0
src/backend/jit/llvm/llvmjit_types.c View File

@@ -0,0 +1,60 @@
1
+/*-------------------------------------------------------------------------
2
+ *
3
+ * llvmjit_types.c
4
+ *	  List of types needed by JIT emitting code.
5
+ *
6
+ * JIT emitting code often needs to access struct elements, create functions
7
+ * with the correct signature etc. To allow synchronizing these types with a
8
+ * low chance of definitions getting out of sync, this file lists types and
9
+ * functions that directly need to be accessed from LLVM.
10
+ *
11
+ * When LlVM is first used in a backend, a bitcode version of this file, will
12
+ * be loaded. The needed types and signatures will be stored into Struct*,
13
+ * Type*, Func* variables.
14
+ *
15
+ * NB: This file will not be linked into the server, it's just converted to
16
+ * bitcode.
17
+ *
18
+ *
19
+ * Copyright (c) 2016-2018, PostgreSQL Global Development Group
20
+ *
21
+ * IDENTIFICATION
22
+ *	  src/backend/lib/llvmjit_types.c
23
+ *
24
+ *-------------------------------------------------------------------------
25
+ */
26
+
27
+#include "postgres.h"
28
+
29
+#include "fmgr.h"
30
+
31
+/*
32
+ * List of types needed for JITing. These have to be non-static, otherwise
33
+ * clang/LLVM will omit them.  As this file will never be linked into
34
+ * anything, that's harmless.
35
+ */
36
+size_t		TypeSizeT;
37
+
38
+
39
+/*
40
+ * To determine which attributes functions need to have (depends e.g. on
41
+ * compiler version and settings) to be compatible for inlining, we simply
42
+ * copy the attributes of this function.
43
+ */
44
+extern Datum AttributeTemplate(PG_FUNCTION_ARGS);
45
+Datum
46
+AttributeTemplate(PG_FUNCTION_ARGS)
47
+{
48
+	PG_RETURN_NULL();
49
+}
50
+
51
+
52
+/*
53
+ * To force signatures of functions used during JITing to be present,
54
+ * reference the functions required. This again has to be non-static, to avoid
55
+ * being removed as unnecessary.
56
+ */
57
+void	   *referenced_functions[] =
58
+{
59
+	strlen
60
+};

+ 11
- 0
src/backend/utils/misc/guc.c View File

@@ -1734,6 +1734,17 @@ static struct config_bool ConfigureNamesBool[] =
1734 1734
 		NULL, NULL, NULL
1735 1735
 	},
1736 1736
 
1737
+	{
1738
+		{"jit_dump_bitcode", PGC_SUSET, DEVELOPER_OPTIONS,
1739
+			gettext_noop("Write out LLVM bitcode to facilitate JIT debugging."),
1740
+			NULL,
1741
+			GUC_NOT_IN_SAMPLE
1742
+		},
1743
+		&jit_dump_bitcode,
1744
+		false,
1745
+		NULL, NULL, NULL
1746
+	},
1747
+
1737 1748
 	/* End-of-list marker */
1738 1749
 	{
1739 1750
 		{NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL, NULL

+ 21
- 0
src/include/jit/jit.h View File

@@ -11,14 +11,34 @@
11 11
 #ifndef JIT_H
12 12
 #define JIT_H
13 13
 
14
+#include "executor/instrument.h"
14 15
 #include "utils/resowner.h"
15 16
 
16 17
 
18
+/* Flags deterimining what kind of JIT operations to perform */
19
+#define PGJIT_NONE     0
20
+#define PGJIT_PERFORM  1 << 0
21
+#define PGJIT_OPT3     1 << 1
22
+
23
+
17 24
 typedef struct JitContext
18 25
 {
26
+	/* see PGJIT_* above */
19 27
 	int			flags;
20 28
 
21 29
 	ResourceOwner resowner;
30
+
31
+	/* number of emitted functions */
32
+	size_t		created_functions;
33
+
34
+	/* accumulated time to generate code */
35
+	instr_time	generation_counter;
36
+
37
+	/* accumulated time for optimization */
38
+	instr_time	optimization_counter;
39
+
40
+	/* accumulated time for code emission */
41
+	instr_time	emission_counter;
22 42
 } JitContext;
23 43
 
24 44
 typedef struct JitProviderCallbacks JitProviderCallbacks;
@@ -38,6 +58,7 @@ struct JitProviderCallbacks
38 58
 /* GUCs */
39 59
 extern bool jit_enabled;
40 60
 extern char *jit_provider;
61
+extern bool jit_dump_bitcode;
41 62
 
42 63
 
43 64
 extern void jit_reset_after_error(void);

+ 30
- 0
src/include/jit/llvmjit.h View File

@@ -30,19 +30,49 @@ extern "C"
30 30
 
31 31
 
32 32
 #include "jit/jit.h"
33
+#include "nodes/pg_list.h"
33 34
 
34 35
 
35 36
 typedef struct LLVMJitContext
36 37
 {
37 38
 	JitContext	base;
39
+
40
+	/* number of modules created */
41
+	size_t		module_generation;
42
+
43
+	/* current, "open for write", module */
44
+	LLVMModuleRef module;
45
+
46
+	/* is there any pending code that needs to be emitted */
47
+	bool		compiled;
48
+
49
+	/* # of objects emitted, used to generate non-conflicting names */
50
+	int			counter;
51
+
52
+	/* list of handles for code emitted via Orc */
53
+	List	   *handles;
38 54
 } LLVMJitContext;
39 55
 
56
+
57
+/* type and struct definitions */
58
+extern LLVMTypeRef TypeSizeT;
59
+
60
+extern LLVMValueRef AttributeTemplate;
61
+extern LLVMValueRef FuncStrlen;
62
+
63
+
40 64
 extern void llvm_enter_fatal_on_oom(void);
41 65
 extern void llvm_leave_fatal_on_oom(void);
42 66
 extern void llvm_reset_after_error(void);
43 67
 extern void llvm_assert_in_fatal_section(void);
44 68
 
45 69
 extern LLVMJitContext *llvm_create_context(int jitFlags);
70
+extern LLVMModuleRef llvm_mutable_module(LLVMJitContext *context);
71
+extern char *llvm_expand_funcname(LLVMJitContext *context, const char *basename);
72
+extern void *llvm_get_function(LLVMJitContext *context, const char *funcname);
73
+extern void llvm_split_symbol_name(const char *name, char **modname, char **funcname);
74
+extern LLVMValueRef llvm_get_decl(LLVMModuleRef mod, LLVMValueRef f);
75
+extern void llvm_copy_attributes(LLVMValueRef from, LLVMValueRef to);
46 76
 
47 77
 
48 78
 /*

+ 3
- 0
src/tools/pgindent/typedefs.list View File

@@ -1103,6 +1103,9 @@ LDAPURLDesc
1103 1103
 LDAP_TIMEVAL
1104 1104
 LINE
1105 1105
 LLVMJitContext
1106
+LLVMJitHandle
1107
+LLVMTypeRef
1108
+LLVMValueRef
1106 1109
 LOCALLOCK
1107 1110
 LOCALLOCKOWNER
1108 1111
 LOCALLOCKTAG

Loading…
Cancel
Save