From ce2b22cb4b1cb4d6ceaaeb8d2be29cf5f31af476 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Thu, 26 Sep 2019 12:42:11 -0700
Subject: [PATCH v1 04/12] Add EXPLAIN option jit_details showing
 per-expression information about JIT.

This is useful both to understand where JIT is applied (and thus where
to improve), and to be able write regression tests to verify that we
can JIT compile specific parts of a query.

Note that currently the printed function names will make it harder to
use this for regression tests - a followup commit will improve that
angle.

Author:
Reviewed-By:
Discussion: https://postgr.es/m/
Backpatch:
---
 src/backend/commands/explain.c      | 144 ++++++++++++++++++++++++++--
 src/backend/executor/execExpr.c     |   8 ++
 src/backend/jit/llvm/llvmjit_expr.c |   9 ++
 src/include/commands/explain.h      |   1 +
 src/include/executor/execExpr.h     |   6 ++
 src/include/nodes/execnodes.h       |   5 +
 6 files changed, 164 insertions(+), 9 deletions(-)

diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index ea6b39d5abb..3ccb76bdfd1 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -19,6 +19,7 @@
 #include "commands/defrem.h"
 #include "commands/prepare.h"
 #include "executor/nodeHash.h"
+#include "executor/execExpr.h"
 #include "foreign/fdwapi.h"
 #include "jit/jit.h"
 #include "nodes/extensible.h"
@@ -69,6 +70,7 @@ static void show_plan_tlist(PlanState *planstate, List *ancestors,
 static void show_expression(Node *node, ExprState *expr, const char *qlabel,
 							PlanState *planstate, List *ancestors,
 							bool useprefix, ExplainState *es);
+static void show_jit_expr_details(ExprState *expr, ExplainState *es);
 static void show_qual(List *qual, ExprState *expr, const char *qlabel,
 					  PlanState *planstate, List *ancestors,
 					  bool useprefix, ExplainState *es);
@@ -170,6 +172,8 @@ ExplainQuery(ParseState *pstate, ExplainStmt *stmt, const char *queryString,
 			timing_set = true;
 			es->timing = defGetBoolean(opt);
 		}
+		else if (strcmp(opt->defname, "jit_details") == 0)
+			es->jit_details = defGetBoolean(opt);
 		else if (strcmp(opt->defname, "summary") == 0)
 		{
 			summary_set = true;
@@ -560,12 +564,11 @@ ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es,
 		ExplainPrintTriggers(es, queryDesc);
 
 	/*
-	 * Print info about JITing. Tied to es->costs because we don't want to
-	 * display this in regression tests, as it'd cause output differences
-	 * depending on build options.  Might want to separate that out from COSTS
-	 * at a later stage.
+	 * Print info about JITing. Tied to es->costs unless jit_details is set,
+	 * because we don't want to display this in regression tests, as it'd
+	 * cause output differences depending on build options.
 	 */
-	if (es->costs)
+	if (es->costs || es->jit_details)
 		ExplainPrintJITSummary(es, queryDesc);
 
 	/*
@@ -2140,10 +2143,40 @@ show_plan_tlist(PlanState *planstate, List *ancestors, ExplainState *es)
 	}
 
 	/* Print results */
-	if (planstate->ps_ProjInfo)
+	if (!planstate->ps_ProjInfo)
+		ExplainPropertyList("Output", result, es);
+	else if (!es->jit_details)
 		ExplainPropertyList("Project", result, es);
+	else if (es->format != EXPLAIN_FORMAT_TEXT)
+	{
+		ExplainOpenGroup("Project", "Project", true, es);
+
+		ExplainPropertyList("Expr", result, es);
+
+		if (planstate->ps_ProjInfo)
+		{
+			ExprState *expr = &planstate->ps_ProjInfo->pi_state;
+
+			show_jit_expr_details(expr, es);
+		}
+		ExplainCloseGroup("Project", "Project", true, es);
+	}
 	else
-		ExplainPropertyList("Output", result, es);
+	{
+		ExplainPropertyList("Project", result, es);
+
+		if (planstate->ps_ProjInfo)
+		{
+			ExprState *expr = &planstate->ps_ProjInfo->pi_state;
+
+			/* XXX: remove \n, probably instead just open-code ExplainPropertyList */
+			es->str->len--;
+
+			appendStringInfoString(es->str, "; ");
+			show_jit_expr_details(expr, es);
+			appendStringInfoChar(es->str, '\n');
+		}
+	}
 }
 
 /*
@@ -2167,8 +2200,101 @@ show_expression(Node *node, ExprState *expr, const char *qlabel,
 	/* Deparse the expression */
 	exprstr = deparse_expression(node, context, useprefix, false);
 
-	/* And add to es->str */
-	ExplainPropertyText(qlabel, exprstr, es);
+	if (!es->jit_details)
+		ExplainPropertyText(qlabel, exprstr, es);
+	else if (es->format != EXPLAIN_FORMAT_TEXT)
+	{
+		ExplainOpenGroup(qlabel, qlabel, true, es);
+
+		ExplainPropertyText("Expr", exprstr, es);
+
+		if (expr != NULL)
+			show_jit_expr_details(expr, es);
+		ExplainCloseGroup(qlabel, qlabel, true, es);
+	}
+	else
+	{
+		appendStringInfoSpaces(es->str, es->indent * 2);
+		appendStringInfo(es->str, "%s: %s", qlabel, exprstr);
+
+		if (expr != NULL)
+		{
+			appendStringInfoString(es->str, "; ");
+
+			show_jit_expr_details(expr, es);
+		}
+
+		appendStringInfoChar(es->str, '\n');
+	}
+}
+
+static void
+show_jit_expr_details(ExprState *expr, ExplainState *es)
+{
+	if (expr == NULL)
+		return;
+
+	Assert(es->jit_details);
+
+	if (es->format == EXPLAIN_FORMAT_TEXT)
+	{
+		if (expr->flags & EEO_FLAG_JIT_EXPR)
+			appendStringInfo(es->str, "JIT-Expr: %s", expr->expr_funcname);
+		else
+			appendStringInfoString(es->str, "JIT-Expr: false");
+
+		/*
+		 * Either show the function name for tuple deforming quoted in "", or
+		 * false if JIT compilation was performed, but no code was generated
+		 * for deforming the respective attribute.
+		 */
+
+		if (expr->scan_funcname)
+			appendStringInfo(es->str, ", JIT-Deform-Scan: %s", expr->scan_funcname);
+		else if (expr->flags & EEO_FLAG_JIT_EXPR &&
+				 expr->flags & EEO_FLAG_DEFORM_SCAN)
+			appendStringInfo(es->str, ", JIT-Deform-Scan: false");
+
+		if (expr->outer_funcname)
+			appendStringInfo(es->str, ", JIT-Deform-Outer: %s", expr->outer_funcname);
+		else if (expr->flags & EEO_FLAG_JIT_EXPR &&
+				 expr->flags & EEO_FLAG_DEFORM_OUTER)
+			appendStringInfo(es->str, ", JIT-Deform-Outer: false");
+
+		if (expr->inner_funcname)
+			appendStringInfo(es->str, ", JIT-Deform-Inner: %s", expr->inner_funcname);
+		else if (expr->flags & EEO_FLAG_JIT_EXPR &&
+				 expr->flags & (EEO_FLAG_DEFORM_INNER))
+			appendStringInfo(es->str, ", JIT-Deform-Inner: false");
+	}
+	else
+	{
+		if (expr->flags & EEO_FLAG_JIT_EXPR)
+			ExplainPropertyText("JIT-Expr", expr->expr_funcname, es);
+		else
+			ExplainPropertyBool("JIT-Expr", false, es);
+
+		if (expr->scan_funcname)
+			ExplainProperty("JIT-Deform-Scan", NULL, expr->scan_funcname, false, es);
+		else if (expr->flags & EEO_FLAG_DEFORM_SCAN)
+			ExplainProperty("JIT-Deform-Scan", NULL, "false", true, es);
+		else
+			ExplainProperty("JIT-Deform-Scan", NULL, "null", true, es);
+
+		if (expr->outer_funcname)
+			ExplainProperty("JIT-Deform-Outer", NULL, expr->outer_funcname, false, es);
+		else if (expr->flags & EEO_FLAG_DEFORM_OUTER)
+			ExplainProperty("JIT-Deform-Outer", NULL, "false", true, es);
+		else
+			ExplainProperty("JIT-Deform-Outer", NULL, "null", true, es);
+
+		if (expr->inner_funcname)
+			ExplainProperty("JIT-Deform-Inner", NULL, expr->inner_funcname, false, es);
+		else if (expr->flags & EEO_FLAG_DEFORM_INNER)
+			ExplainProperty("JIT-Deform-Inner", NULL, "false", true, es);
+		else
+			ExplainProperty("JIT-Deform-Inner", NULL, "null", true, es);
+	}
 }
 
 /*
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index 39442f8866f..2c792d59b58 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -2372,6 +2372,7 @@ ExecComputeSlotInfo(ExprState *state, ExprEvalStep *op)
 	TupleDesc	desc = NULL;
 	const TupleTableSlotOps *tts_ops = NULL;
 	bool		isfixed = false;
+	ExprEvalOp	opcode = op->opcode;
 
 	if (op->d.fetch.known_desc != NULL)
 	{
@@ -2444,6 +2445,13 @@ ExecComputeSlotInfo(ExprState *state, ExprEvalStep *op)
 		op->d.fetch.kind = NULL;
 		op->d.fetch.known_desc = NULL;
 	}
+
+	if (opcode == EEOP_INNER_FETCHSOME)
+		state->flags |= EEO_FLAG_DEFORM_INNER;
+	else if (opcode == EEOP_OUTER_FETCHSOME)
+		state->flags |= EEO_FLAG_DEFORM_OUTER;
+	else if (opcode == EEOP_SCAN_FETCHSOME)
+		state->flags |= EEO_FLAG_DEFORM_SCAN;
 }
 
 /*
diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c
index 7efc8f23ee3..d1d07751698 100644
--- a/src/backend/jit/llvm/llvmjit_expr.c
+++ b/src/backend/jit/llvm/llvmjit_expr.c
@@ -145,6 +145,7 @@ llvm_compile_expr(ExprState *state)
 
 	funcname = llvm_expand_funcname(context, "evalexpr");
 	context->base.instr.created_expr_functions++;
+	state->expr_funcname = funcname;
 
 	/* Create the signature and function */
 	{
@@ -336,6 +337,13 @@ llvm_compile_expr(ExprState *state)
 
 						LLVMBuildCall(b, l_jit_deform,
 									  params, lengthof(params), "");
+
+						if (opcode == EEOP_INNER_FETCHSOME)
+							state->inner_funcname = pstrdup(LLVMGetValueName(l_jit_deform));
+						else if (opcode == EEOP_OUTER_FETCHSOME)
+							state->outer_funcname = pstrdup(LLVMGetValueName(l_jit_deform));
+						else
+							state->scan_funcname = pstrdup(LLVMGetValueName(l_jit_deform));
 					}
 					else
 					{
@@ -2462,6 +2470,7 @@ llvm_compile_expr(ExprState *state)
 	INSTR_TIME_SET_CURRENT(endtime);
 	INSTR_TIME_ACCUM_DIFF(context->base.instr.generation_counter,
 						  endtime, starttime);
+	state->flags |= EEO_FLAG_JIT_EXPR;
 
 	return true;
 }
diff --git a/src/include/commands/explain.h b/src/include/commands/explain.h
index 8639891c164..5dbbeb3a3c3 100644
--- a/src/include/commands/explain.h
+++ b/src/include/commands/explain.h
@@ -36,6 +36,7 @@ typedef struct ExplainState
 	bool		timing;			/* print detailed node timing */
 	bool		summary;		/* print total planning and execution timing */
 	bool		settings;		/* print modified settings */
+	bool		jit_details;	/* print per-expression details about JIT */
 	ExplainFormat format;		/* output format */
 	/* state for output formatting --- not reset for each new plan tree */
 	int			indent;			/* current indentation level */
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index d21dbead0a2..5ebe50df888 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -26,6 +26,12 @@ struct SubscriptingRefState;
 #define EEO_FLAG_INTERPRETER_INITIALIZED	(1 << 1)
 /* jump-threading is in use */
 #define EEO_FLAG_DIRECT_THREADED			(1 << 2)
+/* is expression jit compiled */
+#define EEO_FLAG_JIT_EXPR					(1 << 3)
+/* does expression require tuple deforming */
+#define EEO_FLAG_DEFORM_INNER				(1 << 4)
+#define EEO_FLAG_DEFORM_OUTER				(1 << 5)
+#define EEO_FLAG_DEFORM_SCAN				(1 << 6)
 
 /* Typical API for out-of-line evaluation subroutines */
 typedef void (*ExecEvalSubroutine) (ExprState *state,
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 44f76082e99..d0b290fb342 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -113,6 +113,11 @@ typedef struct ExprState
 
 	Datum	   *innermost_domainval;
 	bool	   *innermost_domainnull;
+
+	const char *expr_funcname;
+	const char *outer_funcname;
+	const char *inner_funcname;
+	const char *scan_funcname;
 } ExprState;
 
 
-- 
2.23.0.162.gf1d4a28250

