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