1 // Copyright (c) 2016 Google Inc.
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 <string>
16 #include <vector>
17
18 #include "gmock/gmock.h"
19 #include "source/name_mapper.h"
20 #include "test/test_fixture.h"
21 #include "test/unit_spirv.h"
22
23 namespace spvtools {
24 namespace {
25
26 using spvtest::ScopedContext;
27 using ::testing::Eq;
28
TEST(TrivialNameTest,Samples)29 TEST(TrivialNameTest, Samples) {
30 auto mapper = GetTrivialNameMapper();
31 EXPECT_EQ(mapper(1), "1");
32 EXPECT_EQ(mapper(1999), "1999");
33 EXPECT_EQ(mapper(1024), "1024");
34 }
35
36 // A test case for the name mappers that actually look at an assembled module.
37 struct NameIdCase {
38 std::string assembly; // Input assembly text
39 uint32_t id;
40 std::string expected_name;
41 };
42
43 using FriendlyNameTest =
44 spvtest::TextToBinaryTestBase<::testing::TestWithParam<NameIdCase>>;
45
TEST_P(FriendlyNameTest,SingleMapping)46 TEST_P(FriendlyNameTest, SingleMapping) {
47 ScopedContext context(SPV_ENV_UNIVERSAL_1_1);
48 auto words = CompileSuccessfully(GetParam().assembly, SPV_ENV_UNIVERSAL_1_1);
49 auto friendly_mapper =
50 FriendlyNameMapper(context.context, words.data(), words.size());
51 NameMapper mapper = friendly_mapper.GetNameMapper();
52 EXPECT_THAT(mapper(GetParam().id), Eq(GetParam().expected_name))
53 << GetParam().assembly << std::endl
54 << " for id " << GetParam().id;
55 }
56
57 INSTANTIATE_TEST_SUITE_P(ScalarType, FriendlyNameTest,
58 ::testing::ValuesIn(std::vector<NameIdCase>{
59 {"%1 = OpTypeVoid", 1, "void"},
60 {"%1 = OpTypeBool", 1, "bool"},
61 {"%1 = OpTypeInt 8 0", 1, "uchar"},
62 {"%1 = OpTypeInt 8 1", 1, "char"},
63 {"%1 = OpTypeInt 16 0", 1, "ushort"},
64 {"%1 = OpTypeInt 16 1", 1, "short"},
65 {"%1 = OpTypeInt 32 0", 1, "uint"},
66 {"%1 = OpTypeInt 32 1", 1, "int"},
67 {"%1 = OpTypeInt 64 0", 1, "ulong"},
68 {"%1 = OpTypeInt 64 1", 1, "long"},
69 {"%1 = OpTypeInt 1 0", 1, "u1"},
70 {"%1 = OpTypeInt 1 1", 1, "i1"},
71 {"%1 = OpTypeInt 33 0", 1, "u33"},
72 {"%1 = OpTypeInt 33 1", 1, "i33"},
73
74 {"%1 = OpTypeFloat 16", 1, "half"},
75 {"%1 = OpTypeFloat 32", 1, "float"},
76 {"%1 = OpTypeFloat 64", 1, "double"},
77 {"%1 = OpTypeFloat 10", 1, "fp10"},
78 {"%1 = OpTypeFloat 55", 1, "fp55"},
79 }));
80
81 INSTANTIATE_TEST_SUITE_P(
82 VectorType, FriendlyNameTest,
83 ::testing::ValuesIn(std::vector<NameIdCase>{
84 {"%1 = OpTypeBool %2 = OpTypeVector %1 1", 2, "v1bool"},
85 {"%1 = OpTypeBool %2 = OpTypeVector %1 2", 2, "v2bool"},
86 {"%1 = OpTypeBool %2 = OpTypeVector %1 3", 2, "v3bool"},
87 {"%1 = OpTypeBool %2 = OpTypeVector %1 4", 2, "v4bool"},
88
89 {"%1 = OpTypeInt 8 0 %2 = OpTypeVector %1 2", 2, "v2uchar"},
90 {"%1 = OpTypeInt 16 1 %2 = OpTypeVector %1 3", 2, "v3short"},
91 {"%1 = OpTypeInt 32 0 %2 = OpTypeVector %1 4", 2, "v4uint"},
92 {"%1 = OpTypeInt 64 1 %2 = OpTypeVector %1 3", 2, "v3long"},
93 {"%1 = OpTypeInt 20 0 %2 = OpTypeVector %1 4", 2, "v4u20"},
94 {"%1 = OpTypeInt 21 1 %2 = OpTypeVector %1 3", 2, "v3i21"},
95
96 {"%1 = OpTypeFloat 32 %2 = OpTypeVector %1 2", 2, "v2float"},
97 // OpName overrides the element name.
98 {"OpName %1 \"time\" %1 = OpTypeFloat 32 %2 = OpTypeVector %1 2", 2,
99 "v2time"},
100 }));
101
102 INSTANTIATE_TEST_SUITE_P(
103 MatrixType, FriendlyNameTest,
104 ::testing::ValuesIn(std::vector<NameIdCase>{
105 {"%1 = OpTypeBool %2 = OpTypeVector %1 2 %3 = OpTypeMatrix %2 2", 3,
106 "mat2v2bool"},
107 {"%1 = OpTypeFloat 32 %2 = OpTypeVector %1 2 %3 = OpTypeMatrix %2 3", 3,
108 "mat3v2float"},
109 {"%1 = OpTypeFloat 32 %2 = OpTypeVector %1 2 %3 = OpTypeMatrix %2 4", 3,
110 "mat4v2float"},
111 {"OpName %1 \"time\" %1 = OpTypeFloat 32 %2 = OpTypeVector %1 2 %3 = "
112 "OpTypeMatrix %2 4",
113 3, "mat4v2time"},
114 {"OpName %2 \"lat_long\" %1 = OpTypeFloat 32 %2 = OpTypeVector %1 2 %3 "
115 "= OpTypeMatrix %2 4",
116 3, "mat4lat_long"},
117 }));
118
119 INSTANTIATE_TEST_SUITE_P(
120 OpName, FriendlyNameTest,
121 ::testing::ValuesIn(std::vector<NameIdCase>{
122 {"OpName %1 \"abcdefg\"", 1, "abcdefg"},
123 {"OpName %1 \"Hello world!\"", 1, "Hello_world_"},
124 {"OpName %1 \"0123456789\"", 1, "0123456789"},
125 {"OpName %1 \"_\"", 1, "_"},
126 // An empty string is not valid for SPIR-V assembly IDs.
127 {"OpName %1 \"\"", 1, "_"},
128 // Test uniqueness when presented with things mapping to "_"
129 {"OpName %1 \"\" OpName %2 \"\"", 1, "_"},
130 {"OpName %1 \"\" OpName %2 \"\"", 2, "__0"},
131 {"OpName %1 \"\" OpName %2 \"\" OpName %3 \"_\"", 3, "__1"},
132 // Test uniqueness of names that are forced to be
133 // numbers.
134 {"OpName %1 \"2\" OpName %2 \"2\"", 1, "2"},
135 {"OpName %1 \"2\" OpName %2 \"2\"", 2, "2_0"},
136 // Test uniqueness in the face of forward references
137 // for Ids that don't already have friendly names.
138 // In particular, the first OpDecorate assigns the name, and
139 // the second one can't override it.
140 {"OpDecorate %1 Volatile OpDecorate %1 Restrict", 1, "1"},
141 // But a forced name can override the name that
142 // would have been assigned via the OpDecorate
143 // forward reference.
144 {"OpName %1 \"mememe\" OpDecorate %1 Volatile OpDecorate %1 Restrict",
145 1, "mememe"},
146 // OpName can override other inferences. We assume valid instruction
147 // ordering, where OpName precedes type definitions.
148 {"OpName %1 \"myfloat\" %1 = OpTypeFloat 32", 1, "myfloat"},
149 }));
150
151 INSTANTIATE_TEST_SUITE_P(
152 UniquenessHeuristic, FriendlyNameTest,
153 ::testing::ValuesIn(std::vector<NameIdCase>{
154 {"%1 = OpTypeVoid %2 = OpTypeVoid %3 = OpTypeVoid", 1, "void"},
155 {"%1 = OpTypeVoid %2 = OpTypeVoid %3 = OpTypeVoid", 2, "void_0"},
156 {"%1 = OpTypeVoid %2 = OpTypeVoid %3 = OpTypeVoid", 3, "void_1"},
157 }));
158
159 INSTANTIATE_TEST_SUITE_P(Arrays, FriendlyNameTest,
160 ::testing::ValuesIn(std::vector<NameIdCase>{
161 {"OpName %2 \"FortyTwo\" %1 = OpTypeFloat 32 "
162 "%2 = OpConstant %1 42 %3 = OpTypeArray %1 %2",
163 3, "_arr_float_FortyTwo"},
164 {"%1 = OpTypeInt 32 0 "
165 "%2 = OpTypeRuntimeArray %1",
166 2, "_runtimearr_uint"},
167 }));
168
169 INSTANTIATE_TEST_SUITE_P(Structs, FriendlyNameTest,
170 ::testing::ValuesIn(std::vector<NameIdCase>{
171 {"%1 = OpTypeBool "
172 "%2 = OpTypeStruct %1 %1 %1",
173 2, "_struct_2"},
174 {"%1 = OpTypeBool "
175 "%2 = OpTypeStruct %1 %1 %1 "
176 "%3 = OpTypeStruct %2 %2",
177 3, "_struct_3"},
178 }));
179
180 INSTANTIATE_TEST_SUITE_P(
181 Pointer, FriendlyNameTest,
182 ::testing::ValuesIn(std::vector<NameIdCase>{
183 {"%1 = OpTypeFloat 32 %2 = OpTypePointer Workgroup %1", 2,
184 "_ptr_Workgroup_float"},
185 {"%1 = OpTypeBool %2 = OpTypePointer Private %1", 2,
186 "_ptr_Private_bool"},
187 // OpTypeForwardPointer doesn't force generation of the name for its
188 // target type.
189 {"%1 = OpTypeBool OpTypeForwardPointer %2 Private %2 = OpTypePointer "
190 "Private %1",
191 2, "_ptr_Private_bool"},
192 }));
193
194 INSTANTIATE_TEST_SUITE_P(ExoticTypes, FriendlyNameTest,
195 ::testing::ValuesIn(std::vector<NameIdCase>{
196 {"%1 = OpTypeEvent", 1, "Event"},
197 {"%1 = OpTypeDeviceEvent", 1, "DeviceEvent"},
198 {"%1 = OpTypeReserveId", 1, "ReserveId"},
199 {"%1 = OpTypeQueue", 1, "Queue"},
200 {"%1 = OpTypeOpaque \"hello world!\"", 1,
201 "Opaque_hello_world_"},
202 {"%1 = OpTypePipe ReadOnly", 1, "PipeReadOnly"},
203 {"%1 = OpTypePipe WriteOnly", 1, "PipeWriteOnly"},
204 {"%1 = OpTypePipe ReadWrite", 1, "PipeReadWrite"},
205 {"%1 = OpTypePipeStorage", 1, "PipeStorage"},
206 {"%1 = OpTypeNamedBarrier", 1, "NamedBarrier"},
207 }));
208
209 // Makes a test case for a BuiltIn variable declaration.
BuiltInCase(std::string assembly_name,std::string expected)210 NameIdCase BuiltInCase(std::string assembly_name, std::string expected) {
211 return NameIdCase{std::string("OpDecorate %1 BuiltIn ") + assembly_name +
212 " %1 = OpVariable %2 Input",
213 1, expected};
214 }
215
216 // Makes a test case for a BuiltIn variable declaration. In this overload,
217 // the expected result is the same as the assembly name.
BuiltInCase(std::string assembly_name)218 NameIdCase BuiltInCase(std::string assembly_name) {
219 return BuiltInCase(assembly_name, assembly_name);
220 }
221
222 // Makes a test case for a BuiltIn variable declaration. In this overload,
223 // the expected result is the same as the assembly name, but with a "gl_"
224 // prefix.
BuiltInGLCase(std::string assembly_name)225 NameIdCase BuiltInGLCase(std::string assembly_name) {
226 return BuiltInCase(assembly_name, std::string("gl_") + assembly_name);
227 }
228
229 INSTANTIATE_TEST_SUITE_P(
230 BuiltIns, FriendlyNameTest,
231 ::testing::ValuesIn(std::vector<NameIdCase>{
232 BuiltInGLCase("Position"),
233 BuiltInGLCase("PointSize"),
234 BuiltInGLCase("ClipDistance"),
235 BuiltInGLCase("CullDistance"),
236 BuiltInCase("VertexId", "gl_VertexID"),
237 BuiltInCase("InstanceId", "gl_InstanceID"),
238 BuiltInCase("PrimitiveId", "gl_PrimitiveID"),
239 BuiltInCase("InvocationId", "gl_InvocationID"),
240 BuiltInGLCase("Layer"),
241 BuiltInGLCase("ViewportIndex"),
242 BuiltInGLCase("TessLevelOuter"),
243 BuiltInGLCase("TessLevelInner"),
244 BuiltInGLCase("TessCoord"),
245 BuiltInGLCase("PatchVertices"),
246 BuiltInGLCase("FragCoord"),
247 BuiltInGLCase("PointCoord"),
248 BuiltInGLCase("FrontFacing"),
249 BuiltInCase("SampleId", "gl_SampleID"),
250 BuiltInGLCase("SamplePosition"),
251 BuiltInGLCase("SampleMask"),
252 BuiltInGLCase("FragDepth"),
253 BuiltInGLCase("HelperInvocation"),
254 BuiltInCase("NumWorkgroups", "gl_NumWorkGroups"),
255 BuiltInCase("WorkgroupSize", "gl_WorkGroupSize"),
256 BuiltInCase("WorkgroupId", "gl_WorkGroupID"),
257 BuiltInCase("LocalInvocationId", "gl_LocalInvocationID"),
258 BuiltInCase("GlobalInvocationId", "gl_GlobalInvocationID"),
259 BuiltInGLCase("LocalInvocationIndex"),
260 BuiltInCase("WorkDim"),
261 BuiltInCase("GlobalSize"),
262 BuiltInCase("EnqueuedWorkgroupSize"),
263 BuiltInCase("GlobalOffset"),
264 BuiltInCase("GlobalLinearId"),
265 BuiltInCase("SubgroupSize"),
266 BuiltInCase("SubgroupMaxSize"),
267 BuiltInCase("NumSubgroups"),
268 BuiltInCase("NumEnqueuedSubgroups"),
269 BuiltInCase("SubgroupId"),
270 BuiltInCase("SubgroupLocalInvocationId"),
271 BuiltInGLCase("VertexIndex"),
272 BuiltInGLCase("InstanceIndex"),
273 BuiltInGLCase("BaseInstance"),
274 BuiltInCase("SubgroupEqMaskKHR"),
275 BuiltInCase("SubgroupGeMaskKHR"),
276 BuiltInCase("SubgroupGtMaskKHR"),
277 BuiltInCase("SubgroupLeMaskKHR"),
278 BuiltInCase("SubgroupLtMaskKHR"),
279 }));
280
281 INSTANTIATE_TEST_SUITE_P(DebugNameOverridesBuiltin, FriendlyNameTest,
282 ::testing::ValuesIn(std::vector<NameIdCase>{
283 {"OpName %1 \"foo\" OpDecorate %1 BuiltIn WorkDim "
284 "%1 = OpVariable %2 Input",
285 1, "foo"}}));
286
287 INSTANTIATE_TEST_SUITE_P(
288 SimpleIntegralConstants, FriendlyNameTest,
289 ::testing::ValuesIn(std::vector<NameIdCase>{
290 {"%1 = OpTypeInt 32 0 %2 = OpConstant %1 0", 2, "uint_0"},
291 {"%1 = OpTypeInt 32 0 %2 = OpConstant %1 1", 2, "uint_1"},
292 {"%1 = OpTypeInt 32 0 %2 = OpConstant %1 2", 2, "uint_2"},
293 {"%1 = OpTypeInt 32 0 %2 = OpConstant %1 9", 2, "uint_9"},
294 {"%1 = OpTypeInt 32 0 %2 = OpConstant %1 42", 2, "uint_42"},
295 {"%1 = OpTypeInt 32 1 %2 = OpConstant %1 0", 2, "int_0"},
296 {"%1 = OpTypeInt 32 1 %2 = OpConstant %1 1", 2, "int_1"},
297 {"%1 = OpTypeInt 32 1 %2 = OpConstant %1 2", 2, "int_2"},
298 {"%1 = OpTypeInt 32 1 %2 = OpConstant %1 9", 2, "int_9"},
299 {"%1 = OpTypeInt 32 1 %2 = OpConstant %1 42", 2, "int_42"},
300 {"%1 = OpTypeInt 32 1 %2 = OpConstant %1 -42", 2, "int_n42"},
301 // Exotic bit widths
302 {"%1 = OpTypeInt 33 0 %2 = OpConstant %1 0", 2, "u33_0"},
303 {"%1 = OpTypeInt 33 1 %2 = OpConstant %1 10", 2, "i33_10"},
304 {"%1 = OpTypeInt 33 1 %2 = OpConstant %1 -19", 2, "i33_n19"},
305 }));
306
307 INSTANTIATE_TEST_SUITE_P(
308 SimpleFloatConstants, FriendlyNameTest,
309 ::testing::ValuesIn(std::vector<NameIdCase>{
310 {"%1 = OpTypeFloat 16\n%2 = OpConstant %1 0x1.ff4p+16", 2,
311 "half_0x1_ff4p_16"},
312 {"%1 = OpTypeFloat 16\n%2 = OpConstant %1 -0x1.d2cp-10", 2,
313 "half_n0x1_d2cpn10"},
314 // 32-bit floats
315 {"%1 = OpTypeFloat 32\n%2 = OpConstant %1 -3.125", 2, "float_n3_125"},
316 {"%1 = OpTypeFloat 32\n%2 = OpConstant %1 0x1.8p+128", 2,
317 "float_0x1_8p_128"}, // NaN
318 {"%1 = OpTypeFloat 32\n%2 = OpConstant %1 -0x1.0002p+128", 2,
319 "float_n0x1_0002p_128"}, // NaN
320 {"%1 = OpTypeFloat 32\n%2 = OpConstant %1 0x1p+128", 2,
321 "float_0x1p_128"}, // Inf
322 {"%1 = OpTypeFloat 32\n%2 = OpConstant %1 -0x1p+128", 2,
323 "float_n0x1p_128"}, // -Inf
324 // 64-bit floats
325 {"%1 = OpTypeFloat 64\n%2 = OpConstant %1 -3.125", 2, "double_n3_125"},
326 {"%1 = OpTypeFloat 64\n%2 = OpConstant %1 0x1.ffffffffffffap-1023", 2,
327 "double_0x1_ffffffffffffapn1023"}, // small normal
328 {"%1 = OpTypeFloat 64\n%2 = OpConstant %1 -0x1.ffffffffffffap-1023", 2,
329 "double_n0x1_ffffffffffffapn1023"},
330 {"%1 = OpTypeFloat 64\n%2 = OpConstant %1 0x1.8p+1024", 2,
331 "double_0x1_8p_1024"}, // NaN
332 {"%1 = OpTypeFloat 64\n%2 = OpConstant %1 -0x1.0002p+1024", 2,
333 "double_n0x1_0002p_1024"}, // NaN
334 {"%1 = OpTypeFloat 64\n%2 = OpConstant %1 0x1p+1024", 2,
335 "double_0x1p_1024"}, // Inf
336 {"%1 = OpTypeFloat 64\n%2 = OpConstant %1 -0x1p+1024", 2,
337 "double_n0x1p_1024"}, // -Inf
338 }));
339
340 INSTANTIATE_TEST_SUITE_P(
341 BooleanConstants, FriendlyNameTest,
342 ::testing::ValuesIn(std::vector<NameIdCase>{
343 {"%1 = OpTypeBool\n%2 = OpConstantTrue %1", 2, "true"},
344 {"%1 = OpTypeBool\n%2 = OpConstantFalse %1", 2, "false"},
345 }));
346
347 } // namespace
348 } // namespace spvtools
349