Le jeudi 22 janvier 2026 à 8:54 PM, Matheus Alcantara <[email protected]> a écrit :
> Hi, > > On 07/01/26 12:08, Pierre Ducroquet wrote: > > > Hi > > > > While reading the code generated by llvmjit, I realized the number of LLVM > > basic blocks used in tuple deforming was directly visible in the generated > > assembly code with the following code: > > 0x723382b781c1: jmp 0x723382b781c3 > > 0x723382b781c3: jmp 0x723382b781eb > > 0x723382b781c5: mov -0x20(%rsp),%rax > > 0x723382b781..: ... ..... > > 0x723382b781e7: mov %cx,(%rax) > > 0x723382b781ea: ret > > 0x723382b781eb: jmp 0x723382b781ed > > 0x723382b781ed: jmp 0x723382b781ef > > 0x723382b781ef: jmp 0x723382b781f1 > > 0x723382b781f1: jmp 0x723382b781f3 > > 0x723382b781f3: mov -0x30(%rsp),%rax > > 0x723382b781..: ... ...... > > 0x723382b78208: mov %rcx,(%rax) > > 0x723382b7820b: jmp 0x723382b781c5 > > That's a lot of useless jumps, and LLVM has a specific pass to get rid of > > these. The attached patch modifies the llvmjit code to always call this > > pass, even below jit_optimize_above_cost. > > > > On a basic benchmark (a simple select * from table where f = 42), this > > optimization saved 7ms of runtime while using only 0.1 ms of extra > > optimization time. > > > The patch needs a rebase due to e5d99b4d9ef. > > You've added the "simplifycfg" only when the "jit_optimize_above_cost" > is not triggered which will use the default<O0> and mem2reg passes, the > > default<O3> pass already include "simplifycfg"? > > > With e5d99b4d9ef being committed, should we add "simplifycfg" when > PGJIT_INLINE bit is set since it also use the default<O0> and mem2reg > > passes? Hi Thank you, here is a rebased version of the patch. To answer your questions: - O3 already includes simplifycfg, so no need to modify O3 - any code generated by our llvmjit provider, esp. tuple deforming, is heavily dependent on simplifycfg, so when O0 is the basis we should always add this pass
From cb5cb74461ac9407c16903bfa9d2855f4e76918e Mon Sep 17 00:00:00 2001 From: Pierre Ducroquet <[email protected]> Date: Wed, 7 Jan 2026 15:43:19 +0100 Subject: [PATCH] llvmjit: always use the simplifycfg pass The simplifycfg pass will remove empty or unreachable LLVM basic blocks, and merge blocks together when possible. This is important because the tuple deforming code will generate a lot of basic blocks, and previously with O0 we did not run this pass, thus creating this kind of (amd64) machine code: 0x723382b781c1: jmp 0x723382b781c3 0x723382b781c3: jmp 0x723382b781eb 0x723382b781c5: mov -0x20(%rsp),%rax 0x723382b781..: ... ..... 0x723382b781e7: mov %cx,(%rax) 0x723382b781ea: ret 0x723382b781eb: jmp 0x723382b781ed 0x723382b781ed: jmp 0x723382b781ef 0x723382b781ef: jmp 0x723382b781f1 0x723382b781f1: jmp 0x723382b781f3 0x723382b781f3: mov -0x30(%rsp),%rax 0x723382b781..: ... ...... 0x723382b78208: mov %rcx,(%rax) 0x723382b7820b: jmp 0x723382b781c5 This is not efficient at all, and triggering the simplifycfg pass ends up tacking a few hundreds micro seconds while possibly saving much more time during execution. On a basic benchmark, I saved 7ms on query runtime while using 0.2ms on extra JIT compilation overhead --- src/backend/jit/llvm/llvmjit.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/backend/jit/llvm/llvmjit.c b/src/backend/jit/llvm/llvmjit.c index 2e8aa4749db..c22f83e97cf 100644 --- a/src/backend/jit/llvm/llvmjit.c +++ b/src/backend/jit/llvm/llvmjit.c @@ -633,6 +633,11 @@ llvm_optimize_module(LLVMJitContext *context, LLVMModuleRef module) { /* we rely on mem2reg heavily, so emit even in the O0 case */ LLVMAddPromoteMemoryToRegisterPass(llvm_fpm); + /* + * the tuple deforming generates a lot of basic blocks, + * simplify them even with O0 + */ + LLVMAddCFGSimplificationPass(llvm_fpm); } LLVMPassManagerBuilderPopulateFunctionPassManager(llvm_pmb, llvm_fpm); @@ -676,10 +681,10 @@ llvm_optimize_module(LLVMJitContext *context, LLVMModuleRef module) passes = "default<O3>"; else if (context->base.flags & PGJIT_INLINE) /* if doing inlining, but no expensive optimization, add inline pass */ - passes = "default<O0>,mem2reg,inline"; + passes = "default<O0>,mem2reg,simplifycfg,inline"; else /* default<O0> includes always-inline pass */ - passes = "default<O0>,mem2reg"; + passes = "default<O0>,mem2reg,simplifycfg"; options = LLVMCreatePassBuilderOptions(); -- 2.43.0
