Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 24 additions & 10 deletions src/interpret.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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; \
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
21 changes: 21 additions & 0 deletions src/pending_changes/gh-224-constexpr-vector-init-bitmap.txt
Original file line number Diff line number Diff line change
@@ -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.
23 changes: 23 additions & 0 deletions tests/tests/regressions/.gh-224-diag.rto/default.1.1.txt
Original file line number Diff line number Diff line change
@@ -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".
23 changes: 23 additions & 0 deletions tests/tests/regressions/.gh-224-diag.rto/default.2.1.txt
Original file line number Diff line number Diff line change
@@ -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".
1 change: 1 addition & 0 deletions tests/tests/regressions/.gh-224.rto/default.1.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fe_only -DTEST_NUMBER=1 --c++20 --g++ --gnu_version=150200 Test_name.c
1 change: 1 addition & 0 deletions tests/tests/regressions/.gh-224.rto/default.2.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fe_only -DTEST_NUMBER=2 --c++20 --clang --clang_version=220100 Test_name.c
19 changes: 19 additions & 0 deletions tests/tests/regressions/gh-224-diag.sft.cpp
Original file line number Diff line number Diff line change
@@ -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.
58 changes: 58 additions & 0 deletions tests/tests/regressions/gh-224.sft.cpp
Original file line number Diff line number Diff line change
@@ -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, "");
Loading