Hi,
Ideally we should have all changes for LLVM 22 in our February minor
releases. I have written up some notes on release synchronisation on
the wiki[1] to show the scheduling problem if we don't. The second
patch here still needs some validation.
1. We won't need our local llvm::backport::SectionMemoryManager for
LLVM 22, so it will be nice to draw a line under that messy business.
See commit message for details.
You can review the differences between our in-tree copy and the code
that was finally committed and will shortly ship in LLVM 22 like this:
LLVM_BRANCH=main
LLVM_URL=https://raw.githubusercontent.com/llvm/llvm-project/refs/heads
curl -s \
$LLVM_URL/$LLVM_BRANCH/llvm/include/llvm/ExecutionEngine/SectionMemoryManager.h
| \
diff -u - src/include/jit/SectionMemoryManager.h
curl -s \
$LLVM_URL/$LLVM_BRANCH/llvm/lib/ExecutionEngine/SectionMemoryManager.cpp | \
diff -u - src/backend/jit/llvm/SectionMemoryManager.cpp
In a week or two, LLVM_BRANCH=release/22.x should work too. I've
attached the output, which shows the expected changes in our copy,
namely:
* top-of-file comments
* namespace change
* tweaks for older LLVM versions
* tree-wide spellchecks and #include "" -> <> changes
They haven't made any changes on their side, except for adding some
LLVM_ABI macros added in LLVM 20 that we missed. See commit message
for why we don't want those.
The place in llvmjit_backport.h that does:
-#if defined(__aarch64__)
+#if defined(__aarch64__) && LLVM_VERSION_MAJOR < 22
#define USE_LLVM_BACKPORT_SECTION_MEMORY_MANAGER
... would be like this in REL_17_STABLE and earlier:
+#if defined(__aarch64__) && LLVM_VERSION_MAJOR > 11 && LLVM_VERSION_MAJOR < 22
That's because we never made the backport work with LLVM < 12, and I
have heard no complaints about that so at this point it looks like we
got away with it.
2. LLVM 22 changed the semantics of the "lifetime.end" instruction.
See commit message for references. Without this change, LLVM main/22
assertions fail in the regression tests with messages like this in
postmaster.log:
Intrinsic has incorrect argument type!
ptr @llvm.lifetime.end.p0
Intrinsic has incorrect argument type!
ptr @llvm.lifetime.end.p0
2026-01-02 17:28:31.394 NZDT client backend[42798] pg_regress/boolean
FATAL: fatal llvm error: Broken module found, compilation aborted!
I haven't seen anything bad happen in non-assertion builds.
Here's a potential minimal fix. I haven't yet proven that the
optimisation is still working as expected. Probably need to compile
an expression that calls an inlined function and then a non-inlined
function with jit_dump_bitcode=true, then find the right XXX.bc file
under pgdata, llvm-dis XXX.bc, llc XXX.ll, then visually inspect XXX.s
with enough caffeine to confirm that it's not spilling something (ie
store instructions) where previously it didn't, but I wanted to post
what I had so far to see if anyone has a better idea or an easy way to
test it...
[1] https://wiki.postgresql.org/wiki/LLVM#Cadence
--- - 2026-01-02 17:03:43.795228000 +1300
+++ src/include/jit/SectionMemoryManager.h 2026-01-02 16:52:50.352095000
+1300
@@ -1,3 +1,8 @@
+/*
+ * This is a copy of LLVM source code modified by the PostgreSQL project.
+ * See SectionMemoryManager.cpp for notes on provenance and license.
+ */
+
//===- SectionMemoryManager.h - Memory manager for MCJIT/RtDyld -*- C++
-*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM
Exceptions.
@@ -11,18 +16,19 @@
//
//===----------------------------------------------------------------------===//
-#ifndef LLVM_EXECUTIONENGINE_SECTIONMEMORYMANAGER_H
-#define LLVM_EXECUTIONENGINE_SECTIONMEMORYMANAGER_H
+#ifndef LLVM_EXECUTIONENGINE_BACKPORT_SECTIONMEMORYMANAGER_H
+#define LLVM_EXECUTIONENGINE_BACKPORT_SECTIONMEMORYMANAGER_H
-#include "llvm/ADT/SmallVector.h"
-#include "llvm/ExecutionEngine/RTDyldMemoryManager.h"
-#include "llvm/Support/Compiler.h"
-#include "llvm/Support/Memory.h"
+#include <llvm/ADT/SmallVector.h>
+#include <llvm/ExecutionEngine/RTDyldMemoryManager.h>
+#include <llvm/Support/Alignment.h>
+#include <llvm/Support/Memory.h>
#include <cstdint>
#include <string>
#include <system_error>
namespace llvm {
+namespace backport {
/// This is a simple memory manager which implements the methods called by
/// the RuntimeDyld class to allocate memory for section-based loading of
@@ -37,7 +43,7 @@
/// in the JITed object. Permissions can be applied either by calling
/// MCJIT::finalizeObject or by calling SectionMemoryManager::finalizeMemory
/// directly. Clients of MCJIT should call MCJIT::finalizeObject.
-class LLVM_ABI SectionMemoryManager : public RTDyldMemoryManager {
+class SectionMemoryManager : public RTDyldMemoryManager {
public:
/// This enum describes the various reasons to allocate pages from
/// allocateMappedMemory.
@@ -49,7 +55,7 @@
/// Implementations of this interface are used by SectionMemoryManager to
/// request pages from the operating system.
- class LLVM_ABI MemoryMapper {
+ class MemoryMapper {
public:
/// This method attempts to allocate \p NumBytes bytes of virtual memory
for
/// \p Purpose. \p NearBlock may point to an existing allocation, in which
@@ -119,9 +125,17 @@
/// Implements allocating all memory in a single block. This is required to
/// limit memory offsets to fit the ARM ABI; large memory systems may
/// otherwise allocate separate sections too far apart.
+#if LLVM_VERSION_MAJOR < 16
+ virtual void reserveAllocationSpace(uintptr_t CodeSize, uint32_t CodeAlign,
+ uintptr_t RODataSize,
+ uint32_t RODataAlign,
+ uintptr_t RWDataSize,
+ uint32_t RWDataAlign) override;
+#else
void reserveAllocationSpace(uintptr_t CodeSize, Align CodeAlign,
uintptr_t RODataSize, Align RODataAlign,
uintptr_t RWDataSize, Align RWDataAlign)
override;
+#endif
/// Allocates a memory block of (at least) the given size suitable for
/// executable code.
@@ -139,7 +153,7 @@
/// a default alignment of 16 will be used.
uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment,
unsigned SectionID, StringRef SectionName,
- bool isReadOnly) override;
+ bool IsReadOnly) override;
/// Update section-specific memory permissions and other attributes.
///
@@ -168,7 +182,7 @@
// The actual block of free memory
sys::MemoryBlock Free;
// If there is a pending allocation from the same reservation right before
- // this block, store it's index in PendingMem, to be able to update the
+ // this block, store its index in PendingMem, to be able to update the
// pending region if part of this block is allocated, rather than having to
// create a new one
unsigned PendingPrefixIndex;
@@ -206,6 +220,7 @@
bool ReserveAllocation;
};
+} // end namespace backport
} // end namespace llvm
-#endif // LLVM_EXECUTIONENGINE_SECTIONMEMORYMANAGER_H
+#endif // LLVM_EXECUTIONENGINE_BACKPORT_SECTIONMEMORYMANAGER_H
--- - 2026-01-02 17:03:57.672285000 +1300
+++ src/backend/jit/llvm/SectionMemoryManager.cpp 2026-01-02
16:52:50.351938000 +1300
@@ -1,3 +1,19 @@
+/*
+ * This file is from LLVM 22 (originally pull request #71968), with minor
+ * modifications to avoid name clash and work with older LLVM versions. It
+ * replaces llvm::SectionMemoryManager, and is injected into llvm::RuntimeDyld
+ * to fix a memory layout bug on large memory ARM systems on LLVM < 22.
+ *
+ * We can remove this code (.cpp, .h, .LICENSE) once LLVM 22 is our minimum
+ * supported version or we've switched to JITLink for at least Aarch64.
+ *
+ * This file is a modified copy of a part of the LLVM source code that
+ * we would normally access from the LLVM library. It is therefore
+ * covered by the license at https://llvm.org/LICENSE.txt, reproduced
+ * verbatim in SectionMemoryManager.LICENSE in fulfillment of clause
+ * 4a.
+ */
+
//===- SectionMemoryManager.cpp - Memory manager for MCJIT/RtDyld *- C++
-*-==//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM
Exceptions.
@@ -11,11 +27,16 @@
//
//===----------------------------------------------------------------------===//
-#include "llvm/ExecutionEngine/SectionMemoryManager.h"
-#include "llvm/Config/config.h"
-#include "llvm/Support/Process.h"
+#include "jit/llvmjit_backport.h"
+#ifdef USE_LLVM_BACKPORT_SECTION_MEMORY_MANAGER
+
+#include "jit/SectionMemoryManager.h"
+#include <llvm/Support/MathExtras.h>
+#include <llvm/Support/Process.h>
+
namespace llvm {
+namespace backport {
bool SectionMemoryManager::hasSpace(const MemoryGroup &MemGroup,
uintptr_t Size) const {
@@ -26,9 +47,21 @@
return false;
}
+#if LLVM_VERSION_MAJOR < 16
+void SectionMemoryManager::reserveAllocationSpace(uintptr_t CodeSize,
+ uint32_t CodeAlign_i,
+ uintptr_t RODataSize,
+ uint32_t RODataAlign_i,
+ uintptr_t RWDataSize,
+ uint32_t RWDataAlign_i) {
+ Align CodeAlign(CodeAlign_i);
+ Align RODataAlign(RODataAlign_i);
+ Align RWDataAlign(RWDataAlign_i);
+#else
void SectionMemoryManager::reserveAllocationSpace(
uintptr_t CodeSize, Align CodeAlign, uintptr_t RODataSize,
Align RODataAlign, uintptr_t RWDataSize, Align RWDataAlign) {
+#endif
if (CodeSize == 0 && RODataSize == 0 && RWDataSize == 0)
return;
@@ -365,4 +398,7 @@
}
}
+} // namespace backport
} // namespace llvm
+
+#endif
From ae70eea20a9173502c5ffc97a6c85741e0b922e1 Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Wed, 31 Dec 2025 17:35:04 +1300
Subject: [PATCH 1/2] jit: Skip local SectionMemoryManager for LLVM 22.
Now that LLVM has committed[1] the bug fix that we slurped into the tree
in commit 9044fc1d, we only need to use our local copy for LLVM < 22.
This avoids further maintenance burden (cf 0dceba21), and also
establishes a garbage collection horizon that will let us delete our
copy when the time comes.
The only change that we don't have from the version that was committed
is the addition of a LLVM_ABI macro to control shared library exports,
but that isn't defined before LLVM 20 (our copy has to work with earlier
versions), and isn't appropriate here as our copy is private and in our
own library.
Also adjust the comments to explain that this is the code from LLVM 22.
Historical details about the pull request are now irrelevant.
[1] https://github.com/llvm/llvm-project/commit/1922c6ca262311bf321d33997471917adcdfbaef
Backpatch-through: 14
Reviewed-by:
Discussion:
---
src/backend/jit/llvm/SectionMemoryManager.cpp | 22 ++++++-------------
src/include/jit/SectionMemoryManager.h | 2 +-
src/include/jit/llvmjit_backport.h | 4 ++--
3 files changed, 10 insertions(+), 18 deletions(-)
diff --git a/src/backend/jit/llvm/SectionMemoryManager.cpp b/src/backend/jit/llvm/SectionMemoryManager.cpp
index 2171db5f382..e113ce81a3d 100644
--- a/src/backend/jit/llvm/SectionMemoryManager.cpp
+++ b/src/backend/jit/llvm/SectionMemoryManager.cpp
@@ -1,25 +1,17 @@
/*
- * This file is from https://github.com/llvm/llvm-project/pull/71968
- * with minor modifications to avoid name clash and work with older
- * LLVM versions. The llvm::backport::SectionMemoryManager class is a
- * drop-in replacement for llvm::SectionMemoryManager, for use with
- * llvm::RuntimeDyld. It fixes a memory layout bug on large memory
- * ARM systems (see pull request for details). If the LLVM project
- * eventually commits the change, we may need to resynchronize our
- * copy with any further modifications, but they would be unlikely to
- * backport it into the LLVM versions that we target so we would still
- * need this copy.
+ * This file is from LLVM 22 (originally pull request #71968), with minor
+ * modifications to avoid name clash and work with older LLVM versions. It
+ * replaces llvm::SectionMemoryManager, and is injected into llvm::RuntimeDyld
+ * to fix a memory layout bug on large memory ARM systems on LLVM < 22.
*
- * In the future we will switch to using JITLink instead of
- * RuntimeDyld where possible, and later remove this code (.cpp, .h,
- * .LICENSE) after all LLVM versions that we target allow it.
+ * We can remove this code (.cpp, .h, .LICENSE) once LLVM 22 is our minimum
+ * supported version or we've switched to JITLink for at least Aarch64.
*
* This file is a modified copy of a part of the LLVM source code that
* we would normally access from the LLVM library. It is therefore
* covered by the license at https://llvm.org/LICENSE.txt, reproduced
* verbatim in SectionMemoryManager.LICENSE in fulfillment of clause
- * 4a. The bugfix changes from the pull request are also covered, per
- * clause 5.
+ * 4a.
*/
//===- SectionMemoryManager.cpp - Memory manager for MCJIT/RtDyld *- C++ -*-==//
diff --git a/src/include/jit/SectionMemoryManager.h b/src/include/jit/SectionMemoryManager.h
index 924a99b0d33..4c9bd1c7f01 100644
--- a/src/include/jit/SectionMemoryManager.h
+++ b/src/include/jit/SectionMemoryManager.h
@@ -1,5 +1,5 @@
/*
- * This is a copy LLVM source code modified by the PostgreSQL project.
+ * This is a copy of LLVM source code modified by the PostgreSQL project.
* See SectionMemoryManager.cpp for notes on provenance and license.
*/
diff --git a/src/include/jit/llvmjit_backport.h b/src/include/jit/llvmjit_backport.h
index cba8eafc4f3..71cfdfc832f 100644
--- a/src/include/jit/llvmjit_backport.h
+++ b/src/include/jit/llvmjit_backport.h
@@ -8,14 +8,14 @@
#include <llvm/Config/llvm-config.h>
/*
- * LLVM's RuntimeDyld can produce code that crashes on larger memory ARM
+ * Pre-LLVM 22 RuntimeDyld can produce code that crashes on large memory ARM
* systems, because llvm::SectionMemoryManager allocates multiple pieces of
* memory that can be placed too far apart for the generated code. See
* src/backend/jit/llvm/SectionMemoryManager.cpp for the patched replacement
* class llvm::backport::SectionMemoryManager that we use as a workaround.
* This header controls whether we use it.
*/
-#if defined(__aarch64__)
+#if defined(__aarch64__) && LLVM_VERSION_MAJOR < 22
#define USE_LLVM_BACKPORT_SECTION_MEMORY_MANAGER
#endif
--
2.52.0
From 72a6e3d4040cde58e26a27febbf48671f025e056 Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Wed, 19 Nov 2025 00:00:13 +1300
Subject: [PATCH 2/2] jit: API changes for LLVM 22.
The lifetime.end intrinsic can now only be used for stack memory
allocated with alloca. We were using it to tell the optimizer that we
are no longer interested in the arguments and null flag in a
FunctionCallInfo struct, so it could avoid actually storing them if it
managed to inline the function and keep everything in registers. It
can't figure that out by itself because it's part of the ExecEvalStep
struct and we scribble on it directly rather than building a new one on
the stack.
Instead, store the special poison value (undef would work too). This
generates no actual code, but tells the optimizer that we are not
interested in the values.
Deform functions use LLVMBuildAlloca() for a stack variable, but that
memory is reclaimed implicitly by the ret instruction. This code should
probably do the same, but the change is non-trivial and not studied yet.
https://github.com/llvm/llvm-project/pull/149310
https://llvm.org/docs/LangRef.html#llvm-lifetime-end-intrinsic
https://llvm.org/docs/LangRef.html#i-alloca
---
src/backend/jit/llvm/llvmjit_expr.c | 18 +++++++++++++-----
1 file changed, 13 insertions(+), 5 deletions(-)
diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c
index f9c7f29e728..4dd2223bd2d 100644
--- a/src/backend/jit/llvm/llvmjit_expr.c
+++ b/src/backend/jit/llvm/llvmjit_expr.c
@@ -62,7 +62,9 @@ static LLVMValueRef build_EvalXFuncInt(LLVMBuilderRef b, LLVMModuleRef mod,
LLVMValueRef v_state,
ExprEvalStep *op,
int natts, LLVMValueRef *v_args);
+#if LLVM_VERSION_MAJOR < 22
static LLVMValueRef create_LifetimeEnd(LLVMModuleRef mod);
+#endif
/* macro making it easier to call ExecEval* functions */
#define build_EvalXFunc(b, mod, funcname, v_state, op, ...) \
@@ -3007,14 +3009,11 @@ BuildV1Call(LLVMJitContext *context, LLVMBuilderRef b,
LLVMModuleRef mod, FunctionCallInfo fcinfo,
LLVMValueRef *v_fcinfo_isnull)
{
- LLVMContextRef lc;
LLVMValueRef v_fn;
LLVMValueRef v_fcinfo_isnullp;
LLVMValueRef v_retval;
LLVMValueRef v_fcinfo;
- lc = LLVMGetModuleContext(mod);
-
v_fn = llvm_function_reference(context, b, mod, fcinfo);
v_fcinfo = l_ptr_const(fcinfo, l_ptr(StructFunctionCallInfoData));
@@ -3031,10 +3030,16 @@ BuildV1Call(LLVMJitContext *context, LLVMBuilderRef b,
*v_fcinfo_isnull = l_load(b, TypeStorageBool, v_fcinfo_isnullp, "");
/*
- * Add lifetime-end annotation, signaling that writes to memory don't have
- * to be retained (important for inlining potential).
+ * Signal that writes to FunctionCallInfoData don't have to be retained
+ * (important for inlining potential).
*/
+#if LLVM_VERSION_MAJOR >= 22
+ for (int i = 0; i < fcinfo->nargs; ++i)
+ LLVMBuildStore(b, LLVMGetPoison(StructNullableDatum), l_funcvaluep(b, v_fcinfo, i));
+ LLVMBuildStore(b, LLVMGetPoison(TypeStorageBool), v_fcinfo_isnullp);
+#else
{
+ LLVMContextRef lc = LLVMGetModuleContext(mod);
LLVMValueRef v_lifetime = create_LifetimeEnd(mod);
LLVMValueRef params[2];
@@ -3046,6 +3051,7 @@ BuildV1Call(LLVMJitContext *context, LLVMBuilderRef b,
params[1] = l_ptr_const(&fcinfo->isnull, l_ptr(LLVMInt8TypeInContext(lc)));
l_call(b, LLVMGetFunctionType(v_lifetime), v_lifetime, params, lengthof(params), "");
}
+#endif
return v_retval;
}
@@ -3083,6 +3089,7 @@ build_EvalXFuncInt(LLVMBuilderRef b, LLVMModuleRef mod, const char *funcname,
return v_ret;
}
+#if LLVM_VERSION_MAJOR < 22
static LLVMValueRef
create_LifetimeEnd(LLVMModuleRef mod)
{
@@ -3112,3 +3119,4 @@ create_LifetimeEnd(LLVMModuleRef mod)
return fn;
}
+#endif
--
2.52.0