spirv-reader: dereference the base pointer for access chain, if needed

Bug: tint:737
Change-Id: I36588ee54ea014e55d3dcc9a54e8d3835c1e83bc
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/53540
Auto-Submit: David Neto <dneto@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: James Price <jrprice@google.com>
Reviewed-by: James Price <jrprice@google.com>
diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc
index da4f579..4e7c4fa 100644
--- a/src/reader/spirv/function.cc
+++ b/src/reader/spirv/function.cc
@@ -3837,6 +3837,9 @@
   // for the base, and then bury that inside nested indexing expressions.
   if (!current_expr) {
     current_expr = MakeOperand(inst, 0);
+    if (current_expr.type->Is<Pointer>()) {
+      current_expr = Dereference(current_expr);
+    }
   }
   const auto constants = constant_mgr_->GetOperandConstants(&inst);
 
diff --git a/src/reader/spirv/function_memory_test.cc b/src/reader/spirv/function_memory_test.cc
index d564ce4..1eed4e5 100644
--- a/src/reader/spirv/function_memory_test.cc
+++ b/src/reader/spirv/function_memory_test.cc
@@ -820,6 +820,81 @@
                         "%60: %60 = OpTypePointer Workgroup %55"));
 }
 
+TEST_F(SpvParserMemoryTest, EmitStatement_AccessChain_DereferenceBase) {
+  // The base operand to OpAccessChain may have to be dereferenced first.
+  // crbug.com/tint/737
+  const std::string assembly = Preamble() + R"(
+     %void = OpTypeVoid
+     %voidfn = OpTypeFunction %void
+
+     %uint = OpTypeInt 32 0
+     %v2uint = OpTypeVector %uint 2
+     %elem_ty = OpTypePointer Private %uint
+     %vec_ty = OpTypePointer Private %v2uint
+
+     %ptrfn = OpTypeFunction %void %vec_ty
+
+     %uint_0 = OpConstant %uint 0
+
+     ; The shortest way to make a pointer example is as a function parameter.
+     %200 = OpFunction %void None %ptrfn
+     %1 = OpFunctionParameter %vec_ty
+     %entry = OpLabel
+     %2 = OpAccessChain %elem_ty %1 %uint_0
+     %3 = OpLoad %uint %2
+     OpReturn
+     OpFunctionEnd
+
+     %100 = OpFunction %void None %voidfn
+     %main_entry = OpLabel
+     OpReturn
+     OpFunctionEnd
+  )";
+  auto p = parser(test::Assemble(assembly));
+  std::cout << assembly << std::endl;
+  ASSERT_TRUE(p->BuildAndParseInternalModule());
+  const auto got = p->program().to_str();
+  const std::string expected = R"(Module{
+  Function x_200 -> __void
+  (
+    VariableConst{
+      x_1
+      none
+      undefined
+      __ptr_private__vec_2__u32
+    }
+  )
+  {
+    VariableDeclStatement{
+      VariableConst{
+        x_3
+        none
+        undefined
+        __u32
+        {
+          MemberAccessor[not set]{
+            UnaryOp[not set]{
+              indirection
+              Identifier[not set]{x_1}
+            }
+            Identifier[not set]{x}
+          }
+        }
+      }
+    }
+    Return{}
+  }
+  Function main -> __void
+  StageDecoration{vertex}
+  ()
+  {
+    Return{}
+  }
+}
+)";
+  EXPECT_EQ(got, expected) << got;
+}
+
 std::string OldStorageBufferPreamble() {
   return Preamble() + R"(
      OpName %myvar "myvar"