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         BuiltInCase("SubgroupEqMaskKHR"),
274         BuiltInCase("SubgroupGeMaskKHR"),
275         BuiltInCase("SubgroupGtMaskKHR"),
276         BuiltInCase("SubgroupLeMaskKHR"),
277         BuiltInCase("SubgroupLtMaskKHR"),
278     }));
279 
280 INSTANTIATE_TEST_SUITE_P(DebugNameOverridesBuiltin, FriendlyNameTest,
281                          ::testing::ValuesIn(std::vector<NameIdCase>{
282                              {"OpName %1 \"foo\" OpDecorate %1 BuiltIn WorkDim "
283                               "%1 = OpVariable %2 Input",
284                               1, "foo"}}));
285 
286 INSTANTIATE_TEST_SUITE_P(
287     SimpleIntegralConstants, FriendlyNameTest,
288     ::testing::ValuesIn(std::vector<NameIdCase>{
289         {"%1 = OpTypeInt 32 0 %2 = OpConstant %1 0", 2, "uint_0"},
290         {"%1 = OpTypeInt 32 0 %2 = OpConstant %1 1", 2, "uint_1"},
291         {"%1 = OpTypeInt 32 0 %2 = OpConstant %1 2", 2, "uint_2"},
292         {"%1 = OpTypeInt 32 0 %2 = OpConstant %1 9", 2, "uint_9"},
293         {"%1 = OpTypeInt 32 0 %2 = OpConstant %1 42", 2, "uint_42"},
294         {"%1 = OpTypeInt 32 1 %2 = OpConstant %1 0", 2, "int_0"},
295         {"%1 = OpTypeInt 32 1 %2 = OpConstant %1 1", 2, "int_1"},
296         {"%1 = OpTypeInt 32 1 %2 = OpConstant %1 2", 2, "int_2"},
297         {"%1 = OpTypeInt 32 1 %2 = OpConstant %1 9", 2, "int_9"},
298         {"%1 = OpTypeInt 32 1 %2 = OpConstant %1 42", 2, "int_42"},
299         {"%1 = OpTypeInt 32 1 %2 = OpConstant %1 -42", 2, "int_n42"},
300         // Exotic bit widths
301         {"%1 = OpTypeInt 33 0 %2 = OpConstant %1 0", 2, "u33_0"},
302         {"%1 = OpTypeInt 33 1 %2 = OpConstant %1 10", 2, "i33_10"},
303         {"%1 = OpTypeInt 33 1 %2 = OpConstant %1 -19", 2, "i33_n19"},
304     }));
305 
306 INSTANTIATE_TEST_SUITE_P(
307     SimpleFloatConstants, FriendlyNameTest,
308     ::testing::ValuesIn(std::vector<NameIdCase>{
309         {"%1 = OpTypeFloat 16\n%2 = OpConstant %1 0x1.ff4p+16", 2,
310          "half_0x1_ff4p_16"},
311         {"%1 = OpTypeFloat 16\n%2 = OpConstant %1 -0x1.d2cp-10", 2,
312          "half_n0x1_d2cpn10"},
313         // 32-bit floats
314         {"%1 = OpTypeFloat 32\n%2 = OpConstant %1 -3.125", 2, "float_n3_125"},
315         {"%1 = OpTypeFloat 32\n%2 = OpConstant %1 0x1.8p+128", 2,
316          "float_0x1_8p_128"},  // NaN
317         {"%1 = OpTypeFloat 32\n%2 = OpConstant %1 -0x1.0002p+128", 2,
318          "float_n0x1_0002p_128"},  // NaN
319         {"%1 = OpTypeFloat 32\n%2 = OpConstant %1 0x1p+128", 2,
320          "float_0x1p_128"},  // Inf
321         {"%1 = OpTypeFloat 32\n%2 = OpConstant %1 -0x1p+128", 2,
322          "float_n0x1p_128"},  // -Inf
323                               // 64-bit floats
324         {"%1 = OpTypeFloat 64\n%2 = OpConstant %1 -3.125", 2, "double_n3_125"},
325         {"%1 = OpTypeFloat 64\n%2 = OpConstant %1 0x1.ffffffffffffap-1023", 2,
326          "double_0x1_ffffffffffffapn1023"},  // small normal
327         {"%1 = OpTypeFloat 64\n%2 = OpConstant %1 -0x1.ffffffffffffap-1023", 2,
328          "double_n0x1_ffffffffffffapn1023"},
329         {"%1 = OpTypeFloat 64\n%2 = OpConstant %1 0x1.8p+1024", 2,
330          "double_0x1_8p_1024"},  // NaN
331         {"%1 = OpTypeFloat 64\n%2 = OpConstant %1 -0x1.0002p+1024", 2,
332          "double_n0x1_0002p_1024"},  // NaN
333         {"%1 = OpTypeFloat 64\n%2 = OpConstant %1 0x1p+1024", 2,
334          "double_0x1p_1024"},  // Inf
335         {"%1 = OpTypeFloat 64\n%2 = OpConstant %1 -0x1p+1024", 2,
336          "double_n0x1p_1024"},  // -Inf
337     }));
338 
339 INSTANTIATE_TEST_SUITE_P(
340     BooleanConstants, FriendlyNameTest,
341     ::testing::ValuesIn(std::vector<NameIdCase>{
342         {"%1 = OpTypeBool\n%2 = OpConstantTrue %1", 2, "true"},
343         {"%1 = OpTypeBool\n%2 = OpConstantFalse %1", 2, "false"},
344     }));
345 
346 }  // namespace
347 }  // namespace spvtools
348