Hi,

On 2025-04-25 15:58:29 -0400, Andres Freund wrote:
> On 2025-04-25 13:37:15 -0400, Tom Lane wrote:
> > Buildfarm member serinus has been producing the identical warning for
> > some time.  I'd been ignoring that because it runs "experimental gcc",
> > but I guess the experiment has leaked out to production distros.
> > 
> > What seems to be happening here is that after inlining
> > assign_simple_var into exec_set_found, the compiler decides that
> > "newvalue" might be zero (since it's a BoolGetDatum result),
> > and then it warns -- in a rather strange way -- about the
> > potential null dereference.
> 
> I don't think it actually is complaining about a null dereference - it thinks
> we're interpreting a boolean as a pointer (for which it obviously is not wide
> enough)
> 
> 
> > The dereference is not reachable
> > because of the preceding "var->datatype->typlen == -1" check,
> > but that's not stopping the optimizer from bitching.
> 
> > I experimented with modifying exec_set_found thus:
> > 
> >     var = (PLpgSQL_var *) (estate->datums[estate->found_varno]);
> > +   Assert(var->datatype->typlen == 1);
> >     assign_simple_var(estate, var, BoolGetDatum(state), false, false);
> > 
> > which should be OK since we're expecting the "found" variable to
> > be boolean.  That does silence the warning, but of course only
> > in --enable-cassert builds.
> 
> One way to address this is outlined here:
> 
> https://postgr.es/m/20230316172818.x6375uvheom3ibt2%40awork3.anarazel.de
> https://postgr.es/m/20240207203138.sknifhlppdtgtxnk%40awork3.anarazel.de
> 
> I've been wondering about adding wrapping something like that in a
> pg_assume(expr) or such.

I've been once more annoyed by this warning. Here's a prototype for the
approach outlined above.

Greetings,

Andres Freund
>From 147e2058e94338802ebc03bcfa7639d3c6bde75a Mon Sep 17 00:00:00 2001
From: Andres Freund <and...@anarazel.de>
Date: Wed, 4 Jun 2025 14:28:19 -0400
Subject: [PATCH v1 1/2] Add pg_assume(expr) macro

This macro can be used to avoid compiler warnings, particularly when using -O3
and not using assertions, and to get the compiler to generate better code.

A subsequent commit introduces a first user.

Discussion: https://postgr.es/m/3prdb6hkep3duglhsujrn52bkvnlkvhc54fzvph2emrsm4vodl@77yy6j4hkemb
Discussion: https://postgr.es/m/20230316172818.x6375uvheom3ibt2%40awork3.anarazel.de
Discussion: https://postgr.es/m/20240207203138.sknifhlppdtgtxnk%40awork3.anarazel.de
---
 src/include/c.h | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/src/include/c.h b/src/include/c.h
index 8cdc16a0f4a..318c8ba978f 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -332,6 +332,36 @@
 #define pg_unreachable() abort()
 #endif
 
+/*
+ * pg_assume(expr) stats that we assume `expr` to evaluate to true. In assert
+ * enabled builds pg_assume() is turned into an assertion, in optimized builds
+ * we try to clue the compiler into the fact that `expr` is true.
+ *
+ * This is useful for two purposes:
+ *
+ * 1) Avoid compiler warnings by telling the compiler about assumptions the
+ *	  code makes. This is particularly useful when building with optimizations
+ *	  and w/o assertions.
+ *
+ * 2) Help the compiler to generate more efficient code
+ *
+ * It is unspecified whether `expr` is evaluated, therefore it better be
+ * side-effect free.
+ */
+#if defined(USE_ASSERT_CHECKING)
+#define pg_assume(expr) Assert(expr)
+#elif defined(HAVE__BUILTIN_UNREACHABLE)
+#define pg_assume(expr) \
+	do { \
+		if (!(expr)) \
+			__builtin_unreachable(); \
+	} while (0)
+#elif defined(_MSC_VER)
+#define pg_assume(expr) __assume(expr)
+#else
+#define pg_assume(expr) ((void) 0)
+#endif
+
 /*
  * Hints to the compiler about the likelihood of a branch. Both likely() and
  * unlikely() return the boolean value of the contained expression.
-- 
2.48.1.76.g4e746b1a31.dirty

>From 3fe01158335d43ed133755aaffd41270c5e7af1a Mon Sep 17 00:00:00 2001
From: Andres Freund <and...@anarazel.de>
Date: Wed, 4 Jun 2025 14:33:53 -0400
Subject: [PATCH v1 2/2] Use pg_assume() to avoid compiler warning below
 exec_set_found()

The warning, visible when building with -O3 and a recent-ish gcc, is due to
gcc not realizing that found is a byvalue type and therefore will never be
interpreted as a varlena type.

Discussion: https://postgr.es/m/3prdb6hkep3duglhsujrn52bkvnlkvhc54fzvph2emrsm4vodl@77yy6j4hkemb
Discussion: https://postgr.es/m/20230316172818.x6375uvheom3ibt2%40awork3.anarazel.de
Discussion: https://postgr.es/m/20240207203138.sknifhlppdtgtxnk%40awork3.anarazel.de
---
 src/pl/plpgsql/src/pl_exec.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index bb99781c56e..ecac29e830b 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -8606,6 +8606,15 @@ exec_set_found(PLpgSQL_execstate *estate, bool state)
 	PLpgSQL_var *var;
 
 	var = (PLpgSQL_var *) (estate->datums[estate->found_varno]);
+
+	/*
+	 * The pg_assume() avoids a spurious warning with some compilers due to
+	 * compiler not realizing the VARATT_IS_EXTERNAL_NON_EXPANDED() branch in
+	 * assign_simple_var() will never be reached, due to "found" being a
+	 * boolean, a byvalue type.
+	 */
+	pg_assume(var->datatype->typlen != -1);
+
 	assign_simple_var(estate, var, BoolGetDatum(state), false, false);
 }
 
-- 
2.48.1.76.g4e746b1a31.dirty

Reply via email to