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

news/2025/4/1 7:43:20/

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;}

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...

3 jit_compile_expr初始化加载llvmjit.so

jit_compile_exprprovider_initload_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_exprprovider_initprovider.compile_expr(state)  -> llvm_compile_expr

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、ResourceOwnersize_t		module_generation;  // 当前context存了几个Module?LLVMModuleRef module;           // 当前正在使用的modulebool		compiled;           // 已经编译过了?List	   *handles;            // 所有挂在当前context下的module
} LLVMJitContext;

llvm_create_context初始化流程

llvm_create_contextllvm_session_initialize【库函数】LLVMInitializeNativeTarget【库函数】LLVMInitializeNativeAsmPrinter【库函数】LLVMInitializeNativeAsmParser【库函数】LLVMContextSetOpaquePointers读取llvmjit_types.bc中需要的类型、函数签名:llvm_create_typesLLVMCreateMemoryBufferWithContentsOfFileLLVMParseBitcode2LLVMDisposeMemoryBuffer【库函数】LLVMGetTargetFromTriple...【库函数】LLVMLoadLibraryPermanentlyllvm_ts_context = LLVMOrcCreateNewThreadSafeContextllvm_opt0_orc = llvm_create_jit_instance【库函数】若干传入机器信息,构造LLVMJIT环境【库函数】若干LLVMOrcJITTargetMachineBuilderCreateFromTargetMachine【库函数】若干LLVMOrcCreateLLJITllvm_opt3_orc = llvm_create_jit_instance【库函数】若干传入机器信息,构造LLVMJIT环境【库函数】若干LLVMOrcJITTargetMachineBuilderCreateFromTargetMachine【库函数】若干LLVMOrcCreateLLJITResourceOwnerEnlargeJIT

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能识别的类型、函数。

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;
}

在这里插入图片描述

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;elseb_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函数并执行,惰性编译、优化。

ExecRunCompiledExprllvm_get_function重要:llvm_compile_moduleLLVMOrcLLJITLookup

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

llvm_compile_modulellvm_inlinellvm_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.ppmy.cn/news/1147701.html

相关文章

KMP算法next数组

next数组规则如下&#xff1a; 注意&#xff0c;下标j从1开始&#xff0c;到最后一个字符结束&#xff08;当然也可以是0&#xff0c;只要所有的值都减1即可&#xff09; next[j] 1 , j 1满足如下条件的k的最大值&#xff1a; n e x t [ j ] { m a x ( k ) ∣ 1 < k &l…

上个月Balada Injector攻击中有超过17,000个WordPress网站被黑

导语 最近&#xff0c;一场名为Balada Injector的攻击活动引起了广泛关注。这次攻击以WordPress网站为目标&#xff0c;据统计&#xff0c;有超过17,000个网站受到了感染。在本文中&#xff0c;我们将详细介绍这次攻击的概述、攻击手段以及如何保护自己的网站。 攻击概述 Balad…

Harmony装饰器

1、装饰器 装饰器是用于装饰类、结构、方法以及变量&#xff0c;并赋予其特殊的含义。如&#xff1a; Component表示自定义组件Entry表示该自定义组件为入口组件State表示组件中的状态变量&#xff0c;状态变量变化会触发UI刷新。 2 、语法范式 Builder/BuilderParam&#…

天软特色因子看板 (2023.10 第02期)

该因子看板跟踪天软特色因子A05005(近一日单笔流通金额占比(%)&#xff0c;该因子为近一个月单笔流通金额占比因子&#xff0c;用以刻/画股票在收盘时&#xff0c;主力资金在总交易金额中所占的比重。。 今日为该因子跟踪第02期&#xff0c;跟踪其在SH000905 (中证500) 中的表现…

graphviz 绘制单链表

dot 代码 digraph LinkedList {rankdirLR; // 设置布局方向为从左到右&#xff08;左侧到右侧&#xff09;node [fontname"Arial", shaperecord, stylefilled, color"#ffffff", fillcolor"#0077be", fontsize12, width1.5, height0.5];edge [fo…

【MAC】升级 Mac os 后报错

背景 17 年买的 mac&#xff0c;发现很多软件都无法安装&#xff0c;于是升级 mac os 到 10.13&#xff0c;从官网下载 10.13 版本&#xff0c;之后升级&#xff0c;升级还算顺利。但使用 git 的时候发现出现问题了。 问题 使用 git 出现如下错误 xcrun: error: invalid ac…

什么是Python虚拟环境?

视频教程地址&#xff1a;https://www.bilibili.com/video/BV1Zy4y1F7hC/ 大家好&#xff0c;这一集我们来介绍一下什么是Python虚假环境。虚拟环境是python基础知识中非常重要的一个知识点。 相信python新手都会遇到过这样的问题&#xff0c;在命令行中下载了某个三方库在py…

Simulink仿真之离散系统

最近&#xff0c;为了完成课程作业&#xff0c;需要用到Simulink验证数字控制器的合理性。题目如下所示。 其实这道题在胡寿松老师的《自动控制原理&#xff08;第七版&#xff09;》的364页有答案。 这里给出数字控制器的脉冲传递函数为 ​​​​​​​ ​​​​​​​…