spirv-reader: fix emission of arrayLength

Pass a pointer argument instead of a reference argument.
Also handle the case where the argument is the result of
an OpCopyObject, which will generate a let-declaration of pointer
type.

Fixed: tint:1042
Change-Id: I25b1b7b95ade1b79130e51691194f32b3240e013
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/59451
Auto-Submit: David Neto <dneto@google.com>
Commit-Queue: James Price <jrprice@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: James Price <jrprice@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc
index 40332d6..f48940b 100644
--- a/src/reader/spirv/function.cc
+++ b/src/reader/spirv/function.cc
@@ -5752,23 +5752,21 @@
     return {};
   }
 
-  auto* member_ident = create<ast::IdentifierExpression>(
-      Source{}, builder_.Symbols().Register(field_name));
   auto member_expr = MakeExpression(struct_ptr_id);
   if (!member_expr) {
     return {};
   }
+  if (member_expr.type->Is<Pointer>()) {
+    member_expr = Dereference(member_expr);
+  }
+  auto* member_ident = create<ast::IdentifierExpression>(
+      Source{}, builder_.Symbols().Register(field_name));
   auto* member_access = create<ast::MemberAccessorExpression>(
       Source{}, member_expr.expr, member_ident);
 
   // Generate the intrinsic function call.
-  std::string call_ident_str = "arrayLength";
-  auto* call_ident = create<ast::IdentifierExpression>(
-      Source{}, builder_.Symbols().Register(call_ident_str));
-
-  ast::ExpressionList params{member_access};
   auto* call_expr =
-      create<ast::CallExpression>(Source{}, call_ident, std::move(params));
+      builder_.Call(Source{}, "arrayLength", builder_.AddressOf(member_access));
 
   return {parser_impl_.ConvertType(inst.type_id()), call_expr};
 }
diff --git a/src/reader/spirv/function_memory_test.cc b/src/reader/spirv/function_memory_test.cc
index e1c6dab..e8af52b 100644
--- a/src/reader/spirv/function_memory_test.cc
+++ b/src/reader/spirv/function_memory_test.cc
@@ -1326,7 +1326,7 @@
   )";
 }
 
-TEST_F(SpvParserMemoryTest, ArrayLength) {
+TEST_F(SpvParserMemoryTest, ArrayLength_FromVar) {
   const auto assembly = RuntimeArrayPreamble() + R"(
 
   %100 = OpFunction %void None %voidfn
@@ -1351,9 +1351,114 @@
       Call[not set]{
         Identifier[not set]{arrayLength}
         (
-          MemberAccessor[not set]{
-            Identifier[not set]{myvar}
-            Identifier[not set]{rtarr}
+          UnaryOp[not set]{
+            address-of
+            MemberAccessor[not set]{
+              Identifier[not set]{myvar}
+              Identifier[not set]{rtarr}
+            }
+          }
+        )
+      }
+    }
+  }
+}
+)")) << body_str;
+}
+
+TEST_F(SpvParserMemoryTest, ArrayLength_FromCopyObject) {
+  const auto assembly = RuntimeArrayPreamble() + R"(
+
+  %100 = OpFunction %void None %voidfn
+
+  %entry = OpLabel
+  %2 = OpCopyObject %ptr_struct %myvar
+  %1 = OpArrayLength %uint %2 1
+  OpReturn
+  OpFunctionEnd
+)";
+  auto p = parser(test::Assemble(assembly));
+  ASSERT_TRUE(p->BuildAndParseInternalModule()) << assembly << p->error();
+  auto fe = p->function_emitter(100);
+  EXPECT_TRUE(fe.EmitBody()) << p->error();
+  const auto body_str = ToString(p->builder(), fe.ast_body());
+  EXPECT_THAT(body_str, HasSubstr(R"(VariableDeclStatement{
+  VariableConst{
+    x_2
+    none
+    undefined
+    __ptr_storage__type_name_S
+    {
+      UnaryOp[not set]{
+        address-of
+        Identifier[not set]{myvar}
+      }
+    }
+  }
+}
+VariableDeclStatement{
+  VariableConst{
+    x_1
+    none
+    undefined
+    __u32
+    {
+      Call[not set]{
+        Identifier[not set]{arrayLength}
+        (
+          UnaryOp[not set]{
+            address-of
+            MemberAccessor[not set]{
+              UnaryOp[not set]{
+                indirection
+                Identifier[not set]{x_2}
+              }
+              Identifier[not set]{rtarr}
+            }
+          }
+        )
+      }
+    }
+  }
+}
+)")) << body_str;
+
+  p->SkipDumpingPending(
+      "crbug.com/tint/1041 track access mode in spirv-reader parser type");
+}
+
+TEST_F(SpvParserMemoryTest, ArrayLength_FromAccessChain) {
+  const auto assembly = RuntimeArrayPreamble() + R"(
+
+  %100 = OpFunction %void None %voidfn
+
+  %entry = OpLabel
+  %2 = OpAccessChain %ptr_struct %myvar ; no indices
+  %1 = OpArrayLength %uint %2 1
+  OpReturn
+  OpFunctionEnd
+)";
+  auto p = parser(test::Assemble(assembly));
+  ASSERT_TRUE(p->BuildAndParseInternalModule()) << assembly << p->error();
+  auto fe = p->function_emitter(100);
+  EXPECT_TRUE(fe.EmitBody()) << p->error();
+  const auto body_str = ToString(p->builder(), fe.ast_body());
+  EXPECT_THAT(body_str, HasSubstr(R"(VariableDeclStatement{
+  VariableConst{
+    x_1
+    none
+    undefined
+    __u32
+    {
+      Call[not set]{
+        Identifier[not set]{arrayLength}
+        (
+          UnaryOp[not set]{
+            address-of
+            MemberAccessor[not set]{
+              Identifier[not set]{myvar}
+              Identifier[not set]{rtarr}
+            }
           }
         )
       }