1 // Copyright 2020 The Tint Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "gmock/gmock.h"
16 #include "src/reader/spirv/parser_impl_test_helper.h"
17 #include "src/reader/spirv/spirv_tools_helpers_test.h"
18
19 namespace tint {
20 namespace reader {
21 namespace spirv {
22 namespace {
23
24 using ::testing::Eq;
25
26 using SpvParserUserNameTest = SpvParserTest;
27
TEST_F(SpvParserUserNameTest,UserName_RespectOpName)28 TEST_F(SpvParserUserNameTest, UserName_RespectOpName) {
29 auto p = parser(test::Assemble(R"(
30 OpName %1 "the_void_type"
31 %1 = OpTypeVoid
32 )"));
33 EXPECT_TRUE(p->BuildAndParseInternalModule());
34 EXPECT_THAT(p->namer().GetName(1), Eq("the_void_type"));
35
36 p->DeliberatelyInvalidSpirv();
37 }
38
TEST_F(SpvParserUserNameTest,UserName_IgnoreEmptyName)39 TEST_F(SpvParserUserNameTest, UserName_IgnoreEmptyName) {
40 auto p = parser(test::Assemble(R"(
41 OpName %1 ""
42 %1 = OpTypeVoid
43 )"));
44 EXPECT_TRUE(p->BuildAndParseInternalModule());
45 EXPECT_FALSE(p->namer().HasName(1));
46
47 p->DeliberatelyInvalidSpirv();
48 }
49
TEST_F(SpvParserUserNameTest,UserName_DistinguishDuplicateSuggestion)50 TEST_F(SpvParserUserNameTest, UserName_DistinguishDuplicateSuggestion) {
51 auto p = parser(test::Assemble(R"(
52 OpName %1 "vanilla"
53 OpName %2 "vanilla"
54 %1 = OpTypeVoid
55 %2 = OpTypeInt 32 0
56 )"));
57 EXPECT_TRUE(p->BuildAndParseInternalModule());
58 EXPECT_THAT(p->namer().GetName(1), Eq("vanilla"));
59 EXPECT_THAT(p->namer().GetName(2), Eq("vanilla_1"));
60
61 p->DeliberatelyInvalidSpirv();
62 }
63
TEST_F(SpvParserUserNameTest,UserName_RespectOpMemberName)64 TEST_F(SpvParserUserNameTest, UserName_RespectOpMemberName) {
65 auto p = parser(test::Assemble(R"(
66 OpMemberName %3 0 "strawberry"
67 OpMemberName %3 1 "vanilla"
68 OpMemberName %3 2 "chocolate"
69 %2 = OpTypeInt 32 0
70 %3 = OpTypeStruct %2 %2 %2
71 )"));
72 EXPECT_TRUE(p->BuildAndParseInternalModule());
73 EXPECT_THAT(p->namer().GetMemberName(3, 0), Eq("strawberry"));
74 EXPECT_THAT(p->namer().GetMemberName(3, 1), Eq("vanilla"));
75 EXPECT_THAT(p->namer().GetMemberName(3, 2), Eq("chocolate"));
76
77 p->DeliberatelyInvalidSpirv();
78 }
79
TEST_F(SpvParserUserNameTest,UserName_IgnoreEmptyMemberName)80 TEST_F(SpvParserUserNameTest, UserName_IgnoreEmptyMemberName) {
81 auto p = parser(test::Assemble(R"(
82 OpMemberName %3 0 ""
83 %2 = OpTypeInt 32 0
84 %3 = OpTypeStruct %2
85 )"));
86 EXPECT_TRUE(p->BuildAndParseInternalModule());
87 EXPECT_THAT(p->namer().GetMemberName(3, 0), Eq("field0"));
88
89 p->DeliberatelyInvalidSpirv();
90 }
91
TEST_F(SpvParserUserNameTest,UserName_SynthesizeMemberNames)92 TEST_F(SpvParserUserNameTest, UserName_SynthesizeMemberNames) {
93 auto p = parser(test::Assemble(R"(
94 %2 = OpTypeInt 32 0
95 %3 = OpTypeStruct %2 %2 %2
96 )"));
97 EXPECT_TRUE(p->BuildAndParseInternalModule());
98 EXPECT_THAT(p->namer().GetMemberName(3, 0), Eq("field0"));
99 EXPECT_THAT(p->namer().GetMemberName(3, 1), Eq("field1"));
100 EXPECT_THAT(p->namer().GetMemberName(3, 2), Eq("field2"));
101
102 p->DeliberatelyInvalidSpirv();
103 }
104
TEST_F(SpvParserUserNameTest,UserName_MemberNamesMixUserAndSynthesized)105 TEST_F(SpvParserUserNameTest, UserName_MemberNamesMixUserAndSynthesized) {
106 auto p = parser(test::Assemble(R"(
107 OpMemberName %3 1 "vanilla"
108 %2 = OpTypeInt 32 0
109 %3 = OpTypeStruct %2 %2 %2
110 )"));
111 EXPECT_TRUE(p->BuildAndParseInternalModule());
112 EXPECT_THAT(p->namer().GetMemberName(3, 0), Eq("field0"));
113 EXPECT_THAT(p->namer().GetMemberName(3, 1), Eq("vanilla"));
114 EXPECT_THAT(p->namer().GetMemberName(3, 2), Eq("field2"));
115
116 p->DeliberatelyInvalidSpirv();
117 }
118
TEST_F(SpvParserUserNameTest,EntryPointNames_AlwaysTakePrecedence)119 TEST_F(SpvParserUserNameTest, EntryPointNames_AlwaysTakePrecedence) {
120 const std::string assembly = R"(
121 OpCapability Shader
122 OpMemoryModel Logical Simple
123 OpEntryPoint Vertex %100 "main"
124 OpEntryPoint Fragment %100 "main_1"
125 OpExecutionMode %100 OriginUpperLeft
126
127 ; attempt to grab the "main_1" that would be the derived name
128 ; for the second entry point.
129 OpName %1 "main_1"
130
131 %void = OpTypeVoid
132 %voidfn = OpTypeFunction %void
133 %uint = OpTypeInt 32 0
134 %uint_0 = OpConstant %uint 0
135
136 %100 = OpFunction %void None %voidfn
137 %100_entry = OpLabel
138 %1 = OpCopyObject %uint %uint_0
139 OpReturn
140 OpFunctionEnd
141 )";
142 auto p = parser(test::Assemble(assembly));
143 EXPECT_TRUE(p->BuildAndParseInternalModule());
144 // The first entry point grabs the best name, "main"
145 EXPECT_THAT(p->namer().Name(100), Eq("main"));
146 // The OpName on %1 is overriden because the second entry point
147 // has grabbed "main_1" first.
148 EXPECT_THAT(p->namer().Name(1), Eq("main_1_1"));
149
150 const auto& ep_info = p->GetEntryPointInfo(100);
151 ASSERT_EQ(2u, ep_info.size());
152 EXPECT_EQ(ep_info[0].name, "main");
153 EXPECT_EQ(ep_info[1].name, "main_1");
154
155 // This test checks two entry point with the same implementation function.
156 // But for the shader stages supported by WGSL, the SPIR-V rules require
157 // conflicting execution modes be applied to them.
158 // I still want to test the name disambiguation behaviour, but the cases
159 // are rejected by SPIR-V validation. This is true at least for the current
160 // WGSL feature set.
161 p->DeliberatelyInvalidSpirv();
162 }
163
TEST_F(SpvParserUserNameTest,EntryPointNames_DistinctFromInnerNames)164 TEST_F(SpvParserUserNameTest, EntryPointNames_DistinctFromInnerNames) {
165 const std::string assembly = R"(
166 OpCapability Shader
167 OpMemoryModel Logical Simple
168 OpEntryPoint Vertex %100 "main"
169 OpEntryPoint Fragment %100 "main_1"
170 OpExecutionMode %100 OriginUpperLeft
171
172 ; attempt to grab the "main_1" that would be the derived name
173 ; for the second entry point.
174 OpName %1 "main_1"
175
176 %void = OpTypeVoid
177 %voidfn = OpTypeFunction %void
178 %uint = OpTypeInt 32 0
179 %uint_0 = OpConstant %uint 0
180
181 %100 = OpFunction %void None %voidfn
182 %100_entry = OpLabel
183 %1 = OpCopyObject %uint %uint_0
184 OpReturn
185 OpFunctionEnd
186 )";
187 auto p = parser(test::Assemble(assembly));
188
189 EXPECT_TRUE(p->BuildAndParseInternalModule());
190 // The first entry point grabs the best name, "main"
191 EXPECT_THAT(p->namer().Name(100), Eq("main"));
192 EXPECT_THAT(p->namer().Name(1), Eq("main_1_1"));
193
194 const auto ep_info = p->GetEntryPointInfo(100);
195 ASSERT_EQ(2u, ep_info.size());
196 EXPECT_EQ(ep_info[0].name, "main");
197 EXPECT_EQ(ep_info[0].inner_name, "main_2");
198 // The second entry point retains its name...
199 EXPECT_EQ(ep_info[1].name, "main_1");
200 // ...but will use the same implementation function.
201 EXPECT_EQ(ep_info[1].inner_name, "main_2");
202
203 // This test checks two entry point with the same implementation function.
204 // But for the shader stages supported by WGSL, the SPIR-V rules require
205 // conflicting execution modes be applied to them.
206 // I still want to test the name disambiguation behaviour, but the cases
207 // are rejected by SPIR-V validation. This is true at least for the current
208 // WGSL feature set.
209 p->DeliberatelyInvalidSpirv();
210 }
211
212 } // namespace
213 } // namespace spirv
214 } // namespace reader
215 } // namespace tint
216