Compute information about EEOP_*_FETCHSOME at expression init time.

Previously this information was computed when JIT compiling an
expression.  But the information is useful for assertions in the
non-JIT case too (for assertions), therefore it makes sense to move
it.

This will, in a followup commit, allow to treat different slot types
differently. E.g. for virtual slots there's no need to generate a JIT
function to deform the slot.

Author: Andres Freund
Discussion: https://postgr.es/m/20181105210039.hh4vvi4vwoq5ba2q@alap3.anarazel.de
This commit is contained in:
Andres Freund 2018-11-15 22:00:30 -08:00
parent 1a0586de36
commit 675af5c01e
3 changed files with 124 additions and 29 deletions

View File

@ -65,6 +65,7 @@ static void ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args,
static void ExecInitExprSlots(ExprState *state, Node *node); static void ExecInitExprSlots(ExprState *state, Node *node);
static void ExecPushExprSlots(ExprState *state, LastAttnumInfo *info); static void ExecPushExprSlots(ExprState *state, LastAttnumInfo *info);
static bool get_last_attnums_walker(Node *node, LastAttnumInfo *info); static bool get_last_attnums_walker(Node *node, LastAttnumInfo *info);
static void ExecComputeSlotInfo(ExprState *state, ExprEvalStep *op);
static void ExecInitWholeRowVar(ExprEvalStep *scratch, Var *variable, static void ExecInitWholeRowVar(ExprEvalStep *scratch, Var *variable,
ExprState *state); ExprState *state);
static void ExecInitArrayRef(ExprEvalStep *scratch, ArrayRef *aref, static void ExecInitArrayRef(ExprEvalStep *scratch, ArrayRef *aref,
@ -2288,21 +2289,30 @@ ExecPushExprSlots(ExprState *state, LastAttnumInfo *info)
{ {
scratch.opcode = EEOP_INNER_FETCHSOME; scratch.opcode = EEOP_INNER_FETCHSOME;
scratch.d.fetch.last_var = info->last_inner; scratch.d.fetch.last_var = info->last_inner;
scratch.d.fetch.fixed = false;
scratch.d.fetch.kind = NULL;
scratch.d.fetch.known_desc = NULL; scratch.d.fetch.known_desc = NULL;
ExecComputeSlotInfo(state, &scratch);
ExprEvalPushStep(state, &scratch); ExprEvalPushStep(state, &scratch);
} }
if (info->last_outer > 0) if (info->last_outer > 0)
{ {
scratch.opcode = EEOP_OUTER_FETCHSOME; scratch.opcode = EEOP_OUTER_FETCHSOME;
scratch.d.fetch.last_var = info->last_outer; scratch.d.fetch.last_var = info->last_outer;
scratch.d.fetch.fixed = false;
scratch.d.fetch.kind = NULL;
scratch.d.fetch.known_desc = NULL; scratch.d.fetch.known_desc = NULL;
ExecComputeSlotInfo(state, &scratch);
ExprEvalPushStep(state, &scratch); ExprEvalPushStep(state, &scratch);
} }
if (info->last_scan > 0) if (info->last_scan > 0)
{ {
scratch.opcode = EEOP_SCAN_FETCHSOME; scratch.opcode = EEOP_SCAN_FETCHSOME;
scratch.d.fetch.last_var = info->last_scan; scratch.d.fetch.last_var = info->last_scan;
scratch.d.fetch.fixed = false;
scratch.d.fetch.kind = NULL;
scratch.d.fetch.known_desc = NULL; scratch.d.fetch.known_desc = NULL;
ExecComputeSlotInfo(state, &scratch);
ExprEvalPushStep(state, &scratch); ExprEvalPushStep(state, &scratch);
} }
} }
@ -2355,6 +2365,94 @@ get_last_attnums_walker(Node *node, LastAttnumInfo *info)
(void *) info); (void *) info);
} }
/*
* Compute additional information for EEOP_*_FETCHSOME ops.
*
* The goal is to determine whether a slot is 'fixed', that is, every
* evaluation of the the expression will have the same type of slot, with an
* equivalent descriptor.
*/
static void
ExecComputeSlotInfo(ExprState *state, ExprEvalStep *op)
{
PlanState *parent = state->parent;
TupleDesc desc = NULL;
const TupleTableSlotOps *tts_ops = NULL;
bool isfixed = false;
if (op->d.fetch.known_desc != NULL)
{
desc = op->d.fetch.known_desc;
tts_ops = op->d.fetch.kind;
isfixed = op->d.fetch.kind != NULL;
}
else if (!parent)
{
isfixed = false;
}
else if (op->opcode == EEOP_INNER_FETCHSOME)
{
PlanState *is = innerPlanState(parent);
if (parent->inneropsset && !parent->inneropsfixed)
{
isfixed = false;
}
else if (parent->inneropsset && parent->innerops)
{
isfixed = true;
tts_ops = parent->innerops;
}
else if (is)
{
tts_ops = ExecGetResultSlotOps(is, &isfixed);
desc = ExecGetResultType(is);
}
}
else if (op->opcode == EEOP_OUTER_FETCHSOME)
{
PlanState *os = outerPlanState(parent);
if (parent->outeropsset && !parent->outeropsfixed)
{
isfixed = false;
}
else if (parent->outeropsset && parent->outerops)
{
isfixed = true;
tts_ops = parent->outerops;
}
else if (os)
{
tts_ops = ExecGetResultSlotOps(os, &isfixed);
desc = ExecGetResultType(os);
}
}
else if (op->opcode == EEOP_SCAN_FETCHSOME)
{
desc = parent->scandesc;
if (parent && parent->scanops)
tts_ops = parent->scanops;
if (parent->scanopsset)
isfixed = parent->scanopsfixed;
}
if (isfixed && desc != NULL && tts_ops != NULL)
{
op->d.fetch.fixed = true;
op->d.fetch.kind = tts_ops;
op->d.fetch.known_desc = desc;
}
else
{
op->d.fetch.fixed = false;
op->d.fetch.kind = NULL;
op->d.fetch.known_desc = NULL;
}
}
/* /*
* Prepare step for the evaluation of a whole-row variable. * Prepare step for the evaluation of a whole-row variable.
* The caller still has to push the step. * The caller still has to push the step.
@ -3255,12 +3353,18 @@ ExecBuildGroupingEqual(TupleDesc ldesc, TupleDesc rdesc,
/* push deform steps */ /* push deform steps */
scratch.opcode = EEOP_INNER_FETCHSOME; scratch.opcode = EEOP_INNER_FETCHSOME;
scratch.d.fetch.last_var = maxatt; scratch.d.fetch.last_var = maxatt;
scratch.d.fetch.fixed = false;
scratch.d.fetch.known_desc = ldesc; scratch.d.fetch.known_desc = ldesc;
scratch.d.fetch.kind = lops;
ExecComputeSlotInfo(state, &scratch);
ExprEvalPushStep(state, &scratch); ExprEvalPushStep(state, &scratch);
scratch.opcode = EEOP_OUTER_FETCHSOME; scratch.opcode = EEOP_OUTER_FETCHSOME;
scratch.d.fetch.last_var = maxatt; scratch.d.fetch.last_var = maxatt;
scratch.d.fetch.fixed = false;
scratch.d.fetch.known_desc = rdesc; scratch.d.fetch.known_desc = rdesc;
scratch.d.fetch.kind = rops;
ExecComputeSlotInfo(state, &scratch);
ExprEvalPushStep(state, &scratch); ExprEvalPushStep(state, &scratch);
/* /*

View File

@ -276,6 +276,8 @@ llvm_compile_expr(ExprState *state)
LLVMValueRef v_slot; LLVMValueRef v_slot;
LLVMBasicBlockRef b_fetch; LLVMBasicBlockRef b_fetch;
LLVMValueRef v_nvalid; LLVMValueRef v_nvalid;
LLVMValueRef l_jit_deform = NULL;
const TupleTableSlotOps *tts_ops = NULL;
b_fetch = l_bb_before_v(opblocks[i + 1], b_fetch = l_bb_before_v(opblocks[i + 1],
"op.%d.fetch", i); "op.%d.fetch", i);
@ -283,40 +285,22 @@ llvm_compile_expr(ExprState *state)
if (op->d.fetch.known_desc) if (op->d.fetch.known_desc)
desc = op->d.fetch.known_desc; desc = op->d.fetch.known_desc;
if (op->d.fetch.fixed)
tts_ops = op->d.fetch.kind;
if (opcode == EEOP_INNER_FETCHSOME) if (opcode == EEOP_INNER_FETCHSOME)
{
PlanState *is = innerPlanState(parent);
v_slot = v_innerslot; v_slot = v_innerslot;
if (!desc &&
is &&
is->ps_ResultTupleSlot &&
TTS_FIXED(is->ps_ResultTupleSlot))
desc = is->ps_ResultTupleSlot->tts_tupleDescriptor;
}
else if (opcode == EEOP_OUTER_FETCHSOME) else if (opcode == EEOP_OUTER_FETCHSOME)
{
PlanState *os = outerPlanState(parent);
v_slot = v_outerslot; v_slot = v_outerslot;
if (!desc &&
os &&
os->ps_ResultTupleSlot &&
TTS_FIXED(os->ps_ResultTupleSlot))
desc = os->ps_ResultTupleSlot->tts_tupleDescriptor;
}
else else
{
v_slot = v_scanslot; v_slot = v_scanslot;
if (!desc && parent)
desc = parent->scandesc;
}
/* /*
* Check if all required attributes are available, or * Check if all required attributes are available, or
* whether deforming is required. * whether deforming is required.
*
* TODO: skip nvalid check if slot is fixed and known to
* be a virtual slot.
*/ */
v_nvalid = v_nvalid =
l_load_struct_gep(b, v_slot, l_load_struct_gep(b, v_slot,
@ -336,19 +320,21 @@ llvm_compile_expr(ExprState *state)
* function specific to tupledesc and the exact number of * function specific to tupledesc and the exact number of
* to-be-extracted attributes. * to-be-extracted attributes.
*/ */
if (desc && (context->base.flags & PGJIT_DEFORM)) if (tts_ops && desc && (context->base.flags & PGJIT_DEFORM))
{ {
LLVMValueRef params[1];
LLVMValueRef l_jit_deform;
l_jit_deform = l_jit_deform =
slot_compile_deform(context, desc, slot_compile_deform(context, desc,
op->d.fetch.last_var); op->d.fetch.last_var);
}
if (l_jit_deform)
{
LLVMValueRef params[1];
params[0] = v_slot; params[0] = v_slot;
LLVMBuildCall(b, l_jit_deform, LLVMBuildCall(b, l_jit_deform,
params, lengthof(params), ""); params, lengthof(params), "");
} }
else else
{ {

View File

@ -262,7 +262,12 @@ typedef struct ExprEvalStep
{ {
/* attribute number up to which to fetch (inclusive) */ /* attribute number up to which to fetch (inclusive) */
int last_var; int last_var;
/* will the type of slot be the same for every invocation */
bool fixed;
/* tuple descriptor, if known */
TupleDesc known_desc; TupleDesc known_desc;
/* type of slot, can only be relied upon if fixed is set */
const TupleTableSlotOps *kind;
} fetch; } fetch;
/* for EEOP_INNER/OUTER/SCAN_[SYS]VAR[_FIRST] */ /* for EEOP_INNER/OUTER/SCAN_[SYS]VAR[_FIRST] */