dawn-cmake/src/tint/writer/spirv/builder_accessor_expression_test.cc
Ben Clayton ab70ff7a2f tint/writer/spirv: Clean up accessor tests
Remove duplicate tests.
Use a common pattern of sanitizing, building and dumping the whole
module instead of calling individual methods. There's too much internal
state being tracked in the tests for my liking.

Split most tests into three:
* Const_* tests will check the emission of creation-time-constant
  expressions.
* Runtime_* tests will check the emission of runtime expressions, where
  indexing operates with a constant index.
* Dynamic_* tests will check the emission of runtime expressions, where
  indexing operates with a runtime index.

Bug: tint:1580
Change-Id: I0f92aaaa570cbd917d2b6937c6396605c6b542c9
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/94333
Reviewed-by: David Neto <dneto@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
2022-06-24 08:57:38 +00:00

1312 lines
36 KiB
C++

// Copyright 2020 The Tint Authors.
//
// 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 "src/tint/writer/spirv/spv_dump.h"
#include "src/tint/writer/spirv/test_helper.h"
using namespace tint::number_suffixes; // NOLINT
namespace tint::writer::spirv {
namespace {
using BuilderTest = TestHelper;
TEST_F(BuilderTest, Const_IndexAccessor_Vector) {
// let ary = vec3<i32>(1, 2, 3);
// var x = ary[1i];
auto* ary = Let("ary", nullptr, vec3<i32>(1_i, 2_i, 3_i));
auto* x = Var("x", nullptr, IndexAccessor(ary, 1_i));
WrapInFunction(ary, x);
spirv::Builder& b = SanitizeAndBuild();
ASSERT_TRUE(b.Build()) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
%1 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%5 = OpTypeVector %6 3
%7 = OpConstant %6 1
%8 = OpConstant %6 2
%9 = OpConstant %6 3
%10 = OpConstantComposite %5 %7 %8 %9
%12 = OpTypePointer Function %6
%13 = OpConstantNull %6
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%11 = OpVariable %12 Function %13
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %11 %8
OpReturn
)");
Validate(b);
}
TEST_F(BuilderTest, Runtime_IndexAccessor_Vector) {
// var ary : vec3<u32>;
// var x = ary[1i];
auto* ary = Var("ary", ty.vec3<u32>());
auto* x = Var("x", nullptr, IndexAccessor(ary, 1_i));
WrapInFunction(ary, x);
spirv::Builder& b = SanitizeAndBuild();
ASSERT_TRUE(b.Build()) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
%1 = OpTypeFunction %2
%8 = OpTypeInt 32 0
%7 = OpTypeVector %8 3
%6 = OpTypePointer Function %7
%9 = OpConstantNull %7
%10 = OpTypeInt 32 1
%11 = OpConstant %10 1
%12 = OpTypePointer Function %8
%16 = OpConstantNull %8
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %9
%15 = OpVariable %12 Function %16
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(%13 = OpAccessChain %12 %5 %11
%14 = OpLoad %8 %13
OpStore %15 %14
OpReturn
)");
Validate(b);
}
TEST_F(BuilderTest, Dynamic_IndexAccessor_Vector) {
// var ary : vec3<f32>;
// var idx : i32;
// var x = ary[idx];
auto* ary = Var("ary", ty.vec3<f32>());
auto* idx = Var("idx", ty.i32());
auto* x = Var("x", nullptr, IndexAccessor(ary, idx));
WrapInFunction(ary, idx, x);
spirv::Builder& b = SanitizeAndBuild();
ASSERT_TRUE(b.Build()) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
%1 = OpTypeFunction %2
%8 = OpTypeFloat 32
%7 = OpTypeVector %8 3
%6 = OpTypePointer Function %7
%9 = OpConstantNull %7
%12 = OpTypeInt 32 1
%11 = OpTypePointer Function %12
%13 = OpConstantNull %12
%15 = OpTypePointer Function %8
%19 = OpConstantNull %8
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %9
%10 = OpVariable %11 Function %13
%18 = OpVariable %15 Function %19
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(%14 = OpLoad %12 %10
%16 = OpAccessChain %15 %5 %14
%17 = OpLoad %8 %16
OpStore %18 %17
OpReturn
)");
Validate(b);
}
TEST_F(BuilderTest, Const_IndexAccessor_Vector2) {
// let ary : vec3<i32>(1, 2, 3);
// var x = ary[1i + 2i];
auto* ary = Let("ary", nullptr, vec3<i32>(1_i, 2_i, 3_i));
auto* x = Var("x", nullptr, IndexAccessor(ary, Add(1_i, 2_i)));
WrapInFunction(ary, x);
spirv::Builder& b = SanitizeAndBuild();
ASSERT_TRUE(b.Build()) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
%1 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%5 = OpTypeVector %6 3
%7 = OpConstant %6 1
%8 = OpConstant %6 2
%9 = OpConstant %6 3
%10 = OpConstantComposite %5 %7 %8 %9
%14 = OpTypePointer Function %6
%15 = OpConstantNull %6
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%13 = OpVariable %14 Function %15
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(%11 = OpIAdd %6 %7 %8
%12 = OpVectorExtractDynamic %6 %10 %11
OpStore %13 %12
OpReturn
)");
Validate(b);
}
TEST_F(BuilderTest, Runtime_IndexAccessor_Vector2) {
// var ary : vec3<f32>;
// var x = ary[1i + 2i];
auto* ary = Var("ary", ty.vec3<f32>());
auto* x = Var("x", nullptr, IndexAccessor(ary, Add(1_i, 2_i)));
WrapInFunction(ary, x);
spirv::Builder& b = SanitizeAndBuild();
ASSERT_TRUE(b.Build()) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
%1 = OpTypeFunction %2
%8 = OpTypeFloat 32
%7 = OpTypeVector %8 3
%6 = OpTypePointer Function %7
%9 = OpConstantNull %7
%10 = OpTypeInt 32 1
%11 = OpConstant %10 1
%12 = OpConstant %10 2
%14 = OpTypePointer Function %8
%18 = OpConstantNull %8
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %9
%17 = OpVariable %14 Function %18
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(%13 = OpIAdd %10 %11 %12
%15 = OpAccessChain %14 %5 %13
%16 = OpLoad %8 %15
OpStore %17 %16
OpReturn
)");
Validate(b);
}
TEST_F(BuilderTest, Dynamic_IndexAccessor_Vector2) {
// var ary : vec3<f32>;
// var one = 1i;
// var x = ary[one + 2i];
auto* ary = Var("ary", ty.vec3<f32>());
auto* one = Var("one", nullptr, Expr(1_i));
auto* x = Var("x", nullptr, IndexAccessor(ary, Add(one, 2_i)));
WrapInFunction(ary, one, x);
spirv::Builder& b = SanitizeAndBuild();
ASSERT_TRUE(b.Build()) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
%1 = OpTypeFunction %2
%8 = OpTypeFloat 32
%7 = OpTypeVector %8 3
%6 = OpTypePointer Function %7
%9 = OpConstantNull %7
%10 = OpTypeInt 32 1
%11 = OpConstant %10 1
%13 = OpTypePointer Function %10
%14 = OpConstantNull %10
%16 = OpConstant %10 2
%18 = OpTypePointer Function %8
%22 = OpConstantNull %8
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %9
%12 = OpVariable %13 Function %14
%21 = OpVariable %18 Function %22
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %12 %11
%15 = OpLoad %10 %12
%17 = OpIAdd %10 %15 %16
%19 = OpAccessChain %18 %5 %17
%20 = OpLoad %8 %19
OpStore %21 %20
OpReturn
)");
Validate(b);
}
TEST_F(BuilderTest, Const_IndexAccessor_Array_MultiLevel) {
// let ary = array<vec3<f32>, 2u>(vec3<f32>(1.0f, 2.0f, 3.0f), vec3<f32>(4.0f, 5.0f, 6.0f));
// var x = ary[1i][2i];
auto* ary =
Let("ary", nullptr,
array(ty.vec3<f32>(), 2_u, vec3<f32>(1._f, 2._f, 3._f), vec3<f32>(4._f, 5._f, 6._f)));
auto* x = Var("x", nullptr, IndexAccessor(IndexAccessor(ary, 1_i), 2_i));
WrapInFunction(ary, x);
spirv::Builder& b = SanitizeAndBuild();
ASSERT_TRUE(b.Build()) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
%1 = OpTypeFunction %2
%7 = OpTypeFloat 32
%6 = OpTypeVector %7 3
%8 = OpTypeInt 32 0
%9 = OpConstant %8 2
%5 = OpTypeArray %6 %9
%10 = OpConstant %7 1
%11 = OpConstant %7 2
%12 = OpConstant %7 3
%13 = OpConstantComposite %6 %10 %11 %12
%14 = OpConstant %7 4
%15 = OpConstant %7 5
%16 = OpConstant %7 6
%17 = OpConstantComposite %6 %14 %15 %16
%18 = OpConstantComposite %5 %13 %17
%19 = OpTypeInt 32 1
%20 = OpConstant %19 1
%22 = OpConstant %19 2
%25 = OpTypePointer Function %7
%26 = OpConstantNull %7
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%24 = OpVariable %25 Function %26
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(%21 = OpCompositeExtract %6 %18 1
%23 = OpCompositeExtract %7 %21 2
OpStore %24 %23
OpReturn
)");
Validate(b);
}
TEST_F(BuilderTest, Runtime_IndexAccessor_Array_MultiLevel) {
// var ary : array<vec3<f32>, 4u>;
// var x = ary[1i][2i];
auto* ary = Var("ary", ty.array(ty.vec3<f32>(), 4_u));
auto* x = Var("x", nullptr, IndexAccessor(IndexAccessor(ary, 1_i), 2_i));
WrapInFunction(ary, x);
spirv::Builder& b = SanitizeAndBuild();
ASSERT_TRUE(b.Build()) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
%1 = OpTypeFunction %2
%9 = OpTypeFloat 32
%8 = OpTypeVector %9 3
%10 = OpTypeInt 32 0
%11 = OpConstant %10 4
%7 = OpTypeArray %8 %11
%6 = OpTypePointer Function %7
%12 = OpConstantNull %7
%13 = OpTypeInt 32 1
%14 = OpConstant %13 1
%15 = OpConstant %13 2
%16 = OpTypePointer Function %9
%20 = OpConstantNull %9
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %12
%19 = OpVariable %16 Function %20
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(%17 = OpAccessChain %16 %5 %14 %15
%18 = OpLoad %9 %17
OpStore %19 %18
OpReturn
)");
Validate(b);
}
TEST_F(BuilderTest, Dynamic_IndexAccessor_Array_MultiLevel) {
// var ary : array<vec3<f32>, 4u>;
// var one = 1i;
// var x = ary[one][2i];
auto* ary = Var("ary", ty.array(ty.vec3<f32>(), 4_u));
auto* one = Var("one", nullptr, Expr(3_i));
auto* x = Var("x", nullptr, IndexAccessor(IndexAccessor(ary, one), 2_i));
WrapInFunction(ary, one, x);
spirv::Builder& b = SanitizeAndBuild();
ASSERT_TRUE(b.Build()) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
%1 = OpTypeFunction %2
%9 = OpTypeFloat 32
%8 = OpTypeVector %9 3
%10 = OpTypeInt 32 0
%11 = OpConstant %10 4
%7 = OpTypeArray %8 %11
%6 = OpTypePointer Function %7
%12 = OpConstantNull %7
%13 = OpTypeInt 32 1
%14 = OpConstant %13 3
%16 = OpTypePointer Function %13
%17 = OpConstantNull %13
%19 = OpConstant %13 2
%20 = OpTypePointer Function %9
%24 = OpConstantNull %9
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %12
%15 = OpVariable %16 Function %17
%23 = OpVariable %20 Function %24
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %15 %14
%18 = OpLoad %13 %15
%21 = OpAccessChain %20 %5 %18 %19
%22 = OpLoad %9 %21
OpStore %23 %22
OpReturn
)");
Validate(b);
}
TEST_F(BuilderTest, Const_IndexAccessor_Array_ArrayWithSwizzle) {
// let ary = array<vec3<f32>, 2u>(vec3<f32>(1.0f, 2.0f, 3.0f), vec3<f32>(4.0f, 5.0f, 6.0f));
// var x = a[1i].xy;
auto* ary =
Let("ary", nullptr,
array(ty.vec3<f32>(), 2_u, vec3<f32>(1._f, 2._f, 3._f), vec3<f32>(4._f, 5._f, 6._f)));
auto* x = Var("x", nullptr, MemberAccessor(IndexAccessor("ary", 1_i), "xy"));
WrapInFunction(ary, x);
spirv::Builder& b = SanitizeAndBuild();
ASSERT_TRUE(b.Build()) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
%1 = OpTypeFunction %2
%7 = OpTypeFloat 32
%6 = OpTypeVector %7 3
%8 = OpTypeInt 32 0
%9 = OpConstant %8 2
%5 = OpTypeArray %6 %9
%10 = OpConstant %7 1
%11 = OpConstant %7 2
%12 = OpConstant %7 3
%13 = OpConstantComposite %6 %10 %11 %12
%14 = OpConstant %7 4
%15 = OpConstant %7 5
%16 = OpConstant %7 6
%17 = OpConstantComposite %6 %14 %15 %16
%18 = OpConstantComposite %5 %13 %17
%19 = OpTypeInt 32 1
%20 = OpConstant %19 1
%22 = OpTypeVector %7 2
%25 = OpTypePointer Function %22
%26 = OpConstantNull %22
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%24 = OpVariable %25 Function %26
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(%21 = OpCompositeExtract %6 %18 1
%23 = OpVectorShuffle %22 %21 %21 0 1
OpStore %24 %23
OpReturn
)");
Validate(b);
}
TEST_F(BuilderTest, Runtime_IndexAccessor_Array_ArrayWithSwizzle) {
// var ary : array<vec3<f32>, 4u>;
// var x = ary[1i].xy;
auto* ary = Var("ary", ty.array(ty.vec3<f32>(), 4_u));
auto* x = Var("x", nullptr, MemberAccessor(IndexAccessor("ary", 1_i), "xy"));
WrapInFunction(ary, x);
spirv::Builder& b = SanitizeAndBuild();
ASSERT_TRUE(b.Build()) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
%1 = OpTypeFunction %2
%9 = OpTypeFloat 32
%8 = OpTypeVector %9 3
%10 = OpTypeInt 32 0
%11 = OpConstant %10 4
%7 = OpTypeArray %8 %11
%6 = OpTypePointer Function %7
%12 = OpConstantNull %7
%13 = OpTypeInt 32 1
%14 = OpConstant %13 1
%15 = OpTypePointer Function %8
%17 = OpTypeVector %9 2
%21 = OpTypePointer Function %17
%22 = OpConstantNull %17
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %12
%20 = OpVariable %21 Function %22
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(%16 = OpAccessChain %15 %5 %14
%18 = OpLoad %8 %16
%19 = OpVectorShuffle %17 %18 %18 0 1
OpStore %20 %19
OpReturn
)");
Validate(b);
}
TEST_F(BuilderTest, Dynamic_IndexAccessor_Array_ArrayWithSwizzle) {
// var ary : array<vec3<f32>, 4u>;
// var one = 1i;
// var x = ary[one].xy;
auto* ary = Var("ary", ty.array(ty.vec3<f32>(), 4_u));
auto* one = Var("one", nullptr, Expr(1_i));
auto* x = Var("x", nullptr, MemberAccessor(IndexAccessor("ary", one), "xy"));
WrapInFunction(ary, one, x);
spirv::Builder& b = SanitizeAndBuild();
ASSERT_TRUE(b.Build()) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
%1 = OpTypeFunction %2
%9 = OpTypeFloat 32
%8 = OpTypeVector %9 3
%10 = OpTypeInt 32 0
%11 = OpConstant %10 4
%7 = OpTypeArray %8 %11
%6 = OpTypePointer Function %7
%12 = OpConstantNull %7
%13 = OpTypeInt 32 1
%14 = OpConstant %13 1
%16 = OpTypePointer Function %13
%17 = OpConstantNull %13
%19 = OpTypePointer Function %8
%21 = OpTypeVector %9 2
%25 = OpTypePointer Function %21
%26 = OpConstantNull %21
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %12
%15 = OpVariable %16 Function %17
%24 = OpVariable %25 Function %26
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %15 %14
%18 = OpLoad %13 %15
%20 = OpAccessChain %19 %5 %18
%22 = OpLoad %8 %20
%23 = OpVectorShuffle %21 %22 %22 0 1
OpStore %24 %23
OpReturn
)");
Validate(b);
}
TEST_F(BuilderTest, Const_IndexAccessor_Nested_Array_f32) {
// let pos : array<array<f32, 2>, 3u> = array<vec2<f32, 2>, 3u>(
// array<f32, 2>(0.0, 0.5),
// array<f32, 2>(-0.5, -0.5),
// array<f32, 2>(0.5, -0.5));
// var x = pos[1u][0u];
auto* pos = Let("pos", ty.array(ty.vec2<f32>(), 3_u),
Construct(ty.array(ty.vec2<f32>(), 3_u), vec2<f32>(0_f, 0.5_f),
vec2<f32>(-0.5_f, -0.5_f), vec2<f32>(0.5_f, -0.5_f)));
auto* x = Var("x", nullptr, IndexAccessor(IndexAccessor(pos, 1_u), 0_u));
WrapInFunction(pos, x);
spirv::Builder& b = SanitizeAndBuild();
ASSERT_TRUE(b.Build()) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
%1 = OpTypeFunction %2
%7 = OpTypeFloat 32
%6 = OpTypeVector %7 2
%8 = OpTypeInt 32 0
%9 = OpConstant %8 3
%5 = OpTypeArray %6 %9
%10 = OpConstantNull %7
%11 = OpConstant %7 0.5
%12 = OpConstantComposite %6 %10 %11
%13 = OpConstant %7 -0.5
%14 = OpConstantComposite %6 %13 %13
%15 = OpConstantComposite %6 %11 %13
%16 = OpConstantComposite %5 %12 %14 %15
%17 = OpConstant %8 1
%19 = OpConstantNull %8
%22 = OpTypePointer Function %7
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%21 = OpVariable %22 Function %10
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(%18 = OpCompositeExtract %6 %16 1
%20 = OpCompositeExtract %7 %18 0
OpStore %21 %20
OpReturn
)");
Validate(b);
}
TEST_F(BuilderTest, Runtime_IndexAccessor_Nested_Array_f32) {
// var pos : array<array<f32, 2>, 3u>;
// var x = pos[1u][2u];
auto* pos = Var("pos", ty.array(ty.vec2<f32>(), 3_u));
auto* x = Var("x", nullptr, IndexAccessor(IndexAccessor(pos, 1_u), 2_u));
WrapInFunction(pos, x);
spirv::Builder& b = SanitizeAndBuild();
ASSERT_TRUE(b.Build()) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
%1 = OpTypeFunction %2
%9 = OpTypeFloat 32
%8 = OpTypeVector %9 2
%10 = OpTypeInt 32 0
%11 = OpConstant %10 3
%7 = OpTypeArray %8 %11
%6 = OpTypePointer Function %7
%12 = OpConstantNull %7
%13 = OpConstant %10 1
%14 = OpConstant %10 2
%15 = OpTypePointer Function %9
%19 = OpConstantNull %9
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %12
%18 = OpVariable %15 Function %19
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(%16 = OpAccessChain %15 %5 %13 %14
%17 = OpLoad %9 %16
OpStore %18 %17
OpReturn
)");
Validate(b);
}
TEST_F(BuilderTest, Dynamic_IndexAccessor_Nested_Array_f32) {
// var pos : array<array<f32, 2>, 3u>;
// var one = 1u;
// var x = pos[one][2u];
auto* pos = Var("pos", ty.array(ty.vec2<f32>(), 3_u));
auto* one = Var("one", nullptr, Expr(2_u));
auto* x = Var("x", nullptr, IndexAccessor(IndexAccessor(pos, "one"), 2_u));
WrapInFunction(pos, one, x);
spirv::Builder& b = SanitizeAndBuild();
ASSERT_TRUE(b.Build()) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
%1 = OpTypeFunction %2
%9 = OpTypeFloat 32
%8 = OpTypeVector %9 2
%10 = OpTypeInt 32 0
%11 = OpConstant %10 3
%7 = OpTypeArray %8 %11
%6 = OpTypePointer Function %7
%12 = OpConstantNull %7
%13 = OpConstant %10 2
%15 = OpTypePointer Function %10
%16 = OpConstantNull %10
%18 = OpTypePointer Function %9
%22 = OpConstantNull %9
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %12
%14 = OpVariable %15 Function %16
%21 = OpVariable %18 Function %22
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %14 %13
%17 = OpLoad %10 %14
%19 = OpAccessChain %18 %5 %17 %13
%20 = OpLoad %9 %19
OpStore %21 %20
OpReturn
)");
Validate(b);
}
TEST_F(BuilderTest, Const_IndexAccessor_Matrix) {
// let a : mat2x2<f32>(vec2<f32>(1., 2.), vec2<f32>(3., 4.));
// var x = a[1i]
auto* a = Let("a", ty.mat2x2<f32>(),
Construct(ty.mat2x2<f32>(), Construct(ty.vec2<f32>(), 1_f, 2_f),
Construct(ty.vec2<f32>(), 3_f, 4_f)));
auto* x = Var("x", nullptr, IndexAccessor("a", 1_i));
WrapInFunction(a, x);
spirv::Builder& b = SanitizeAndBuild();
ASSERT_TRUE(b.Build()) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
%1 = OpTypeFunction %2
%7 = OpTypeFloat 32
%6 = OpTypeVector %7 2
%5 = OpTypeMatrix %6 2
%8 = OpConstant %7 1
%9 = OpConstant %7 2
%10 = OpConstantComposite %6 %8 %9
%11 = OpConstant %7 3
%12 = OpConstant %7 4
%13 = OpConstantComposite %6 %11 %12
%14 = OpConstantComposite %5 %10 %13
%16 = OpTypePointer Function %6
%17 = OpConstantNull %6
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%15 = OpVariable %16 Function %17
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(OpStore %15 %13
OpReturn
)");
Validate(b);
}
TEST_F(BuilderTest, Runtime_IndexAccessor_Matrix) {
// var a : mat2x2<f32>;
// var x = a[1i]
auto* a = Var("a", ty.mat2x2<f32>());
auto* x = Var("x", nullptr, IndexAccessor("a", 1_i));
WrapInFunction(a, x);
spirv::Builder& b = SanitizeAndBuild();
ASSERT_TRUE(b.Build()) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
%1 = OpTypeFunction %2
%9 = OpTypeFloat 32
%8 = OpTypeVector %9 2
%7 = OpTypeMatrix %8 2
%6 = OpTypePointer Function %7
%10 = OpConstantNull %7
%11 = OpTypeInt 32 1
%12 = OpConstant %11 1
%13 = OpTypePointer Function %8
%17 = OpConstantNull %8
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %10
%16 = OpVariable %13 Function %17
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(%14 = OpAccessChain %13 %5 %12
%15 = OpLoad %8 %14
OpStore %16 %15
OpReturn
)");
Validate(b);
}
TEST_F(BuilderTest, Dynamic_IndexAccessor_Matrix) {
// var a : mat2x2<f32>;
// var idx : i32
// var x = a[idx]
auto* a = Var("a", ty.mat2x2<f32>());
auto* idx = Var("idx", ty.i32());
auto* x = Var("x", nullptr, IndexAccessor("a", idx));
WrapInFunction(a, idx, x);
spirv::Builder& b = SanitizeAndBuild();
ASSERT_TRUE(b.Build()) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
%1 = OpTypeFunction %2
%9 = OpTypeFloat 32
%8 = OpTypeVector %9 2
%7 = OpTypeMatrix %8 2
%6 = OpTypePointer Function %7
%10 = OpConstantNull %7
%13 = OpTypeInt 32 1
%12 = OpTypePointer Function %13
%14 = OpConstantNull %13
%16 = OpTypePointer Function %8
%20 = OpConstantNull %8
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %10
%11 = OpVariable %12 Function %14
%19 = OpVariable %16 Function %20
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(%15 = OpLoad %13 %11
%17 = OpAccessChain %16 %5 %15
%18 = OpLoad %8 %17
OpStore %19 %18
OpReturn
)");
Validate(b);
}
TEST_F(BuilderTest, MemberAccessor) {
// my_struct {
// a : f32
// b : f32
// }
// var ident : my_struct
// ident.b
auto* s = Structure("my_struct", {
Member("a", ty.f32()),
Member("b", ty.f32()),
});
auto* var = Var("ident", ty.Of(s));
auto* expr = MemberAccessor("ident", "b");
WrapInFunction(var, expr);
spirv::Builder& b = SanitizeAndBuild();
ASSERT_TRUE(b.Build()) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
%1 = OpTypeFunction %2
%8 = OpTypeFloat 32
%7 = OpTypeStruct %8 %8
%6 = OpTypePointer Function %7
%9 = OpConstantNull %7
%10 = OpTypeInt 32 0
%11 = OpConstant %10 1
%12 = OpTypePointer Function %8
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %9
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(%13 = OpAccessChain %12 %5 %11
%14 = OpLoad %8 %13
OpReturn
)");
Validate(b);
}
TEST_F(BuilderTest, MemberAccessor_Nested) {
// inner_struct {
// a : f32
// b : f32
// }
// my_struct {
// inner : inner_struct
// }
//
// var ident : my_struct
// ident.inner.a
auto* inner_struct = Structure("Inner", {
Member("a", ty.f32()),
Member("b", ty.f32()),
});
auto* s_type = Structure("my_struct", {Member("inner", ty.Of(inner_struct))});
auto* var = Var("ident", ty.Of(s_type));
auto* expr = MemberAccessor(MemberAccessor("ident", "inner"), "b");
WrapInFunction(var, expr);
spirv::Builder& b = SanitizeAndBuild();
ASSERT_TRUE(b.Build()) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
%1 = OpTypeFunction %2
%9 = OpTypeFloat 32
%8 = OpTypeStruct %9 %9
%7 = OpTypeStruct %8
%6 = OpTypePointer Function %7
%10 = OpConstantNull %7
%11 = OpTypeInt 32 0
%12 = OpConstant %11 0
%13 = OpConstant %11 1
%14 = OpTypePointer Function %9
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %10
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(%15 = OpAccessChain %14 %5 %12 %13
%16 = OpLoad %9 %15
OpReturn
)");
Validate(b);
}
TEST_F(BuilderTest, MemberAccessor_NonPointer) {
// my_struct {
// a : f32
// b : f32
// }
// let ident : my_struct = my_struct();
// ident.b
auto* s = Structure("my_struct", {
Member("a", ty.f32()),
Member("b", ty.f32()),
});
auto* var = Let("ident", ty.Of(s), Construct(ty.Of(s), 0_f, 0_f));
auto* expr = MemberAccessor("ident", "b");
WrapInFunction(var, expr);
spirv::Builder& b = SanitizeAndBuild();
ASSERT_TRUE(b.Build()) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
%1 = OpTypeFunction %2
%6 = OpTypeFloat 32
%5 = OpTypeStruct %6 %6
%7 = OpConstantNull %6
%8 = OpConstantComposite %5 %7 %7
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"()");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(%9 = OpCompositeExtract %6 %8 1
OpReturn
)");
Validate(b);
}
TEST_F(BuilderTest, MemberAccessor_Nested_NonPointer) {
// inner_struct {
// a : f32
// b : f32
// }
// my_struct {
// inner : inner_struct
// }
//
// let ident : my_struct = my_struct();
// ident.inner.a
auto* inner_struct = Structure("Inner", {
Member("a", ty.f32()),
Member("b", ty.f32()),
});
auto* s_type = Structure("my_struct", {Member("inner", ty.Of(inner_struct))});
auto* var = Let("ident", ty.Of(s_type),
Construct(ty.Of(s_type), Construct(ty.Of(inner_struct), 0_f, 0_f)));
auto* expr = MemberAccessor(MemberAccessor("ident", "inner"), "b");
WrapInFunction(var, expr);
spirv::Builder& b = SanitizeAndBuild();
ASSERT_TRUE(b.Build()) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
%1 = OpTypeFunction %2
%7 = OpTypeFloat 32
%6 = OpTypeStruct %7 %7
%5 = OpTypeStruct %6
%8 = OpConstantNull %7
%9 = OpConstantComposite %6 %8 %8
%10 = OpConstantComposite %5 %9
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"()");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(%11 = OpCompositeExtract %6 %10 0
%12 = OpCompositeExtract %7 %11 1
OpReturn
)");
Validate(b);
}
TEST_F(BuilderTest, MemberAccessor_Nested_WithAlias) {
// struct Inner {
// a : f32
// b : f32
// };
// type Alias = Inner;
// my_struct {
// inner : Inner
// }
//
// var ident : my_struct
// ident.inner.a
auto* inner_struct = Structure("Inner", {
Member("a", ty.f32()),
Member("b", ty.f32()),
});
auto* alias = Alias("Alias", ty.Of(inner_struct));
auto* s_type = Structure("Outer", {Member("inner", ty.Of(alias))});
auto* var = Var("ident", ty.Of(s_type));
auto* expr = MemberAccessor(MemberAccessor("ident", "inner"), "a");
WrapInFunction(var, expr);
spirv::Builder& b = SanitizeAndBuild();
ASSERT_TRUE(b.Build()) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
%1 = OpTypeFunction %2
%9 = OpTypeFloat 32
%8 = OpTypeStruct %9 %9
%7 = OpTypeStruct %8
%6 = OpTypePointer Function %7
%10 = OpConstantNull %7
%11 = OpTypeInt 32 0
%12 = OpConstant %11 0
%13 = OpTypePointer Function %9
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %10
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(%14 = OpAccessChain %13 %5 %12 %12
%15 = OpLoad %9 %14
OpReturn
)");
Validate(b);
}
TEST_F(BuilderTest, MemberAccessor_Nested_Assignment_LHS) {
// inner_struct {
// a : f32
// }
// my_struct {
// inner : inner_struct
// }
//
// var ident : my_struct
// ident.inner.a = 2.0f;
auto* inner_struct = Structure("Inner", {
Member("a", ty.f32()),
Member("b", ty.f32()),
});
auto* s_type = Structure("my_struct", {Member("inner", ty.Of(inner_struct))});
auto* var = Var("ident", ty.Of(s_type));
auto* expr = Assign(MemberAccessor(MemberAccessor("ident", "inner"), "a"), Expr(2_f));
WrapInFunction(var, expr);
spirv::Builder& b = SanitizeAndBuild();
ASSERT_TRUE(b.Build()) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
%1 = OpTypeFunction %2
%9 = OpTypeFloat 32
%8 = OpTypeStruct %9 %9
%7 = OpTypeStruct %8
%6 = OpTypePointer Function %7
%10 = OpConstantNull %7
%11 = OpTypeInt 32 0
%12 = OpConstant %11 0
%13 = OpTypePointer Function %9
%15 = OpConstant %9 2
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %10
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(%14 = OpAccessChain %13 %5 %12 %12
OpStore %14 %15
OpReturn
)");
Validate(b);
}
TEST_F(BuilderTest, MemberAccessor_Nested_Assignment_RHS) {
// inner_struct {
// a : f32
// }
// my_struct {
// inner : inner_struct
// }
//
// var ident : my_struct
// var store : f32 = ident.inner.a
auto* inner_struct = Structure("Inner", {
Member("a", ty.f32()),
Member("b", ty.f32()),
});
auto* s_type = Structure("my_struct", {Member("inner", ty.Of(inner_struct))});
auto* var = Var("ident", ty.Of(s_type));
auto* store = Var("store", ty.f32());
auto* rhs = MemberAccessor(MemberAccessor("ident", "inner"), "a");
auto* expr = Assign("store", rhs);
WrapInFunction(var, store, expr);
spirv::Builder& b = SanitizeAndBuild();
ASSERT_TRUE(b.Build()) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
%1 = OpTypeFunction %2
%9 = OpTypeFloat 32
%8 = OpTypeStruct %9 %9
%7 = OpTypeStruct %8
%6 = OpTypePointer Function %7
%10 = OpConstantNull %7
%12 = OpTypePointer Function %9
%13 = OpConstantNull %9
%14 = OpTypeInt 32 0
%15 = OpConstant %14 0
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %10
%11 = OpVariable %12 Function %13
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(%16 = OpAccessChain %12 %5 %15 %15
%17 = OpLoad %9 %16
OpStore %11 %17
OpReturn
)");
Validate(b);
}
TEST_F(BuilderTest, MemberAccessor_Swizzle_Single) {
// ident.y
auto* var = Var("ident", ty.vec3<f32>());
auto* expr = MemberAccessor("ident", "y");
WrapInFunction(var, expr);
spirv::Builder& b = SanitizeAndBuild();
ASSERT_TRUE(b.Build()) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
%1 = OpTypeFunction %2
%8 = OpTypeFloat 32
%7 = OpTypeVector %8 3
%6 = OpTypePointer Function %7
%9 = OpConstantNull %7
%10 = OpTypeInt 32 0
%11 = OpConstant %10 1
%12 = OpTypePointer Function %8
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %9
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(%13 = OpAccessChain %12 %5 %11
%14 = OpLoad %8 %13
OpReturn
)");
Validate(b);
}
TEST_F(BuilderTest, MemberAccessor_Swizzle_MultipleNames) {
// ident.yx
auto* var = Var("ident", ty.vec3<f32>());
auto* expr = MemberAccessor("ident", "yx");
WrapInFunction(var, expr);
spirv::Builder& b = SanitizeAndBuild();
ASSERT_TRUE(b.Build()) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
%1 = OpTypeFunction %2
%8 = OpTypeFloat 32
%7 = OpTypeVector %8 3
%6 = OpTypePointer Function %7
%9 = OpConstantNull %7
%10 = OpTypeVector %8 2
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %9
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(%11 = OpLoad %7 %5
%12 = OpVectorShuffle %10 %11 %11 1 0
OpReturn
)");
Validate(b);
}
TEST_F(BuilderTest, MemberAccessor_Swizzle_of_Swizzle) {
// ident.yxz.xz
auto* var = Var("ident", ty.vec3<f32>());
auto* expr = MemberAccessor(MemberAccessor("ident", "yxz"), "xz");
WrapInFunction(var, expr);
spirv::Builder& b = SanitizeAndBuild();
ASSERT_TRUE(b.Build()) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
%1 = OpTypeFunction %2
%8 = OpTypeFloat 32
%7 = OpTypeVector %8 3
%6 = OpTypePointer Function %7
%9 = OpConstantNull %7
%12 = OpTypeVector %8 2
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %9
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(%10 = OpLoad %7 %5
%11 = OpVectorShuffle %7 %10 %10 1 0 2
%13 = OpVectorShuffle %12 %11 %11 0 2
OpReturn
)");
Validate(b);
}
TEST_F(BuilderTest, MemberAccessor_Member_of_Swizzle) {
// ident.yxz.x
auto* var = Var("ident", ty.vec3<f32>());
auto* expr = MemberAccessor(MemberAccessor("ident", "yxz"), "x");
WrapInFunction(var, expr);
spirv::Builder& b = SanitizeAndBuild();
ASSERT_TRUE(b.Build()) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
%1 = OpTypeFunction %2
%8 = OpTypeFloat 32
%7 = OpTypeVector %8 3
%6 = OpTypePointer Function %7
%9 = OpConstantNull %7
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %9
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(%10 = OpLoad %7 %5
%11 = OpVectorShuffle %7 %10 %10 1 0 2
%12 = OpCompositeExtract %8 %11 0
OpReturn
)");
Validate(b);
}
TEST_F(BuilderTest, MemberAccessor_Array_of_Swizzle) {
// index.yxz[1i]
auto* var = Var("ident", ty.vec3<f32>());
auto* expr = IndexAccessor(MemberAccessor("ident", "yxz"), 1_i);
WrapInFunction(var, expr);
spirv::Builder& b = SanitizeAndBuild();
ASSERT_TRUE(b.Build()) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
%1 = OpTypeFunction %2
%8 = OpTypeFloat 32
%7 = OpTypeVector %8 3
%6 = OpTypePointer Function %7
%9 = OpConstantNull %7
%12 = OpTypeInt 32 1
%13 = OpConstant %12 1
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %9
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()), R"(%10 = OpLoad %7 %5
%11 = OpVectorShuffle %7 %10 %10 1 0 2
%14 = OpCompositeExtract %8 %11 1
OpReturn
)");
Validate(b);
}
TEST_F(BuilderTest, IndexAccessor_Mixed_ArrayAndMember) {
// type C = struct {
// baz : vec3<f32>
// }
// type B = struct {
// bar : C;
// }
// type A = struct {
// foo : array<B, 3>
// }
// var index : array<A, 2u>
// index[0i].foo[2i].bar.baz.yx
auto* c_type = Structure("C", {Member("baz", ty.vec3<f32>())});
auto* b_type = Structure("B", {Member("bar", ty.Of(c_type))});
auto* b_ary_type = ty.array(ty.Of(b_type), 3_u);
auto* a_type = Structure("A", {Member("foo", b_ary_type)});
auto* a_ary_type = ty.array(ty.Of(a_type), 2_u);
auto* var = Var("index", a_ary_type);
auto* expr = MemberAccessor(
MemberAccessor(
MemberAccessor(IndexAccessor(MemberAccessor(IndexAccessor("index", 0_i), "foo"), 2_i),
"bar"),
"baz"),
"yx");
WrapInFunction(var, expr);
spirv::Builder& b = SanitizeAndBuild();
ASSERT_TRUE(b.Build()) << b.error();
EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeVoid
%1 = OpTypeFunction %2
%13 = OpTypeFloat 32
%12 = OpTypeVector %13 3
%11 = OpTypeStruct %12
%10 = OpTypeStruct %11
%14 = OpTypeInt 32 0
%15 = OpConstant %14 3
%9 = OpTypeArray %10 %15
%8 = OpTypeStruct %9
%16 = OpConstant %14 2
%7 = OpTypeArray %8 %16
%6 = OpTypePointer Function %7
%17 = OpConstantNull %7
%18 = OpTypeInt 32 1
%19 = OpConstantNull %18
%20 = OpConstant %14 0
%21 = OpConstant %18 2
%22 = OpTypePointer Function %12
%24 = OpTypeVector %13 2
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].variables()), R"(%5 = OpVariable %6 Function %17
)");
EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
R"(%23 = OpAccessChain %22 %5 %19 %20 %21 %20 %20
%25 = OpLoad %12 %23
%26 = OpVectorShuffle %24 %25 %25 1 0
OpReturn
)");
Validate(b);
}
} // namespace
} // namespace tint::writer::spirv