From 3744226db2b5a8864cfd05fd695d412364d17104 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Mon, 28 Oct 2019 16:44:18 -0700
Subject: [PATCH v2 4/8] 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     |   7 ++
 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, 163 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 7e486449eca..9005759cd06 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -2458,6 +2458,13 @@ ExecComputeSlotInfo(ExprState *state, ExprEvalStep *op)
 	if (op->d.fetch.fixed && op->d.fetch.kind == &TTSOpsVirtual)
 		return false;
 
+	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;
+
 	return true;
 }
 
diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c
index 4ba8c78cbc9..be8d424c8d0 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.385.gbc12974a89

