name_mapper.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. // Copyright (c) 2016 Google Inc.
  2. // Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights
  3. // reserved.
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License");
  6. // you may not use this file except in compliance with the License.
  7. // You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. #include "source/name_mapper.h"
  17. #include <algorithm>
  18. #include <cassert>
  19. #include <iterator>
  20. #include <sstream>
  21. #include <string>
  22. #include <unordered_map>
  23. #include <unordered_set>
  24. #include "source/binary.h"
  25. #include "source/latest_version_spirv_header.h"
  26. #include "source/parsed_operand.h"
  27. #include "source/to_string.h"
  28. #include "spirv-tools/libspirv.h"
  29. namespace spvtools {
  30. NameMapper GetTrivialNameMapper() {
  31. return [](uint32_t i) { return spvtools::to_string(i); };
  32. }
  33. FriendlyNameMapper::FriendlyNameMapper(const spv_const_context context,
  34. const uint32_t* code,
  35. const size_t wordCount)
  36. : grammar_(AssemblyGrammar(context)) {
  37. spv_diagnostic diag = nullptr;
  38. // We don't care if the parse fails.
  39. spvBinaryParse(context, this, code, wordCount, nullptr,
  40. ParseInstructionForwarder, &diag);
  41. spvDiagnosticDestroy(diag);
  42. }
  43. std::string FriendlyNameMapper::NameForId(uint32_t id) {
  44. auto iter = name_for_id_.find(id);
  45. if (iter == name_for_id_.end()) {
  46. // It must have been an invalid module, so just return a trivial mapping.
  47. // We don't care about uniqueness.
  48. return to_string(id);
  49. } else {
  50. return iter->second;
  51. }
  52. }
  53. std::string FriendlyNameMapper::Sanitize(const std::string& suggested_name) {
  54. if (suggested_name.empty()) return "_";
  55. // Otherwise, replace invalid characters by '_'.
  56. std::string result;
  57. std::string valid =
  58. "abcdefghijklmnopqrstuvwxyz"
  59. "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  60. "_0123456789";
  61. std::transform(suggested_name.begin(), suggested_name.end(),
  62. std::back_inserter(result), [&valid](const char c) {
  63. return (std::string::npos == valid.find(c)) ? '_' : c;
  64. });
  65. return result;
  66. }
  67. void FriendlyNameMapper::SaveName(uint32_t id,
  68. const std::string& suggested_name) {
  69. if (name_for_id_.find(id) != name_for_id_.end()) return;
  70. const std::string sanitized_suggested_name = Sanitize(suggested_name);
  71. std::string name = sanitized_suggested_name;
  72. auto inserted = used_names_.insert(name);
  73. if (!inserted.second) {
  74. const std::string base_name = sanitized_suggested_name + "_";
  75. for (uint32_t index = 0; !inserted.second; ++index) {
  76. name = base_name + to_string(index);
  77. inserted = used_names_.insert(name);
  78. }
  79. }
  80. name_for_id_[id] = name;
  81. }
  82. void FriendlyNameMapper::SaveBuiltInName(uint32_t target_id,
  83. uint32_t built_in) {
  84. #define GLCASE(name) \
  85. case spv::BuiltIn::name: \
  86. SaveName(target_id, "gl_" #name); \
  87. return;
  88. #define GLCASE2(name, suggested) \
  89. case spv::BuiltIn::name: \
  90. SaveName(target_id, "gl_" #suggested); \
  91. return;
  92. #define CASE(name) \
  93. case spv::BuiltIn::name: \
  94. SaveName(target_id, #name); \
  95. return;
  96. switch (spv::BuiltIn(built_in)) {
  97. GLCASE(Position)
  98. GLCASE(PointSize)
  99. GLCASE(ClipDistance)
  100. GLCASE(CullDistance)
  101. GLCASE2(VertexId, VertexID)
  102. GLCASE2(InstanceId, InstanceID)
  103. GLCASE2(PrimitiveId, PrimitiveID)
  104. GLCASE2(InvocationId, InvocationID)
  105. GLCASE(Layer)
  106. GLCASE(ViewportIndex)
  107. GLCASE(TessLevelOuter)
  108. GLCASE(TessLevelInner)
  109. GLCASE(TessCoord)
  110. GLCASE(PatchVertices)
  111. GLCASE(FragCoord)
  112. GLCASE(PointCoord)
  113. GLCASE(FrontFacing)
  114. GLCASE2(SampleId, SampleID)
  115. GLCASE(SamplePosition)
  116. GLCASE(SampleMask)
  117. GLCASE(FragDepth)
  118. GLCASE(HelperInvocation)
  119. GLCASE2(NumWorkgroups, NumWorkGroups)
  120. GLCASE2(WorkgroupSize, WorkGroupSize)
  121. GLCASE2(WorkgroupId, WorkGroupID)
  122. GLCASE2(LocalInvocationId, LocalInvocationID)
  123. GLCASE2(GlobalInvocationId, GlobalInvocationID)
  124. GLCASE(LocalInvocationIndex)
  125. CASE(WorkDim)
  126. CASE(GlobalSize)
  127. CASE(EnqueuedWorkgroupSize)
  128. CASE(GlobalOffset)
  129. CASE(GlobalLinearId)
  130. CASE(SubgroupSize)
  131. CASE(SubgroupMaxSize)
  132. CASE(NumSubgroups)
  133. CASE(NumEnqueuedSubgroups)
  134. CASE(SubgroupId)
  135. CASE(SubgroupLocalInvocationId)
  136. GLCASE(VertexIndex)
  137. GLCASE(InstanceIndex)
  138. GLCASE(BaseInstance)
  139. CASE(SubgroupEqMaskKHR)
  140. CASE(SubgroupGeMaskKHR)
  141. CASE(SubgroupGtMaskKHR)
  142. CASE(SubgroupLeMaskKHR)
  143. CASE(SubgroupLtMaskKHR)
  144. default:
  145. break;
  146. }
  147. #undef GLCASE
  148. #undef GLCASE2
  149. #undef CASE
  150. }
  151. spv_result_t FriendlyNameMapper::ParseInstruction(
  152. const spv_parsed_instruction_t& inst) {
  153. const auto result_id = inst.result_id;
  154. switch (spv::Op(inst.opcode)) {
  155. case spv::Op::OpName:
  156. SaveName(inst.words[1], spvDecodeLiteralStringOperand(inst, 1));
  157. break;
  158. case spv::Op::OpDecorate:
  159. // Decorations come after OpName. So OpName will take precedence over
  160. // decorations.
  161. //
  162. // In theory, we should also handle OpGroupDecorate. But that's unlikely
  163. // to occur.
  164. if (spv::Decoration(inst.words[2]) == spv::Decoration::BuiltIn) {
  165. assert(inst.num_words > 3);
  166. SaveBuiltInName(inst.words[1], inst.words[3]);
  167. }
  168. break;
  169. case spv::Op::OpTypeVoid:
  170. SaveName(result_id, "void");
  171. break;
  172. case spv::Op::OpTypeBool:
  173. SaveName(result_id, "bool");
  174. break;
  175. case spv::Op::OpTypeInt: {
  176. std::string signedness;
  177. std::string root;
  178. const auto bit_width = inst.words[2];
  179. switch (bit_width) {
  180. case 8:
  181. root = "char";
  182. break;
  183. case 16:
  184. root = "short";
  185. break;
  186. case 32:
  187. root = "int";
  188. break;
  189. case 64:
  190. root = "long";
  191. break;
  192. default:
  193. root = to_string(bit_width);
  194. signedness = "i";
  195. break;
  196. }
  197. if (0 == inst.words[3]) signedness = "u";
  198. SaveName(result_id, signedness + root);
  199. } break;
  200. case spv::Op::OpTypeFloat: {
  201. const auto bit_width = inst.words[2];
  202. // TODO: Handle optional fpencoding enum once actually used.
  203. switch (bit_width) {
  204. case 16:
  205. SaveName(result_id, "half");
  206. break;
  207. case 32:
  208. SaveName(result_id, "float");
  209. break;
  210. case 64:
  211. SaveName(result_id, "double");
  212. break;
  213. default:
  214. SaveName(result_id, std::string("fp") + to_string(bit_width));
  215. break;
  216. }
  217. } break;
  218. case spv::Op::OpTypeVector:
  219. SaveName(result_id, std::string("v") + to_string(inst.words[3]) +
  220. NameForId(inst.words[2]));
  221. break;
  222. case spv::Op::OpTypeMatrix:
  223. SaveName(result_id, std::string("mat") + to_string(inst.words[3]) +
  224. NameForId(inst.words[2]));
  225. break;
  226. case spv::Op::OpTypeArray:
  227. SaveName(result_id, std::string("_arr_") + NameForId(inst.words[2]) +
  228. "_" + NameForId(inst.words[3]));
  229. break;
  230. case spv::Op::OpTypeRuntimeArray:
  231. SaveName(result_id,
  232. std::string("_runtimearr_") + NameForId(inst.words[2]));
  233. break;
  234. case spv::Op::OpTypeNodePayloadArrayAMDX:
  235. SaveName(result_id,
  236. std::string("_payloadarr_") + NameForId(inst.words[2]));
  237. break;
  238. case spv::Op::OpTypePointer:
  239. SaveName(result_id, std::string("_ptr_") +
  240. NameForEnumOperand(SPV_OPERAND_TYPE_STORAGE_CLASS,
  241. inst.words[2]) +
  242. "_" + NameForId(inst.words[3]));
  243. break;
  244. case spv::Op::OpTypeUntypedPointerKHR:
  245. SaveName(result_id, std::string("_ptr_") +
  246. NameForEnumOperand(SPV_OPERAND_TYPE_STORAGE_CLASS,
  247. inst.words[2]));
  248. break;
  249. case spv::Op::OpTypePipe:
  250. SaveName(result_id,
  251. std::string("Pipe") +
  252. NameForEnumOperand(SPV_OPERAND_TYPE_ACCESS_QUALIFIER,
  253. inst.words[2]));
  254. break;
  255. case spv::Op::OpTypeEvent:
  256. SaveName(result_id, "Event");
  257. break;
  258. case spv::Op::OpTypeDeviceEvent:
  259. SaveName(result_id, "DeviceEvent");
  260. break;
  261. case spv::Op::OpTypeReserveId:
  262. SaveName(result_id, "ReserveId");
  263. break;
  264. case spv::Op::OpTypeQueue:
  265. SaveName(result_id, "Queue");
  266. break;
  267. case spv::Op::OpTypeOpaque:
  268. SaveName(result_id, std::string("Opaque_") +
  269. Sanitize(spvDecodeLiteralStringOperand(inst, 1)));
  270. break;
  271. case spv::Op::OpTypePipeStorage:
  272. SaveName(result_id, "PipeStorage");
  273. break;
  274. case spv::Op::OpTypeNamedBarrier:
  275. SaveName(result_id, "NamedBarrier");
  276. break;
  277. case spv::Op::OpTypeStruct:
  278. // Structs are mapped rather simplisitically. Just indicate that they
  279. // are a struct and then give the raw Id number.
  280. SaveName(result_id, std::string("_struct_") + to_string(result_id));
  281. break;
  282. case spv::Op::OpConstantTrue:
  283. SaveName(result_id, "true");
  284. break;
  285. case spv::Op::OpConstantFalse:
  286. SaveName(result_id, "false");
  287. break;
  288. case spv::Op::OpConstant: {
  289. std::ostringstream value;
  290. EmitNumericLiteral(&value, inst, inst.operands[2]);
  291. auto value_str = value.str();
  292. // Use 'n' to signify negative. Other invalid characters will be mapped
  293. // to underscore.
  294. for (auto& c : value_str)
  295. if (c == '-') c = 'n';
  296. SaveName(result_id, NameForId(inst.type_id) + "_" + value_str);
  297. } break;
  298. default:
  299. // If this instruction otherwise defines an Id, then save a mapping for
  300. // it. This is needed to ensure uniqueness in there is an OpName with
  301. // string something like "1" that might collide with this result_id.
  302. // We should only do this if a name hasn't already been registered by some
  303. // previous forward reference.
  304. if (result_id && name_for_id_.find(result_id) == name_for_id_.end())
  305. SaveName(result_id, to_string(result_id));
  306. break;
  307. }
  308. return SPV_SUCCESS;
  309. }
  310. std::string FriendlyNameMapper::NameForEnumOperand(spv_operand_type_t type,
  311. uint32_t word) {
  312. spv_operand_desc desc = nullptr;
  313. if (SPV_SUCCESS == grammar_.lookupOperand(type, word, &desc)) {
  314. return desc->name;
  315. } else {
  316. // Invalid input. Just give something.
  317. return std::string("StorageClass") + to_string(word);
  318. }
  319. }
  320. } // namespace spvtools