[val] Explicit layout refactor (#6792)

Fixes #6780

* Combine layout checks into a single pass to share logic
  * checks both required explicit layouts and invalid layouts
  * remove checks from validate_decorations.cpp
* Introduce enums for layout modes and requirements
* Centralize which instructions are examined for layouts
* Add options to specify descriptor sizes to the validator (buffer,
sampler, image, and tensor)
diff --git a/Android.mk b/Android.mk
index 4d091d6..31c31ae 100644
--- a/Android.mk
+++ b/Android.mk
@@ -54,6 +54,7 @@
 		source/val/validate_decorations.cpp \
 		source/val/validate_derivatives.cpp \
 		source/val/validate_dot_product.cpp \
+		source/val/validate_explicit_layout.cpp \
 		source/val/validate_extensions.cpp \
 		source/val/validate_execution_limitations.cpp \
 		source/val/validate_function.cpp \
diff --git a/BUILD.gn b/BUILD.gn
index 7443aa5..a6672fd 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -366,6 +366,7 @@
     "source/val/validate_derivatives.cpp",
     "source/val/validate_dot_product.cpp",
     "source/val/validate_execution_limitations.cpp",
+    "source/val/validate_explicit_layout.cpp",
     "source/val/validate_extensions.cpp",
     "source/val/validate_function.cpp",
     "source/val/validate_graph.cpp",
diff --git a/include/spirv-tools/libspirv.h b/include/spirv-tools/libspirv.h
index 0c5b129..bfbdb31 100644
--- a/include/spirv-tools/libspirv.h
+++ b/include/spirv-tools/libspirv.h
@@ -788,6 +788,24 @@
 SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetFriendlyNames(
     spv_validator_options options, bool val);
 
+// Sets custom size and alignment for buffer and acceleration structure
+// descriptor heap resources.
+SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetBufferDescriptorLayout(
+    spv_validator_options options, uint32_t size, uint32_t alignment);
+
+// Sets custom size and alignment for image and sampled image descriptor heap
+// resources.
+SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetImageDescriptorLayout(
+    spv_validator_options options, uint32_t size, uint32_t alignment);
+
+// Sets custom size and alignment for sampler descriptor heap resources.
+SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetSamplerDescriptorLayout(
+    spv_validator_options options, uint32_t size, uint32_t alignment);
+
+// Sets custom size and alignment for tensor descriptor heap resources.
+SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetTensorDescriptorLayout(
+    spv_validator_options options, uint32_t size, uint32_t alignment);
+
 // Creates an optimizer options object with default options. Returns a valid
 // options object. The object remains valid until it is passed into
 // |spvOptimizerOptionsDestroy|.
diff --git a/include/spirv-tools/libspirv.hpp b/include/spirv-tools/libspirv.hpp
index 1b3ed86..b82fa50 100644
--- a/include/spirv-tools/libspirv.hpp
+++ b/include/spirv-tools/libspirv.hpp
@@ -138,6 +138,28 @@
     spvValidatorOptionsSetAllowVulkan32BitBitwise(options_, val);
   }
 
+  // Sets custom size and alignment for buffer and acceleration structure
+  // descriptor heap resources.
+  void SetBufferDescriptorLayout(uint32_t size, uint32_t alignment) {
+    spvValidatorOptionsSetBufferDescriptorLayout(options_, size, alignment);
+  }
+
+  // Sets custom size and alignment for image and sampled image descriptor heap
+  // resources.
+  void SetImageDescriptorLayout(uint32_t size, uint32_t alignment) {
+    spvValidatorOptionsSetImageDescriptorLayout(options_, size, alignment);
+  }
+
+  // Sets custom size and alignment for sampler descriptor heap resources.
+  void SetSamplerDescriptorLayout(uint32_t size, uint32_t alignment) {
+    spvValidatorOptionsSetSamplerDescriptorLayout(options_, size, alignment);
+  }
+
+  // Sets custom size and alignment for tensor descriptor heap resources.
+  void SetTensorDescriptorLayout(uint32_t size, uint32_t alignment) {
+    spvValidatorOptionsSetTensorDescriptorLayout(options_, size, alignment);
+  }
+
   // Records whether or not the validator should relax the rules on pointer
   // usage in logical addressing mode.
   //
diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt
index dbe6090..538ae6a 100644
--- a/source/CMakeLists.txt
+++ b/source/CMakeLists.txt
@@ -263,6 +263,7 @@
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_dot_product.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_extensions.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_execution_limitations.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_explicit_layout.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_function.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_graph.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_group.cpp
diff --git a/source/spirv_validator_options.cpp b/source/spirv_validator_options.cpp
index a9591f6..bbe6703 100644
--- a/source/spirv_validator_options.cpp
+++ b/source/spirv_validator_options.cpp
@@ -140,3 +140,30 @@
                                          bool val) {
   options->use_friendly_names = val;
 }
+
+void spvValidatorOptionsSetBufferDescriptorLayout(spv_validator_options options,
+                                                  uint32_t size,
+                                                  uint32_t alignment) {
+  options->buffer_descriptor_layout.size = size;
+  options->buffer_descriptor_layout.alignment = alignment;
+}
+
+void spvValidatorOptionsSetImageDescriptorLayout(spv_validator_options options,
+                                                 uint32_t size,
+                                                 uint32_t alignment) {
+  options->image_descriptor_layout.size = size;
+  options->image_descriptor_layout.alignment = alignment;
+}
+
+void spvValidatorOptionsSetSamplerDescriptorLayout(
+    spv_validator_options options, uint32_t size, uint32_t alignment) {
+  options->sampler_descriptor_layout.size = size;
+  options->sampler_descriptor_layout.alignment = alignment;
+}
+
+void spvValidatorOptionsSetTensorDescriptorLayout(spv_validator_options options,
+                                                  uint32_t size,
+                                                  uint32_t alignment) {
+  options->tensor_descriptor_layout.size = size;
+  options->tensor_descriptor_layout.alignment = alignment;
+}
diff --git a/source/spirv_validator_options.h b/source/spirv_validator_options.h
index 9f0c125..131d80d 100644
--- a/source/spirv_validator_options.h
+++ b/source/spirv_validator_options.h
@@ -35,6 +35,11 @@
   uint32_t max_id_bound{0x3FFFFF};
 };
 
+struct OpaqueResourceLayout {
+  uint32_t size{0};
+  uint32_t alignment{1};
+};
+
 // Manages command line options passed to the SPIR-V Validator. New struct
 // members may be added for any new option.
 struct spv_validator_options_t {
@@ -66,6 +71,11 @@
   bool allow_vulkan_32_bit_bitwise;
   bool before_hlsl_legalization;
   bool use_friendly_names;
+
+  OpaqueResourceLayout buffer_descriptor_layout;
+  OpaqueResourceLayout image_descriptor_layout;
+  OpaqueResourceLayout sampler_descriptor_layout;
+  OpaqueResourceLayout tensor_descriptor_layout;
 };
 
 #endif  // SOURCE_SPIRV_VALIDATOR_OPTIONS_H_
diff --git a/source/val/validate.cpp b/source/val/validate.cpp
index b3eab0c..c2b67c5 100644
--- a/source/val/validate.cpp
+++ b/source/val/validate.cpp
@@ -427,6 +427,7 @@
   if (auto error = PerformCfgChecks(*vstate)) return error;
   if (auto error = CheckIdDefinitionDominateUse(*vstate)) return error;
   if (auto error = ValidateDecorations(*vstate)) return error;
+  if (auto error = ValidateExplicitLayout(*vstate)) return error;
   if (auto error = ValidateInterfaces(*vstate)) return error;
   // TODO(dsinclair): Restructure ValidateBuiltins so we can move into the
   // for() above as it loops over all ordered_instructions internally.
diff --git a/source/val/validate.h b/source/val/validate.h
index 717fb34..35a6524 100644
--- a/source/val/validate.h
+++ b/source/val/validate.h
@@ -144,6 +144,8 @@
 /// has been propagated down to the group members.
 spv_result_t ValidateDecorations(ValidationState_t& _);
 
+spv_result_t ValidateExplicitLayout(ValidationState_t& _);
+
 /// Performs validation of built-in variables.
 spv_result_t ValidateBuiltIns(ValidationState_t& _);
 
diff --git a/source/val/validate_decorations.cpp b/source/val/validate_decorations.cpp
index 623a470..9b1d66d 100644
--- a/source/val/validate_decorations.cpp
+++ b/source/val/validate_decorations.cpp
@@ -35,43 +35,6 @@
 namespace val {
 namespace {
 
-// Distinguish between row and column major matrix layouts.
-enum MatrixLayout { kRowMajor, kColumnMajor };
-
-// A functor for hashing a pair of integers.
-struct PairHash {
-  std::size_t operator()(const std::pair<uint32_t, uint32_t> pair) const {
-    const uint32_t a = pair.first;
-    const uint32_t b = pair.second;
-    const uint32_t rotated_b = (b >> 2) | ((b & 3) << 30);
-    return a ^ rotated_b;
-  }
-};
-
-// Struct member layout attributes that are inherited through arrays.
-struct LayoutConstraints {
-  explicit LayoutConstraints(
-      MatrixLayout the_majorness = MatrixLayout::kColumnMajor,
-      uint32_t stride = 0)
-      : majorness(the_majorness), matrix_stride(stride) {}
-  MatrixLayout majorness;
-  uint32_t matrix_stride;
-};
-
-// A type for mapping (struct id, member id) to layout constraints.
-using MemberConstraints = std::unordered_map<std::pair<uint32_t, uint32_t>,
-                                             LayoutConstraints, PairHash>;
-
-// Returns the array stride of the given array type.
-uint32_t GetArrayStride(uint32_t array_id, ValidationState_t& vstate) {
-  for (auto& decoration : vstate.id_decorations(array_id)) {
-    if (spv::Decoration::ArrayStride == decoration.dec_type()) {
-      return decoration.params()[0];
-    }
-  }
-  return 0;
-}
-
 // Returns true if the given structure type has a Block decoration.
 bool isBlock(uint32_t struct_id, ValidationState_t& vstate) {
   const auto& decorations = vstate.id_decorations(struct_id);
@@ -111,612 +74,6 @@
   return members;
 }
 
-// Returns whether the given structure is missing Offset decoration for any
-// member. Handles also nested structures.
-bool isMissingOffsetInStruct(uint32_t struct_id, ValidationState_t& vstate) {
-  const auto* inst = vstate.FindDef(struct_id);
-  std::vector<bool> hasOffset;
-  std::vector<uint32_t> struct_members;
-  if (inst->opcode() == spv::Op::OpTypeStruct) {
-    // Check offsets of member decorations.
-    struct_members = getStructMembers(struct_id, vstate);
-    hasOffset.resize(struct_members.size(), false);
-
-    for (auto& decoration : vstate.id_decorations(struct_id)) {
-      if ((spv::Decoration::Offset == decoration.dec_type() ||
-           spv::Decoration::OffsetIdEXT == decoration.dec_type()) &&
-          Decoration::kInvalidMember != decoration.struct_member_index()) {
-        // Offset 0xffffffff is not valid so ignore it for simplicity's sake.
-        if (decoration.params()[0] == 0xffffffff) return true;
-        hasOffset[decoration.struct_member_index()] = true;
-      }
-    }
-  } else if (inst->opcode() == spv::Op::OpTypeArray ||
-             inst->opcode() == spv::Op::OpTypeRuntimeArray) {
-    hasOffset.resize(1, true);
-    struct_members.push_back(inst->GetOperandAs<uint32_t>(1u));
-  }
-  // Look through nested structs (which may be in an array).
-  bool nestedStructsMissingOffset = false;
-  for (auto id : struct_members) {
-    if (isMissingOffsetInStruct(id, vstate)) {
-      nestedStructsMissingOffset = true;
-      break;
-    }
-  }
-  return nestedStructsMissingOffset ||
-         !std::all_of(hasOffset.begin(), hasOffset.end(),
-                      [](const bool b) { return b; });
-}
-
-// Rounds x up to the next alignment. Assumes alignment is a power of two.
-uint32_t align(uint32_t x, uint32_t alignment) {
-  return (x + alignment - 1) & ~(alignment - 1);
-}
-
-// Returns base alignment of struct member. If |roundUp| is true, also
-// ensure that structs, arrays, and matrices are aligned at least to a
-// multiple of 16 bytes.  (That is, when roundUp is true, this function
-// returns the *extended* alignment as it's called by the Vulkan spec.)
-uint32_t getBaseAlignment(uint32_t member_id, bool roundUp,
-                          const LayoutConstraints& inherited,
-                          MemberConstraints& constraints,
-                          ValidationState_t& vstate) {
-  const auto inst = vstate.FindDef(member_id);
-  const auto& words = inst->words();
-  // Minimal alignment is byte-aligned.
-  uint32_t baseAlignment = 1;
-  switch (inst->opcode()) {
-    case spv::Op::OpTypeSampledImage:
-    case spv::Op::OpTypeSampler:
-    case spv::Op::OpTypeImage:
-      if (vstate.HasCapability(spv::Capability::BindlessTextureNV))
-        return vstate.samplerimage_variable_address_mode() / 8;
-      // SPV_EXT_descriptor_heap provides a way to access opaque images, we
-      // assume alignment is validated at runtime as it is determined by the
-      // client API
-      if (vstate.HasCapability(spv::Capability::DescriptorHeapEXT)) return 1;
-      assert(0);
-      return 0;
-    case spv::Op::OpTypeInt:
-    case spv::Op::OpTypeFloat:
-      baseAlignment = words[2] / 8;
-      break;
-    case spv::Op::OpTypeVector: {
-      const auto componentId = words[2];
-      const auto numComponents = words[3];
-      const auto componentAlignment = getBaseAlignment(
-          componentId, roundUp, inherited, constraints, vstate);
-      baseAlignment =
-          componentAlignment *
-          ((numComponents == 3 || numComponents > 4) ? 4 : numComponents);
-      break;
-    }
-    case spv::Op::OpTypeVectorIdEXT: {
-      const auto componentId = words[2];
-      const auto numComponents = vstate.GetDimension(inst->id());
-      assert(numComponents != 0);
-      const auto componentAlignment = getBaseAlignment(
-          componentId, roundUp, inherited, constraints, vstate);
-      baseAlignment =
-          componentAlignment *
-          ((numComponents == 3 || numComponents > 4) ? 4 : numComponents);
-      break;
-    }
-    case spv::Op::OpTypeMatrix: {
-      const auto column_type = words[2];
-      if (inherited.majorness == kColumnMajor) {
-        baseAlignment = getBaseAlignment(column_type, roundUp, inherited,
-                                         constraints, vstate);
-      } else {
-        // A row-major matrix of C columns has a base alignment equal to the
-        // base alignment of a vector of C matrix components.
-        const auto num_columns = words[3];
-        const auto component_inst = vstate.FindDef(column_type);
-        const auto component_id = component_inst->words()[2];
-        const auto componentAlignment = getBaseAlignment(
-            component_id, roundUp, inherited, constraints, vstate);
-        baseAlignment =
-            componentAlignment * (num_columns == 3 ? 4 : num_columns);
-      }
-      if (roundUp) baseAlignment = align(baseAlignment, 16u);
-    } break;
-    case spv::Op::OpTypeArray:
-    case spv::Op::OpTypeRuntimeArray:
-      baseAlignment =
-          getBaseAlignment(words[2], roundUp, inherited, constraints, vstate);
-      if (roundUp) baseAlignment = align(baseAlignment, 16u);
-      break;
-    case spv::Op::OpTypeStruct: {
-      const auto members = getStructMembers(member_id, vstate);
-      for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size());
-           memberIdx < numMembers; ++memberIdx) {
-        const auto id = members[memberIdx];
-        const auto& constraint =
-            constraints[std::make_pair(member_id, memberIdx)];
-        baseAlignment = std::max(
-            baseAlignment,
-            getBaseAlignment(id, roundUp, constraint, constraints, vstate));
-      }
-      if (roundUp) baseAlignment = align(baseAlignment, 16u);
-      break;
-    }
-    case spv::Op::OpTypePointer:
-    case spv::Op::OpTypeUntypedPointerKHR:
-      baseAlignment = vstate.pointer_size_and_alignment();
-      break;
-    default:
-      assert(0);
-      break;
-  }
-
-  return baseAlignment;
-}
-
-// Returns scalar alignment of a type.
-uint32_t getScalarAlignment(uint32_t type_id, ValidationState_t& vstate) {
-  const auto inst = vstate.FindDef(type_id);
-  const auto& words = inst->words();
-  switch (inst->opcode()) {
-    case spv::Op::OpTypeSampledImage:
-    case spv::Op::OpTypeSampler:
-    case spv::Op::OpTypeImage:
-      if (vstate.HasCapability(spv::Capability::BindlessTextureNV))
-        return vstate.samplerimage_variable_address_mode() / 8;
-      // SPV_EXT_descriptor_heap provides a way to access opaque images, we
-      // assume alignment is validated at runtime as it is determined by the
-      // client API
-      if (vstate.HasCapability(spv::Capability::DescriptorHeapEXT)) return 1;
-      assert(0);
-      return 0;
-    case spv::Op::OpTypeInt:
-    case spv::Op::OpTypeFloat:
-      return words[2] / 8;
-    case spv::Op::OpTypeVector:
-    case spv::Op::OpTypeVectorIdEXT:
-    case spv::Op::OpTypeMatrix:
-    case spv::Op::OpTypeArray:
-    case spv::Op::OpTypeRuntimeArray: {
-      const auto compositeMemberTypeId = words[2];
-      return getScalarAlignment(compositeMemberTypeId, vstate);
-    }
-    case spv::Op::OpTypeStruct: {
-      const auto members = getStructMembers(type_id, vstate);
-      uint32_t max_member_alignment = 1;
-      for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size());
-           memberIdx < numMembers; ++memberIdx) {
-        const auto id = members[memberIdx];
-        uint32_t member_alignment = getScalarAlignment(id, vstate);
-        if (member_alignment > max_member_alignment) {
-          max_member_alignment = member_alignment;
-        }
-      }
-      return max_member_alignment;
-    } break;
-    case spv::Op::OpTypePointer:
-    case spv::Op::OpTypeUntypedPointerKHR:
-      return vstate.pointer_size_and_alignment();
-    default:
-      assert(0);
-      break;
-  }
-
-  return 1;
-}
-
-// Returns size of a struct member. Doesn't include padding at the end of struct
-// or array.  Assumes that in the struct case, all members have offsets.
-uint32_t getSize(uint32_t member_id, const LayoutConstraints& inherited,
-                 MemberConstraints& constraints, ValidationState_t& vstate) {
-  const auto inst = vstate.FindDef(member_id);
-  const auto& words = inst->words();
-  switch (inst->opcode()) {
-    case spv::Op::OpTypeSampledImage:
-    case spv::Op::OpTypeSampler:
-    case spv::Op::OpTypeImage:
-      if (vstate.HasCapability(spv::Capability::BindlessTextureNV))
-        return vstate.samplerimage_variable_address_mode() / 8;
-      // SPV_EXT_descriptor_heap provides a way to access opaque images, we
-      // assume alignment is validated at runtime as it is determined by the
-      // client API
-      if (vstate.HasCapability(spv::Capability::DescriptorHeapEXT)) return 1;
-      assert(0);
-      return 0;
-    case spv::Op::OpTypeInt:
-    case spv::Op::OpTypeFloat:
-      return words[2] / 8;
-    case spv::Op::OpTypeVector: {
-      const auto componentId = words[2];
-      const auto numComponents = words[3];
-      const auto componentSize =
-          getSize(componentId, inherited, constraints, vstate);
-      const auto size = componentSize * numComponents;
-      return size;
-    }
-    case spv::Op::OpTypeVectorIdEXT: {
-      const auto componentId = words[2];
-      const auto numComponents = vstate.GetDimension(inst->id());
-      assert(numComponents != 0);
-      const auto componentSize =
-          getSize(componentId, inherited, constraints, vstate);
-      const auto size = componentSize * numComponents;
-      return size;
-    }
-    case spv::Op::OpTypeArray: {
-      const auto sizeInst = vstate.FindDef(words[3]);
-      if (spvOpcodeIsSpecConstant(sizeInst->opcode())) return 0;
-      assert(spv::Op::OpConstant == sizeInst->opcode());
-      const uint32_t num_elem = sizeInst->words()[3];
-      const uint32_t elem_type = words[2];
-      const uint32_t elem_size =
-          getSize(elem_type, inherited, constraints, vstate);
-      // Account for gaps due to alignments in the first N-1 elements,
-      // then add the size of the last element.
-      const auto size =
-          (num_elem - 1) * GetArrayStride(member_id, vstate) + elem_size;
-      return size;
-    }
-    case spv::Op::OpTypeRuntimeArray:
-      return 0;
-    case spv::Op::OpTypeMatrix: {
-      const auto num_columns = words[3];
-      if (inherited.majorness == kColumnMajor) {
-        return num_columns * inherited.matrix_stride;
-      } else {
-        // Row major case.
-        const auto column_type = words[2];
-        const auto component_inst = vstate.FindDef(column_type);
-        const auto num_rows = component_inst->words()[3];
-        const auto scalar_elem_type = component_inst->words()[2];
-        const uint32_t scalar_elem_size =
-            getSize(scalar_elem_type, inherited, constraints, vstate);
-        return (num_rows - 1) * inherited.matrix_stride +
-               num_columns * scalar_elem_size;
-      }
-    }
-    case spv::Op::OpTypeStruct: {
-      const auto& members = getStructMembers(member_id, vstate);
-      if (members.empty()) return 0;
-      const auto lastIdx = uint32_t(members.size() - 1);
-      const auto& lastMember = members.back();
-      uint32_t offset = 0xffffffff;
-      // Find the offset of the last element and add the size.
-      auto member_decorations =
-          vstate.id_member_decorations(member_id, lastIdx);
-      for (auto decoration = member_decorations.begin;
-           decoration != member_decorations.end; ++decoration) {
-        assert(decoration->struct_member_index() == (int)lastIdx);
-        if (spv::Decoration::Offset == decoration->dec_type()) {
-          offset = decoration->params()[0];
-        }
-      }
-      // This check depends on the fact that all members have offsets.  This
-      // has been checked earlier in the flow.
-      assert(offset != 0xffffffff);
-      const auto& constraint = constraints[std::make_pair(lastMember, lastIdx)];
-      return offset + getSize(lastMember, constraint, constraints, vstate);
-    }
-    case spv::Op::OpTypePointer:
-    case spv::Op::OpTypeUntypedPointerKHR:
-      return vstate.pointer_size_and_alignment();
-    default:
-      assert(0);
-      return 0;
-  }
-}
-
-// A member is defined to improperly straddle if either of the following are
-// true:
-// - It is a vector with total size less than or equal to 16 bytes, and has
-// Offset decorations placing its first byte at F and its last byte at L, where
-// floor(F / 16) != floor(L / 16).
-// - It is a vector with total size greater than 16 bytes and has its Offset
-// decorations placing its first byte at a non-integer multiple of 16.
-bool hasImproperStraddle(uint32_t id, uint32_t offset,
-                         const LayoutConstraints& inherited,
-                         MemberConstraints& constraints,
-                         ValidationState_t& vstate) {
-  const auto size = getSize(id, inherited, constraints, vstate);
-  const auto F = offset;
-  const auto L = offset + size - 1;
-  if (size <= 16) {
-    if ((F >> 4) != (L >> 4)) return true;
-  } else {
-    if (F % 16 != 0) return true;
-  }
-  return false;
-}
-
-// Returns true if |offset| satsifies an alignment to |alignment|.  In the case
-// of |alignment| of zero, the |offset| must also be zero.
-bool IsAlignedTo(uint32_t offset, uint32_t alignment) {
-  if (alignment == 0) return offset == 0;
-  return 0 == (offset % alignment);
-}
-
-// Returns SPV_SUCCESS if the given struct satisfies standard layout rules for
-// Block or BufferBlocks in Vulkan.  Otherwise emits a diagnostic and returns
-// something other than SPV_SUCCESS.  Matrices inherit the specified column
-// or row major-ness.
-spv_result_t checkLayout(uint32_t struct_id, spv::StorageClass storage_class,
-                         const char* decoration_str, bool blockRules,
-                         bool scalar_block_layout, uint32_t incoming_offset,
-                         MemberConstraints& constraints,
-                         ValidationState_t& vstate) {
-  if (vstate.options()->skip_block_layout) return SPV_SUCCESS;
-
-  // blockRules are the same as bufferBlock rules if the uniform buffer
-  // standard layout extension is being used.
-  if (vstate.options()->uniform_buffer_standard_layout) blockRules = false;
-
-  // Relaxed layout and scalar layout can both be in effect at the same time.
-  // For example, relaxed layout is implied by Vulkan 1.1.  But scalar layout
-  // is more permissive than relaxed layout.
-  const bool relaxed_block_layout = vstate.IsRelaxedBlockLayout();
-
-  auto fail = [&vstate, struct_id, storage_class, decoration_str, blockRules,
-               relaxed_block_layout,
-               scalar_block_layout](uint32_t member_idx) -> DiagnosticStream {
-    DiagnosticStream ds = std::move(
-        vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(struct_id))
-        << "Structure id " << struct_id << " decorated as " << decoration_str
-        << " for variable in " << StorageClassToString(storage_class)
-        << " storage class must follow "
-        << (scalar_block_layout
-                ? "scalar "
-                : (relaxed_block_layout ? "relaxed " : "standard "))
-        << (blockRules ? "uniform buffer" : "storage buffer")
-        << " layout rules: member " << member_idx << " ");
-    return ds;
-  };
-
-  // People often use spirv-val from Vulkan Validation Layers, it ends up
-  // mapping the various block layout rules from the enabled feature. This
-  // offers a hint to help the user understand possbily why things are not
-  // working when the shader itself "seems" valid, but just was a lack of adding
-  // a supported feature
-  auto extra = [&vstate, scalar_block_layout, storage_class,
-                relaxed_block_layout, blockRules]() {
-    if (!scalar_block_layout) {
-      if (storage_class == spv::StorageClass::Workgroup) {
-        return vstate.MissingFeature(
-            "workgroupMemoryExplicitLayoutScalarBlockLayout feature",
-            "--workgroup-scalar-block-layout", true);
-      } else if (!relaxed_block_layout) {
-        return vstate.MissingFeature("VK_KHR_relaxed_block_layout extension",
-                                     "--relax-block-layout", true);
-      } else if (blockRules) {
-        return vstate.MissingFeature("uniformBufferStandardLayout feature",
-                                     "--uniform-buffer-standard-layout", true);
-      } else {
-        return vstate.MissingFeature("scalarBlockLayout feature",
-                                     "--scalar-block-layout", true);
-      }
-    }
-    return std::string("");
-  };
-
-  // If we are checking the layout of untyped pointers or physical storage
-  // buffer pointers, we may not actually have a struct here. Instead, pretend
-  // we have a struct with a single member at offset 0.
-  const auto& struct_type = vstate.FindDef(struct_id);
-  std::vector<uint32_t> members;
-  if (struct_type->opcode() == spv::Op::OpTypeStruct) {
-    members = getStructMembers(struct_id, vstate);
-  } else {
-    members.push_back(struct_id);
-  }
-
-  // To check for member overlaps, we want to traverse the members in
-  // offset order.
-  struct MemberOffsetPair {
-    uint32_t member;
-    uint32_t offset;
-  };
-  std::vector<MemberOffsetPair> member_offsets;
-
-  // With untyped pointers or physical storage buffers, we might be checking
-  // layouts that do not originate from a structure.
-  if (struct_type->opcode() == spv::Op::OpTypeStruct) {
-    member_offsets.reserve(members.size());
-    for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size());
-         memberIdx < numMembers; memberIdx++) {
-      uint32_t offset = 0xffffffff;
-      auto member_decorations =
-          vstate.id_member_decorations(struct_id, memberIdx);
-      for (auto decoration = member_decorations.begin;
-           decoration != member_decorations.end; ++decoration) {
-        assert(decoration->struct_member_index() == (int)memberIdx);
-        switch (decoration->dec_type()) {
-          case spv::Decoration::Offset:
-            offset = decoration->params()[0];
-            break;
-          default:
-            break;
-        }
-      }
-      member_offsets.push_back(
-          MemberOffsetPair{memberIdx, incoming_offset + offset});
-    }
-    std::stable_sort(
-        member_offsets.begin(), member_offsets.end(),
-        [](const MemberOffsetPair& lhs, const MemberOffsetPair& rhs) {
-          return lhs.offset < rhs.offset;
-        });
-  } else {
-    member_offsets.push_back({0, 0});
-  }
-
-  // Now scan from lowest offset to highest offset.
-  uint32_t nextValidOffset = 0;
-  for (size_t ordered_member_idx = 0;
-       ordered_member_idx < member_offsets.size(); ordered_member_idx++) {
-    const auto& member_offset = member_offsets[ordered_member_idx];
-    const auto memberIdx = member_offset.member;
-    const auto offset = member_offset.offset;
-    auto id = members[member_offset.member];
-    const LayoutConstraints& constraint =
-        constraints[std::make_pair(struct_id, uint32_t(memberIdx))];
-    // Scalar layout takes precedence because it's more permissive, and implying
-    // an alignment that divides evenly into the alignment that would otherwise
-    // be used.
-    const auto alignment =
-        scalar_block_layout
-            ? getScalarAlignment(id, vstate)
-            : getBaseAlignment(id, blockRules, constraint, constraints, vstate);
-    const auto inst = vstate.FindDef(id);
-    const auto opcode = inst->opcode();
-    const auto size = getSize(id, constraint, constraints, vstate);
-    // Check offset.
-    if (offset == 0xffffffff)
-      return fail(memberIdx) << "is missing an Offset decoration" << extra();
-
-    if (opcode == spv::Op::OpTypeRuntimeArray &&
-        ordered_member_idx != member_offsets.size() - 1) {
-      return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(struct_id))
-             << vstate.VkErrorID(4680) << "Structure id " << struct_id
-             << " has a runtime array at offset " << offset
-             << ", but other members at larger offsets";
-    }
-
-    if (!scalar_block_layout && relaxed_block_layout &&
-        (opcode == spv::Op::OpTypeVector ||
-         opcode == spv::Op::OpTypeVectorIdEXT)) {
-      // In relaxed block layout, the vector offset must be aligned to the
-      // vector's scalar element type.
-      const auto componentId = inst->words()[2];
-      const auto scalar_alignment = getScalarAlignment(componentId, vstate);
-      if (!IsAlignedTo(offset, scalar_alignment)) {
-        return fail(memberIdx) << "at offset " << offset
-                               << " is not aligned to scalar element size "
-                               << scalar_alignment << extra();
-      }
-    } else {
-      // Without relaxed block layout, the offset must be divisible by the
-      // alignment requirement.
-      if (!IsAlignedTo(offset, alignment)) {
-        return fail(memberIdx) << "at offset " << offset
-                               << " is not aligned to " << alignment << extra();
-      }
-    }
-    if (offset < nextValidOffset)
-      return fail(memberIdx) << "at offset " << offset
-                             << " overlaps previous member ending at offset "
-                             << nextValidOffset - 1 << extra();
-    if (!scalar_block_layout && relaxed_block_layout) {
-      // Check improper straddle of vectors.
-      if ((spv::Op::OpTypeVector == opcode ||
-           spv::Op::OpTypeVectorIdEXT == opcode) &&
-          hasImproperStraddle(id, offset, constraint, constraints, vstate))
-        return fail(memberIdx)
-               << "is an improperly straddling vector at offset " << offset
-               << extra();
-    }
-    // Check struct members recursively.
-    spv_result_t recursive_status = SPV_SUCCESS;
-    if (spv::Op::OpTypeStruct == opcode &&
-        SPV_SUCCESS != (recursive_status = checkLayout(
-                            id, storage_class, decoration_str, blockRules,
-                            scalar_block_layout, offset, constraints, vstate)))
-      return recursive_status;
-    // Check matrix stride.
-    if (spv::Op::OpTypeMatrix == opcode) {
-      const auto stride = constraint.matrix_stride;
-      if (!IsAlignedTo(stride, alignment)) {
-        return fail(memberIdx)
-               << "is a matrix with stride " << stride
-               << " not satisfying alignment to " << alignment << extra();
-      }
-    }
-
-    // Check arrays and runtime arrays recursively.
-    auto array_inst = inst;
-    auto array_alignment = alignment;
-    while (array_inst->opcode() == spv::Op::OpTypeArray ||
-           array_inst->opcode() == spv::Op::OpTypeRuntimeArray) {
-      const auto typeId = array_inst->word(2);
-      const auto element_inst = vstate.FindDef(typeId);
-      // Check array stride.
-      uint32_t array_stride = 0;
-      for (auto& decoration : vstate.id_decorations(array_inst->id())) {
-        if (spv::Decoration::ArrayStride == decoration.dec_type()) {
-          array_stride = decoration.params()[0];
-          if (array_stride == 0) {
-            return fail(memberIdx)
-                   << "contains an array with stride 0" << extra();
-          }
-          if (!IsAlignedTo(array_stride, array_alignment))
-            return fail(memberIdx)
-                   << "contains an array with stride " << decoration.params()[0]
-                   << " not satisfying alignment to " << alignment << extra();
-        }
-      }
-
-      bool is_int32 = false;
-      bool is_const = false;
-      uint32_t num_elements = 0;
-      if (array_inst->opcode() == spv::Op::OpTypeArray) {
-        std::tie(is_int32, is_const, num_elements) =
-            vstate.EvalInt32IfConst(array_inst->word(3));
-      }
-      num_elements = std::max(1u, num_elements);
-      // Check each element recursively if it is a struct. There is a
-      // limitation to this check if the array size is a spec constant or is a
-      // runtime array then we will only check a single element. This means
-      // some improper straddles might be missed.
-      if (spv::Op::OpTypeStruct == element_inst->opcode()) {
-        std::vector<bool> seen(16, false);
-        for (uint32_t i = 0; i < num_elements; ++i) {
-          uint32_t next_offset = i * array_stride + offset;
-          // Stop checking if offsets repeat in terms of 16-byte multiples.
-          if (seen[next_offset % 16]) {
-            break;
-          }
-
-          if (SPV_SUCCESS !=
-              (recursive_status = checkLayout(
-                   typeId, storage_class, decoration_str, blockRules,
-                   scalar_block_layout, next_offset, constraints, vstate)))
-            return recursive_status;
-
-          seen[next_offset % 16] = true;
-        }
-      } else if (spv::Op::OpTypeMatrix == element_inst->opcode()) {
-        // Matrix stride would be on the array element in the struct.
-        const auto stride = constraint.matrix_stride;
-        if (!IsAlignedTo(stride, alignment)) {
-          return fail(memberIdx)
-                 << "is a matrix with stride " << stride
-                 << " not satisfying alignment to " << alignment << extra();
-        }
-      }
-
-      // Proceed to the element in case it is an array.
-      array_inst = element_inst;
-      array_alignment = scalar_block_layout
-                            ? getScalarAlignment(array_inst->id(), vstate)
-                            : getBaseAlignment(array_inst->id(), blockRules,
-                                               constraint, constraints, vstate);
-
-      const auto element_size =
-          getSize(element_inst->id(), constraint, constraints, vstate);
-      if (element_size > array_stride) {
-        return fail(memberIdx)
-               << "contains an array with stride " << array_stride
-               << ", but with an element size of " << element_size << extra();
-      }
-    }
-    nextValidOffset = offset + size;
-    if (!scalar_block_layout &&
-        (spv::Op::OpTypeArray == opcode || spv::Op::OpTypeStruct == opcode)) {
-      // Non-scalar block layout rules don't permit anything in the padding of
-      // a struct or array.
-      nextValidOffset = align(nextValidOffset, alignment);
-    }
-  }
-  return SPV_SUCCESS;
-}
-
 // Returns true if variable or structure id has given decoration. Handles also
 // nested structures.
 bool hasDecoration(uint32_t id, spv::Decoration decoration,
@@ -735,45 +92,6 @@
   return false;
 }
 
-// Returns true if all ids of given type have a specified decoration.
-bool checkForRequiredDecoration(uint32_t struct_id,
-                                std::function<bool(spv::Decoration)> checker,
-                                spv::Op type, ValidationState_t& vstate) {
-  const auto& members = getStructMembers(struct_id, vstate);
-  for (size_t memberIdx = 0; memberIdx < members.size(); memberIdx++) {
-    auto id = members[memberIdx];
-    if (type == spv::Op::OpTypeMatrix) {
-      // Matrix decorations also apply to arrays of matrices.
-      auto memberInst = vstate.FindDef(id);
-      while (memberInst->opcode() == spv::Op::OpTypeArray ||
-             memberInst->opcode() == spv::Op::OpTypeRuntimeArray) {
-        memberInst = vstate.FindDef(memberInst->GetOperandAs<uint32_t>(1u));
-      }
-      id = memberInst->id();
-    }
-    if (type != vstate.FindDef(id)->opcode()) continue;
-    bool found = false;
-    for (auto& dec : vstate.id_decorations(id)) {
-      if (checker(dec.dec_type())) found = true;
-    }
-    for (auto& dec : vstate.id_decorations(struct_id)) {
-      if (checker(dec.dec_type()) &&
-          (int)memberIdx == dec.struct_member_index()) {
-        found = true;
-      }
-    }
-    if (!found) {
-      return false;
-    }
-  }
-  for (auto id : getStructMembers(struct_id, spv::Op::OpTypeStruct, vstate)) {
-    if (!checkForRequiredDecoration(id, checker, type, vstate)) {
-      return false;
-    }
-  }
-  return true;
-}
-
 spv_result_t CheckLinkageAttrOfFunctions(ValidationState_t& vstate) {
   for (const auto& function : vstate.functions()) {
     if (function.block_count() == 0u) {
@@ -1120,89 +438,6 @@
   return SPV_SUCCESS;
 }
 
-// Load |constraints| with all the member constraints for structs contained
-// within the given array type.
-void ComputeMemberConstraintsForArray(MemberConstraints* constraints,
-                                      uint32_t array_id,
-                                      const LayoutConstraints& inherited,
-                                      ValidationState_t& vstate);
-
-// Load |constraints| with all the member constraints for the given struct,
-// and all its contained structs.
-void ComputeMemberConstraintsForStruct(MemberConstraints* constraints,
-                                       uint32_t struct_id,
-                                       const LayoutConstraints& inherited,
-                                       ValidationState_t& vstate) {
-  assert(constraints);
-  const auto& members = getStructMembers(struct_id, vstate);
-  for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size());
-       memberIdx < numMembers; memberIdx++) {
-    LayoutConstraints& constraint =
-        (*constraints)[std::make_pair(struct_id, memberIdx)];
-    constraint = inherited;
-    auto member_decorations =
-        vstate.id_member_decorations(struct_id, memberIdx);
-    for (auto decoration = member_decorations.begin;
-         decoration != member_decorations.end; ++decoration) {
-      assert(decoration->struct_member_index() == (int)memberIdx);
-      switch (decoration->dec_type()) {
-        case spv::Decoration::RowMajor:
-          constraint.majorness = kRowMajor;
-          break;
-        case spv::Decoration::ColMajor:
-          constraint.majorness = kColumnMajor;
-          break;
-        case spv::Decoration::MatrixStride:
-          constraint.matrix_stride = decoration->params()[0];
-          break;
-        default:
-          break;
-      }
-    }
-
-    // Now recurse
-    auto member_type_id = members[memberIdx];
-    const auto member_type_inst = vstate.FindDef(member_type_id);
-    const auto opcode = member_type_inst->opcode();
-    switch (opcode) {
-      case spv::Op::OpTypeArray:
-      case spv::Op::OpTypeRuntimeArray:
-        ComputeMemberConstraintsForArray(constraints, member_type_id, inherited,
-                                         vstate);
-        break;
-      case spv::Op::OpTypeStruct:
-        ComputeMemberConstraintsForStruct(constraints, member_type_id,
-                                          inherited, vstate);
-        break;
-      default:
-        break;
-    }
-  }
-}
-
-void ComputeMemberConstraintsForArray(MemberConstraints* constraints,
-                                      uint32_t array_id,
-                                      const LayoutConstraints& inherited,
-                                      ValidationState_t& vstate) {
-  assert(constraints);
-  auto elem_type_id = vstate.FindDef(array_id)->words()[2];
-  const auto elem_type_inst = vstate.FindDef(elem_type_id);
-  const auto opcode = elem_type_inst->opcode();
-  switch (opcode) {
-    case spv::Op::OpTypeArray:
-    case spv::Op::OpTypeRuntimeArray:
-      ComputeMemberConstraintsForArray(constraints, elem_type_id, inherited,
-                                       vstate);
-      break;
-    case spv::Op::OpTypeStruct:
-      ComputeMemberConstraintsForStruct(constraints, elem_type_id, inherited,
-                                        vstate);
-      break;
-    default:
-      break;
-  }
-}
-
 spv_result_t CheckDecorationsOfVariables(ValidationState_t& vstate) {
   if (!spvIsVulkanEnv(vstate.context()->target_env)) {
     return SPV_SUCCESS;
@@ -1269,10 +504,6 @@
   std::unordered_set<uint32_t> uses_push_constant;
   for (const auto& inst : vstate.ordered_instructions()) {
     const auto& words = inst.words();
-    auto type_id = inst.type_id();
-    const Instruction* type_inst = vstate.FindDef(type_id);
-    bool scalar_block_layout = false;
-    MemberConstraints constraints;
     if (spv::Op::OpVariable == inst.opcode() ||
         spv::Op::OpUntypedVariableKHR == inst.opcode()) {
       const bool untyped_pointer =
@@ -1367,10 +598,6 @@
             id = id_inst->GetOperandAs<uint32_t>(1u);
             id_inst = vstate.FindDef(id);
           }
-          // Struct requirement is checked on variables so just move on here.
-          if (spv::Op::OpTypeStruct != id_inst->opcode()) continue;
-          ComputeMemberConstraintsForStruct(&constraints, id,
-                                            LayoutConstraints(), vstate);
         }
 
         if (spvIsVulkanEnv(vstate.context()->target_env)) {
@@ -1416,11 +643,6 @@
             const bool blockDeco = spv::Decoration::Block == dec.dec_type();
             const bool bufferDeco =
                 spv::Decoration::BufferBlock == dec.dec_type();
-            const bool blockRules = uniform && blockDeco;
-            const bool bufferRules = (uniform && bufferDeco) ||
-                                     ((push_constant || storage_buffer ||
-                                       phys_storage_buffer || workgroup) &&
-                                      blockDeco);
             if (uniform && blockDeco) {
               vstate.RegisterPointerToUniformBlock(ptrInst->id());
               vstate.RegisterStructForUniformBlock(id);
@@ -1430,187 +652,9 @@
               vstate.RegisterPointerToStorageBuffer(ptrInst->id());
               vstate.RegisterStructForStorageBuffer(id);
             }
-
-            if (blockRules || bufferRules) {
-              const char* deco_str = blockDeco ? "Block" : "BufferBlock";
-              spv_result_t recursive_status = SPV_SUCCESS;
-              scalar_block_layout =
-                  workgroup ? vstate.options()->workgroup_scalar_block_layout
-                            : vstate.options()->scalar_block_layout;
-
-              if (isMissingOffsetInStruct(id, vstate)) {
-                return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
-                       << "Structure id " << id << " decorated as " << deco_str
-                       << " must be explicitly laid out with Offset "
-                          "decorations.";
-              }
-
-              if (!checkForRequiredDecoration(
-                      id,
-                      [](spv::Decoration d) {
-                        return d == spv::Decoration::ArrayStride ||
-                               d == spv::Decoration::ArrayStrideIdEXT;
-                      },
-                      spv::Op::OpTypeArray, vstate)) {
-                return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
-                       << "Structure id " << id << " decorated as " << deco_str
-                       << " must be explicitly laid out with ArrayStride or "
-                          "ArrayStrideIdEXT "
-                          "decorations.";
-              }
-
-              if (!checkForRequiredDecoration(
-                      id,
-                      [](spv::Decoration d) {
-                        return d == spv::Decoration::MatrixStride;
-                      },
-                      spv::Op::OpTypeMatrix, vstate)) {
-                return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
-                       << "Structure id " << id << " decorated as " << deco_str
-                       << " must be explicitly laid out with MatrixStride "
-                          "decorations.";
-              }
-
-              if (!checkForRequiredDecoration(
-                      id,
-                      [](spv::Decoration d) {
-                        return d == spv::Decoration::RowMajor ||
-                               d == spv::Decoration::ColMajor;
-                      },
-                      spv::Op::OpTypeMatrix, vstate)) {
-                return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id))
-                       << "Structure id " << id << " decorated as " << deco_str
-                       << " must be explicitly laid out with RowMajor or "
-                          "ColMajor decorations.";
-              }
-
-              if (spvIsVulkanEnv(vstate.context()->target_env)) {
-                if (blockRules &&
-                    (SPV_SUCCESS !=
-                     (recursive_status = checkLayout(
-                          id, storageClass, deco_str, true, scalar_block_layout,
-                          0, constraints, vstate)))) {
-                  return recursive_status;
-                } else if (bufferRules &&
-                           (SPV_SUCCESS != (recursive_status = checkLayout(
-                                                id, storageClass, deco_str,
-                                                false, scalar_block_layout, 0,
-                                                constraints, vstate)))) {
-                  return recursive_status;
-                }
-              }
-            }
           }
         }
       }
-    } else if (type_inst && type_inst->opcode() == spv::Op::OpTypePointer &&
-               type_inst->GetOperandAs<spv::StorageClass>(1u) ==
-                   spv::StorageClass::PhysicalStorageBuffer) {
-      const bool buffer = true;
-      const auto pointee_type_id = type_inst->GetOperandAs<uint32_t>(2u);
-      const auto* data_type_inst = vstate.FindDef(pointee_type_id);
-      scalar_block_layout = vstate.options()->scalar_block_layout;
-      if (data_type_inst->opcode() == spv::Op::OpTypeStruct) {
-        ComputeMemberConstraintsForStruct(&constraints, pointee_type_id,
-                                          LayoutConstraints(), vstate);
-      }
-      if (auto res = checkLayout(
-              pointee_type_id, spv::StorageClass::PhysicalStorageBuffer,
-              "Block", !buffer, scalar_block_layout, 0, constraints, vstate)) {
-        return res;
-      }
-    } else if (vstate.HasCapability(spv::Capability::UntypedPointersKHR) &&
-               spvIsVulkanEnv(vstate.context()->target_env)) {
-      // Untyped variables are checked above. Here we check that instructions
-      // using an untyped pointer have a valid layout.
-      uint32_t ptr_ty_id = 0;
-      uint32_t data_type_id = 0;
-      switch (inst.opcode()) {
-        case spv::Op::OpUntypedAccessChainKHR:
-        case spv::Op::OpUntypedInBoundsAccessChainKHR:
-        case spv::Op::OpUntypedPtrAccessChainKHR:
-        case spv::Op::OpUntypedInBoundsPtrAccessChainKHR:
-          ptr_ty_id = inst.type_id();
-          data_type_id = inst.GetOperandAs<uint32_t>(2);
-          break;
-        case spv::Op::OpLoad:
-          if (vstate.GetIdOpcode(vstate.GetOperandTypeId(&inst, 2)) ==
-              spv::Op::OpTypeUntypedPointerKHR) {
-            const auto ptr_id = inst.GetOperandAs<uint32_t>(2);
-            ptr_ty_id = vstate.FindDef(ptr_id)->type_id();
-            data_type_id = inst.type_id();
-          }
-          break;
-        case spv::Op::OpStore:
-          if (vstate.GetIdOpcode(vstate.GetOperandTypeId(&inst, 0)) ==
-              spv::Op::OpTypeUntypedPointerKHR) {
-            const auto ptr_id = inst.GetOperandAs<uint32_t>(0);
-            ptr_ty_id = vstate.FindDef(ptr_id)->type_id();
-            data_type_id = vstate.GetOperandTypeId(&inst, 1);
-          }
-          break;
-        case spv::Op::OpUntypedArrayLengthKHR:
-          ptr_ty_id = vstate.FindDef(inst.GetOperandAs<uint32_t>(3))->type_id();
-          data_type_id = inst.GetOperandAs<uint32_t>(2);
-          break;
-        default:
-          break;
-      }
-
-      if (ptr_ty_id == 0 || data_type_id == 0) {
-        // Not an untyped pointer.
-        continue;
-      }
-
-      const auto sc =
-          vstate.FindDef(ptr_ty_id)->GetOperandAs<spv::StorageClass>(1);
-
-      auto data_type = vstate.FindDef(data_type_id);
-      scalar_block_layout =
-          sc == spv::StorageClass::Workgroup
-              ? vstate.options()->workgroup_scalar_block_layout
-              : vstate.options()->scalar_block_layout;
-
-      // If the data type is an array that contains a Block- or
-      // BufferBlock-decorated struct, then use the struct for layout checks
-      // instead of the array. In this case, the array represents a descriptor
-      // array which should not have an explicit layout.
-      if (data_type->opcode() == spv::Op::OpTypeArray ||
-          data_type->opcode() == spv::Op::OpTypeRuntimeArray) {
-        const auto ele_type =
-            vstate.FindDef(data_type->GetOperandAs<uint32_t>(1u));
-        if (ele_type->opcode() == spv::Op::OpTypeStruct &&
-            (vstate.HasDecoration(ele_type->id(), spv::Decoration::Block) ||
-             vstate.HasDecoration(ele_type->id(),
-                                  spv::Decoration::BufferBlock))) {
-          data_type = ele_type;
-          data_type_id = ele_type->id();
-        }
-      }
-
-      // Assume uniform storage class uses block rules unless we see a
-      // BufferBlock decorated struct in the data type.
-      bool bufferRules = sc == spv::StorageClass::Uniform ? false : true;
-      if (data_type->opcode() == spv::Op::OpTypeStruct) {
-        if (sc == spv::StorageClass::Uniform) {
-          bufferRules =
-              vstate.HasDecoration(data_type_id, spv::Decoration::BufferBlock);
-        }
-        ComputeMemberConstraintsForStruct(&constraints, data_type_id,
-                                          LayoutConstraints(), vstate);
-      }
-      const char* deco_str =
-          bufferRules
-              ? (sc == spv::StorageClass::Uniform ? "BufferBlock" : "Block")
-              : "Block";
-
-      if (!vstate.IsDescriptorHeapBaseVariable(&inst)) {
-        if (auto result =
-                checkLayout(data_type_id, sc, deco_str, !bufferRules,
-                            scalar_block_layout, 0, constraints, vstate)) {
-          return result;
-        }
-      }
     }
   }
   return SPV_SUCCESS;
@@ -1887,12 +931,11 @@
                 "memory object "
                 "declaration (a variable or a function parameter)";
     }
-    const auto var_storage_class =
-        opcode == spv::Op::OpVariable
-            ? inst.GetOperandAs<spv::StorageClass>(2)
-            : opcode == spv::Op::OpUntypedVariableKHR
-                  ? inst.GetOperandAs<spv::StorageClass>(3)
-                  : spv::StorageClass::Max;
+    const auto var_storage_class = opcode == spv::Op::OpVariable
+                                       ? inst.GetOperandAs<spv::StorageClass>(2)
+                                   : opcode == spv::Op::OpUntypedVariableKHR
+                                       ? inst.GetOperandAs<spv::StorageClass>(3)
+                                       : spv::StorageClass::Max;
 
     if (opcode == spv::Op::OpBufferPointerEXT) {
       auto result_type = vstate.FindDef(inst.type_id());
@@ -2250,253 +1293,6 @@
   return SPV_SUCCESS;
 }
 
-bool AllowsLayout(ValidationState_t& vstate, const spv::StorageClass sc) {
-  switch (sc) {
-    case spv::StorageClass::StorageBuffer:
-    case spv::StorageClass::Uniform:
-    case spv::StorageClass::PhysicalStorageBuffer:
-    case spv::StorageClass::PushConstant:
-      // Always explicitly laid out.
-      return true;
-    case spv::StorageClass::UniformConstant:
-      return false;
-    case spv::StorageClass::Workgroup:
-      return vstate.HasCapability(
-          spv::Capability::WorkgroupMemoryExplicitLayoutKHR);
-    case spv::StorageClass::Function:
-    case spv::StorageClass::Private:
-      return vstate.version() <= SPV_SPIRV_VERSION_WORD(1, 4);
-    case spv::StorageClass::Input:
-    case spv::StorageClass::Output:
-      // Block is used generally and mesh shaders use Offset.
-      return true;
-    default:
-      // TODO: Some storage classes in ray tracing use explicit layout
-      // decorations, but it is not well documented which. For now treat other
-      // storage classes as allowed to be laid out. See Vulkan internal issue
-      // 4192.
-      return true;
-  }
-}
-
-// Returns a decoration used to make it explicit
-spv::Decoration UsesExplicitLayout(
-    ValidationState_t& vstate, uint32_t type_id,
-    std::unordered_map<uint32_t, spv::Decoration>& cache) {
-  if (type_id == 0) {
-    return spv::Decoration::Max;
-  }
-
-  if (cache.count(type_id)) {
-    return cache[type_id];
-  }
-
-  spv::Decoration res = spv::Decoration::Max;
-  const auto type_inst = vstate.FindDef(type_id);
-  if (type_inst->opcode() == spv::Op::OpTypeStruct ||
-      type_inst->opcode() == spv::Op::OpTypeArray ||
-      type_inst->opcode() == spv::Op::OpTypeRuntimeArray ||
-      type_inst->opcode() == spv::Op::OpTypePointer ||
-      type_inst->opcode() == spv::Op::OpTypeUntypedPointerKHR) {
-    const auto& id_decs = vstate.id_decorations();
-    const auto iter = id_decs.find(type_id);
-    if (iter != id_decs.end()) {
-      bool allowLayoutDecorations = false;
-      if (type_inst->opcode() == spv::Op::OpTypePointer ||
-          type_inst->opcode() == spv::Op::OpTypeUntypedPointerKHR) {
-        const auto sc = type_inst->GetOperandAs<spv::StorageClass>(1);
-        allowLayoutDecorations = AllowsLayout(vstate, sc);
-      }
-      if (!allowLayoutDecorations) {
-        for (const auto& d : iter->second) {
-          const spv::Decoration dec = d.dec_type();
-          if (dec == spv::Decoration::Block ||
-              dec == spv::Decoration::BufferBlock ||
-              dec == spv::Decoration::Offset ||
-              dec == spv::Decoration::ArrayStride ||
-              dec == spv::Decoration::MatrixStride) {
-            res = dec;
-            break;
-          }
-        }
-      }
-    }
-
-    if (res == spv::Decoration::Max) {
-      switch (type_inst->opcode()) {
-        case spv::Op::OpTypeStruct:
-          for (uint32_t i = 1;
-               res == spv::Decoration::Max && i < type_inst->operands().size();
-               i++) {
-            res = UsesExplicitLayout(
-                vstate, type_inst->GetOperandAs<uint32_t>(i), cache);
-          }
-          break;
-        case spv::Op::OpTypeArray:
-        case spv::Op::OpTypeRuntimeArray:
-          res = UsesExplicitLayout(vstate, type_inst->GetOperandAs<uint32_t>(1),
-                                   cache);
-          break;
-        case spv::Op::OpTypePointer: {
-          const auto sc = type_inst->GetOperandAs<spv::StorageClass>(1);
-          if (!AllowsLayout(vstate, sc)) {
-            res = UsesExplicitLayout(
-                vstate, type_inst->GetOperandAs<uint32_t>(2), cache);
-          }
-        }
-        default:
-          break;
-      }
-    }
-  }
-
-  cache[type_id] = res;
-  return res;
-}
-
-spv_result_t CheckInvalidVulkanExplicitLayout(ValidationState_t& vstate) {
-  if (!spvIsVulkanEnv(vstate.context()->target_env)) {
-    return SPV_SUCCESS;
-  }
-
-  std::unordered_map<uint32_t, spv::Decoration> cache;
-  for (const auto& inst : vstate.ordered_instructions()) {
-    const auto type_id = inst.type_id();
-    const auto type_inst = vstate.FindDef(type_id);
-
-    spv::StorageClass sc = spv::StorageClass::Max;
-    spv::Decoration layout_dec = spv::Decoration::Max;
-    uint32_t fail_id = 0;
-    uint32_t base_id = 0;
-    // Variables are the main place to check for improper decorations, but some
-    // untyped pointer instructions must also be checked since those types may
-    // never be instantiated by a variable. Unlike verifying a valid layout,
-    // physical storage buffer does not need checked here since it is always
-    // explicitly laid out.
-    switch (inst.opcode()) {
-      case spv::Op::OpVariable:
-      case spv::Op::OpUntypedVariableKHR: {
-        sc = inst.GetOperandAs<spv::StorageClass>(2);
-        auto check_id = type_id;
-        base_id = inst.id();
-        if (inst.opcode() == spv::Op::OpUntypedVariableKHR) {
-          if (inst.operands().size() > 3) {
-            check_id = inst.GetOperandAs<uint32_t>(3);
-          }
-        }
-        if (!AllowsLayout(vstate, sc)) {
-          layout_dec = UsesExplicitLayout(vstate, check_id, cache);
-          if (layout_dec != spv::Decoration::Max) {
-            fail_id = check_id;
-          }
-        }
-        break;
-      }
-      case spv::Op::OpUntypedAccessChainKHR:
-      case spv::Op::OpUntypedInBoundsAccessChainKHR:
-      case spv::Op::OpUntypedPtrAccessChainKHR:
-      case spv::Op::OpUntypedInBoundsPtrAccessChainKHR: {
-        // Check both the base type and return type. The return type may have an
-        // invalid array stride.
-        sc = type_inst->GetOperandAs<spv::StorageClass>(1);
-        base_id = vstate.FindDef(inst.GetOperandAs<uint32_t>(3))->id();
-        if (!AllowsLayout(vstate, sc)) {
-          const auto base_type_id = inst.GetOperandAs<uint32_t>(2);
-          layout_dec = UsesExplicitLayout(vstate, base_type_id, cache);
-          if (layout_dec != spv::Decoration::Max) {
-            fail_id = base_type_id;
-          } else {
-            layout_dec = UsesExplicitLayout(vstate, type_id, cache);
-            if (layout_dec != spv::Decoration::Max) {
-              fail_id = type_id;
-            }
-          }
-        }
-        break;
-      }
-      case spv::Op::OpUntypedArrayLengthKHR: {
-        // Check the data type.
-        const auto ptr_ty_id =
-            vstate.FindDef(inst.GetOperandAs<uint32_t>(3))->type_id();
-        const auto ptr_ty = vstate.FindDef(ptr_ty_id);
-        sc = ptr_ty->GetOperandAs<spv::StorageClass>(1);
-        base_id = vstate.FindDef(inst.GetOperandAs<uint32_t>(3))->id();
-        if (!AllowsLayout(vstate, sc)) {
-          const auto base_type_id = inst.GetOperandAs<uint32_t>(2);
-          layout_dec = UsesExplicitLayout(vstate, base_type_id, cache);
-          if (layout_dec != spv::Decoration::Max) {
-            fail_id = base_type_id;
-          }
-        }
-        break;
-      }
-      case spv::Op::OpLoad: {
-        const auto ptr_id = inst.GetOperandAs<uint32_t>(2);
-        const auto ptr_type = vstate.FindDef(vstate.FindDef(ptr_id)->type_id());
-        base_id = ptr_id;
-        if (ptr_type->opcode() == spv::Op::OpTypeUntypedPointerKHR) {
-          // For untyped pointers check the return type for an invalid layout.
-          sc = ptr_type->GetOperandAs<spv::StorageClass>(1);
-          if (!AllowsLayout(vstate, sc)) {
-            layout_dec = UsesExplicitLayout(vstate, type_id, cache);
-            if (layout_dec != spv::Decoration::Max) {
-              fail_id = type_id;
-            }
-          }
-        }
-        break;
-      }
-      case spv::Op::OpStore: {
-        const auto ptr_id = inst.GetOperandAs<uint32_t>(1);
-        const auto ptr_type = vstate.FindDef(vstate.FindDef(ptr_id)->type_id());
-        base_id = inst.GetOperandAs<uint32_t>(0);
-        if (ptr_type->opcode() == spv::Op::OpTypeUntypedPointerKHR) {
-          // For untyped pointers, check the type of the data operand for an
-          // invalid layout.
-          sc = ptr_type->GetOperandAs<spv::StorageClass>(1);
-          if (!AllowsLayout(vstate, sc)) {
-            const auto data_type_id = vstate.GetOperandTypeId(&inst, 1);
-            layout_dec = UsesExplicitLayout(vstate, data_type_id, cache);
-            if (layout_dec != spv::Decoration::Max) {
-              fail_id = inst.GetOperandAs<uint32_t>(2);
-            }
-          }
-        }
-        break;
-      }
-      case spv::Op::OpBufferPointerEXT: {
-        const auto ptr_id = inst.GetOperandAs<uint32_t>(1);
-        const auto ptr_type = vstate.FindDef(vstate.FindDef(ptr_id)->type_id());
-        sc = ptr_type->GetOperandAs<spv::StorageClass>(1);
-        // OpBufferPointerEXT needs to be in explicit layout, which it is,
-        // because it must be Uniform/StorageBuffer
-        if (sc != spv::StorageClass::StorageBuffer &&
-            sc != spv::StorageClass::Uniform) {
-          return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
-                 << "OpBufferPointerEXT Result Type must be a pointer type "
-                 << "with a Storage Class of Uniform or StorageBuffer.";
-        }
-        break;
-      }
-      default:
-        break;
-    }
-
-    if (fail_id != 0 &&
-        !vstate.IsDescriptorHeapBaseVariable(vstate.FindDef(base_id))) {
-      return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
-             << vstate.VkErrorID(10684)
-             << "Invalid explicit layout decorations on type for operand "
-             << vstate.getIdName(fail_id) << ", the "
-             << spvtools::StorageClassToString(sc)
-             << " storage class has a explicit layout from the "
-             << vstate.SpvDecorationString(layout_dec) << " decoration.";
-    }
-  }
-
-  return SPV_SUCCESS;
-}
-
 }  // namespace
 
 spv_result_t ValidateDecorations(ValidationState_t& vstate) {
@@ -2509,7 +1305,6 @@
   if (auto error = CheckVulkanMemoryModelDeprecatedDecorations(vstate))
     return error;
   if (auto error = CheckDecorationsFromDecoration(vstate)) return error;
-  if (auto error = CheckInvalidVulkanExplicitLayout(vstate)) return error;
   return SPV_SUCCESS;
 }
 
diff --git a/source/val/validate_explicit_layout.cpp b/source/val/validate_explicit_layout.cpp
new file mode 100644
index 0000000..e25c79e
--- /dev/null
+++ b/source/val/validate_explicit_layout.cpp
@@ -0,0 +1,1130 @@
+// Copyright (c) 2026 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <algorithm>
+#include <cassert>
+#include <ostream>
+#include <sstream>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
+#include "source/spirv_constant.h"
+#include "source/spirv_target_env.h"
+#include "source/util/hash_combine.h"
+#include "source/val/validation_state.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+enum class LayoutMode : uint8_t {
+  // Vulkan scalar block rules
+  kScalar,
+  // Vulkan standard alignment rules (i.e. std430)
+  kStandard,
+  // Vulkan extended alignment rules (i.e. std140)
+  kExtended,
+};
+
+std::ostream& operator<<(std::ostream& str, const LayoutMode& mode) {
+  switch (mode) {
+    case LayoutMode::kScalar:
+      str << "scalar";
+      break;
+    case LayoutMode::kStandard:
+      str << "standard";
+      break;
+    case LayoutMode::kExtended:
+      str << "extended";
+      break;
+  }
+  return str;
+}
+
+enum class LayoutRequirement : uint8_t {
+  // Must be laid out
+  kRequired,
+  // Must not be laid out
+  kProhibited,
+  // Either laid or not
+  kAllowed,
+};
+
+struct Impl {
+  ValidationState_t& vstate;
+
+  // Relevant information to describe a memory instruction for the purposes of
+  // layout validation.
+  struct MemoryReference {
+    // The data type of the memory instruction (after stripping any descriptor
+    // array).
+    uint32_t type_id = 0;
+    // The descriptor array type id (if there is one).
+    uint32_t descriptor_array_id = 0;
+    // The storage class for the memory instruction.
+    spv::StorageClass storage_class;
+    // The layout mode (only relevant if a layout is required).
+    LayoutMode layout;
+    // The layout requirement for the instruction.
+    LayoutRequirement requirement;
+    // Whether it is an untyped pointer base.
+    bool untyped = false;
+  };
+
+  // Matrix constraints from a struct member to carry to the actual matrix type.
+  struct MatrixConstraints {
+    uint32_t stride = 0;
+    bool col_major = true;
+  };
+
+  // Cache valid checks for types that should have no layout.
+  std::unordered_set<uint32_t> no_layout_cache_;
+
+  // Struct member info
+  struct MemberInfo {
+    // Structure member index (note: index + 1 is instruction index).
+    uint32_t index;
+    // Whether or not an Offset/OffsetIdEXT decoration is present.
+    bool has_offset = false;
+    // Offset value. Max uint32_t is used for no evaluation (e.g. spec
+    // constant).
+    uint32_t offset = std::numeric_limits<uint32_t>::max();
+    // Whether or not RowMajor or ColMajor decoration is present.
+    bool has_matrix = false;
+    // Matrix constraints (stride and majorness).
+    MatrixConstraints matrix_constraints;
+  };
+
+  // Cache of structure member information.
+  std::unordered_map<uint32_t, std::vector<MemberInfo>> struct_members_;
+
+  struct LayoutKey {
+    uint32_t type_id = 0;
+    LayoutMode layout;
+    uint32_t incoming_offset = 0;
+    MatrixConstraints matrix_constraints{};
+
+    bool operator==(const LayoutKey& other) const {
+      return type_id == other.type_id && layout == other.layout &&
+             incoming_offset == other.incoming_offset &&
+             matrix_constraints.stride == other.matrix_constraints.stride &&
+             matrix_constraints.col_major == other.matrix_constraints.col_major;
+    }
+  };
+  struct LayoutKeyHash {
+    size_t operator()(const LayoutKey& key) const noexcept {
+      return spvtools::utils::hash_combine(
+          0, key.type_id, static_cast<uint32_t>(key.layout),
+          key.incoming_offset, key.matrix_constraints.stride,
+          key.matrix_constraints.col_major);
+    }
+  };
+
+  // Caches valid results of CheckLayout calls.
+  std::unordered_set<LayoutKey, LayoutKeyHash> layout_cache_;
+
+  // Returns the layout requirements for `sc`.
+  // Workgroup is expected to be explicitly laid out if `is_block` is true.
+  // UniformConstant is expected to be explicitly laid out if `descriptor_heap`
+  // is true.
+  LayoutRequirement GetStorageClassRequirement(spv::StorageClass sc,
+                                               bool is_block,
+                                               bool descriptor_heap) {
+    switch (sc) {
+      case spv::StorageClass::Workgroup:
+        return is_block ? LayoutRequirement::kRequired
+                        : LayoutRequirement::kProhibited;
+      case spv::StorageClass::StorageBuffer:
+      case spv::StorageClass::Uniform:
+      case spv::StorageClass::PushConstant:
+      case spv::StorageClass::PhysicalStorageBuffer:
+        return LayoutRequirement::kRequired;
+      case spv::StorageClass::UniformConstant:
+        return descriptor_heap ? LayoutRequirement::kRequired
+                               : LayoutRequirement::kProhibited;
+      case spv::StorageClass::Function:
+      case spv::StorageClass::Private:
+        return vstate.version() <= SPV_SPIRV_VERSION_WORD(1, 4)
+                   ? LayoutRequirement::kAllowed
+                   : LayoutRequirement::kProhibited;
+      case spv::StorageClass::Input:
+      case spv::StorageClass::Output:
+        // Block is used generally and mesh shaders use Offset.
+        // TODO: This is a little over permissive.
+        return LayoutRequirement::kAllowed;
+      default:
+        // TODO: Some storage classes in ray tracing use explicit layout
+        // decorations, but it is not well documented which. For now treat
+        // other storage classes as allowed to be laid out. See Vulkan
+        // internal issue 4192.
+        return LayoutRequirement::kAllowed;
+    }
+  }
+
+  // Returns the layout rules for `sc`.
+  LayoutMode GetStorageClassLayout(spv::StorageClass sc, bool is_buffer_block) {
+    switch (sc) {
+      case spv::StorageClass::Workgroup:
+        return vstate.options()->workgroup_scalar_block_layout
+                   ? LayoutMode::kScalar
+                   : LayoutMode::kStandard;
+        break;
+      case spv::StorageClass::StorageBuffer:
+      case spv::StorageClass::PushConstant:
+      case spv::StorageClass::UniformConstant:
+      case spv::StorageClass::PhysicalStorageBuffer:
+        return vstate.options()->scalar_block_layout ? LayoutMode::kScalar
+                                                     : LayoutMode::kStandard;
+        break;
+      case spv::StorageClass::Uniform:
+        return vstate.options()->scalar_block_layout
+                   ? LayoutMode::kScalar
+                   : ((is_buffer_block ||
+                       vstate.options()->uniform_buffer_standard_layout)
+                          ? LayoutMode::kStandard
+                          : LayoutMode::kExtended);
+        break;
+      default:
+        break;
+    }
+    return LayoutMode::kStandard;
+  }
+
+  // Returns true if `inst` is a memory reference instruction
+  // Populates `reference` with the necessary information.
+  //
+  // The following are interesting memory references:
+  // For typed pointers:
+  //  * OpVariable
+  //  * Memory instructions on PhysicalStorageBuffer
+  //  * OpBufferPointerEXT
+  // For untyped pointers:
+  //  * All memory instructions
+  bool GetMemoryReference(const Instruction* inst, MemoryReference* reference) {
+    auto* type_inst = vstate.FindDef(inst->type_id());
+    if (inst->opcode() == spv::Op::OpVariable) {
+      auto sc = type_inst->GetOperandAs<spv::StorageClass>(1u);
+      const bool is_descriptor_heap = vstate.IsDescriptorHeapBaseVariable(inst);
+      reference->storage_class = sc;
+      reference->type_id = type_inst->GetOperandAs<uint32_t>(2u);
+      const bool is_block_decorated =
+          vstate.GetIdOpcode(reference->type_id) == spv::Op::OpTypeStruct &&
+          vstate.HasDecoration(reference->type_id, spv::Decoration::Block);
+      reference->requirement = GetStorageClassRequirement(
+          sc, is_block_decorated, is_descriptor_heap);
+
+      // Unwrap the descriptor array.
+      if (sc == spv::StorageClass::StorageBuffer ||
+          sc == spv::StorageClass::Uniform ||
+          sc == spv::StorageClass::UniformConstant) {
+        const auto* data_type = vstate.FindDef(reference->type_id);
+        if (data_type->opcode() == spv::Op::OpTypeArray ||
+            data_type->opcode() == spv::Op::OpTypeRuntimeArray) {
+          reference->descriptor_array_id = reference->type_id;
+          reference->type_id = data_type->GetOperandAs<uint32_t>(1u);
+        }
+      }
+
+      const bool buffer_block =
+          vstate.GetIdOpcode(reference->type_id) == spv::Op::OpTypeStruct &&
+          vstate.HasDecoration(reference->type_id,
+                               spv::Decoration::BufferBlock);
+      reference->layout = GetStorageClassLayout(sc, buffer_block);
+
+      return true;
+    } else if (type_inst && type_inst->opcode() == spv::Op::OpTypePointer &&
+               type_inst->GetOperandAs<spv::StorageClass>(1u) ==
+                   spv::StorageClass::PhysicalStorageBuffer) {
+      reference->storage_class = spv::StorageClass::PhysicalStorageBuffer;
+      reference->type_id = type_inst->GetOperandAs<uint32_t>(2u);
+      reference->layout = GetStorageClassLayout(
+          spv::StorageClass::PhysicalStorageBuffer, false);
+      reference->requirement = LayoutRequirement::kRequired;
+
+      return true;
+    } else if (inst->opcode() == spv::Op::OpBufferPointerEXT &&
+               type_inst->opcode() == spv::Op::OpTypePointer) {
+      auto sc = type_inst->GetOperandAs<spv::StorageClass>(1u);
+      reference->storage_class = sc;
+      uint32_t pointee_ty_id = type_inst->GetOperandAs<uint32_t>(2u);
+      const bool buffer_block =
+          vstate.GetIdOpcode(pointee_ty_id) == spv::Op::OpTypeStruct &&
+          vstate.HasDecoration(pointee_ty_id, spv::Decoration::BufferBlock);
+      reference->layout = GetStorageClassLayout(sc, buffer_block);
+      reference->requirement = LayoutRequirement::kRequired;
+      reference->type_id = pointee_ty_id;
+
+      return true;
+    } else if (vstate.HasCapability(spv::Capability::UntypedPointersKHR) &&
+               spvIsVulkanEnv(vstate.context()->target_env)) {
+      uint32_t ptr_ty_id = 0;
+      uint32_t data_ty_id = 0;
+      switch (inst->opcode()) {
+        case spv::Op::OpUntypedVariableKHR:
+          if (inst->operands().size() > 3) {
+            ptr_ty_id = inst->type_id();
+            data_ty_id = inst->GetOperandAs<uint32_t>(3u);
+          } else {
+            return false;
+          }
+          break;
+        case spv::Op::OpUntypedAccessChainKHR:
+        case spv::Op::OpUntypedInBoundsAccessChainKHR:
+        case spv::Op::OpUntypedPtrAccessChainKHR:
+        case spv::Op::OpUntypedInBoundsPtrAccessChainKHR:
+          ptr_ty_id = inst->type_id();
+          data_ty_id = inst->GetOperandAs<uint32_t>(2);
+          break;
+        case spv::Op::OpLoad:
+          if (vstate.GetIdOpcode(vstate.GetOperandTypeId(inst, 2)) ==
+              spv::Op::OpTypeUntypedPointerKHR) {
+            const auto ptr_id = inst->GetOperandAs<uint32_t>(2);
+            ptr_ty_id = vstate.FindDef(ptr_id)->type_id();
+            data_ty_id = inst->type_id();
+          } else {
+            return false;
+          }
+          break;
+        case spv::Op::OpStore:
+          if (vstate.GetIdOpcode(vstate.GetOperandTypeId(inst, 0)) ==
+              spv::Op::OpTypeUntypedPointerKHR) {
+            const auto ptr_id = inst->GetOperandAs<uint32_t>(0);
+            ptr_ty_id = vstate.FindDef(ptr_id)->type_id();
+            data_ty_id = vstate.GetOperandTypeId(inst, 1);
+          } else {
+            return false;
+          }
+          break;
+        case spv::Op::OpUntypedArrayLengthKHR:
+          ptr_ty_id =
+              vstate.FindDef(inst->GetOperandAs<uint32_t>(3))->type_id();
+          data_ty_id = inst->GetOperandAs<uint32_t>(2);
+          break;
+        default:
+          return false;
+      }
+
+      // If the data type is an array that contains a Block- or
+      // BufferBlock-decorated struct, then use the struct for layout checks
+      // instead of the array. In this case, the array represents a descriptor
+      // array which should not have an explicit layout.
+      const auto* data_type = vstate.FindDef(data_ty_id);
+      if (data_type->opcode() == spv::Op::OpTypeArray ||
+          data_type->opcode() == spv::Op::OpTypeRuntimeArray) {
+        uint32_t ele_ty_id = data_type->GetOperandAs<uint32_t>(1u);
+        if (vstate.HasDecoration(ele_ty_id, spv::Decoration::Block) ||
+            vstate.HasDecoration(ele_ty_id, spv::Decoration::BufferBlock)) {
+          reference->descriptor_array_id = data_ty_id;
+          data_ty_id = ele_ty_id;
+        }
+      }
+
+      auto sc = vstate.FindDef(ptr_ty_id)->GetOperandAs<spv::StorageClass>(1u);
+      reference->storage_class = sc;
+      reference->type_id = data_ty_id;
+      reference->requirement = LayoutRequirement::kRequired;
+      reference->untyped = true;
+      const bool buffer_block =
+          vstate.GetIdOpcode(data_ty_id) == spv::Op::OpTypeStruct &&
+          vstate.HasDecoration(data_ty_id, spv::Decoration::BufferBlock);
+      reference->layout = GetStorageClassLayout(sc, buffer_block);
+
+      return true;
+    }
+
+    // Not a memory reference.
+    return false;
+  }
+
+  // Checks that no instruction in the type tree of `type_id` has any explicit
+  // layout decorations.
+  spv_result_t CheckNoLayout(const Instruction* inst, uint32_t type_id,
+                             spv::StorageClass sc) {
+    if (no_layout_cache_.count(type_id)) {
+      return SPV_SUCCESS;
+    }
+
+    const auto* type_inst = vstate.FindDef(type_id);
+    if (type_inst->opcode() == spv::Op::OpTypePointer) {
+      // PhysicalStorageBuffer and variable pointers can have ArrayStride
+      // decorations even if they are stored in a non-laid storage class (e.g.
+      // Function).
+      auto ptr_sc = type_inst->GetOperandAs<spv::StorageClass>(1u);
+      if (GetStorageClassRequirement(ptr_sc, true, false) !=
+          LayoutRequirement::kProhibited) {
+        return SPV_SUCCESS;
+      }
+    }
+
+    const auto& id_decs = vstate.id_decorations();
+    const auto iter = id_decs.find(type_id);
+    if (iter != id_decs.end()) {
+      for (const auto& d : iter->second) {
+        const spv::Decoration dec = d.dec_type();
+        if (dec == spv::Decoration::Block ||
+            dec == spv::Decoration::BufferBlock ||
+            dec == spv::Decoration::Offset ||
+            dec == spv::Decoration::OffsetIdEXT ||
+            dec == spv::Decoration::ArrayStride ||
+            dec == spv::Decoration::ArrayStrideIdEXT ||
+            dec == spv::Decoration::MatrixStride ||
+            dec == spv::Decoration::RowMajor ||
+            dec == spv::Decoration::ColMajor) {
+          return vstate.diag(SPV_ERROR_INVALID_ID, inst)
+                 << vstate.VkErrorID(10684)
+                 << "Invalid explicit layout decorations on type "
+                 << vstate.getIdName(type_id) << ", the "
+                 << spvtools::StorageClassToString(sc)
+                 << " storage class has an explicit layout from the "
+                 << vstate.SpvDecorationString(dec) << " decoration";
+        }
+      }
+    }
+
+    switch (type_inst->opcode()) {
+      case spv::Op::OpTypeStruct:
+        for (uint32_t i = 1; i < type_inst->operands().size(); i++) {
+          if (auto error = CheckNoLayout(
+                  inst, type_inst->GetOperandAs<uint32_t>(i), sc)) {
+            return error;
+          }
+        }
+        break;
+      case spv::Op::OpTypeRuntimeArray:
+      case spv::Op::OpTypeArray:
+        if (auto error = CheckNoLayout(
+                inst, type_inst->GetOperandAs<uint32_t>(1u), sc)) {
+          return error;
+        }
+        break;
+      case spv::Op::OpTypePointer: {
+        auto ptr_sc = type_inst->GetOperandAs<spv::StorageClass>(1u);
+        if (auto error = CheckNoLayout(
+                inst, type_inst->GetOperandAs<uint32_t>(2u), ptr_sc)) {
+          return error;
+        }
+        break;
+      }
+      default:
+        break;
+    }
+
+    no_layout_cache_.insert(type_id);
+
+    return SPV_SUCCESS;
+  }
+
+  // Returns true if type_id contains a matrix. Only looks through arrays.
+  bool ContainsMatrix(uint32_t type_id) {
+    const auto* type_inst = vstate.FindDef(type_id);
+    switch (type_inst->opcode()) {
+      case spv::Op::OpTypeMatrix:
+        return true;
+      case spv::Op::OpTypeArray:
+      case spv::Op::OpTypeRuntimeArray:
+        return ContainsMatrix(type_inst->GetOperandAs<uint32_t>(1u));
+      default:
+        break;
+    }
+
+    return false;
+  }
+
+  // Gets the value for an id-based layout decoration (e.g. ArrayStrideIdEXT and
+  // OffsetIdEXT). Returns max uint32_t if the value cannot be evaluated (e.g.
+  // spec constant).
+  uint32_t GetIdDecorationValue(uint32_t id) {
+    const auto* inst = vstate.FindDef(id);
+    if (!spvOpcodeIsConstant(inst->opcode())) {
+      return std::numeric_limits<uint32_t>::max();
+    }
+    uint64_t value = 0;
+    if (vstate.EvalConstantValUint64(id, &value)) {
+      return static_cast<uint32_t>(value);
+    }
+    return std::numeric_limits<uint32_t>::max();
+  }
+
+  // Returns whether an array stride decoration is present on the array and its
+  // value.
+  std::pair<bool, uint32_t> GetArrayStride(uint32_t array_id) {
+    uint32_t stride = std::numeric_limits<uint32_t>::max();
+    bool has_stride = false;
+    for (auto& d : vstate.id_decorations(array_id)) {
+      if (d.dec_type() == spv::Decoration::ArrayStride) {
+        stride = d.params()[0];
+        has_stride = true;
+        break;
+      } else if (d.dec_type() == spv::Decoration::ArrayStrideIdEXT) {
+        stride = GetIdDecorationValue(d.params()[0]);
+        has_stride = true;
+        break;
+      }
+    }
+    return std::make_pair(has_stride, stride);
+  }
+
+  // Gets the offset value from an offset decoration.
+  uint32_t GetOffset(spv::Decoration dec, uint32_t param) {
+    // param is a literal value
+    if (dec == spv::Decoration::Offset) {
+      return param;
+    }
+    // param is an id
+    return GetIdDecorationValue(param);
+  }
+
+  // Returns true if value is aligned to align.
+  bool IsAlignedTo(uint32_t value, uint32_t align) {
+    if (align == 0) return value == 0;
+    return (value % align) == 0;
+  }
+
+  // Rounds up value to next multiple of align.
+  uint32_t AlignTo(uint32_t value, uint32_t align) {
+    return (value + align - 1) & ~(align - 1);
+  }
+
+  // A member is defined to improperly straddle if either of the following are
+  // true:
+  // - It is a vector with total size less than or equal to 16 bytes, and has
+  // Offset decorations placing its first byte at F and its last byte at L,
+  // where floor(F / 16) != floor(L / 16).
+  // - It is a vector with total size greater than 16 bytes and has its Offset
+  // decorations placing its first byte at a non-integer multiple of 16.
+  bool HasImproperStraddle(uint32_t offset, uint32_t size) {
+    const auto F = offset;
+    const auto L = offset + size - 1;
+    if (size <= 16) {
+      if ((F >> 4) != (L >> 4)) return true;
+    } else {
+      if (F % 16 != 0) return true;
+    }
+    return false;
+  }
+
+  // Returns the alignment for type_id for the given layout rules.
+  uint32_t GetAlign(uint32_t type_id, LayoutMode mode,
+                    const MatrixConstraints& matrix_constraints) {
+    const auto* type_inst = vstate.FindDef(type_id);
+    uint32_t align = 1;
+    switch (type_inst->opcode()) {
+      case spv::Op::OpTypeSampledImage:
+      case spv::Op::OpTypeSampler:
+      case spv::Op::OpTypeImage:
+        if (vstate.HasCapability(spv::Capability::BindlessTextureNV)) {
+          return vstate.samplerimage_variable_address_mode() / 8;
+        }
+        if (type_inst->opcode() == spv::Op::OpTypeSampler) {
+          return vstate.options()->sampler_descriptor_layout.alignment;
+        }
+        if (type_inst->opcode() == spv::Op::OpTypeImage) {
+          return vstate.options()->image_descriptor_layout.alignment;
+        }
+        break;
+      case spv::Op::OpTypeBufferEXT:
+      case spv::Op::OpTypeAccelerationStructureKHR:
+        return vstate.options()->buffer_descriptor_layout.alignment;
+      case spv::Op::OpTypeInt:
+      case spv::Op::OpTypeFloat:
+        return type_inst->GetOperandAs<uint32_t>(1u) / 8;
+      case spv::Op::OpTypeVector:
+      case spv::Op::OpTypeVectorIdEXT: {
+        const auto ele_id = type_inst->GetOperandAs<uint32_t>(1u);
+        const auto num_eles = vstate.GetDimension(type_id);
+        align = GetAlign(ele_id, mode, {});
+        if (mode == LayoutMode::kScalar || vstate.IsRelaxedBlockLayout()) {
+          return align;
+        }
+        return align * ((num_eles == 3 || num_eles > 4) ? 4 : num_eles);
+      }
+      case spv::Op::OpTypeMatrix:
+        if (mode == LayoutMode::kScalar) {
+          const auto* vec_inst =
+              vstate.FindDef(type_inst->GetOperandAs<uint32_t>(1u));
+          const auto ele_id = vec_inst->GetOperandAs<uint32_t>(1u);
+          return GetAlign(ele_id, mode, {});
+        }
+
+        if (matrix_constraints.col_major) {
+          align = GetAlign(type_inst->GetOperandAs<uint32_t>(1u), mode, {});
+        } else {
+          // A row-major matrix of C columns has a base alignment equal to the
+          // base alignment of a vector of C matrix components.
+          const auto num_cols = type_inst->GetOperandAs<uint32_t>(2u);
+          const auto* col_inst =
+              vstate.FindDef(type_inst->GetOperandAs<uint32_t>(1u));
+          const auto ele_id = col_inst->GetOperandAs<uint32_t>(1u);
+          align = GetAlign(ele_id, mode, {});
+          // The equivalent vector may not exist so we replicate the vector rule
+          // here.
+          if (mode != LayoutMode::kScalar && !vstate.IsRelaxedBlockLayout()) {
+            align = align * (num_cols == 3 ? 4 : num_cols);
+          }
+        }
+        if (mode == LayoutMode::kExtended) {
+          align = AlignTo(align, 16u);
+        }
+        return align;
+      case spv::Op::OpTypeArray:
+      case spv::Op::OpTypeRuntimeArray:
+        align = GetAlign(type_inst->GetOperandAs<uint32_t>(1u), mode,
+                         matrix_constraints);
+        if (mode == LayoutMode::kExtended) {
+          align = AlignTo(align, 16u);
+        }
+        return align;
+      case spv::Op::OpTypeStruct:
+        for (uint32_t i = 1; i < type_inst->operands().size(); i++) {
+          const auto member_id = type_inst->GetOperandAs<uint32_t>(i);
+          MatrixConstraints mat_constraints;
+          GetMatrixConstraints(type_id, i - 1, &mat_constraints);
+          align = std::max(align, GetAlign(member_id, mode, mat_constraints));
+          if (mode == LayoutMode::kExtended) {
+            align = AlignTo(align, 16u);
+          }
+        }
+        return align;
+      case spv::Op::OpTypePointer:
+      case spv::Op::OpTypeUntypedPointerKHR:
+        return vstate.pointer_size_and_alignment();
+      default:
+        break;
+    }
+    assert(0 && "unhandled type");
+    return 1;
+  }
+
+  // Returns the size of the given type.
+  uint32_t GetSize(uint32_t type_id,
+                   const MatrixConstraints& matrix_constraints) {
+    const auto* type_inst = vstate.FindDef(type_id);
+    switch (type_inst->opcode()) {
+      case spv::Op::OpTypeSampledImage:
+      case spv::Op::OpTypeSampler:
+      case spv::Op::OpTypeImage:
+        if (vstate.HasCapability(spv::Capability::BindlessTextureNV)) {
+          return vstate.samplerimage_variable_address_mode() / 8;
+        }
+        if (type_inst->opcode() == spv::Op::OpTypeSampler) {
+          return vstate.options()->sampler_descriptor_layout.size;
+        }
+        if (type_inst->opcode() == spv::Op::OpTypeImage) {
+          return vstate.options()->image_descriptor_layout.size;
+        }
+        break;
+      case spv::Op::OpTypeBufferEXT:
+      case spv::Op::OpTypeAccelerationStructureKHR:
+        return vstate.options()->buffer_descriptor_layout.size;
+      case spv::Op::OpTypeInt:
+      case spv::Op::OpTypeFloat:
+        return type_inst->GetOperandAs<uint32_t>(1u) / 8;
+      case spv::Op::OpTypeVector:
+      case spv::Op::OpTypeVectorIdEXT: {
+        const auto ele_id = type_inst->GetOperandAs<uint32_t>(1u);
+        const auto num_eles = vstate.GetDimension(type_id);
+        return GetSize(ele_id, {}) * num_eles;
+      }
+      case spv::Op::OpTypeArray: {
+        const auto count_id = type_inst->GetOperandAs<uint32_t>(2u);
+        uint64_t count = 0;
+        if (!vstate.EvalConstantValUint64(count_id, &count)) {
+          return 0;
+        }
+        const auto [has_stride, stride] = GetArrayStride(type_id);
+        const auto ele_size =
+            GetSize(type_inst->GetOperandAs<uint32_t>(1u), matrix_constraints);
+        // uint32 max is a marker for unevaluatable.
+        if (stride == std::numeric_limits<uint32_t>::max()) {
+          return ele_size;
+        }
+        return (static_cast<uint32_t>(count) - 1) * stride + ele_size;
+      }
+      case spv::Op::OpTypeRuntimeArray:
+        return 0;
+      case spv::Op::OpTypeMatrix: {
+        const auto num_cols = type_inst->GetOperandAs<uint32_t>(2u);
+        if (matrix_constraints.col_major) {
+          return num_cols * matrix_constraints.stride;
+        } else {
+          const auto* col_inst =
+              vstate.FindDef(type_inst->GetOperandAs<uint32_t>(1u));
+          const auto ele_id = col_inst->GetOperandAs<uint32_t>(1u);
+          const auto num_rows = col_inst->GetOperandAs<uint32_t>(2u);
+          return (num_rows - 1) * matrix_constraints.stride +
+                 num_cols * GetSize(ele_id, {});
+        }
+      }
+      case spv::Op::OpTypeStruct: {
+        const auto& members = GetStructMembers(type_id);
+        if (members.empty()) return 0;
+        const auto& last = members.back();
+        if (last.offset == std::numeric_limits<uint32_t>::max()) {
+          return 0;
+        }
+        return last.offset +
+               GetSize(type_inst->GetOperandAs<uint32_t>(last.index + 1),
+                       last.matrix_constraints);
+      }
+      case spv::Op::OpTypePointer:
+      case spv::Op::OpTypeUntypedPointerKHR:
+        return vstate.pointer_size_and_alignment();
+      default:
+        break;
+    }
+    assert(0 && "unhandled type");
+    return 0;
+  }
+
+  // Returns true if type_id has a matrix and populates matrix_constraints.
+  bool GetMatrixConstraints(uint32_t type_id, uint32_t index,
+                            MatrixConstraints* matrix_constraints) {
+    bool has_matrix = false;
+    auto member_decorations = vstate.id_member_decorations(type_id, index);
+    for (auto decoration = member_decorations.begin;
+         decoration != member_decorations.end; ++decoration) {
+      if (decoration->dec_type() == spv::Decoration::ColMajor ||
+          decoration->dec_type() == spv::Decoration::RowMajor) {
+        has_matrix = true;
+        matrix_constraints->col_major =
+            decoration->dec_type() == spv::Decoration::ColMajor;
+      }
+      if (decoration->dec_type() == spv::Decoration::MatrixStride) {
+        matrix_constraints->stride = decoration->params()[0];
+      }
+    }
+    return has_matrix;
+  }
+
+  // Gathers (and caches) structure members and their decorations.
+  const std::vector<MemberInfo>& GetStructMembers(uint32_t type_id) {
+    if (struct_members_.count(type_id)) {
+      return struct_members_[type_id];
+    }
+
+    const auto* type_inst = vstate.FindDef(type_id);
+    std::vector<MemberInfo> member_info;
+    member_info.reserve(type_inst->operands().size() - 1);
+    for (uint32_t i = 1; i < type_inst->operands().size(); i++) {
+      auto member_idx = i - 1;
+      auto member_decorations =
+          vstate.id_member_decorations(type_id, member_idx);
+      member_info.push_back(MemberInfo{member_idx});
+      auto& member = member_info.back();
+      member.has_matrix =
+          GetMatrixConstraints(type_id, member_idx, &member.matrix_constraints);
+      for (auto decoration = member_decorations.begin;
+           decoration != member_decorations.end; ++decoration) {
+        switch (decoration->dec_type()) {
+          case spv::Decoration::Offset:
+          case spv::Decoration::OffsetIdEXT:
+            member.has_offset = true;
+            member.offset =
+                GetOffset(decoration->dec_type(), decoration->params()[0]);
+            break;
+          default:
+            break;
+        }
+      }
+    }
+    // Sort by offset value.
+    std::stable_sort(member_info.begin(), member_info.end(),
+                     [](const MemberInfo& lhs, const MemberInfo& rhs) {
+                       return lhs.offset < rhs.offset;
+                     });
+    struct_members_[type_id] = std::move(member_info);
+    return struct_members_[type_id];
+  }
+
+  // Returns some common messaging to improve diagnostics.
+  std::string CommonError(const Instruction* inst,
+                          spv::StorageClass storage_class, LayoutMode mode) {
+    std::string s;
+    std::ostringstream str(s);
+    str << " Instantiated via " << vstate.getIdName(inst->id()) << " in the "
+        << spvtools::StorageClassToString(storage_class)
+        << " storage class using " << mode << " layout rules.";
+    if (mode != LayoutMode::kScalar) {
+      if (storage_class == spv::StorageClass::Workgroup) {
+        str << vstate.MissingFeature(
+            "workgroupMemoryExplicitLayoutScalarBlockLayout feature",
+            "--workgroup-scalar-block-layout", true);
+      } else if (!vstate.IsRelaxedBlockLayout()) {
+        str << vstate.MissingFeature("VK_KHR_relaxed_block_layout extension",
+                                     "--relax-block-layout", true);
+      } else if (storage_class == spv::StorageClass::Uniform &&
+                 mode != LayoutMode::kStandard) {
+        str << vstate.MissingFeature("uniformBufferStandardLayout feature",
+                                     "--uniform-buffer-standard-layout", true);
+      } else {
+        str << vstate.MissingFeature("scalarBlockLayout feature",
+                                     "--scalar-block-layout", true);
+      }
+    }
+    return str.str();
+  }
+
+  // Checks struct layouts
+  // * Each member must
+  //   * Have an offset decoration
+  //   * If the member is a matrix or array(s) of matrices
+  //     * Must have majorness and matrix stride decorations
+  //   * If member is a runtime array, it must be last by offset
+  //   * Offset must be aligned
+  //   * Total offset must be aligned
+  //   * Offset + size < next elements offset
+  //   * If member is a vector, it must have a valid straddle
+  //   * The member has a valid layout
+  spv_result_t CheckStructLayout(const Instruction* inst, uint32_t type_id,
+                                 spv::StorageClass storage_class,
+                                 LayoutMode mode, uint32_t incoming_offset,
+                                 const MatrixConstraints& matrix_constraints) {
+    const auto* type_inst = vstate.FindDef(type_id);
+    const auto& member_info = GetStructMembers(type_id);
+    for (auto& member : member_info) {
+      const auto member_id =
+          type_inst->GetOperandAs<uint32_t>(member.index + 1);
+      if (!member.has_offset) {
+        return vstate.diag(SPV_ERROR_INVALID_ID, type_inst)
+               << "Structure member " << member.index
+               << " must be explicitly laid out with Offset or OffsetIdEXT "
+                  "decorations."
+               << CommonError(inst, storage_class, mode);
+      }
+      if (ContainsMatrix(member_id)) {
+        if (!member.has_matrix) {
+          return vstate.diag(SPV_ERROR_INVALID_ID, type_inst)
+                 << "Structure member " << member.index
+                 << " containing a matrix must be explicitly laid out "
+                    "with RowMajor or ColMajor decorations."
+                 << CommonError(inst, storage_class, mode);
+        } else if (member.matrix_constraints.stride == 0) {
+          return vstate.diag(SPV_ERROR_INVALID_ID, type_inst)
+                 << "Structure member " << member.index
+                 << " containing a matrix must be explicitly laid out "
+                    "with MatrixStride decorations."
+                 << CommonError(inst, storage_class, mode);
+        }
+      }
+    }
+
+    uint32_t next_offset = 0;
+    uint32_t ordered_index = 0;
+    for (const auto& member : member_info) {
+      auto member_idx = member.index;
+      auto offset = member.offset;
+      // We have no information
+      if (offset == std::numeric_limits<uint32_t>::max()) {
+        continue;
+      }
+      auto member_id = type_inst->GetOperandAs<uint32_t>(member_idx + 1);
+      auto member_inst = vstate.FindDef(member_id);
+      if (member_inst->opcode() == spv::Op::OpTypeRuntimeArray &&
+          ordered_index != member_info.size() - 1) {
+        return vstate.diag(SPV_ERROR_INVALID_ID, type_inst)
+               << vstate.VkErrorID(4680)
+               << "Structure has a runtime array at offset " << offset
+               << ", but other members at larger offsets."
+               << CommonError(inst, storage_class, mode);
+      }
+      ordered_index++;
+
+      MatrixConstraints mat_constraints = matrix_constraints;
+      if (member.has_matrix) {
+        mat_constraints = member.matrix_constraints;
+      }
+      uint32_t align = GetAlign(member_id, mode, mat_constraints);
+      if (!IsAlignedTo(offset, align)) {
+        return vstate.diag(SPV_ERROR_INVALID_ID, type_inst)
+               << "Structure member " << member_idx << " at offset " << offset
+               << " is not aligned to " << align << "."
+               << CommonError(inst, storage_class, mode);
+      }
+      if (!IsAlignedTo(offset + incoming_offset, align)) {
+        return vstate.diag(SPV_ERROR_INVALID_ID, type_inst)
+               << "Structure member " << member_idx << " at offset " << offset
+               << " plus incoming offset " << incoming_offset
+               << " is not aligned to " << align << "."
+               << CommonError(inst, storage_class, mode);
+      }
+      if (offset < next_offset) {
+        return vstate.diag(SPV_ERROR_INVALID_ID, type_inst)
+               << "Structure member " << member_idx << " at offset " << offset
+               << " overlaps previous member ending at offset "
+               << next_offset - 1 << "."
+               << CommonError(inst, storage_class, mode);
+      }
+      if (mode != LayoutMode::kScalar && vstate.IsRelaxedBlockLayout()) {
+        if (member_inst->opcode() == spv::Op::OpTypeVector ||
+            member_inst->opcode() == spv::Op::OpTypeVectorIdEXT) {
+          uint32_t size = GetSize(member_id, {});
+          if (HasImproperStraddle(incoming_offset + offset, size)) {
+            return vstate.diag(SPV_ERROR_INVALID_ID, type_inst)
+                   << "Structure member " << member_idx
+                   << ": Vector has improper straddle due to offset "
+                   << incoming_offset + offset << "."
+                   << CommonError(inst, storage_class, mode);
+          }
+        }
+      }
+      if (auto error = CheckLayout(inst, member_id, storage_class, mode,
+                                   incoming_offset + offset, mat_constraints)) {
+        return error;
+      }
+
+      uint32_t size = GetSize(member_id, mat_constraints);
+      next_offset = size + offset;
+      if (mode != LayoutMode::kScalar &&
+          (member_inst->opcode() == spv::Op::OpTypeArray ||
+           member_inst->opcode() == spv::Op::OpTypeStruct)) {
+        next_offset = AlignTo(next_offset, align);
+      }
+    }
+
+    return SPV_SUCCESS;
+  }
+
+  // Checks array layouts
+  // * Arrays must have stride decoration
+  // * Stride must be non-zero
+  // * Stride must be aligned
+  // * Stride must be greater or equal to element size
+  // * Elements have valid layout
+  spv_result_t CheckArrayLayout(const Instruction* inst, uint32_t type_id,
+                                spv::StorageClass storage_class,
+                                LayoutMode mode, uint32_t incoming_offset,
+                                const MatrixConstraints& matrix_constraints) {
+    const auto* type_inst = vstate.FindDef(type_id);
+    auto [has_stride, stride] = GetArrayStride(type_id);
+    if (!has_stride) {
+      return vstate.diag(SPV_ERROR_INVALID_ID, type_inst)
+             << "Array must be explicitly laid out with ArrayStride or "
+                "ArrayStrideIdEXT decorations."
+             << CommonError(inst, storage_class, mode);
+    }
+    uint32_t ele_id = type_inst->GetOperandAs<uint32_t>(1u);
+    uint32_t ele_size = GetSize(ele_id, matrix_constraints);
+    uint32_t align = GetAlign(type_id, mode, matrix_constraints);
+    if (stride == 0) {
+      return vstate.diag(SPV_ERROR_INVALID_ID, type_inst)
+             << "Array must not have a stride of 0."
+             << CommonError(inst, storage_class, mode);
+    }
+
+    // uint32 max stride is unevaluatable (e.g. spec constant).
+    if (stride == std::numeric_limits<uint32_t>::max()) {
+      return SPV_SUCCESS;
+    }
+
+    if (!IsAlignedTo(stride, align)) {
+      return vstate.diag(SPV_ERROR_INVALID_ID, type_inst)
+             << "Array stride " << stride << " must satisfy alignment " << align
+             << "." << CommonError(inst, storage_class, mode);
+    }
+    if (stride != std::numeric_limits<uint32_t>::max() && stride < ele_size) {
+      return vstate.diag(SPV_ERROR_INVALID_ID, type_inst)
+             << "Array stride " << stride
+             << " is smaller than element type size " << ele_size << "."
+             << CommonError(inst, storage_class, mode);
+    }
+
+    uint32_t num_elements = 0;
+    if (type_inst->opcode() == spv::Op::OpTypeArray) {
+      uint64_t count = 0;
+      if (vstate.EvalConstantValUint64(type_inst->GetOperandAs<uint32_t>(2u),
+                                       &count)) {
+        num_elements = static_cast<uint32_t>(count);
+      }
+    }
+    num_elements = std::max(1u, num_elements);
+    std::vector<bool> seen(16, false);
+    for (uint32_t i = 0; i < num_elements; ++i) {
+      uint32_t next_offset = i * stride + incoming_offset;
+      // Stop checking if offsets repeat in terms of 16-byte multiples.
+      if (seen[next_offset % 16]) {
+        break;
+      }
+
+      if (auto error = CheckLayout(inst, ele_id, storage_class, mode,
+                                   next_offset, matrix_constraints)) {
+        return error;
+      }
+
+      seen[next_offset % 16] = true;
+    }
+    return SPV_SUCCESS;
+  }
+
+  // Checks the layout matrices
+  // * Stride must be a multiple of align
+  // * Stride must be greater or equal to minor size
+  spv_result_t CheckMatrixLayout(const Instruction* inst, uint32_t type_id,
+                                 spv::StorageClass storage_class,
+                                 LayoutMode mode,
+                                 const MatrixConstraints& matrix_constraints) {
+    // We already checked that any struct containing a matrix has a non-zero
+    // stride so if we have 0 stride here then it will come from the result
+    // of an access chain or other instruction. Other rules are meant to
+    // catch any misuse we can skip it here.
+    if (matrix_constraints.stride == 0) {
+      return SPV_SUCCESS;
+    }
+    const auto* type_inst = vstate.FindDef(type_id);
+    const auto align = GetAlign(type_id, mode, matrix_constraints);
+    if (!IsAlignedTo(matrix_constraints.stride, align)) {
+      return vstate.diag(SPV_ERROR_INVALID_ID, type_inst)
+             << "Matrix with a stride " << matrix_constraints.stride
+             << " not satisfying alignment to " << align << "."
+             << CommonError(inst, storage_class, mode) << "\n";
+    }
+    const auto ele_id = type_inst->GetOperandAs<uint32_t>(1u);
+    uint32_t size = 0;
+    if (matrix_constraints.col_major) {
+      size = GetSize(ele_id, {});
+    } else {
+      // Element size is # cols * ele size.
+      const auto* ele_inst = vstate.FindDef(ele_id);
+      const auto scalar_id = ele_inst->GetOperandAs<uint32_t>(1u);
+      size = GetSize(scalar_id, {}) * type_inst->GetOperandAs<uint32_t>(2u);
+    }
+    if (matrix_constraints.stride < size) {
+      return vstate.diag(SPV_ERROR_INVALID_ID, type_inst)
+             << "Matrix stride " << matrix_constraints.stride
+             << " is smaller than column size " << size << "."
+             << CommonError(inst, storage_class, mode);
+    }
+    return SPV_SUCCESS;
+  }
+
+  // Returns true if type_id satisfies the given layout rules.
+  spv_result_t CheckLayout(const Instruction* inst, uint32_t type_id,
+                           spv::StorageClass storage_class, LayoutMode mode,
+                           uint32_t incoming_offset,
+                           const MatrixConstraints& matrix_constraints) {
+    if (vstate.options()->skip_block_layout) {
+      return SPV_SUCCESS;
+    }
+
+    LayoutKey key{type_id, mode, incoming_offset, matrix_constraints};
+    if (layout_cache_.count(key)) {
+      return SPV_SUCCESS;
+    }
+
+    const auto* type_inst = vstate.FindDef(type_id);
+    switch (type_inst->opcode()) {
+      case spv::Op::OpTypeStruct:
+        if (auto error =
+                CheckStructLayout(inst, type_id, storage_class, mode,
+                                  incoming_offset, matrix_constraints)) {
+          return error;
+        }
+        break;
+      case spv::Op::OpTypeArray:
+      case spv::Op::OpTypeRuntimeArray:
+        if (auto error =
+                CheckArrayLayout(inst, type_id, storage_class, mode,
+                                 incoming_offset, matrix_constraints)) {
+          return error;
+        }
+        break;
+      case spv::Op::OpTypeMatrix:
+        if (auto error = CheckMatrixLayout(inst, type_id, storage_class, mode,
+                                           matrix_constraints)) {
+          return error;
+        }
+        break;
+      default:
+        break;
+    }
+
+    layout_cache_.insert(key);
+
+    return SPV_SUCCESS;
+  }
+
+  // Performs a single pass over the IR and for each memory reference determines
+  // what validation is necessary.
+  spv_result_t Run() {
+    if (!spvIsVulkanEnv(vstate.context()->target_env)) {
+      return SPV_SUCCESS;
+    }
+
+    for (const auto& inst : vstate.ordered_instructions()) {
+      MemoryReference reference;
+      if (!GetMemoryReference(&inst, &reference)) {
+        continue;
+      }
+
+      // Descriptor arrays shouldn't have a stride. Check for that here since
+      // most descriptors require a layout.
+      if (reference.descriptor_array_id != 0) {
+        const bool array_stride = vstate.HasDecoration(
+            reference.descriptor_array_id, spv::Decoration::ArrayStride);
+        const bool array_stride_id = vstate.HasDecoration(
+            reference.descriptor_array_id, spv::Decoration::ArrayStrideIdEXT);
+        if (array_stride || array_stride_id) {
+          return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
+                 << vstate.VkErrorID(10684)
+                 << "Invalid explicit layout decorations on type "
+                 << vstate.getIdName(reference.descriptor_array_id) << ", the "
+                 << spvtools::StorageClassToString(reference.storage_class)
+                 << " storage class has an explicit layout from the "
+                 << vstate.SpvDecorationString(
+                        array_stride ? spv::Decoration::ArrayStride
+                                     : spv::Decoration::ArrayStrideIdEXT)
+                 << " decoration";
+        }
+      }
+
+      // Untyped pointers require a layout. Workgroup variables must be blocks
+      // to have a layout.
+      if (reference.untyped &&
+          reference.storage_class == spv::StorageClass::Workgroup &&
+          inst.opcode() == spv::Op::OpUntypedVariableKHR &&
+          (vstate.GetIdOpcode(reference.type_id) != spv::Op::OpTypeStruct ||
+           !vstate.HasDecoration(reference.type_id, spv::Decoration::Block))) {
+        return vstate.diag(SPV_ERROR_INVALID_ID, &inst)
+               << vstate.VkErrorID(10684)
+               << "Untyped variables in Workgroup storage class must be "
+                  "block-decorated structs";
+      }
+
+      if (reference.requirement == LayoutRequirement::kRequired) {
+        if (auto error =
+                CheckLayout(&inst, reference.type_id, reference.storage_class,
+                            reference.layout, 0, {})) {
+          return error;
+        }
+      } else if (reference.requirement == LayoutRequirement::kProhibited) {
+        if (auto error = CheckNoLayout(&inst, reference.type_id,
+                                       reference.storage_class)) {
+          return error;
+        }
+      }
+    }
+    return SPV_SUCCESS;
+  }
+};
+
+}  // namespace
+
+spv_result_t ValidateExplicitLayout(ValidationState_t& vstate) {
+  return Impl{vstate}.Run();
+}
+
+}  // namespace val
+}  // namespace spvtools
diff --git a/source/val/validate_memory.cpp b/source/val/validate_memory.cpp
index 8a825c1..761063b 100644
--- a/source/val/validate_memory.cpp
+++ b/source/val/validate_memory.cpp
@@ -2824,21 +2824,29 @@
 
 spv_result_t ValidateBufferPointerEXT(ValidationState_t& _,
                                       const Instruction* inst) {
-  const auto storage_class_ptr = _.FindDef(inst->GetOperandAs<uint32_t>(0));
+  const auto storage_class_ptr = _.FindDef(inst->type_id());
   if (storage_class_ptr->opcode() != spv::Op::OpTypeUntypedPointerKHR &&
       storage_class_ptr->opcode() != spv::Op::OpTypePointer) {
     return _.diag(SPV_ERROR_INVALID_ID, inst)
            << "OpBufferPointerEXT's Result Type should be "
            << "a pointer type.";
-  } else {
-    // Buffer operand
-    auto buffer =
-        _.FindUntypedBaseVariable(_.FindDef(inst->GetOperandAs<uint32_t>(2)));
-    if (!buffer || !_.IsBuiltin(buffer->id(), spv::BuiltIn::ResourceHeapEXT)) {
-      return _.diag(SPV_ERROR_INVALID_ID, inst)
-             << "OpBufferPointerEXT's buffer must be an untyped pointer"
-             << " into a variable declared with the ResourceHeapEXT built-in";
-    }
+  }
+
+  auto sc = storage_class_ptr->GetOperandAs<spv::StorageClass>(1u);
+  if (sc != spv::StorageClass::StorageBuffer &&
+      sc != spv::StorageClass::Uniform) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "OpBufferPointerEXT Result Type must be a pointer type "
+           << "with a Storage Class of Uniform or StorageBuffer.";
+  }
+
+  // Buffer operand
+  auto buffer =
+      _.FindUntypedBaseVariable(_.FindDef(inst->GetOperandAs<uint32_t>(2)));
+  if (!buffer || !_.IsBuiltin(buffer->id(), spv::BuiltIn::ResourceHeapEXT)) {
+    return _.diag(SPV_ERROR_INVALID_ID, inst)
+           << "OpBufferPointerEXT's buffer must be an untyped pointer"
+           << " into a variable declared with the ResourceHeapEXT built-in";
   }
   return SPV_SUCCESS;
 }
diff --git a/source/val/validation_state.cpp b/source/val/validation_state.cpp
index 27bfc18..fe57eea 100644
--- a/source/val/validation_state.cpp
+++ b/source/val/validation_state.cpp
@@ -1849,6 +1849,27 @@
 
   if (inst->opcode() == spv::Op::OpConstantNull) {
     *val = 0;
+  } else if (inst->opcode() == spv::Op::OpConstantSizeOfEXT) {
+    auto type_op = GetIdOpcode(inst->GetOperandAs<uint32_t>(2u));
+    *val = 0;
+    switch (type_op) {
+      case spv::Op::OpTypeBufferEXT:
+      case spv::Op::OpTypeAccelerationStructureKHR:
+        *val = options()->buffer_descriptor_layout.size;
+        break;
+      case spv::Op::OpTypeSampler:
+        *val = options()->sampler_descriptor_layout.size;
+        break;
+      case spv::Op::OpTypeImage:
+        *val = options()->image_descriptor_layout.size;
+        break;
+      case spv::Op::OpTypeTensorARM:
+        *val = options()->tensor_descriptor_layout.size;
+        break;
+      default:
+        break;
+    }
+    return *val > 0;
   } else if (inst->opcode() != spv::Op::OpConstant) {
     // Spec constant values cannot be evaluated so don't consider constant for
     // static validation
@@ -1874,6 +1895,27 @@
 
   if (inst->opcode() == spv::Op::OpConstantNull) {
     *val = 0;
+  } else if (inst->opcode() == spv::Op::OpConstantSizeOfEXT) {
+    auto type_op = GetIdOpcode(inst->GetOperandAs<uint32_t>(2u));
+    *val = 0;
+    switch (type_op) {
+      case spv::Op::OpTypeBufferEXT:
+      case spv::Op::OpTypeAccelerationStructureKHR:
+        *val = static_cast<int64_t>(options()->buffer_descriptor_layout.size);
+        break;
+      case spv::Op::OpTypeSampler:
+        *val = static_cast<int64_t>(options()->sampler_descriptor_layout.size);
+        break;
+      case spv::Op::OpTypeImage:
+        *val = static_cast<int64_t>(options()->image_descriptor_layout.size);
+        break;
+      case spv::Op::OpTypeTensorARM:
+        *val = static_cast<int64_t>(options()->tensor_descriptor_layout.size);
+        break;
+      default:
+        break;
+    }
+    return *val > 0;
   } else if (inst->opcode() != spv::Op::OpConstant) {
     // Spec constant values cannot be evaluated so don't consider constant for
     // static validation
diff --git a/test/opt/remove_unused_interface_variables_test.cpp b/test/opt/remove_unused_interface_variables_test.cpp
index bb273e5..2d5fb3a 100644
--- a/test/opt/remove_unused_interface_variables_test.cpp
+++ b/test/opt/remove_unused_interface_variables_test.cpp
@@ -195,6 +195,7 @@
 ; CHECK:                       OpEntryPoint Fragment %1 "main" %2
                                OpExecutionMode %1 OriginUpperLeft
                                OpDecorate %2 BuiltIn ResourceHeapEXT
+                               OpDecorateId %_runtimearr_type_buffer_ext ArrayStrideIdEXT %buffer_size
                        %uint = OpTypeInt 32 0
                       %float = OpTypeFloat 32
                     %v4float = OpTypeVector %float 4
@@ -206,6 +207,7 @@
           %type_buffer_image = OpTypeImage %float Buffer 2 0 0 1 Rgba32f
         %type_buffer_image_0 = OpTypeImage %float Buffer 2 0 0 2 Rgba32f
             %type_buffer_ext = OpTypeBufferEXT StorageBuffer
+                %buffer_size = OpConstantSizeOfEXT %uint %type_buffer_ext
 %_runtimearr_type_buffer_ext = OpTypeRuntimeArray %type_buffer_ext
                           %2 = OpUntypedVariableKHR %type_untyped_pointer UniformConstant
                           %1 = OpFunction %void None %10
diff --git a/test/val/CMakeLists.txt b/test/val/CMakeLists.txt
index 91a3dd5..73ed334 100644
--- a/test/val/CMakeLists.txt
+++ b/test/val/CMakeLists.txt
@@ -38,6 +38,7 @@
        val_derivatives_test.cpp
        val_dot_product_test.cpp
        val_entry_point_test.cpp
+       val_explicit_layout_test.cpp
        val_explicit_reserved_test.cpp
        val_invalid_type_test.cpp
        val_extensions_test.cpp
diff --git a/test/val/val_decoration_test.cpp b/test/val/val_decoration_test.cpp
index 6447be9..53cef8b 100644
--- a/test/val/val_decoration_test.cpp
+++ b/test/val/val_decoration_test.cpp
@@ -1073,130 +1073,6 @@
   EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a structure type"));
 }
 
-TEST_F(ValidateDecorations, BlockMissingOffsetBad) {
-  std::string spirv = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint GLCompute %main "main"
-               OpExecutionMode %main LocalSize 1 1 1
-               OpSource GLSL 430
-               OpDecorate %Output Block
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-     %Output = OpTypeStruct %float
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("must be explicitly laid out with Offset decorations"));
-}
-
-TEST_F(ValidateDecorations, BufferBlockMissingOffsetBad) {
-  std::string spirv = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint GLCompute %main "main"
-               OpExecutionMode %main LocalSize 1 1 1
-               OpSource GLSL 430
-               OpDecorate %Output BufferBlock
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-     %Output = OpTypeStruct %float
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("must be explicitly laid out with Offset decorations"));
-}
-
-TEST_F(ValidateDecorations, BlockNestedStructMissingOffsetBad) {
-  std::string spirv = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint GLCompute %main "main"
-               OpExecutionMode %main LocalSize 1 1 1
-               OpSource GLSL 430
-               OpMemberDecorate %S 0 Offset 0
-               OpMemberDecorate %Output 0 Offset 0
-               OpMemberDecorate %Output 1 Offset 16
-               OpMemberDecorate %Output 2 Offset 32
-               OpDecorate %Output Block
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v4float = OpTypeVector %float 4
-    %v3float = OpTypeVector %float 3
-        %int = OpTypeInt 32 1
-          %S = OpTypeStruct %v3float %int
-     %Output = OpTypeStruct %float %v4float %S
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("must be explicitly laid out with Offset decorations"));
-}
-
-TEST_F(ValidateDecorations, BufferBlockNestedStructMissingOffsetBad) {
-  std::string spirv = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint GLCompute %main "main"
-               OpExecutionMode %main LocalSize 1 1 1
-               OpSource GLSL 430
-               OpMemberDecorate %S 0 Offset 0
-               OpMemberDecorate %Output 0 Offset 0
-               OpMemberDecorate %Output 1 Offset 16
-               OpMemberDecorate %Output 2 Offset 32
-               OpDecorate %Output BufferBlock
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v4float = OpTypeVector %float 4
-    %v3float = OpTypeVector %float 3
-        %int = OpTypeInt 32 1
-          %S = OpTypeStruct %v3float %int
-     %Output = OpTypeStruct %float %v4float %S
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("must be explicitly laid out with Offset decorations"));
-}
-
 TEST_F(ValidateDecorations, BlockGLSLSharedBad) {
   std::string spirv = R"(
                OpCapability Shader
@@ -1221,8 +1097,7 @@
   )";
 
   CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
   EXPECT_THAT(
       getDiagnosticString(),
       HasSubstr(
@@ -1493,440 +1368,6 @@
               HasSubstr("[VUID-StandaloneSpirv-GLSLShared-04669]"));
 }
 
-TEST_F(ValidateDecorations, BlockMissingArrayStrideBad) {
-  std::string spirv = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint GLCompute %main "main"
-               OpExecutionMode %main LocalSize 1 1 1
-               OpSource GLSL 430
-               OpDecorate %Output Block
-               OpMemberDecorate %Output 0 Offset 0
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-        %int = OpTypeInt 32 1
-      %int_3 = OpConstant %int 3
-      %array = OpTypeArray %float %int_3
-     %Output = OpTypeStruct %array
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("must be explicitly laid out with ArrayStride or "
-                        "ArrayStrideIdEXT decorations"));
-}
-
-TEST_F(ValidateDecorations, BufferBlockMissingArrayStrideBad) {
-  std::string spirv = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint GLCompute %main "main"
-               OpExecutionMode %main LocalSize 1 1 1
-               OpSource GLSL 430
-               OpDecorate %Output BufferBlock
-               OpMemberDecorate %Output 0 Offset 0
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-        %int = OpTypeInt 32 1
-      %int_3 = OpConstant %int 3
-      %array = OpTypeArray %float %int_3
-     %Output = OpTypeStruct %array
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("must be explicitly laid out with ArrayStride or "
-                        "ArrayStrideIdEXT decorations"));
-}
-
-TEST_F(ValidateDecorations, BlockNestedStructMissingArrayStrideBad) {
-  std::string spirv = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint GLCompute %main "main"
-               OpExecutionMode %main LocalSize 1 1 1
-               OpSource GLSL 430
-               OpMemberDecorate %S 0 Offset 0
-               OpMemberDecorate %Output 0 Offset 0
-               OpMemberDecorate %Output 1 Offset 16
-               OpMemberDecorate %Output 2 Offset 32
-               OpDecorate %Output Block
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v4float = OpTypeVector %float 4
-        %int = OpTypeInt 32 1
-      %int_3 = OpConstant %int 3
-      %array = OpTypeArray %float %int_3
-          %S = OpTypeStruct %array
-     %Output = OpTypeStruct %float %v4float %S
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("must be explicitly laid out with ArrayStride or "
-                        "ArrayStrideIdEXT decorations"));
-}
-
-TEST_F(ValidateDecorations, BufferBlockNestedStructMissingArrayStrideBad) {
-  std::string spirv = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint GLCompute %main "main"
-               OpExecutionMode %main LocalSize 1 1 1
-               OpSource GLSL 430
-               OpMemberDecorate %S 0 Offset 0
-               OpMemberDecorate %Output 0 Offset 0
-               OpMemberDecorate %Output 1 Offset 16
-               OpMemberDecorate %Output 2 Offset 32
-               OpDecorate %Output BufferBlock
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v4float = OpTypeVector %float 4
-        %int = OpTypeInt 32 1
-      %int_3 = OpConstant %int 3
-      %array = OpTypeArray %float %int_3
-          %S = OpTypeStruct %array
-     %Output = OpTypeStruct %float %v4float %S
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("must be explicitly laid out with ArrayStride or "
-                        "ArrayStrideIdEXT decorations"));
-}
-
-TEST_F(ValidateDecorations, BlockMissingMatrixStrideBad) {
-  std::string spirv = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint GLCompute %main "main"
-               OpExecutionMode %main LocalSize 1 1 1
-               OpSource GLSL 430
-               OpDecorate %Output Block
-               OpMemberDecorate %Output 0 Offset 0
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v3float = OpTypeVector %float 3
-     %matrix = OpTypeMatrix %v3float 4
-     %Output = OpTypeStruct %matrix
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("must be explicitly laid out with MatrixStride decorations"));
-}
-
-TEST_F(ValidateDecorations, BufferBlockMissingMatrixStrideBad) {
-  std::string spirv = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint GLCompute %main "main"
-               OpExecutionMode %main LocalSize 1 1 1
-               OpSource GLSL 430
-               OpDecorate %Output BufferBlock
-               OpMemberDecorate %Output 0 Offset 0
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v3float = OpTypeVector %float 3
-     %matrix = OpTypeMatrix %v3float 4
-     %Output = OpTypeStruct %matrix
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("must be explicitly laid out with MatrixStride decorations"));
-}
-
-TEST_F(ValidateDecorations, BlockMissingMatrixStrideArrayBad) {
-  std::string spirv = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint GLCompute %main "main"
-               OpExecutionMode %main LocalSize 1 1 1
-               OpSource GLSL 430
-               OpDecorate %Output Block
-               OpMemberDecorate %Output 0 Offset 0
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v3float = OpTypeVector %float 3
-     %matrix = OpTypeMatrix %v3float 4
-        %int = OpTypeInt 32 1
-      %int_3 = OpConstant %int 3
-      %array = OpTypeArray %matrix %int_3
-     %Output = OpTypeStruct %matrix
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("must be explicitly laid out with MatrixStride decorations"));
-}
-
-TEST_F(ValidateDecorations, BufferBlockMissingMatrixStrideArrayBad) {
-  std::string spirv = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint GLCompute %main "main"
-               OpExecutionMode %main LocalSize 1 1 1
-               OpSource GLSL 430
-               OpDecorate %Output BufferBlock
-               OpMemberDecorate %Output 0 Offset 0
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v3float = OpTypeVector %float 3
-     %matrix = OpTypeMatrix %v3float 4
-        %int = OpTypeInt 32 1
-      %int_3 = OpConstant %int 3
-      %array = OpTypeArray %matrix %int_3
-     %Output = OpTypeStruct %matrix
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("must be explicitly laid out with MatrixStride decorations"));
-}
-
-TEST_F(ValidateDecorations, BlockNestedStructMissingMatrixStrideBad) {
-  std::string spirv = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint GLCompute %main "main"
-               OpExecutionMode %main LocalSize 1 1 1
-               OpSource GLSL 430
-               OpMemberDecorate %S 0 Offset 0
-               OpMemberDecorate %Output 0 Offset 0
-               OpMemberDecorate %Output 1 Offset 16
-               OpMemberDecorate %Output 2 Offset 32
-               OpDecorate %Output Block
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v3float = OpTypeVector %float 3
-    %v4float = OpTypeVector %float 4
-     %matrix = OpTypeMatrix %v3float 4
-          %S = OpTypeStruct %matrix
-     %Output = OpTypeStruct %float %v4float %S
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("must be explicitly laid out with MatrixStride decorations"));
-}
-
-TEST_F(ValidateDecorations, BufferBlockNestedStructMissingMatrixStrideBad) {
-  std::string spirv = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint GLCompute %main "main"
-               OpExecutionMode %main LocalSize 1 1 1
-               OpSource GLSL 430
-               OpMemberDecorate %S 0 Offset 0
-               OpMemberDecorate %Output 0 Offset 0
-               OpMemberDecorate %Output 1 Offset 16
-               OpMemberDecorate %Output 2 Offset 32
-               OpDecorate %Output BufferBlock
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v3float = OpTypeVector %float 3
-    %v4float = OpTypeVector %float 4
-     %matrix = OpTypeMatrix %v3float 4
-          %S = OpTypeStruct %matrix
-     %Output = OpTypeStruct %float %v4float %S
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState());
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("must be explicitly laid out with MatrixStride decorations"));
-}
-
-TEST_F(ValidateDecorations, BlockStandardUniformBufferLayout) {
-  std::string spirv = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint GLCompute %main "main"
-               OpExecutionMode %main LocalSize 1 1 1
-               OpSource GLSL 430
-               OpMemberDecorate %F 0 Offset 0
-               OpMemberDecorate %F 1 Offset 8
-               OpDecorate %_arr_float_uint_2 ArrayStride 16
-               OpDecorate %_arr_mat3v3float_uint_2 ArrayStride 48
-               OpMemberDecorate %O 0 Offset 0
-               OpMemberDecorate %O 1 Offset 16
-               OpMemberDecorate %O 2 Offset 32
-               OpMemberDecorate %O 3 Offset 64
-               OpMemberDecorate %O 4 ColMajor
-               OpMemberDecorate %O 4 Offset 80
-               OpMemberDecorate %O 4 MatrixStride 16
-               OpDecorate %_arr_O_uint_2 ArrayStride 176
-               OpMemberDecorate %Output 0 Offset 0
-               OpMemberDecorate %Output 1 Offset 8
-               OpMemberDecorate %Output 2 Offset 16
-               OpMemberDecorate %Output 3 Offset 32
-               OpMemberDecorate %Output 4 Offset 48
-               OpMemberDecorate %Output 5 Offset 64
-               OpMemberDecorate %Output 6 ColMajor
-               OpMemberDecorate %Output 6 Offset 96
-               OpMemberDecorate %Output 6 MatrixStride 16
-               OpMemberDecorate %Output 7 Offset 128
-               OpDecorate %Output Block
-               OpDecorate %dataOutput DescriptorSet 0
-               OpDecorate %dataOutput Binding 0
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v2float = OpTypeVector %float 2
-    %v3float = OpTypeVector %float 3
-        %int = OpTypeInt 32 1
-       %uint = OpTypeInt 32 0
-     %v2uint = OpTypeVector %uint 2
-          %F = OpTypeStruct %int %v2uint
-     %uint_2 = OpConstant %uint 2
-%_arr_float_uint_2 = OpTypeArray %float %uint_2
-%mat2v3float = OpTypeMatrix %v3float 2
-     %v3uint = OpTypeVector %uint 3
-%mat3v3float = OpTypeMatrix %v3float 3
-%_arr_mat3v3float_uint_2 = OpTypeArray %mat3v3float %uint_2
-          %O = OpTypeStruct %v3uint %v2float %_arr_float_uint_2 %v2float %_arr_mat3v3float_uint_2
-%_arr_O_uint_2 = OpTypeArray %O %uint_2
-     %Output = OpTypeStruct %float %v2float %v3float %F %float %_arr_float_uint_2 %mat2v3float %_arr_O_uint_2
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_SUCCESS,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
-}
-
-TEST_F(ValidateDecorations, BlockLayoutPermitsTightVec3ScalarPackingGood) {
-  // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1666
-  std::string spirv = R"(
-               OpCapability Shader
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %main "main"
-               OpSource GLSL 450
-               OpMemberDecorate %S 0 Offset 0
-               OpMemberDecorate %S 1 Offset 12
-               OpDecorate %S Block
-               OpDecorate %B DescriptorSet 0
-               OpDecorate %B Binding 0
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v3float = OpTypeVector %float 3
-          %S = OpTypeStruct %v3float %float
-%_ptr_Uniform_S = OpTypePointer Uniform %S
-          %B = OpVariable %_ptr_Uniform_S Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0))
-      << getDiagnosticString();
-}
-
 TEST_F(ValidateDecorations, BlockCantAppearWithinABlockBad) {
   // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1587
   std::string spirv = R"(
@@ -2141,642 +1582,6 @@
                         "another Block or BufferBlock."));
 }
 
-TEST_F(ValidateDecorations, BlockLayoutForbidsTightScalarVec3PackingBad) {
-  // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1666
-  std::string spirv = R"(
-               OpCapability Shader
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %main "main"
-               OpSource GLSL 450
-               OpMemberDecorate %S 0 Offset 0
-               OpMemberDecorate %S 1 Offset 4
-               OpDecorate %S Block
-               OpDecorate %B DescriptorSet 0
-               OpDecorate %B Binding 0
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v3float = OpTypeVector %float 3
-          %S = OpTypeStruct %float %v3float
-%_ptr_Uniform_S = OpTypePointer Uniform %S
-          %B = OpVariable %_ptr_Uniform_S Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("Structure id 2 decorated as Block for variable in Uniform "
-                "storage class must follow standard uniform buffer layout "
-                "rules: member 1 at offset 4 is not aligned to 16"));
-}
-
-TEST_F(ValidateDecorations,
-       BlockLayoutPermitsTightScalarVec3PackingWithRelaxedLayoutGood) {
-  // Same as previous test, but with explicit option to relax block layout.
-  std::string spirv = R"(
-               OpCapability Shader
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %main "main"
-               OpSource GLSL 450
-               OpMemberDecorate %S 0 Offset 0
-               OpMemberDecorate %S 1 Offset 4
-               OpDecorate %S Block
-               OpDecorate %B DescriptorSet 0
-               OpDecorate %B Binding 0
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v3float = OpTypeVector %float 3
-          %S = OpTypeStruct %float %v3float
-%_ptr_Uniform_S = OpTypePointer Uniform %S
-          %B = OpVariable %_ptr_Uniform_S Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  spvValidatorOptionsSetRelaxBlockLayout(getValidatorOptions(), true);
-  EXPECT_EQ(SPV_SUCCESS,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(getDiagnosticString(), Eq(""));
-}
-
-TEST_F(ValidateDecorations,
-       BlockLayoutPermitsTightScalarVec3PackingBadOffsetWithRelaxedLayoutBad) {
-  // Same as previous test, but with the vector not aligned to its scalar
-  // element. Use offset 5 instead of a multiple of 4.
-  std::string spirv = R"(
-               OpCapability Shader
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %main "main"
-               OpSource GLSL 450
-               OpMemberDecorate %S 0 Offset 0
-               OpMemberDecorate %S 1 Offset 5
-               OpDecorate %S Block
-               OpDecorate %B DescriptorSet 0
-               OpDecorate %B Binding 0
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v3float = OpTypeVector %float 3
-          %S = OpTypeStruct %float %v3float
-%_ptr_Uniform_S = OpTypePointer Uniform %S
-          %B = OpVariable %_ptr_Uniform_S Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  spvValidatorOptionsSetRelaxBlockLayout(getValidatorOptions(), true);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr(
-          "Structure id 2 decorated as Block for variable in Uniform storage "
-          "class must follow relaxed uniform buffer layout rules: member 1 at "
-          "offset 5 is not aligned to scalar element size 4"));
-}
-
-TEST_F(ValidateDecorations,
-       BlockLayoutPermitsTightScalarVec3PackingWithVulkan1_1Good) {
-  // Same as previous test, but with Vulkan 1.1.  Vulkan 1.1 included
-  // VK_KHR_relaxed_block_layout in core.
-  std::string spirv = R"(
-               OpCapability Shader
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %main "main"
-               OpSource GLSL 450
-               OpMemberDecorate %S 0 Offset 0
-               OpMemberDecorate %S 1 Offset 4
-               OpDecorate %S Block
-               OpDecorate %B DescriptorSet 0
-               OpDecorate %B Binding 0
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v3float = OpTypeVector %float 3
-          %S = OpTypeStruct %float %v3float
-%_ptr_Uniform_S = OpTypePointer Uniform %S
-          %B = OpVariable %_ptr_Uniform_S Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_SUCCESS,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
-  EXPECT_THAT(getDiagnosticString(), Eq(""));
-}
-
-TEST_F(ValidateDecorations,
-       BlockLayoutPermitsTightScalarVec3PackingWithScalarLayoutGood) {
-  // Same as previous test, but with scalar block layout.
-  std::string spirv = R"(
-               OpCapability Shader
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %main "main"
-               OpSource GLSL 450
-               OpMemberDecorate %S 0 Offset 0
-               OpMemberDecorate %S 1 Offset 4
-               OpDecorate %S Block
-               OpDecorate %B DescriptorSet 0
-               OpDecorate %B Binding 0
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v3float = OpTypeVector %float 3
-          %S = OpTypeStruct %float %v3float
-%_ptr_Uniform_S = OpTypePointer Uniform %S
-          %B = OpVariable %_ptr_Uniform_S Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true);
-  EXPECT_EQ(SPV_SUCCESS,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(getDiagnosticString(), Eq(""));
-}
-
-TEST_F(ValidateDecorations,
-       BlockLayoutPermitsScalarAlignedArrayWithScalarLayoutGood) {
-  // The array at offset 4 is ok with scalar block layout.
-  std::string spirv = R"(
-               OpCapability Shader
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %main "main"
-               OpSource GLSL 450
-               OpMemberDecorate %S 0 Offset 0
-               OpMemberDecorate %S 1 Offset 4
-               OpDecorate %S Block
-               OpDecorate %B DescriptorSet 0
-               OpDecorate %B Binding 0
-               OpDecorate %arr_float ArrayStride 4
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-       %uint = OpTypeInt 32 0
-     %uint_3 = OpConstant %uint 3
-      %float = OpTypeFloat 32
-  %arr_float = OpTypeArray %float %uint_3
-          %S = OpTypeStruct %float %arr_float
-%_ptr_Uniform_S = OpTypePointer Uniform %S
-          %B = OpVariable %_ptr_Uniform_S Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true);
-  EXPECT_EQ(SPV_SUCCESS,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(getDiagnosticString(), Eq(""));
-}
-
-TEST_F(ValidateDecorations,
-       BlockLayoutPermitsScalarAlignedArrayOfVec3WithScalarLayoutGood) {
-  // The array at offset 4 is ok with scalar block layout, even though
-  // its elements are vec3.
-  // This is the same as the previous case, but the array elements are vec3
-  // instead of float.
-  std::string spirv = R"(
-               OpCapability Shader
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %main "main"
-               OpSource GLSL 450
-               OpMemberDecorate %S 0 Offset 0
-               OpMemberDecorate %S 1 Offset 4
-               OpDecorate %S Block
-               OpDecorate %B DescriptorSet 0
-               OpDecorate %B Binding 0
-               OpDecorate %arr_vec3 ArrayStride 12
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-       %uint = OpTypeInt 32 0
-     %uint_3 = OpConstant %uint 3
-      %float = OpTypeFloat 32
-       %vec3 = OpTypeVector %float 3
-   %arr_vec3 = OpTypeArray %vec3 %uint_3
-          %S = OpTypeStruct %float %arr_vec3
-%_ptr_Uniform_S = OpTypePointer Uniform %S
-          %B = OpVariable %_ptr_Uniform_S Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true);
-  EXPECT_EQ(SPV_SUCCESS,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(getDiagnosticString(), Eq(""));
-}
-
-TEST_F(ValidateDecorations,
-       BlockLayoutPermitsScalarAlignedStructWithScalarLayoutGood) {
-  // Scalar block layout permits the struct at offset 4, even though
-  // it contains a vector with base alignment 8 and scalar alignment 4.
-  std::string spirv = R"(
-               OpCapability Shader
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %main "main"
-               OpSource GLSL 450
-               OpMemberDecorate %S 0 Offset 0
-               OpMemberDecorate %S 1 Offset 4
-               OpMemberDecorate %st 0 Offset 0
-               OpMemberDecorate %st 1 Offset 8
-               OpDecorate %S Block
-               OpDecorate %B DescriptorSet 0
-               OpDecorate %B Binding 0
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-       %vec2 = OpTypeVector %float 2
-        %st  = OpTypeStruct %vec2 %float
-          %S = OpTypeStruct %float %st
-%_ptr_Uniform_S = OpTypePointer Uniform %S
-          %B = OpVariable %_ptr_Uniform_S Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true);
-  EXPECT_EQ(SPV_SUCCESS,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(getDiagnosticString(), Eq(""));
-}
-
-TEST_F(
-    ValidateDecorations,
-    BlockLayoutPermitsFieldsInBaseAlignmentPaddingAtEndOfStructWithScalarLayoutGood) {
-  // Scalar block layout permits fields in what would normally be the padding at
-  // the end of a struct.
-  std::string spirv = R"(
-               OpCapability Shader
-               OpCapability Float64
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %main "main"
-               OpSource GLSL 450
-               OpMemberDecorate %st 0 Offset 0
-               OpMemberDecorate %st 1 Offset 8
-               OpMemberDecorate %S 0 Offset 0
-               OpMemberDecorate %S 1 Offset 12
-               OpDecorate %S Block
-               OpDecorate %B DescriptorSet 0
-               OpDecorate %B Binding 0
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-     %double = OpTypeFloat 64
-         %st = OpTypeStruct %double %float
-          %S = OpTypeStruct %st %float
-%_ptr_Uniform_S = OpTypePointer Uniform %S
-          %B = OpVariable %_ptr_Uniform_S Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true);
-  EXPECT_EQ(SPV_SUCCESS,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(getDiagnosticString(), Eq(""));
-}
-
-TEST_F(
-    ValidateDecorations,
-    BlockLayoutPermitsStraddlingVectorWithScalarLayoutOverrideRelaxBlockLayoutGood) {
-  // Same as previous, but set relaxed block layout first.  Scalar layout always
-  // wins.
-  std::string spirv = R"(
-               OpCapability Shader
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %main "main"
-               OpSource GLSL 450
-               OpMemberDecorate %S 0 Offset 0
-               OpMemberDecorate %S 1 Offset 4
-               OpDecorate %S Block
-               OpDecorate %B DescriptorSet 0
-               OpDecorate %B Binding 0
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-       %vec4 = OpTypeVector %float 4
-          %S = OpTypeStruct %float %vec4
-%_ptr_Uniform_S = OpTypePointer Uniform %S
-          %B = OpVariable %_ptr_Uniform_S Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  spvValidatorOptionsSetRelaxBlockLayout(getValidatorOptions(), true);
-  spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true);
-  EXPECT_EQ(SPV_SUCCESS,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(getDiagnosticString(), Eq(""));
-}
-
-TEST_F(
-    ValidateDecorations,
-    BlockLayoutPermitsStraddlingVectorWithRelaxedLayoutOverridenByScalarBlockLayoutGood) {
-  // Same as previous, but set scalar block layout first.  Scalar layout always
-  // wins.
-  std::string spirv = R"(
-               OpCapability Shader
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %main "main"
-               OpSource GLSL 450
-               OpMemberDecorate %S 0 Offset 0
-               OpMemberDecorate %S 1 Offset 4
-               OpDecorate %S Block
-               OpDecorate %B DescriptorSet 0
-               OpDecorate %B Binding 0
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-       %vec4 = OpTypeVector %float 4
-          %S = OpTypeStruct %float %vec4
-%_ptr_Uniform_S = OpTypePointer Uniform %S
-          %B = OpVariable %_ptr_Uniform_S Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true);
-  spvValidatorOptionsSetRelaxBlockLayout(getValidatorOptions(), true);
-  EXPECT_EQ(SPV_SUCCESS,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(getDiagnosticString(), Eq(""));
-}
-
-TEST_F(ValidateDecorations, BufferBlock16bitStandardStorageBufferLayout) {
-  std::string spirv = R"(
-             OpCapability Shader
-             OpCapability StorageUniform16
-             OpExtension "SPV_KHR_16bit_storage"
-             OpMemoryModel Logical GLSL450
-             OpEntryPoint GLCompute %main "main"
-             OpExecutionMode %main LocalSize 1 1 1
-             OpDecorate %f32arr ArrayStride 4
-             OpDecorate %f16arr ArrayStride 2
-             OpMemberDecorate %SSBO32 0 Offset 0
-             OpMemberDecorate %SSBO16 0 Offset 0
-             OpDecorate %SSBO32 BufferBlock
-             OpDecorate %SSBO16 BufferBlock
-             OpDecorate %varSSBO32 DescriptorSet 0
-             OpDecorate %varSSBO32 Binding 0
-             OpDecorate %varSSBO16 DescriptorSet 0
-             OpDecorate %varSSBO16 Binding 1
-     %void = OpTypeVoid
-    %voidf = OpTypeFunction %void
-      %u32 = OpTypeInt 32 0
-      %i32 = OpTypeInt 32 1
-      %f32 = OpTypeFloat 32
-    %uvec3 = OpTypeVector %u32 3
- %c_i32_32 = OpConstant %i32 32
-%c_i32_128 = OpConstant %i32 128
-   %f32arr = OpTypeArray %f32 %c_i32_128
-      %f16 = OpTypeFloat 16
-   %f16arr = OpTypeArray %f16 %c_i32_128
-   %SSBO32 = OpTypeStruct %f32arr
-   %SSBO16 = OpTypeStruct %f16arr
-%_ptr_Uniform_SSBO32 = OpTypePointer Uniform %SSBO32
- %varSSBO32 = OpVariable %_ptr_Uniform_SSBO32 Uniform
-%_ptr_Uniform_SSBO16 = OpTypePointer Uniform %SSBO16
- %varSSBO16 = OpVariable %_ptr_Uniform_SSBO16 Uniform
-     %main = OpFunction %void None %voidf
-    %label = OpLabel
-             OpReturn
-             OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_SUCCESS,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
-}
-
-TEST_F(ValidateDecorations, BlockArrayExtendedAlignmentGood) {
-  // For uniform buffer, Array base alignment is 16, and ArrayStride
-  // must be a multiple of 16.
-  std::string spirv = R"(
-               OpCapability Shader
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %main "main"
-               OpSource GLSL 450
-               OpDecorate %_arr_float_uint_2 ArrayStride 16
-               OpMemberDecorate %S 0 Offset 0
-               OpMemberDecorate %S 1 Offset 16
-               OpDecorate %S Block
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v2float = OpTypeVector %float 2
-       %uint = OpTypeInt 32 0
-     %uint_2 = OpConstant %uint 2
-%_arr_float_uint_2 = OpTypeArray %float %uint_2
-          %S = OpTypeStruct %v2float %_arr_float_uint_2
-%_ptr_PushConstant_S = OpTypePointer PushConstant %S
-          %u = OpVariable %_ptr_PushConstant_S PushConstant
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState())
-      << getDiagnosticString();
-}
-
-TEST_F(ValidateDecorations, BlockArrayBaseAlignmentBad) {
-  // For uniform buffer, Array base alignment is 16.
-  std::string spirv = R"(
-               OpCapability Shader
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %main "main"
-               OpSource GLSL 450
-               OpDecorate %_arr_float_uint_2 ArrayStride 16
-               OpMemberDecorate %S 0 Offset 0
-               OpMemberDecorate %S 1 Offset 8
-               OpDecorate %S Block
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v2float = OpTypeVector %float 2
-       %uint = OpTypeInt 32 0
-     %uint_2 = OpConstant %uint 2
-%_arr_float_uint_2 = OpTypeArray %float %uint_2
-          %S = OpTypeStruct %v2float %_arr_float_uint_2
-%_ptr_Uniform_S = OpTypePointer Uniform %S
-          %u = OpVariable %_ptr_Uniform_S Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr(
-          "Structure id 3 decorated as Block for variable in Uniform "
-          "storage class must follow standard uniform buffer layout rules: "
-          "member 1 at offset 8 is not aligned to 16"));
-}
-
-TEST_F(ValidateDecorations, BlockArrayBaseAlignmentWithRelaxedLayoutStillBad) {
-  // For uniform buffer, Array base alignment is 16, and ArrayStride
-  // must be a multiple of 16.  This case uses relaxed block layout.  Relaxed
-  // layout only relaxes rules for vector alignment, not array alignment.
-  std::string spirv = R"(
-               OpCapability Shader
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %main "main"
-               OpSource GLSL 450
-               OpDecorate %_arr_float_uint_2 ArrayStride 16
-               OpDecorate %u DescriptorSet 0
-               OpDecorate %u Binding 0
-               OpMemberDecorate %S 0 Offset 0
-               OpMemberDecorate %S 1 Offset 8
-               OpDecorate %S Block
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v2float = OpTypeVector %float 2
-       %uint = OpTypeInt 32 0
-     %uint_2 = OpConstant %uint 2
-%_arr_float_uint_2 = OpTypeArray %float %uint_2
-          %S = OpTypeStruct %v2float %_arr_float_uint_2
-%_ptr_Uniform_S = OpTypePointer Uniform %S
-          %u = OpVariable %_ptr_Uniform_S Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
-  spvValidatorOptionsSetRelaxBlockLayout(getValidatorOptions(), true);
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr(
-          "Structure id 4 decorated as Block for variable in Uniform "
-          "storage class must follow standard uniform buffer layout rules: "
-          "member 1 at offset 8 is not aligned to 16"));
-}
-
-TEST_F(ValidateDecorations, BlockArrayBaseAlignmentWithVulkan1_1StillBad) {
-  // Same as previous test, but with Vulkan 1.1, which includes
-  // VK_KHR_relaxed_block_layout in core.
-  std::string spirv = R"(
-               OpCapability Shader
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %main "main"
-               OpSource GLSL 450
-               OpDecorate %_arr_float_uint_2 ArrayStride 16
-               OpDecorate %u DescriptorSet 0
-               OpDecorate %u Binding 0
-               OpMemberDecorate %S 0 Offset 0
-               OpMemberDecorate %S 1 Offset 8
-               OpDecorate %S Block
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v2float = OpTypeVector %float 2
-       %uint = OpTypeInt 32 0
-     %uint_2 = OpConstant %uint 2
-%_arr_float_uint_2 = OpTypeArray %float %uint_2
-          %S = OpTypeStruct %v2float %_arr_float_uint_2
-%_ptr_Uniform_S = OpTypePointer Uniform %S
-          %u = OpVariable %_ptr_Uniform_S Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr(
-          "Structure id 4 decorated as Block for variable in Uniform "
-          "storage class must follow relaxed uniform buffer layout rules: "
-          "member 1 at offset 8 is not aligned to 16"));
-}
-
-TEST_F(ValidateDecorations,
-       BlockArrayBaseAlignmentWithBlockStandardLayoutGood) {
-  // Same as previous test, but with VK_KHR_uniform_buffer_standard_layout
-  std::string spirv = R"(
-               OpCapability Shader
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %main "main"
-               OpSource GLSL 450
-               OpDecorate %_arr_float_uint_2 ArrayStride 16
-               OpDecorate %u DescriptorSet 0
-               OpDecorate %u Binding 0
-               OpMemberDecorate %S 0 Offset 0
-               OpMemberDecorate %S 1 Offset 8
-               OpDecorate %S Block
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v2float = OpTypeVector %float 2
-       %uint = OpTypeInt 32 0
-     %uint_2 = OpConstant %uint 2
-%_arr_float_uint_2 = OpTypeArray %float %uint_2
-          %S = OpTypeStruct %v2float %_arr_float_uint_2
-%_ptr_Uniform_S = OpTypePointer Uniform %S
-          %u = OpVariable %_ptr_Uniform_S Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  spvValidatorOptionsSetUniformBufferStandardLayout(getValidatorOptions(),
-                                                    true);
-  EXPECT_EQ(SPV_SUCCESS,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(getDiagnosticString(), Eq(""));
-}
-
 TEST_F(ValidateDecorations, VulkanBufferBlockOnStorageBufferBad) {
   std::string spirv = R"(
             OpCapability Shader
@@ -2810,145 +1615,6 @@
                         "the StorageBuffer storage class"));
 }
 
-TEST_F(ValidateDecorations, PushConstantArrayBaseAlignmentGood) {
-  // Tests https://github.com/KhronosGroup/SPIRV-Tools/issues/1664
-  // From GLSL vertex shader:
-  // #version 450
-  // layout(push_constant) uniform S { vec2 v; float arr[2]; } u;
-  // void main() { }
-
-  std::string spirv = R"(
-               OpCapability Shader
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %main "main"
-               OpSource GLSL 450
-               OpDecorate %_arr_float_uint_2 ArrayStride 4
-               OpMemberDecorate %S 0 Offset 0
-               OpMemberDecorate %S 1 Offset 8
-               OpDecorate %S Block
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v2float = OpTypeVector %float 2
-       %uint = OpTypeInt 32 0
-     %uint_2 = OpConstant %uint 2
-%_arr_float_uint_2 = OpTypeArray %float %uint_2
-          %S = OpTypeStruct %v2float %_arr_float_uint_2
-%_ptr_PushConstant_S = OpTypePointer PushConstant %S
-          %u = OpVariable %_ptr_PushConstant_S PushConstant
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0))
-      << getDiagnosticString();
-}
-
-TEST_F(ValidateDecorations, PushConstantArrayBadAlignmentBad) {
-  // Like the previous test, but with offset 7 instead of 8.
-  std::string spirv = R"(
-               OpCapability Shader
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %main "main"
-               OpSource GLSL 450
-               OpDecorate %_arr_float_uint_2 ArrayStride 4
-               OpMemberDecorate %S 0 Offset 0
-               OpMemberDecorate %S 1 Offset 7
-               OpDecorate %S Block
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v2float = OpTypeVector %float 2
-       %uint = OpTypeInt 32 0
-     %uint_2 = OpConstant %uint 2
-%_arr_float_uint_2 = OpTypeArray %float %uint_2
-          %S = OpTypeStruct %v2float %_arr_float_uint_2
-%_ptr_PushConstant_S = OpTypePointer PushConstant %S
-          %u = OpVariable %_ptr_PushConstant_S PushConstant
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr(
-          "Structure id 3 decorated as Block for variable in PushConstant "
-          "storage class must follow standard storage buffer layout rules: "
-          "member 1 at offset 7 is not aligned to 4"));
-}
-
-TEST_F(ValidateDecorations,
-       PushConstantLayoutPermitsTightVec3ScalarPackingGood) {
-  // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1666
-  std::string spirv = R"(
-               OpCapability Shader
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %main "main"
-               OpSource GLSL 450
-               OpMemberDecorate %S 0 Offset 0
-               OpMemberDecorate %S 1 Offset 12
-               OpDecorate %S Block
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v3float = OpTypeVector %float 3
-          %S = OpTypeStruct %v3float %float
-%_ptr_PushConstant_S = OpTypePointer PushConstant %S
-          %B = OpVariable %_ptr_PushConstant_S PushConstant
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0))
-      << getDiagnosticString();
-}
-
-TEST_F(ValidateDecorations,
-       PushConstantLayoutForbidsTightScalarVec3PackingBad) {
-  // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1666
-  std::string spirv = R"(
-               OpCapability Shader
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %main "main"
-               OpSource GLSL 450
-               OpMemberDecorate %S 0 Offset 0
-               OpMemberDecorate %S 1 Offset 4
-               OpDecorate %S Block
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v3float = OpTypeVector %float 3
-          %S = OpTypeStruct %float %v3float
-%_ptr_Uniform_S = OpTypePointer PushConstant %S
-          %B = OpVariable %_ptr_Uniform_S PushConstant
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr(
-          "Structure id 2 decorated as Block for variable in PushConstant "
-          "storage class must follow standard storage buffer layout "
-          "rules: member 1 at offset 4 is not aligned to 16"));
-}
-
 TEST_F(ValidateDecorations, PushConstantMissingBlockGood) {
   std::string spirv = R"(
             OpCapability Shader
@@ -3776,1074 +2442,6 @@
       << getDiagnosticString();
 }
 
-TEST_F(ValidateDecorations, StorageBufferStorageClassArrayBaseAlignmentGood) {
-  // Spot check buffer rules when using StorageBuffer storage class with Block
-  // decoration.
-  std::string spirv = R"(
-               OpCapability Shader
-               OpExtension "SPV_KHR_storage_buffer_storage_class"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %main "main"
-               OpSource GLSL 450
-               OpDecorate %_arr_float_uint_2 ArrayStride 4
-               OpMemberDecorate %S 0 Offset 0
-               OpMemberDecorate %S 1 Offset 8
-               OpDecorate %S Block
-               OpDecorate %u DescriptorSet 0
-               OpDecorate %u Binding 0
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v2float = OpTypeVector %float 2
-       %uint = OpTypeInt 32 0
-     %uint_2 = OpConstant %uint 2
-%_arr_float_uint_2 = OpTypeArray %float %uint_2
-          %S = OpTypeStruct %v2float %_arr_float_uint_2
-%_ptr_Uniform_S = OpTypePointer StorageBuffer %S
-          %u = OpVariable %_ptr_Uniform_S StorageBuffer
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0))
-      << getDiagnosticString();
-}
-
-TEST_F(ValidateDecorations, StorageBufferStorageClassArrayBadAlignmentBad) {
-  // Like the previous test, but with offset 7.
-  std::string spirv = R"(
-               OpCapability Shader
-               OpExtension "SPV_KHR_storage_buffer_storage_class"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %main "main"
-               OpSource GLSL 450
-               OpDecorate %_arr_float_uint_2 ArrayStride 4
-               OpMemberDecorate %S 0 Offset 0
-               OpMemberDecorate %S 1 Offset 7
-               OpDecorate %S Block
-               OpDecorate %u DescriptorSet 0
-               OpDecorate %u Binding 0
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v2float = OpTypeVector %float 2
-       %uint = OpTypeInt 32 0
-     %uint_2 = OpConstant %uint 2
-%_arr_float_uint_2 = OpTypeArray %float %uint_2
-          %S = OpTypeStruct %v2float %_arr_float_uint_2
-%_ptr_Uniform_S = OpTypePointer StorageBuffer %S
-          %u = OpVariable %_ptr_Uniform_S StorageBuffer
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr(
-          "Structure id 3 decorated as Block for variable in StorageBuffer "
-          "storage class must follow standard storage buffer layout rules: "
-          "member 1 at offset 7 is not aligned to 4"));
-}
-
-TEST_F(ValidateDecorations, BufferBlockStandardStorageBufferLayout) {
-  std::string spirv = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint GLCompute %main "main"
-               OpExecutionMode %main LocalSize 1 1 1
-               OpSource GLSL 430
-               OpMemberDecorate %F 0 Offset 0
-               OpMemberDecorate %F 1 Offset 8
-               OpDecorate %_arr_float_uint_2 ArrayStride 4
-               OpDecorate %_arr_mat3v3float_uint_2 ArrayStride 48
-               OpMemberDecorate %O 0 Offset 0
-               OpMemberDecorate %O 1 Offset 16
-               OpMemberDecorate %O 2 Offset 24
-               OpMemberDecorate %O 3 Offset 32
-               OpMemberDecorate %O 4 ColMajor
-               OpMemberDecorate %O 4 Offset 48
-               OpMemberDecorate %O 4 MatrixStride 16
-               OpDecorate %_arr_O_uint_2 ArrayStride 144
-               OpMemberDecorate %Output 0 Offset 0
-               OpMemberDecorate %Output 1 Offset 8
-               OpMemberDecorate %Output 2 Offset 16
-               OpMemberDecorate %Output 3 Offset 32
-               OpMemberDecorate %Output 4 Offset 48
-               OpMemberDecorate %Output 5 Offset 52
-               OpMemberDecorate %Output 6 ColMajor
-               OpMemberDecorate %Output 6 Offset 64
-               OpMemberDecorate %Output 6 MatrixStride 16
-               OpMemberDecorate %Output 7 Offset 96
-               OpDecorate %Output BufferBlock
-               OpDecorate %dataOutput DescriptorSet 0
-               OpDecorate %dataOutput Binding 0
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v2float = OpTypeVector %float 2
-    %v3float = OpTypeVector %float 3
-        %int = OpTypeInt 32 1
-       %uint = OpTypeInt 32 0
-     %v2uint = OpTypeVector %uint 2
-          %F = OpTypeStruct %int %v2uint
-     %uint_2 = OpConstant %uint 2
-%_arr_float_uint_2 = OpTypeArray %float %uint_2
-%mat2v3float = OpTypeMatrix %v3float 2
-     %v3uint = OpTypeVector %uint 3
-%mat3v3float = OpTypeMatrix %v3float 3
-%_arr_mat3v3float_uint_2 = OpTypeArray %mat3v3float %uint_2
-          %O = OpTypeStruct %v3uint %v2float %_arr_float_uint_2 %v2float %_arr_mat3v3float_uint_2
-%_arr_O_uint_2 = OpTypeArray %O %uint_2
-     %Output = OpTypeStruct %float %v2float %v3float %F %float %_arr_float_uint_2 %mat2v3float %_arr_O_uint_2
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_SUCCESS,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
-}
-
-TEST_F(ValidateDecorations,
-       StorageBufferLayoutPermitsTightVec3ScalarPackingGood) {
-  // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1666
-  std::string spirv = R"(
-               OpCapability Shader
-               OpExtension "SPV_KHR_storage_buffer_storage_class"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %main "main"
-               OpSource GLSL 450
-               OpMemberDecorate %S 0 Offset 0
-               OpMemberDecorate %S 1 Offset 12
-               OpDecorate %S Block
-               OpDecorate %B DescriptorSet 0
-               OpDecorate %B Binding 0
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v3float = OpTypeVector %float 3
-          %S = OpTypeStruct %v3float %float
-%_ptr_StorageBuffer_S = OpTypePointer StorageBuffer %S
-          %B = OpVariable %_ptr_StorageBuffer_S StorageBuffer
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0))
-      << getDiagnosticString();
-}
-
-TEST_F(ValidateDecorations,
-       StorageBufferLayoutForbidsTightScalarVec3PackingBad) {
-  // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1666
-  std::string spirv = R"(
-               OpCapability Shader
-               OpExtension "SPV_KHR_storage_buffer_storage_class"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %main "main"
-               OpSource GLSL 450
-               OpMemberDecorate %S 0 Offset 0
-               OpMemberDecorate %S 1 Offset 4
-               OpDecorate %S Block
-               OpDecorate %B DescriptorSet 0
-               OpDecorate %B Binding 0
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v3float = OpTypeVector %float 3
-          %S = OpTypeStruct %float %v3float
-%_ptr_StorageBuffer_S = OpTypePointer StorageBuffer %S
-          %B = OpVariable %_ptr_StorageBuffer_S StorageBuffer
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr(
-          "Structure id 2 decorated as Block for variable in StorageBuffer "
-          "storage class must follow standard storage buffer layout "
-          "rules: member 1 at offset 4 is not aligned to 16"));
-}
-
-TEST_F(ValidateDecorations,
-       BlockStandardUniformBufferLayoutIncorrectOffset0Bad) {
-  std::string spirv = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint GLCompute %main "main"
-               OpExecutionMode %main LocalSize 1 1 1
-               OpSource GLSL 430
-               OpMemberDecorate %F 0 Offset 0
-               OpMemberDecorate %F 1 Offset 8
-               OpDecorate %_arr_float_uint_2 ArrayStride 16
-               OpDecorate %_arr_mat3v3float_uint_2 ArrayStride 48
-               OpMemberDecorate %O 0 Offset 0
-               OpMemberDecorate %O 1 Offset 16
-               OpMemberDecorate %O 2 Offset 24
-               OpMemberDecorate %O 3 Offset 33
-               OpMemberDecorate %O 4 ColMajor
-               OpMemberDecorate %O 4 Offset 80
-               OpMemberDecorate %O 4 MatrixStride 16
-               OpDecorate %_arr_O_uint_2 ArrayStride 176
-               OpMemberDecorate %Output 0 Offset 0
-               OpMemberDecorate %Output 1 Offset 8
-               OpMemberDecorate %Output 2 Offset 16
-               OpMemberDecorate %Output 3 Offset 32
-               OpMemberDecorate %Output 4 Offset 48
-               OpMemberDecorate %Output 5 Offset 64
-               OpMemberDecorate %Output 6 ColMajor
-               OpMemberDecorate %Output 6 Offset 96
-               OpMemberDecorate %Output 6 MatrixStride 16
-               OpMemberDecorate %Output 7 Offset 128
-               OpDecorate %Output Block
-               OpDecorate %dataOutput DescriptorSet 0
-               OpDecorate %dataOutput Binding 0
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v2float = OpTypeVector %float 2
-    %v3float = OpTypeVector %float 3
-        %int = OpTypeInt 32 1
-       %uint = OpTypeInt 32 0
-     %v2uint = OpTypeVector %uint 2
-          %F = OpTypeStruct %int %v2uint
-     %uint_2 = OpConstant %uint 2
-%_arr_float_uint_2 = OpTypeArray %float %uint_2
-%mat2v3float = OpTypeMatrix %v3float 2
-     %v3uint = OpTypeVector %uint 3
-%mat3v3float = OpTypeMatrix %v3float 3
-%_arr_mat3v3float_uint_2 = OpTypeArray %mat3v3float %uint_2
-          %O = OpTypeStruct %v3uint %v2float %_arr_float_uint_2 %v2float %_arr_mat3v3float_uint_2
-%_arr_O_uint_2 = OpTypeArray %O %uint_2
-     %Output = OpTypeStruct %float %v2float %v3float %F %float %_arr_float_uint_2 %mat2v3float %_arr_O_uint_2
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("Structure id 6 decorated as Block for variable in Uniform "
-                "storage class must follow standard uniform buffer layout "
-                "rules: member 2 at offset 152 is not aligned to 16"));
-}
-
-TEST_F(ValidateDecorations,
-       BlockStandardUniformBufferLayoutIncorrectOffset1Bad) {
-  std::string spirv = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint GLCompute %main "main"
-               OpExecutionMode %main LocalSize 1 1 1
-               OpSource GLSL 430
-               OpMemberDecorate %F 0 Offset 0
-               OpMemberDecorate %F 1 Offset 8
-               OpDecorate %_arr_float_uint_2 ArrayStride 16
-               OpDecorate %_arr_mat3v3float_uint_2 ArrayStride 48
-               OpMemberDecorate %O 0 Offset 0
-               OpMemberDecorate %O 1 Offset 16
-               OpMemberDecorate %O 2 Offset 32
-               OpMemberDecorate %O 3 Offset 64
-               OpMemberDecorate %O 4 ColMajor
-               OpMemberDecorate %O 4 Offset 80
-               OpMemberDecorate %O 4 MatrixStride 16
-               OpDecorate %_arr_O_uint_2 ArrayStride 176
-               OpMemberDecorate %Output 0 Offset 0
-               OpMemberDecorate %Output 1 Offset 8
-               OpMemberDecorate %Output 2 Offset 16
-               OpMemberDecorate %Output 3 Offset 32
-               OpMemberDecorate %Output 4 Offset 48
-               OpMemberDecorate %Output 5 Offset 71
-               OpMemberDecorate %Output 6 ColMajor
-               OpMemberDecorate %Output 6 Offset 96
-               OpMemberDecorate %Output 6 MatrixStride 16
-               OpMemberDecorate %Output 7 Offset 128
-               OpDecorate %Output Block
-               OpDecorate %dataOutput DescriptorSet 0
-               OpDecorate %dataOutput Binding 0
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v2float = OpTypeVector %float 2
-    %v3float = OpTypeVector %float 3
-        %int = OpTypeInt 32 1
-       %uint = OpTypeInt 32 0
-     %v2uint = OpTypeVector %uint 2
-          %F = OpTypeStruct %int %v2uint
-     %uint_2 = OpConstant %uint 2
-%_arr_float_uint_2 = OpTypeArray %float %uint_2
-%mat2v3float = OpTypeMatrix %v3float 2
-     %v3uint = OpTypeVector %uint 3
-%mat3v3float = OpTypeMatrix %v3float 3
-%_arr_mat3v3float_uint_2 = OpTypeArray %mat3v3float %uint_2
-          %O = OpTypeStruct %v3uint %v2float %_arr_float_uint_2 %v2float %_arr_mat3v3float_uint_2
-%_arr_O_uint_2 = OpTypeArray %O %uint_2
-     %Output = OpTypeStruct %float %v2float %v3float %F %float %_arr_float_uint_2 %mat2v3float %_arr_O_uint_2
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("Structure id 8 decorated as Block for variable in Uniform "
-                "storage class must follow standard uniform buffer layout "
-                "rules: member 5 at offset 71 is not aligned to 16"));
-}
-
-TEST_F(ValidateDecorations, BlockUniformBufferLayoutIncorrectArrayStrideBad) {
-  std::string spirv = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint GLCompute %main "main"
-               OpExecutionMode %main LocalSize 1 1 1
-               OpSource GLSL 430
-               OpMemberDecorate %F 0 Offset 0
-               OpMemberDecorate %F 1 Offset 8
-               OpDecorate %_arr_float_uint_2 ArrayStride 16
-               OpDecorate %_arr_mat3v3float_uint_2 ArrayStride 49
-               OpMemberDecorate %O 0 Offset 0
-               OpMemberDecorate %O 1 Offset 16
-               OpMemberDecorate %O 2 Offset 32
-               OpMemberDecorate %O 3 Offset 64
-               OpMemberDecorate %O 4 ColMajor
-               OpMemberDecorate %O 4 Offset 80
-               OpMemberDecorate %O 4 MatrixStride 16
-               OpDecorate %_arr_O_uint_2 ArrayStride 176
-               OpMemberDecorate %Output 0 Offset 0
-               OpMemberDecorate %Output 1 Offset 8
-               OpMemberDecorate %Output 2 Offset 16
-               OpMemberDecorate %Output 3 Offset 32
-               OpMemberDecorate %Output 4 Offset 48
-               OpMemberDecorate %Output 5 Offset 64
-               OpMemberDecorate %Output 6 ColMajor
-               OpMemberDecorate %Output 6 Offset 96
-               OpMemberDecorate %Output 6 MatrixStride 16
-               OpMemberDecorate %Output 7 Offset 128
-               OpDecorate %Output Block
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v2float = OpTypeVector %float 2
-    %v3float = OpTypeVector %float 3
-        %int = OpTypeInt 32 1
-       %uint = OpTypeInt 32 0
-     %v2uint = OpTypeVector %uint 2
-          %F = OpTypeStruct %int %v2uint
-     %uint_2 = OpConstant %uint 2
-%_arr_float_uint_2 = OpTypeArray %float %uint_2
-%mat2v3float = OpTypeMatrix %v3float 2
-     %v3uint = OpTypeVector %uint 3
-%mat3v3float = OpTypeMatrix %v3float 3
-%_arr_mat3v3float_uint_2 = OpTypeArray %mat3v3float %uint_2
-          %O = OpTypeStruct %v3uint %v2float %_arr_float_uint_2 %v2float %_arr_mat3v3float_uint_2
-%_arr_O_uint_2 = OpTypeArray %O %uint_2
-     %Output = OpTypeStruct %float %v2float %v3float %F %float %_arr_float_uint_2 %mat2v3float %_arr_O_uint_2
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr(
-          "Structure id 6 decorated as Block for variable in Uniform storage "
-          "class must follow standard uniform buffer layout rules: member 4 "
-          "contains "
-          "an array with stride 49 not satisfying alignment to 16"));
-}
-
-TEST_F(ValidateDecorations,
-       BufferBlockStandardStorageBufferLayoutImproperStraddleBad) {
-  std::string spirv = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint GLCompute %main "main"
-               OpExecutionMode %main LocalSize 1 1 1
-               OpSource GLSL 430
-               OpMemberDecorate %Output 0 Offset 0
-               OpMemberDecorate %Output 1 Offset 8
-               OpDecorate %Output BufferBlock
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v3float = OpTypeVector %float 3
-     %Output = OpTypeStruct %float %v3float
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("Structure id 3 decorated as BufferBlock for variable in "
-                "Uniform storage class must follow standard storage buffer "
-                "layout rules: member 1 at offset 8 is not aligned to 16"));
-}
-
-TEST_F(ValidateDecorations,
-       BlockUniformBufferLayoutOffsetInsideArrayPaddingBad) {
-  // In this case the 2nd member fits entirely within the padding.
-  std::string spirv = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint GLCompute %main "main"
-               OpExecutionMode %main LocalSize 1 1 1
-               OpSource GLSL 430
-               OpDecorate %_arr_float_uint_2 ArrayStride 16
-               OpMemberDecorate %Output 0 Offset 0
-               OpMemberDecorate %Output 1 Offset 20
-               OpDecorate %Output Block
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-       %uint = OpTypeInt 32 0
-     %v2uint = OpTypeVector %uint 2
-     %uint_2 = OpConstant %uint 2
-%_arr_float_uint_2 = OpTypeArray %float %uint_2
-     %Output = OpTypeStruct %_arr_float_uint_2 %float
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr(
-          "Structure id 4 decorated as Block for variable in Uniform storage "
-          "class must follow standard uniform buffer layout rules: member 1 at "
-          "offset 20 overlaps previous member ending at offset 31"));
-}
-
-TEST_F(ValidateDecorations,
-       BlockUniformBufferLayoutOffsetInsideStructPaddingBad) {
-  // In this case the 2nd member fits entirely within the padding.
-  std::string spirv = R"(
-               OpCapability Shader
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint GLCompute %1 "main"
-               OpExecutionMode %1 LocalSize 1 1 1
-               OpMemberDecorate %_struct_6 0 Offset 0
-               OpMemberDecorate %_struct_2 0 Offset 0
-               OpMemberDecorate %_struct_2 1 Offset 4
-               OpDecorate %_struct_2 Block
-       %void = OpTypeVoid
-          %4 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-  %_struct_6 = OpTypeStruct %float
-  %_struct_2 = OpTypeStruct %_struct_6 %float
-%_ptr_Uniform__struct_2 = OpTypePointer Uniform %_struct_2
-          %8 = OpVariable %_ptr_Uniform__struct_2 Uniform
-          %1 = OpFunction %void None %4
-          %9 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr(
-          "Structure id 3 decorated as Block for variable in Uniform storage "
-          "class must follow standard uniform buffer layout rules: member 1 at "
-          "offset 4 overlaps previous member ending at offset 15"));
-}
-
-TEST_F(ValidateDecorations, BlockLayoutOffsetOutOfOrderGoodUniversal1_0) {
-  std::string spirv = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint GLCompute %main "main"
-               OpExecutionMode %main LocalSize 1 1 1
-               OpMemberDecorate %Outer 0 Offset 4
-               OpMemberDecorate %Outer 1 Offset 0
-               OpDecorate %Outer Block
-               OpDecorate %O DescriptorSet 0
-               OpDecorate %O Binding 0
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-       %uint = OpTypeInt 32 0
-      %Outer = OpTypeStruct %uint %uint
-%_ptr_Uniform_Outer = OpTypePointer Uniform %Outer
-          %O = OpVariable %_ptr_Uniform_Outer Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_SUCCESS,
-            ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_0));
-}
-
-TEST_F(ValidateDecorations, BlockLayoutOffsetOutOfOrderGoodOpenGL4_5) {
-  std::string spirv = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint GLCompute %main "main"
-               OpExecutionMode %main LocalSize 1 1 1
-               OpMemberDecorate %Outer 0 Offset 4
-               OpMemberDecorate %Outer 1 Offset 0
-               OpDecorate %Outer Block
-               OpDecorate %O DescriptorSet 0
-               OpDecorate %O Binding 0
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-       %uint = OpTypeInt 32 0
-      %Outer = OpTypeStruct %uint %uint
-%_ptr_Uniform_Outer = OpTypePointer Uniform %Outer
-          %O = OpVariable %_ptr_Uniform_Outer Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_SUCCESS,
-            ValidateAndRetrieveValidationState(SPV_ENV_OPENGL_4_5));
-}
-
-TEST_F(ValidateDecorations, BlockLayoutOffsetOutOfOrderGoodVulkan1_1) {
-  std::string spirv = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint GLCompute %main "main"
-               OpExecutionMode %main LocalSize 1 1 1
-               OpMemberDecorate %Outer 0 Offset 4
-               OpMemberDecorate %Outer 1 Offset 0
-               OpDecorate %Outer Block
-               OpDecorate %O DescriptorSet 0
-               OpDecorate %O Binding 0
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-       %uint = OpTypeInt 32 0
-      %Outer = OpTypeStruct %uint %uint
-%_ptr_Uniform_Outer = OpTypePointer Uniform %Outer
-          %O = OpVariable %_ptr_Uniform_Outer Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1))
-      << getDiagnosticString();
-  EXPECT_THAT(getDiagnosticString(), Eq(""));
-}
-
-TEST_F(ValidateDecorations, BlockLayoutOffsetOverlapBad) {
-  std::string spirv = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint GLCompute %main "main"
-               OpExecutionMode %main LocalSize 1 1 1
-               OpMemberDecorate %Outer 0 Offset 0
-               OpMemberDecorate %Outer 1 Offset 16
-               OpMemberDecorate %Inner 0 Offset 0
-               OpMemberDecorate %Inner 1 Offset 16
-               OpDecorate %Outer Block
-               OpDecorate %O DescriptorSet 0
-               OpDecorate %O Binding 0
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-       %uint = OpTypeInt 32 0
-      %Inner = OpTypeStruct %uint %uint
-      %Outer = OpTypeStruct %Inner %uint
-%_ptr_Uniform_Outer = OpTypePointer Uniform %Outer
-          %O = OpVariable %_ptr_Uniform_Outer Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr(
-          "Structure id 3 decorated as Block for variable in Uniform storage "
-          "class must follow standard uniform buffer layout rules: member 1 at "
-          "offset 16 overlaps previous member ending at offset 31"));
-}
-
-TEST_F(ValidateDecorations, BufferBlockEmptyStruct) {
-  std::string spirv = R"(
-               OpCapability Shader
-          %1 = OpExtInstImport "GLSL.std.450"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint GLCompute %main "main"
-               OpExecutionMode %main LocalSize 1 1 1
-               OpSource GLSL 430
-               OpMemberDecorate %Output 0 Offset 0
-               OpDecorate %Output BufferBlock
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-          %S = OpTypeStruct
-     %Output = OpTypeStruct %S
-%_ptr_Uniform_Output = OpTypePointer Uniform %Output
- %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
-}
-
-TEST_F(ValidateDecorations, RowMajorMatrixTightPackingGood) {
-  // Row major matrix rule:
-  //     A row-major matrix of C columns has a base alignment equal to
-  //     the base alignment of a vector of C matrix components.
-  // Note: The "matrix component" is the scalar element type.
-
-  // The matrix has 3 columns and 2 rows (C=3, R=2).
-  // So the base alignment of b is the same as a vector of 3 floats, which is 16
-  // bytes. The matrix consists of two of these, and therefore occupies 2 x 16
-  // bytes, or 32 bytes.
-  //
-  // So the offsets can be:
-  // a -> 0
-  // b -> 16
-  // c -> 48
-  // d -> 60 ; d fits at bytes 12-15 after offset of c. Tight (vec3;float)
-  // packing
-
-  std::string spirv = R"(
-               OpCapability Shader
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %1 "main"
-               OpSource GLSL 450
-               OpMemberDecorate %_struct_2 0 Offset 0
-               OpMemberDecorate %_struct_2 1 RowMajor
-               OpMemberDecorate %_struct_2 1 Offset 16
-               OpMemberDecorate %_struct_2 1 MatrixStride 16
-               OpMemberDecorate %_struct_2 2 Offset 48
-               OpMemberDecorate %_struct_2 3 Offset 60
-               OpDecorate %_struct_2 Block
-               OpDecorate %3 DescriptorSet 0
-               OpDecorate %3 Binding 0
-       %void = OpTypeVoid
-          %5 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v4float = OpTypeVector %float 4
-    %v2float = OpTypeVector %float 2
-%mat3v2float = OpTypeMatrix %v2float 3
-    %v3float = OpTypeVector %float 3
-  %_struct_2 = OpTypeStruct %v4float %mat3v2float %v3float %float
-%_ptr_Uniform__struct_2 = OpTypePointer Uniform %_struct_2
-          %3 = OpVariable %_ptr_Uniform__struct_2 Uniform
-          %1 = OpFunction %void None %5
-         %12 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState())
-      << getDiagnosticString();
-}
-
-TEST_F(ValidateDecorations, ArrayArrayRowMajorMatrixTightPackingGood) {
-  // Like the previous case, but we have an array of arrays of matrices.
-  // The RowMajor decoration goes on the struct member (surprisingly).
-
-  std::string spirv = R"(
-               OpCapability Shader
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %1 "main"
-               OpSource GLSL 450
-               OpMemberDecorate %_struct_2 0 Offset 0
-               OpMemberDecorate %_struct_2 1 RowMajor
-               OpMemberDecorate %_struct_2 1 Offset 16
-               OpMemberDecorate %_struct_2 1 MatrixStride 16
-               OpMemberDecorate %_struct_2 2 Offset 80
-               OpMemberDecorate %_struct_2 3 Offset 92
-               OpDecorate %arr_mat ArrayStride 32
-               OpDecorate %arr_arr_mat ArrayStride 32
-               OpDecorate %_struct_2 Block
-               OpDecorate %3 DescriptorSet 0
-               OpDecorate %3 Binding 0
-       %void = OpTypeVoid
-          %5 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v4float = OpTypeVector %float 4
-    %v2float = OpTypeVector %float 2
-%mat3v2float = OpTypeMatrix %v2float 3
-%uint        = OpTypeInt 32 0
-%uint_1      = OpConstant %uint 1
-%uint_2      = OpConstant %uint 2
-    %arr_mat = OpTypeArray %mat3v2float %uint_1
-%arr_arr_mat = OpTypeArray %arr_mat %uint_2
-    %v3float = OpTypeVector %float 3
-  %_struct_2 = OpTypeStruct %v4float %arr_arr_mat %v3float %float
-%_ptr_Uniform__struct_2 = OpTypePointer Uniform %_struct_2
-          %3 = OpVariable %_ptr_Uniform__struct_2 Uniform
-          %1 = OpFunction %void None %5
-         %12 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0))
-      << getDiagnosticString();
-}
-
-TEST_F(ValidateDecorations, ArrayArrayRowMajorMatrixNextMemberOverlapsBad) {
-  // Like the previous case, but the offset of member 2 overlaps the matrix.
-  std::string spirv = R"(
-               OpCapability Shader
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %1 "main"
-               OpSource GLSL 450
-               OpMemberDecorate %_struct_2 0 Offset 0
-               OpMemberDecorate %_struct_2 1 RowMajor
-               OpMemberDecorate %_struct_2 1 Offset 16
-               OpMemberDecorate %_struct_2 1 MatrixStride 16
-               OpMemberDecorate %_struct_2 2 Offset 64
-               OpMemberDecorate %_struct_2 3 Offset 92
-               OpDecorate %arr_mat ArrayStride 32
-               OpDecorate %arr_arr_mat ArrayStride 32
-               OpDecorate %_struct_2 Block
-               OpDecorate %3 DescriptorSet 0
-               OpDecorate %3 Binding 0
-       %void = OpTypeVoid
-          %5 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v4float = OpTypeVector %float 4
-    %v2float = OpTypeVector %float 2
-%mat3v2float = OpTypeMatrix %v2float 3
-%uint        = OpTypeInt 32 0
-%uint_1      = OpConstant %uint 1
-%uint_2      = OpConstant %uint 2
-    %arr_mat = OpTypeArray %mat3v2float %uint_1
-%arr_arr_mat = OpTypeArray %arr_mat %uint_2
-    %v3float = OpTypeVector %float 3
-  %_struct_2 = OpTypeStruct %v4float %arr_arr_mat %v3float %float
-%_ptr_Uniform__struct_2 = OpTypePointer Uniform %_struct_2
-          %3 = OpVariable %_ptr_Uniform__struct_2 Uniform
-          %1 = OpFunction %void None %5
-         %12 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr(
-          "Structure id 2 decorated as Block for variable in Uniform storage "
-          "class must follow standard uniform buffer layout rules: member 2 at "
-          "offset 64 overlaps previous member ending at offset 79"));
-}
-
-TEST_F(ValidateDecorations, StorageBufferArraySizeCalculationPackGood) {
-  // Original GLSL
-
-  // #version 450
-  // layout (set=0,binding=0) buffer S {
-  //   uvec3 arr[2][2]; // first 3 elements are 16 bytes, last is 12
-  //   uint i;  // Can't have offset 60 = 3x16 + 12
-  // } B;
-  // void main() {}
-
-  std::string spirv = R"(
-               OpCapability Shader
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %1 "main"
-               OpDecorate %_arr_v3uint_uint_2 ArrayStride 16
-               OpDecorate %_arr__arr_v3uint_uint_2_uint_2 ArrayStride 32
-               OpMemberDecorate %_struct_4 0 Offset 0
-               OpMemberDecorate %_struct_4 1 Offset 64
-               OpDecorate %_struct_4 BufferBlock
-               OpDecorate %5 DescriptorSet 0
-               OpDecorate %5 Binding 0
-       %void = OpTypeVoid
-          %7 = OpTypeFunction %void
-       %uint = OpTypeInt 32 0
-     %v3uint = OpTypeVector %uint 3
-     %uint_2 = OpConstant %uint 2
-%_arr_v3uint_uint_2 = OpTypeArray %v3uint %uint_2
-%_arr__arr_v3uint_uint_2_uint_2 = OpTypeArray %_arr_v3uint_uint_2 %uint_2
-  %_struct_4 = OpTypeStruct %_arr__arr_v3uint_uint_2_uint_2 %uint
-%_ptr_Uniform__struct_4 = OpTypePointer Uniform %_struct_4
-          %5 = OpVariable %_ptr_Uniform__struct_4 Uniform
-          %1 = OpFunction %void None %7
-         %12 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_SUCCESS,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
-}
-
-TEST_F(ValidateDecorations, StorageBufferArraySizeCalculationPackGoodScalar) {
-  // Original GLSL
-
-  // #version 450
-  // layout (set=0,binding=0) buffer S {
-  //   uvec3 arr[2][2]; // first 3 elements are 16 bytes, last is 12
-  //   uint i;  // Can have offset 60 = 3x16 + 12
-  // } B;
-  // void main() {}
-
-  std::string spirv = R"(
-               OpCapability Shader
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %1 "main"
-               OpDecorate %_arr_v3uint_uint_2 ArrayStride 16
-               OpDecorate %_arr__arr_v3uint_uint_2_uint_2 ArrayStride 32
-               OpMemberDecorate %_struct_4 0 Offset 0
-               OpMemberDecorate %_struct_4 1 Offset 60
-               OpDecorate %_struct_4 BufferBlock
-               OpDecorate %5 DescriptorSet 0
-               OpDecorate %5 Binding 0
-       %void = OpTypeVoid
-          %7 = OpTypeFunction %void
-       %uint = OpTypeInt 32 0
-     %v3uint = OpTypeVector %uint 3
-     %uint_2 = OpConstant %uint 2
-%_arr_v3uint_uint_2 = OpTypeArray %v3uint %uint_2
-%_arr__arr_v3uint_uint_2_uint_2 = OpTypeArray %_arr_v3uint_uint_2 %uint_2
-  %_struct_4 = OpTypeStruct %_arr__arr_v3uint_uint_2_uint_2 %uint
-%_ptr_Uniform__struct_4 = OpTypePointer Uniform %_struct_4
-          %5 = OpVariable %_ptr_Uniform__struct_4 Uniform
-          %1 = OpFunction %void None %7
-         %12 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  options_->scalar_block_layout = true;
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_SUCCESS,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
-}
-
-TEST_F(ValidateDecorations, StorageBufferArraySizeCalculationPackBad) {
-  // Like previous but, the offset of the second member is too small.
-
-  std::string spirv = R"(
-               OpCapability Shader
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %1 "main"
-               OpDecorate %_arr_v3uint_uint_2 ArrayStride 16
-               OpDecorate %_arr__arr_v3uint_uint_2_uint_2 ArrayStride 32
-               OpMemberDecorate %_struct_4 0 Offset 0
-               OpMemberDecorate %_struct_4 1 Offset 60
-               OpDecorate %_struct_4 BufferBlock
-               OpDecorate %5 DescriptorSet 0
-               OpDecorate %5 Binding 0
-       %void = OpTypeVoid
-          %7 = OpTypeFunction %void
-       %uint = OpTypeInt 32 0
-     %v3uint = OpTypeVector %uint 3
-     %uint_2 = OpConstant %uint 2
-%_arr_v3uint_uint_2 = OpTypeArray %v3uint %uint_2
-%_arr__arr_v3uint_uint_2_uint_2 = OpTypeArray %_arr_v3uint_uint_2 %uint_2
-  %_struct_4 = OpTypeStruct %_arr__arr_v3uint_uint_2_uint_2 %uint
-%_ptr_Uniform__struct_4 = OpTypePointer Uniform %_struct_4
-          %5 = OpVariable %_ptr_Uniform__struct_4 Uniform
-          %1 = OpFunction %void None %7
-         %12 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Structure id 4 decorated as BufferBlock for variable "
-                        "in Uniform storage class must follow standard storage "
-                        "buffer layout rules: member 1 at offset 60 overlaps "
-                        "previous member ending at offset 63"));
-}
-
-TEST_F(ValidateDecorations, UniformBufferArraySizeCalculationPackGood) {
-  // Like the corresponding buffer block case, but the array padding must
-  // count for the last element as well, and so the offset of the second
-  // member must be at least 64.
-  std::string spirv = R"(
-               OpCapability Shader
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %1 "main"
-               OpDecorate %_arr_v3uint_uint_2 ArrayStride 16
-               OpDecorate %_arr__arr_v3uint_uint_2_uint_2 ArrayStride 32
-               OpMemberDecorate %_struct_4 0 Offset 0
-               OpMemberDecorate %_struct_4 1 Offset 64
-               OpDecorate %_struct_4 Block
-               OpDecorate %5 DescriptorSet 0
-               OpDecorate %5 Binding 0
-       %void = OpTypeVoid
-          %7 = OpTypeFunction %void
-       %uint = OpTypeInt 32 0
-     %v3uint = OpTypeVector %uint 3
-     %uint_2 = OpConstant %uint 2
-%_arr_v3uint_uint_2 = OpTypeArray %v3uint %uint_2
-%_arr__arr_v3uint_uint_2_uint_2 = OpTypeArray %_arr_v3uint_uint_2 %uint_2
-  %_struct_4 = OpTypeStruct %_arr__arr_v3uint_uint_2_uint_2 %uint
-%_ptr_Uniform__struct_4 = OpTypePointer Uniform %_struct_4
-          %5 = OpVariable %_ptr_Uniform__struct_4 Uniform
-          %1 = OpFunction %void None %7
-         %12 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_SUCCESS,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
-}
-
-TEST_F(ValidateDecorations, UniformBufferArraySizeCalculationPackBad) {
-  // Like previous but, the offset of the second member is too small.
-
-  std::string spirv = R"(
-               OpCapability Shader
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %1 "main"
-               OpDecorate %_arr_v3uint_uint_2 ArrayStride 16
-               OpDecorate %_arr__arr_v3uint_uint_2_uint_2 ArrayStride 32
-               OpMemberDecorate %_struct_4 0 Offset 0
-               OpMemberDecorate %_struct_4 1 Offset 60
-               OpDecorate %_struct_4 Block
-               OpDecorate %5 DescriptorSet 0
-               OpDecorate %5 Binding 0
-       %void = OpTypeVoid
-          %7 = OpTypeFunction %void
-       %uint = OpTypeInt 32 0
-     %v3uint = OpTypeVector %uint 3
-     %uint_2 = OpConstant %uint 2
-%_arr_v3uint_uint_2 = OpTypeArray %v3uint %uint_2
-%_arr__arr_v3uint_uint_2_uint_2 = OpTypeArray %_arr_v3uint_uint_2 %uint_2
-  %_struct_4 = OpTypeStruct %_arr__arr_v3uint_uint_2_uint_2 %uint
-%_ptr_Uniform__struct_4 = OpTypePointer Uniform %_struct_4
-          %5 = OpVariable %_ptr_Uniform__struct_4 Uniform
-          %1 = OpFunction %void None %7
-         %12 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr(
-          "Structure id 4 decorated as Block for variable in Uniform storage "
-          "class must follow standard uniform buffer layout rules: member 1 at "
-          "offset 60 overlaps previous member ending at offset 63"));
-}
-
-TEST_F(ValidateDecorations, LayoutNotCheckedWhenSkipBlockLayout) {
-  // Checks that block layout is not verified in skipping block layout mode.
-  // Even for obviously wrong layout.
-  std::string spirv = R"(
-               OpCapability Shader
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %main "main"
-               OpSource GLSL 450
-               OpMemberDecorate %S 0 Offset 3 ; wrong alignment
-               OpMemberDecorate %S 1 Offset 3 ; same offset as before!
-               OpDecorate %S Block
-               OpDecorate %B DescriptorSet 0
-               OpDecorate %B Binding 0
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v3float = OpTypeVector %float 3
-          %S = OpTypeStruct %float %v3float
-%_ptr_Uniform_S = OpTypePointer Uniform %S
-          %B = OpVariable %_ptr_Uniform_S Uniform
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv);
-  spvValidatorOptionsSetSkipBlockLayout(getValidatorOptions(), true);
-  EXPECT_EQ(SPV_SUCCESS,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(getDiagnosticString(), Eq(""));
-}
-
 TEST_F(ValidateDecorations, EntryPointVariableWrongStorageClass) {
   const std::string spirv = R"(
 OpCapability Shader
@@ -5508,42 +3106,6 @@
                         "'1[%1]'"));
 }
 
-TEST_F(ValidateDecorations, RecurseThroughRuntimeArray) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %outer Block
-OpMemberDecorate %inner 0 Offset 0
-OpMemberDecorate %inner 1 Offset 1
-OpDecorate %runtime ArrayStride 16
-OpMemberDecorate %outer 0 Offset 0
-%int = OpTypeInt 32 0
-%inner = OpTypeStruct %int %int
-%runtime = OpTypeRuntimeArray %inner
-%outer = OpTypeStruct %runtime
-%outer_ptr = OpTypePointer StorageBuffer %outer
-%var = OpVariable %outer_ptr StorageBuffer
-%void = OpTypeVoid
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr(
-          "Structure id 3 decorated as Block for variable in StorageBuffer "
-          "storage class must follow standard storage buffer layout "
-          "rules: member 1 at offset 1 is not aligned to 4"));
-}
-
 TEST_F(ValidateDecorations, VulkanStructWithoutDecorationWithRuntimeArray) {
   std::string str = R"(
               OpCapability Shader
@@ -6623,229 +4185,6 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState());
 }
 
-TEST_F(ValidateDecorations, InvalidStraddle) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpMemberDecorate %inner_struct 0 Offset 0
-OpMemberDecorate %inner_struct 1 Offset 4
-OpDecorate %outer_struct Block
-OpMemberDecorate %outer_struct 0 Offset 0
-OpMemberDecorate %outer_struct 1 Offset 8
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-%void = OpTypeVoid
-%float = OpTypeFloat 32
-%float2 = OpTypeVector %float 2
-%inner_struct = OpTypeStruct %float %float2
-%outer_struct = OpTypeStruct %float2 %inner_struct
-%ptr_ssbo_outer = OpTypePointer StorageBuffer %outer_struct
-%var = OpVariable %ptr_ssbo_outer StorageBuffer
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Structure id 2 decorated as Block for variable in "
-                        "StorageBuffer storage class must follow relaxed "
-                        "storage buffer layout rules: member 1 is an "
-                        "improperly straddling vector at offset 12"));
-}
-
-TEST_F(ValidateDecorations, DescriptorArray) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %struct Block
-OpMemberDecorate %struct 0 Offset 0
-OpMemberDecorate %struct 1 Offset 1
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-%void = OpTypeVoid
-%float = OpTypeFloat 32
-%int = OpTypeInt 32 0
-%int_2 = OpConstant %int 2
-%float2 = OpTypeVector %float 2
-%struct = OpTypeStruct %float %float2
-%struct_array = OpTypeArray %struct %int_2
-%ptr_ssbo_array = OpTypePointer StorageBuffer %struct_array
-%var = OpVariable %ptr_ssbo_array StorageBuffer
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Structure id 2 decorated as Block for variable in "
-                        "StorageBuffer storage class must follow standard "
-                        "storage buffer layout rules: member 1 at offset 1 is "
-                        "not aligned to 8"));
-}
-
-TEST_F(ValidateDecorations, DescriptorRuntimeArray) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpCapability RuntimeDescriptorArrayEXT
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-OpExtension "SPV_EXT_descriptor_indexing"
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %struct Block
-OpMemberDecorate %struct 0 Offset 0
-OpMemberDecorate %struct 1 Offset 1
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-%void = OpTypeVoid
-%float = OpTypeFloat 32
-%int = OpTypeInt 32 0
-%float2 = OpTypeVector %float 2
-%struct = OpTypeStruct %float %float2
-%struct_array = OpTypeRuntimeArray %struct
-%ptr_ssbo_array = OpTypePointer StorageBuffer %struct_array
-%var = OpVariable %ptr_ssbo_array StorageBuffer
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Structure id 2 decorated as Block for variable in "
-                        "StorageBuffer storage class must follow standard "
-                        "storage buffer layout rules: member 1 at offset 1 is "
-                        "not aligned to 8"));
-}
-
-TEST_F(ValidateDecorations, MultiDimensionalArray) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %struct Block
-OpMemberDecorate %struct 0 Offset 0
-OpDecorate %array_4 ArrayStride 4
-OpDecorate %array_3 ArrayStride 48
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%int_3 = OpConstant %int 3
-%int_4 = OpConstant %int 4
-%array_4 = OpTypeArray %int %int_4
-%array_3 = OpTypeArray %array_4 %int_3
-%struct = OpTypeStruct %array_3
-%ptr_struct = OpTypePointer Uniform %struct
-%var = OpVariable %ptr_struct Uniform
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Structure id 2 decorated as Block for variable in "
-                        "Uniform storage class must follow standard uniform "
-                        "buffer layout rules: member 0 contains an array with "
-                        "stride 4 not satisfying alignment to 16"));
-}
-
-TEST_F(ValidateDecorations, ImproperStraddleInArray) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %struct Block
-OpMemberDecorate %struct 0 Offset 0
-OpDecorate %array ArrayStride 24
-OpMemberDecorate %inner 0 Offset 0
-OpMemberDecorate %inner 1 Offset 4
-OpMemberDecorate %inner 2 Offset 12
-OpMemberDecorate %inner 3 Offset 16
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%int_2 = OpConstant %int 2
-%int2 = OpTypeVector %int 2
-%inner = OpTypeStruct %int %int2 %int %int
-%array = OpTypeArray %inner %int_2
-%struct = OpTypeStruct %array
-%ptr_struct = OpTypePointer StorageBuffer %struct
-%var = OpVariable %ptr_struct StorageBuffer
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Structure id 4 decorated as Block for variable in "
-                        "StorageBuffer storage class must follow relaxed "
-                        "storage buffer layout rules: member 1 is an "
-                        "improperly straddling vector at offset 28"));
-}
-
-TEST_F(ValidateDecorations, LargeArray) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %struct Block
-OpMemberDecorate %struct 0 Offset 0
-OpDecorate %array ArrayStride 24
-OpMemberDecorate %inner 0 Offset 0
-OpMemberDecorate %inner 1 Offset 8
-OpMemberDecorate %inner 2 Offset 16
-OpMemberDecorate %inner 3 Offset 20
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%int_2000000 = OpConstant %int 2000000
-%int2 = OpTypeVector %int 2
-%inner = OpTypeStruct %int %int2 %int %int
-%array = OpTypeArray %inner %int_2000000
-%struct = OpTypeStruct %array
-%ptr_struct = OpTypePointer StorageBuffer %struct
-%var = OpVariable %ptr_struct StorageBuffer
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
-  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
-}
-
 // NonReadable/NonWritable
 
 // Returns a SPIR-V shader module with variables in various storage classes,
@@ -7417,7 +4756,8 @@
 %uint_2 = OpConstant %uint 2
 %arr_v3float_uint_2 = OpTypeArray %v3float %uint_2
 %float_0 = OpConstant %float 0
-%_ptr_Output_type = OpTypePointer Output %)" + type + R"(
+%_ptr_Output_type = OpTypePointer Output %)" +
+         type + R"(
 %entryPointOutput = OpVariable %_ptr_Output_type Output
 %main = OpFunction %void None %3
 %5 = OpLabel
@@ -7998,72 +5338,6 @@
                         "identified with a Block or BufferBlock decoration"));
 }
 
-TEST_F(ValidateDecorations, VulkanArrayStrideZero) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-OpDecorate %struct Block
-OpMemberDecorate %struct 0 Offset 0
-OpDecorate %array ArrayStride 0
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%int_4 = OpConstant %int 4
-%array = OpTypeArray %int %int_4
-%struct = OpTypeStruct %array
-%ptr_ssbo_struct = OpTypePointer StorageBuffer %struct
-%var = OpVariable %ptr_ssbo_struct StorageBuffer
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("contains an array with stride 0"));
-}
-
-TEST_F(ValidateDecorations, VulkanArrayStrideTooSmall) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-OpDecorate %struct Block
-OpMemberDecorate %struct 0 Offset 0
-OpDecorate %inner ArrayStride 4
-OpDecorate %outer ArrayStride 4
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%int_4 = OpConstant %int 4
-%inner = OpTypeArray %int %int_4
-%outer = OpTypeArray %inner %int_4
-%struct = OpTypeStruct %outer
-%ptr_ssbo_struct = OpTypePointer StorageBuffer %struct
-%var = OpVariable %ptr_ssbo_struct StorageBuffer
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr(
-          "contains an array with stride 4, but with an element size of 16"));
-}
-
 TEST_F(ValidateDecorations, FunctionsWithOpGroupDecorate) {
   std::string spirv = R"(
                 OpCapability Addresses
@@ -8169,38 +5443,6 @@
   EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a variable"));
 }
 
-TEST_F(ValidateDecorations, WorkgroupSingleBlockVariable) {
-  std::string spirv = R"(
-               OpCapability Shader
-               OpCapability WorkgroupMemoryExplicitLayoutKHR
-               OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint GLCompute %main "main" %_
-               OpExecutionMode %main LocalSize 8 1 1
-               OpMemberDecorate %first 0 Offset 0
-               OpDecorate %first Block
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-        %int = OpTypeInt 32 1
-      %first = OpTypeStruct %int
-%_ptr_Workgroup_first = OpTypePointer Workgroup %first
-          %_ = OpVariable %_ptr_Workgroup_first Workgroup
-      %int_0 = OpConstant %int 0
-      %int_2 = OpConstant %int 2
-%_ptr_Workgroup_int = OpTypePointer Workgroup %int
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-         %13 = OpAccessChain %_ptr_Workgroup_int %_ %int_0
-               OpStore %13 %int_2
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
-  EXPECT_EQ(SPV_SUCCESS,
-	    ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4));
-}
-
 TEST_F(ValidateDecorations, WorkgroupBlockVariableRequiresV14) {
   std::string spirv = R"(
                OpCapability Shader
@@ -8235,209 +5477,6 @@
               HasSubstr("requires SPIR-V version 1.4 or later"));
 }
 
-TEST_F(ValidateDecorations, WorkgroupSingleNonBlockVariable) {
-  std::string spirv = R"(
-               OpCapability Shader
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint GLCompute %main "main" %a
-               OpExecutionMode %main LocalSize 8 1 1
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-        %int = OpTypeInt 32 1
-%_ptr_Workgroup_int = OpTypePointer Workgroup %int
-          %a = OpVariable %_ptr_Workgroup_int Workgroup
-      %int_2 = OpConstant %int 2
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpStore %a %int_2
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
-  EXPECT_EQ(SPV_SUCCESS,
-	    ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4));
-}
-
-TEST_F(ValidateDecorations, WorkgroupMultiBlockVariable) {
-  std::string spirv = R"(
-               OpCapability Shader
-               OpCapability WorkgroupMemoryExplicitLayoutKHR
-               OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint GLCompute %main "main" %_ %__0
-               OpExecutionMode %main LocalSize 8 1 1
-               OpMemberDecorate %first 0 Offset 0
-               OpDecorate %first Block
-               OpMemberDecorate %second 0 Offset 0
-               OpDecorate %second Block
-               OpDecorate %_ Aliased
-               OpDecorate %__0 Aliased
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-        %int = OpTypeInt 32 1
-      %first = OpTypeStruct %int
-%_ptr_Workgroup_first = OpTypePointer Workgroup %first
-          %_ = OpVariable %_ptr_Workgroup_first Workgroup
-      %int_0 = OpConstant %int 0
-      %int_2 = OpConstant %int 2
-%_ptr_Workgroup_int = OpTypePointer Workgroup %int
-     %second = OpTypeStruct %int
-%_ptr_Workgroup_second = OpTypePointer Workgroup %second
-        %__0 = OpVariable %_ptr_Workgroup_second Workgroup
-      %int_3 = OpConstant %int 3
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-         %13 = OpAccessChain %_ptr_Workgroup_int %_ %int_0
-               OpStore %13 %int_2
-         %18 = OpAccessChain %_ptr_Workgroup_int %__0 %int_0
-               OpStore %18 %int_3
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
-  EXPECT_EQ(SPV_SUCCESS,
-	    ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4));
-}
-
-TEST_F(ValidateDecorations, WorkgroupBlockVariableWith8BitType) {
-  std::string spirv = R"(
-               OpCapability Shader
-               OpCapability Int8
-               OpCapability WorkgroupMemoryExplicitLayout8BitAccessKHR
-               OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint GLCompute %main "main" %_
-               OpExecutionMode %main LocalSize 2 1 1
-               OpMemberDecorate %first 0 Offset 0
-               OpDecorate %first Block
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-       %char = OpTypeInt 8 1
-      %first = OpTypeStruct %char
-%_ptr_Workgroup_first = OpTypePointer Workgroup %first
-          %_ = OpVariable %_ptr_Workgroup_first Workgroup
-        %int = OpTypeInt 32 1
-      %int_0 = OpConstant %int 0
-     %char_2 = OpConstant %char 2
-%_ptr_Workgroup_char = OpTypePointer Workgroup %char
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-         %14 = OpAccessChain %_ptr_Workgroup_char %_ %int_0
-               OpStore %14 %char_2
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
-  EXPECT_EQ(SPV_SUCCESS,
-	    ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4));
-}
-
-TEST_F(ValidateDecorations, WorkgroupMultiNonBlockVariable) {
-  std::string spirv = R"(
-               OpCapability Shader
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint GLCompute %main "main" %a %b
-               OpExecutionMode %main LocalSize 8 1 1
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-        %int = OpTypeInt 32 1
-%_ptr_Workgroup_int = OpTypePointer Workgroup %int
-          %a = OpVariable %_ptr_Workgroup_int Workgroup
-      %int_2 = OpConstant %int 2
-          %b = OpVariable %_ptr_Workgroup_int Workgroup
-      %int_3 = OpConstant %int 3
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpStore %a %int_2
-               OpStore %b %int_3
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
-  EXPECT_EQ(SPV_SUCCESS,
-	    ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4));
-}
-
-TEST_F(ValidateDecorations, WorkgroupBlockVariableWith16BitType) {
-  std::string spirv = R"(
-               OpCapability Shader
-               OpCapability Float16
-               OpCapability Int16
-               OpCapability WorkgroupMemoryExplicitLayoutKHR
-               OpCapability WorkgroupMemoryExplicitLayout16BitAccessKHR
-               OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint GLCompute %main "main" %_
-               OpExecutionMode %main LocalSize 2 1 1
-               OpMemberDecorate %first 0 Offset 0
-               OpMemberDecorate %first 1 Offset 2
-               OpDecorate %first Block
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %short = OpTypeInt 16 1
-       %half = OpTypeFloat 16
-      %first = OpTypeStruct %short %half
-%_ptr_Workgroup_first = OpTypePointer Workgroup %first
-          %_ = OpVariable %_ptr_Workgroup_first Workgroup
-        %int = OpTypeInt 32 1
-      %int_0 = OpConstant %int 0
-    %short_3 = OpConstant %short 3
-%_ptr_Workgroup_short = OpTypePointer Workgroup %short
-      %int_1 = OpConstant %int 1
-%half_0x1_898p_3 = OpConstant %half 0x1.898p+3
-%_ptr_Workgroup_half = OpTypePointer Workgroup %half
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-         %15 = OpAccessChain %_ptr_Workgroup_short %_ %int_0
-               OpStore %15 %short_3
-         %19 = OpAccessChain %_ptr_Workgroup_half %_ %int_1
-               OpStore %19 %half_0x1_898p_3
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
-  EXPECT_EQ(SPV_SUCCESS,
-	    ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4));
-}
-
-TEST_F(ValidateDecorations, WorkgroupBlockVariableScalarLayout) {
-  std::string spirv = R"(
-               OpCapability Shader
-               OpCapability WorkgroupMemoryExplicitLayoutKHR
-               OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint Vertex %main "main" %B
-               OpSource GLSL 450
-               OpMemberDecorate %S 0 Offset 0
-               OpMemberDecorate %S 1 Offset 4
-               OpMemberDecorate %S 2 Offset 16
-               OpMemberDecorate %S 3 Offset 28
-               OpDecorate %S Block
-               OpDecorate %B Aliased
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-      %float = OpTypeFloat 32
-    %v3float = OpTypeVector %float 3
-          %S = OpTypeStruct %float %v3float %v3float %v3float
-%_ptr_Workgroup_S = OpTypePointer Workgroup %S
-          %B = OpVariable %_ptr_Workgroup_S Workgroup
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
-  spvValidatorOptionsSetWorkgroupScalarBlockLayout(getValidatorOptions(), true);
-  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4))
-      << getDiagnosticString();
-}
-
 TEST_F(ValidateDecorations, WorkgroupMixBlockAndNonBlockBad) {
   std::string spirv = R"(
                OpCapability Shader
@@ -8559,77 +5598,6 @@
   EXPECT_THAT(getDiagnosticString(), HasSubstr("must be a structure type"));
 }
 
-TEST_F(ValidateDecorations, WorkgroupSingleBlockVariableMissingLayout) {
-  std::string spirv = R"(
-               OpCapability Shader
-               OpCapability WorkgroupMemoryExplicitLayoutKHR
-               OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint GLCompute %main "main" %_
-               OpExecutionMode %main LocalSize 8 1 1
-               OpDecorate %first Block
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-        %int = OpTypeInt 32 1
-      %first = OpTypeStruct %int
-%_ptr_Workgroup_first = OpTypePointer Workgroup %first
-          %_ = OpVariable %_ptr_Workgroup_first Workgroup
-      %int_0 = OpConstant %int 0
-      %int_2 = OpConstant %int 2
-%_ptr_Workgroup_int = OpTypePointer Workgroup %int
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-         %13 = OpAccessChain %_ptr_Workgroup_int %_ %int_0
-               OpStore %13 %int_2
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1_SPIRV_1_4));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("Block must be explicitly laid out with Offset decorations"));
-}
-
-TEST_F(ValidateDecorations, WorkgroupSingleBlockVariableBadLayout) {
-  std::string spirv = R"(
-               OpCapability Shader
-               OpCapability WorkgroupMemoryExplicitLayoutKHR
-               OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
-               OpMemoryModel Logical GLSL450
-               OpEntryPoint GLCompute %main "main" %_
-               OpExecutionMode %main LocalSize 8 1 1
-               OpMemberDecorate %first 0 Offset 1
-               OpDecorate %first Block
-       %void = OpTypeVoid
-          %3 = OpTypeFunction %void
-        %int = OpTypeInt 32 1
-      %first = OpTypeStruct %int
-%_ptr_Workgroup_first = OpTypePointer Workgroup %first
-          %_ = OpVariable %_ptr_Workgroup_first Workgroup
-      %int_0 = OpConstant %int 0
-      %int_2 = OpConstant %int 2
-%_ptr_Workgroup_int = OpTypePointer Workgroup %int
-       %main = OpFunction %void None %3
-          %5 = OpLabel
-         %13 = OpAccessChain %_ptr_Workgroup_int %_ %int_0
-               OpStore %13 %int_2
-               OpReturn
-               OpFunctionEnd
-  )";
-
-  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID,
-            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1_SPIRV_1_4));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("Block for variable in Workgroup storage class must follow "
-                "relaxed storage buffer layout rules: "
-                "member 0 at offset 1 is not aligned to 4"));
-}
-
 TEST_F(ValidateDecorations, WorkgroupBlockNoCapability) {
   std::string spirv = R"(
                OpCapability Shader
@@ -8661,215 +5629,6 @@
           "unless declaring the WorkgroupMemoryExplicitLayoutKHR capability"));
 }
 
-TEST_F(ValidateDecorations, BadMatrixStrideUniform) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %block Block
-OpMemberDecorate %block 0 Offset 0
-OpMemberDecorate %block 0 MatrixStride 3
-OpMemberDecorate %block 0 ColMajor
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-%void = OpTypeVoid
-%float = OpTypeFloat 32
-%float4 = OpTypeVector %float 4
-%matrix4x4 = OpTypeMatrix %float4 4
-%block = OpTypeStruct %matrix4x4
-%block_ptr = OpTypePointer Uniform %block
-%var = OpVariable %block_ptr Uniform
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr(
-          "Structure id 2 decorated as Block for variable in Uniform storage "
-          "class must follow standard uniform buffer layout rules: member 0 is "
-          "a matrix with stride 3 not satisfying alignment to 16"));
-}
-
-TEST_F(ValidateDecorations, BadMatrixStrideStorageBuffer) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %block Block
-OpMemberDecorate %block 0 Offset 0
-OpMemberDecorate %block 0 MatrixStride 3
-OpMemberDecorate %block 0 ColMajor
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-%void = OpTypeVoid
-%float = OpTypeFloat 32
-%float4 = OpTypeVector %float 4
-%matrix4x4 = OpTypeMatrix %float4 4
-%block = OpTypeStruct %matrix4x4
-%block_ptr = OpTypePointer StorageBuffer %block
-%var = OpVariable %block_ptr StorageBuffer
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr(
-          "Structure id 2 decorated as Block for variable in StorageBuffer "
-          "storage class must follow standard storage buffer layout rules: "
-          "member 0 is a matrix with stride 3 not satisfying alignment to 16"));
-}
-
-TEST_F(ValidateDecorations, BadMatrixStridePushConstant) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %block Block
-OpMemberDecorate %block 0 Offset 0
-OpMemberDecorate %block 0 MatrixStride 3
-OpMemberDecorate %block 0 ColMajor
-%void = OpTypeVoid
-%float = OpTypeFloat 32
-%float4 = OpTypeVector %float 4
-%matrix4x4 = OpTypeMatrix %float4 4
-%block = OpTypeStruct %matrix4x4
-%block_ptr = OpTypePointer PushConstant %block
-%var = OpVariable %block_ptr PushConstant
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr(
-          "Structure id 2 decorated as Block for variable in PushConstant "
-          "storage class must follow standard storage buffer layout rules: "
-          "member 0 is a matrix with stride 3 not satisfying alignment to 16"));
-}
-
-TEST_F(ValidateDecorations, BadMatrixStrideStorageBufferScalarLayout) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %block Block
-OpMemberDecorate %block 0 Offset 0
-OpMemberDecorate %block 0 MatrixStride 3
-OpMemberDecorate %block 0 RowMajor
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-%void = OpTypeVoid
-%float = OpTypeFloat 32
-%float4 = OpTypeVector %float 4
-%matrix4x4 = OpTypeMatrix %float4 4
-%block = OpTypeStruct %matrix4x4
-%block_ptr = OpTypePointer StorageBuffer %block
-%var = OpVariable %block_ptr StorageBuffer
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  options_->scalar_block_layout = true;
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr(
-          "Structure id 2 decorated as Block for variable in StorageBuffer "
-          "storage class must follow scalar storage buffer layout rules: "
-          "member 0 is a matrix with stride 3 not satisfying alignment to 4"));
-}
-
-TEST_F(ValidateDecorations, MissingOffsetStructNestedInArray) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %array ArrayStride 4
-OpDecorate %outer Block
-OpMemberDecorate %outer 0 Offset 0
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%int_4 = OpConstant %int 4
-%inner = OpTypeStruct %int
-%array = OpTypeArray %inner %int_4
-%outer = OpTypeStruct %array
-%ptr_ssbo_outer = OpTypePointer StorageBuffer %outer
-%var = OpVariable %ptr_ssbo_outer StorageBuffer
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Structure id 3 decorated as Block must be explicitly "
-                        "laid out with Offset decorations"));
-}
-
-TEST_F(ValidateDecorations, AllOnesOffset) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-OpDecorate %outer Block
-OpMemberDecorate %outer 0 Offset 0
-OpMemberDecorate %struct 0 Offset 4294967295
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%struct = OpTypeStruct %int
-%outer = OpTypeStruct %struct
-%ptr = OpTypePointer Uniform %outer
-%var = OpVariable %ptr Uniform
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("decorated as Block must be explicitly laid out with "
-                        "Offset decorations"));
-}
-
 TEST_F(ValidateDecorations, PerVertexVulkanGood) {
   const std::string spirv = R"(
                OpCapability Shader
@@ -9384,306 +6143,6 @@
   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
 }
 
-TEST_F(ValidateDecorations, Std140ColMajorMat2x2) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %block Block
-OpMemberDecorate %block 0 Offset 0
-OpMemberDecorate %block 0 ColMajor
-OpMemberDecorate %block 0 MatrixStride 8
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-%void = OpTypeVoid
-%void_fn = OpTypeFunction %void
-%float = OpTypeFloat 32
-%float2 = OpTypeVector %float 2
-%matrix = OpTypeMatrix %float2 2
-%block = OpTypeStruct %matrix
-%ptr_block = OpTypePointer Uniform %block
-%var = OpVariable %ptr_block Uniform
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr(
-          "member 0 is a matrix with stride 8 not satisfying alignment to 16"));
-}
-
-TEST_F(ValidateDecorations, Std140RowMajorMat2x2) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %block Block
-OpMemberDecorate %block 0 Offset 0
-OpMemberDecorate %block 0 RowMajor
-OpMemberDecorate %block 0 MatrixStride 8
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-%void = OpTypeVoid
-%void_fn = OpTypeFunction %void
-%float = OpTypeFloat 32
-%float2 = OpTypeVector %float 2
-%matrix = OpTypeMatrix %float2 2
-%block = OpTypeStruct %matrix
-%ptr_block = OpTypePointer Uniform %block
-%var = OpVariable %ptr_block Uniform
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr(
-          "member 0 is a matrix with stride 8 not satisfying alignment to 16"));
-}
-
-TEST_F(ValidateDecorations, Std140ColMajorMat4x2) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %block Block
-OpMemberDecorate %block 0 Offset 0
-OpMemberDecorate %block 0 ColMajor
-OpMemberDecorate %block 0 MatrixStride 8
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-%void = OpTypeVoid
-%void_fn = OpTypeFunction %void
-%float = OpTypeFloat 32
-%float2 = OpTypeVector %float 2
-%matrix = OpTypeMatrix %float2 4
-%block = OpTypeStruct %matrix
-%ptr_block = OpTypePointer Uniform %block
-%var = OpVariable %ptr_block Uniform
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr(
-          "member 0 is a matrix with stride 8 not satisfying alignment to 16"));
-}
-
-TEST_F(ValidateDecorations, Std140ColMajorMat2x3) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %block Block
-OpMemberDecorate %block 0 Offset 0
-OpMemberDecorate %block 0 ColMajor
-OpMemberDecorate %block 0 MatrixStride 12
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-%void = OpTypeVoid
-%void_fn = OpTypeFunction %void
-%float = OpTypeFloat 32
-%float3 = OpTypeVector %float 3
-%matrix = OpTypeMatrix %float3 2
-%block = OpTypeStruct %matrix
-%ptr_block = OpTypePointer Uniform %block
-%var = OpVariable %ptr_block Uniform
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("member 0 is a matrix with stride 12 not satisfying "
-                        "alignment to 16"));
-}
-
-TEST_F(ValidateDecorations, MatrixMissingMajornessUniform) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %block Block
-OpMemberDecorate %block 0 Offset 0
-OpMemberDecorate %block 0 MatrixStride 16
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-%void = OpTypeVoid
-%void_fn = OpTypeFunction %void
-%float = OpTypeFloat 32
-%float2 = OpTypeVector %float 2
-%matrix = OpTypeMatrix %float2 2
-%block = OpTypeStruct %matrix
-%ptr_block = OpTypePointer Uniform %block
-%var = OpVariable %ptr_block Uniform
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr(
-          "must be explicitly laid out with RowMajor or ColMajor decorations"));
-}
-
-TEST_F(ValidateDecorations, MatrixMissingMajornessStorageBuffer) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %block Block
-OpMemberDecorate %block 0 Offset 0
-OpMemberDecorate %block 0 MatrixStride 16
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-%void = OpTypeVoid
-%void_fn = OpTypeFunction %void
-%float = OpTypeFloat 32
-%float2 = OpTypeVector %float 2
-%matrix = OpTypeMatrix %float2 2
-%block = OpTypeStruct %matrix
-%ptr_block = OpTypePointer StorageBuffer %block
-%var = OpVariable %ptr_block StorageBuffer
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr(
-          "must be explicitly laid out with RowMajor or ColMajor decorations"));
-}
-
-TEST_F(ValidateDecorations, MatrixMissingMajornessPushConstant) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %block Block
-OpMemberDecorate %block 0 Offset 0
-OpMemberDecorate %block 0 MatrixStride 16
-%void = OpTypeVoid
-%void_fn = OpTypeFunction %void
-%float = OpTypeFloat 32
-%float2 = OpTypeVector %float 2
-%matrix = OpTypeMatrix %float2 2
-%block = OpTypeStruct %matrix
-%ptr_block = OpTypePointer PushConstant %block
-%var = OpVariable %ptr_block PushConstant
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr(
-          "must be explicitly laid out with RowMajor or ColMajor decorations"));
-}
-
-TEST_F(ValidateDecorations, StructWithRowAndColMajor) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %block Block
-OpMemberDecorate %block 0 Offset 0
-OpMemberDecorate %block 0 MatrixStride 16
-OpMemberDecorate %block 0 ColMajor
-OpMemberDecorate %block 1 Offset 32
-OpMemberDecorate %block 1 MatrixStride 16
-OpMemberDecorate %block 1 RowMajor
-%void = OpTypeVoid
-%void_fn = OpTypeFunction %void
-%float = OpTypeFloat 32
-%float2 = OpTypeVector %float 2
-%matrix = OpTypeMatrix %float2 2
-%block = OpTypeStruct %matrix %matrix
-%ptr_block = OpTypePointer PushConstant %block
-%var = OpVariable %ptr_block PushConstant
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
-  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-}
-
-TEST_F(ValidateDecorations, PhysicalStorageBufferWithOffset) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpCapability Int64
-OpCapability PhysicalStorageBufferAddresses
-OpMemoryModel PhysicalStorageBuffer64 GLSL450
-OpEntryPoint GLCompute %main "main" %pc
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %pc_block Block
-OpMemberDecorate %pc_block 0 Offset 0
-OpMemberDecorate %pssbo_struct 0 Offset 0
-%void = OpTypeVoid
-%long = OpTypeInt 64 0
-%float = OpTypeFloat 32
-%int = OpTypeInt 32 0
-%int_0 = OpConstant %int 0
-%pc_block = OpTypeStruct %long
-%pc_block_ptr = OpTypePointer PushConstant %pc_block
-%pc_long_ptr = OpTypePointer PushConstant %long
-%pc = OpVariable %pc_block_ptr PushConstant
-%pssbo_struct = OpTypeStruct %float
-%pssbo_ptr = OpTypePointer PhysicalStorageBuffer %pssbo_struct
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-%pc_gep = OpAccessChain %pc_long_ptr %pc %int_0
-%addr = OpLoad %long %pc_gep
-%ptr = OpConvertUToPtr %pssbo_ptr %addr
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
-  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
-}
-
 TEST_F(ValidateDecorations, UntypedVariableDuplicateInterface) {
   const std::string spirv = R"(
 OpCapability Shader
@@ -9715,314 +6174,6 @@
                         "interface '2[%var]' is disallowed"));
 }
 
-TEST_F(ValidateDecorations, PhysicalStorageBufferMissingOffset) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpCapability Int64
-OpCapability PhysicalStorageBufferAddresses
-OpMemoryModel PhysicalStorageBuffer64 GLSL450
-OpEntryPoint GLCompute %main "main" %pc
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %pc_block Block
-OpMemberDecorate %pc_block 0 Offset 0
-%void = OpTypeVoid
-%long = OpTypeInt 64 0
-%float = OpTypeFloat 32
-%int = OpTypeInt 32 0
-%int_0 = OpConstant %int 0
-%pc_block = OpTypeStruct %long
-%pc_block_ptr = OpTypePointer PushConstant %pc_block
-%pc_long_ptr = OpTypePointer PushConstant %long
-%pc = OpVariable %pc_block_ptr PushConstant
-%pssbo_struct = OpTypeStruct %float
-%pssbo_ptr = OpTypePointer PhysicalStorageBuffer %pssbo_struct
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-%pc_gep = OpAccessChain %pc_long_ptr %pc %int_0
-%addr = OpLoad %long %pc_gep
-%ptr = OpConvertUToPtr %pssbo_ptr %addr
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("decorated as Block for variable in PhysicalStorageBuffer "
-                "storage class must follow relaxed storage buffer layout "
-                "rules: member 0 is missing an Offset decoration"));
-}
-
-TEST_F(ValidateDecorations, PhysicalStorageBufferMissingArrayStride) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpCapability Int64
-OpCapability PhysicalStorageBufferAddresses
-OpMemoryModel PhysicalStorageBuffer64 GLSL450
-OpEntryPoint GLCompute %main "main" %pc
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %pc_block Block
-OpMemberDecorate %pc_block 0 Offset 0
-%void = OpTypeVoid
-%long = OpTypeInt 64 0
-%float = OpTypeFloat 32
-%int = OpTypeInt 32 0
-%int_0 = OpConstant %int 0
-%int_4 = OpConstant %int 4
-%pc_block = OpTypeStruct %long
-%pc_block_ptr = OpTypePointer PushConstant %pc_block
-%pc_long_ptr = OpTypePointer PushConstant %long
-%pc = OpVariable %pc_block_ptr PushConstant
-%pssbo_array = OpTypeArray %float %int_4
-%pssbo_ptr = OpTypePointer PhysicalStorageBuffer %pssbo_array
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-%pc_gep = OpAccessChain %pc_long_ptr %pc %int_0
-%addr = OpLoad %long %pc_gep
-%ptr = OpConvertUToPtr %pssbo_ptr %addr
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr(
-          "decorated as Block for variable in PhysicalStorageBuffer storage "
-          "class must follow relaxed storage buffer layout rules: member 0 "
-          "contains an array with stride 0, but with an element size of 4"));
-}
-
-TEST_F(ValidateDecorations, MatrixArrayMissingMajorness) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-OpDecorate %block Block
-OpMemberDecorate %block 0 Offset 0
-OpMemberDecorate %block 0 MatrixStride 16
-OpDecorate %array ArrayStride 32
-%void = OpTypeVoid
-%float = OpTypeFloat 32
-%int = OpTypeInt 32 0
-%int_2 = OpConstant %int 2
-%vec = OpTypeVector %float 2
-%mat = OpTypeMatrix %vec 2
-%array = OpTypeArray %mat %int_2
-%block = OpTypeStruct %array
-%ptr = OpTypePointer Uniform %block
-%var = OpVariable %ptr Uniform
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr(
-          "must be explicitly laid out with RowMajor or ColMajor decorations"));
-}
-
-TEST_F(ValidateDecorations, MatrixArrayMissingStride) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-OpDecorate %block Block
-OpMemberDecorate %block 0 Offset 0
-OpMemberDecorate %block 0 ColMajor
-OpDecorate %array ArrayStride 32
-%void = OpTypeVoid
-%float = OpTypeFloat 32
-%int = OpTypeInt 32 0
-%int_2 = OpConstant %int 2
-%vec = OpTypeVector %float 2
-%mat = OpTypeMatrix %vec 2
-%array = OpTypeArray %mat %int_2
-%block = OpTypeStruct %array
-%ptr = OpTypePointer Uniform %block
-%var = OpVariable %ptr Uniform
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("must be explicitly laid out with MatrixStride decorations"));
-}
-
-TEST_F(ValidateDecorations, MatrixArrayBadStride) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-OpDecorate %block Block
-OpMemberDecorate %block 0 Offset 0
-OpMemberDecorate %block 0 ColMajor
-OpMemberDecorate %block 0 MatrixStride 8
-OpDecorate %array ArrayStride 32
-%void = OpTypeVoid
-%float = OpTypeFloat 32
-%int = OpTypeInt 32 0
-%int_2 = OpConstant %int 2
-%vec = OpTypeVector %float 2
-%mat = OpTypeMatrix %vec 2
-%array = OpTypeArray %mat %int_2
-%block = OpTypeStruct %array
-%ptr = OpTypePointer Uniform %block
-%var = OpVariable %ptr Uniform
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("is a matrix with stride 8 not satisfying alignment to 16"));
-}
-
-TEST_F(ValidateDecorations, MatrixArrayArrayMissingMajorness) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-OpDecorate %block Block
-OpMemberDecorate %block 0 Offset 0
-OpMemberDecorate %block 0 MatrixStride 16
-OpDecorate %array ArrayStride 32
-OpDecorate %rta ArrayStride 64
-%void = OpTypeVoid
-%float = OpTypeFloat 32
-%int = OpTypeInt 32 0
-%int_2 = OpConstant %int 2
-%vec = OpTypeVector %float 2
-%mat = OpTypeMatrix %vec 2
-%array = OpTypeArray %mat %int_2
-%rta = OpTypeRuntimeArray %array
-%block = OpTypeStruct %rta
-%ptr = OpTypePointer StorageBuffer %block
-%var = OpVariable %ptr StorageBuffer
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr(
-          "must be explicitly laid out with RowMajor or ColMajor decorations"));
-}
-
-TEST_F(ValidateDecorations, MatrixArrayArrayMissingStride) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-OpDecorate %block Block
-OpMemberDecorate %block 0 Offset 0
-OpMemberDecorate %block 0 ColMajor
-OpDecorate %array ArrayStride 32
-OpDecorate %rta ArrayStride 64
-%void = OpTypeVoid
-%float = OpTypeFloat 32
-%int = OpTypeInt 32 0
-%int_2 = OpConstant %int 2
-%vec = OpTypeVector %float 2
-%mat = OpTypeMatrix %vec 2
-%array = OpTypeArray %mat %int_2
-%rta = OpTypeRuntimeArray %array
-%block = OpTypeStruct %rta
-%ptr = OpTypePointer StorageBuffer %block
-%var = OpVariable %ptr StorageBuffer
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("must be explicitly laid out with MatrixStride decorations"));
-}
-
-TEST_F(ValidateDecorations, MatrixArrayArrayBadStride) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-OpDecorate %block Block
-OpMemberDecorate %block 0 Offset 0
-OpMemberDecorate %block 0 ColMajor
-OpMemberDecorate %block 0 MatrixStride 8
-OpDecorate %array ArrayStride 32
-OpDecorate %a ArrayStride 64
-%void = OpTypeVoid
-%float = OpTypeFloat 32
-%int = OpTypeInt 32 0
-%int_2 = OpConstant %int 2
-%vec = OpTypeVector %float 2
-%mat = OpTypeMatrix %vec 2
-%array = OpTypeArray %mat %int_2
-%a = OpTypeArray %array %int_2
-%block = OpTypeStruct %a
-%ptr = OpTypePointer Uniform %block
-%var = OpVariable %ptr Uniform
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("is a matrix with stride 8 not satisfying alignment to 16"));
-}
-
 TEST_F(ValidateDecorations, MultipleBuiltinsInputVertex) {
   const std::string body = R"(
                OpCapability Shader
@@ -10320,61 +6471,6 @@
               AnyVUID("VUID-StandaloneSpirv-OpEntryPoint-09659"));
 }
 
-TEST_F(ValidateDecorations, UntypedVariableWorkgroupRequiresStruct) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpCapability UntypedPointersKHR
-OpCapability WorkgroupMemoryExplicitLayoutKHR
-OpExtension "SPV_KHR_untyped_pointers"
-OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main" %var
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%ptr = OpTypeUntypedPointerKHR Workgroup
-%var = OpUntypedVariableKHR %ptr Workgroup %int
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Untyped workgroup variables in shaders must be block "
-                        "decorated structs"));
-}
-
-TEST_F(ValidateDecorations, UntypedVariableWorkgroupRequiresBlockStruct) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpCapability UntypedPointersKHR
-OpCapability WorkgroupMemoryExplicitLayoutKHR
-OpExtension "SPV_KHR_untyped_pointers"
-OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main" %var
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%struct = OpTypeStruct %int
-%ptr = OpTypeUntypedPointerKHR Workgroup
-%var = OpUntypedVariableKHR %ptr Workgroup %struct
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("Untyped workgroup variables in shaders must be block "
-                        "decorated"));
-}
-
 TEST_F(ValidateDecorations, UntypedVariableStorageBufferMissingBlock) {
   const std::string spirv = R"(
 OpCapability Shader
@@ -10479,7 +6575,8 @@
 %struct = OpTypeStruct %int
 %ptr = OpTypeUntypedPointerKHR )" +
                             sc + R"(
-%var = OpUntypedVariableKHR %ptr )" + sc + R"( %struct
+%var = OpUntypedVariableKHR %ptr )" +
+                            sc + R"( %struct
 %void_fn = OpTypeFunction %void
 %main = OpFunction %void None %void_fn
 %entry = OpLabel
@@ -10513,7 +6610,8 @@
 %struct = OpTypeStruct %int
 %ptr = OpTypeUntypedPointerKHR )" +
                             sc + R"(
-%var = OpUntypedVariableKHR %ptr )" + sc + R"( %struct
+%var = OpUntypedVariableKHR %ptr )" +
+                            sc + R"( %struct
 %void_fn = OpTypeFunction %void
 %main = OpFunction %void None %void_fn
 %entry = OpLabel
@@ -10532,164 +6630,6 @@
                          UntypedVariableSetAndBinding,
                          Values("StorageBuffer", "Uniform"));
 
-using UntypedPointerLayout =
-    spvtest::ValidateBase<std::tuple<std::string, std::string>>;
-
-TEST_P(UntypedPointerLayout, BadOffset) {
-  const auto sc = std::get<0>(GetParam());
-  const auto op = std::get<1>(GetParam());
-  const std::string set = (sc == "StorageBuffer" || sc == "Uniform"
-                               ? R"(OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-)"
-                               : R"()");
-  const std::string spirv = R"(
-OpCapability Shader
-OpCapability VariablePointers
-OpCapability UntypedPointersKHR
-OpCapability WorkgroupMemoryExplicitLayoutKHR
-OpExtension "SPV_KHR_untyped_pointers"
-OpExtension "SPV_KHR_variable_pointers"
-OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main" %var
-OpExecutionMode %main LocalSize 1 1 1
-OpName %var "var"
-OpDecorate %struct Block
-OpMemberDecorate %struct 0 Offset 0
-OpMemberDecorate %struct 1 Offset 4
-)" + set + R"(OpMemberDecorate %test_type 0 Offset 0
-OpMemberDecorate %test_type 1 Offset 1
-OpDecorate %ptr ArrayStride 16
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%int_0 = OpConstant %int 0
-%struct = OpTypeStruct %int %int
-%test_type = OpTypeStruct %int %int
-%test_val = OpConstantNull %test_type
-%ptr = OpTypeUntypedPointerKHR )" +
-                            sc + R"(
-%var = OpUntypedVariableKHR %ptr )" +
-                            sc + R"( %struct
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-)" + op + R"(
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_2);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
-  const bool read_only = sc == "Uniform" || sc == "PushConstant";
-  if (!read_only || op.find("OpStore") == std::string::npos) {
-    EXPECT_THAT(getDiagnosticString(),
-                HasSubstr("member 1 at offset 1 is not aligned to"));
-  }
-}
-
-TEST_P(UntypedPointerLayout, BadStride) {
-  const auto sc = std::get<0>(GetParam());
-  const auto op = std::get<1>(GetParam());
-  const std::string set = (sc == "StorageBuffer" || sc == "Uniform"
-                               ? R"(OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-)"
-                               : R"()");
-  const std::string spirv = R"(
-OpCapability Shader
-OpCapability VariablePointers
-OpCapability UntypedPointersKHR
-OpCapability WorkgroupMemoryExplicitLayoutKHR
-OpExtension "SPV_KHR_untyped_pointers"
-OpExtension "SPV_KHR_variable_pointers"
-OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main" %var
-OpExecutionMode %main LocalSize 1 1 1
-OpName %var "var"
-OpDecorate %struct Block
-OpMemberDecorate %struct 0 Offset 0
-OpMemberDecorate %struct 1 Offset 4
-)" + set + R"(OpDecorate %test_type ArrayStride 4
-OpDecorate %ptr ArrayStride 16
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%int_0 = OpConstant %int 0
-%int_4 = OpConstant %int 4
-%int4 = OpTypeVector %int 4
-%test_type = OpTypeArray %int4 %int_4
-%test_val = OpConstantNull %test_type
-%struct = OpTypeStruct %int %int
-%ptr = OpTypeUntypedPointerKHR )" +
-                            sc + R"(
-%var = OpUntypedVariableKHR %ptr )" +
-                            sc + R"( %struct
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-)" + op + R"(
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_2);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
-  const bool read_only = sc == "Uniform" || sc == "PushConstant";
-  if (!read_only || op.find("OpStore") == std::string::npos) {
-    EXPECT_THAT(
-        getDiagnosticString(),
-        HasSubstr("array with stride 4 not satisfying alignment to 16"));
-  }
-}
-
-INSTANTIATE_TEST_SUITE_P(
-    ValidateUntypedPointerLayout, UntypedPointerLayout,
-    Combine(Values("StorageBuffer", "Uniform", "PushConstant", "Workgroup"),
-            Values("%gep = OpUntypedAccessChainKHR %ptr %test_type %var %int_0",
-                   "%gep = OpUntypedInBoundsAccessChainKHR %ptr %test_type "
-                   "%var %int_0",
-                   "%gep = OpUntypedPtrAccessChainKHR %ptr %test_type %var "
-                   "%int_0 %int_0",
-                   "%ld = OpLoad %test_type %var", "OpStore %var %test_val")));
-
-TEST_F(ValidateDecorations, UntypedArrayLengthMissingOffset) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpCapability UntypedPointersKHR
-OpExtension "SPV_KHR_untyped_pointers"
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %struct Block
-OpDecorate %block Block
-OpMemberDecorate %block 0 Offset 0
-OpDecorate %array ArrayStride 4
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%array = OpTypeRuntimeArray %int
-%struct = OpTypeStruct %array
-%block = OpTypeStruct %array
-%ptr = OpTypeUntypedPointerKHR StorageBuffer
-%var = OpUntypedVariableKHR %ptr StorageBuffer %block
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-%len = OpUntypedArrayLengthKHR %int %struct %var 0
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_2);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("member 0 is missing an Offset decoration"));
-}
-
 TEST_F(ValidateDecorations, ComponentMultipleArrays) {
   const std::string spirv = R"(
                OpCapability Tessellation
@@ -10958,527 +6898,6 @@
                         "decorated with ArrayStride"));
 }
 
-TEST_F(ValidateDecorations, BlockArrayWithoutStride) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %struct Block
-OpMemberDecorate %struct 0 Offset 0
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-%int = OpTypeInt 32 0
-%int_4 = OpConstant %int 4
-%struct = OpTypeStruct %int
-%array = OpTypeArray %struct %int_4
-%ptr = OpTypePointer StorageBuffer %array
-%var = OpVariable %ptr StorageBuffer
-%void = OpTypeVoid
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
-  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-}
-
-TEST_F(ValidateDecorations, BlockArrayWithoutStrideUntypedAccessChain) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpCapability UntypedPointersKHR
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-OpExtension "SPV_KHR_untyped_pointers"
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %struct Block
-OpMemberDecorate %struct 0 Offset 0
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-%int = OpTypeInt 32 0
-%int_4 = OpConstant %int 4
-%struct = OpTypeStruct %int
-%array = OpTypeArray %struct %int_4
-%void = OpTypeVoid
-%ptr = OpTypeUntypedPointerKHR StorageBuffer
-%var = OpUntypedVariableKHR %ptr StorageBuffer %array
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-%gep = OpUntypedAccessChainKHR %ptr %array %var
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
-  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-}
-
-TEST_F(ValidateDecorations, InvalidLayoutBlockFunctionPre1p4) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %block Block
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%block = OpTypeStruct %int
-%ptr_function_block = OpTypePointer Function %block
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-%var = OpVariable %ptr_function_block Function
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
-  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
-}
-
-TEST_F(ValidateDecorations, InvalidLayoutBlockFunctionPost1p4) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %block Block
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%block = OpTypeStruct %int
-%ptr_function_block = OpTypePointer Function %block
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-%var = OpVariable %ptr_function_block Function
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_5);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
-  EXPECT_THAT(getDiagnosticString(),
-              AnyVUID("VUID-StandaloneSpirv-None-10684"));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("Invalid explicit layout decorations on type for operand"));
-}
-
-TEST_F(ValidateDecorations, InvalidLayoutOffsetPrivatePre1p4) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpMemberDecorate %block 0 Offset 0
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%block = OpTypeStruct %int
-%ptr_private_block = OpTypePointer Private %block
-%void_fn = OpTypeFunction %void
-%var = OpVariable %ptr_private_block Private
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
-  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-}
-
-TEST_F(ValidateDecorations, InvalidLayoutOffsetPrivatePost1p4) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpMemberDecorate %block 0 Offset 0
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%block = OpTypeStruct %int
-%ptr_private_block = OpTypePointer Private %block
-%void_fn = OpTypeFunction %void
-%var = OpVariable %ptr_private_block Private
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
-  EXPECT_THAT(getDiagnosticString(),
-              AnyVUID("VUID-StandaloneSpirv-None-10684"));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("Invalid explicit layout decorations on type for operand"));
-}
-
-TEST_F(ValidateDecorations, InvalidLayoutArrayStrideWorkgroupExplicitLayout) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpCapability WorkgroupMemoryExplicitLayoutKHR
-OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %array ArrayStride 4
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%int_4 = OpConstant %int 4
-%array = OpTypeArray %int %int_4
-%ptr_wg_block = OpTypePointer Workgroup %array
-%void_fn = OpTypeFunction %void
-%var = OpVariable %ptr_wg_block Workgroup
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
-  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
-}
-
-TEST_F(ValidateDecorations, InvalidLayoutArrayStrideWorkgroup) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %array ArrayStride 4
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%int_4 = OpConstant %int 4
-%array = OpTypeArray %int %int_4
-%ptr_wg_block = OpTypePointer Workgroup %array
-%void_fn = OpTypeFunction %void
-%var = OpVariable %ptr_wg_block Workgroup
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(getDiagnosticString(),
-              AnyVUID("VUID-StandaloneSpirv-None-10684"));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("Invalid explicit layout decorations on type for operand"));
-}
-
-TEST_F(ValidateDecorations, InvalidLayoutArrayStrideUniformConstant) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %array ArrayStride 4
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%int_4 = OpConstant %int 4
-%sampler = OpTypeSampler
-%array = OpTypeArray %sampler %int_4
-%ptr_uc_block = OpTypePointer UniformConstant %array
-%void_fn = OpTypeFunction %void
-%var = OpVariable %ptr_uc_block UniformConstant
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(getDiagnosticString(),
-              AnyVUID("VUID-StandaloneSpirv-None-10684"));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("Invalid explicit layout decorations on type for operand"));
-}
-
-TEST_F(ValidateDecorations, InvalidLayoutMatrixStrideFunctionPost1p4) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpMemberDecorate %block 0 MatrixStride 16
-%void = OpTypeVoid
-%float = OpTypeFloat 32
-%v4float = OpTypeVector %float 4
-%mat4x4 = OpTypeMatrix %v4float 4
-%block = OpTypeStruct %mat4x4
-%ptr_function_block = OpTypePointer Function %block
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-%var = OpVariable %ptr_function_block Function
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
-  EXPECT_THAT(getDiagnosticString(),
-              AnyVUID("VUID-StandaloneSpirv-None-10684"));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("Invalid explicit layout decorations on type for operand"));
-}
-
-TEST_F(ValidateDecorations, InvalidLayoutNestedMatrixStrideFunctionPost1p4) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpMemberDecorate %block 0 MatrixStride 16
-%void = OpTypeVoid
-%float = OpTypeFloat 32
-%v4float = OpTypeVector %float 4
-%mat4x4 = OpTypeMatrix %v4float 4
-%block = OpTypeStruct %mat4x4
-%block2 = OpTypeStruct %block
-%int = OpTypeInt 32 0
-%int_2 = OpConstant %int 2
-%array = OpTypeArray %block2 %int_2
-%ptr_function_array = OpTypePointer Function %array
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-%var = OpVariable %ptr_function_array Function
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
-  EXPECT_THAT(getDiagnosticString(),
-              AnyVUID("VUID-StandaloneSpirv-None-10684"));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("Invalid explicit layout decorations on type for operand"));
-}
-
-TEST_F(ValidateDecorations, InvalidLayoutBufferBlockWorkgroup) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %block BufferBlock
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%block = OpTypeStruct %int
-%ptr_wg_block = OpTypePointer Workgroup %block
-%void_fn = OpTypeFunction %void
-%var = OpVariable %ptr_wg_block Workgroup
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(getDiagnosticString(),
-              AnyVUID("VUID-StandaloneSpirv-None-10684"));
-  EXPECT_THAT(
-      getDiagnosticString(),
-      HasSubstr("Invalid explicit layout decorations on type for operand"));
-}
-
-TEST_F(ValidateDecorations, InvalidLayoutUntypedStore) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpCapability UntypedPointersKHR
-OpExtension "SPV_KHR_untyped_pointers"
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %block Block
-OpMemberDecorate %block 0 Offset 0
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-%void = OpTypeVoid
-%int = OpTypeInt 32 0
-%block = OpTypeStruct %int
-%block_null = OpConstantNull %block
-%ptr = OpTypeUntypedPointerKHR StorageBuffer
-%var = OpUntypedVariableKHR %ptr StorageBuffer %block
-%void_fn = OpTypeFunction %void
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpStore %var %block_null
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
-  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-}
-
-TEST_F(ValidateDecorations, ExplicitLayoutOnPtrPhysicalStorageBuffer) {
-  const std::string spirv = R"(
-OpCapability PhysicalStorageBufferAddresses
-OpCapability Int64
-OpCapability Shader
-OpExtension "SPV_KHR_physical_storage_buffer"
-OpMemoryModel PhysicalStorageBuffer64 GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %_ptr_PhysicalStorageBuffer_int ArrayStride 4
-%void = OpTypeVoid
-%void_fn = OpTypeFunction %void
-%int = OpTypeInt 32 1
-%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int  ; ArrayStride 4
-%Foo = OpTypeStruct %_ptr_PhysicalStorageBuffer_int
-%_ptr_Function_Foo = OpTypePointer Function %Foo
-%int_0 = OpConstant %int 0
-%_ptr_Function__ptr_PhysicalStorageBuffer_int = OpTypePointer Function %_ptr_PhysicalStorageBuffer_int
-%ulong = OpTypeInt 64 0
-%ulong_0 = OpConstant %ulong 0
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-%obj = OpVariable %_ptr_Function_Foo Function
-%obj_member = OpAccessChain %_ptr_Function__ptr_PhysicalStorageBuffer_int %obj %int_0
-%nullptr = OpConvertUToPtr %_ptr_PhysicalStorageBuffer_int %ulong_0
-OpStore %obj_member %nullptr
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_5);
-  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
-}
-
-TEST_F(ValidateDecorations, RuntimeArrayNotLargestOffsetInBlock) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpExtension "SPV_KHR_storage_buffer_storage_class"
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-OpDecorate %block Block
-OpMemberDecorate %block 0 Offset 16
-OpMemberDecorate %block 1 Offset 0
-%void = OpTypeVoid
-%void_fn = OpTypeFunction %void
-%int = OpTypeInt 32 0
-%array = OpTypeRuntimeArray %int
-%block = OpTypeStruct %int %array
-%ptr = OpTypePointer StorageBuffer %block
-%var = OpVariable %ptr StorageBuffer
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("has a runtime array at offset 0, but other members at "
-                        "larger offsets"));
-  EXPECT_THAT(getDiagnosticString(),
-              AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680"));
-}
-
-TEST_F(ValidateDecorations, RuntimeArrayNotLargestOffsetInBufferBlock) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpMemoryModel Logical GLSL450
-OpEntryPoint GLCompute %main "main"
-OpExecutionMode %main LocalSize 1 1 1
-OpDecorate %var DescriptorSet 0
-OpDecorate %var Binding 0
-OpDecorate %block BufferBlock
-OpMemberDecorate %block 0 Offset 16
-OpMemberDecorate %block 1 Offset 0
-%void = OpTypeVoid
-%void_fn = OpTypeFunction %void
-%int = OpTypeInt 32 0
-%array = OpTypeRuntimeArray %int
-%block = OpTypeStruct %int %array
-%ptr = OpTypePointer Uniform %block
-%var = OpVariable %ptr Uniform
-%main = OpFunction %void None %void_fn
-%entry = OpLabel
-OpReturn
-OpFunctionEnd
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
-  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-  EXPECT_THAT(getDiagnosticString(),
-              HasSubstr("has a runtime array at offset 0, but other members at "
-                        "larger offsets"));
-  EXPECT_THAT(getDiagnosticString(),
-              AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680"));
-}
-
-TEST_F(ValidateDecorations, LongVectorUniformPass) {
-  const std::string spirv = R"(
-OpCapability Shader
-OpCapability Int8
-OpCapability UniformAndStorageBuffer8BitAccess
-OpCapability LongVectorEXT
-
-OpExtension "SPV_KHR_8bit_storage"
-OpExtension "SPV_EXT_long_vector"
-
-OpMemoryModel Logical GLSL450
-OpEntryPoint Vertex %BP_main "main"
-
-OpDecorate %input0 DescriptorSet 0
-OpDecorate %input0 Binding 0
-OpDecorate %a10testtype ArrayStride 12
-OpDecorate %buf BufferBlock
-OpMemberDecorate %buf 0 Offset 0
-
-%void = OpTypeVoid
-%bool = OpTypeBool
-%u32 = OpTypeInt 32 0
-%voidf = OpTypeFunction %void
-%c_u32_10 = OpConstant %u32 10
-%vectorSizeConst = OpConstant %u32 12
-
-%scalartype = OpTypeInt 8 1
-%testtype = OpTypeVectorIdEXT %scalartype %vectorSizeConst
-
-%a10testtype = OpTypeArray %testtype %c_u32_10
-%buf = OpTypeStruct %a10testtype
-%bufptr = OpTypePointer Uniform %buf
-
-%input0 = OpVariable %bufptr Uniform
-
-
-%BP_main = OpFunction %void None %voidf
-%BP_label = OpLabel
-OpReturn
-OpFunctionEnd
-
-)";
-
-  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
-  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
-}
-
 TEST_F(ValidateDecorations, LongVectorUniformSpecConstantFail) {
   const std::string spirv = R"(
 OpCapability Shader
diff --git a/test/val/val_explicit_layout_test.cpp b/test/val/val_explicit_layout_test.cpp
new file mode 100644
index 0000000..a38c061
--- /dev/null
+++ b/test/val/val_explicit_layout_test.cpp
@@ -0,0 +1,5659 @@
+// Copyright (c) 2026 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Validation tests for explicit layout
+
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "source/val/decoration.h"
+#include "spirv-tools/libspirv.h"
+#include "test/unit_spirv.h"
+#include "test/val/val_code_generator.h"
+#include "test/val/val_fixtures.h"
+
+namespace spvtools {
+namespace val {
+namespace {
+
+using ::testing::Combine;
+using ::testing::Eq;
+using ::testing::HasSubstr;
+using ::testing::Values;
+
+using ValidateExplicitLayout = spvtest::ValidateBase<bool>;
+
+TEST_F(ValidateExplicitLayout, BlockMissingOffsetBad) {
+  std::string spirv = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 430
+               OpDecorate %Output Block
+               OpDecorate %dataOutput DescriptorSet 0
+               OpDecorate %dataOutput Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+     %Output = OpTypeStruct %float
+%_ptr_Uniform_Output = OpTypePointer Uniform %Output
+ %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("must be explicitly laid out with Offset or "
+                        "OffsetIdEXT decorations"));
+}
+
+TEST_F(ValidateExplicitLayout, BufferBlockMissingOffsetBad) {
+  std::string spirv = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 430
+               OpDecorate %Output BufferBlock
+               OpDecorate %dataOutput DescriptorSet 0
+               OpDecorate %dataOutput Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+     %Output = OpTypeStruct %float
+%_ptr_Uniform_Output = OpTypePointer Uniform %Output
+ %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("must be explicitly laid out with Offset or "
+                        "OffsetIdEXT decorations"));
+}
+
+TEST_F(ValidateExplicitLayout, BlockNestedStructMissingOffsetBad) {
+  std::string spirv = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 430
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %Output 0 Offset 0
+               OpMemberDecorate %Output 1 Offset 16
+               OpMemberDecorate %Output 2 Offset 32
+               OpDecorate %Output Block
+               OpDecorate %dataOutput DescriptorSet 0
+               OpDecorate %dataOutput Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+    %v3float = OpTypeVector %float 3
+        %int = OpTypeInt 32 1
+          %S = OpTypeStruct %v3float %int
+     %Output = OpTypeStruct %float %v4float %S
+%_ptr_Uniform_Output = OpTypePointer Uniform %Output
+ %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("must be explicitly laid out with Offset or "
+                        "OffsetIdEXT decorations"));
+}
+
+TEST_F(ValidateExplicitLayout, BufferBlockNestedStructMissingOffsetBad) {
+  std::string spirv = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 430
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %Output 0 Offset 0
+               OpMemberDecorate %Output 1 Offset 16
+               OpMemberDecorate %Output 2 Offset 32
+               OpDecorate %Output BufferBlock
+               OpDecorate %dataOutput DescriptorSet 0
+               OpDecorate %dataOutput Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+    %v3float = OpTypeVector %float 3
+        %int = OpTypeInt 32 1
+          %S = OpTypeStruct %v3float %int
+     %Output = OpTypeStruct %float %v4float %S
+%_ptr_Uniform_Output = OpTypePointer Uniform %Output
+ %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("must be explicitly laid out with Offset or "
+                        "OffsetIdEXT decorations"));
+}
+
+TEST_F(ValidateExplicitLayout, BlockMissingArrayStrideBad) {
+  std::string spirv = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 430
+               OpDecorate %Output Block
+               OpMemberDecorate %Output 0 Offset 0
+               OpDecorate %dataOutput DescriptorSet 0
+               OpDecorate %dataOutput Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+        %int = OpTypeInt 32 1
+      %int_3 = OpConstant %int 3
+      %array = OpTypeArray %float %int_3
+     %Output = OpTypeStruct %array
+%_ptr_Uniform_Output = OpTypePointer Uniform %Output
+ %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("must be explicitly laid out with ArrayStride or "
+                        "ArrayStrideIdEXT decorations"));
+}
+
+TEST_F(ValidateExplicitLayout, BufferBlockMissingArrayStrideBad) {
+  std::string spirv = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 430
+               OpDecorate %Output BufferBlock
+               OpMemberDecorate %Output 0 Offset 0
+               OpDecorate %dataOutput DescriptorSet 0
+               OpDecorate %dataOutput Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+        %int = OpTypeInt 32 1
+      %int_3 = OpConstant %int 3
+      %array = OpTypeArray %float %int_3
+     %Output = OpTypeStruct %array
+%_ptr_Uniform_Output = OpTypePointer Uniform %Output
+ %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("must be explicitly laid out with ArrayStride or "
+                        "ArrayStrideIdEXT decorations"));
+}
+
+TEST_F(ValidateExplicitLayout, BlockNestedStructMissingArrayStrideBad) {
+  std::string spirv = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 430
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %Output 0 Offset 0
+               OpMemberDecorate %Output 1 Offset 16
+               OpMemberDecorate %Output 2 Offset 32
+               OpDecorate %Output Block
+               OpDecorate %dataOutput DescriptorSet 0
+               OpDecorate %dataOutput Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+        %int = OpTypeInt 32 1
+      %int_3 = OpConstant %int 3
+      %array = OpTypeArray %float %int_3
+          %S = OpTypeStruct %array
+     %Output = OpTypeStruct %float %v4float %S
+%_ptr_Uniform_Output = OpTypePointer Uniform %Output
+ %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("must be explicitly laid out with ArrayStride or "
+                        "ArrayStrideIdEXT decorations"));
+}
+
+TEST_F(ValidateExplicitLayout, BufferBlockNestedStructMissingArrayStrideBad) {
+  std::string spirv = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 430
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %Output 0 Offset 0
+               OpMemberDecorate %Output 1 Offset 16
+               OpMemberDecorate %Output 2 Offset 32
+               OpDecorate %Output BufferBlock
+               OpDecorate %dataOutput DescriptorSet 0
+               OpDecorate %dataOutput Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+        %int = OpTypeInt 32 1
+      %int_3 = OpConstant %int 3
+      %array = OpTypeArray %float %int_3
+          %S = OpTypeStruct %array
+     %Output = OpTypeStruct %float %v4float %S
+%_ptr_Uniform_Output = OpTypePointer Uniform %Output
+ %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("must be explicitly laid out with ArrayStride or "
+                        "ArrayStrideIdEXT decorations"));
+}
+
+TEST_F(ValidateExplicitLayout, BlockMissingMatrixStrideBad) {
+  std::string spirv = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 430
+               OpDecorate %Output Block
+               OpMemberDecorate %Output 0 Offset 0
+               OpMemberDecorate %Output 0 ColMajor
+               OpDecorate %dataOutput DescriptorSet 0
+               OpDecorate %dataOutput Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+     %matrix = OpTypeMatrix %v3float 4
+     %Output = OpTypeStruct %matrix
+%_ptr_Uniform_Output = OpTypePointer Uniform %Output
+ %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("must be explicitly laid out with MatrixStride decorations"));
+}
+
+TEST_F(ValidateExplicitLayout, BufferBlockMissingMatrixStrideBad) {
+  std::string spirv = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 430
+               OpDecorate %Output BufferBlock
+               OpMemberDecorate %Output 0 Offset 0
+               OpMemberDecorate %Output 0 ColMajor
+               OpDecorate %dataOutput DescriptorSet 0
+               OpDecorate %dataOutput Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+     %matrix = OpTypeMatrix %v3float 4
+     %Output = OpTypeStruct %matrix
+%_ptr_Uniform_Output = OpTypePointer Uniform %Output
+ %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("must be explicitly laid out with MatrixStride decorations"));
+}
+
+TEST_F(ValidateExplicitLayout, BlockMissingMatrixStrideArrayBad) {
+  std::string spirv = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 430
+               OpDecorate %Output Block
+               OpMemberDecorate %Output 0 Offset 0
+               OpMemberDecorate %Output 0 RowMajor
+               OpDecorate %dataOutput DescriptorSet 0
+               OpDecorate %dataOutput Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+     %matrix = OpTypeMatrix %v3float 4
+        %int = OpTypeInt 32 1
+      %int_3 = OpConstant %int 3
+      %array = OpTypeArray %matrix %int_3
+     %Output = OpTypeStruct %matrix
+%_ptr_Uniform_Output = OpTypePointer Uniform %Output
+ %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("must be explicitly laid out with MatrixStride decorations"));
+}
+
+TEST_F(ValidateExplicitLayout, BufferBlockMissingMatrixStrideArrayBad) {
+  std::string spirv = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 430
+               OpDecorate %Output BufferBlock
+               OpMemberDecorate %Output 0 Offset 0
+               OpMemberDecorate %Output 0 RowMajor
+               OpDecorate %dataOutput DescriptorSet 0
+               OpDecorate %dataOutput Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+     %matrix = OpTypeMatrix %v3float 4
+        %int = OpTypeInt 32 1
+      %int_3 = OpConstant %int 3
+      %array = OpTypeArray %matrix %int_3
+     %Output = OpTypeStruct %matrix
+%_ptr_Uniform_Output = OpTypePointer Uniform %Output
+ %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("must be explicitly laid out with MatrixStride decorations"));
+}
+
+TEST_F(ValidateExplicitLayout, BlockNestedStructMissingMatrixStrideBad) {
+  std::string spirv = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 430
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %S 0 ColMajor
+               OpMemberDecorate %Output 0 Offset 0
+               OpMemberDecorate %Output 1 Offset 16
+               OpMemberDecorate %Output 2 Offset 32
+               OpDecorate %Output Block
+               OpDecorate %dataOutput DescriptorSet 0
+               OpDecorate %dataOutput Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+    %v4float = OpTypeVector %float 4
+     %matrix = OpTypeMatrix %v3float 4
+          %S = OpTypeStruct %matrix
+     %Output = OpTypeStruct %float %v4float %S
+%_ptr_Uniform_Output = OpTypePointer Uniform %Output
+ %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("must be explicitly laid out with MatrixStride decorations"));
+}
+
+TEST_F(ValidateExplicitLayout, BufferBlockNestedStructMissingMatrixStrideBad) {
+  std::string spirv = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 430
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %S 0 ColMajor
+               OpMemberDecorate %Output 0 Offset 0
+               OpMemberDecorate %Output 1 Offset 16
+               OpMemberDecorate %Output 2 Offset 32
+               OpDecorate %Output BufferBlock
+               OpDecorate %dataOutput DescriptorSet 0
+               OpDecorate %dataOutput Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+    %v4float = OpTypeVector %float 4
+     %matrix = OpTypeMatrix %v3float 4
+          %S = OpTypeStruct %matrix
+     %Output = OpTypeStruct %float %v4float %S
+%_ptr_Uniform_Output = OpTypePointer Uniform %Output
+ %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("must be explicitly laid out with MatrixStride decorations"));
+}
+
+TEST_F(ValidateExplicitLayout, BlockStandardUniformBufferLayout) {
+  std::string spirv = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 430
+               OpMemberDecorate %F 0 Offset 0
+               OpMemberDecorate %F 1 Offset 8
+               OpDecorate %_arr_float_uint_2 ArrayStride 16
+               OpDecorate %_arr_mat3v3float_uint_2 ArrayStride 48
+               OpMemberDecorate %O 0 Offset 0
+               OpMemberDecorate %O 1 Offset 16
+               OpMemberDecorate %O 2 Offset 32
+               OpMemberDecorate %O 3 Offset 64
+               OpMemberDecorate %O 4 ColMajor
+               OpMemberDecorate %O 4 Offset 80
+               OpMemberDecorate %O 4 MatrixStride 16
+               OpDecorate %_arr_O_uint_2 ArrayStride 176
+               OpMemberDecorate %Output 0 Offset 0
+               OpMemberDecorate %Output 1 Offset 8
+               OpMemberDecorate %Output 2 Offset 16
+               OpMemberDecorate %Output 3 Offset 32
+               OpMemberDecorate %Output 4 Offset 48
+               OpMemberDecorate %Output 5 Offset 64
+               OpMemberDecorate %Output 6 ColMajor
+               OpMemberDecorate %Output 6 Offset 96
+               OpMemberDecorate %Output 6 MatrixStride 16
+               OpMemberDecorate %Output 7 Offset 128
+               OpDecorate %Output Block
+               OpDecorate %dataOutput DescriptorSet 0
+               OpDecorate %dataOutput Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v2float = OpTypeVector %float 2
+    %v3float = OpTypeVector %float 3
+        %int = OpTypeInt 32 1
+       %uint = OpTypeInt 32 0
+     %v2uint = OpTypeVector %uint 2
+          %F = OpTypeStruct %int %v2uint
+     %uint_2 = OpConstant %uint 2
+%_arr_float_uint_2 = OpTypeArray %float %uint_2
+%mat2v3float = OpTypeMatrix %v3float 2
+     %v3uint = OpTypeVector %uint 3
+%mat3v3float = OpTypeMatrix %v3float 3
+%_arr_mat3v3float_uint_2 = OpTypeArray %mat3v3float %uint_2
+          %O = OpTypeStruct %v3uint %v2float %_arr_float_uint_2 %v2float %_arr_mat3v3float_uint_2
+%_arr_O_uint_2 = OpTypeArray %O %uint_2
+     %Output = OpTypeStruct %float %v2float %v3float %F %float %_arr_float_uint_2 %mat2v3float %_arr_O_uint_2
+%_ptr_Uniform_Output = OpTypePointer Uniform %Output
+ %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateExplicitLayout, BlockLayoutPermitsTightVec3ScalarPackingGood) {
+  // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1666
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main"
+               OpSource GLSL 450
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %S 1 Offset 12
+               OpDecorate %S Block
+               OpDecorate %B DescriptorSet 0
+               OpDecorate %B Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+          %S = OpTypeStruct %v3float %float
+%_ptr_Uniform_S = OpTypePointer Uniform %S
+          %B = OpVariable %_ptr_Uniform_S Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0))
+      << getDiagnosticString();
+}
+
+TEST_F(ValidateExplicitLayout, BlockLayoutForbidsTightScalarVec3PackingBad) {
+  // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1666
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main"
+               OpSource GLSL 450
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %S 1 Offset 4
+               OpDecorate %S Block
+               OpDecorate %B DescriptorSet 0
+               OpDecorate %B Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+          %S = OpTypeStruct %float %v3float
+%_ptr_Uniform_S = OpTypePointer Uniform %S
+          %B = OpVariable %_ptr_Uniform_S Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Structure member 1 at offset 4 is not aligned to 16"));
+}
+
+TEST_F(ValidateExplicitLayout,
+       BlockLayoutPermitsTightScalarVec3PackingWithRelaxedLayoutGood) {
+  // Same as previous test, but with explicit option to relax block layout.
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main"
+               OpSource GLSL 450
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %S 1 Offset 4
+               OpDecorate %S Block
+               OpDecorate %B DescriptorSet 0
+               OpDecorate %B Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+          %S = OpTypeStruct %float %v3float
+%_ptr_Uniform_S = OpTypePointer Uniform %S
+          %B = OpVariable %_ptr_Uniform_S Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  spvValidatorOptionsSetRelaxBlockLayout(getValidatorOptions(), true);
+  EXPECT_EQ(SPV_SUCCESS,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(), Eq(""));
+}
+
+TEST_F(ValidateExplicitLayout,
+       BlockLayoutPermitsTightScalarVec3PackingBadOffsetWithRelaxedLayoutBad) {
+  // Same as previous test, but with the vector not aligned to its scalar
+  // element. Use offset 5 instead of a multiple of 4.
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main"
+               OpSource GLSL 450
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %S 1 Offset 5
+               OpDecorate %S Block
+               OpDecorate %B DescriptorSet 0
+               OpDecorate %B Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+          %S = OpTypeStruct %float %v3float
+%_ptr_Uniform_S = OpTypePointer Uniform %S
+          %B = OpVariable %_ptr_Uniform_S Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  spvValidatorOptionsSetRelaxBlockLayout(getValidatorOptions(), true);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Structure member 1 at offset 5 is not aligned to 4"));
+}
+
+TEST_F(ValidateExplicitLayout,
+       BlockLayoutPermitsTightScalarVec3PackingWithVulkan1_1Good) {
+  // Same as previous test, but with Vulkan 1.1.  Vulkan 1.1 included
+  // VK_KHR_relaxed_block_layout in core.
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main"
+               OpSource GLSL 450
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %S 1 Offset 4
+               OpDecorate %S Block
+               OpDecorate %B DescriptorSet 0
+               OpDecorate %B Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+          %S = OpTypeStruct %float %v3float
+%_ptr_Uniform_S = OpTypePointer Uniform %S
+          %B = OpVariable %_ptr_Uniform_S Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(getDiagnosticString(), Eq(""));
+}
+
+TEST_F(ValidateExplicitLayout,
+       BlockLayoutPermitsTightScalarVec3PackingWithScalarLayoutGood) {
+  // Same as previous test, but with scalar block layout.
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main"
+               OpSource GLSL 450
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %S 1 Offset 4
+               OpDecorate %S Block
+               OpDecorate %B DescriptorSet 0
+               OpDecorate %B Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+          %S = OpTypeStruct %float %v3float
+%_ptr_Uniform_S = OpTypePointer Uniform %S
+          %B = OpVariable %_ptr_Uniform_S Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true);
+  EXPECT_EQ(SPV_SUCCESS,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(), Eq(""));
+}
+
+TEST_F(ValidateExplicitLayout,
+       BlockLayoutPermitsScalarAlignedArrayWithScalarLayoutGood) {
+  // The array at offset 4 is ok with scalar block layout.
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main"
+               OpSource GLSL 450
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %S 1 Offset 4
+               OpDecorate %S Block
+               OpDecorate %B DescriptorSet 0
+               OpDecorate %B Binding 0
+               OpDecorate %arr_float ArrayStride 4
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %uint_3 = OpConstant %uint 3
+      %float = OpTypeFloat 32
+  %arr_float = OpTypeArray %float %uint_3
+          %S = OpTypeStruct %float %arr_float
+%_ptr_Uniform_S = OpTypePointer Uniform %S
+          %B = OpVariable %_ptr_Uniform_S Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true);
+  EXPECT_EQ(SPV_SUCCESS,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(), Eq(""));
+}
+
+TEST_F(ValidateExplicitLayout,
+       BlockLayoutPermitsScalarAlignedArrayOfVec3WithScalarLayoutGood) {
+  // The array at offset 4 is ok with scalar block layout, even though
+  // its elements are vec3.
+  // This is the same as the previous case, but the array elements are vec3
+  // instead of float.
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main"
+               OpSource GLSL 450
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %S 1 Offset 4
+               OpDecorate %S Block
+               OpDecorate %B DescriptorSet 0
+               OpDecorate %B Binding 0
+               OpDecorate %arr_vec3 ArrayStride 12
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %uint_3 = OpConstant %uint 3
+      %float = OpTypeFloat 32
+       %vec3 = OpTypeVector %float 3
+   %arr_vec3 = OpTypeArray %vec3 %uint_3
+          %S = OpTypeStruct %float %arr_vec3
+%_ptr_Uniform_S = OpTypePointer Uniform %S
+          %B = OpVariable %_ptr_Uniform_S Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true);
+  EXPECT_EQ(SPV_SUCCESS,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(), Eq(""));
+}
+
+TEST_F(ValidateExplicitLayout,
+       BlockLayoutPermitsScalarAlignedStructWithScalarLayoutGood) {
+  // Scalar block layout permits the struct at offset 4, even though
+  // it contains a vector with base alignment 8 and scalar alignment 4.
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main"
+               OpSource GLSL 450
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %S 1 Offset 4
+               OpMemberDecorate %st 0 Offset 0
+               OpMemberDecorate %st 1 Offset 8
+               OpDecorate %S Block
+               OpDecorate %B DescriptorSet 0
+               OpDecorate %B Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+       %vec2 = OpTypeVector %float 2
+        %st  = OpTypeStruct %vec2 %float
+          %S = OpTypeStruct %float %st
+%_ptr_Uniform_S = OpTypePointer Uniform %S
+          %B = OpVariable %_ptr_Uniform_S Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true);
+  EXPECT_EQ(SPV_SUCCESS,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(), Eq(""));
+}
+
+TEST_F(
+    ValidateExplicitLayout,
+    BlockLayoutPermitsFieldsInBaseAlignmentPaddingAtEndOfStructWithScalarLayoutGood) {
+  // Scalar block layout permits fields in what would normally be the padding at
+  // the end of a struct.
+  std::string spirv = R"(
+               OpCapability Shader
+               OpCapability Float64
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main"
+               OpSource GLSL 450
+               OpMemberDecorate %st 0 Offset 0
+               OpMemberDecorate %st 1 Offset 8
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %S 1 Offset 12
+               OpDecorate %S Block
+               OpDecorate %B DescriptorSet 0
+               OpDecorate %B Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+     %double = OpTypeFloat 64
+         %st = OpTypeStruct %double %float
+          %S = OpTypeStruct %st %float
+%_ptr_Uniform_S = OpTypePointer Uniform %S
+          %B = OpVariable %_ptr_Uniform_S Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true);
+  EXPECT_EQ(SPV_SUCCESS,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(), Eq(""));
+}
+
+TEST_F(
+    ValidateExplicitLayout,
+    BlockLayoutPermitsStraddlingVectorWithScalarLayoutOverrideRelaxBlockLayoutGood) {
+  // Same as previous, but set relaxed block layout first.  Scalar layout always
+  // wins.
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main"
+               OpSource GLSL 450
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %S 1 Offset 4
+               OpDecorate %S Block
+               OpDecorate %B DescriptorSet 0
+               OpDecorate %B Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+       %vec4 = OpTypeVector %float 4
+          %S = OpTypeStruct %float %vec4
+%_ptr_Uniform_S = OpTypePointer Uniform %S
+          %B = OpVariable %_ptr_Uniform_S Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  spvValidatorOptionsSetRelaxBlockLayout(getValidatorOptions(), true);
+  spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true);
+  EXPECT_EQ(SPV_SUCCESS,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(), Eq(""));
+}
+
+TEST_F(
+    ValidateExplicitLayout,
+    BlockLayoutPermitsStraddlingVectorWithRelaxedLayoutOverridenByScalarBlockLayoutGood) {
+  // Same as previous, but set scalar block layout first.  Scalar layout always
+  // wins.
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main"
+               OpSource GLSL 450
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %S 1 Offset 4
+               OpDecorate %S Block
+               OpDecorate %B DescriptorSet 0
+               OpDecorate %B Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+       %vec4 = OpTypeVector %float 4
+          %S = OpTypeStruct %float %vec4
+%_ptr_Uniform_S = OpTypePointer Uniform %S
+          %B = OpVariable %_ptr_Uniform_S Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true);
+  spvValidatorOptionsSetRelaxBlockLayout(getValidatorOptions(), true);
+  EXPECT_EQ(SPV_SUCCESS,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(), Eq(""));
+}
+
+TEST_F(ValidateExplicitLayout, BufferBlock16bitStandardStorageBufferLayout) {
+  std::string spirv = R"(
+             OpCapability Shader
+             OpCapability StorageUniform16
+             OpExtension "SPV_KHR_16bit_storage"
+             OpMemoryModel Logical GLSL450
+             OpEntryPoint GLCompute %main "main"
+             OpExecutionMode %main LocalSize 1 1 1
+             OpDecorate %f32arr ArrayStride 4
+             OpDecorate %f16arr ArrayStride 2
+             OpMemberDecorate %SSBO32 0 Offset 0
+             OpMemberDecorate %SSBO16 0 Offset 0
+             OpDecorate %SSBO32 BufferBlock
+             OpDecorate %SSBO16 BufferBlock
+             OpDecorate %varSSBO32 DescriptorSet 0
+             OpDecorate %varSSBO32 Binding 0
+             OpDecorate %varSSBO16 DescriptorSet 0
+             OpDecorate %varSSBO16 Binding 1
+     %void = OpTypeVoid
+    %voidf = OpTypeFunction %void
+      %u32 = OpTypeInt 32 0
+      %i32 = OpTypeInt 32 1
+      %f32 = OpTypeFloat 32
+    %uvec3 = OpTypeVector %u32 3
+ %c_i32_32 = OpConstant %i32 32
+%c_i32_128 = OpConstant %i32 128
+   %f32arr = OpTypeArray %f32 %c_i32_128
+      %f16 = OpTypeFloat 16
+   %f16arr = OpTypeArray %f16 %c_i32_128
+   %SSBO32 = OpTypeStruct %f32arr
+   %SSBO16 = OpTypeStruct %f16arr
+%_ptr_Uniform_SSBO32 = OpTypePointer Uniform %SSBO32
+ %varSSBO32 = OpVariable %_ptr_Uniform_SSBO32 Uniform
+%_ptr_Uniform_SSBO16 = OpTypePointer Uniform %SSBO16
+ %varSSBO16 = OpVariable %_ptr_Uniform_SSBO16 Uniform
+     %main = OpFunction %void None %voidf
+    %label = OpLabel
+             OpReturn
+             OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateExplicitLayout, BlockArrayExtendedAlignmentGood) {
+  // For uniform buffer, Array base alignment is 16, and ArrayStride
+  // must be a multiple of 16.
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main"
+               OpSource GLSL 450
+               OpDecorate %_arr_float_uint_2 ArrayStride 16
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %S 1 Offset 16
+               OpDecorate %S Block
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v2float = OpTypeVector %float 2
+       %uint = OpTypeInt 32 0
+     %uint_2 = OpConstant %uint 2
+%_arr_float_uint_2 = OpTypeArray %float %uint_2
+          %S = OpTypeStruct %v2float %_arr_float_uint_2
+%_ptr_PushConstant_S = OpTypePointer PushConstant %S
+          %u = OpVariable %_ptr_PushConstant_S PushConstant
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0))
+      << getDiagnosticString();
+}
+
+TEST_F(ValidateExplicitLayout, BlockArrayBaseAlignmentBad) {
+  // For uniform buffer, Array base alignment is 16.
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main"
+               OpSource GLSL 450
+               OpDecorate %_arr_float_uint_2 ArrayStride 16
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %S 1 Offset 8
+               OpDecorate %S Block
+               OpDecorate %u DescriptorSet 0
+               OpDecorate %u Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v2float = OpTypeVector %float 2
+       %uint = OpTypeInt 32 0
+     %uint_2 = OpConstant %uint 2
+%_arr_float_uint_2 = OpTypeArray %float %uint_2
+          %S = OpTypeStruct %v2float %_arr_float_uint_2
+%_ptr_Uniform_S = OpTypePointer Uniform %S
+          %u = OpVariable %_ptr_Uniform_S Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Structure member 1 at offset 8 is not aligned to 16"));
+}
+
+TEST_F(ValidateExplicitLayout,
+       BlockArrayBaseAlignmentWithRelaxedLayoutStillBad) {
+  // For uniform buffer, Array base alignment is 16, and ArrayStride
+  // must be a multiple of 16.  This case uses relaxed block layout.  Relaxed
+  // layout only relaxes rules for vector alignment, not array alignment.
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main"
+               OpSource GLSL 450
+               OpDecorate %_arr_float_uint_2 ArrayStride 16
+               OpDecorate %u DescriptorSet 0
+               OpDecorate %u Binding 0
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %S 1 Offset 8
+               OpDecorate %S Block
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v2float = OpTypeVector %float 2
+       %uint = OpTypeInt 32 0
+     %uint_2 = OpConstant %uint 2
+%_arr_float_uint_2 = OpTypeArray %float %uint_2
+          %S = OpTypeStruct %v2float %_arr_float_uint_2
+%_ptr_Uniform_S = OpTypePointer Uniform %S
+          %u = OpVariable %_ptr_Uniform_S Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  spvValidatorOptionsSetRelaxBlockLayout(getValidatorOptions(), true);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Structure member 1 at offset 8 is not aligned to 16"));
+}
+
+TEST_F(ValidateExplicitLayout, BlockArrayBaseAlignmentWithVulkan1_1StillBad) {
+  // Same as previous test, but with Vulkan 1.1, which includes
+  // VK_KHR_relaxed_block_layout in core.
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main"
+               OpSource GLSL 450
+               OpDecorate %_arr_float_uint_2 ArrayStride 16
+               OpDecorate %u DescriptorSet 0
+               OpDecorate %u Binding 0
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %S 1 Offset 8
+               OpDecorate %S Block
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v2float = OpTypeVector %float 2
+       %uint = OpTypeInt 32 0
+     %uint_2 = OpConstant %uint 2
+%_arr_float_uint_2 = OpTypeArray %float %uint_2
+          %S = OpTypeStruct %v2float %_arr_float_uint_2
+%_ptr_Uniform_S = OpTypePointer Uniform %S
+          %u = OpVariable %_ptr_Uniform_S Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Structure member 1 at offset 8 is not aligned to 16"));
+}
+
+TEST_F(ValidateExplicitLayout,
+       BlockArrayBaseAlignmentWithBlockStandardLayoutGood) {
+  // Same as previous test, but with VK_KHR_uniform_buffer_standard_layout
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main"
+               OpSource GLSL 450
+               OpDecorate %_arr_float_uint_2 ArrayStride 16
+               OpDecorate %u DescriptorSet 0
+               OpDecorate %u Binding 0
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %S 1 Offset 8
+               OpDecorate %S Block
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v2float = OpTypeVector %float 2
+       %uint = OpTypeInt 32 0
+     %uint_2 = OpConstant %uint 2
+%_arr_float_uint_2 = OpTypeArray %float %uint_2
+          %S = OpTypeStruct %v2float %_arr_float_uint_2
+%_ptr_Uniform_S = OpTypePointer Uniform %S
+          %u = OpVariable %_ptr_Uniform_S Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  spvValidatorOptionsSetUniformBufferStandardLayout(getValidatorOptions(),
+                                                    true);
+  EXPECT_EQ(SPV_SUCCESS,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(), Eq(""));
+}
+
+TEST_F(ValidateExplicitLayout, PushConstantArrayBaseAlignmentGood) {
+  // Tests https://github.com/KhronosGroup/SPIRV-Tools/issues/1664
+  // From GLSL vertex shader:
+  // #version 450
+  // layout(push_constant) uniform S { vec2 v; float arr[2]; } u;
+  // void main() { }
+
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main"
+               OpSource GLSL 450
+               OpDecorate %_arr_float_uint_2 ArrayStride 4
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %S 1 Offset 8
+               OpDecorate %S Block
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v2float = OpTypeVector %float 2
+       %uint = OpTypeInt 32 0
+     %uint_2 = OpConstant %uint 2
+%_arr_float_uint_2 = OpTypeArray %float %uint_2
+          %S = OpTypeStruct %v2float %_arr_float_uint_2
+%_ptr_PushConstant_S = OpTypePointer PushConstant %S
+          %u = OpVariable %_ptr_PushConstant_S PushConstant
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0))
+      << getDiagnosticString();
+}
+
+TEST_F(ValidateExplicitLayout, PushConstantArrayBadAlignmentBad) {
+  // Like the previous test, but with offset 7 instead of 8.
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main"
+               OpSource GLSL 450
+               OpDecorate %_arr_float_uint_2 ArrayStride 4
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %S 1 Offset 7
+               OpDecorate %S Block
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v2float = OpTypeVector %float 2
+       %uint = OpTypeInt 32 0
+     %uint_2 = OpConstant %uint 2
+%_arr_float_uint_2 = OpTypeArray %float %uint_2
+          %S = OpTypeStruct %v2float %_arr_float_uint_2
+%_ptr_PushConstant_S = OpTypePointer PushConstant %S
+          %u = OpVariable %_ptr_PushConstant_S PushConstant
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Structure member 1 at offset 7 is not aligned to 4"));
+}
+
+TEST_F(ValidateExplicitLayout,
+       PushConstantLayoutPermitsTightVec3ScalarPackingGood) {
+  // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1666
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main"
+               OpSource GLSL 450
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %S 1 Offset 12
+               OpDecorate %S Block
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+          %S = OpTypeStruct %v3float %float
+%_ptr_PushConstant_S = OpTypePointer PushConstant %S
+          %B = OpVariable %_ptr_PushConstant_S PushConstant
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0))
+      << getDiagnosticString();
+}
+
+TEST_F(ValidateExplicitLayout,
+       PushConstantLayoutForbidsTightScalarVec3PackingBad) {
+  // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1666
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main"
+               OpSource GLSL 450
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %S 1 Offset 4
+               OpDecorate %S Block
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+          %S = OpTypeStruct %float %v3float
+%_ptr_Uniform_S = OpTypePointer PushConstant %S
+          %B = OpVariable %_ptr_Uniform_S PushConstant
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Structure member 1 at offset 4 is not aligned to 16"));
+}
+
+TEST_F(ValidateExplicitLayout,
+       StorageBufferStorageClassArrayBaseAlignmentGood) {
+  // Spot check buffer rules when using StorageBuffer storage class with Block
+  // decoration.
+  std::string spirv = R"(
+               OpCapability Shader
+               OpExtension "SPV_KHR_storage_buffer_storage_class"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main"
+               OpSource GLSL 450
+               OpDecorate %_arr_float_uint_2 ArrayStride 4
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %S 1 Offset 8
+               OpDecorate %S Block
+               OpDecorate %u DescriptorSet 0
+               OpDecorate %u Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v2float = OpTypeVector %float 2
+       %uint = OpTypeInt 32 0
+     %uint_2 = OpConstant %uint 2
+%_arr_float_uint_2 = OpTypeArray %float %uint_2
+          %S = OpTypeStruct %v2float %_arr_float_uint_2
+%_ptr_Uniform_S = OpTypePointer StorageBuffer %S
+          %u = OpVariable %_ptr_Uniform_S StorageBuffer
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0))
+      << getDiagnosticString();
+}
+
+TEST_F(ValidateExplicitLayout, StorageBufferStorageClassArrayBadAlignmentBad) {
+  // Like the previous test, but with offset 7.
+  std::string spirv = R"(
+               OpCapability Shader
+               OpExtension "SPV_KHR_storage_buffer_storage_class"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main"
+               OpSource GLSL 450
+               OpDecorate %_arr_float_uint_2 ArrayStride 4
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %S 1 Offset 7
+               OpDecorate %S Block
+               OpDecorate %u DescriptorSet 0
+               OpDecorate %u Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v2float = OpTypeVector %float 2
+       %uint = OpTypeInt 32 0
+     %uint_2 = OpConstant %uint 2
+%_arr_float_uint_2 = OpTypeArray %float %uint_2
+          %S = OpTypeStruct %v2float %_arr_float_uint_2
+%_ptr_Uniform_S = OpTypePointer StorageBuffer %S
+          %u = OpVariable %_ptr_Uniform_S StorageBuffer
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Structure member 1 at offset 7 is not aligned to 4"));
+}
+
+TEST_F(ValidateExplicitLayout, BufferBlockStandardStorageBufferLayout) {
+  std::string spirv = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 430
+               OpMemberDecorate %F 0 Offset 0
+               OpMemberDecorate %F 1 Offset 8
+               OpDecorate %_arr_float_uint_2 ArrayStride 4
+               OpDecorate %_arr_mat3v3float_uint_2 ArrayStride 48
+               OpMemberDecorate %O 0 Offset 0
+               OpMemberDecorate %O 1 Offset 16
+               OpMemberDecorate %O 2 Offset 24
+               OpMemberDecorate %O 3 Offset 32
+               OpMemberDecorate %O 4 ColMajor
+               OpMemberDecorate %O 4 Offset 48
+               OpMemberDecorate %O 4 MatrixStride 16
+               OpDecorate %_arr_O_uint_2 ArrayStride 144
+               OpMemberDecorate %Output 0 Offset 0
+               OpMemberDecorate %Output 1 Offset 8
+               OpMemberDecorate %Output 2 Offset 16
+               OpMemberDecorate %Output 3 Offset 32
+               OpMemberDecorate %Output 4 Offset 48
+               OpMemberDecorate %Output 5 Offset 52
+               OpMemberDecorate %Output 6 ColMajor
+               OpMemberDecorate %Output 6 Offset 64
+               OpMemberDecorate %Output 6 MatrixStride 16
+               OpMemberDecorate %Output 7 Offset 96
+               OpDecorate %Output BufferBlock
+               OpDecorate %dataOutput DescriptorSet 0
+               OpDecorate %dataOutput Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v2float = OpTypeVector %float 2
+    %v3float = OpTypeVector %float 3
+        %int = OpTypeInt 32 1
+       %uint = OpTypeInt 32 0
+     %v2uint = OpTypeVector %uint 2
+          %F = OpTypeStruct %int %v2uint
+     %uint_2 = OpConstant %uint 2
+%_arr_float_uint_2 = OpTypeArray %float %uint_2
+%mat2v3float = OpTypeMatrix %v3float 2
+     %v3uint = OpTypeVector %uint 3
+%mat3v3float = OpTypeMatrix %v3float 3
+%_arr_mat3v3float_uint_2 = OpTypeArray %mat3v3float %uint_2
+          %O = OpTypeStruct %v3uint %v2float %_arr_float_uint_2 %v2float %_arr_mat3v3float_uint_2
+%_arr_O_uint_2 = OpTypeArray %O %uint_2
+     %Output = OpTypeStruct %float %v2float %v3float %F %float %_arr_float_uint_2 %mat2v3float %_arr_O_uint_2
+%_ptr_Uniform_Output = OpTypePointer Uniform %Output
+ %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateExplicitLayout,
+       StorageBufferLayoutPermitsTightVec3ScalarPackingGood) {
+  // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1666
+  std::string spirv = R"(
+               OpCapability Shader
+               OpExtension "SPV_KHR_storage_buffer_storage_class"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main"
+               OpSource GLSL 450
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %S 1 Offset 12
+               OpDecorate %S Block
+               OpDecorate %B DescriptorSet 0
+               OpDecorate %B Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+          %S = OpTypeStruct %v3float %float
+%_ptr_StorageBuffer_S = OpTypePointer StorageBuffer %S
+          %B = OpVariable %_ptr_StorageBuffer_S StorageBuffer
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0))
+      << getDiagnosticString();
+}
+
+TEST_F(ValidateExplicitLayout,
+       StorageBufferLayoutForbidsTightScalarVec3PackingBad) {
+  // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1666
+  std::string spirv = R"(
+               OpCapability Shader
+               OpExtension "SPV_KHR_storage_buffer_storage_class"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main"
+               OpSource GLSL 450
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %S 1 Offset 4
+               OpDecorate %S Block
+               OpDecorate %B DescriptorSet 0
+               OpDecorate %B Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+          %S = OpTypeStruct %float %v3float
+%_ptr_StorageBuffer_S = OpTypePointer StorageBuffer %S
+          %B = OpVariable %_ptr_StorageBuffer_S StorageBuffer
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Structure member 1 at offset 4 is not aligned to 16"));
+}
+
+TEST_F(ValidateExplicitLayout,
+       BlockStandardUniformBufferLayoutIncorrectOffset0Bad) {
+  std::string spirv = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 430
+               OpMemberDecorate %F 0 Offset 0
+               OpMemberDecorate %F 1 Offset 8
+               OpDecorate %_arr_float_uint_2 ArrayStride 16
+               OpDecorate %_arr_mat3v3float_uint_2 ArrayStride 48
+               OpMemberDecorate %O 0 Offset 0
+               OpMemberDecorate %O 1 Offset 16
+               OpMemberDecorate %O 2 Offset 24
+               OpMemberDecorate %O 3 Offset 33
+               OpMemberDecorate %O 4 ColMajor
+               OpMemberDecorate %O 4 Offset 80
+               OpMemberDecorate %O 4 MatrixStride 16
+               OpDecorate %_arr_O_uint_2 ArrayStride 176
+               OpMemberDecorate %Output 0 Offset 0
+               OpMemberDecorate %Output 1 Offset 8
+               OpMemberDecorate %Output 2 Offset 16
+               OpMemberDecorate %Output 3 Offset 32
+               OpMemberDecorate %Output 4 Offset 48
+               OpMemberDecorate %Output 5 Offset 64
+               OpMemberDecorate %Output 6 ColMajor
+               OpMemberDecorate %Output 6 Offset 96
+               OpMemberDecorate %Output 6 MatrixStride 16
+               OpMemberDecorate %Output 7 Offset 128
+               OpDecorate %Output Block
+               OpDecorate %dataOutput DescriptorSet 0
+               OpDecorate %dataOutput Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v2float = OpTypeVector %float 2
+    %v3float = OpTypeVector %float 3
+        %int = OpTypeInt 32 1
+       %uint = OpTypeInt 32 0
+     %v2uint = OpTypeVector %uint 2
+          %F = OpTypeStruct %int %v2uint
+     %uint_2 = OpConstant %uint 2
+%_arr_float_uint_2 = OpTypeArray %float %uint_2
+%mat2v3float = OpTypeMatrix %v3float 2
+     %v3uint = OpTypeVector %uint 3
+%mat3v3float = OpTypeMatrix %v3float 3
+%_arr_mat3v3float_uint_2 = OpTypeArray %mat3v3float %uint_2
+          %O = OpTypeStruct %v3uint %v2float %_arr_float_uint_2 %v2float %_arr_mat3v3float_uint_2
+%_arr_O_uint_2 = OpTypeArray %O %uint_2
+     %Output = OpTypeStruct %float %v2float %v3float %F %float %_arr_float_uint_2 %mat2v3float %_arr_O_uint_2
+%_ptr_Uniform_Output = OpTypePointer Uniform %Output
+ %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Structure member 2 at offset 24 is not aligned to 16"));
+}
+
+TEST_F(ValidateExplicitLayout,
+       BlockStandardUniformBufferLayoutIncorrectOffset1Bad) {
+  std::string spirv = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 430
+               OpMemberDecorate %F 0 Offset 0
+               OpMemberDecorate %F 1 Offset 8
+               OpDecorate %_arr_float_uint_2 ArrayStride 16
+               OpDecorate %_arr_mat3v3float_uint_2 ArrayStride 48
+               OpMemberDecorate %O 0 Offset 0
+               OpMemberDecorate %O 1 Offset 16
+               OpMemberDecorate %O 2 Offset 32
+               OpMemberDecorate %O 3 Offset 64
+               OpMemberDecorate %O 4 ColMajor
+               OpMemberDecorate %O 4 Offset 80
+               OpMemberDecorate %O 4 MatrixStride 16
+               OpDecorate %_arr_O_uint_2 ArrayStride 176
+               OpMemberDecorate %Output 0 Offset 0
+               OpMemberDecorate %Output 1 Offset 8
+               OpMemberDecorate %Output 2 Offset 16
+               OpMemberDecorate %Output 3 Offset 32
+               OpMemberDecorate %Output 4 Offset 48
+               OpMemberDecorate %Output 5 Offset 71
+               OpMemberDecorate %Output 6 ColMajor
+               OpMemberDecorate %Output 6 Offset 96
+               OpMemberDecorate %Output 6 MatrixStride 16
+               OpMemberDecorate %Output 7 Offset 128
+               OpDecorate %Output Block
+               OpDecorate %dataOutput DescriptorSet 0
+               OpDecorate %dataOutput Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v2float = OpTypeVector %float 2
+    %v3float = OpTypeVector %float 3
+        %int = OpTypeInt 32 1
+       %uint = OpTypeInt 32 0
+     %v2uint = OpTypeVector %uint 2
+          %F = OpTypeStruct %int %v2uint
+     %uint_2 = OpConstant %uint 2
+%_arr_float_uint_2 = OpTypeArray %float %uint_2
+%mat2v3float = OpTypeMatrix %v3float 2
+     %v3uint = OpTypeVector %uint 3
+%mat3v3float = OpTypeMatrix %v3float 3
+%_arr_mat3v3float_uint_2 = OpTypeArray %mat3v3float %uint_2
+          %O = OpTypeStruct %v3uint %v2float %_arr_float_uint_2 %v2float %_arr_mat3v3float_uint_2
+%_arr_O_uint_2 = OpTypeArray %O %uint_2
+     %Output = OpTypeStruct %float %v2float %v3float %F %float %_arr_float_uint_2 %mat2v3float %_arr_O_uint_2
+%_ptr_Uniform_Output = OpTypePointer Uniform %Output
+ %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Structure member 5 at offset 71 is not aligned to 16"));
+}
+
+TEST_F(ValidateExplicitLayout,
+       BlockUniformBufferLayoutIncorrectArrayStrideBad) {
+  std::string spirv = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 430
+               OpMemberDecorate %F 0 Offset 0
+               OpMemberDecorate %F 1 Offset 8
+               OpDecorate %_arr_float_uint_2 ArrayStride 16
+               OpDecorate %_arr_mat3v3float_uint_2 ArrayStride 49
+               OpMemberDecorate %O 0 Offset 0
+               OpMemberDecorate %O 1 Offset 16
+               OpMemberDecorate %O 2 Offset 32
+               OpMemberDecorate %O 3 Offset 64
+               OpMemberDecorate %O 4 ColMajor
+               OpMemberDecorate %O 4 Offset 80
+               OpMemberDecorate %O 4 MatrixStride 16
+               OpDecorate %_arr_O_uint_2 ArrayStride 177
+               OpMemberDecorate %Output 0 Offset 0
+               OpMemberDecorate %Output 1 Offset 8
+               OpMemberDecorate %Output 2 Offset 16
+               OpMemberDecorate %Output 3 Offset 32
+               OpMemberDecorate %Output 4 Offset 48
+               OpMemberDecorate %Output 5 Offset 64
+               OpMemberDecorate %Output 6 ColMajor
+               OpMemberDecorate %Output 6 Offset 96
+               OpMemberDecorate %Output 6 MatrixStride 16
+               OpMemberDecorate %Output 7 Offset 128
+               OpDecorate %Output Block
+               OpDecorate %dataOutput DescriptorSet 0
+               OpDecorate %dataOutput Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v2float = OpTypeVector %float 2
+    %v3float = OpTypeVector %float 3
+        %int = OpTypeInt 32 1
+       %uint = OpTypeInt 32 0
+     %v2uint = OpTypeVector %uint 2
+          %F = OpTypeStruct %int %v2uint
+     %uint_2 = OpConstant %uint 2
+%_arr_float_uint_2 = OpTypeArray %float %uint_2
+%mat2v3float = OpTypeMatrix %v3float 2
+     %v3uint = OpTypeVector %uint 3
+%mat3v3float = OpTypeMatrix %v3float 3
+%_arr_mat3v3float_uint_2 = OpTypeArray %mat3v3float %uint_2
+          %O = OpTypeStruct %v3uint %v2float %_arr_float_uint_2 %v2float %_arr_mat3v3float_uint_2
+%_arr_O_uint_2 = OpTypeArray %O %uint_2
+     %Output = OpTypeStruct %float %v2float %v3float %F %float %_arr_float_uint_2 %mat2v3float %_arr_O_uint_2
+%_ptr_Uniform_Output = OpTypePointer Uniform %Output
+ %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Array stride 177 must satisfy alignment 16"));
+}
+
+TEST_F(ValidateExplicitLayout,
+       BufferBlockStandardStorageBufferLayoutImproperStraddleBad) {
+  std::string spirv = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 430
+               OpMemberDecorate %Output 0 Offset 0
+               OpMemberDecorate %Output 1 Offset 8
+               OpDecorate %Output BufferBlock
+               OpDecorate %dataOutput DescriptorSet 0
+               OpDecorate %dataOutput Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+     %Output = OpTypeStruct %float %v3float
+%_ptr_Uniform_Output = OpTypePointer Uniform %Output
+ %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Structure member 1 at offset 8 is not aligned to 16"));
+}
+
+TEST_F(ValidateExplicitLayout,
+       BlockUniformBufferLayoutOffsetInsideArrayPaddingBad) {
+  // In this case the 2nd member fits entirely within the padding.
+  std::string spirv = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 430
+               OpDecorate %_arr_float_uint_2 ArrayStride 16
+               OpMemberDecorate %Output 0 Offset 0
+               OpMemberDecorate %Output 1 Offset 20
+               OpDecorate %Output Block
+               OpDecorate %dataOutput DescriptorSet 0
+               OpDecorate %dataOutput Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+       %uint = OpTypeInt 32 0
+     %v2uint = OpTypeVector %uint 2
+     %uint_2 = OpConstant %uint 2
+%_arr_float_uint_2 = OpTypeArray %float %uint_2
+     %Output = OpTypeStruct %_arr_float_uint_2 %float
+%_ptr_Uniform_Output = OpTypePointer Uniform %Output
+ %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Structure member 1 at offset 20 overlaps previous "
+                        "member ending at offset 31"));
+}
+
+TEST_F(ValidateExplicitLayout,
+       BlockUniformBufferLayoutOffsetInsideStructPaddingBad) {
+  // In this case the 2nd member fits entirely within the padding.
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %1 "main"
+               OpExecutionMode %1 LocalSize 1 1 1
+               OpMemberDecorate %_struct_6 0 Offset 0
+               OpMemberDecorate %_struct_2 0 Offset 0
+               OpMemberDecorate %_struct_2 1 Offset 4
+               OpDecorate %_struct_2 Block
+               OpDecorate %8 DescriptorSet 0
+               OpDecorate %8 Binding 0
+       %void = OpTypeVoid
+          %4 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+  %_struct_6 = OpTypeStruct %float
+  %_struct_2 = OpTypeStruct %_struct_6 %float
+%_ptr_Uniform__struct_2 = OpTypePointer Uniform %_struct_2
+          %8 = OpVariable %_ptr_Uniform__struct_2 Uniform
+          %1 = OpFunction %void None %4
+          %9 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Structure member 1 at offset 4 overlaps previous "
+                        "member ending at offset 15"));
+}
+
+TEST_F(ValidateExplicitLayout, BlockLayoutOffsetOutOfOrderGoodUniversal1_0) {
+  std::string spirv = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpMemberDecorate %Outer 0 Offset 4
+               OpMemberDecorate %Outer 1 Offset 0
+               OpDecorate %Outer Block
+               OpDecorate %O DescriptorSet 0
+               OpDecorate %O Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+      %Outer = OpTypeStruct %uint %uint
+%_ptr_Uniform_Outer = OpTypePointer Uniform %Outer
+          %O = OpVariable %_ptr_Uniform_Outer Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS,
+            ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_0));
+}
+
+TEST_F(ValidateExplicitLayout, BlockLayoutOffsetOutOfOrderGoodOpenGL4_5) {
+  std::string spirv = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpMemberDecorate %Outer 0 Offset 4
+               OpMemberDecorate %Outer 1 Offset 0
+               OpDecorate %Outer Block
+               OpDecorate %O DescriptorSet 0
+               OpDecorate %O Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+      %Outer = OpTypeStruct %uint %uint
+%_ptr_Uniform_Outer = OpTypePointer Uniform %Outer
+          %O = OpVariable %_ptr_Uniform_Outer Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS,
+            ValidateAndRetrieveValidationState(SPV_ENV_OPENGL_4_5));
+}
+
+TEST_F(ValidateExplicitLayout, BlockLayoutOffsetOutOfOrderGoodVulkan1_1) {
+  std::string spirv = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpMemberDecorate %Outer 0 Offset 4
+               OpMemberDecorate %Outer 1 Offset 0
+               OpDecorate %Outer Block
+               OpDecorate %O DescriptorSet 0
+               OpDecorate %O Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+      %Outer = OpTypeStruct %uint %uint
+%_ptr_Uniform_Outer = OpTypePointer Uniform %Outer
+          %O = OpVariable %_ptr_Uniform_Outer Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1))
+      << getDiagnosticString();
+  EXPECT_THAT(getDiagnosticString(), Eq(""));
+}
+
+TEST_F(ValidateExplicitLayout, BlockLayoutOffsetOverlapBad) {
+  std::string spirv = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpMemberDecorate %Outer 0 Offset 0
+               OpMemberDecorate %Outer 1 Offset 16
+               OpMemberDecorate %Inner 0 Offset 0
+               OpMemberDecorate %Inner 1 Offset 16
+               OpDecorate %Outer Block
+               OpDecorate %O DescriptorSet 0
+               OpDecorate %O Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+      %Inner = OpTypeStruct %uint %uint
+      %Outer = OpTypeStruct %Inner %uint
+%_ptr_Uniform_Outer = OpTypePointer Uniform %Outer
+          %O = OpVariable %_ptr_Uniform_Outer Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Structure member 1 at offset 16 overlaps previous "
+                        "member ending at offset 31"));
+}
+
+TEST_F(ValidateExplicitLayout, BufferBlockEmptyStruct) {
+  std::string spirv = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 430
+               OpMemberDecorate %Output 0 Offset 0
+               OpDecorate %Output BufferBlock
+               OpDecorate %dataOutput DescriptorSet 0
+               OpDecorate %dataOutput Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+          %S = OpTypeStruct
+     %Output = OpTypeStruct %S
+%_ptr_Uniform_Output = OpTypePointer Uniform %Output
+ %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateExplicitLayout, RowMajorMatrixTightPackingGood) {
+  // Row major matrix rule:
+  //     A row-major matrix of C columns has a base alignment equal to
+  //     the base alignment of a vector of C matrix components.
+  // Note: The "matrix component" is the scalar element type.
+
+  // The matrix has 3 columns and 2 rows (C=3, R=2).
+  // So the base alignment of b is the same as a vector of 3 floats, which is 16
+  // bytes. The matrix consists of two of these, and therefore occupies 2 x 16
+  // bytes, or 32 bytes.
+  //
+  // So the offsets can be:
+  // a -> 0
+  // b -> 16
+  // c -> 48
+  // d -> 60 ; d fits at bytes 12-15 after offset of c. Tight (vec3;float)
+  // packing
+
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %1 "main"
+               OpSource GLSL 450
+               OpMemberDecorate %_struct_2 0 Offset 0
+               OpMemberDecorate %_struct_2 1 RowMajor
+               OpMemberDecorate %_struct_2 1 Offset 16
+               OpMemberDecorate %_struct_2 1 MatrixStride 16
+               OpMemberDecorate %_struct_2 2 Offset 48
+               OpMemberDecorate %_struct_2 3 Offset 60
+               OpDecorate %_struct_2 Block
+               OpDecorate %3 DescriptorSet 0
+               OpDecorate %3 Binding 0
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+    %v2float = OpTypeVector %float 2
+%mat3v2float = OpTypeMatrix %v2float 3
+    %v3float = OpTypeVector %float 3
+  %_struct_2 = OpTypeStruct %v4float %mat3v2float %v3float %float
+%_ptr_Uniform__struct_2 = OpTypePointer Uniform %_struct_2
+          %3 = OpVariable %_ptr_Uniform__struct_2 Uniform
+          %1 = OpFunction %void None %5
+         %12 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0))
+      << getDiagnosticString();
+}
+
+TEST_F(ValidateExplicitLayout, ArrayArrayRowMajorMatrixTightPackingGood) {
+  // Like the previous case, but we have an array of arrays of matrices.
+  // The RowMajor decoration goes on the struct member (surprisingly).
+
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %1 "main"
+               OpSource GLSL 450
+               OpMemberDecorate %_struct_2 0 Offset 0
+               OpMemberDecorate %_struct_2 1 RowMajor
+               OpMemberDecorate %_struct_2 1 Offset 16
+               OpMemberDecorate %_struct_2 1 MatrixStride 16
+               OpMemberDecorate %_struct_2 2 Offset 80
+               OpMemberDecorate %_struct_2 3 Offset 92
+               OpDecorate %arr_mat ArrayStride 32
+               OpDecorate %arr_arr_mat ArrayStride 32
+               OpDecorate %_struct_2 Block
+               OpDecorate %3 DescriptorSet 0
+               OpDecorate %3 Binding 0
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+    %v2float = OpTypeVector %float 2
+%mat3v2float = OpTypeMatrix %v2float 3
+%uint        = OpTypeInt 32 0
+%uint_1      = OpConstant %uint 1
+%uint_2      = OpConstant %uint 2
+    %arr_mat = OpTypeArray %mat3v2float %uint_1
+%arr_arr_mat = OpTypeArray %arr_mat %uint_2
+    %v3float = OpTypeVector %float 3
+  %_struct_2 = OpTypeStruct %v4float %arr_arr_mat %v3float %float
+%_ptr_Uniform__struct_2 = OpTypePointer Uniform %_struct_2
+          %3 = OpVariable %_ptr_Uniform__struct_2 Uniform
+          %1 = OpFunction %void None %5
+         %12 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0))
+      << getDiagnosticString();
+}
+
+TEST_F(ValidateExplicitLayout, ArrayArrayRowMajorMatrixNextMemberOverlapsBad) {
+  // Like the previous case, but the offset of member 2 overlaps the matrix.
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %1 "main"
+               OpSource GLSL 450
+               OpMemberDecorate %_struct_2 0 Offset 0
+               OpMemberDecorate %_struct_2 1 RowMajor
+               OpMemberDecorate %_struct_2 1 Offset 16
+               OpMemberDecorate %_struct_2 1 MatrixStride 16
+               OpMemberDecorate %_struct_2 2 Offset 64
+               OpMemberDecorate %_struct_2 3 Offset 92
+               OpDecorate %arr_mat ArrayStride 32
+               OpDecorate %arr_arr_mat ArrayStride 32
+               OpDecorate %_struct_2 Block
+               OpDecorate %3 DescriptorSet 0
+               OpDecorate %3 Binding 0
+       %void = OpTypeVoid
+          %5 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v4float = OpTypeVector %float 4
+    %v2float = OpTypeVector %float 2
+%mat3v2float = OpTypeMatrix %v2float 3
+%uint        = OpTypeInt 32 0
+%uint_1      = OpConstant %uint 1
+%uint_2      = OpConstant %uint 2
+    %arr_mat = OpTypeArray %mat3v2float %uint_1
+%arr_arr_mat = OpTypeArray %arr_mat %uint_2
+    %v3float = OpTypeVector %float 3
+  %_struct_2 = OpTypeStruct %v4float %arr_arr_mat %v3float %float
+%_ptr_Uniform__struct_2 = OpTypePointer Uniform %_struct_2
+          %3 = OpVariable %_ptr_Uniform__struct_2 Uniform
+          %1 = OpFunction %void None %5
+         %12 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Structure member 2 at offset 64 overlaps previous "
+                        "member ending at offset 79"));
+}
+
+TEST_F(ValidateExplicitLayout, StorageBufferArraySizeCalculationPackGood) {
+  // Original GLSL
+
+  // #version 450
+  // layout (set=0,binding=0) buffer S {
+  //   uvec3 arr[2][2]; // first 3 elements are 16 bytes, last is 12
+  //   uint i;  // Can't have offset 60 = 3x16 + 12
+  // } B;
+  // void main() {}
+
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %1 "main"
+               OpDecorate %_arr_v3uint_uint_2 ArrayStride 16
+               OpDecorate %_arr__arr_v3uint_uint_2_uint_2 ArrayStride 32
+               OpMemberDecorate %_struct_4 0 Offset 0
+               OpMemberDecorate %_struct_4 1 Offset 64
+               OpDecorate %_struct_4 BufferBlock
+               OpDecorate %5 DescriptorSet 0
+               OpDecorate %5 Binding 0
+       %void = OpTypeVoid
+          %7 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %v3uint = OpTypeVector %uint 3
+     %uint_2 = OpConstant %uint 2
+%_arr_v3uint_uint_2 = OpTypeArray %v3uint %uint_2
+%_arr__arr_v3uint_uint_2_uint_2 = OpTypeArray %_arr_v3uint_uint_2 %uint_2
+  %_struct_4 = OpTypeStruct %_arr__arr_v3uint_uint_2_uint_2 %uint
+%_ptr_Uniform__struct_4 = OpTypePointer Uniform %_struct_4
+          %5 = OpVariable %_ptr_Uniform__struct_4 Uniform
+          %1 = OpFunction %void None %7
+         %12 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateExplicitLayout,
+       StorageBufferArraySizeCalculationPackGoodScalar) {
+  // Original GLSL
+
+  // #version 450
+  // layout (set=0,binding=0) buffer S {
+  //   uvec3 arr[2][2]; // first 3 elements are 16 bytes, last is 12
+  //   uint i;  // Can have offset 60 = 3x16 + 12
+  // } B;
+  // void main() {}
+
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %1 "main"
+               OpDecorate %_arr_v3uint_uint_2 ArrayStride 16
+               OpDecorate %_arr__arr_v3uint_uint_2_uint_2 ArrayStride 32
+               OpMemberDecorate %_struct_4 0 Offset 0
+               OpMemberDecorate %_struct_4 1 Offset 60
+               OpDecorate %_struct_4 BufferBlock
+               OpDecorate %5 DescriptorSet 0
+               OpDecorate %5 Binding 0
+       %void = OpTypeVoid
+          %7 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %v3uint = OpTypeVector %uint 3
+     %uint_2 = OpConstant %uint 2
+%_arr_v3uint_uint_2 = OpTypeArray %v3uint %uint_2
+%_arr__arr_v3uint_uint_2_uint_2 = OpTypeArray %_arr_v3uint_uint_2 %uint_2
+  %_struct_4 = OpTypeStruct %_arr__arr_v3uint_uint_2_uint_2 %uint
+%_ptr_Uniform__struct_4 = OpTypePointer Uniform %_struct_4
+          %5 = OpVariable %_ptr_Uniform__struct_4 Uniform
+          %1 = OpFunction %void None %7
+         %12 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  options_->scalar_block_layout = true;
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateExplicitLayout, StorageBufferArraySizeCalculationPackBad) {
+  // Like previous but, the offset of the second member is too small.
+
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %1 "main"
+               OpDecorate %_arr_v3uint_uint_2 ArrayStride 16
+               OpDecorate %_arr__arr_v3uint_uint_2_uint_2 ArrayStride 32
+               OpMemberDecorate %_struct_4 0 Offset 0
+               OpMemberDecorate %_struct_4 1 Offset 60
+               OpDecorate %_struct_4 BufferBlock
+               OpDecorate %5 DescriptorSet 0
+               OpDecorate %5 Binding 0
+       %void = OpTypeVoid
+          %7 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %v3uint = OpTypeVector %uint 3
+     %uint_2 = OpConstant %uint 2
+%_arr_v3uint_uint_2 = OpTypeArray %v3uint %uint_2
+%_arr__arr_v3uint_uint_2_uint_2 = OpTypeArray %_arr_v3uint_uint_2 %uint_2
+  %_struct_4 = OpTypeStruct %_arr__arr_v3uint_uint_2_uint_2 %uint
+%_ptr_Uniform__struct_4 = OpTypePointer Uniform %_struct_4
+          %5 = OpVariable %_ptr_Uniform__struct_4 Uniform
+          %1 = OpFunction %void None %7
+         %12 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Structure member 1 at offset 60 overlaps "
+                        "previous member ending at offset 63"));
+}
+
+TEST_F(ValidateExplicitLayout, UniformBufferArraySizeCalculationPackGood) {
+  // Like the corresponding buffer block case, but the array padding must
+  // count for the last element as well, and so the offset of the second
+  // member must be at least 64.
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %1 "main"
+               OpDecorate %_arr_v3uint_uint_2 ArrayStride 16
+               OpDecorate %_arr__arr_v3uint_uint_2_uint_2 ArrayStride 32
+               OpMemberDecorate %_struct_4 0 Offset 0
+               OpMemberDecorate %_struct_4 1 Offset 64
+               OpDecorate %_struct_4 Block
+               OpDecorate %5 DescriptorSet 0
+               OpDecorate %5 Binding 0
+       %void = OpTypeVoid
+          %7 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %v3uint = OpTypeVector %uint 3
+     %uint_2 = OpConstant %uint 2
+%_arr_v3uint_uint_2 = OpTypeArray %v3uint %uint_2
+%_arr__arr_v3uint_uint_2_uint_2 = OpTypeArray %_arr_v3uint_uint_2 %uint_2
+  %_struct_4 = OpTypeStruct %_arr__arr_v3uint_uint_2_uint_2 %uint
+%_ptr_Uniform__struct_4 = OpTypePointer Uniform %_struct_4
+          %5 = OpVariable %_ptr_Uniform__struct_4 Uniform
+          %1 = OpFunction %void None %7
+         %12 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_SUCCESS,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateExplicitLayout, UniformBufferArraySizeCalculationPackBad) {
+  // Like previous but, the offset of the second member is too small.
+
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %1 "main"
+               OpDecorate %_arr_v3uint_uint_2 ArrayStride 16
+               OpDecorate %_arr__arr_v3uint_uint_2_uint_2 ArrayStride 32
+               OpMemberDecorate %_struct_4 0 Offset 0
+               OpMemberDecorate %_struct_4 1 Offset 60
+               OpDecorate %_struct_4 Block
+               OpDecorate %5 DescriptorSet 0
+               OpDecorate %5 Binding 0
+       %void = OpTypeVoid
+          %7 = OpTypeFunction %void
+       %uint = OpTypeInt 32 0
+     %v3uint = OpTypeVector %uint 3
+     %uint_2 = OpConstant %uint 2
+%_arr_v3uint_uint_2 = OpTypeArray %v3uint %uint_2
+%_arr__arr_v3uint_uint_2_uint_2 = OpTypeArray %_arr_v3uint_uint_2 %uint_2
+  %_struct_4 = OpTypeStruct %_arr__arr_v3uint_uint_2_uint_2 %uint
+%_ptr_Uniform__struct_4 = OpTypePointer Uniform %_struct_4
+          %5 = OpVariable %_ptr_Uniform__struct_4 Uniform
+          %1 = OpFunction %void None %7
+         %12 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Structure member 1 at offset 60 overlaps previous "
+                        "member ending at offset 63"));
+}
+
+TEST_F(ValidateExplicitLayout, LayoutNotCheckedWhenSkipBlockLayout) {
+  // Checks that block layout is not verified in skipping block layout mode.
+  // Even for obviously wrong layout.
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main"
+               OpSource GLSL 450
+               OpMemberDecorate %S 0 Offset 3 ; wrong alignment
+               OpMemberDecorate %S 1 Offset 3 ; same offset as before!
+               OpDecorate %S Block
+               OpDecorate %B DescriptorSet 0
+               OpDecorate %B Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+          %S = OpTypeStruct %float %v3float
+%_ptr_Uniform_S = OpTypePointer Uniform %S
+          %B = OpVariable %_ptr_Uniform_S Uniform
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv);
+  spvValidatorOptionsSetSkipBlockLayout(getValidatorOptions(), true);
+  EXPECT_EQ(SPV_SUCCESS,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(), Eq(""));
+}
+
+TEST_F(ValidateExplicitLayout, RecurseThroughRuntimeArray) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %outer Block
+OpMemberDecorate %inner 0 Offset 0
+OpMemberDecorate %inner 1 Offset 1
+OpDecorate %runtime ArrayStride 16
+OpMemberDecorate %outer 0 Offset 0
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%int = OpTypeInt 32 0
+%inner = OpTypeStruct %int %int
+%runtime = OpTypeRuntimeArray %inner
+%outer = OpTypeStruct %runtime
+%outer_ptr = OpTypePointer StorageBuffer %outer
+%var = OpVariable %outer_ptr StorageBuffer
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Structure member 1 at offset 1 is not aligned to 4"));
+}
+
+TEST_F(ValidateExplicitLayout, InvalidStraddle) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpMemberDecorate %inner_struct 0 Offset 0
+OpMemberDecorate %inner_struct 1 Offset 4
+OpDecorate %outer_struct Block
+OpMemberDecorate %outer_struct 0 Offset 0
+OpMemberDecorate %outer_struct 1 Offset 8
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+%float2 = OpTypeVector %float 2
+%inner_struct = OpTypeStruct %float %float2
+%outer_struct = OpTypeStruct %float2 %inner_struct
+%ptr_ssbo_outer = OpTypePointer StorageBuffer %outer_struct
+%var = OpVariable %ptr_ssbo_outer StorageBuffer
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Vector has improper straddle due to offset 12"));
+}
+
+TEST_F(ValidateExplicitLayout, DescriptorArray) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Offset 0
+OpMemberDecorate %struct 1 Offset 1
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+%int = OpTypeInt 32 0
+%int_2 = OpConstant %int 2
+%float2 = OpTypeVector %float 2
+%struct = OpTypeStruct %float %float2
+%struct_array = OpTypeArray %struct %int_2
+%ptr_ssbo_array = OpTypePointer StorageBuffer %struct_array
+%var = OpVariable %ptr_ssbo_array StorageBuffer
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Structure member 1 at offset 1 is not aligned to 8"));
+}
+
+TEST_F(ValidateExplicitLayout, DescriptorRuntimeArray) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability RuntimeDescriptorArrayEXT
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpExtension "SPV_EXT_descriptor_indexing"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Offset 0
+OpMemberDecorate %struct 1 Offset 1
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+%int = OpTypeInt 32 0
+%float2 = OpTypeVector %float 2
+%struct = OpTypeStruct %float %float2
+%struct_array = OpTypeRuntimeArray %struct
+%ptr_ssbo_array = OpTypePointer StorageBuffer %struct_array
+%var = OpVariable %ptr_ssbo_array StorageBuffer
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Structure member 1 at offset 1 is not aligned to 8"));
+}
+
+TEST_F(ValidateExplicitLayout, MultiDimensionalArray) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Offset 0
+OpDecorate %array_4 ArrayStride 4
+OpDecorate %array_3 ArrayStride 48
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_3 = OpConstant %int 3
+%int_4 = OpConstant %int 4
+%array_4 = OpTypeArray %int %int_4
+%array_3 = OpTypeArray %array_4 %int_3
+%struct = OpTypeStruct %array_3
+%ptr_struct = OpTypePointer Uniform %struct
+%var = OpVariable %ptr_struct Uniform
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Array stride 4 must satisfy alignment 16"));
+}
+
+TEST_F(ValidateExplicitLayout, ImproperStraddleInArray) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Offset 0
+OpDecorate %array ArrayStride 24
+OpMemberDecorate %inner 0 Offset 0
+OpMemberDecorate %inner 1 Offset 4
+OpMemberDecorate %inner 2 Offset 12
+OpMemberDecorate %inner 3 Offset 16
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_2 = OpConstant %int 2
+%int2 = OpTypeVector %int 2
+%inner = OpTypeStruct %int %int2 %int %int
+%array = OpTypeArray %inner %int_2
+%struct = OpTypeStruct %array
+%ptr_struct = OpTypePointer StorageBuffer %struct
+%var = OpVariable %ptr_struct StorageBuffer
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Vector has improper straddle due to offset 28"));
+}
+
+TEST_F(ValidateExplicitLayout, LargeArray) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Offset 0
+OpDecorate %array ArrayStride 24
+OpMemberDecorate %inner 0 Offset 0
+OpMemberDecorate %inner 1 Offset 8
+OpMemberDecorate %inner 2 Offset 16
+OpMemberDecorate %inner 3 Offset 20
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_2000000 = OpConstant %int 2000000
+%int2 = OpTypeVector %int 2
+%inner = OpTypeStruct %int %int2 %int %int
+%array = OpTypeArray %inner %int_2000000
+%struct = OpTypeStruct %array
+%ptr_struct = OpTypePointer StorageBuffer %struct
+%var = OpVariable %ptr_struct StorageBuffer
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+}
+
+TEST_F(ValidateExplicitLayout, VulkanArrayStrideZero) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Offset 0
+OpDecorate %array ArrayStride 0
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_4 = OpConstant %int 4
+%array = OpTypeArray %int %int_4
+%struct = OpTypeStruct %array
+%ptr_ssbo_struct = OpTypePointer StorageBuffer %struct
+%var = OpVariable %ptr_ssbo_struct StorageBuffer
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Array must not have a stride of 0"));
+}
+
+TEST_F(ValidateExplicitLayout, VulkanArrayStrideTooSmall) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Offset 0
+OpDecorate %inner ArrayStride 4
+OpDecorate %outer ArrayStride 4
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_4 = OpConstant %int 4
+%inner = OpTypeArray %int %int_4
+%outer = OpTypeArray %inner %int_4
+%struct = OpTypeStruct %outer
+%ptr_ssbo_struct = OpTypePointer StorageBuffer %struct
+%var = OpVariable %ptr_ssbo_struct StorageBuffer
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Array stride 4 is smaller than element type size 16"));
+}
+
+TEST_F(ValidateExplicitLayout, WorkgroupSingleBlockVariable) {
+  std::string spirv = R"(
+               OpCapability Shader
+               OpCapability WorkgroupMemoryExplicitLayoutKHR
+               OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main" %_
+               OpExecutionMode %main LocalSize 8 1 1
+               OpMemberDecorate %first 0 Offset 0
+               OpDecorate %first Block
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+      %first = OpTypeStruct %int
+%_ptr_Workgroup_first = OpTypePointer Workgroup %first
+          %_ = OpVariable %_ptr_Workgroup_first Workgroup
+      %int_0 = OpConstant %int 0
+      %int_2 = OpConstant %int 2
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %13 = OpAccessChain %_ptr_Workgroup_int %_ %int_0
+               OpStore %13 %int_2
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_EQ(SPV_SUCCESS,
+            ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4));
+}
+
+TEST_F(ValidateExplicitLayout, WorkgroupSingleNonBlockVariable) {
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main" %a
+               OpExecutionMode %main LocalSize 8 1 1
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+          %a = OpVariable %_ptr_Workgroup_int Workgroup
+      %int_2 = OpConstant %int 2
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpStore %a %int_2
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_EQ(SPV_SUCCESS,
+            ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4));
+}
+
+TEST_F(ValidateExplicitLayout, WorkgroupMultiBlockVariable) {
+  std::string spirv = R"(
+               OpCapability Shader
+               OpCapability WorkgroupMemoryExplicitLayoutKHR
+               OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main" %_ %__0
+               OpExecutionMode %main LocalSize 8 1 1
+               OpMemberDecorate %first 0 Offset 0
+               OpDecorate %first Block
+               OpMemberDecorate %second 0 Offset 0
+               OpDecorate %second Block
+               OpDecorate %_ Aliased
+               OpDecorate %__0 Aliased
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+      %first = OpTypeStruct %int
+%_ptr_Workgroup_first = OpTypePointer Workgroup %first
+          %_ = OpVariable %_ptr_Workgroup_first Workgroup
+      %int_0 = OpConstant %int 0
+      %int_2 = OpConstant %int 2
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+     %second = OpTypeStruct %int
+%_ptr_Workgroup_second = OpTypePointer Workgroup %second
+        %__0 = OpVariable %_ptr_Workgroup_second Workgroup
+      %int_3 = OpConstant %int 3
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %13 = OpAccessChain %_ptr_Workgroup_int %_ %int_0
+               OpStore %13 %int_2
+         %18 = OpAccessChain %_ptr_Workgroup_int %__0 %int_0
+               OpStore %18 %int_3
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_EQ(SPV_SUCCESS,
+            ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4));
+}
+
+TEST_F(ValidateExplicitLayout, WorkgroupBlockVariableWith8BitType) {
+  std::string spirv = R"(
+               OpCapability Shader
+               OpCapability Int8
+               OpCapability WorkgroupMemoryExplicitLayout8BitAccessKHR
+               OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main" %_
+               OpExecutionMode %main LocalSize 2 1 1
+               OpMemberDecorate %first 0 Offset 0
+               OpDecorate %first Block
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+       %char = OpTypeInt 8 1
+      %first = OpTypeStruct %char
+%_ptr_Workgroup_first = OpTypePointer Workgroup %first
+          %_ = OpVariable %_ptr_Workgroup_first Workgroup
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+     %char_2 = OpConstant %char 2
+%_ptr_Workgroup_char = OpTypePointer Workgroup %char
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %14 = OpAccessChain %_ptr_Workgroup_char %_ %int_0
+               OpStore %14 %char_2
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_EQ(SPV_SUCCESS,
+            ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4));
+}
+
+TEST_F(ValidateExplicitLayout, WorkgroupMultiNonBlockVariable) {
+  std::string spirv = R"(
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main" %a %b
+               OpExecutionMode %main LocalSize 8 1 1
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+          %a = OpVariable %_ptr_Workgroup_int Workgroup
+      %int_2 = OpConstant %int 2
+          %b = OpVariable %_ptr_Workgroup_int Workgroup
+      %int_3 = OpConstant %int 3
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpStore %a %int_2
+               OpStore %b %int_3
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_EQ(SPV_SUCCESS,
+            ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4));
+}
+
+TEST_F(ValidateExplicitLayout, WorkgroupBlockVariableWith16BitType) {
+  std::string spirv = R"(
+               OpCapability Shader
+               OpCapability Float16
+               OpCapability Int16
+               OpCapability WorkgroupMemoryExplicitLayoutKHR
+               OpCapability WorkgroupMemoryExplicitLayout16BitAccessKHR
+               OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main" %_
+               OpExecutionMode %main LocalSize 2 1 1
+               OpMemberDecorate %first 0 Offset 0
+               OpMemberDecorate %first 1 Offset 2
+               OpDecorate %first Block
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %short = OpTypeInt 16 1
+       %half = OpTypeFloat 16
+      %first = OpTypeStruct %short %half
+%_ptr_Workgroup_first = OpTypePointer Workgroup %first
+          %_ = OpVariable %_ptr_Workgroup_first Workgroup
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+    %short_3 = OpConstant %short 3
+%_ptr_Workgroup_short = OpTypePointer Workgroup %short
+      %int_1 = OpConstant %int 1
+%half_0x1_898p_3 = OpConstant %half 0x1.898p+3
+%_ptr_Workgroup_half = OpTypePointer Workgroup %half
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %15 = OpAccessChain %_ptr_Workgroup_short %_ %int_0
+               OpStore %15 %short_3
+         %19 = OpAccessChain %_ptr_Workgroup_half %_ %int_1
+               OpStore %19 %half_0x1_898p_3
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_EQ(SPV_SUCCESS,
+            ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4));
+}
+
+TEST_F(ValidateExplicitLayout, WorkgroupBlockVariableScalarLayout) {
+  std::string spirv = R"(
+               OpCapability Shader
+               OpCapability WorkgroupMemoryExplicitLayoutKHR
+               OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %main "main" %B
+               OpSource GLSL 450
+               OpMemberDecorate %S 0 Offset 0
+               OpMemberDecorate %S 1 Offset 4
+               OpMemberDecorate %S 2 Offset 16
+               OpMemberDecorate %S 3 Offset 28
+               OpDecorate %S Block
+               OpDecorate %B Aliased
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+      %float = OpTypeFloat 32
+    %v3float = OpTypeVector %float 3
+          %S = OpTypeStruct %float %v3float %v3float %v3float
+%_ptr_Workgroup_S = OpTypePointer Workgroup %S
+          %B = OpVariable %_ptr_Workgroup_S Workgroup
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
+  spvValidatorOptionsSetWorkgroupScalarBlockLayout(getValidatorOptions(), true);
+  EXPECT_EQ(SPV_SUCCESS,
+            ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_4))
+      << getDiagnosticString();
+}
+
+TEST_F(ValidateExplicitLayout, WorkgroupSingleBlockVariableMissingLayout) {
+  std::string spirv = R"(
+               OpCapability Shader
+               OpCapability WorkgroupMemoryExplicitLayoutKHR
+               OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main" %_
+               OpExecutionMode %main LocalSize 8 1 1
+               OpDecorate %first Block
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+      %first = OpTypeStruct %int
+%_ptr_Workgroup_first = OpTypePointer Workgroup %first
+          %_ = OpVariable %_ptr_Workgroup_first Workgroup
+      %int_0 = OpConstant %int 0
+      %int_2 = OpConstant %int 2
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %13 = OpAccessChain %_ptr_Workgroup_int %_ %int_0
+               OpStore %13 %int_2
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1_SPIRV_1_4));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Structure member 0 must be explicitly laid out with "
+                        "Offset or OffsetIdEXT decorations"));
+}
+
+TEST_F(ValidateExplicitLayout, WorkgroupSingleBlockVariableBadLayout) {
+  std::string spirv = R"(
+               OpCapability Shader
+               OpCapability WorkgroupMemoryExplicitLayoutKHR
+               OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main" %_
+               OpExecutionMode %main LocalSize 8 1 1
+               OpMemberDecorate %first 0 Offset 1
+               OpDecorate %first Block
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+      %first = OpTypeStruct %int
+%_ptr_Workgroup_first = OpTypePointer Workgroup %first
+          %_ = OpVariable %_ptr_Workgroup_first Workgroup
+      %int_0 = OpConstant %int 0
+      %int_2 = OpConstant %int 2
+%_ptr_Workgroup_int = OpTypePointer Workgroup %int
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %13 = OpAccessChain %_ptr_Workgroup_int %_ %int_0
+               OpStore %13 %int_2
+               OpReturn
+               OpFunctionEnd
+  )";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID,
+            ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1_SPIRV_1_4));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Structure member 0 at offset 1 is not aligned to 4"));
+}
+
+TEST_F(ValidateExplicitLayout, BadMatrixStrideUniform) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+OpMemberDecorate %block 0 MatrixStride 3
+OpMemberDecorate %block 0 ColMajor
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+%float4 = OpTypeVector %float 4
+%matrix4x4 = OpTypeMatrix %float4 4
+%block = OpTypeStruct %matrix4x4
+%block_ptr = OpTypePointer Uniform %block
+%var = OpVariable %block_ptr Uniform
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Matrix with a stride 3 not satisfying alignment to 16"));
+}
+
+TEST_F(ValidateExplicitLayout, BadMatrixStrideStorageBuffer) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+OpMemberDecorate %block 0 MatrixStride 3
+OpMemberDecorate %block 0 ColMajor
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+%float4 = OpTypeVector %float 4
+%matrix4x4 = OpTypeMatrix %float4 4
+%block = OpTypeStruct %matrix4x4
+%block_ptr = OpTypePointer StorageBuffer %block
+%var = OpVariable %block_ptr StorageBuffer
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Matrix with a stride 3 not satisfying alignment to 16"));
+}
+
+TEST_F(ValidateExplicitLayout, BadMatrixStridePushConstant) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+OpMemberDecorate %block 0 MatrixStride 3
+OpMemberDecorate %block 0 ColMajor
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+%float4 = OpTypeVector %float 4
+%matrix4x4 = OpTypeMatrix %float4 4
+%block = OpTypeStruct %matrix4x4
+%block_ptr = OpTypePointer PushConstant %block
+%var = OpVariable %block_ptr PushConstant
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Matrix with a stride 3 not satisfying alignment to 16"));
+}
+
+TEST_F(ValidateExplicitLayout, BadMatrixStrideStorageBufferScalarLayout) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+OpMemberDecorate %block 0 MatrixStride 3
+OpMemberDecorate %block 0 RowMajor
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+%float4 = OpTypeVector %float 4
+%matrix4x4 = OpTypeMatrix %float4 4
+%block = OpTypeStruct %matrix4x4
+%block_ptr = OpTypePointer StorageBuffer %block
+%var = OpVariable %block_ptr StorageBuffer
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  options_->scalar_block_layout = true;
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Matrix with a stride 3 not satisfying alignment to 4"));
+}
+
+TEST_F(ValidateExplicitLayout, MissingOffsetStructNestedInArray) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %array ArrayStride 4
+OpDecorate %outer Block
+OpMemberDecorate %outer 0 Offset 0
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_4 = OpConstant %int 4
+%inner = OpTypeStruct %int
+%array = OpTypeArray %inner %int_4
+%outer = OpTypeStruct %array
+%ptr_ssbo_outer = OpTypePointer StorageBuffer %outer
+%var = OpVariable %ptr_ssbo_outer StorageBuffer
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Structure member 0 must be explicitly laid out with "
+                        "Offset or OffsetIdEXT decorations"));
+}
+
+TEST_F(ValidateExplicitLayout, Std140ColMajorMat2x2) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+OpMemberDecorate %block 0 ColMajor
+OpMemberDecorate %block 0 MatrixStride 8
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%float2 = OpTypeVector %float 2
+%matrix = OpTypeMatrix %float2 2
+%block = OpTypeStruct %matrix
+%ptr_block = OpTypePointer Uniform %block
+%var = OpVariable %ptr_block Uniform
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Matrix with a stride 8 not satisfying alignment to 16"));
+}
+
+TEST_F(ValidateExplicitLayout, Std140RowMajorMat2x2) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+OpMemberDecorate %block 0 RowMajor
+OpMemberDecorate %block 0 MatrixStride 8
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%float2 = OpTypeVector %float 2
+%matrix = OpTypeMatrix %float2 2
+%block = OpTypeStruct %matrix
+%ptr_block = OpTypePointer Uniform %block
+%var = OpVariable %ptr_block Uniform
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Matrix with a stride 8 not satisfying alignment to 16"));
+}
+
+TEST_F(ValidateExplicitLayout, Std140ColMajorMat4x2) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+OpMemberDecorate %block 0 ColMajor
+OpMemberDecorate %block 0 MatrixStride 8
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%float2 = OpTypeVector %float 2
+%matrix = OpTypeMatrix %float2 4
+%block = OpTypeStruct %matrix
+%ptr_block = OpTypePointer Uniform %block
+%var = OpVariable %ptr_block Uniform
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Matrix with a stride 8 not satisfying alignment to 16"));
+}
+
+TEST_F(ValidateExplicitLayout, Std140ColMajorMat2x3) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+OpMemberDecorate %block 0 ColMajor
+OpMemberDecorate %block 0 MatrixStride 12
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%float3 = OpTypeVector %float 3
+%matrix = OpTypeMatrix %float3 2
+%block = OpTypeStruct %matrix
+%ptr_block = OpTypePointer Uniform %block
+%var = OpVariable %ptr_block Uniform
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Matrix with a stride 12 not satisfying "
+                        "alignment to 16"));
+}
+
+TEST_F(ValidateExplicitLayout, MatrixMissingMajornessUniform) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+OpMemberDecorate %block 0 MatrixStride 16
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%float2 = OpTypeVector %float 2
+%matrix = OpTypeMatrix %float2 2
+%block = OpTypeStruct %matrix
+%ptr_block = OpTypePointer Uniform %block
+%var = OpVariable %ptr_block Uniform
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "must be explicitly laid out with RowMajor or ColMajor decorations"));
+}
+
+TEST_F(ValidateExplicitLayout, MatrixMissingMajornessStorageBuffer) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+OpMemberDecorate %block 0 MatrixStride 16
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%float2 = OpTypeVector %float 2
+%matrix = OpTypeMatrix %float2 2
+%block = OpTypeStruct %matrix
+%ptr_block = OpTypePointer StorageBuffer %block
+%var = OpVariable %ptr_block StorageBuffer
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "must be explicitly laid out with RowMajor or ColMajor decorations"));
+}
+
+TEST_F(ValidateExplicitLayout, MatrixMissingMajornessPushConstant) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+OpMemberDecorate %block 0 MatrixStride 16
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%float2 = OpTypeVector %float 2
+%matrix = OpTypeMatrix %float2 2
+%block = OpTypeStruct %matrix
+%ptr_block = OpTypePointer PushConstant %block
+%var = OpVariable %ptr_block PushConstant
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "must be explicitly laid out with RowMajor or ColMajor decorations"));
+}
+
+TEST_F(ValidateExplicitLayout, StructWithRowAndColMajor) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+OpMemberDecorate %block 0 MatrixStride 16
+OpMemberDecorate %block 0 ColMajor
+OpMemberDecorate %block 1 Offset 32
+OpMemberDecorate %block 1 MatrixStride 16
+OpMemberDecorate %block 1 RowMajor
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%float = OpTypeFloat 32
+%float2 = OpTypeVector %float 2
+%matrix = OpTypeMatrix %float2 2
+%block = OpTypeStruct %matrix %matrix
+%ptr_block = OpTypePointer PushConstant %block
+%var = OpVariable %ptr_block PushConstant
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateExplicitLayout, PhysicalStorageBufferWithOffset) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability Int64
+OpCapability PhysicalStorageBufferAddresses
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
+OpEntryPoint GLCompute %main "main" %pc
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %pc_block Block
+OpMemberDecorate %pc_block 0 Offset 0
+OpMemberDecorate %pssbo_struct 0 Offset 0
+%void = OpTypeVoid
+%long = OpTypeInt 64 0
+%float = OpTypeFloat 32
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%pc_block = OpTypeStruct %long
+%pc_block_ptr = OpTypePointer PushConstant %pc_block
+%pc_long_ptr = OpTypePointer PushConstant %long
+%pc = OpVariable %pc_block_ptr PushConstant
+%pssbo_struct = OpTypeStruct %float
+%pssbo_ptr = OpTypePointer PhysicalStorageBuffer %pssbo_struct
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%pc_gep = OpAccessChain %pc_long_ptr %pc %int_0
+%addr = OpLoad %long %pc_gep
+%ptr = OpConvertUToPtr %pssbo_ptr %addr
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+}
+
+TEST_F(ValidateExplicitLayout, PhysicalStorageBufferMissingOffset) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability Int64
+OpCapability PhysicalStorageBufferAddresses
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
+OpEntryPoint GLCompute %main "main" %pc
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %pc_block Block
+OpMemberDecorate %pc_block 0 Offset 0
+%void = OpTypeVoid
+%long = OpTypeInt 64 0
+%float = OpTypeFloat 32
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%pc_block = OpTypeStruct %long
+%pc_block_ptr = OpTypePointer PushConstant %pc_block
+%pc_long_ptr = OpTypePointer PushConstant %long
+%pc = OpVariable %pc_block_ptr PushConstant
+%pssbo_struct = OpTypeStruct %float
+%pssbo_ptr = OpTypePointer PhysicalStorageBuffer %pssbo_struct
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%pc_gep = OpAccessChain %pc_long_ptr %pc %int_0
+%addr = OpLoad %long %pc_gep
+%ptr = OpConvertUToPtr %pssbo_ptr %addr
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Structure member 0 must be explicitly laid out with "
+                        "Offset or OffsetIdEXT decorations"));
+}
+
+TEST_F(ValidateExplicitLayout, PhysicalStorageBufferMissingArrayStride) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability Int64
+OpCapability PhysicalStorageBufferAddresses
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
+OpEntryPoint GLCompute %main "main" %pc
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %pc_block Block
+OpMemberDecorate %pc_block 0 Offset 0
+%void = OpTypeVoid
+%long = OpTypeInt 64 0
+%float = OpTypeFloat 32
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_4 = OpConstant %int 4
+%pc_block = OpTypeStruct %long
+%pc_block_ptr = OpTypePointer PushConstant %pc_block
+%pc_long_ptr = OpTypePointer PushConstant %long
+%pc = OpVariable %pc_block_ptr PushConstant
+%pssbo_array = OpTypeArray %float %int_4
+%pssbo_ptr = OpTypePointer PhysicalStorageBuffer %pssbo_array
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%pc_gep = OpAccessChain %pc_long_ptr %pc %int_0
+%addr = OpLoad %long %pc_gep
+%ptr = OpConvertUToPtr %pssbo_ptr %addr
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Array must be explicitly laid out with ArrayStride or "
+                        "ArrayStrideIdEXT decorations"));
+}
+
+TEST_F(ValidateExplicitLayout, MatrixArrayMissingMajorness) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+OpMemberDecorate %block 0 MatrixStride 16
+OpDecorate %array ArrayStride 32
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+%int = OpTypeInt 32 0
+%int_2 = OpConstant %int 2
+%vec = OpTypeVector %float 2
+%mat = OpTypeMatrix %vec 2
+%array = OpTypeArray %mat %int_2
+%block = OpTypeStruct %array
+%ptr = OpTypePointer Uniform %block
+%var = OpVariable %ptr Uniform
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "must be explicitly laid out with RowMajor or ColMajor decorations"));
+}
+
+TEST_F(ValidateExplicitLayout, MatrixArrayMissingStride) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+OpMemberDecorate %block 0 ColMajor
+OpDecorate %array ArrayStride 32
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+%int = OpTypeInt 32 0
+%int_2 = OpConstant %int 2
+%vec = OpTypeVector %float 2
+%mat = OpTypeMatrix %vec 2
+%array = OpTypeArray %mat %int_2
+%block = OpTypeStruct %array
+%ptr = OpTypePointer Uniform %block
+%var = OpVariable %ptr Uniform
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("must be explicitly laid out with MatrixStride decorations"));
+}
+
+TEST_F(ValidateExplicitLayout, MatrixArrayBadStride) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+OpMemberDecorate %block 0 ColMajor
+OpMemberDecorate %block 0 MatrixStride 8
+OpDecorate %array ArrayStride 32
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+%int = OpTypeInt 32 0
+%int_2 = OpConstant %int 2
+%vec = OpTypeVector %float 2
+%mat = OpTypeMatrix %vec 2
+%array = OpTypeArray %mat %int_2
+%block = OpTypeStruct %array
+%ptr = OpTypePointer Uniform %block
+%var = OpVariable %ptr Uniform
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Matrix with a stride 8 not satisfying alignment to 16"));
+}
+
+TEST_F(ValidateExplicitLayout, MatrixArrayArrayMissingMajorness) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+OpMemberDecorate %block 0 MatrixStride 16
+OpDecorate %array ArrayStride 32
+OpDecorate %rta ArrayStride 64
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+%int = OpTypeInt 32 0
+%int_2 = OpConstant %int 2
+%vec = OpTypeVector %float 2
+%mat = OpTypeMatrix %vec 2
+%array = OpTypeArray %mat %int_2
+%rta = OpTypeRuntimeArray %array
+%block = OpTypeStruct %rta
+%ptr = OpTypePointer StorageBuffer %block
+%var = OpVariable %ptr StorageBuffer
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr(
+          "must be explicitly laid out with RowMajor or ColMajor decorations"));
+}
+
+TEST_F(ValidateExplicitLayout, MatrixArrayArrayMissingStride) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+OpMemberDecorate %block 0 ColMajor
+OpDecorate %array ArrayStride 32
+OpDecorate %rta ArrayStride 64
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+%int = OpTypeInt 32 0
+%int_2 = OpConstant %int 2
+%vec = OpTypeVector %float 2
+%mat = OpTypeMatrix %vec 2
+%array = OpTypeArray %mat %int_2
+%rta = OpTypeRuntimeArray %array
+%block = OpTypeStruct %rta
+%ptr = OpTypePointer StorageBuffer %block
+%var = OpVariable %ptr StorageBuffer
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("must be explicitly laid out with MatrixStride decorations"));
+}
+
+TEST_F(ValidateExplicitLayout, MatrixArrayArrayBadStride) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+OpMemberDecorate %block 0 ColMajor
+OpMemberDecorate %block 0 MatrixStride 8
+OpDecorate %array ArrayStride 32
+OpDecorate %a ArrayStride 64
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+%int = OpTypeInt 32 0
+%int_2 = OpConstant %int 2
+%vec = OpTypeVector %float 2
+%mat = OpTypeMatrix %vec 2
+%array = OpTypeArray %mat %int_2
+%a = OpTypeArray %array %int_2
+%block = OpTypeStruct %a
+%ptr = OpTypePointer Uniform %block
+%var = OpVariable %ptr Uniform
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Matrix with a stride 8 not satisfying alignment to 16"));
+}
+
+TEST_F(ValidateExplicitLayout, UntypedVariableWorkgroupRequiresStruct) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpCapability WorkgroupMemoryExplicitLayoutKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main" %var
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%ptr = OpTypeUntypedPointerKHR Workgroup
+%var = OpUntypedVariableKHR %ptr Workgroup %int
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Untyped workgroup variables in shaders must be block "
+                        "decorated structs"));
+}
+
+TEST_F(ValidateExplicitLayout, UntypedVariableWorkgroupRequiresBlockStruct) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpCapability WorkgroupMemoryExplicitLayoutKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main" %var
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%struct = OpTypeStruct %int
+%ptr = OpTypeUntypedPointerKHR Workgroup
+%var = OpUntypedVariableKHR %ptr Workgroup %struct
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Untyped workgroup variables in shaders must be block "
+                        "decorated"));
+}
+
+TEST_F(ValidateExplicitLayout, UntypedArrayLengthMissingOffset) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %struct Block
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+OpDecorate %array ArrayStride 4
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%array = OpTypeRuntimeArray %int
+%struct = OpTypeStruct %array
+%block = OpTypeStruct %array
+%ptr = OpTypeUntypedPointerKHR StorageBuffer
+%var = OpUntypedVariableKHR %ptr StorageBuffer %block
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%len = OpUntypedArrayLengthKHR %int %struct %var 0
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Structure member 0 must be explicitly laid out with "
+                        "Offset or OffsetIdEXT decorations"));
+}
+
+TEST_F(ValidateExplicitLayout, BlockArrayWithoutStride) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Offset 0
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%int = OpTypeInt 32 0
+%int_4 = OpConstant %int 4
+%struct = OpTypeStruct %int
+%array = OpTypeArray %struct %int_4
+%ptr = OpTypePointer StorageBuffer %array
+%var = OpVariable %ptr StorageBuffer
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateExplicitLayout, BlockArrayWithoutStrideUntypedAccessChain) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpExtension "SPV_KHR_untyped_pointers"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Offset 0
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%int = OpTypeInt 32 0
+%int_4 = OpConstant %int 4
+%struct = OpTypeStruct %int
+%array = OpTypeArray %struct %int_4
+%void = OpTypeVoid
+%ptr = OpTypeUntypedPointerKHR StorageBuffer
+%var = OpUntypedVariableKHR %ptr StorageBuffer %array
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = OpUntypedAccessChainKHR %ptr %array %var
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateExplicitLayout, InvalidLayoutBlockFunctionPre1p4) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %block Block
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%block = OpTypeStruct %int
+%ptr_function_block = OpTypePointer Function %block
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%var = OpVariable %ptr_function_block Function
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateExplicitLayout, InvalidLayoutBlockFunctionPost1p4) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %block Block
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%block = OpTypeStruct %int
+%ptr_function_block = OpTypePointer Function %block
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%var = OpVariable %ptr_function_block Function
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_5);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-None-10684"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Invalid explicit layout decorations on type"));
+}
+
+TEST_F(ValidateExplicitLayout, InvalidLayoutOffsetPrivatePre1p4) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpMemberDecorate %block 0 Offset 0
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%block = OpTypeStruct %int
+%ptr_private_block = OpTypePointer Private %block
+%void_fn = OpTypeFunction %void
+%var = OpVariable %ptr_private_block Private
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateExplicitLayout, InvalidLayoutOffsetPrivatePost1p4) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpMemberDecorate %block 0 Offset 0
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%block = OpTypeStruct %int
+%ptr_private_block = OpTypePointer Private %block
+%void_fn = OpTypeFunction %void
+%var = OpVariable %ptr_private_block Private
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-None-10684"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Invalid explicit layout decorations on type"));
+}
+
+TEST_F(ValidateExplicitLayout,
+       InvalidLayoutArrayStrideWorkgroupExplicitLayout_MissingBlock) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability WorkgroupMemoryExplicitLayoutKHR
+OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %array ArrayStride 4
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_4 = OpConstant %int 4
+%array = OpTypeArray %int %int_4
+%ptr_wg_block = OpTypePointer Workgroup %array
+%void_fn = OpTypeFunction %void
+%var = OpVariable %ptr_wg_block Workgroup
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Invalid explicit layout decorations on type"));
+}
+
+TEST_F(ValidateExplicitLayout,
+       InvalidLayoutArrayStrideWorkgroupExplicitLayout) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability WorkgroupMemoryExplicitLayoutKHR
+OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %array ArrayStride 4
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_4 = OpConstant %int 4
+%array = OpTypeArray %int %int_4
+%block = OpTypeStruct %array
+%ptr_wg_block = OpTypePointer Workgroup %block
+%void_fn = OpTypeFunction %void
+%var = OpVariable %ptr_wg_block Workgroup
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+}
+
+TEST_F(ValidateExplicitLayout, InvalidLayoutArrayStrideWorkgroup) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %array ArrayStride 4
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_4 = OpConstant %int 4
+%array = OpTypeArray %int %int_4
+%ptr_wg_block = OpTypePointer Workgroup %array
+%void_fn = OpTypeFunction %void
+%var = OpVariable %ptr_wg_block Workgroup
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-None-10684"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Invalid explicit layout decorations on type"));
+}
+
+TEST_F(ValidateExplicitLayout, InvalidLayoutArrayStrideUniformConstant) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %array ArrayStride 4
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_4 = OpConstant %int 4
+%sampler = OpTypeSampler
+%array = OpTypeArray %sampler %int_4
+%ptr_uc_block = OpTypePointer UniformConstant %array
+%void_fn = OpTypeFunction %void
+%var = OpVariable %ptr_uc_block UniformConstant
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-None-10684"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Invalid explicit layout decorations on type"));
+}
+
+TEST_F(ValidateExplicitLayout, InvalidLayoutMatrixStrideFunctionPost1p4) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpMemberDecorate %block 0 MatrixStride 16
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%mat4x4 = OpTypeMatrix %v4float 4
+%block = OpTypeStruct %mat4x4
+%ptr_function_block = OpTypePointer Function %block
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%var = OpVariable %ptr_function_block Function
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-None-10684"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Invalid explicit layout decorations on type"));
+}
+
+TEST_F(ValidateExplicitLayout, InvalidLayoutNestedMatrixStrideFunctionPost1p4) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpMemberDecorate %block 0 MatrixStride 16
+%void = OpTypeVoid
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%mat4x4 = OpTypeMatrix %v4float 4
+%block = OpTypeStruct %mat4x4
+%block2 = OpTypeStruct %block
+%int = OpTypeInt 32 0
+%int_2 = OpConstant %int 2
+%array = OpTypeArray %block2 %int_2
+%ptr_function_array = OpTypePointer Function %array
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%var = OpVariable %ptr_function_array Function
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-None-10684"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Invalid explicit layout decorations on type"));
+}
+
+TEST_F(ValidateExplicitLayout, InvalidLayoutBufferBlockWorkgroup) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %block BufferBlock
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%block = OpTypeStruct %int
+%ptr_wg_block = OpTypePointer Workgroup %block
+%void_fn = OpTypeFunction %void
+%var = OpVariable %ptr_wg_block Workgroup
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-None-10684"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Invalid explicit layout decorations on type"));
+}
+
+TEST_F(ValidateExplicitLayout, InvalidLayoutUntypedStore) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 0
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%block = OpTypeStruct %int
+%block_null = OpConstantNull %block
+%ptr = OpTypeUntypedPointerKHR StorageBuffer
+%var = OpUntypedVariableKHR %ptr StorageBuffer %block
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpStore %var %block_null
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateExplicitLayout, ExplicitLayoutOnPtrPhysicalStorageBuffer) {
+  const std::string spirv = R"(
+OpCapability PhysicalStorageBufferAddresses
+OpCapability Int64
+OpCapability Shader
+OpExtension "SPV_KHR_physical_storage_buffer"
+OpMemoryModel PhysicalStorageBuffer64 GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %_ptr_PhysicalStorageBuffer_int ArrayStride 4
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 1
+%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int  ; ArrayStride 4
+%Foo = OpTypeStruct %_ptr_PhysicalStorageBuffer_int
+%_ptr_Function_Foo = OpTypePointer Function %Foo
+%int_0 = OpConstant %int 0
+%_ptr_Function__ptr_PhysicalStorageBuffer_int = OpTypePointer Function %_ptr_PhysicalStorageBuffer_int
+%ulong = OpTypeInt 64 0
+%ulong_0 = OpConstant %ulong 0
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%obj = OpVariable %_ptr_Function_Foo Function
+%obj_member = OpAccessChain %_ptr_Function__ptr_PhysicalStorageBuffer_int %obj %int_0
+%nullptr = OpConvertUToPtr %_ptr_PhysicalStorageBuffer_int %ulong_0
+OpStore %obj_member %nullptr
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_5);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+}
+
+TEST_F(ValidateExplicitLayout, RuntimeArrayNotLargestOffsetInBlock) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+OpDecorate %block Block
+OpMemberDecorate %block 0 Offset 16
+OpMemberDecorate %block 1 Offset 0
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 0
+%array = OpTypeRuntimeArray %int
+%block = OpTypeStruct %int %array
+%ptr = OpTypePointer StorageBuffer %block
+%var = OpVariable %ptr StorageBuffer
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("has a runtime array at offset 0, but other members at "
+                        "larger offsets"));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680"));
+}
+
+TEST_F(ValidateExplicitLayout, RuntimeArrayNotLargestOffsetInBufferBlock) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+OpDecorate %block BufferBlock
+OpMemberDecorate %block 0 Offset 16
+OpMemberDecorate %block 1 Offset 0
+OpDecorate %array ArrayStride 4
+%void = OpTypeVoid
+%void_fn = OpTypeFunction %void
+%int = OpTypeInt 32 0
+%array = OpTypeRuntimeArray %int
+%block = OpTypeStruct %int %array
+%ptr = OpTypePointer Uniform %block
+%var = OpVariable %ptr Uniform
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("has a runtime array at offset 0, but other members at "
+                        "larger offsets"));
+  EXPECT_THAT(getDiagnosticString(),
+              AnyVUID("VUID-StandaloneSpirv-OpTypeRuntimeArray-04680"));
+}
+
+TEST_F(ValidateExplicitLayout, LongVectorUniformPass_ImproperStraddle) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability Int8
+OpCapability UniformAndStorageBuffer8BitAccess
+OpCapability LongVectorEXT
+
+OpExtension "SPV_KHR_8bit_storage"
+OpExtension "SPV_EXT_long_vector"
+
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %BP_main "main"
+
+OpDecorate %input0 DescriptorSet 0
+OpDecorate %input0 Binding 0
+OpDecorate %a10testtype ArrayStride 16
+OpDecorate %buf BufferBlock
+OpMemberDecorate %buf 0 Offset 0
+
+%void = OpTypeVoid
+%bool = OpTypeBool
+%u32 = OpTypeInt 32 0
+%voidf = OpTypeFunction %void
+%c_u32_10 = OpConstant %u32 10
+%vectorSizeConst = OpConstant %u32 12
+
+%scalartype = OpTypeInt 8 1
+%testtype = OpTypeVectorIdEXT %scalartype %vectorSizeConst
+
+%a10testtype = OpTypeArray %testtype %c_u32_10
+%buf = OpTypeStruct %a10testtype
+%bufptr = OpTypePointer Uniform %buf
+
+%input0 = OpVariable %bufptr Uniform
+
+
+%BP_main = OpFunction %void None %voidf
+%BP_label = OpLabel
+OpReturn
+OpFunctionEnd
+
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateExplicitLayout, LongVectorUniformPass) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability Int8
+OpCapability UniformAndStorageBuffer8BitAccess
+OpCapability LongVectorEXT
+
+OpExtension "SPV_KHR_8bit_storage"
+OpExtension "SPV_EXT_long_vector"
+
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %BP_main "main"
+
+OpDecorate %input0 DescriptorSet 0
+OpDecorate %input0 Binding 0
+OpDecorate %a10testtype ArrayStride 12
+OpDecorate %buf BufferBlock
+OpMemberDecorate %buf 0 Offset 0
+
+%void = OpTypeVoid
+%bool = OpTypeBool
+%u32 = OpTypeInt 32 0
+%voidf = OpTypeFunction %void
+%c_u32_10 = OpConstant %u32 10
+%vectorSizeConst = OpConstant %u32 12
+
+%scalartype = OpTypeInt 8 1
+%testtype = OpTypeVectorIdEXT %scalartype %vectorSizeConst
+
+%a10testtype = OpTypeArray %testtype %c_u32_10
+%buf = OpTypeStruct %a10testtype
+%bufptr = OpTypePointer Uniform %buf
+
+%input0 = OpVariable %bufptr Uniform
+
+
+%BP_main = OpFunction %void None %voidf
+%BP_label = OpLabel
+OpReturn
+OpFunctionEnd
+
+)";
+
+  options_->scalar_block_layout = true;
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0));
+}
+
+TEST_F(ValidateExplicitLayout, BufferPointerEXTMissingOffsetBad) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpCapability DescriptorHeapEXT
+OpExtension "SPV_KHR_untyped_pointers"
+OpExtension "SPV_EXT_descriptor_heap"
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main" %resource_heap
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %resource_heap BuiltIn ResourceHeapEXT
+OpDecorate %Struct Block
+OpDecorate %storage_buffer_array_type ArrayStride 16
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%float = OpTypeFloat 32
+%int_0 = OpConstant %int 0
+%storage_buffer_type = OpTypeBufferEXT StorageBuffer
+%storage_buffer_array_type = OpTypeRuntimeArray %storage_buffer_type
+%Struct = OpTypeStruct %float
+%_ptr_StorageBuffer_Struct = OpTypePointer StorageBuffer %Struct
+%ptr_uniformconstant = OpTypeUntypedPointerKHR UniformConstant
+%ptr_storagebuffer = OpTypeUntypedPointerKHR StorageBuffer
+%resource_heap = OpUntypedVariableKHR %ptr_uniformconstant UniformConstant
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%buffer_ptr = OpUntypedAccessChainKHR %ptr_uniformconstant %storage_buffer_array_type %resource_heap %int_0
+%buffer_data_ptr = OpBufferPointerEXT %_ptr_StorageBuffer_Struct %buffer_ptr
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Structure member 0 must be explicitly laid out with "
+                        "Offset or OffsetIdEXT decorations"));
+}
+
+TEST_F(ValidateExplicitLayout, BufferPointerEXTUnalignedOffsetBad) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpCapability DescriptorHeapEXT
+OpExtension "SPV_KHR_untyped_pointers"
+OpExtension "SPV_EXT_descriptor_heap"
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main" %resource_heap
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %resource_heap BuiltIn ResourceHeapEXT
+OpDecorate %Struct Block
+OpMemberDecorate %Struct 0 Offset 2
+OpDecorate %storage_buffer_array_type ArrayStride 16
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%float = OpTypeFloat 32
+%int_0 = OpConstant %int 0
+%storage_buffer_type = OpTypeBufferEXT StorageBuffer
+%storage_buffer_array_type = OpTypeRuntimeArray %storage_buffer_type
+%Struct = OpTypeStruct %float
+%_ptr_StorageBuffer_Struct = OpTypePointer StorageBuffer %Struct
+%ptr_uniformconstant = OpTypeUntypedPointerKHR UniformConstant
+%ptr_storagebuffer = OpTypeUntypedPointerKHR StorageBuffer
+%resource_heap = OpUntypedVariableKHR %ptr_uniformconstant UniformConstant
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%buffer_ptr = OpUntypedAccessChainKHR %ptr_uniformconstant %storage_buffer_array_type %resource_heap %int_0
+%buffer_data_ptr = OpBufferPointerEXT %_ptr_StorageBuffer_Struct %buffer_ptr
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Structure member 0 at offset 2 is not aligned to 4"));
+}
+
+TEST_F(ValidateExplicitLayout, BufferPointerEXTMissingArrayStrideBad) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpCapability DescriptorHeapEXT
+OpExtension "SPV_KHR_untyped_pointers"
+OpExtension "SPV_EXT_descriptor_heap"
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main" %resource_heap
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %resource_heap BuiltIn ResourceHeapEXT
+OpDecorate %Struct Block
+OpMemberDecorate %Struct 0 Offset 0
+OpDecorate %storage_buffer_array_type ArrayStride 16
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%float = OpTypeFloat 32
+%int_0 = OpConstant %int 0
+%int_4 = OpConstant %int 4
+%array = OpTypeArray %float %int_4
+%storage_buffer_type = OpTypeBufferEXT StorageBuffer
+%storage_buffer_array_type = OpTypeRuntimeArray %storage_buffer_type
+%Struct = OpTypeStruct %array
+%_ptr_StorageBuffer_Struct = OpTypePointer StorageBuffer %Struct
+%ptr_uniformconstant = OpTypeUntypedPointerKHR UniformConstant
+%ptr_storagebuffer = OpTypeUntypedPointerKHR StorageBuffer
+%resource_heap = OpUntypedVariableKHR %ptr_uniformconstant UniformConstant
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%buffer_ptr = OpUntypedAccessChainKHR %ptr_uniformconstant %storage_buffer_array_type %resource_heap %int_0
+%buffer_data_ptr = OpBufferPointerEXT %_ptr_StorageBuffer_Struct %buffer_ptr
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Array must be explicitly laid out with ArrayStride or "
+                        "ArrayStrideIdEXT decorations"));
+}
+
+TEST_F(ValidateExplicitLayout, BufferPointerEXTMissingMatrixStrideBad) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpCapability DescriptorHeapEXT
+OpExtension "SPV_KHR_untyped_pointers"
+OpExtension "SPV_EXT_descriptor_heap"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main" %resource_heap
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %resource_heap BuiltIn ResourceHeapEXT
+OpDecorate %Struct Block
+OpMemberDecorate %Struct 0 Offset 0
+OpDecorate %storage_buffer_array_type ArrayStride 16
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%int_0 = OpConstant %int 0
+%mat = OpTypeMatrix %v4float 4
+%storage_buffer_type = OpTypeBufferEXT StorageBuffer
+%storage_buffer_array_type = OpTypeRuntimeArray %storage_buffer_type
+%Struct = OpTypeStruct %mat
+%_ptr_Uniform_Struct = OpTypePointer Uniform %Struct
+%ptr_uniformconstant = OpTypeUntypedPointerKHR UniformConstant
+%ptr_uniform = OpTypeUntypedPointerKHR Uniform
+%resource_heap = OpUntypedVariableKHR %ptr_uniformconstant UniformConstant
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%buffer_ptr = OpUntypedAccessChainKHR %ptr_uniformconstant %storage_buffer_array_type %resource_heap %int_0
+%buffer_data_ptr = OpBufferPointerEXT %_ptr_Uniform_Struct %buffer_ptr
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Structure member 0 containing a matrix must be "
+                        "explicitly laid out with RowMajor or ColMajor "
+                        "decorations"));
+}
+
+TEST_F(ValidateExplicitLayout, BufferPointerEXTStorageBufferScalarGood) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpCapability DescriptorHeapEXT
+OpExtension "SPV_KHR_untyped_pointers"
+OpExtension "SPV_EXT_descriptor_heap"
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main" %resource_heap
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %resource_heap BuiltIn ResourceHeapEXT
+OpDecorate %Struct Block
+OpMemberDecorate %Struct 0 Offset 0
+OpMemberDecorate %Struct 1 Offset 12
+OpDecorate %storage_buffer_array_type ArrayStride 16
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%float = OpTypeFloat 32
+%v3float = OpTypeVector %float 3
+%int_0 = OpConstant %int 0
+%storage_buffer_type = OpTypeBufferEXT StorageBuffer
+%storage_buffer_array_type = OpTypeRuntimeArray %storage_buffer_type
+%Struct = OpTypeStruct %v3float %float
+%_ptr_StorageBuffer_Struct = OpTypePointer StorageBuffer %Struct
+%ptr_uniformconstant = OpTypeUntypedPointerKHR UniformConstant
+%ptr_storagebuffer = OpTypeUntypedPointerKHR StorageBuffer
+%resource_heap = OpUntypedVariableKHR %ptr_uniformconstant UniformConstant
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%buffer_ptr = OpUntypedAccessChainKHR %ptr_uniformconstant %storage_buffer_array_type %resource_heap %int_0
+%buffer_data_ptr = OpBufferPointerEXT %_ptr_StorageBuffer_Struct %buffer_ptr
+OpReturn
+OpFunctionEnd
+)";
+
+  options_->scalar_block_layout = true;
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+}
+
+TEST_F(ValidateExplicitLayout, BufferPointerEXTUniformExtendedAlignmentBad) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpCapability DescriptorHeapEXT
+OpExtension "SPV_KHR_untyped_pointers"
+OpExtension "SPV_EXT_descriptor_heap"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main" %resource_heap
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %resource_heap BuiltIn ResourceHeapEXT
+OpDecorate %Struct Block
+OpMemberDecorate %Struct 0 Offset 0
+OpMemberDecorate %Struct 1 Offset 4
+OpDecorate %storage_buffer_array_type ArrayStride 16
+OpDecorate %array ArrayStride 16
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%float = OpTypeFloat 32
+%int_0 = OpConstant %int 0
+%int_4 = OpConstant %int 4
+%array = OpTypeArray %float %int_4
+%storage_buffer_type = OpTypeBufferEXT StorageBuffer
+%storage_buffer_array_type = OpTypeRuntimeArray %storage_buffer_type
+%Struct = OpTypeStruct %float %array
+%_ptr_Uniform_Struct = OpTypePointer Uniform %Struct
+%ptr_uniformconstant = OpTypeUntypedPointerKHR UniformConstant
+%resource_heap = OpUntypedVariableKHR %ptr_uniformconstant UniformConstant
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%buffer_ptr = OpUntypedAccessChainKHR %ptr_uniformconstant %storage_buffer_array_type %resource_heap %int_0
+%buffer_data_ptr = OpBufferPointerEXT %_ptr_Uniform_Struct %buffer_ptr
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Structure member 1 at offset 4 is not aligned to 16"));
+}
+
+TEST_F(ValidateExplicitLayout, BufferPointerEXTArrayResultBadStrideBad) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpCapability DescriptorHeapEXT
+OpExtension "SPV_KHR_untyped_pointers"
+OpExtension "SPV_EXT_descriptor_heap"
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main" %resource_heap
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %resource_heap BuiltIn ResourceHeapEXT
+OpDecorate %storage_buffer_array_type ArrayStride 16
+OpDecorate %array ArrayStride 2
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%float = OpTypeFloat 32
+%int_0 = OpConstant %int 0
+%int_4 = OpConstant %int 4
+%array = OpTypeArray %float %int_4
+%storage_buffer_type = OpTypeBufferEXT StorageBuffer
+%storage_buffer_array_type = OpTypeRuntimeArray %storage_buffer_type
+%_ptr_StorageBuffer_array = OpTypePointer StorageBuffer %array
+%ptr_uniformconstant = OpTypeUntypedPointerKHR UniformConstant
+%resource_heap = OpUntypedVariableKHR %ptr_uniformconstant UniformConstant
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%buffer_ptr = OpUntypedAccessChainKHR %ptr_uniformconstant %storage_buffer_array_type %resource_heap %int_0
+%buffer_data_ptr = OpBufferPointerEXT %_ptr_StorageBuffer_array %buffer_ptr
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Array stride 2 must satisfy alignment 4"));
+}
+
+TEST_F(ValidateExplicitLayout, StructOffsetIdEXTSamplerHeapGood) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpCapability DescriptorHeapEXT
+OpExtension "SPV_KHR_untyped_pointers"
+OpExtension "SPV_EXT_descriptor_heap"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main" %resource_heap
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %resource_heap BuiltIn SamplerHeapEXT
+OpMemberDecorateIdEXT %Struct 0 OffsetIdEXT %int_0
+OpMemberDecorateIdEXT %Struct 1 OffsetIdEXT %int_4
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_4 = OpConstant %int 4
+%sampler = OpTypeSampler
+%Struct = OpTypeStruct %sampler %int
+%ptr_uniformconstant = OpTypeUntypedPointerKHR UniformConstant
+%resource_heap = OpUntypedVariableKHR %ptr_uniformconstant UniformConstant
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = OpUntypedAccessChainKHR %ptr_uniformconstant %Struct %resource_heap %int_0
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+}
+
+TEST_F(ValidateExplicitLayout, StructOffsetIdEXTSamplerHeapUnalignedBad) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpCapability DescriptorHeapEXT
+OpExtension "SPV_KHR_untyped_pointers"
+OpExtension "SPV_EXT_descriptor_heap"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main" %resource_heap
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %resource_heap BuiltIn SamplerHeapEXT
+OpMemberDecorateIdEXT %Struct 0 OffsetIdEXT %int_0
+OpMemberDecorateIdEXT %Struct 1 OffsetIdEXT %int_2
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_2 = OpConstant %int 2
+%sampler = OpTypeSampler
+%Struct = OpTypeStruct %sampler %int
+%ptr_uniformconstant = OpTypeUntypedPointerKHR UniformConstant
+%resource_heap = OpUntypedVariableKHR %ptr_uniformconstant UniformConstant
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = OpUntypedAccessChainKHR %ptr_uniformconstant %Struct %resource_heap %int_0
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Structure member 1 at offset 2 is not aligned to 4"));
+}
+
+TEST_F(ValidateExplicitLayout, StructOffsetIdEXTResourceHeapOverlapBad) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpCapability DescriptorHeapEXT
+OpCapability SampledBuffer
+OpExtension "SPV_KHR_untyped_pointers"
+OpExtension "SPV_EXT_descriptor_heap"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main" %resource_heap
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %resource_heap BuiltIn ResourceHeapEXT
+OpMemberDecorateIdEXT %Struct 0 OffsetIdEXT %int_0
+OpMemberDecorateIdEXT %Struct 1 OffsetIdEXT %int_4
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_4 = OpConstant %int 4
+%image = OpTypeImage %int Buffer 0 0 0 2 R32ui
+%Struct = OpTypeStruct %image %int
+%ptr_uniformconstant = OpTypeUntypedPointerKHR UniformConstant
+%resource_heap = OpUntypedVariableKHR %ptr_uniformconstant UniformConstant
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = OpUntypedAccessChainKHR %ptr_uniformconstant %Struct %resource_heap %int_0
+OpReturn
+OpFunctionEnd
+)";
+
+  options_->image_descriptor_layout.size = 8;
+  options_->image_descriptor_layout.alignment = 8;
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Structure member 1 at offset 4 overlaps previous "
+                        "member ending at offset 7"));
+}
+
+TEST_F(ValidateExplicitLayout, StructOffsetIdEXTResourceHeapOverlapSpecId) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpCapability DescriptorHeapEXT
+OpCapability SampledBuffer
+OpExtension "SPV_KHR_untyped_pointers"
+OpExtension "SPV_EXT_descriptor_heap"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main" %resource_heap
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %resource_heap BuiltIn ResourceHeapEXT
+OpMemberDecorateIdEXT %Struct 0 OffsetIdEXT %int_0
+OpMemberDecorateIdEXT %Struct 1 OffsetIdEXT %int_4
+OpDecorate %int_4 SpecId 1
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_4 = OpSpecConstant %int 4
+%image = OpTypeImage %int Buffer 0 0 0 2 R32ui
+%Struct = OpTypeStruct %image %int
+%ptr_uniformconstant = OpTypeUntypedPointerKHR UniformConstant
+%resource_heap = OpUntypedVariableKHR %ptr_uniformconstant UniformConstant
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = OpUntypedAccessChainKHR %ptr_uniformconstant %Struct %resource_heap %int_0
+OpReturn
+OpFunctionEnd
+)";
+
+  options_->image_descriptor_layout.size = 8;
+  options_->image_descriptor_layout.alignment = 8;
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+}
+
+TEST_F(ValidateExplicitLayout, ArrayStrideIdEXTSamplerHeapGood) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpCapability DescriptorHeapEXT
+OpExtension "SPV_KHR_untyped_pointers"
+OpExtension "SPV_EXT_descriptor_heap"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main" %resource_heap
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %resource_heap BuiltIn SamplerHeapEXT
+OpDecorateId %array ArrayStrideIdEXT %int_4
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_4 = OpConstant %int 4
+%int_10 = OpConstant %int 10
+%sampler = OpTypeSampler
+%array = OpTypeArray %sampler %int_10
+%ptr_uniformconstant = OpTypeUntypedPointerKHR UniformConstant
+%resource_heap = OpUntypedVariableKHR %ptr_uniformconstant UniformConstant
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = OpUntypedAccessChainKHR %ptr_uniformconstant %array %resource_heap %int_0
+OpReturn
+OpFunctionEnd
+)";
+
+  options_->sampler_descriptor_layout.size = 4;
+  options_->sampler_descriptor_layout.alignment = 4;
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+}
+
+TEST_F(ValidateExplicitLayout, ArrayStrideIdEXTSamplerHeapTooSmallBad) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpCapability DescriptorHeapEXT
+OpExtension "SPV_KHR_untyped_pointers"
+OpExtension "SPV_EXT_descriptor_heap"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main" %resource_heap
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %resource_heap BuiltIn SamplerHeapEXT
+OpDecorateId %array ArrayStrideIdEXT %int_2
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_2 = OpConstant %int 2
+%int_10 = OpConstant %int 10
+%sampler = OpTypeSampler
+%array = OpTypeArray %sampler %int_10
+%ptr_uniformconstant = OpTypeUntypedPointerKHR UniformConstant
+%resource_heap = OpUntypedVariableKHR %ptr_uniformconstant UniformConstant
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = OpUntypedAccessChainKHR %ptr_uniformconstant %array %resource_heap %int_0
+OpReturn
+OpFunctionEnd
+)";
+
+  options_->sampler_descriptor_layout.size = 4;
+  options_->sampler_descriptor_layout.alignment = 2;
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Array stride 2 is smaller than element type size 4"));
+}
+
+TEST_F(ValidateExplicitLayout,
+       ArrayStrideIdEXTSamplerHeapTooSmallSpecConstant) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpCapability DescriptorHeapEXT
+OpExtension "SPV_KHR_untyped_pointers"
+OpExtension "SPV_EXT_descriptor_heap"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main" %resource_heap
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %resource_heap BuiltIn SamplerHeapEXT
+OpDecorateId %array ArrayStrideIdEXT %int_2
+OpDecorate %int_2 SpecId 1
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_2 = OpSpecConstant %int 2
+%int_10 = OpConstant %int 10
+%sampler = OpTypeSampler
+%array = OpTypeArray %sampler %int_10
+%ptr_uniformconstant = OpTypeUntypedPointerKHR UniformConstant
+%resource_heap = OpUntypedVariableKHR %ptr_uniformconstant UniformConstant
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = OpUntypedAccessChainKHR %ptr_uniformconstant %array %resource_heap %int_0
+OpReturn
+OpFunctionEnd
+)";
+
+  options_->sampler_descriptor_layout.size = 4;
+  options_->sampler_descriptor_layout.alignment = 2;
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+}
+
+TEST_F(ValidateExplicitLayout, ImageArraySpecConstant) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpCapability DescriptorHeapEXT
+OpCapability ImageBuffer
+OpExtension "SPV_KHR_untyped_pointers"
+OpExtension "SPV_EXT_descriptor_heap"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %1 "main" %2
+OpExecutionMode %1 LocalSize 1 1 1
+OpDecorate %2 BuiltIn ResourceHeapEXT
+OpDecorate %3 SpecId 0
+OpDecorateId %4 ArrayStrideIdEXT %3
+%5 = OpTypeVoid
+%6 = OpTypeFunction %5
+%7 = OpTypeInt 32 0
+%8 = OpConstant %7 0
+%9 = OpConstant %7 2
+%10 = OpConstant %7 51966
+%11 = OpConstant %7 0
+%3 = OpSpecConstant %7 0
+%12 = OpTypeUntypedPointerKHR UniformConstant
+%2 = OpUntypedVariableKHR %12 UniformConstant
+%13 = OpTypeImage %7 Buffer 0 0 0 2 R32ui
+%4 = OpTypeRuntimeArray %13
+%1 = OpFunction %5 None %6
+%14 = OpLabel
+%15 = OpUntypedAccessChainKHR %12 %4 %2 %9
+%16 = OpLoad %13 %15
+OpImageWrite %16 %8 %10
+OpReturn
+OpFunctionEnd
+)";
+
+  options_->image_descriptor_layout.size = 64;
+  options_->image_descriptor_layout.alignment = 64;
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+}
+
+TEST_F(ValidateExplicitLayout, CheckNoLayoutWithOffsetIdEXTBad) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpCapability DescriptorHeapEXT
+OpExtension "SPV_KHR_untyped_pointers"
+OpExtension "SPV_EXT_descriptor_heap"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpMemberDecorateIdEXT %Struct 0 OffsetIdEXT %int_0
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%sampler = OpTypeSampler
+%Struct = OpTypeStruct %sampler %int
+%_ptr_Function_Struct = OpTypePointer Function %Struct
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%var = OpVariable %_ptr_Function_Struct Function
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Invalid explicit layout decorations on type"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("has an explicit layout from the OffsetIdEXT decoration"));
+}
+
+TEST_F(ValidateExplicitLayout, CheckNoLayoutWithArrayStrideIdEXTBad) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpCapability DescriptorHeapEXT
+OpExtension "SPV_KHR_untyped_pointers"
+OpExtension "SPV_EXT_descriptor_heap"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorateId %array ArrayStrideIdEXT %int_4
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_4 = OpConstant %int 4
+%int_10 = OpConstant %int 10
+%sampler = OpTypeSampler
+%array = OpTypeArray %sampler %int_10
+%_ptr_Workgroup_array = OpTypePointer Workgroup %array
+%var = OpVariable %_ptr_Workgroup_array Workgroup
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Invalid explicit layout decorations on type"));
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("has an explicit layout from the ArrayStrideIdEXT decoration"));
+}
+
+TEST_F(ValidateExplicitLayout, DescriptorArrayWithArrayStrideBad) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability DescriptorHeapEXT
+OpExtension "SPV_EXT_descriptor_heap"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+OpDecorate %array ArrayStride 16
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_4 = OpConstant %int 4
+%sampler = OpTypeSampler
+%array = OpTypeArray %sampler %int_4
+%_ptr_UniformConstant_array = OpTypePointer UniformConstant %array
+%var = OpVariable %_ptr_UniformConstant_array UniformConstant
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Invalid explicit layout decorations on type"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("the UniformConstant storage class has an explicit "
+                        "layout from the ArrayStride decoration"));
+}
+
+TEST_F(ValidateExplicitLayout, DescriptorArrayWithArrayStrideIdEXTBad) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpCapability DescriptorHeapEXT
+OpCapability SampledBuffer
+OpExtension "SPV_KHR_untyped_pointers"
+OpExtension "SPV_EXT_descriptor_heap"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main" %var
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+OpDecorateId %array ArrayStrideIdEXT %int_8
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_4 = OpConstant %int 4
+%int_8 = OpConstant %int 8
+%image = OpTypeImage %int Buffer 0 0 0 2 R32ui
+%array = OpTypeArray %image %int_4
+%_ptr_UniformConstant_array = OpTypePointer UniformConstant %array
+%var = OpVariable %_ptr_UniformConstant_array UniformConstant
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Invalid explicit layout decorations on type"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("the UniformConstant storage class has an explicit "
+                        "layout from the ArrayStrideIdEXT decoration"));
+}
+
+TEST_F(ValidateExplicitLayout, ResourceHeapMissingArrayStride) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpCapability DescriptorHeapEXT
+OpExtension "SPV_EXT_descriptor_heap"
+OpExtension "SPV_KHR_untyped_pointers"
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main" %resource_heap %_
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %resource_heap BuiltIn ResourceHeapEXT
+OpDecorate %Heap Block
+OpMemberDecorate %Heap 0 Offset 0
+OpDecorate %UBO Block
+OpMemberDecorate %UBO 0 Offset 0
+OpDecorate %_ Binding 0
+OpDecorate %_ DescriptorSet 0
+%void = OpTypeVoid
+%3 = OpTypeFunction %void
+%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant
+%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant
+%int = OpTypeInt 32 1
+%int_3 = OpConstant %int 3
+%uint = OpTypeInt 32 0
+%Heap = OpTypeStruct %uint
+%int_0 = OpConstant %int 0
+%uint_0 = OpConstant %uint 0
+%_ptr_StorageBuffer = OpTypeUntypedPointerKHR StorageBuffer
+%16 = OpTypeBufferEXT StorageBuffer
+%17 = OpConstantSizeOfEXT %int %16
+%_runtimearr_16 = OpTypeRuntimeArray %16
+%UBO = OpTypeStruct %uint
+%_ptr_Uniform_UBO = OpTypePointer Uniform %UBO
+%_ = OpVariable %_ptr_Uniform_UBO Uniform
+%main = OpFunction %void None %3
+%5 = OpLabel
+%15 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_16 %resource_heap %int_3
+%19 = OpBufferPointerEXT %_ptr_StorageBuffer %15
+%20 = OpUntypedAccessChainKHR %_ptr_StorageBuffer %Heap %19 %int_0
+OpStore %20 %uint_0
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Array must be explicitly laid out with ArrayStride or "
+                        "ArrayStrideIdEXT decorations"));
+}
+
+TEST_F(ValidateExplicitLayout, SamplerDescriptorLayoutSamplerHeapGood) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpCapability DescriptorHeapEXT
+OpExtension "SPV_KHR_untyped_pointers"
+OpExtension "SPV_EXT_descriptor_heap"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main" %resource_heap
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %resource_heap BuiltIn SamplerHeapEXT
+OpDecorate %Struct Block
+OpMemberDecorate %Struct 0 Offset 0
+OpMemberDecorate %Struct 1 Offset 8
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%sampler = OpTypeSampler
+%Struct = OpTypeStruct %sampler %int
+%ptr_uniformconstant = OpTypeUntypedPointerKHR UniformConstant
+%resource_heap = OpUntypedVariableKHR %ptr_uniformconstant UniformConstant
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = OpUntypedAccessChainKHR %ptr_uniformconstant %Struct %resource_heap %int_0
+OpReturn
+OpFunctionEnd
+)";
+
+  options_->sampler_descriptor_layout.size = 8;
+  options_->sampler_descriptor_layout.alignment = 8;
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+}
+
+TEST_F(ValidateExplicitLayout, ImageDescriptorLayoutResourceHeapGood) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpCapability DescriptorHeapEXT
+OpCapability SampledBuffer
+OpExtension "SPV_KHR_untyped_pointers"
+OpExtension "SPV_EXT_descriptor_heap"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main" %resource_heap
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %resource_heap BuiltIn ResourceHeapEXT
+OpDecorate %array ArrayStride 16
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_4 = OpConstant %int 4
+%image = OpTypeImage %int Buffer 0 0 0 2 R32ui
+%array = OpTypeArray %image %int_4
+%ptr_uniformconstant = OpTypeUntypedPointerKHR UniformConstant
+%resource_heap = OpUntypedVariableKHR %ptr_uniformconstant UniformConstant
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = OpUntypedAccessChainKHR %ptr_uniformconstant %array %resource_heap %int_0
+OpReturn
+OpFunctionEnd
+)";
+
+  options_->image_descriptor_layout.size = 16;
+  options_->image_descriptor_layout.alignment = 16;
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+}
+
+TEST_F(ValidateExplicitLayout, BufferDescriptorLayoutResourceHeapTooSmallBad) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpCapability DescriptorHeapEXT
+OpExtension "SPV_KHR_untyped_pointers"
+OpExtension "SPV_EXT_descriptor_heap"
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main" %resource_heap
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %resource_heap BuiltIn ResourceHeapEXT
+OpDecorate %array ArrayStride 8
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_4 = OpConstant %int 4
+%buffer = OpTypeBufferEXT StorageBuffer
+%array = OpTypeArray %buffer %int_4
+%ptr_uniformconstant = OpTypeUntypedPointerKHR UniformConstant
+%resource_heap = OpUntypedVariableKHR %ptr_uniformconstant UniformConstant
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = OpUntypedAccessChainKHR %ptr_uniformconstant %array %resource_heap %int_0
+OpReturn
+OpFunctionEnd
+)";
+
+  options_->buffer_descriptor_layout.size = 16;
+  options_->buffer_descriptor_layout.alignment = 8;
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Array stride 8 is smaller than element type size 16"));
+}
+
+TEST_F(ValidateExplicitLayout, BindlessTextureNVSamplerHeapUnalignedBad) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability UntypedPointersKHR
+OpCapability DescriptorHeapEXT
+OpCapability BindlessTextureNV
+OpExtension "SPV_KHR_untyped_pointers"
+OpExtension "SPV_EXT_descriptor_heap"
+OpExtension "SPV_NV_bindless_texture"
+OpMemoryModel Logical GLSL450
+OpSamplerImageAddressingModeNV 64
+OpEntryPoint GLCompute %main "main" %resource_heap
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %resource_heap BuiltIn SamplerHeapEXT
+OpDecorate %Struct Block
+OpMemberDecorate %Struct 0 Offset 4
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%sampler = OpTypeSampler
+%Struct = OpTypeStruct %sampler
+%ptr_uniformconstant = OpTypeUntypedPointerKHR UniformConstant
+%resource_heap = OpUntypedVariableKHR %ptr_uniformconstant UniformConstant
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%gep = OpUntypedAccessChainKHR %ptr_uniformconstant %Struct %resource_heap %int_0
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_4);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_4));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Structure member 0 at offset 4 is not aligned to 8"));
+}
+
+TEST_F(ValidateExplicitLayout,
+       StorageBufferPtrInFunctionVariableWithArrayStrideGood) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointersStorageBuffer
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %ptr_sb ArrayStride 8
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%float = OpTypeFloat 32
+%ptr_sb = OpTypePointer StorageBuffer %float
+%_ptr_Function_ptr_sb = OpTypePointer Function %ptr_sb
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%var = OpVariable %_ptr_Function_ptr_sb Function
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+}
+
+TEST_F(ValidateExplicitLayout, NonPointerInFunctionVariableWithArrayStrideBad) {
+  const std::string spirv = R"(
+OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main"
+OpExecutionMode %main LocalSize 1 1 1
+OpDecorate %array ArrayStride 4
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%float = OpTypeFloat 32
+%int_4 = OpConstant %int 4
+%array = OpTypeArray %float %int_4
+%_ptr_Function_array = OpTypePointer Function %array
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+%var = OpVariable %_ptr_Function_array Function
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_3);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_3));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("Invalid explicit layout decorations on type"));
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("the Function storage class has an explicit layout "
+                        "from the ArrayStride decoration"));
+}
+
+using UntypedPointerLayout =
+    spvtest::ValidateBase<std::tuple<std::string, std::string>>;
+
+TEST_P(UntypedPointerLayout, BadOffset) {
+  const auto sc = std::get<0>(GetParam());
+  const auto op = std::get<1>(GetParam());
+  const std::string set = (sc == "StorageBuffer" || sc == "Uniform"
+                               ? R"(OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+)"
+                               : R"()");
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpCapability UntypedPointersKHR
+OpCapability WorkgroupMemoryExplicitLayoutKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpExtension "SPV_KHR_variable_pointers"
+OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main" %var
+OpExecutionMode %main LocalSize 1 1 1
+OpName %var "var"
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Offset 0
+OpMemberDecorate %struct 1 Offset 4
+)" + set + R"(OpMemberDecorate %test_type 0 Offset 0
+OpMemberDecorate %test_type 1 Offset 1
+OpDecorate %ptr ArrayStride 16
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%struct = OpTypeStruct %int %int
+%test_type = OpTypeStruct %int %int
+%test_val = OpConstantNull %test_type
+%ptr = OpTypeUntypedPointerKHR )" +
+                            sc + R"(
+%var = OpUntypedVariableKHR %ptr )" +
+                            sc + R"( %struct
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+)" + op + R"(
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+  const bool read_only = sc == "Uniform" || sc == "PushConstant";
+  if (!read_only || op.find("OpStore") == std::string::npos) {
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("member 1 at offset 1 is not aligned to"));
+  }
+}
+
+TEST_P(UntypedPointerLayout, BadStride_TooSmall) {
+  const auto sc = std::get<0>(GetParam());
+  const auto op = std::get<1>(GetParam());
+  const std::string set = (sc == "StorageBuffer" || sc == "Uniform"
+                               ? R"(OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+)"
+                               : R"()");
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpCapability UntypedPointersKHR
+OpCapability WorkgroupMemoryExplicitLayoutKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpExtension "SPV_KHR_variable_pointers"
+OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main" %var
+OpExecutionMode %main LocalSize 1 1 1
+OpName %var "var"
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Offset 0
+OpMemberDecorate %struct 1 Offset 4
+)" + set + R"(OpDecorate %test_type ArrayStride 4
+OpDecorate %ptr ArrayStride 16
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_4 = OpConstant %int 4
+%int4 = OpTypeVector %int 4
+%test_type = OpTypeArray %int4 %int_4
+%test_val = OpConstantNull %test_type
+%struct = OpTypeStruct %int %int
+%ptr = OpTypeUntypedPointerKHR )" +
+                            sc + R"(
+%var = OpUntypedVariableKHR %ptr )" +
+                            sc + R"( %struct
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+)" + op + R"(
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+  const bool read_only = sc == "Uniform" || sc == "PushConstant";
+  if (sc == "Uniform") {
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("Array stride 4 must satisfy alignment 16"));
+  } else if (!read_only || op.find("OpStore") == std::string::npos) {
+    EXPECT_THAT(
+        getDiagnosticString(),
+        HasSubstr("Array stride 4 is smaller than element type size 16"));
+  }
+}
+
+TEST_P(UntypedPointerLayout, BadStride_Unaligned) {
+  const auto sc = std::get<0>(GetParam());
+  const auto op = std::get<1>(GetParam());
+  const std::string set = (sc == "StorageBuffer" || sc == "Uniform"
+                               ? R"(OpDecorate %var DescriptorSet 0
+OpDecorate %var Binding 0
+)"
+                               : R"()");
+  const std::string spirv = R"(
+OpCapability Shader
+OpCapability VariablePointers
+OpCapability UntypedPointersKHR
+OpCapability WorkgroupMemoryExplicitLayoutKHR
+OpExtension "SPV_KHR_untyped_pointers"
+OpExtension "SPV_KHR_variable_pointers"
+OpExtension "SPV_KHR_workgroup_memory_explicit_layout"
+OpExtension "SPV_KHR_storage_buffer_storage_class"
+OpMemoryModel Logical GLSL450
+OpEntryPoint GLCompute %main "main" %var
+OpExecutionMode %main LocalSize 1 1 1
+OpName %var "var"
+OpDecorate %struct Block
+OpMemberDecorate %struct 0 Offset 0
+OpMemberDecorate %struct 1 Offset 4
+)" + set + R"(OpDecorate %test_type ArrayStride 15
+OpDecorate %ptr ArrayStride 16
+%void = OpTypeVoid
+%int = OpTypeInt 32 0
+%int_0 = OpConstant %int 0
+%int_4 = OpConstant %int 4
+%int4 = OpTypeVector %int 4
+%test_type = OpTypeArray %int4 %int_4
+%test_val = OpConstantNull %test_type
+%struct = OpTypeStruct %int %int
+%ptr = OpTypeUntypedPointerKHR )" +
+                            sc + R"(
+%var = OpUntypedVariableKHR %ptr )" +
+                            sc + R"( %struct
+%void_fn = OpTypeFunction %void
+%main = OpFunction %void None %void_fn
+%entry = OpLabel
+)" + op + R"(
+OpReturn
+OpFunctionEnd
+)";
+
+  CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_2);
+  EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_2));
+  const bool read_only = sc == "Uniform" || sc == "PushConstant";
+  if (sc == "Uniform") {
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("Array stride 15 must satisfy alignment 16"));
+  } else if (!read_only || op.find("OpStore") == std::string::npos) {
+    EXPECT_THAT(getDiagnosticString(),
+                HasSubstr("Array stride 15 must satisfy alignment 4"));
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    ValidateUntypedPointerLayout, UntypedPointerLayout,
+    Combine(Values("StorageBuffer", "Uniform", "PushConstant", "Workgroup"),
+            Values("%gep = OpUntypedAccessChainKHR %ptr %test_type %var %int_0",
+                   "%gep = OpUntypedInBoundsAccessChainKHR %ptr %test_type "
+                   "%var %int_0",
+                   "%gep = OpUntypedPtrAccessChainKHR %ptr %test_type %var "
+                   "%int_0 %int_0",
+                   "%gep = OpUntypedInBoundsPtrAccessChainKHR %ptr %test_type "
+                   "%var %int_0 %int_0",
+                   "%ld = OpLoad %test_type %var", "OpStore %var %test_val")));
+
+}  // namespace
+}  // namespace val
+}  // namespace spvtools
diff --git a/test/val/val_extension_spv_ext_descriptor_heap.cpp b/test/val/val_extension_spv_ext_descriptor_heap.cpp
index e8b0b01..9c56fbc 100644
--- a/test/val/val_extension_spv_ext_descriptor_heap.cpp
+++ b/test/val/val_extension_spv_ext_descriptor_heap.cpp
@@ -1228,8 +1228,9 @@
        %uint = OpTypeInt 32 0
 %_ptr_Output_uint = OpTypePointer Output %uint
           %o = OpVariable %_ptr_Output_uint Output
-%_ptr_Uniform = OpTypeUntypedPointerKHR UniformConstant
-%resource_heap = OpUntypedVariableKHR %_ptr_Uniform UniformConstant
+%_ptr_Uniform = OpTypeUntypedPointerKHR Uniform
+%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant
+%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant
         %int = OpTypeInt 32 1
       %int_9 = OpConstant %int 9
           %U = OpTypeStruct %uint
@@ -1239,7 +1240,7 @@
 %_runtimearr_17 = OpTypeRuntimeArray %17
        %main = OpFunction %void None %3
           %5 = OpLabel
-         %16 = OpUntypedAccessChainKHR %_ptr_Uniform %_runtimearr_17 %resource_heap %int_9
+         %16 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_17 %resource_heap %int_9
          %20 = OpBufferPointerEXT %_ptr_Uniform %16
          %21 = OpUntypedAccessChainKHR %_ptr_Uniform %U %20 %int_0
          %22 = OpLoad %uint %21
@@ -1819,15 +1820,15 @@
                OpExecutionMode %1 LocalSize 1 1 1
                OpDecorate %2 BuiltIn ResourceHeapEXT
                OpMemberDecorate %struct 0 Offset 0
-               OpMemberDecorateIdEXT %struct 1 OffsetIdEXT %uint_0
+               OpMemberDecorateIdEXT %struct 1 OffsetIdEXT %uint_4
                OpMemberDecorate %image_struct 0 Offset 0
        %void = OpTypeVoid
           %7 = OpTypeFunction %void
        %uint = OpTypeInt 32 0
      %uint_0 = OpConstant %uint 0
      %uint_1 = OpConstant %uint 1
+     %uint_4 = OpConstant %uint 4
  %uint_51966 = OpConstant %uint 51966
-   %uint_0_0 = OpConstant %uint 0
 %_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant
           %2 = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant
          %14 = OpTypeImage %uint Buffer 0 0 0 2 R32ui
diff --git a/test/val/val_validation_state_test.cpp b/test/val/val_validation_state_test.cpp
index a5e88de..05a5de4 100644
--- a/test/val/val_validation_state_test.cpp
+++ b/test/val/val_validation_state_test.cpp
@@ -232,6 +232,31 @@
   EXPECT_EQ(100u, options_->universal_limits_.max_access_chain_indexes);
 }
 
+TEST_F(ValidationStateTest, CheckDescriptorHeapLayoutOptions) {
+  EXPECT_EQ(0u, options_->buffer_descriptor_layout.size);
+  EXPECT_EQ(1u, options_->buffer_descriptor_layout.alignment);
+  EXPECT_EQ(0u, options_->image_descriptor_layout.size);
+  EXPECT_EQ(1u, options_->image_descriptor_layout.alignment);
+  EXPECT_EQ(0u, options_->sampler_descriptor_layout.size);
+  EXPECT_EQ(1u, options_->sampler_descriptor_layout.alignment);
+  EXPECT_EQ(0u, options_->tensor_descriptor_layout.size);
+  EXPECT_EQ(1u, options_->tensor_descriptor_layout.alignment);
+
+  spvValidatorOptionsSetBufferDescriptorLayout(options_, 16u, 8u);
+  spvValidatorOptionsSetImageDescriptorLayout(options_, 64u, 16u);
+  spvValidatorOptionsSetSamplerDescriptorLayout(options_, 32u, 8u);
+  spvValidatorOptionsSetTensorDescriptorLayout(options_, 128u, 32u);
+
+  EXPECT_EQ(16u, options_->buffer_descriptor_layout.size);
+  EXPECT_EQ(8u, options_->buffer_descriptor_layout.alignment);
+  EXPECT_EQ(64u, options_->image_descriptor_layout.size);
+  EXPECT_EQ(16u, options_->image_descriptor_layout.alignment);
+  EXPECT_EQ(32u, options_->sampler_descriptor_layout.size);
+  EXPECT_EQ(8u, options_->sampler_descriptor_layout.alignment);
+  EXPECT_EQ(128u, options_->tensor_descriptor_layout.size);
+  EXPECT_EQ(32u, options_->tensor_descriptor_layout.alignment);
+}
+
 TEST_F(ValidationStateTest, CheckNonRecursiveBodyGood) {
   std::string spirv = std::string(kHeader) + kNonRecursiveBody;
   CompileSuccessfully(spirv);
diff --git a/tools/val/val.cpp b/tools/val/val.cpp
index 377fd0b..6729930 100644
--- a/tools/val/val.cpp
+++ b/tools/val/val.cpp
@@ -76,6 +76,10 @@
                                    not be allowed by the target environment.
   --before-hlsl-legalization       Allows code patterns that are intended to be
                                    fixed by spirv-opt's legalization passes.
+  --buffer-descriptor-layout       <size>:<align> Set size and alignment for buffer and acceleration structure descriptor heap resources.
+  --image-descriptor-layout        <size>:<align> Set size and alignment for image and sampled image descriptor heap resources.
+  --sampler-descriptor-layout      <size>:<align> Set size and alignment for sampler descriptor heap resources.
+  --tensor-descriptor-layout       <size>:<align> Set size and alignment for tensor descriptor heap resources.
   --version                        Display validator version information.
   --target-env                     {%s}
                                    Use validation rules from the specified environment.
@@ -222,6 +226,82 @@
         options.SetAllowVulkan32BitBitwise(true);
       } else if (0 == strcmp(cur_arg, "--relax-struct-store")) {
         options.SetRelaxStructStore(true);
+      } else if (0 == strcmp(cur_arg, "--buffer-descriptor-layout")) {
+        if (argi + 1 < argc) {
+          uint32_t size = 0, alignment = 0;
+          if (sscanf(argv[++argi], "%u:%u", &size, &alignment) == 2 &&
+              size > 0 && alignment > 0) {
+            options.SetBufferDescriptorLayout(size, alignment);
+          } else {
+            fprintf(stderr,
+                    "error: Invalid argument to --buffer-descriptor-layout "
+                    "(expected <size>:<align>)\n");
+            continue_processing = false;
+            return_code = 1;
+          }
+        } else {
+          fprintf(stderr,
+                  "error: Missing argument to --buffer-descriptor-layout\n");
+          continue_processing = false;
+          return_code = 1;
+        }
+      } else if (0 == strcmp(cur_arg, "--image-descriptor-layout")) {
+        if (argi + 1 < argc) {
+          uint32_t size = 0, alignment = 0;
+          if (sscanf(argv[++argi], "%u:%u", &size, &alignment) == 2 &&
+              size > 0 && alignment > 0) {
+            options.SetImageDescriptorLayout(size, alignment);
+          } else {
+            fprintf(stderr,
+                    "error: Invalid argument to --image-descriptor-layout "
+                    "(expected <size>:<align>)\n");
+            continue_processing = false;
+            return_code = 1;
+          }
+        } else {
+          fprintf(stderr,
+                  "error: Missing argument to --image-descriptor-layout\n");
+          continue_processing = false;
+          return_code = 1;
+        }
+      } else if (0 == strcmp(cur_arg, "--sampler-descriptor-layout")) {
+        if (argi + 1 < argc) {
+          uint32_t size = 0, alignment = 0;
+          if (sscanf(argv[++argi], "%u:%u", &size, &alignment) == 2 &&
+              size > 0 && alignment > 0) {
+            options.SetSamplerDescriptorLayout(size, alignment);
+          } else {
+            fprintf(stderr,
+                    "error: Invalid argument to --sampler-descriptor-layout "
+                    "(expected <size>:<align>)\n");
+            continue_processing = false;
+            return_code = 1;
+          }
+        } else {
+          fprintf(stderr,
+                  "error: Missing argument to --sampler-descriptor-layout\n");
+          continue_processing = false;
+          return_code = 1;
+        }
+      } else if (0 == strcmp(cur_arg, "--tensor-descriptor-layout")) {
+        if (argi + 1 < argc) {
+          uint32_t size = 0, alignment = 0;
+          if (sscanf(argv[++argi], "%u:%u", &size, &alignment) == 2 &&
+              size > 0 && alignment > 0) {
+            options.SetTensorDescriptorLayout(size, alignment);
+          } else {
+            fprintf(stderr,
+                    "error: Invalid argument to --tensor-descriptor-layout "
+                    "(expected <size>:<align>)\n");
+            continue_processing = false;
+            return_code = 1;
+          }
+        } else {
+          fprintf(stderr,
+                  "error: Missing argument to --tensor-descriptor-layout\n");
+          continue_processing = false;
+          return_code = 1;
+        }
       } else if (0 == cur_arg[1]) {
         // Setting a filename of "-" to indicate stdin.
         if (!inFile) {