[clang] [CIR] Evaluate VLA bounds for a static local of variably modified type (PR #214926)
Bruno Cardoso Lopes via cfe-commits
cfe-commits at lists.llvm.org
Fri Aug 7 21:02:38 PDT 2026
https://github.com/bcardosolopes created https://github.com/llvm/llvm-project/pull/214926
emitStaticVarDecl bailed with errorNYI whenever a `static` local had a variably modified type -- in practice a pointer to a VLA, since the static itself can't be one. CIRGenFunction::emitVariablyModifiedType already exists and already handles this walk, so this is the same one-liner classic CodeGen uses in CodeGenFunction::EmitStaticVarDecl.
Found when building binutils. This is autoconf's AC_C_VARARRAYS probe, so the NYI was quietly changing what configure-based projects build: the probe failed under -fclangir, config.h came out with HAVE_C_VARARRAYS undefined and __STDC_NO_VLA__ defined, and gnulib took its non-VLA fallback paths. Nothing errored -- the CIR build just compiled different source than the baseline.
>From 71152e5be3909318542cb48e40a800b499105c36 Mon Sep 17 00:00:00 2001
From: Bruno Cardoso Lopes <bruno.cardoso at gmail.com>
Date: Fri, 7 Aug 2026 20:53:30 -0700
Subject: [PATCH] [CIR] Evaluate VLA bounds for a static local of variably
modified type
emitStaticVarDecl bailed with errorNYI whenever a `static` local had a
variably modified type -- in practice a pointer to a VLA, since the static
itself can't be one. CIRGenFunction::emitVariablyModifiedType already exists
and already handles this walk, so this is the same one-liner classic CodeGen
uses in CodeGenFunction::EmitStaticVarDecl.
This is autoconf's AC_C_VARARRAYS probe, so the NYI was quietly changing what
configure-based projects build: the probe failed under -fclangir, config.h came
out with HAVE_C_VARARRAYS undefined and __STDC_NO_VLA__ defined, and gnulib took
its non-VLA fallback paths. Nothing errored -- the CIR build just compiled
different source than the baseline.
Add clang/test/CIR/CodeGen/static-vla-pointer.c covering both the AC_C_VARARRAYS
shape and a bound with side effects, checked against classic CodeGen.
---
clang/lib/CIR/CodeGen/CIRGenDecl.cpp | 6 +--
clang/test/CIR/CodeGen/static-vla-pointer.c | 57 +++++++++++++++++++++
2 files changed, 59 insertions(+), 4 deletions(-)
create mode 100644 clang/test/CIR/CodeGen/static-vla-pointer.c
diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
index e17550a8c1668..d2fa07b3afa9a 100644
--- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
@@ -725,10 +725,8 @@ void CIRGenFunction::emitStaticVarDecl(const VarDecl &d,
// We can't have a VLA here, but we can have a pointer to a VLA,
// even though that doesn't really make any sense.
// Make sure to evaluate VLA bounds now so that we have them for later.
- if (d.getType()->isVariablyModifiedType()) {
- cgm.errorNYI(d.getSourceRange(),
- "emitStaticVarDecl: variably modified type");
- }
+ if (d.getType()->isVariablyModifiedType())
+ emitVariablyModifiedType(d.getType());
// Save the type in case adding the initializer forces a type change.
mlir::Type expectedType = addr.getType();
diff --git a/clang/test/CIR/CodeGen/static-vla-pointer.c b/clang/test/CIR/CodeGen/static-vla-pointer.c
new file mode 100644
index 0000000000000..1342d46ffcade
--- /dev/null
+++ b/clang/test/CIR/CodeGen/static-vla-pointer.c
@@ -0,0 +1,57 @@
+// RUN: %clang_cc1 -Wno-error=incompatible-pointer-types -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
+// RUN: %clang_cc1 -Wno-error=incompatible-pointer-types -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM
+// RUN: %clang_cc1 -Wno-error=incompatible-pointer-types -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
+
+// A `static` local can't be a VLA, but it can have a variably modified type --
+// a pointer to a VLA. The variable itself is an ordinary global; the VLA bound
+// still has to be evaluated in the enclosing function, for its side effects.
+
+int bound(void);
+int buf[100];
+
+// The two static locals are ordinary globals initialized to &buf. Both
+// functions emit their globals into the same module prologue, so check them
+// together and out of order.
+
+// CIR-DAG: cir.global "private" internal dso_local @side_effecting_bound.p = #cir.global_view<@buf> : !cir.ptr<!s32i>
+// CIR-DAG: cir.global "private" internal dso_local @vararrays_probe.q = #cir.global_view<@buf> : !cir.ptr<!s32i>
+
+// LLVM-DAG: @side_effecting_bound.p = internal global ptr @buf
+// LLVM-DAG: @vararrays_probe.q = internal global ptr @buf
+
+// OGCG-DAG: @side_effecting_bound.p = internal global ptr @buf
+// OGCG-DAG: @vararrays_probe.q = internal global ptr @buf
+
+void side_effecting_bound(void) {
+ static int (*p)[bound()] = &buf;
+ (void)p;
+}
+
+// CIR-LABEL: cir.func{{.*}} @side_effecting_bound()
+// CIR: cir.call @bound() : () -> !s32i
+
+// LLVM-LABEL: define {{.*}} void @side_effecting_bound()
+// LLVM: call i32 @bound()
+
+// OGCG-LABEL: define {{.*}} void @side_effecting_bound()
+// OGCG: call i32 @bound()
+
+// This is autoconf's AC_C_VARARRAYS probe. It mainly has to compile at all; a
+// failure here silently flips HAVE_C_VARARRAYS in every configure-based project.
+
+int vararrays_probe(int m, int c[m][m]) {
+ static int (*q)[m] = &buf;
+ return c && q != 0;
+}
+
+// CIR-LABEL: cir.func{{.*}} @vararrays_probe(
+// CIR: cir.get_global @vararrays_probe.q : !cir.ptr<!cir.ptr<!s32i>>
+
+// LLVM-LABEL: define {{.*}} i32 @vararrays_probe(
+// LLVM: load ptr, ptr @vararrays_probe.q
+
+// OGCG-LABEL: define {{.*}} i32 @vararrays_probe(
+// OGCG: load ptr, ptr @vararrays_probe.q
More information about the cfe-commits
mailing list