From 7094935d2dd7334922a782d8ffa8c08b9bcbb7fc Mon Sep 17 00:00:00 2001
From: soumyadeep2007 <sochakraborty@pivotal.io>
Date: Sun, 6 Oct 2019 17:47:08 -0700
Subject: [PATCH v2] Optimize generated functions earlier to lower memory usage

We can optimize functions as soon as they are generated as opposed to
optimizing during module optimization, in order to lower memory usage.
This commit introduces `llvm_optimize_function()`, which will execute
function level passes in accordance with the optimization settings
inside `LLVMJitContext`.
---
 src/backend/jit/llvm/llvmjit.c        | 82 ++++++++++++++++-----------
 src/backend/jit/llvm/llvmjit_deform.c |  3 +
 src/backend/jit/llvm/llvmjit_expr.c   |  3 +
 src/include/jit/llvmjit.h             |  2 +
 4 files changed, 57 insertions(+), 33 deletions(-)

diff --git a/src/backend/jit/llvm/llvmjit.c b/src/backend/jit/llvm/llvmjit.c
index 65f8273d30..cdac7b1c5d 100644
--- a/src/backend/jit/llvm/llvmjit.c
+++ b/src/backend/jit/llvm/llvmjit.c
@@ -122,7 +122,7 @@ static void llvm_release_context(JitContext *context);
 static void llvm_session_initialize(void);
 static void llvm_shutdown(int code, Datum arg);
 static void llvm_compile_module(LLVMJitContext *context);
-static void llvm_optimize_module(LLVMJitContext *context, LLVMModuleRef module);
+static void llvm_optimize_module(LLVMJitContext *context);
 
 static void llvm_create_types(void);
 static uint64_t llvm_resolve_symbol(const char *name, void *ctx);
@@ -435,14 +435,52 @@ llvm_function_reference(LLVMJitContext *context,
 static LLVMPassManagerBuilderRef llvm_pmb;
 
 /*
- * Optimize code in module using the flags set in context.
+ * Optimize code at the function-level for func using the flags set in context.
+ */
+void
+llvm_optimize_function(LLVMJitContext *context, LLVMValueRef func)
+{
+	LLVMPassManagerRef llvm_fpm;
+	int compile_optlevel = context->base.flags & PGJIT_OPT3;
+	instr_time starttime;
+	instr_time endtime;
+
+	INSTR_TIME_SET_CURRENT(starttime);
+
+	if (llvm_pmb == NULL)
+	{
+		llvm_pmb = LLVMPassManagerBuilderCreate();
+		LLVMPassManagerBuilderUseLibraryInfo(llvm_pmb, llvm_targetlibraryinfo);
+	}
+
+	LLVMPassManagerBuilderSetOptLevel(llvm_pmb, compile_optlevel);
+	llvm_fpm = LLVMCreateFunctionPassManagerForModule(context->module);
+	LLVMAddAnalysisPasses(llvm_opt3_targetmachine, llvm_fpm);
+	if (compile_optlevel == 0)
+	{
+		/* we rely on mem2reg heavily, so emit even in the O0 case */
+		LLVMAddPromoteMemoryToRegisterPass(llvm_fpm);
+	}
+	LLVMPassManagerBuilderPopulateFunctionPassManager(llvm_pmb, llvm_fpm);
+
+	LLVMRunFunctionPassManager(llvm_fpm, func);
+
+	LLVMFinalizeFunctionPassManager(llvm_fpm);
+	LLVMDisposePassManager(llvm_fpm);
+
+	INSTR_TIME_SET_CURRENT(endtime);
+	INSTR_TIME_ACCUM_DIFF(context->base.instr.optimization_counter,
+						  endtime, starttime);
+}
+
+/*
+ * Optimize code at the module-level for the current module using the flags set
+ * in context.
  */
 static void
-llvm_optimize_module(LLVMJitContext *context, LLVMModuleRef module)
+llvm_optimize_module(LLVMJitContext *context)
 {
 	LLVMPassManagerRef llvm_mpm;
-	LLVMPassManagerRef llvm_fpm;
-	LLVMValueRef func;
 	int			compile_optlevel;
 
 	if (context->base.flags & PGJIT_OPT3)
@@ -450,11 +488,11 @@ llvm_optimize_module(LLVMJitContext *context, LLVMModuleRef module)
 	else
 		compile_optlevel = 0;
 
-	if (llvm_pmb == NULL)
-	{
-		llvm_pmb = LLVMPassManagerBuilderCreate();
-		LLVMPassManagerBuilderUseLibraryInfo(llvm_pmb, llvm_targetlibraryinfo);
-	}
+	/*
+	 * llvm_pmb must have been initialized by now while
+	 * optimizing generated functions.
+	 */
+	Assert(llvm_pmb != NULL);
 
 	/*
 	 * Have to create a inliner every pass, as it gets "consumed" by
@@ -462,33 +500,11 @@ llvm_optimize_module(LLVMJitContext *context, LLVMModuleRef module)
 	 */
 	LLVMPassManagerBuilderSetOptLevel(llvm_pmb, compile_optlevel);
 
-	llvm_fpm = LLVMCreateFunctionPassManagerForModule(module);
-	LLVMAddAnalysisPasses(llvm_opt3_targetmachine, llvm_fpm);
-
 	if (context->base.flags & PGJIT_OPT3)
 	{
 		/* TODO: Unscientifically determined threshold */
 		LLVMPassManagerBuilderUseInlinerWithThreshold(llvm_pmb, 1024);
 	}
-	else
-	{
-		/* we rely on mem2reg heavily, so emit even in the O0 case */
-		LLVMAddPromoteMemoryToRegisterPass(llvm_fpm);
-	}
-
-	LLVMPassManagerBuilderPopulateFunctionPassManager(llvm_pmb, llvm_fpm);
-
-	/*
-	 * Do function level optimization. This could be moved to the point where
-	 * functions are emitted, to reduce memory usage a bit.
-	 */
-	LLVMInitializeFunctionPassManager(llvm_fpm);
-	for (func = LLVMGetFirstFunction(context->module);
-		 func != NULL;
-		 func = LLVMGetNextFunction(func))
-		LLVMRunFunctionPassManager(llvm_fpm, func);
-	LLVMFinalizeFunctionPassManager(llvm_fpm);
-	LLVMDisposePassManager(llvm_fpm);
 
 	/*
 	 * Perform module level optimization. We do so even in the non-optimized
@@ -579,7 +595,7 @@ llvm_compile_module(LLVMJitContext *context)
 
 	/* optimize according to the chosen optimization settings */
 	INSTR_TIME_SET_CURRENT(starttime);
-	llvm_optimize_module(context, context->module);
+	llvm_optimize_module(context);
 	INSTR_TIME_SET_CURRENT(endtime);
 	INSTR_TIME_ACCUM_DIFF(context->base.instr.optimization_counter,
 						  endtime, starttime);
diff --git a/src/backend/jit/llvm/llvmjit_deform.c b/src/backend/jit/llvm/llvmjit_deform.c
index 533a4aa117..6ff0821072 100644
--- a/src/backend/jit/llvm/llvmjit_deform.c
+++ b/src/backend/jit/llvm/llvmjit_deform.c
@@ -759,5 +759,8 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc,
 
 	LLVMDisposeBuilder(b);
 
+	/* optimize according to the chosen optimization settings */
+	llvm_optimize_function(context, v_deform_fn);
+
 	return v_deform_fn;
 }
diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c
index d527dd02b0..bbe0fd0cb3 100644
--- a/src/backend/jit/llvm/llvmjit_expr.c
+++ b/src/backend/jit/llvm/llvmjit_expr.c
@@ -2576,6 +2576,9 @@ llvm_compile_expr(ExprState *state, ExprStateBuilder *esb)
 	LLVMDisposeDIBuilder(ecs.b_d);
 	LLVMDisposeBuilder(b);
 
+	/* optimize according to the chosen optimization settings */
+	llvm_optimize_function(ecs.context, ecs.fn);
+
 	/*
 	 * Don't immediately emit function, instead do so the first time the
 	 * expression is actually evaluated. That allows to emit a lot of
diff --git a/src/include/jit/llvmjit.h b/src/include/jit/llvmjit.h
index b7b6ed9b34..751bb69e25 100644
--- a/src/include/jit/llvmjit.h
+++ b/src/include/jit/llvmjit.h
@@ -115,6 +115,8 @@ extern LLVMValueRef llvm_function_reference(LLVMJitContext *context,
 						LLVMBuilderRef builder,
 						LLVMModuleRef mod,
 						FunctionCallInfo fcinfo);
+extern void llvm_optimize_function(LLVMJitContext *context,
+						LLVMValueRef func);
 
 extern void llvm_inline(LLVMModuleRef mod);
 
-- 
2.23.0

