From c624562a93080d9a1b1411d5a069aec3820338ad Mon Sep 17 00:00:00 2001
From: Greg Burd <greg@burd.me>
Date: Mon, 8 Jun 2026 11:14:50 -0400
Subject: [PATCH v5] Disable auto-vectorization on RISC-V with Clang older than
 22

Clang's loop vectorizer miscompiles data-dependent scatter-store loops of
the form "dst[idx[i]] = expr" on RISC-V when the V extension is enabled and
auto-vectorization runs at -O2 or above.  The indexed scatter is lowered to
a unit-stride store, silently dropping the permutation and producing wrong
results.

This was first observed as authentication failures from contrib/pgcrypto on
the riscv64 buildfarm animal greenfly: des_init() builds its permutation
tables with exactly this idiom, so the resulting DES tables were corrupt.
The same source idiom appears elsewhere in the tree (for example in
src/timezone/zic.c, which is not meaningfully exercised by the regression
tests), so a per-call-site workaround in crypt-des.c does not scale -- a
later Clang release could vectorize a site we have not annotated.

The bug is fixed in Clang 22; Clang 20 and 21 are affected and the fix was
not backported.  Clang 20 still ships as the default in current
distributions for riscv64, so rather than refuse to build with affected
compilers, disable auto-vectorization globally for the affected combination
by adding -fno-vectorize.  The configure/meson probe keys on the compiler
(__clang__ && __riscv && __clang_major__ < 22) so that a fixed Clang, GCC,
or any non-RISC-V target is unaffected and pays no cost.

Author: Greg Burd <greg@burd.me>
Discussion: https://postgr.es/m/d9f246d9-70dc-45ed-b0a2-1c583e2835e3@app.fastmail.com
---
 configure.ac | 19 +++++++++++++++++++
 meson.build  | 25 +++++++++++++++++++++++++
 2 files changed, 44 insertions(+)

diff --git a/configure.ac b/configure.ac
index dc9c0fd247a..a2dba65457b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -804,6 +804,25 @@ choke me
 [AC_MSG_ERROR([Compiling PostgreSQL with clang, on 32bit x86, requires SSE2 support. Use -msse2 or use gcc.])])
 fi
 
+# Defend against a Clang loop-vectorizer wrong-code bug on RISC-V.  Clang
+# versions before 22 miscompile data-dependent scatter-store loops of the
+# form "dst[idx[i]] = expr" when the V extension is enabled and auto-
+# vectorization runs (at -O2 and above): the indexed scatter is lowered to
+# a unit-stride store, silently dropping the permutation.  We hit this in
+# des_init() (contrib/pgcrypto), and the same idiom appears elsewhere in
+# the tree (e.g. zic.c), so a per-site workaround does not scale.  The bug
+# is fixed in Clang 22; rather than refuse such compilers outright (Clang
+# 20 still ships in current distributions for riscv64), disable auto-
+# vectorization globally on the affected combination.  The test keys on the
+# compiler so that a fixed Clang, or any other compiler, is unaffected.
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [
+@%:@if defined(__clang__) && defined(__riscv) && __clang_major__ < 22
+choke me
+@%:@endif
+])], [],
+[PGAC_PROG_CC_CFLAGS_OPT([-fno-vectorize])
+ PGAC_PROG_CXX_CFLAGS_OPT([-fno-vectorize])])
+
 AC_PROG_CPP
 AC_SUBST(GCC)
 
diff --git a/meson.build b/meson.build
index 286d3b01f15..12c0244fc4d 100644
--- a/meson.build
+++ b/meson.build
@@ -2175,6 +2175,31 @@ choke me
 endif
 
 
+# Defend against a Clang loop-vectorizer wrong-code bug on RISC-V.  Clang
+# versions before 22 miscompile data-dependent scatter-store loops of the
+# form "dst[idx[i]] = expr" when the V extension is enabled and auto-
+# vectorization runs (at -O2 and above): the indexed scatter is lowered to
+# a unit-stride store, silently dropping the permutation.  We hit this in
+# des_init() (contrib/pgcrypto), and the same idiom appears elsewhere in
+# the tree (e.g. zic.c), so a per-site workaround does not scale.  The bug
+# is fixed in Clang 22; rather than refuse such compilers outright (Clang
+# 20 still ships in current distributions for riscv64), disable auto-
+# vectorization globally on the affected combination.  The test keys on the
+# compiler so that a fixed Clang, or any other compiler, is unaffected.
+if not cc.compiles('''
+#if defined(__clang__) && defined(__riscv) && __clang_major__ < 22
+choke me
+#endif''',
+    name: 'whether Clang on RISC-V needs auto-vectorization disabled',
+    args: test_c_args)
+  no_vectorize_cflags = cc.get_supported_arguments(['-fno-vectorize'])
+  cflags += no_vectorize_cflags
+  if have_cxx
+    cxxflags += cxx.get_supported_arguments(['-fno-vectorize'])
+  endif
+endif
+
+
 # Check whether the C++ compiler supports designated initializers.
 # These are used by PG_MODULE_MAGIC, and we use the result of this
 # test to decide whether to enable the test_cplusplusext test module.
-- 
2.50.1

