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 "name_mapper.h" 16 17 #include <cassert> 18 #include <algorithm> 19 #include <iterator> 20 #include <sstream> 21 #include <string> 22 #include <unordered_map> 23 #include <unordered_set> 24 25 #include "spirv-tools/libspirv.h" 26 #include "spirv/1.2/spirv.h" 27 28 #include "parsed_operand.h" 29 30 namespace { 31 32 // Converts a uint32_t to its string decimal representation. 33 std::string to_string(uint32_t id) { 34 // Use stringstream, since some versions of Android compilers lack 35 // std::to_string. 36 std::stringstream os; 37 os << id; 38 return os.str(); 39 } 40 41 } // anonymous namespace 42 43 namespace libspirv { 44 45 NameMapper GetTrivialNameMapper() { return to_string; } 46 47 FriendlyNameMapper::FriendlyNameMapper(const spv_const_context context, 48 const uint32_t* code, 49 const size_t wordCount) 50 : grammar_(libspirv::AssemblyGrammar(context)) { 51 spv_diagnostic diag = nullptr; 52 // We don't care if the parse fails. 53 spvBinaryParse(context, this, code, wordCount, nullptr, 54 ParseInstructionForwarder, &diag); 55 spvDiagnosticDestroy(diag); 56 } 57 58 std::string FriendlyNameMapper::NameForId(uint32_t id) { 59 auto iter = name_for_id_.find(id); 60 if (iter == name_for_id_.end()) { 61 // It must have been an invalid module, so just return a trivial mapping. 62 // We don't care about uniqueness. 63 return to_string(id); 64 } else { 65 return iter->second; 66 } 67 } 68 69 std::string FriendlyNameMapper::Sanitize(const std::string& suggested_name) { 70 if (suggested_name.empty()) return "_"; 71 // Otherwise, replace invalid characters by '_'. 72 std::string result; 73 std::string valid = 74 "abcdefghijklmnopqrstuvwxyz" 75 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 76 "_0123456789"; 77 std::transform(suggested_name.begin(), suggested_name.end(), 78 std::back_inserter(result), [&valid](const char c) { 79 return (std::string::npos == valid.find(c)) ? '_' : c; 80 }); 81 return result; 82 } 83 84 void FriendlyNameMapper::SaveName(uint32_t id, 85 const std::string& suggested_name) { 86 if (name_for_id_.find(id) != name_for_id_.end()) return; 87 88 const std::string sanitized_suggested_name = Sanitize(suggested_name); 89 std::string name = sanitized_suggested_name; 90 auto inserted = used_names_.insert(name); 91 if (!inserted.second) { 92 const std::string base_name = sanitized_suggested_name + "_"; 93 for (uint32_t index = 0; !inserted.second; ++index) { 94 name = base_name + to_string(index); 95 inserted = used_names_.insert(name); 96 } 97 } 98 name_for_id_[id] = name; 99 } 100 101 void FriendlyNameMapper::SaveBuiltInName(uint32_t target_id, 102 uint32_t built_in) { 103 #define GLCASE(name) \ 104 case SpvBuiltIn##name: \ 105 SaveName(target_id, "gl_" #name); \ 106 return; 107 #define GLCASE2(name, suggested) \ 108 case SpvBuiltIn##name: \ 109 SaveName(target_id, "gl_" #suggested); \ 110 return; 111 #define CASE(name) \ 112 case SpvBuiltIn##name: \ 113 SaveName(target_id, #name); \ 114 return; 115 switch (built_in) { 116 GLCASE(Position) 117 GLCASE(PointSize) 118 GLCASE(ClipDistance) 119 GLCASE(CullDistance) 120 GLCASE2(VertexId, VertexID) 121 GLCASE2(InstanceId, InstanceID) 122 GLCASE2(PrimitiveId, PrimitiveID) 123 GLCASE2(InvocationId, InvocationID) 124 GLCASE(Layer) 125 GLCASE(ViewportIndex) 126 GLCASE(TessLevelOuter) 127 GLCASE(TessLevelInner) 128 GLCASE(TessCoord) 129 GLCASE(PatchVertices) 130 GLCASE(FragCoord) 131 GLCASE(PointCoord) 132 GLCASE(FrontFacing) 133 GLCASE2(SampleId, SampleID) 134 GLCASE(SamplePosition) 135 GLCASE(SampleMask) 136 GLCASE(FragDepth) 137 GLCASE(HelperInvocation) 138 GLCASE2(NumWorkgroups, NumWorkGroups) 139 GLCASE2(WorkgroupSize, WorkGroupSize) 140 GLCASE2(WorkgroupId, WorkGroupID) 141 GLCASE2(LocalInvocationId, LocalInvocationID) 142 GLCASE2(GlobalInvocationId, GlobalInvocationID) 143 GLCASE(LocalInvocationIndex) 144 CASE(WorkDim) 145 CASE(GlobalSize) 146 CASE(EnqueuedWorkgroupSize) 147 CASE(GlobalOffset) 148 CASE(GlobalLinearId) 149 CASE(SubgroupSize) 150 CASE(SubgroupMaxSize) 151 CASE(NumSubgroups) 152 CASE(NumEnqueuedSubgroups) 153 CASE(SubgroupId) 154 CASE(SubgroupLocalInvocationId) 155 GLCASE(VertexIndex) 156 GLCASE(InstanceIndex) 157 CASE(SubgroupEqMaskKHR) 158 CASE(SubgroupGeMaskKHR) 159 CASE(SubgroupGtMaskKHR) 160 CASE(SubgroupLeMaskKHR) 161 CASE(SubgroupLtMaskKHR) 162 default: 163 break; 164 } 165 #undef GLCASE 166 #undef GLCASE2 167 #undef CASE 168 } 169 170 spv_result_t FriendlyNameMapper::ParseInstruction( 171 const spv_parsed_instruction_t& inst) { 172 const auto result_id = inst.result_id; 173 switch (inst.opcode) { 174 case SpvOpName: 175 SaveName(inst.words[1], reinterpret_cast<const char*>(inst.words + 2)); 176 break; 177 case SpvOpDecorate: 178 // Decorations come after OpName. So OpName will take precedence over 179 // decorations. 180 // 181 // In theory, we should also handle OpGroupDecorate. But that's unlikely 182 // to occur. 183 if (inst.words[2] == SpvDecorationBuiltIn) { 184 assert(inst.num_words > 3); 185 SaveBuiltInName(inst.words[1], inst.words[3]); 186 } 187 break; 188 case SpvOpTypeVoid: 189 SaveName(result_id, "void"); 190 break; 191 case SpvOpTypeBool: 192 SaveName(result_id, "bool"); 193 break; 194 case SpvOpTypeInt: { 195 std::string signedness; 196 std::string root; 197 const auto bit_width = inst.words[2]; 198 switch (bit_width) { 199 case 8: 200 root = "char"; 201 break; 202 case 16: 203 root = "short"; 204 break; 205 case 32: 206 root = "int"; 207 break; 208 case 64: 209 root = "long"; 210 break; 211 default: 212 root = to_string(bit_width); 213 signedness = "i"; 214 break; 215 } 216 if (0 == inst.words[3]) signedness = "u"; 217 SaveName(result_id, signedness + root); 218 } break; 219 case SpvOpTypeFloat: { 220 const auto bit_width = inst.words[2]; 221 switch (bit_width) { 222 case 16: 223 SaveName(result_id, "half"); 224 break; 225 case 32: 226 SaveName(result_id, "float"); 227 break; 228 case 64: 229 SaveName(result_id, "double"); 230 break; 231 default: 232 SaveName(result_id, std::string("fp") + to_string(bit_width)); 233 break; 234 } 235 } break; 236 case SpvOpTypeVector: 237 SaveName(result_id, std::string("v") + to_string(inst.words[3]) + 238 NameForId(inst.words[2])); 239 break; 240 case SpvOpTypeMatrix: 241 SaveName(result_id, std::string("mat") + to_string(inst.words[3]) + 242 NameForId(inst.words[2])); 243 break; 244 case SpvOpTypeArray: 245 SaveName(result_id, std::string("_arr_") + NameForId(inst.words[2]) + 246 "_" + NameForId(inst.words[3])); 247 break; 248 case SpvOpTypeRuntimeArray: 249 SaveName(result_id, 250 std::string("_runtimearr_") + NameForId(inst.words[2])); 251 break; 252 case SpvOpTypePointer: 253 SaveName(result_id, std::string("_ptr_") + 254 NameForEnumOperand(SPV_OPERAND_TYPE_STORAGE_CLASS, 255 inst.words[2]) + 256 "_" + NameForId(inst.words[3])); 257 break; 258 case SpvOpTypePipe: 259 SaveName(result_id, 260 std::string("Pipe") + 261 NameForEnumOperand(SPV_OPERAND_TYPE_ACCESS_QUALIFIER, 262 inst.words[2])); 263 break; 264 case SpvOpTypeEvent: 265 SaveName(result_id, "Event"); 266 break; 267 case SpvOpTypeDeviceEvent: 268 SaveName(result_id, "DeviceEvent"); 269 break; 270 case SpvOpTypeReserveId: 271 SaveName(result_id, "ReserveId"); 272 break; 273 case SpvOpTypeQueue: 274 SaveName(result_id, "Queue"); 275 break; 276 case SpvOpTypeOpaque: 277 SaveName(result_id, 278 std::string("Opaque_") + 279 Sanitize(reinterpret_cast<const char*>(inst.words + 2))); 280 break; 281 case SpvOpTypePipeStorage: 282 SaveName(result_id, "PipeStorage"); 283 break; 284 case SpvOpTypeNamedBarrier: 285 SaveName(result_id, "NamedBarrier"); 286 break; 287 case SpvOpTypeStruct: 288 // Structs are mapped rather simplisitically. Just indicate that they 289 // are a struct and then give the raw Id number. 290 SaveName(result_id, std::string("_struct_") + to_string(result_id)); 291 break; 292 case SpvOpConstantTrue: 293 SaveName(result_id, "true"); 294 break; 295 case SpvOpConstantFalse: 296 SaveName(result_id, "false"); 297 break; 298 case SpvOpConstant: { 299 std::ostringstream value; 300 EmitNumericLiteral(&value, inst, inst.operands[2]); 301 auto value_str = value.str(); 302 // Use 'n' to signify negative. Other invalid characters will be mapped 303 // to underscore. 304 for (auto& c : value_str) 305 if (c == '-') c = 'n'; 306 SaveName(result_id, NameForId(inst.type_id) + "_" + value_str); 307 } break; 308 default: 309 // If this instruction otherwise defines an Id, then save a mapping for 310 // it. This is needed to ensure uniqueness in there is an OpName with 311 // string something like "1" that might collide with this result_id. 312 // We should only do this if a name hasn't already been registered by some 313 // previous forward reference. 314 if (result_id && name_for_id_.find(result_id) == name_for_id_.end()) 315 SaveName(result_id, to_string(result_id)); 316 break; 317 } 318 return SPV_SUCCESS; 319 } 320 321 std::string FriendlyNameMapper::NameForEnumOperand(spv_operand_type_t type, 322 uint32_t word) { 323 spv_operand_desc desc = nullptr; 324 if (SPV_SUCCESS == grammar_.lookupOperand(type, word, &desc)) { 325 return desc->name; 326 } else { 327 // Invalid input. Just give something sane. 328 return std::string("StorageClass") + to_string(word); 329 } 330 } 331 332 } // namespace libspirv 333