Postgresql源碼(129)JIT函數中如何使用PG的類型llvmjit_types

0 總結

llvmjit_types文件分三部分

  1. 類型定義:llvm通過變量找到對應結構體的定義,在通過結構體內的偏移量宏使用成員變量。
  2. 模版函數定義:
    • 第一:AttributeTemplate被當做一個函數屬性的模板(例如nofree、nosync等clang前端為函數增加的屬性),AttributeTemplate是一個簡單函數被clang賦予了一套屬性,這些屬性在后續處理時傾向被內聯。所以在生成其他函數時,也想用這一套屬性,讓其他的函數(例如表達式計算函數)也能被內聯處理。
    • 第二:作為一些入參是PG_FUNCTION_ARGS的PG函數做函數類型模版。
      • v_fn = LLVMAddFunction(mod, funcname, LLVMGetFunctionType(AttributeTemplate));
  3. 函數引用:這些函數是所有llvmjit會用到的函數,這里用數組引用后,會在llvmjit_types.bc文件中生成引用信息,在使用llvm調用函數時,可以從這里找到函數類型,用LLVMAddFunction增加函數到mod中。
PGFunction	TypePGFunction;
size_t		TypeSizeT;
bool		TypeStorageBool;
...
...
==========================extern Datum AttributeTemplate(PG_FUNCTION_ARGS);
Datum
AttributeTemplate(PG_FUNCTION_ARGS)
{AssertVariableIsOfType(&AttributeTemplate, PGFunction);PG_RETURN_NULL();
}
...
...
==========================void	   *referenced_functions[] =
{ExecAggInitGroup,ExecAggCopyTransValue,ExecEvalPreOrderedDistinctSingle,ExecEvalPreOrderedDistinctMulti,ExecEvalAggOrderedTransDatum,ExecEvalAggOrderedTransTuple,ExecEvalArrayCoerce,...
}

1 類型同步到llvm

  • 總結:類型同步。
  • 解釋:在jit函數生成過程中,需要引用pg代碼中定義好的結構,正常的做法是在llvmjit_types中重新創建出來告訴llvm類型定義信息,但這樣做工作量很大且兩份相同的代碼也容易出錯。目前的做法是維護一個小文件llvmjit_types.c,引用了jit所需的每一種類型:

llvmjit_types.c:

 */
PGFunction	TypePGFunction;
size_t		TypeSizeT;
bool		TypeStorageBool;ExecEvalSubroutine TypeExecEvalSubroutine;
ExecEvalBoolSubroutine TypeExecEvalBoolSubroutine;NullableDatum StructNullableDatum;
AggState	StructAggState;
AggStatePerGroupData StructAggStatePerGroupData;
AggStatePerTransData StructAggStatePerTransData;
ExprContext StructExprContext;
ExprEvalStep StructExprEvalStep;
ExprState	StructExprState;
FunctionCallInfoBaseData StructFunctionCallInfoData;
HeapTupleData StructHeapTupleData;
HeapTupleHeaderData StructHeapTupleHeaderData;
MemoryContextData StructMemoryContextData;
TupleTableSlot StructTupleTableSlot;
HeapTupleTableSlot StructHeapTupleTableSlot;
MinimalTupleTableSlot StructMinimalTupleTableSlot;
TupleDescData StructTupleDescData;
PlanState	StructPlanState;
MinimalTupleData StructMinimalTupleData;

llvmjit_types.c里面定義了一些類型的變量,這些變量的bitcode在初始化時(llvm_create_types),會加載到module中(llvm_types_module)。然后再通過llvm_pg_var_type函數,把類型讀取出來保存到全局變量中:

static void
llvm_create_types(void)
{...snprintf(path, MAXPGPATH, "%s/%s", pkglib_path, "llvmjit_types.bc");if (LLVMCreateMemoryBufferWithContentsOfFile(path, &buf, &msg))...if (LLVMParseBitcodeInContext2(llvm_context, buf, &llvm_types_module))...LLVMDisposeMemoryBuffer(buf);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");StructHeapTupleHeaderData = llvm_pg_var_type("StructHeapTupleHeaderData");StructTupleDescData = llvm_pg_var_type("StructTupleDescData");StructAggState = llvm_pg_var_type("StructAggState");StructAggStatePerGroupData = llvm_pg_var_type("StructAggStatePerGroupData");StructAggStatePerTransData = llvm_pg_var_type("StructAggStatePerTransData");StructPlanState = llvm_pg_var_type("StructPlanState");StructMinimalTupleData = llvm_pg_var_type("StructMinimalTupleData");AttributeTemplate = LLVMGetNamedFunction(llvm_types_module, "AttributeTemplate");ExecEvalSubroutineTemplate = LLVMGetNamedFunction(llvm_types_module, "ExecEvalSubroutineTemplate");ExecEvalBoolSubroutineTemplate = LLVMGetNamedFunction(llvm_types_module, "ExecEvalBoolSubroutineTemplate");
}

這樣做可以很方便的同步類型定義,但這樣無法同步結構體內變量的偏移量,只能把偏移量維護在結構體中了,所以我們會看到結構體中多了一些宏來表示成員變量的位置:

typedef struct TupleTableSlot
{NodeTag		type;
#define FIELDNO_TUPLETABLESLOT_FLAGS 1uint16		tts_flags;		/* Boolean states */
#define FIELDNO_TUPLETABLESLOT_NVALID 2AttrNumber	tts_nvalid;		/* # of valid values in tts_values */const TupleTableSlotOps *const tts_ops; /* implementation of slot */
#define FIELDNO_TUPLETABLESLOT_TUPLEDESCRIPTOR 4TupleDesc	tts_tupleDescriptor;	/* slot's tuple descriptor */
#define FIELDNO_TUPLETABLESLOT_VALUES 5Datum	   *tts_values;		/* current per-attribute values */
#define FIELDNO_TUPLETABLESLOT_ISNULL 6bool	   *tts_isnull;		/* current per-attribute isnull flags */MemoryContext tts_mcxt;		/* slot itself is in this context */ItemPointerData tts_tid;	/* stored tuple's tid */Oid			tts_tableOid;	/* table oid of tuple */
} TupleTableSlot;

1.1 類型同步使用實例

非JIT表達式計算EEOP_SCAN_FETCHSOME流程:

  1. 從econtext中拿到tts賦給scanslot。
  2. 走EEOP_SCAN_FETCHSOME分支計算econtext。
ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)TupleTableSlot *scanslot;...scanslot = econtext->ecxt_scantuple;...EEO_SWITCH(){...EEO_CASE(EEOP_SCAN_FETCHSOME){CheckOpSlotCompatibility(op, scanslot);slot_getsomeattrs(scanslot, op->d.fetch.last_var);EEO_NEXT();}...}

JIT表達式計算EEOP_SCAN_FETCHSOME流程:

	eval_fn = LLVMAddFunction(mod, funcname,llvm_pg_var_func_type("ExecInterpExprStillValid"));v_econtext = LLVMGetParam(eval_fn, 1);LLVMValueRef v_scanslot;
  • 下面執行的操作等價與scanslot = econtext->ecxt_scantuple;從結構體中拿一個成員變量的值。
    IR中的結構體是不會記錄成員名稱的,所以需要告知llvm成員變量在結構體中的偏移位置FIELDNO_EXPRCONTEXT_SCANTUPLE = 1。
  • LLVMBuildLoad從內存中加載值。
  • LLVMStructGetTypeAtIndex拿到結構體指定位置的類型。
  • LLVMBuildStructGEP拿到結構體1位置的成員地址(GEP=GetElementPtr)
	/** l_load_struct_gep = * * LLVMBuildLoad(b,*               LLVMStructGetTypeAtIndex(StructExprContext, 1),*               LLVMBuildStructGEP(b, StructExprContext, v_econtext, 1, "")*               "v_scanslot")*/v_scanslot = l_load_struct_gep(b,StructExprContext,v_econtext,FIELDNO_EXPRCONTEXT_SCANTUPLE,"v_scanslot");...case EEOP_SCAN_FETCHSOME:{TupleDesc	desc = NULL;LLVMValueRef v_slot;LLVMBasicBlockRef b_fetch;LLVMValueRef v_nvalid;LLVMValueRef l_jit_deform = NULL;const TupleTableSlotOps *tts_ops = NULL;
  • 前面已經為每一個case都創建了一個BasicBlock。
  • l_bb_before_v在當前switch的BasicBlock前增加了一個新的Block。
  • 新的Block的語義:
    • if (v_nvalid >= op->d.fetch.last_var) // 跳轉到下一個case的Block:opblocks[opno + 1]
    • else // 繼續執行 當前Block 中的代碼
		b_fetch = l_bb_before_v(opblocks[opno + 1],"op.%d.fetch", opno);v_slot = v_scanslot;v_nvalid =l_load_struct_gep(b,StructTupleTableSlot,v_slot,FIELDNO_TUPLETABLESLOT_NVALID,"");LLVMBuildCondBr(b,LLVMBuildICmp(b, LLVMIntUGE, v_nvalid,l_int16_const(lc, op->d.fetch.last_var),""),opblocks[opno + 1], b_fetch);
  • 將builder的插入點調整到b_fetch塊的末尾,繼續在b_fetch中增加代碼:
		LLVMPositionBuilderAtEnd(b, b_fetch);{LLVMValueRef params[2];params[0] = v_slot;params[1] = l_int32_const(lc, op->d.fetch.last_var);
  • 創建一個調用指令,等價與slot_getsomeattrs(scanslot, op->d.fetch.last_var);
/** API調用:* LLVMBuildCall2(*   b, *   LLVMGetFunctionType(LLVMGetNamedFunction(llvm_types_module, "slot_getsomeattrs_int")), *   LLVMAddFunction(mod, "slot_getsomeattrs_int", LLVMGetFunctionType(LLVMGetNamedFunction(llvm_types_module, "slot_getsomeattrs_int"))), *   params, *   2,*   "");*/l_call(b,llvm_pg_var_func_type("slot_getsomeattrs_int"),llvm_pg_func(mod, "slot_getsomeattrs_int"),params, lengthof(params), "");}
  • 繼續到下一個Block執行。
		LLVMBuildBr(b, opblocks[opno + 1]);break;}

2 AttributeTemplate函數的作用

  • 第一:AttributeTemplate被當做一個函數屬性的模板(例如nofree、nosync等clang前端為函數增加的屬性),AttributeTemplate是一個簡單函數被clang賦予了一套屬性,這些屬性在后續處理時傾向被內聯。所以在生成其他函數時,也想用這一套屬性,讓其他的函數(例如表達式計算函數)也能被內聯處理。
  • 第二:作為一些入參是PG_FUNCTION_ARGS的PG函數做函數類型模版。
    • v_fn = LLVMAddFunction(mod, funcname, LLVMGetFunctionType(AttributeTemplate));

下面看下AttributeTemplate有哪些屬性:(llvmjit_types.ll)

; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) uwtable
define dso_local i64 @AttributeTemplate(ptr nocapture noundef writeonly %0) local_unnamed_addr #0 {%2 = getelementptr inbounds %struct.FunctionCallInfoBaseData, ptr %0, i64 0, i32 4store i8 1, ptr %2, align 4ret i64 0
}

可以看到函數的屬性:

  • mustprogress: 函數必須在有限的步驟內取得進展,不能無限循環。
  • nofree: 函數內不會進行內存釋放操作。
  • norecurse: 函數不會遞歸調用自己。
  • nosync: 函數內不會進行同步操作,如互斥鎖。
  • nounwind: 函數不會拋出異常。
  • willreturn: 函數保證最終會返回。
  • memory(argmem: write): 函數可能會寫入傳入的參數內存。
    • memory(argmem: write): May only write argument memory.
  • uwtable: 函數具有一個“Unwind Table”,在拋出異常時用于幫助恢復棧狀態。

函數參數的屬性:

  • nocapture: 函數不會保存指針的副本,不會使指針逃逸到函數外部。
  • noundef: 參數不會是一個未定義的值。
  • writeonly: 函數只會寫入指向的內存,不會讀取它。

在構造表達式計算函數時,使用llvm_copy_attributes將AttributeTemplate函數的屬性拷貝到了表達式計算函數上面:【AttributeTemplate屬性】 → 【evalexpr_3_0屬性】

llvm_compile_expr/* create function */eval_fn = LLVMAddFunction(mod, funcname,llvm_pg_var_func_type("ExecInterpExprStillValid"));......llvm_copy_attributes(AttributeTemplate, eval_fn);

拷貝后的evalexpr_3_0函數,可以看到函數屬性和參數屬性都已經和AttributeTemplate一致的:

; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) uwtable
define i64 @evalexpr_3_0(ptr nocapture noundef writeonly %0, ptr %1, ptr %2) #0 {
entry:

3 函數指針數組的作用

void	   *referenced_functions[] =
{ExecAggInitGroup,ExecAggCopyTransValue,...ExecEvalJsonCoercionFinish,ExecEvalJsonExprPath,MakeExpandedObjectReadOnlyInternal,slot_getmissingattrs,slot_getsomeattrs_int,strlen,varsize_any,ExecInterpExprStillValid,
};

這些函數是所有llvmjit會用到的函數,這里用數組引用后,會在llvmjit_types.bc文件中生成引用信息:

^45 = gv: (name: "ExecEvalSubPlan") ; guid = 11106370218607637427
^46 = gv: (name: "ExecEvalCurrentOfExpr") ; guid = 11138569114739303931
^47 = gv: (name: "slot_getsomeattrs_int") ; guid = 11630412520694092271
^48 = gv: (name: "MakeExpandedObjectReadOnlyInternal") ; guid = 11922486409292019551
^49 = gv: (name: "ExecEvalFieldStoreDeForm") ; guid = 11938814657973506909

在使用llvm調用函數時,可以從這里找到函數類型,用LLVMAddFunction增加函數聲明到mod中。

LLVMValueRef
llvm_pg_func(LLVMModuleRef mod, const char *funcname)...	v_srcfn = LLVMGetNamedFunction(llvm_types_module, funcname);...v_fn = LLVMAddFunction(mod,funcname,LLVMGetFunctionType(v_srcfn));...return v_fn;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/14562.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/14562.shtml
英文地址,請注明出處:http://en.pswp.cn/web/14562.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

SpringBoot項目中redis序列化和反序列化LocalDateTime失敗

實體類中包含了LocalDateTime 類型的屬性,把實體類數據存入Redis后變成這樣: 此時,存入redis不會報錯,但是從redis獲取的時候,會報錯: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Ca…

Springboot項目打包:將依賴的jar包輸出到指定目錄

場景 公司要對springboot項目依賴的jar包進行升級,但是遇到一個問題,項目打包之后,沒辦法看到他里面依賴的jar包,版本到底是不是升上去了,沒辦法看到。 下面是項目打的jar包 我們通過反編譯工具jdgui,來…

VUE3和VUE2

VUE3和VUE2 上一篇文章中,我們對VUE3進行了一個初步的認識了解,本篇文章我們來進一步學習一下,順便看一下VUE2的寫法VUE3是否能做到兼容😀。 一、新建組件 我們在components中新建一個組件,名稱為Peron,…

緩存降級

當Redis緩存出現問題或者無法正常工作時,需要有一種應對措施,避免直接訪問數據庫而導致整個系統癱瘓。緩存降級就是這樣一種機制。 主要的緩存降級策略包括: 本地緩存降級 當Redis緩存不可用時,可以先嘗試使用本地進程內緩存,如Guava Cache或Caffeine等。這樣可以減少對Redis…

陰影映射(線段樹)

實時陰影是電子游戲中最為重要的畫面效果之一。在計算機圖形學中,通常使用陰影映射方法來實現實時陰影。 游戲開發部正在開發一款 2D 游戲,同時希望能夠在 2D 游戲中模仿 3D 游戲的光影效果,請幫幫游戲開發部! 給定 x-y 平面上的…

再次學習History.scrollRestoration

再次學習History.scrollRestoration 之前在react.dev的源代碼中了解到了這個HIstory的屬性,當時寫了一篇筆記來記錄我對它的理解,現在看來還是一知半解。所以今天打算重新學習一下這個屬性,主要從屬性以及所屬對象的介紹、使用方法&#xff0…

每日一題(2)——100~200間的素數

方法一&#xff1a; public class suCount {public static void main(String[] args){int sum0;c1:for(int i100;i<200;i){for(int j2;j<i;j){if(i%j0)continue c1;//continue中斷循環&#xff0c;且返回外層循環&#xff0c;進入下一次遍歷else if(ji-1){System.out.pr…

Linux信號:信號的保存

目錄 一、信號在內核中的表示 二、sigset_t 2.1sigset_t的概念和意義 2.2信號集操作數 三、信號集操作數的使用 3.1sigprocmask 3.2sigpending 3.3sigemptyset 四、代碼演示 一、信號在內核中的表示 實際執行信號的處理動作稱為信號 遞達(Delivery) 。 信號從產生到遞達…

Mysql數據庫——DML操作

目錄 添加數據&#xff08;INSERT&#xff09; 修改數據&#xff08;UPDATE&#xff09; 刪除數據&#xff08;DELETE&#xff09; 添加數據&#xff1a; &#xff08;1). 給指定字段添加數據 &#xff08;2). 給全部字段添加數據 &#xff08;3). 批量添加數據 修改數據: 案例…

【STM32】HAL庫點燈

【STM32】HAL庫點燈 一、探究目標二、探究原理2.1 ST開發庫2.1.1 直接配置寄存器2.1.2 標準外設庫2.1.3 HAL庫2.2 HAL開發2.2.1 環境配置2.2.2 時鐘配置2.2.3 GPIO配置2.2.4 工程創建2.2.5 KEIL代碼![在這里插入圖片描述](https://img-blog.csdnimg.cn/direct/bf1c95d5c6724a6a…

NextGen Mirth Connect XStream反序列化遠程代碼執行漏洞(CVE-2023-43208)

0x01 產品簡介 NextGen Mirth Connect是是美國NextGen公司的一個醫療集成引擎,主要用于醫療領域的系統集成和數據交換,支持多種協議和標準。 0x02 漏洞概述 NextGen Mirth Connect 4.4.1之前版本存在遠程代碼執行漏洞,未經身份認證的攻擊者可利用該漏洞遠程執行代碼。 0…

混合組網VS傳統網絡:智能硬件混合組網優劣勢淺要解析

智能硬件混合組網是一種利用多種通信技術相結合的方法&#xff0c;以實現更靈活、更可靠的網絡連接。通過藍牙、Wi-Fi、LoRa、4G相互之間的不同通訊方式&#xff0c;根據應用場景的不同以及現場實際環境&#xff0c;優選最佳物聯網混合組網方案&#xff0c;以達到部署最便捷性價…

一張SSL證書如何同時保護多個域名及其子域名?

在互聯網時代&#xff0c;數據安全和隱私保護變得至關重要&#xff0c;而SSL證書作為確保網站安全的重要工具&#xff0c;其重要性不言而喻。本文將詳細探討一種特殊的SSL證書——多域名通配符SSL證書&#xff0c;它為網站管理員提供了一種高效、經濟的方式來保護多個域名及其子…

學Java以及IDEA工具中遇到的常用單詞

Arithmetic 算術 operator 運算符 relational 關系 logic 邏輯 assign 分配 TernaryOperator 三元運算符、 gender 性別 lebal 標簽 array 數組 two dimesional 二維 object 對象 method 方法 row 行 column 列 parameter 參數 recursion 遞歸 overload 方法重載 calculate 計算…

MyBatis從入門到“入土“

&#x1f495;喜歡的朋友可以關注一下&#xff0c;下次更新不迷路&#xff01;&#x1f495;(●?●) 目錄 一、Mybatis為何物&#xff1f;&#x1f44c; 二、快速入門&#x1f923; 1、新建項目&#x1f60a; 2、數據庫建表&#x1f60a; 3、導入依賴的jar包&#x1f60a;…

Linux學習筆記6

TFTP 服務器搭建和測試 關于TFTP:TFTP&#xff08;Trivial File Transfer Protocol&#xff0c;簡單文件傳輸協議&#xff09;&#xff0c;是一個基于UDP 協議實現 的用于在客戶機和服務器之間進行簡單文件傳輸的協議&#xff0c;適合于開銷不大、不復雜的應用場合 搭建服務器…

后量子密碼的發展和應用

后量子算法&#xff0c;特別是后量子密碼(PQC)&#xff0c;是近年來密碼學領域的一個熱門話題。隨著量子計算技術的快速發展&#xff0c;傳統的公鑰密碼算法面臨著被量子計算機破解的威脅。為了應對這一挑戰&#xff0c;后量子密碼應運而生&#xff0c;成為了一種能夠抵抗量子計…

【論文筆記】| 蛋白質大模型ProLLaMA

【論文筆記】| 蛋白質大模型ProLLaMA ProLLaMA: A Protein Large Language Model for Multi-Task Protein Language Processing Peking University Theme: Domain Specific LLM Main work&#xff1a; 當前 ProLLM 的固有局限性&#xff1a;&#xff08;i&#xff09;缺乏自然…

Redis篇 在linux系統上安裝Redis

安裝Redis 在Ubuntu上安裝Redis 在Ubuntu上安裝Redis 在linux系統中,我們安裝Redis,必須先使它有root權限. 那么在linux中,如何切換到root用戶權限呢? sudo su 就可切換到用戶權限了. 在切換到用戶權限后,我們需要用一條命令來搜索Redis相關的軟件包 apt search redis 會出現非…

ROS2學習——節點話題通信(2)

目錄 一、ROS2節點 1.概念 2.實例 &#xff08;1&#xff09;ros2 run &#xff08;2&#xff09;ros2 node list &#xff08;3&#xff09;remapping重映射 &#xff08;4&#xff09;ros2 node info 二、話題 &#xff08;1&#xff09; ros2 topic list &#xf…