[COMMITTED 14/43] gccrs: Implement basic support for #[repr(simd)]

arthur.cohen@opensrcsec.com arthur.cohen@opensrcsec.com
Thu Sep 10 08:19:27 GMT 2026


From: Yap Zhi Heng <yapzhhg@gmail.com>

Only type-checking support is implemented, simd representation structs are still
compiled as regular structs.

Note that this implementation is specific to 1.49.0, #[repr(simd)] structs uses
a single array field from 1.50.0 onwards.

gcc/rust/ChangeLog:

	* typecheck/rust-tyty.h (ADTType::ReprKind): Add new SIMD variant.
	* typecheck/rust-hir-type-check-base.cc (TypeCheckBase::parse_repr_options): Parse
	#[repr(simd)] attribute, rename inline_option to repr_option.
	* typecheck/rust-hir-type-check-item.h (TypeCheckItem::validate_repr_simd): New
	function.
	* typecheck/rust-hir-type-check-item.cc (TypeCheckItem::validate_repr_simd):
	Implement type-checking of #[repr(simd)] structs.
	(TypeCheckItem::visit (HIR::TupleStruct)): Support type-checking simd representation.
	(TypeCheckItem::visit (HIR::StructStruct)): Ditto.

Signed-off-by: Yap Zhi Heng <yapzhhg@gmail.com>
---
 .../typecheck/rust-hir-type-check-base.cc     |  50 ++++---
 .../typecheck/rust-hir-type-check-item.cc     | 134 +++++++++++++++++-
 gcc/rust/typecheck/rust-hir-type-check-item.h |   3 +
 gcc/rust/typecheck/rust-tyty.h                |   2 +-
 gcc/testsuite/rust/compile/repr_simd.rs       |  35 +++++
 5 files changed, 196 insertions(+), 28 deletions(-)
 create mode 100644 gcc/testsuite/rust/compile/repr_simd.rs

diff --git a/gcc/rust/typecheck/rust-hir-type-check-base.cc b/gcc/rust/typecheck/rust-hir-type-check-base.cc
index f966f002aa2..4c276ce83d0 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-base.cc
+++ b/gcc/rust/typecheck/rust-hir-type-check-base.cc
@@ -506,23 +506,24 @@ TypeCheckBase::parse_repr_options (const AST::AttrVec &attrs, location_t locus)
 	      continue;
 	    }
 
-	  const std::string inline_option = items.at (0)->as_string ();
+	  const std::string repr_option = items.at (0)->as_string ();
 
 	  // TODO: it would probably be better to make the MetaItems more aware
 	  // of constructs with nesting like #[repr(packed(2))] rather than
 	  // manually parsing the string "packed(2)" here.
 
-	  size_t oparen = inline_option.find ('(', 0);
+	  size_t oparen = repr_option.find ('(', 0);
 	  bool is_pack = false;
 	  bool is_align = false;
 	  bool is_c = false;
 	  bool is_integer = false;
 	  bool is_transparent = false;
+	  bool is_simd = false;
 	  unsigned char value = 1;
 
 	  if (oparen == std::string::npos)
 	    {
-	      if (inline_option.compare ("align") == 0)
+	      if (repr_option.compare ("align") == 0)
 		{
 		  rust_error_at (attr.get_locus (), ErrorCode::E0589,
 				 "invalid %<repr(align)%> attribute: %<align%> "
@@ -531,36 +532,37 @@ TypeCheckBase::parse_repr_options (const AST::AttrVec &attrs, location_t locus)
 		  break;
 		}
 
-	      is_pack = inline_option.compare ("packed") == 0;
-	      is_c = inline_option.compare ("C") == 0;
-	      is_integer = (inline_option.compare ("isize") == 0
-			    || inline_option.compare ("i8") == 0
-			    || inline_option.compare ("i16") == 0
-			    || inline_option.compare ("i32") == 0
-			    || inline_option.compare ("i64") == 0
-			    || inline_option.compare ("i128") == 0
-			    || inline_option.compare ("usize") == 0
-			    || inline_option.compare ("u8") == 0
-			    || inline_option.compare ("u16") == 0
-			    || inline_option.compare ("u32") == 0
-			    || inline_option.compare ("u64") == 0
-			    || inline_option.compare ("u128") == 0);
-	      is_transparent = inline_option.compare ("transparent") == 0;
+	      is_pack = repr_option.compare ("packed") == 0;
+	      is_c = repr_option.compare ("C") == 0;
+	      is_integer = (repr_option.compare ("isize") == 0
+			    || repr_option.compare ("i8") == 0
+			    || repr_option.compare ("i16") == 0
+			    || repr_option.compare ("i32") == 0
+			    || repr_option.compare ("i64") == 0
+			    || repr_option.compare ("i128") == 0
+			    || repr_option.compare ("usize") == 0
+			    || repr_option.compare ("u8") == 0
+			    || repr_option.compare ("u16") == 0
+			    || repr_option.compare ("u32") == 0
+			    || repr_option.compare ("u64") == 0
+			    || repr_option.compare ("u128") == 0);
+	      is_transparent = repr_option.compare ("transparent") == 0;
+	      is_simd = repr_option.compare ("simd") == 0;
 	    }
 
 	  else
 	    {
-	      std::string rep = inline_option.substr (0, oparen);
+	      std::string rep = repr_option.substr (0, oparen);
 	      is_pack = rep.compare ("packed") == 0;
 	      is_align = rep.compare ("align") == 0;
 
-	      size_t cparen = inline_option.find (')', oparen);
+	      size_t cparen = repr_option.find (')', oparen);
 	      if (cparen == std::string::npos)
 		{
 		  rust_error_at (locus, "malformed attribute");
 		}
 
-	      std::string value_str = inline_option.substr (oparen, cparen);
+	      std::string value_str = repr_option.substr (oparen, cparen);
 	      value = strtoul (value_str.c_str () + 1, NULL, 10);
 	    }
 
@@ -594,13 +596,17 @@ TypeCheckBase::parse_repr_options (const AST::AttrVec &attrs, location_t locus)
 	  else if (is_integer)
 	    {
 	      repr.repr_kind = TyTy::ADTType::ReprKind::INT;
-	      bool ok = context->lookup_builtin (inline_option, &repr.repr);
+	      bool ok = context->lookup_builtin (repr_option, &repr.repr);
 	      if (!ok)
 		{
 		  rust_error_at (attr.get_locus (), ErrorCode::E0552,
 				 "unrecognized representation hint");
 		}
 	    }
+	  else if (is_simd)
+	    {
+	      repr.repr_kind = TyTy::ADTType::ReprKind::SIMD;
+	    }
 	  else
 	    {
 	      rust_error_at (attr.get_locus (), ErrorCode::E0552,
diff --git a/gcc/rust/typecheck/rust-hir-type-check-item.cc b/gcc/rust/typecheck/rust-hir-type-check-item.cc
index 44d5434b126..112ba0b4165 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-item.cc
+++ b/gcc/rust/typecheck/rust-hir-type-check-item.cc
@@ -265,6 +265,11 @@ TypeCheckItem::visit (HIR::TupleStruct &struct_decl)
       ResolveWhereClauseItem::Resolve (*where_clause_item, region_constraints);
     }
 
+  // Process #[repr(X)] attribute, if any
+  const AST::AttrVec &attrs = struct_decl.get_outer_attrs ();
+  TyTy::ADTType::ReprOptions repr
+    = parse_repr_options (attrs, struct_decl.get_locus ());
+
   std::vector<TyTy::StructFieldType *> fields;
   size_t idx = 0;
   for (auto &field : struct_decl.get_fields ())
@@ -280,6 +285,13 @@ TypeCheckItem::visit (HIR::TupleStruct &struct_decl)
       idx++;
     }
 
+  if (repr.repr_kind == TyTy::ADTType::ReprKind::SIMD)
+    {
+      bool is_valid = validate_repr_simd (fields, struct_decl.get_locus ());
+      if (!is_valid)
+	return;
+    }
+
   // get the path
 
   auto &nr_ctx = Resolver2_0::FinalizedNameResolutionContext::get ();
@@ -299,11 +311,6 @@ TypeCheckItem::visit (HIR::TupleStruct &struct_decl)
 			  TyTy::VariantDef::VariantType::TUPLE, tl::nullopt,
 			  std::move (fields)));
 
-  // Process #[repr(X)] attribute, if any
-  const AST::AttrVec &attrs = struct_decl.get_outer_attrs ();
-  TyTy::ADTType::ReprOptions repr
-    = parse_repr_options (attrs, struct_decl.get_locus ());
-
   auto *type = new TyTy::ADTType (
     struct_decl.get_mappings ().get_defid (),
     struct_decl.get_mappings ().get_hirid (),
@@ -365,6 +372,12 @@ TypeCheckItem::visit (HIR::StructStruct &struct_decl)
       context->insert_type (field.get_mappings (), ty_field->get_field_type ());
     }
 
+  if (repr.repr_kind == TyTy::ADTType::ReprKind::SIMD)
+    {
+      bool is_valid = validate_repr_simd (fields, struct_decl.get_locus ());
+      if (!is_valid)
+	return;
+    }
   if (repr.repr_kind == TyTy::ADTType::ReprKind::TRANSPARENT)
     {
       size_t num_non_zst = 0;
@@ -986,5 +999,116 @@ TypeCheckItem::resolve_impl_block_self (HIR::ImplBlock &impl_block)
   return TypeCheckType::Resolve (impl_block.get_type ());
 }
 
+bool
+TypeCheckItem::validate_repr_simd (
+  const std::vector<TyTy::StructFieldType *> &fields, location_t locus)
+{
+  if (fields.empty ())
+    {
+      rust_error_at (locus, ErrorCode::E0075, "SIMD vector cannot be empty");
+      return false;
+    }
+
+  // in 1.49, repr simd assumes all fields are same type with its size
+  // being power-of-two.
+  //
+  // TODO update this typecheck to make repr simd take in a single field
+  // of an array instead when we move past 1.49. Relevant Rust github
+  // issues/PRs:
+  // - https://github.com/rust-lang/compiler-team/issues/621
+  // - https://github.com/rust-lang/rust/pull/78863 (implemented
+  //   for 1.50.0)
+
+  TyTy::BaseType *first_field_ty = fields.at (0)->get_field_type ();
+  TyTy::TypeKind ty_kind = first_field_ty->get_kind ();
+  bool fields_are_same_type = true;
+
+  switch (ty_kind)
+    {
+    case TyTy::TypeKind::INT:
+      {
+	auto int_ty = static_cast<TyTy::IntType *> (first_field_ty);
+	auto int_kind = int_ty->get_int_kind ();
+	for (const auto field : fields)
+	  {
+	    if (field->get_field_type ()->get_kind () != ty_kind)
+	      {
+		fields_are_same_type = false;
+		break;
+	      }
+	    auto field_int_ty
+	      = static_cast<TyTy::IntType *> (field->get_field_type ());
+	    if (field_int_ty->get_int_kind () != int_kind)
+	      {
+		fields_are_same_type = false;
+		break;
+	      }
+	  }
+	break;
+      }
+    case TyTy::TypeKind::UINT:
+      {
+	auto uint_ty = static_cast<TyTy::UintType *> (first_field_ty);
+	auto uint_kind = uint_ty->get_uint_kind ();
+	for (const auto field : fields)
+	  {
+	    if (field->get_field_type ()->get_kind () != ty_kind)
+	      {
+		fields_are_same_type = false;
+		break;
+	      }
+	    auto field_uint_ty
+	      = static_cast<TyTy::UintType *> (field->get_field_type ());
+	    if (field_uint_ty->get_uint_kind () != uint_kind)
+	      {
+		fields_are_same_type = false;
+		break;
+	      }
+	  }
+	break;
+      }
+    case TyTy::TypeKind::FLOAT:
+      {
+	auto float_ty = static_cast<TyTy::FloatType *> (first_field_ty);
+	auto float_kind = float_ty->get_float_kind ();
+	for (const auto field : fields)
+	  {
+	    if (field->get_field_type ()->get_kind () != ty_kind)
+	      {
+		fields_are_same_type = false;
+		break;
+	      }
+	    auto field_float_ty
+	      = static_cast<TyTy::FloatType *> (field->get_field_type ());
+	    if (field_float_ty->get_float_kind () != float_kind)
+	      {
+		fields_are_same_type = false;
+		break;
+	      }
+	  }
+	break;
+      }
+    default:
+      rust_error_at (locus, ErrorCode::E0077,
+		     "SIMD vector element type should be a primitive scalar");
+      return false;
+    }
+
+  if (!fields_are_same_type)
+    {
+      rust_error_at (locus, "SIMD struct fields should be of the same type");
+      return false;
+    }
+
+  // check whether field count is power of 2
+  size_t field_count = fields.size ();
+  if ((field_count & (field_count - 1)) != 0)
+    {
+      rust_error_at (locus, "Size of SIMD struct must be a power of 2");
+      return false;
+    }
+  return true;
+}
+
 } // namespace Resolver
 } // namespace Rust
diff --git a/gcc/rust/typecheck/rust-hir-type-check-item.h b/gcc/rust/typecheck/rust-hir-type-check-item.h
index 1f8cddef4fe..52be8e2f680 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-item.h
+++ b/gcc/rust/typecheck/rust-hir-type-check-item.h
@@ -88,6 +88,9 @@ protected:
 
   TyTy::BaseType *resolve_impl_block_self (HIR::ImplBlock &impl_block);
 
+  bool validate_repr_simd (const std::vector<TyTy::StructFieldType *> &fields,
+			   location_t locus);
+
 private:
   TypeCheckItem ();
 
diff --git a/gcc/rust/typecheck/rust-tyty.h b/gcc/rust/typecheck/rust-tyty.h
index 3eaf23a3ce8..d607c6b4963 100644
--- a/gcc/rust/typecheck/rust-tyty.h
+++ b/gcc/rust/typecheck/rust-tyty.h
@@ -921,7 +921,7 @@ public:
     ALIGN,
     PACKED,
     TRANSPARENT,
-    // SIMD,
+    SIMD,
     // ...
   };
 
diff --git a/gcc/testsuite/rust/compile/repr_simd.rs b/gcc/testsuite/rust/compile/repr_simd.rs
new file mode 100644
index 00000000000..c8f4dc382f3
--- /dev/null
+++ b/gcc/testsuite/rust/compile/repr_simd.rs
@@ -0,0 +1,35 @@
+#![feature(no_core, repr_simd)]
+#![no_core]
+
+// all simd types declared in stdarch/crates/core_arch/src/x86/mod.rs
+#[repr(simd)]
+pub struct __m128i(i64, i64);
+#[repr(simd)]
+pub struct __m128(f32, f32, f32, f32);
+#[repr(simd)]
+pub struct __m128d(f64, f64);
+#[repr(simd)]
+pub struct __m256i(i64, i64, i64, i64);
+#[repr(simd)]
+pub struct __m256(f32, f32, f32, f32, f32, f32, f32, f32);
+#[repr(simd)]
+pub struct __m256d(f64, f64, f64, f64);
+#[repr(simd)]
+pub struct __m512i(i64, i64, i64, i64, i64, i64, i64, i64);
+#[repr(simd)]
+pub struct __m512(
+    f32, f32, f32, f32, f32, f32, f32, f32,
+    f32, f32, f32, f32, f32, f32, f32, f32,
+);
+#[repr(simd)]
+pub struct __m512d(f64, f64, f64, f64, f64, f64, f64, f64);
+
+// errorneous simd types
+#[repr(simd)]
+pub struct Foo (f32, f32, f32); // { dg-error "Size of SIMD struct must be a power of 2" }
+#[repr(simd)]
+pub struct Bar (f32, i32); // { dg-error "SIMD struct fields should be of the same type" }
+#[repr(simd)]
+pub struct Baz (str); // { dg-error "SIMD vector element type should be a primitive scalar .E0077." }
+
+fn main() {}
\ No newline at end of file
-- 
2.50.1



More information about the Gcc-rust mailing list