From 3c40d92c9d4e7ba5216734517faa887ba87d990c Mon Sep 17 00:00:00 2001 From: Daveed Vandevoorde Date: Fri, 30 Oct 2026 22:35:31 +0000 Subject: [PATCH] Constant evaluation of GNU vector objects [GH #224] --- src/interpret.c | 34 +++++++---- .../gh-224-constexpr-vector-init-bitmap.txt | 21 +++++++ .../.gh-224-diag.rto/default.1.1.txt | 23 ++++++++ .../.gh-224-diag.rto/default.2.1.txt | 23 ++++++++ .../regressions/.gh-224.rto/default.1.1.txt | 1 + .../regressions/.gh-224.rto/default.2.1.txt | 1 + tests/tests/regressions/gh-224-diag.sft.cpp | 19 ++++++ tests/tests/regressions/gh-224.sft.cpp | 58 +++++++++++++++++++ 8 files changed, 170 insertions(+), 10 deletions(-) create mode 100644 src/pending_changes/gh-224-constexpr-vector-init-bitmap.txt create mode 100644 tests/tests/regressions/.gh-224-diag.rto/default.1.1.txt create mode 100644 tests/tests/regressions/.gh-224-diag.rto/default.2.1.txt create mode 100644 tests/tests/regressions/.gh-224.rto/default.1.1.txt create mode 100644 tests/tests/regressions/.gh-224.rto/default.2.1.txt create mode 100644 tests/tests/regressions/gh-224-diag.sft.cpp create mode 100644 tests/tests/regressions/gh-224.sft.cpp diff --git a/src/interpret.c b/src/interpret.c index 6c9269bc60..4d12755cd2 100644 --- a/src/interpret.c +++ b/src/interpret.c @@ -4439,6 +4439,21 @@ given number of bytes. (((n_bytes)+CHAR_BIT-1)/CHAR_BIT) +/* +Produce TRUE if type tp has subobjects whose initialization is tracked +individually (i.e., if a complete object of that type needs an initialization +bitmap in its prefix). +*/ +#if GNU_VECTOR_TYPES_ALLOWED +#define has_tracked_subobjects(tp) \ + (is_immediate_class_type(tp) || type_is(tp, tk_array) || \ + type_is(tp, tk_vector)) +#else /* !GNU_VECTOR_TYPES_ALLOWED */ +#define has_tracked_subobjects(tp) \ + (is_immediate_class_type(tp) || type_is(tp, tk_array)) +#endif /* GNU_VECTOR_TYPES_ALLOWED */ + + /* Given a type utp and the size n_bytes required to represent its value, compute the number of bytes needed as a bookkeeping prefix for a complete object of @@ -4447,8 +4462,7 @@ that type. #define compute_prefix_size_for_type(utp, n_bytes, prefix_size) \ { \ a_byte_count bitmap_size; \ - if (is_immediate_class_type(utp) || \ - utp->kind == (a_type_kind)tk_array) { \ + if (has_tracked_subobjects(utp)) { \ bitmap_size = compute_bitmap_size(n_bytes); \ } else { \ bitmap_size = 0; \ @@ -4617,11 +4631,11 @@ static void mark_whole_subobject_initialized( a_byte *complete_obj) /* If tp is a scalar type, this routine does the same work as -mark_subobject_initialized. If tp is a class or array type, it marks the -indicated subobject and all its subobject as initialized. +mark_subobject_initialized. If tp is a class, array, or vector type, it marks +the indicated subobject and all its subobjects as initialized. */ { - if (is_immediate_class_type(tp) || tp->kind == (a_type_kind)tk_array) { + if (has_tracked_subobjects(tp)) { a_boolean result = TRUE; a_byte_count n_bytes = value_bytes_for_type(ips, tp, &result); a_byte_count off = (a_byte_count)(subobj - complete_obj); @@ -4668,15 +4682,15 @@ static a_boolean mark_whole_subobject_uninitialized( a_byte *complete_obj) /* If tp is a scalar type, this routine does the same work as -mark_subobject_uninitialized. If tp is a class or array type, it marks the -indicated subobject and all its subobjects as uninitialized. Return TRUE if -successful, FALSE if not (e.g., if the subobject type included a member with -an error type). +mark_subobject_uninitialized. If tp is a class, array, or vector type, it +marks the indicated subobject and all its subobjects as uninitialized. Return +TRUE if successful, FALSE if not (e.g., if the subobject type included a member +with an error type). */ { a_boolean result = TRUE; - if (is_immediate_class_type(tp) || tp->kind == (a_type_kind)tk_array) { + if (has_tracked_subobjects(tp)) { a_byte_count n_bytes = value_bytes_for_type(ips, tp, &result); if (result) { a_byte_count off = (a_byte_count)(subobj - complete_obj); diff --git a/src/pending_changes/gh-224-constexpr-vector-init-bitmap.txt b/src/pending_changes/gh-224-constexpr-vector-init-bitmap.txt new file mode 100644 index 0000000000..8d44dc9841 --- /dev/null +++ b/src/pending_changes/gh-224-constexpr-vector-init-bitmap.txt @@ -0,0 +1,21 @@ +[GH #224] +Constant evaluation of GNU vector objects + +The constexpr interpreter tracks the initialization of each element of a GNU +vector, but did not reserve space for that bookkeeping in complete objects of +vector type. Evaluating such an object (e.g., zero-initializing a 64-byte +vector) could therefore corrupt memory and crash the front end. In addition, +initializing or destroying a whole vector subobject only updated the state of +its first element, so that reading another element could give a spurious +"access to uninitialized object" error (or fail to diagnose an access after +destruction). For example, with --g++ --c++20: + + typedef char V64 __attribute__((vector_size(64))); + typedef int V4 __attribute__((vector_size(16))); + int main() { + V64 a = V64(); // Could previously crash. + return (int)a[0]; + } + struct T { V4 v; }; + constexpr int f() { T t; t.v = V4{1, 2, 3, 4}; return t.v[3]; } + static_assert(f() == 4, ""); // Previously a spurious error. diff --git a/tests/tests/regressions/.gh-224-diag.rto/default.1.1.txt b/tests/tests/regressions/.gh-224-diag.rto/default.1.1.txt new file mode 100644 index 0000000000..46d59c738a --- /dev/null +++ b/tests/tests/regressions/.gh-224-diag.rto/default.1.1.txt @@ -0,0 +1,23 @@ +fe_only -DTEST_NUMBER=1 --c++20 --g++ --gnu_version=150200 Test_name.c +"Test_name.c", line 12: error: expression must have a constant value + static_assert(d1() == 4, ""); // Error. + ^ +"Test_name.c", line 11: note: access to uninitialized object + constexpr int d1() { vi4 a = {1, 2, 3, 4}; a.~vi4(); return a[3]; } + ^ + +"Test_name.c", line 15: error: expression must have a constant value + static_assert(d2() == 0, ""); // Error. + ^ +"Test_name.c", line 14: note: access to uninitialized object + constexpr int d2() { vl8 a = vl8(); a.~vl8(); return (int)a[7]; } + ^ + +"Test_name.c", line 19: error: expression must have a constant value + static_assert(d3() == 0, ""); // Error. + ^ +"Test_name.c", line 18: note: access to uninitialized object + constexpr int d3() { S s{}; s.v.~vl8(); return (int)s.v[7]; } + ^ + +3 errors detected in the compilation of "Test_name.c". diff --git a/tests/tests/regressions/.gh-224-diag.rto/default.2.1.txt b/tests/tests/regressions/.gh-224-diag.rto/default.2.1.txt new file mode 100644 index 0000000000..f9bbd6220c --- /dev/null +++ b/tests/tests/regressions/.gh-224-diag.rto/default.2.1.txt @@ -0,0 +1,23 @@ +fe_only -DTEST_NUMBER=2 --c++20 --clang --clang_version=220100 Test_name.c +"Test_name.c", line 12: error: expression must have a constant value + static_assert(d1() == 4, ""); // Error. + ^ +"Test_name.c", line 11: note: access to uninitialized object + constexpr int d1() { vi4 a = {1, 2, 3, 4}; a.~vi4(); return a[3]; } + ^ + +"Test_name.c", line 15: error: expression must have a constant value + static_assert(d2() == 0, ""); // Error. + ^ +"Test_name.c", line 14: note: access to uninitialized object + constexpr int d2() { vl8 a = vl8(); a.~vl8(); return (int)a[7]; } + ^ + +"Test_name.c", line 19: error: expression must have a constant value + static_assert(d3() == 0, ""); // Error. + ^ +"Test_name.c", line 18: note: access to uninitialized object + constexpr int d3() { S s{}; s.v.~vl8(); return (int)s.v[7]; } + ^ + +3 errors detected in the compilation of "Test_name.c". diff --git a/tests/tests/regressions/.gh-224.rto/default.1.1.txt b/tests/tests/regressions/.gh-224.rto/default.1.1.txt new file mode 100644 index 0000000000..566baa0764 --- /dev/null +++ b/tests/tests/regressions/.gh-224.rto/default.1.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=1 --c++20 --g++ --gnu_version=150200 Test_name.c diff --git a/tests/tests/regressions/.gh-224.rto/default.2.1.txt b/tests/tests/regressions/.gh-224.rto/default.2.1.txt new file mode 100644 index 0000000000..6eaa284f92 --- /dev/null +++ b/tests/tests/regressions/.gh-224.rto/default.2.1.txt @@ -0,0 +1 @@ +fe_only -DTEST_NUMBER=2 --c++20 --clang --clang_version=220100 Test_name.c diff --git a/tests/tests/regressions/gh-224-diag.sft.cpp b/tests/tests/regressions/gh-224-diag.sft.cpp new file mode 100644 index 0000000000..c2f77dbc7b --- /dev/null +++ b/tests/tests/regressions/gh-224-diag.sft.cpp @@ -0,0 +1,19 @@ +//type:fn +//options:--c++20 --g++ --gnu_version=150200:--c++20 --clang --clang_version=220100 +//remark:Reading an element of a destroyed GNU vector is not constant. + +// Destroying a vector subobject used to mark only its first element as +// uninitialized, so reading a later element was accepted (d3). + +typedef int vi4 __attribute__((vector_size(16))); +typedef long long vl8 __attribute__((vector_size(64))); + +constexpr int d1() { vi4 a = {1, 2, 3, 4}; a.~vi4(); return a[3]; } +static_assert(d1() == 4, ""); // Error. + +constexpr int d2() { vl8 a = vl8(); a.~vl8(); return (int)a[7]; } +static_assert(d2() == 0, ""); // Error. + +struct S { vl8 v; }; +constexpr int d3() { S s{}; s.v.~vl8(); return (int)s.v[7]; } +static_assert(d3() == 0, ""); // Error. diff --git a/tests/tests/regressions/gh-224.sft.cpp b/tests/tests/regressions/gh-224.sft.cpp new file mode 100644 index 0000000000..f2dfa01fa5 --- /dev/null +++ b/tests/tests/regressions/gh-224.sft.cpp @@ -0,0 +1,58 @@ +//type:fp +//options:--c++20 --g++ --gnu_version=150200:--c++20 --clang --clang_version=220100 +//remark:Constant evaluation of GNU vector objects and their elements. + +// The interpreter did not reserve an initialization bitmap for complete +// objects of GNU vector type, so tracking the initialization of their elements +// wrote past the object's bookkeeping prefix (a 64-byte vector suffices). +// Assigning a whole vector subobject also marked only its first element as +// initialized. + +typedef char vc64 __attribute__((vector_size(64))); +typedef int vi4 __attribute__((vector_size(16))); +typedef long long vl8 __attribute__((vector_size(64))); + +int main() { + vc64 a = vc64(); + return (int)a[0]; +} + +constexpr int f1() { vc64 a = vc64(); return a[63]; } +static_assert(f1() == 0, ""); + +constexpr int f2() { vc64 a{}; return a[0] + a[63]; } +static_assert(f2() == 0, ""); + +constexpr int f3() { vl8 a = vl8(); vl8 b = a; return (int)b[7]; } +static_assert(f3() == 0, ""); + +constexpr int f4() { + vl8 a = vl8(); + a[7] = 3; + vl8 b; + b = a; + return (int)b[7]; +} +static_assert(f4() == 3, ""); + +constexpr vc64 g5() { return vc64(); } +constexpr int f5() { return g5()[63]; } +static_assert(f5() == 0, ""); + +constexpr vl8 v6 = vl8(); +static_assert(v6[7] == 0, ""); + +struct S { vl8 v; }; +struct T { vi4 v; int i; }; + +constexpr int f7() { S s{}; return (int)s.v[7]; } +static_assert(f7() == 0, ""); + +constexpr int f8() { S s; s.v = vl8(); return (int)s.v[7]; } +static_assert(f8() == 0, ""); + +constexpr int f9() { T t; t.v = vi4{1, 2, 3, 4}; t.i = 0; return t.v[3]; } +static_assert(f9() == 4, ""); + +constexpr int f10() { vi4 a[2]; a[1] = vi4{1, 2, 3, 4}; return a[1][3]; } +static_assert(f10() == 4, "");