HLSL-IR: Fix ArrayLengthFromUniform not applying

to arrayLength introduced by Robustness. As with AST, this transform
must run after Robustness, as the latter may add arrayLength calls.

Fixes dawn_end2end_tests:
ClampedOOBDynamicBufferOffsetTests.CheckOOBAccess/*

Bug: 375287154
Change-Id: I47d0ea5999d8d97c72365b4b993e571d6c4b5040
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/212318
Auto-Submit: Antonio Maiorano <amaiorano@google.com>
Reviewed-by: James Price <jrprice@google.com>
Commit-Queue: James Price <jrprice@google.com>
diff --git a/src/tint/lang/hlsl/writer/arraylength_test.cc b/src/tint/lang/hlsl/writer/arraylength_test.cc
index 4707eb5..ed6031f 100644
--- a/src/tint/lang/hlsl/writer/arraylength_test.cc
+++ b/src/tint/lang/hlsl/writer/arraylength_test.cc
@@ -228,5 +228,58 @@
 )");
 }
 
+TEST_F(HlslWriterTest, ArrayLength_Robustness) {
+    auto* dst = b.Var("dst", ty.ptr(storage, ty.array<u32>()));
+    dst->SetBindingPoint(0, 1);
+    b.ir.root_block->Append(dst);
+    auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+    b.Append(func->Block(), [&] {
+        auto* access = b.Access(ty.ptr(storage, ty.u32()), dst, 0_u);
+        b.Store(access, 123_u);
+        b.Return(func);
+    });
+
+    Options options;
+    options.disable_robustness = false;
+    ASSERT_TRUE(Generate(options)) << err_ << output_.hlsl;
+    EXPECT_EQ(output_.hlsl, R"(
+RWByteAddressBuffer dst : register(u1);
+void foo() {
+  uint v = 0u;
+  dst.GetDimensions(v);
+  dst.Store((0u + (uint(min(0u, ((v / 4u) - 1u))) * 4u)), 123u);
+}
+
+)");
+}
+
+TEST_F(HlslWriterTest, ArrayLength_RobustnessAndArrayLengthFromUniform) {
+    auto* dst = b.Var("dst", ty.ptr(storage, ty.array<u32>()));
+    dst->SetBindingPoint(0, 1);
+    b.ir.root_block->Append(dst);
+    auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
+    b.Append(func->Block(), [&] {
+        auto* access = b.Access(ty.ptr(storage, ty.u32()), dst, 0_u);
+        b.Store(access, 123_u);
+        b.Return(func);
+    });
+
+    Options options;
+    options.disable_robustness = false;
+    options.array_length_from_uniform.ubo_binding = {30, 0};
+    options.array_length_from_uniform.bindpoint_to_size_index[{0, 1}] = 0;
+    ASSERT_TRUE(Generate(options)) << err_ << output_.hlsl;
+    EXPECT_EQ(output_.hlsl, R"(
+RWByteAddressBuffer dst : register(u1);
+cbuffer cbuffer_tint_storage_buffer_sizes : register(b0, space30) {
+  uint4 tint_storage_buffer_sizes[1];
+};
+void foo() {
+  dst.Store((0u + (uint(min(0u, ((tint_storage_buffer_sizes[0u].x / 4u) - 1u))) * 4u)), 123u);
+}
+
+)");
+}
+
 }  // namespace
 }  // namespace tint::hlsl::writer
diff --git a/src/tint/lang/hlsl/writer/raise/raise.cc b/src/tint/lang/hlsl/writer/raise/raise.cc
index 53a75b1..e3b620e 100644
--- a/src/tint/lang/hlsl/writer/raise/raise.cc
+++ b/src/tint/lang/hlsl/writer/raise/raise.cc
@@ -76,17 +76,6 @@
     PopulateBindingRelatedOptions(options, remapper_data, multiplanar_map,
                                   array_length_from_uniform_options);
 
-    {
-        auto result = core::ir::transform::ArrayLengthFromUniform(
-            module,
-            BindingPoint{array_length_from_uniform_options.ubo_binding.group,
-                         array_length_from_uniform_options.ubo_binding.binding},
-            array_length_from_uniform_options.bindpoint_to_size_index);
-        if (result != Success) {
-            return result.Failure();
-        }
-    }
-
     RUN_TRANSFORM(core::ir::transform::BindingRemapper, module, remapper_data);
     RUN_TRANSFORM(core::ir::transform::MultiplanarExternalTexture, module, multiplanar_map);
 
@@ -159,6 +148,18 @@
         RUN_TRANSFORM(core::ir::transform::Robustness, module, config);
     }
 
+    // ArrayLengthFromUniform must run after Robustness, which introduces arrayLength calls.
+    {
+        auto result = core::ir::transform::ArrayLengthFromUniform(
+            module,
+            BindingPoint{array_length_from_uniform_options.ubo_binding.group,
+                         array_length_from_uniform_options.ubo_binding.binding},
+            array_length_from_uniform_options.bindpoint_to_size_index);
+        if (result != Success) {
+            return result.Failure();
+        }
+    }
+
     if (!options.disable_workgroup_init) {
         // Must run before ShaderIO as it may introduce a builtin parameter (local_invocation_index)
         RUN_TRANSFORM(core::ir::transform::ZeroInitWorkgroupMemory, module);
@@ -190,7 +191,6 @@
     }
 
     // TODO(dsinclair): TruncateInterstageVariables
-    // TODO(dsinclair): CalculateArrayLength
 
     // DemoteToHelper must come before any transform that introduces non-core instructions.
     // Run after ShaderIO to ensure the discards are added to the entry point it introduces.