Postgresql源码(115)LLVM JIT运行逻辑分析(上)

news/2024/7/9 23:28:56 标签: postgresql, 数据库, llvm, jit

1 JIT入口开关

  1. 总入口:jit_enabled打开 且 生成计划成本超过jit_above_cost启动JIT
    • 计划成本超过jit_optimize_above_cost,执行PGJIT_OPT3使用O3对IR进行优化。
    • 计划成本超过jit_inline_above_cost,执行PGJIT_INLINE
    • jit_expressions开关如果打开,执行PGJIT_EXPR表达式优化。
    • jit_tuple_deforming开关如果打开,执行PGJIT_DEFORM优化拆解元组流程。
standard_planner
	...
	...
	result->jitFlags = PGJIT_NONE;
	
	if (jit_enabled && jit_above_cost >= 0 &&
		top_plan->total_cost > jit_above_cost)
	{
		result->jitFlags |= PGJIT_PERFORM;

		/*
		 * Decide how much effort should be put into generating better code.
		 */
		if (jit_optimize_above_cost >= 0 &&
			top_plan->total_cost > jit_optimize_above_cost)
			result->jitFlags |= PGJIT_OPT3;
		if (jit_inline_above_cost >= 0 &&
			top_plan->total_cost > jit_inline_above_cost)
			result->jitFlags |= PGJIT_INLINE;

		/*
		 * Decide which operations should be JITed.
		 */
		if (jit_expressions)
			result->jitFlags |= PGJIT_EXPR;
		if (jit_tuple_deforming)
			result->jitFlags |= PGJIT_DEFORM;
	}

jit_compile_expr_39">2 从表达式堆栈进入JIT逻辑jit_compile_expr

《Postgresql源码(113)表达式JIT计算简单分析》

#0  jit_compile_expr (state=0x1deae18) at jit.c:180
#1  0x000000000071fa6b in ExecReadyExpr (state=0x1deae18) at execExpr.c:874
#2  0x000000000071e60b in ExecInitExpr (node=0x1dfabb8, parent=0x0) at execExpr.c:152
#3  0x00000000008b3395 in evaluate_expr (expr=0x1dfabb8, result_type=23, result_typmod=-1, result_collation=0) at clauses.c:4892
#4  0x00000000008b26f8 in evaluate_function (funcid=1397, result_type=23, result_typmod=-1, result_collid=0, input_collid=0, args=0x1dfab68, funcvariadic=false, func_tuple=0x7fd9588871a8, context=0x7ffdd8867f20) at clauses.c:4409

...

jit_compile_exprllvmjitso_50">3 jit_compile_expr初始化加载llvmjit.so

jit_compile_expr
	provider_init
		load_external_function(path, "_PG_jit_provider_init", true, NULL)

dlopen动态加载llvmjit.so,并调用so中的_PG_jit_provider_init初始化:

void
_PG_jit_provider_init(JitProviderCallbacks *cb)
{
	cb->reset_after_error = llvm_reset_after_error;
	cb->release_context = llvm_release_context;
	cb->compile_expr = llvm_compile_expr;
}

为provider配置入口函数:

typedef struct JitProviderCallbacks JitProviderCallbacks;

struct JitProviderCallbacks
{
	JitProviderResetAfterErrorCB reset_after_error;
	JitProviderReleaseContextCB release_context;
	JitProviderCompileExprCB compile_expr;
};

static JitProviderCallbacks provider;

jit_compile_expr继续调用hook:provider.compile_expr进入llvm逻辑:

jit_compile_expr
	provider_init
	provider.compile_expr(state)  -> llvm_compile_expr

llvm_compile_exprllvm_create_context_86">4 llvm_compile_expr执行初始化llvm_create_context

llvm_create_context初始化生成LLVMJitContext结构:

typedef struct JitContext
{
	/* see PGJIT_* above */
	int			flags;

	ResourceOwner resowner;

	JitInstrumentation instr;
} JitContext;

typedef struct LLVMJitContext
{
	JitContext	base;               // 上面的JIT FLAG、ResourceOwner
	size_t		module_generation;  // 当前context存了几个Module?
	LLVMModuleRef module;           // 当前正在使用的module
	bool		compiled;           // 已经编译过了?
	List	   *handles;            // 所有挂在当前context下的module
} LLVMJitContext;

llvm_create_context初始化流程

llvm_create_context
	llvm_session_initialize
		【库函数】LLVMInitializeNativeTarget
		【库函数】LLVMInitializeNativeAsmPrinter
		【库函数】LLVMInitializeNativeAsmParser
		【库函数】LLVMContextSetOpaquePointers
		
		读取llvmjit_types.bc中需要的类型、函数签名:llvm_create_types
			LLVMCreateMemoryBufferWithContentsOfFile
			LLVMParseBitcode2
			LLVMDisposeMemoryBuffer
			
		【库函数】LLVMGetTargetFromTriple
		...
		【库函数】LLVMLoadLibraryPermanently
		
		llvm_ts_context = LLVMOrcCreateNewThreadSafeContext
		llvm_opt0_orc = llvm_create_jit_instance
			【库函数】若干
			传入机器信息,构造LLVMJIT环境
			【库函数】若干
			LLVMOrcJITTargetMachineBuilderCreateFromTargetMachine
			【库函数】若干
			LLVMOrcCreateLLJIT
			
		llvm_opt3_orc = llvm_create_jit_instance
			【库函数】若干
			传入机器信息,构造LLVMJIT环境
			【库函数】若干
			LLVMOrcJITTargetMachineBuilderCreateFromTargetMachine
			【库函数】若干
			LLVMOrcCreateLLJIT
			
	ResourceOwnerEnlargeJIT

llvmjit_types.bc读取的类型、函数

	/*
	 * Load triple & layout from clang emitted file so we're guaranteed to be
	 * compatible.
	 */
	llvm_triple = pstrdup(LLVMGetTarget(llvm_types_module));
	llvm_layout = pstrdup(LLVMGetDataLayoutStr(llvm_types_module));

	TypeSizeT = llvm_pg_var_type("TypeSizeT");
	TypeParamBool = load_return_type(llvm_types_module, "FunctionReturningBool");
	TypeStorageBool = llvm_pg_var_type("TypeStorageBool");
	TypePGFunction = llvm_pg_var_type("TypePGFunction");
	StructNullableDatum = llvm_pg_var_type("StructNullableDatum");
	StructExprContext = llvm_pg_var_type("StructExprContext");
	StructExprEvalStep = llvm_pg_var_type("StructExprEvalStep");
	StructExprState = llvm_pg_var_type("StructExprState");
	StructFunctionCallInfoData = llvm_pg_var_type("StructFunctionCallInfoData");
	StructMemoryContextData = llvm_pg_var_type("StructMemoryContextData");
	StructTupleTableSlot = llvm_pg_var_type("StructTupleTableSlot");
	StructHeapTupleTableSlot = llvm_pg_var_type("StructHeapTupleTableSlot");
	StructMinimalTupleTableSlot = llvm_pg_var_type("StructMinimalTupleTableSlot");
	StructHeapTupleData = llvm_pg_var_type("StructHeapTupleData");
	StructTupleDescData = llvm_pg_var_type("StructTupleDescData");
	StructAggState = llvm_pg_var_type("StructAggState");
	StructAggStatePerGroupData = llvm_pg_var_type("StructAggStatePerGroupData");
	StructAggStatePerTransData = llvm_pg_var_type("StructAggStatePerTransData");

	AttributeTemplate = LLVMGetNamedFunction(llvm_types_module, "AttributeTemplate");

在这里插入图片描述
读取到的所有类型、函数指针等记录在全局变量llvm_types_module中,用llvm_pg_var_type等函数调用LLVM库函数转换为LLVM能识别的类型、函数。

llvm_compile_exprmodule_180">5 llvm_compile_expr创建module

创建Module需要的llvm_triple、llvm_layout都来自llvm_create_types函数,读取llvmjit_types.bc拿到的信息。

LLVMModuleRef
llvm_mutable_module(LLVMJitContext *context)
{
	llvm_assert_in_fatal_section();

	/*
	 * If there's no in-progress module, create a new one.
	 */
	if (!context->module)
	{
		context->compiled = false;
		context->module_generation = llvm_generation++;
		context->module = LLVMModuleCreateWithName("pg");
		LLVMSetTarget(context->module, llvm_triple);
		LLVMSetDataLayout(context->module, llvm_layout);
	}

	return context->module;
}

在这里插入图片描述

llvm_compile_exprmodule_204">6 llvm_compile_expr新增函数到module中

llvm_compile_expr

新增函数到module

	eval_fn = LLVMAddFunction(mod, funcname,
							  llvm_pg_var_func_type("TypeExprStateEvalFunc"));

函数中加BB

	entry = LLVMAppendBasicBlock(eval_fn, "entry");

按表达式分支逻辑为BB添加代码

			case EEOP_FUNCEXPR_STRICT:
				{
					FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
					LLVMValueRef v_fcinfo_isnull;
					LLVMValueRef v_retval;

					if (opcode == EEOP_FUNCEXPR_STRICT)
					{
						LLVMBasicBlockRef b_nonull;
						LLVMBasicBlockRef *b_checkargnulls;
						LLVMValueRef v_fcinfo;

						/*
						 * Block for the actual function call, if args are
						 * non-NULL.
						 */
						b_nonull = l_bb_before_v(opblocks[opno + 1],
												 "b.%d.no-null-args", opno);

						/* should make sure they're optimized beforehand */
						if (op->d.func.nargs == 0)
							elog(ERROR, "argumentless strict functions are pointless");

						v_fcinfo =
							l_ptr_const(fcinfo, l_ptr(StructFunctionCallInfoData));

						/*
						 * set resnull to true, if the function is actually
						 * called, it'll be reset
						 */
						LLVMBuildStore(b, l_sbool_const(1), v_resnullp);

						/* create blocks for checking args, one for each */
						b_checkargnulls =
							palloc(sizeof(LLVMBasicBlockRef *) * op->d.func.nargs);
						for (int argno = 0; argno < op->d.func.nargs; argno++)
							b_checkargnulls[argno] =
								l_bb_before_v(b_nonull, "b.%d.isnull.%d", opno,
											  argno);

						/* jump to check of first argument */
						LLVMBuildBr(b, b_checkargnulls[0]);

						/* check each arg for NULLness */
						for (int argno = 0; argno < op->d.func.nargs; argno++)
						{
							LLVMValueRef v_argisnull;
							LLVMBasicBlockRef b_argnotnull;

							LLVMPositionBuilderAtEnd(b, b_checkargnulls[argno]);

							/*
							 * Compute block to jump to if argument is not
							 * null.
							 */
							if (argno + 1 == op->d.func.nargs)
								b_argnotnull = b_nonull;
							else
								b_argnotnull = b_checkargnulls[argno + 1];

							/* and finally load & check NULLness of arg */
							v_argisnull = l_funcnull(b, v_fcinfo, argno);
							LLVMBuildCondBr(b,
											LLVMBuildICmp(b, LLVMIntEQ,
														  v_argisnull,
														  l_sbool_const(1),
														  ""),
											opblocks[opno + 1],
											b_argnotnull);
						}

						LLVMPositionBuilderAtEnd(b, b_nonull);
					}

					v_retval = BuildV1Call(context, b, mod, fcinfo,
										   &v_fcinfo_isnull);
					LLVMBuildStore(b, v_retval, v_resvaluep);
					LLVMBuildStore(b, v_fcinfo_isnull, v_resnullp);

					LLVMBuildBr(b, opblocks[opno + 1]);
					break;
				}

7 (核心步骤)ExecRunCompiledExpr对module进行编译、优化、执行

ExecRunCompiledExpr找到jit函数并执行,惰性编译、优化。

ExecRunCompiledExpr
	llvm_get_function
		重要:llvm_compile_module
		LLVMOrcLLJITLookup

在找函数执行时,编译这一步是核心逻辑,编译会对上面逻辑进行优化处理:

llvm_compile_module
	llvm_inline
	llvm_optimize_module

优化一:llvm_inline

llvm_build_inline_plan会查询module里面的function,到函数目录查找对应的bc文件,并加载bc文件中函数的逻辑(增加LLVM编译后,所有源码文件都会用clang额外生成一个bc文件,提供给inline使用)。function_inlinable函数会检查当前函数引用的其他函数时候能inline。

优化二:llvm_optimize_module

将IR过一遍PASS,下一篇继续分析后面的流程。


http://www.niftyadmin.cn/n/5076524.html

相关文章

【gcc】RtpTransportControllerSend学习笔记 1

本文是woder大神 的文章的学习笔记。主要是大神文章: webrtc源码分析(8)-拥塞控制(上)-码率预估 的学习笔记。大神的webrtc源码分析(8)-拥塞控制(上)-码率预估 详尽而具体,堪称神作。因为直接看大神的文章,自己啥也没记住,所以同时跟着看代码。跟着大神走一遍,不求甚解,…

【MySQL】基本查询 (一)

文章目录 一. 基础查询二. where条件子句三. NULL的比较结束语 操作如下表 //创建表结构 mysql> create table exam_result(-> id int unsigned primary key auto_increment,-> name varchar(20) not null comment 同学姓名,-> chinese float default 0.0 comment…

JavaScript Web APIs第六天笔记

Web APIs - 第6天笔记 目标&#xff1a;能够利用正则表达式完成小兔鲜注册页面的表单验证&#xff0c;具备常见的表单验证能力 正则表达式综合案例阶段案例 正则表达式 正则表达式&#xff08;Regular Expression&#xff09;是一种字符串匹配的模式&#xff08;规则&#xf…

【单片机】18-红外线遥控

一、红外遥控背景知识 1.人机界面 &#xff08;1&#xff09;当面操作&#xff1a;按键&#xff0c;旋转/触摸按键&#xff0c;触摸屏 &#xff08;2&#xff09;遥控操作&#xff1a;红外遥控&#xff0c;433M/2.4G无线通信【穿墙能力强】&#xff0c;蓝牙-WIFI-Zigbee-LoRa等…

DAZ To UMA⭐五.模型在Blender中的配置教程

文章目录 🟥 创建符合UMA的材质球属性1️⃣ 合并材质球🎁 选择材质球🎁 合并材质球🎁 删除多余材质球2️⃣ 将身体按材质球拆分🎁 进入身体编辑模式🎁 全选身体🎁 按材质分割身体🎁 重命名不同部位3️⃣ 将其余部位进行拆分🟧 更正选择缩放🟩 更新骨骼结构…

leetcode42 接雨水

题目 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 示例 输入&#xff1a;height [0,1,0,2,1,0,1,3,2,1,2,1] 输出&#xff1a;6 解释&#xff1a;上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高…

Java线程的基本操作(设置和获取、sleep、interrupt、join、yield、daemon、线程状态总结)

&#x1f648;作者简介&#xff1a;练习时长两年半的Java up主 &#x1f649;个人主页&#xff1a;程序员老茶 &#x1f64a; ps:点赞&#x1f44d;是免费的&#xff0c;却可以让写博客的作者开兴好久好久&#x1f60e; &#x1f4da;系列专栏&#xff1a;Java全栈&#xff0c;…

【数据结构与算法】如何对快速排序进行细节优化以及实现非递归版本的快速排序?

君兮_的个人主页 即使走的再远&#xff0c;也勿忘启程时的初心 C/C 游戏开发 Hello,米娜桑们&#xff0c;这里是君兮_&#xff0c;国庆长假结束了&#xff0c;无论是工作还是学习都该回到正轨上来了&#xff0c;从今天开始恢复正常的更新频率&#xff0c;今天为大家带来的内容…