Subject: LLVM 22

From: Thomas Munro <[email-masked]>
Date: Sat, 3 Jan 2026 15:02:37 +1300

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-masked]>
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-masked]>
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

---

From: Thomas Munro <[email-masked]>
Date: Sun, 4 Jan 2026 18:02:00 +1300

> See commit message for details.

While that's true, there is a problem with the patch I posted:
"ReserveAlloc" is not enabled when called from C.  I can't actually
reproduce the issue locally due to lack of RAM connected to an ARM
CPU, or I'd have noticed that...  I'll attempt to do something about
that upstream[1], let's see...  if not, we can still use the new
in-tree SectionMemoryManager, but we'll still need some C++ glue code.

[1] https://github.com/llvm/llvm-project/issues/174305

---

From: "Matheus Alcantara" <[email-masked]>
Date: Mon, 05 Jan 2026 18:56:10 -0300

Hi,

> FATAL:  fatal llvm error: Broken module found, compilation aborted!
I've managed to reproduce this using LLVM 22.

> what I had so far to see if anyone has a better idea or an easy way to
I'm not super familiar with reading assembly code but I tried my best to
inspect the LLVM 22 and LLVM 21 outputs and if I understood correctly I
think that 0002 is working as expected.

I've noticed a reduction on some instructions when using LLVM 22 with
the 0002 patch compared with LLVM 21. For example, here we needed less
instructions to set up the registers:

LLVM 22:
    LBB2_8:                                 ; %b.op.1.start
        mov	x20, #40824                     ; =0x9f78
        movk	x20, #19456, lsl #16
        movk	x20, #1, lsl #32
        ldr	x8, [x23]
        ldrb	w9, [x24]
        str	x8, [x20, #152]
        strb	w9, [x20, #160]

LLVM 21:
    LBB2_8:                                 ; %b.op.1.start
        mov	x25, #25352                     ; =0x6308
        movk	x25, #2946, lsl #16
        movk	x25, #1, lsl #32
        mov	x20, #23533                     ; =0x5bed
        movk	x20, #2946, lsl #16
        movk	x20, #1, lsl #32
        ldr	x8, [x23]
        ldrb	w9, [x24]
        stur	x8, [x25, #-248]
        sturb	w9, [x25, #-240]

I've also noticed that the generated assembly code for LLVM 22 use the
str and strb instructions instead of stur and sturb in some cases, which
according to IA is an improvement but unfortunately I did not find any
reference to prove this, sorry.

To test this I did the following steps:
set jit_above_cost = 0; 
set jit_inline_above_cost = 0; 
set jit_optimize_above_cost = 0;
set jit_dump_bitcode = true;

explain(analyze) select i % 2 = 0 OR i % 3 = 0 from generate_series(1, 100) i;

I'm attaching the .s files for the llvm 22 and for the llvm 21 outputs
that I used to inspect.

--
Matheus Alcantara
EDB: https://www.enterprisedb.com

---

From: Thomas Munro <[email-masked]>
Date: Tue, 6 Jan 2026 12:50:38 +1300

> I've managed to reproduce this using LLVM 22.

Thanks for testing!

> think that 0002 is working as expected.

Cool.  And as another sanity test, if you comment out the new poison
code so that we don't try to prevent unwanted spills/stores, can you
see any?

> reference to prove this, sorry.

Interesting.

---

From: "Matheus Alcantara" <[email-masked]>
Date: Tue, 06 Jan 2026 15:35:06 -0300

> code so that we don't try to prevent unwanted spills/stores, can you
Yes, I've commented the poison code block introduced on 0002 and the
generated assembly code seems more bloated, for example:

LLVM 22 with 0002 and the poison code block commented:
    LBB2_8:
        mov	x25, #15624                     ; =0x3d08
        movk	x25, #7427, lsl #16
        movk	x25, #1, lsl #32
        mov	x20, #13805                     ; =0x35ed
        movk	x20, #7427, lsl #16
        movk	x20, #1, lsl #32
        ldr	x8, [x23]
        ldrb	w9, [x24]
        stur	x8, [x25, #-248]
        sturb	w9, [x25, #-240]
        mov	w26, #1                         ; =0x1
        strb	w26, [x20, #1419]
        ldurb	w8, [x25, #-240]
        cmp	w8, #1
        b.eq	LBB2_11

LLVM 22 with 0002:
    LBB2_8:
        mov	w26, #1                         ; =0x1
        strb	w26, [x20]
        cmp	w9, #1
        b.eq	LBB2_11

IIUC with the commented code the LLVM compiler added an extra
load ldurb followed by cmp w8, #1. With the patch it performs a
comparison cmp w9, #1 directly using a register it already has.