diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 16a80a0..f998062 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -564,8 +564,9 @@ ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es,
 	 * at a later stage.
 	 */
 	if (queryDesc->estate->es_jit && es->costs &&
-		queryDesc->estate->es_jit->created_functions > 0)
-		ExplainPrintJIT(es, queryDesc);
+		queryDesc->estate->es_jit->jit_instr.created_functions > 0)
+		ExplainPrintJIT(es, queryDesc->estate->es_jit_flags,
+						&queryDesc->estate->es_jit->jit_instr, -1);
 
 	/*
 	 * Close down the query and free resources.  Include time for this in the
@@ -690,43 +691,54 @@ ExplainPrintTriggers(ExplainState *es, QueryDesc *queryDesc)
 
 /*
  * ExplainPrintJIT -
- *	  Append information about JITing to es->str.
+ *	  Append information about JITing to es->str. Can be used to print
+ *    the JIT instrumentation of the backend or that of a specific worker.
  */
 void
-ExplainPrintJIT(ExplainState *es, QueryDesc *queryDesc)
+ExplainPrintJIT(ExplainState *es, int jit_flags,
+				JitInstrumentation *jit_instr, int worker_i)
 {
-	JitContext *jc = queryDesc->estate->es_jit;
+	bool		for_workers = (worker_i >= 0);
+	char	   *jit_group_label = (for_workers ? "Workers JIT" : "JIT");
 
-	ExplainOpenGroup("JIT", "JIT", true, es);
+	ExplainOpenGroup(jit_group_label, jit_group_label, true, es);
 
-	if (es->format == EXPLAIN_FORMAT_TEXT)
+	if (for_workers)
 	{
-		es->indent += 1;
-		appendStringInfo(es->str, "JIT:\n");
+		ExplainPropertyInteger("Jit for Worker ", NULL, worker_i, es);
+
+		/* Jump to the specific worker */
+		jit_instr = jit_instr + worker_i;
 	}
+	else
+		ExplainPropertyText("Jit", "", es);
+
+	if (es->format == EXPLAIN_FORMAT_TEXT)
+		es->indent += 1;
+
+	ExplainPropertyInteger("Functions", NULL, jit_instr->created_functions, es);
 
-	ExplainPropertyInteger("Functions", NULL, jc->created_functions, es);
 	if (es->analyze && es->timing)
 		ExplainPropertyFloat("Generation Time", "ms",
-							 1000.0 * INSTR_TIME_GET_DOUBLE(jc->generation_counter),
+							 1000.0 * INSTR_TIME_GET_DOUBLE(jit_instr->generation_counter),
 							 3, es);
 
-	ExplainPropertyBool("Inlining", jc->flags & PGJIT_INLINE, es);
+	ExplainPropertyBool("Inlining", jit_flags & PGJIT_INLINE, es);
 
 	if (es->analyze && es->timing)
 		ExplainPropertyFloat("Inlining Time", "ms",
-							 1000.0 * INSTR_TIME_GET_DOUBLE(jc->inlining_counter),
+							 1000.0 * INSTR_TIME_GET_DOUBLE(jit_instr->inlining_counter),
 							 3, es);
 
-	ExplainPropertyBool("Optimization", jc->flags & PGJIT_OPT3, es);
+	ExplainPropertyBool("Optimization", jit_flags & PGJIT_OPT3, es);
 	if (es->analyze && es->timing)
 		ExplainPropertyFloat("Optimization Time", "ms",
-							 1000.0 * INSTR_TIME_GET_DOUBLE(jc->optimization_counter),
+							 1000.0 * INSTR_TIME_GET_DOUBLE(jit_instr->optimization_counter),
 							 3, es);
 
 	if (es->analyze && es->timing)
 		ExplainPropertyFloat("Emission Time", "ms",
-							 1000.0 * INSTR_TIME_GET_DOUBLE(jc->emission_counter),
+							 1000.0 * INSTR_TIME_GET_DOUBLE(jit_instr->emission_counter),
 							 3, es);
 
 	ExplainCloseGroup("JIT", "JIT", true, es);
@@ -1515,6 +1527,26 @@ ExplainNode(PlanState *planstate, List *ancestors,
 					ExplainPropertyInteger("Workers Launched", NULL,
 										   nworkers, es);
 				}
+
+				/* Print per-worker Jit instrumentation. */
+
+				if (es->analyze && es->verbose &&
+					outerPlanState(planstate)->worker_jit_instrument)
+				{
+					PlanState *child = outerPlanState(planstate);
+					int			n;
+					SharedJitInstrumentation *w = child->worker_jit_instrument;
+
+					/* TODO: Consider es->costs before printing ? */
+					for (n = 0; n < w->num_workers; ++n)
+					{
+						/* TODO: Just skip for created_functions <= 0 ?? */
+						if (w->jit_instr[n].created_functions > 0)
+							ExplainPrintJIT(es, child->state->es_jit_flags,
+											w->jit_instr, n);
+					}
+				}
+
 				if (gather->single_copy || es->format != EXPLAIN_FORMAT_TEXT)
 					ExplainPropertyBool("Single Copy", gather->single_copy, es);
 			}
diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c
index c93084e..8152099 100644
--- a/src/backend/executor/execParallel.c
+++ b/src/backend/executor/execParallel.c
@@ -37,6 +37,7 @@
 #include "executor/nodeSort.h"
 #include "executor/nodeSubplan.h"
 #include "executor/tqueue.h"
+#include "jit/jit.h"
 #include "nodes/nodeFuncs.h"
 #include "optimizer/planmain.h"
 #include "optimizer/planner.h"
@@ -62,6 +63,7 @@
 #define PARALLEL_KEY_INSTRUMENTATION	UINT64CONST(0xE000000000000006)
 #define PARALLEL_KEY_DSA				UINT64CONST(0xE000000000000007)
 #define PARALLEL_KEY_QUERY_TEXT		UINT64CONST(0xE000000000000008)
+#define PARALLEL_KEY_JIT_INSTRUMENTATION UINT64CONST(0xE000000000000009)
 
 #define PARALLEL_TUPLE_QUEUE_SIZE		65536
 
@@ -573,9 +575,11 @@ ExecInitParallelPlan(PlanState *planstate, EState *estate,
 	char	   *paramlistinfo_space;
 	BufferUsage *bufusage_space;
 	SharedExecutorInstrumentation *instrumentation = NULL;
+	SharedJitInstrumentation *jit_instrumentation = NULL;
 	int			pstmt_len;
 	int			paramlistinfo_len;
 	int			instrumentation_len = 0;
+	int			jit_instrumentation_len;
 	int			instrument_offset = 0;
 	Size		dsa_minsize = dsa_minimum_size();
 	char	   *query_string;
@@ -671,6 +675,17 @@ ExecInitParallelPlan(PlanState *planstate, EState *estate,
 		shm_toc_estimate_keys(&pcxt->estimator, 1);
 	}
 
+	/* Estimate space for JIT instrumentation, if required. */
+	if (estate->es_jit_flags != PGJIT_NONE)
+	{
+		jit_instrumentation_len =
+			offsetof(SharedJitInstrumentation, jit_instr) +
+			sizeof(JitInstrumentation) * nworkers;
+		jit_instrumentation_len = MAXALIGN(jit_instrumentation_len);
+		shm_toc_estimate_chunk(&pcxt->estimator, jit_instrumentation_len);
+		shm_toc_estimate_keys(&pcxt->estimator, 1);
+	}
+
 	/* Estimate space for DSA area. */
 	shm_toc_estimate_chunk(&pcxt->estimator, dsa_minsize);
 	shm_toc_estimate_keys(&pcxt->estimator, 1);
@@ -744,6 +759,17 @@ ExecInitParallelPlan(PlanState *planstate, EState *estate,
 		pei->instrumentation = instrumentation;
 	}
 
+	if (estate->es_jit_flags != PGJIT_NONE)
+	{
+		jit_instrumentation = shm_toc_allocate(pcxt->toc, jit_instrumentation_len);
+		jit_instrumentation->num_workers = nworkers;
+		memset(jit_instrumentation->jit_instr, 0,
+			   sizeof(JitInstrumentation) * nworkers);
+		shm_toc_insert(pcxt->toc, PARALLEL_KEY_JIT_INSTRUMENTATION,
+					   jit_instrumentation);
+		pei->jit_instrumentation = jit_instrumentation;
+	}
+
 	/*
 	 * Create a DSA area that can be used by the leader and all workers.
 	 * (However, if we failed to create a DSM and are using private memory
@@ -1004,6 +1030,51 @@ ExecParallelRetrieveInstrumentation(PlanState *planstate,
 }
 
 /*
+ * Add up the workers' JIT instrumentation from dynamic shared memory.
+ */
+static void
+ExecParallelRetrieveJitInstrumentation(PlanState *planstate,
+									   SharedJitInstrumentation *shared_jit)
+{
+	int			n;
+	JitContext *jit = planstate->state->es_jit;
+	MemoryContext oldcontext;
+	int			ibytes;
+
+	/*
+	 * If the leader hasn't yet created a jit context, allocate a base context.
+	 * The provider context will be created on demand.
+	 */
+	if (!jit)
+	{
+		planstate->state->es_jit = jit =
+			jit_create_base_context(planstate->state->es_jit_flags);
+	}
+
+	/* Accummulate all the workers' instrumentations. */
+	for (n = 0; n < shared_jit->num_workers; ++n)
+		InstrJitAgg(&jit->jit_instr, &shared_jit->jit_instr[n]);
+
+	/*
+	 * Also store the per-worker detail.
+	 *
+	 * Similar to ExecParallelRetrieveInstrumentation(), allocate the
+	 * instrumentation in per-query context.
+	 */
+
+	oldcontext = MemoryContextSwitchTo(planstate->state->es_query_cxt);
+	ibytes = offsetof(SharedJitInstrumentation, jit_instr)
+			 + mul_size(shared_jit->num_workers, sizeof(JitInstrumentation));
+	ibytes = MAXALIGN(ibytes);
+	elog(LOG, "ibytes : %d; num_workers: %d; sizeof(JitInstrumentation): %ld",
+			   ibytes, shared_jit->num_workers, sizeof(JitInstrumentation));
+	planstate->worker_jit_instrument = palloc(ibytes);
+	MemoryContextSwitchTo(oldcontext);
+
+	memcpy(planstate->worker_jit_instrument, shared_jit, ibytes);
+}
+
+/*
  * Finish parallel execution.  We wait for parallel workers to finish, and
  * accumulate their buffer usage.
  */
@@ -1068,6 +1139,11 @@ ExecParallelCleanup(ParallelExecutorInfo *pei)
 		ExecParallelRetrieveInstrumentation(pei->planstate,
 											pei->instrumentation);
 
+	/* Accumulate JIT instrumentation, if any. */
+	if (pei->jit_instrumentation)
+		ExecParallelRetrieveJitInstrumentation(pei->planstate,
+											pei->jit_instrumentation);
+
 	/* Free any serialized parameters. */
 	if (DsaPointerIsValid(pei->param_exec))
 	{
@@ -1274,6 +1350,7 @@ ParallelQueryMain(dsm_segment *seg, shm_toc *toc)
 	DestReceiver *receiver;
 	QueryDesc  *queryDesc;
 	SharedExecutorInstrumentation *instrumentation;
+	SharedJitInstrumentation *jit_instrumentation;
 	int			instrument_options = 0;
 	void	   *area_space;
 	dsa_area   *area;
@@ -1287,6 +1364,8 @@ ParallelQueryMain(dsm_segment *seg, shm_toc *toc)
 	instrumentation = shm_toc_lookup(toc, PARALLEL_KEY_INSTRUMENTATION, true);
 	if (instrumentation != NULL)
 		instrument_options = instrumentation->instrument_options;
+	jit_instrumentation = shm_toc_lookup(toc, PARALLEL_KEY_JIT_INSTRUMENTATION,
+										 true);
 	queryDesc = ExecParallelGetQueryDesc(toc, receiver, instrument_options);
 
 	/* Setting debug_query_string for individual workers */
@@ -1350,6 +1429,14 @@ ParallelQueryMain(dsm_segment *seg, shm_toc *toc)
 		ExecParallelReportInstrumentation(queryDesc->planstate,
 										  instrumentation);
 
+	/* Report JIT instrumentation data if any */
+	if (queryDesc->estate->es_jit && jit_instrumentation != NULL)
+	{
+		Assert(ParallelWorkerNumber < jit_instrumentation->num_workers);
+		jit_instrumentation->jit_instr[ParallelWorkerNumber] =
+			queryDesc->estate->es_jit->jit_instr;
+	}
+
 	/* Must do this after capturing instrumentation. */
 	ExecutorEnd(queryDesc);
 
diff --git a/src/backend/jit/jit.c b/src/backend/jit/jit.c
index 7d3883d..8c4385b 100644
--- a/src/backend/jit/jit.c
+++ b/src/backend/jit/jit.c
@@ -27,6 +27,7 @@
 #include "executor/execExpr.h"
 #include "jit/jit.h"
 #include "miscadmin.h"
+#include "utils/memutils.h"
 #include "utils/resowner_private.h"
 #include "utils/fmgrprotos.h"
 
@@ -140,9 +141,11 @@ jit_reset_after_error(void)
 void
 jit_release_context(JitContext *context)
 {
-	if (provider_successfully_loaded)
-		provider.release_context(context);
-
+	if (provider_successfully_loaded && context->provider_context)
+	{
+		provider.release_context(context->provider_context);
+		pfree(context->provider_context);
+	}
 	ResourceOwnerForgetJIT(context->resowner, PointerGetDatum(context));
 	pfree(context);
 }
@@ -182,6 +185,36 @@ jit_compile_expr(struct ExprState *state)
 	return false;
 }
 
+JitContext *
+jit_create_base_context(int flags)
+{
+	JitContext *context;
+
+	ResourceOwnerEnlargeJIT(CurrentResourceOwner);
+
+	context = MemoryContextAllocZero(TopMemoryContext, sizeof(JitContext));
+	context->flags = flags;
+
+	/* ensure cleanup */
+	context->resowner = CurrentResourceOwner;
+	ResourceOwnerRememberJIT(CurrentResourceOwner, PointerGetDatum(context));
+
+	return context;
+}
+
+/* Aggregate JIT instrumentation information */
+void
+InstrJitAgg(JitInstrumentation *dst, JitInstrumentation *add)
+{
+	dst->created_functions = add_size(dst->created_functions,
+									  add->created_functions);
+
+	INSTR_TIME_ADD(dst->generation_counter, add->generation_counter);
+	INSTR_TIME_ADD(dst->inlining_counter, add->inlining_counter);
+	INSTR_TIME_ADD(dst->optimization_counter, add->optimization_counter);
+	INSTR_TIME_ADD(dst->emission_counter, add->emission_counter);
+}
+
 static bool
 file_exists(const char *name)
 {
diff --git a/src/backend/jit/llvm/llvmjit.c b/src/backend/jit/llvm/llvmjit.c
index 640c27f..6c6f078 100644
--- a/src/backend/jit/llvm/llvmjit.c
+++ b/src/backend/jit/llvm/llvmjit.c
@@ -102,7 +102,8 @@ static LLVMOrcJITStackRef llvm_opt0_orc;
 static LLVMOrcJITStackRef llvm_opt3_orc;
 
 
-static void llvm_release_context(JitContext *context);
+static void *llvm_create_provider_context(JitContext *jit_context);
+static void llvm_release_context(void *context);
 static void llvm_session_initialize(void);
 static void llvm_shutdown(int code, Datum arg);
 static void llvm_compile_module(LLVMJitContext *context);
@@ -129,28 +130,39 @@ _PG_jit_provider_init(JitProviderCallbacks *cb)
 /*
  * Create a context for JITing work.
  *
+ * The provider first creates the base context if not already created; and then
+ * creates the provider-specific context.
  * The context, including subsidiary resources, will be cleaned up either when
  * the context is explicitly released, or when the lifetime of
  * CurrentResourceOwner ends (usually the end of the current [sub]xact).
  */
-LLVMJitContext *
-llvm_create_context(int jitFlags)
+JitContext *
+llvm_create_context(JitContext *jit, int flags)
+{
+	if (!jit)
+		jit = jit_create_base_context(flags);
+
+	if (!jit->provider_context)
+		jit->provider_context = llvm_create_provider_context(jit);
+
+	return jit;
+}
+
+/*
+ * Create a provider-specific JIT context.
+ */
+static void *
+llvm_create_provider_context(JitContext *jit_context)
 {
 	LLVMJitContext *context;
 
 	llvm_assert_in_fatal_section();
-
 	llvm_session_initialize();
 
-	ResourceOwnerEnlargeJIT(CurrentResourceOwner);
-
-	context = MemoryContextAllocZero(TopMemoryContext,
-									 sizeof(LLVMJitContext));
-	context->base.flags = jitFlags;
+	context = MemoryContextAllocZero(TopMemoryContext, sizeof(LLVMJitContext));
 
-	/* ensure cleanup */
-	context->base.resowner = CurrentResourceOwner;
-	ResourceOwnerRememberJIT(CurrentResourceOwner, PointerGetDatum(context));
+	/* Have a handle to the base context */
+	context->base = jit_context;
 
 	return context;
 }
@@ -159,7 +171,7 @@ llvm_create_context(int jitFlags)
  * Release resources required by one llvm context.
  */
 static void
-llvm_release_context(JitContext *context)
+llvm_release_context(void *context)
 {
 	LLVMJitContext *llvm_context = (LLVMJitContext *) context;
 
@@ -224,7 +236,7 @@ llvm_expand_funcname(struct LLVMJitContext *context, const char *basename)
 {
 	Assert(context->module != NULL);
 
-	context->base.created_functions++;
+	context->base->jit_instr.created_functions++;
 
 	/*
 	 * Previously we used dots to separate, but turns out some tools, e.g.
@@ -422,7 +434,7 @@ llvm_optimize_module(LLVMJitContext *context, LLVMModuleRef module)
 	LLVMValueRef func;
 	int			compile_optlevel;
 
-	if (context->base.flags & PGJIT_OPT3)
+	if (context->base->flags & PGJIT_OPT3)
 		compile_optlevel = 3;
 	else
 		compile_optlevel = 0;
@@ -436,7 +448,7 @@ llvm_optimize_module(LLVMJitContext *context, LLVMModuleRef module)
 	LLVMPassManagerBuilderSetOptLevel(llvm_pmb, compile_optlevel);
 	llvm_fpm = LLVMCreateFunctionPassManagerForModule(module);
 
-	if (context->base.flags & PGJIT_OPT3)
+	if (context->base->flags & PGJIT_OPT3)
 	{
 		/* TODO: Unscientifically determined threshhold */
 		LLVMPassManagerBuilderUseInlinerWithThreshold(llvm_pmb, 512);
@@ -469,11 +481,11 @@ llvm_optimize_module(LLVMJitContext *context, LLVMModuleRef module)
 	LLVMPassManagerBuilderPopulateModulePassManager(llvm_pmb,
 													llvm_mpm);
 	/* always use always-inliner pass */
-	if (!(context->base.flags & PGJIT_OPT3))
+	if (!(context->base->flags & PGJIT_OPT3))
 		LLVMAddAlwaysInlinerPass(llvm_mpm);
 	/* if doing inlining, but no expensive optimization, add inlining pass */
-	if (context->base.flags & PGJIT_INLINE
-		&& !(context->base.flags & PGJIT_OPT3))
+	if (context->base->flags & PGJIT_INLINE
+		&& !(context->base->flags & PGJIT_OPT3))
 		LLVMAddFunctionInliningPass(llvm_mpm);
 	LLVMRunPassManager(llvm_mpm, context->module);
 	LLVMDisposePassManager(llvm_mpm);
@@ -493,18 +505,18 @@ llvm_compile_module(LLVMJitContext *context)
 	instr_time	starttime;
 	instr_time	endtime;
 
-	if (context->base.flags & PGJIT_OPT3)
+	if (context->base->flags & PGJIT_OPT3)
 		compile_orc = llvm_opt3_orc;
 	else
 		compile_orc = llvm_opt0_orc;
 
 	/* perform inlining */
-	if (context->base.flags & PGJIT_INLINE)
+	if (context->base->flags & PGJIT_INLINE)
 	{
 		INSTR_TIME_SET_CURRENT(starttime);
 		llvm_inline(context->module);
 		INSTR_TIME_SET_CURRENT(endtime);
-		INSTR_TIME_ACCUM_DIFF(context->base.inlining_counter,
+		INSTR_TIME_ACCUM_DIFF(context->base->jit_instr.inlining_counter,
 							  endtime, starttime);
 	}
 
@@ -524,7 +536,7 @@ llvm_compile_module(LLVMJitContext *context)
 	INSTR_TIME_SET_CURRENT(starttime);
 	llvm_optimize_module(context, context->module);
 	INSTR_TIME_SET_CURRENT(endtime);
-	INSTR_TIME_ACCUM_DIFF(context->base.optimization_counter,
+	INSTR_TIME_ACCUM_DIFF(context->base->jit_instr.optimization_counter,
 						  endtime, starttime);
 
 	if (jit_dump_bitcode)
@@ -575,7 +587,7 @@ llvm_compile_module(LLVMJitContext *context)
 	}
 #endif
 	INSTR_TIME_SET_CURRENT(endtime);
-	INSTR_TIME_ACCUM_DIFF(context->base.emission_counter,
+	INSTR_TIME_ACCUM_DIFF(context->base->jit_instr.emission_counter,
 						  endtime, starttime);
 
 	context->module = NULL;
@@ -596,9 +608,9 @@ llvm_compile_module(LLVMJitContext *context)
 
 	ereport(DEBUG1,
 			(errmsg("time to inline: %.3fs, opt: %.3fs, emit: %.3fs",
-					INSTR_TIME_GET_DOUBLE(context->base.inlining_counter),
-					INSTR_TIME_GET_DOUBLE(context->base.optimization_counter),
-					INSTR_TIME_GET_DOUBLE(context->base.emission_counter)),
+					INSTR_TIME_GET_DOUBLE(context->base->jit_instr.inlining_counter),
+					INSTR_TIME_GET_DOUBLE(context->base->jit_instr.optimization_counter),
+					INSTR_TIME_GET_DOUBLE(context->base->jit_instr.emission_counter)),
 			 errhidestmt(true),
 			 errhidecontext(true)));
 }
diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c
index 0f31093..1214214 100644
--- a/src/backend/jit/llvm/llvmjit_expr.c
+++ b/src/backend/jit/llvm/llvmjit_expr.c
@@ -123,19 +123,19 @@ llvm_compile_expr(ExprState *state)
 	llvm_enter_fatal_on_oom();
 
 	/* get or create JIT context */
-	if (parent && parent->state->es_jit)
+	if (parent)
 	{
-		context = (LLVMJitContext *) parent->state->es_jit;
+		parent->state->es_jit = llvm_create_context(parent->state->es_jit,
+													parent->state->es_jit_flags);
+		context = parent->state->es_jit->provider_context;
 	}
 	else
 	{
-		context = llvm_create_context(parent->state->es_jit_flags);
-
-		if (parent)
-		{
-			parent->state->es_jit = &context->base;
-		}
-
+		/*
+		 * TODO: The !parent case is not handled correctly even on HEAD. Where
+		 * would we get jit_flags from ?
+		 */
+		context = (LLVMJitContext *) llvm_create_context(NULL, 0);
 	}
 
 	INSTR_TIME_SET_CURRENT(starttime);
@@ -336,7 +336,7 @@ llvm_compile_expr(ExprState *state)
 					 * function specific to tupledesc and the exact number of
 					 * to-be-extracted attributes.
 					 */
-					if (desc && (context->base.flags & PGJIT_DEFORM))
+					if (desc && (context->base->flags & PGJIT_DEFORM))
 					{
 						LLVMValueRef params[1];
 						LLVMValueRef l_jit_deform;
@@ -2557,7 +2557,7 @@ llvm_compile_expr(ExprState *state)
 	llvm_leave_fatal_on_oom();
 
 	INSTR_TIME_SET_CURRENT(endtime);
-	INSTR_TIME_ACCUM_DIFF(context->base.generation_counter,
+	INSTR_TIME_ACCUM_DIFF(context->base->jit_instr.generation_counter,
 						  endtime, starttime);
 
 	return true;
diff --git a/src/include/commands/explain.h b/src/include/commands/explain.h
index 9b75baa..164cf59 100644
--- a/src/include/commands/explain.h
+++ b/src/include/commands/explain.h
@@ -81,7 +81,8 @@ extern void ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into,
 extern void ExplainPrintPlan(ExplainState *es, QueryDesc *queryDesc);
 extern void ExplainPrintTriggers(ExplainState *es, QueryDesc *queryDesc);
 
-extern void ExplainPrintJIT(ExplainState *es, QueryDesc *queryDesc);
+extern void ExplainPrintJIT(ExplainState *es, int jit_flags,
+				JitInstrumentation *jit_instr, int worker_i);
 
 extern void ExplainQueryText(ExplainState *es, QueryDesc *queryDesc);
 
diff --git a/src/include/executor/execParallel.h b/src/include/executor/execParallel.h
index 626a66c..ea1991f 100644
--- a/src/include/executor/execParallel.h
+++ b/src/include/executor/execParallel.h
@@ -27,6 +27,7 @@ typedef struct ParallelExecutorInfo
 	ParallelContext *pcxt;		/* parallel context we're using */
 	BufferUsage *buffer_usage;	/* points to bufusage area in DSM */
 	SharedExecutorInstrumentation *instrumentation; /* optional */
+	SharedJitInstrumentation *jit_instrumentation; /* optional */
 	dsa_area   *area;			/* points to DSA area in DSM */
 	dsa_pointer param_exec;		/* serialized PARAM_EXEC parameters */
 	bool		finished;		/* set true by ExecParallelFinish */
diff --git a/src/include/jit/jit.h b/src/include/jit/jit.h
index b451f40..c5ea103 100644
--- a/src/include/jit/jit.h
+++ b/src/include/jit/jit.h
@@ -23,14 +23,8 @@
 #define PGJIT_EXPR	   (1 << 3)
 #define PGJIT_DEFORM   (1 << 4)
 
-
-typedef struct JitContext
+typedef struct JitInstrumentation
 {
-	/* see PGJIT_* above */
-	int			flags;
-
-	ResourceOwner resowner;
-
 	/* number of emitted functions */
 	size_t		created_functions;
 
@@ -45,6 +39,27 @@ typedef struct JitContext
 
 	/* accumulated time for code emission */
 	instr_time	emission_counter;
+} JitInstrumentation;
+
+/*
+ * DSM structure for accumulating jit instrumentation of all workers.
+ *
+ * num_workers: Number of workers.
+ */
+typedef struct SharedJitInstrumentation
+{
+	int			num_workers;
+	JitInstrumentation jit_instr[FLEXIBLE_ARRAY_MEMBER];
+	/* array of JitInstrumentation objects * num_workers follows */
+} SharedJitInstrumentation;
+
+typedef struct JitContext
+{
+	/* see PGJIT_* above */
+	int			flags;
+	ResourceOwner resowner;
+	JitInstrumentation jit_instr;
+	void	   *provider_context;
 } JitContext;
 
 typedef struct JitProviderCallbacks JitProviderCallbacks;
@@ -52,13 +67,15 @@ typedef struct JitProviderCallbacks JitProviderCallbacks;
 extern void _PG_jit_provider_init(JitProviderCallbacks *cb);
 typedef void (*JitProviderInit) (JitProviderCallbacks *cb);
 typedef void (*JitProviderResetAfterErrorCB) (void);
-typedef void (*JitProviderReleaseContextCB) (JitContext *context);
+typedef JitContext * (*JitProviderCreateContextCB) (int flags);
+typedef void (*JitProviderReleaseContextCB) (void *context);
 struct ExprState;
 typedef bool (*JitProviderCompileExprCB) (struct ExprState *state);
 
 struct JitProviderCallbacks
 {
 	JitProviderResetAfterErrorCB reset_after_error;
+	JitProviderCreateContextCB create_context;
 	JitProviderReleaseContextCB release_context;
 	JitProviderCompileExprCB compile_expr;
 };
@@ -85,6 +102,8 @@ extern void jit_release_context(JitContext *context);
  * not be able to perform JIT (i.e. return false).
  */
 extern bool jit_compile_expr(struct ExprState *state);
+extern JitContext *jit_create_base_context(int flags);
+extern void InstrJitAgg(JitInstrumentation *dst, JitInstrumentation *add);
 
 
 #endif							/* JIT_H */
diff --git a/src/include/jit/llvmjit.h b/src/include/jit/llvmjit.h
index c81cff8..e9ef624 100644
--- a/src/include/jit/llvmjit.h
+++ b/src/include/jit/llvmjit.h
@@ -37,7 +37,7 @@ extern "C"
 
 typedef struct LLVMJitContext
 {
-	JitContext	base;
+	JitContext	*base;
 
 	/* number of modules created */
 	size_t		module_generation;
@@ -91,7 +91,7 @@ extern void llvm_leave_fatal_on_oom(void);
 extern void llvm_reset_after_error(void);
 extern void llvm_assert_in_fatal_section(void);
 
-extern LLVMJitContext *llvm_create_context(int jitFlags);
+extern JitContext *llvm_create_context(JitContext *jit, int flags);
 extern LLVMModuleRef llvm_mutable_module(LLVMJitContext *context);
 extern char *llvm_expand_funcname(LLVMJitContext *context, const char *basename);
 extern void *llvm_get_function(LLVMJitContext *context, const char *funcname);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 687d7cd..51a3611 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -18,6 +18,7 @@
 #include "access/heapam.h"
 #include "access/tupconvert.h"
 #include "executor/instrument.h"
+#include "jit/jit.h"
 #include "lib/pairingheap.h"
 #include "nodes/params.h"
 #include "nodes/plannodes.h"
@@ -923,6 +924,9 @@ typedef struct PlanState
 	Instrumentation *instrument;	/* Optional runtime stats for this node */
 	WorkerInstrumentation *worker_instrument;	/* per-worker instrumentation */
 
+	/* Per-worker JIT instrumentation */
+	SharedJitInstrumentation *worker_jit_instrument;
+
 	/*
 	 * Common structural data for all Plan types.  These links to subsidiary
 	 * state trees parallel links in the associated plan tree (except for the
