Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 2a0faed

Browse files
committed
Add expression compilation support to LLVM JIT provider.
In addition to the interpretation of expressions (which back evaluation of WHERE clauses, target list projection, aggregates transition values etc) support compiling expressions to native code, using the infrastructure added in earlier commits. To avoid duplicating a lot of code, only support emitting code for cases that are likely to be performance critical. For expression steps that aren't deemed that, use the existing interpreter. The generated code isn't great - some architectural changes are required to address that. But this already yields a significant speedup for some analytics queries, particularly with WHERE clauses filtering a lot, or computing multiple aggregates. Author: Andres Freund Tested-By: Thomas Munro Discussion: https://postgr.es/m/20170901064131.tazjxwus3k2w3ybh@alap3.anarazel.de Disable JITing for VALUES() nodes. VALUES() nodes are only ever executed once. This is primarily helpful for debugging, when forcing JITing even for cheap queries. Author: Andres Freund Discussion: https://postgr.es/m/20170901064131.tazjxwus3k2w3ybh@alap3.anarazel.de
1 parent 7ced1d1 commit 2a0faed

File tree

13 files changed

+2890
-3
lines changed

13 files changed

+2890
-3
lines changed

src/backend/executor/execExpr.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include "executor/execExpr.h"
3737
#include "executor/nodeSubplan.h"
3838
#include "funcapi.h"
39+
#include "jit/jit.h"
3940
#include "miscadmin.h"
4041
#include "nodes/makefuncs.h"
4142
#include "nodes/nodeFuncs.h"
@@ -623,6 +624,9 @@ ExecCheck(ExprState *state, ExprContext *econtext)
623624
static void
624625
ExecReadyExpr(ExprState *state)
625626
{
627+
if (jit_compile_expr(state))
628+
return;
629+
626630
ExecReadyInterpretedExpr(state);
627631
}
628632

src/backend/executor/nodeValuesscan.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
#include "executor/executor.h"
2727
#include "executor/nodeValuesscan.h"
28+
#include "jit/jit.h"
2829
#include "utils/expandeddatum.h"
2930

3031

@@ -98,6 +99,7 @@ ValuesNext(ValuesScanState *node)
9899
bool *isnull;
99100
ListCell *lc;
100101
int resind;
102+
int saved_jit_flags;
101103

102104
/*
103105
* Get rid of any prior cycle's leftovers. We use ReScanExprContext
@@ -128,7 +130,15 @@ ValuesNext(ValuesScanState *node)
128130
oldsubplans = node->ss.ps.subPlan;
129131
node->ss.ps.subPlan = NIL;
130132

133+
/*
134+
* As the expressions are only ever used once, disable JIT for
135+
* them. This is worthwhile because it's common to insert significant
136+
* amounts of data via VALUES().
137+
*/
138+
saved_jit_flags = econtext->ecxt_estate->es_jit_flags;
139+
econtext->ecxt_estate->es_jit_flags = PGJIT_NONE;
131140
exprstatelist = ExecInitExprList(exprlist, &node->ss.ps);
141+
econtext->ecxt_estate->es_jit_flags = saved_jit_flags;
132142

133143
node->ss.ps.subPlan = oldsubplans;
134144

src/backend/jit/jit.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525

2626
#include "fmgr.h"
27+
#include "executor/execExpr.h"
2728
#include "jit/jit.h"
2829
#include "miscadmin.h"
2930
#include "utils/resowner_private.h"
@@ -35,6 +36,7 @@ bool jit_enabled = true;
3536
char *jit_provider = "llvmjit";
3637
bool jit_debugging_support = false;
3738
bool jit_dump_bitcode = false;
39+
bool jit_expressions = true;
3840
bool jit_profiling_support = false;
3941
double jit_above_cost = 100000;
4042
double jit_optimize_above_cost = 500000;
@@ -143,6 +145,41 @@ jit_release_context(JitContext *context)
143145
pfree(context);
144146
}
145147

148+
/*
149+
* Ask provider to JIT compile an expression.
150+
*
151+
* Returns true if successful, false if not.
152+
*/
153+
bool
154+
jit_compile_expr(struct ExprState *state)
155+
{
156+
/*
157+
* We can easily create a one-off context for functions without an
158+
* associated PlanState (and thus EState). But because there's no executor
159+
* shutdown callback that could deallocate the created function, they'd
160+
* live to the end of the transactions, where they'd be cleaned up by the
161+
* resowner machinery. That can lead to a noticeable amount of memory
162+
* usage, and worse, trigger some quadratic behaviour in gdb. Therefore,
163+
* at least for now, don't create a JITed function in those circumstances.
164+
*/
165+
if (!state->parent)
166+
return false;
167+
168+
/* if no jitting should be performed at all */
169+
if (!(state->parent->state->es_jit_flags & PGJIT_PERFORM))
170+
return false;
171+
172+
/* or if expressions aren't JITed */
173+
if (!(state->parent->state->es_jit_flags & PGJIT_EXPR))
174+
return false;
175+
176+
/* this also takes !jit_enabled into account */
177+
if (provider_init())
178+
return provider.compile_expr(state);
179+
180+
return false;
181+
}
182+
146183
static bool
147184
file_exists(const char *name)
148185
{

src/backend/jit/llvm/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ OBJS=$(WIN32RES)
3939
# Infrastructure
4040
OBJS += llvmjit.o llvmjit_error.o llvmjit_wrap.o
4141
# Code generation
42-
OBJS +=
42+
OBJS += llvmjit_expr.o
4343

4444
all: all-shared-lib llvmjit_types.bc
4545

src/backend/jit/llvm/llvmjit.c

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "postgres.h"
1515

1616
#include "jit/llvmjit.h"
17+
#include "jit/llvmjit_emit.h"
1718

1819
#include "miscadmin.h"
1920

@@ -114,6 +115,7 @@ _PG_jit_provider_init(JitProviderCallbacks *cb)
114115
{
115116
cb->reset_after_error = llvm_reset_after_error;
116117
cb->release_context = llvm_release_context;
118+
cb->compile_expr = llvm_compile_expr;
117119
}
118120

119121
/*
@@ -339,6 +341,68 @@ llvm_copy_attributes(LLVMValueRef v_from, LLVMValueRef v_to)
339341
}
340342
}
341343

344+
/*
345+
* Return a callable LLVMValueRef for fcinfo.
346+
*/
347+
LLVMValueRef
348+
llvm_function_reference(LLVMJitContext *context,
349+
LLVMBuilderRef builder,
350+
LLVMModuleRef mod,
351+
FunctionCallInfo fcinfo)
352+
{
353+
char *modname;
354+
char *basename;
355+
char *funcname;
356+
357+
LLVMValueRef v_fn;
358+
359+
fmgr_symbol(fcinfo->flinfo->fn_oid, &modname, &basename);
360+
361+
if (modname != NULL && basename != NULL)
362+
{
363+
/* external function in loadable library */
364+
funcname = psprintf("pgextern.%s.%s", modname, basename);
365+
}
366+
else if (basename != NULL)
367+
{
368+
/* internal function */
369+
funcname = psprintf("%s", basename);
370+
}
371+
else
372+
{
373+
/*
374+
* Function we don't know to handle, return pointer. We do so by
375+
* creating a global constant containing a pointer to the function.
376+
* Makes IR more readable.
377+
*/
378+
LLVMValueRef v_fn_addr;
379+
380+
funcname = psprintf("pgoidextern.%u",
381+
fcinfo->flinfo->fn_oid);
382+
v_fn = LLVMGetNamedGlobal(mod, funcname);
383+
if (v_fn != 0)
384+
return LLVMBuildLoad(builder, v_fn, "");
385+
386+
v_fn_addr = l_ptr_const(fcinfo->flinfo->fn_addr, TypePGFunction);
387+
388+
v_fn = LLVMAddGlobal(mod, TypePGFunction, funcname);
389+
LLVMSetInitializer(v_fn, v_fn_addr);
390+
LLVMSetGlobalConstant(v_fn, true);
391+
392+
return LLVMBuildLoad(builder, v_fn, "");
393+
return v_fn;
394+
}
395+
396+
/* check if function already has been added */
397+
v_fn = LLVMGetNamedFunction(mod, funcname);
398+
if (v_fn != 0)
399+
return v_fn;
400+
401+
v_fn = LLVMAddFunction(mod, funcname, LLVMGetElementType(TypePGFunction));
402+
403+
return v_fn;
404+
}
405+
342406
/*
343407
* Optimize code in module using the flags set in context.
344408
*/

0 commit comments

Comments
 (0)