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
23namespace spvtools {
24namespace {
25
26using spvtest::ScopedContext;
27using ::testing::Eq;
28
29TEST(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.
37struct NameIdCase {
38  std::string assembly;  // Input assembly text
39  uint32_t id;
40  std::string expected_name;
41};
42
43using FriendlyNameTest =
44    spvtest::TextToBinaryTestBase<::testing::TestWithParam<NameIdCase>>;
45
46TEST_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
57INSTANTIATE_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
81INSTANTIATE_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
102INSTANTIATE_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
119INSTANTIATE_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
151INSTANTIATE_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
159INSTANTIATE_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
169INSTANTIATE_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
180INSTANTIATE_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
194INSTANTIATE_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.
210NameIdCase 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.
218NameIdCase 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.
225NameIdCase BuiltInGLCase(std::string assembly_name) {
226  return BuiltInCase(assembly_name, std::string("gl_") + assembly_name);
227}
228
229INSTANTIATE_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
281INSTANTIATE_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
287INSTANTIATE_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
307INSTANTIATE_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
340INSTANTIATE_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