[spirv-writer] Allow emitting an array stride.

This CL adds the ability to attach a stride to an array type and have it
emitted during SPIR-V generation.

Bug: tint:5
Change-Id: I9c0f0a6afef6ae6662b64f4da2c150ba3f8da29f
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/23223
Reviewed-by: David Neto <dneto@google.com>
diff --git a/src/ast/type/array_type.cc b/src/ast/type/array_type.cc
index 2c840d3..6fb5f95 100644
--- a/src/ast/type/array_type.cc
+++ b/src/ast/type/array_type.cc
@@ -37,6 +37,8 @@
   std::string type_name = "__array" + subtype_->type_name();
   if (!IsRuntimeArray())
     type_name += "_" + std::to_string(size_);
+  if (has_array_stride())
+    type_name += "_" + std::to_string(array_stride_);
 
   return type_name;
 }
diff --git a/src/ast/type/array_type.h b/src/ast/type/array_type.h
index 7bc8023..e89684c 100644
--- a/src/ast/type/array_type.h
+++ b/src/ast/type/array_type.h
@@ -45,6 +45,14 @@
   /// i.e. the size is determined at runtime
   bool IsRuntimeArray() const { return size_ == 0; }
 
+  /// Sets the array stride
+  /// @param stride the stride to set
+  void set_array_stride(uint32_t stride) { array_stride_ = stride; }
+  /// @returns the array stride or 0 if none set.
+  uint32_t array_stride() const { return array_stride_; }
+  /// @returns true if the array has a stride set
+  bool has_array_stride() const { return array_stride_ != 0; }
+
   /// @returns the array type
   Type* type() const { return subtype_; }
   /// @returns the array size. Size is 0 for a runtime array
@@ -56,6 +64,7 @@
  private:
   Type* subtype_ = nullptr;
   uint32_t size_ = 0;
+  uint32_t array_stride_ = 0;
 };
 
 }  // namespace type
diff --git a/src/ast/type/array_type_test.cc b/src/ast/type/array_type_test.cc
index b7ff819..a4e4eae 100644
--- a/src/ast/type/array_type_test.cc
+++ b/src/ast/type/array_type_test.cc
@@ -61,10 +61,23 @@
 
 TEST_F(ArrayTypeTest, TypeName) {
   I32Type i32;
+  ArrayType arr{&i32};
+  EXPECT_EQ(arr.type_name(), "__array__i32");
+}
+
+TEST_F(ArrayTypeTest, TypeName_RuntimeArray) {
+  I32Type i32;
   ArrayType arr{&i32, 3};
   EXPECT_EQ(arr.type_name(), "__array__i32_3");
 }
 
+TEST_F(ArrayTypeTest, TypeName_WithStride) {
+  I32Type i32;
+  ArrayType arr{&i32, 3};
+  arr.set_array_stride(16);
+  EXPECT_EQ(arr.type_name(), "__array__i32_3_16");
+}
+
 }  // namespace
 }  // namespace type
 }  // namespace ast
diff --git a/src/ast/type/struct_type.h b/src/ast/type/struct_type.h
index 18ce296..97cb4f2 100644
--- a/src/ast/type/struct_type.h
+++ b/src/ast/type/struct_type.h
@@ -41,6 +41,11 @@
   /// @returns the struct name
   const std::string& name() const { return name_; }
 
+  /// @returns true if the struct has a block decoration
+  bool IsBlockDecorated() const {
+    return struct_->decoration() == StructDecoration::kBlock;
+  }
+
   /// @returns true if the type is a struct type
   bool IsStruct() const override;
 
diff --git a/src/writer/spirv/builder.cc b/src/writer/spirv/builder.cc
index cf3ee87..607f2ce 100644
--- a/src/writer/spirv/builder.cc
+++ b/src/writer/spirv/builder.cc
@@ -1681,6 +1681,7 @@
     return false;
   }
 
+  auto result_id = result.to_i();
   if (ary->IsRuntimeArray()) {
     push_type(spv::Op::OpTypeRuntimeArray, {result, Operand::Int(elem_type)});
   } else {
@@ -1692,6 +1693,17 @@
     push_type(spv::Op::OpTypeArray,
               {result, Operand::Int(elem_type), Operand::Int(len_id)});
   }
+
+  // SPIR-V explicitly requires no array stride if the array contains a struct
+  // which has a Block decoration.
+  if (ary->type()->IsStruct() && ary->type()->AsStruct()->IsBlockDecorated()) {
+    return true;
+  }
+  if (ary->has_array_stride()) {
+    push_annot(spv::Op::OpDecorate,
+               {Operand::Int(result_id), Operand::Int(SpvDecorationArrayStride),
+                Operand::Int(ary->array_stride())});
+  }
   return true;
 }
 
diff --git a/src/writer/spirv/builder_type_test.cc b/src/writer/spirv/builder_type_test.cc
index b49e987..b554b31 100644
--- a/src/writer/spirv/builder_type_test.cc
+++ b/src/writer/spirv/builder_type_test.cc
@@ -118,6 +118,27 @@
 )");
 }
 
+TEST_F(BuilderTest_Type, GenerateArray_WithStride) {
+  ast::type::I32Type i32;
+  ast::type::ArrayType ary(&i32, 4);
+  ary.set_array_stride(16u);
+
+  ast::Module mod;
+  Builder b(&mod);
+  auto id = b.GenerateTypeIfNeeded(&ary);
+  ASSERT_FALSE(b.has_error()) << b.error();
+  EXPECT_EQ(1u, id);
+
+  EXPECT_EQ(DumpInstructions(b.annots()), R"(OpDecorate %1 ArrayStride 16
+)");
+
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeInt 32 1
+%3 = OpTypeInt 32 0
+%4 = OpConstant %3 4
+%1 = OpTypeArray %2 %4
+)");
+}
+
 TEST_F(BuilderTest_Type, ReturnsGeneratedArray) {
   ast::type::I32Type i32;
   ast::type::ArrayType ary(&i32, 4);