fuzzer_util.cpp 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044
  1. // Copyright (c) 2019 Google LLC
  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. #include "source/fuzz/fuzzer_util.h"
  15. #include <algorithm>
  16. #include <unordered_set>
  17. #include "source/opt/build_module.h"
  18. namespace spvtools {
  19. namespace fuzz {
  20. namespace fuzzerutil {
  21. bool IsFreshId(opt::IRContext* context, uint32_t id) {
  22. return !context->get_def_use_mgr()->GetDef(id);
  23. }
  24. void UpdateModuleIdBound(opt::IRContext* context, uint32_t id) {
  25. // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2541) consider the
  26. // case where the maximum id bound is reached.
  27. context->module()->SetIdBound(
  28. std::max(context->module()->id_bound(), id + 1));
  29. }
  30. opt::BasicBlock* MaybeFindBlock(opt::IRContext* context,
  31. uint32_t maybe_block_id) {
  32. auto inst = context->get_def_use_mgr()->GetDef(maybe_block_id);
  33. if (inst == nullptr) {
  34. // No instruction defining this id was found.
  35. return nullptr;
  36. }
  37. if (inst->opcode() != SpvOpLabel) {
  38. // The instruction defining the id is not a label, so it cannot be a block
  39. // id.
  40. return nullptr;
  41. }
  42. return context->cfg()->block(maybe_block_id);
  43. }
  44. bool PhiIdsOkForNewEdge(
  45. opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to,
  46. const google::protobuf::RepeatedField<google::protobuf::uint32>& phi_ids) {
  47. if (bb_from->IsSuccessor(bb_to)) {
  48. // There is already an edge from |from_block| to |to_block|, so there is
  49. // no need to extend OpPhi instructions. Do not allow phi ids to be
  50. // present. This might turn out to be too strict; perhaps it would be OK
  51. // just to ignore the ids in this case.
  52. return phi_ids.empty();
  53. }
  54. // The edge would add a previously non-existent edge from |from_block| to
  55. // |to_block|, so we go through the given phi ids and check that they exactly
  56. // match the OpPhi instructions in |to_block|.
  57. uint32_t phi_index = 0;
  58. // An explicit loop, rather than applying a lambda to each OpPhi in |bb_to|,
  59. // makes sense here because we need to increment |phi_index| for each OpPhi
  60. // instruction.
  61. for (auto& inst : *bb_to) {
  62. if (inst.opcode() != SpvOpPhi) {
  63. // The OpPhi instructions all occur at the start of the block; if we find
  64. // a non-OpPhi then we have seen them all.
  65. break;
  66. }
  67. if (phi_index == static_cast<uint32_t>(phi_ids.size())) {
  68. // Not enough phi ids have been provided to account for the OpPhi
  69. // instructions.
  70. return false;
  71. }
  72. // Look for an instruction defining the next phi id.
  73. opt::Instruction* phi_extension =
  74. context->get_def_use_mgr()->GetDef(phi_ids[phi_index]);
  75. if (!phi_extension) {
  76. // The id given to extend this OpPhi does not exist.
  77. return false;
  78. }
  79. if (phi_extension->type_id() != inst.type_id()) {
  80. // The instruction given to extend this OpPhi either does not have a type
  81. // or its type does not match that of the OpPhi.
  82. return false;
  83. }
  84. if (context->get_instr_block(phi_extension)) {
  85. // The instruction defining the phi id has an associated block (i.e., it
  86. // is not a global value). Check whether its definition dominates the
  87. // exit of |from_block|.
  88. auto dominator_analysis =
  89. context->GetDominatorAnalysis(bb_from->GetParent());
  90. if (!dominator_analysis->Dominates(phi_extension,
  91. bb_from->terminator())) {
  92. // The given id is no good as its definition does not dominate the exit
  93. // of |from_block|
  94. return false;
  95. }
  96. }
  97. phi_index++;
  98. }
  99. // We allow some of the ids provided for extending OpPhi instructions to be
  100. // unused. Their presence does no harm, and requiring a perfect match may
  101. // make transformations less likely to cleanly apply.
  102. return true;
  103. }
  104. void AddUnreachableEdgeAndUpdateOpPhis(
  105. opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to,
  106. bool condition_value,
  107. const google::protobuf::RepeatedField<google::protobuf::uint32>& phi_ids) {
  108. assert(PhiIdsOkForNewEdge(context, bb_from, bb_to, phi_ids) &&
  109. "Precondition on phi_ids is not satisfied");
  110. assert(bb_from->terminator()->opcode() == SpvOpBranch &&
  111. "Precondition on terminator of bb_from is not satisfied");
  112. // Get the id of the boolean constant to be used as the condition.
  113. uint32_t bool_id = MaybeGetBoolConstant(context, condition_value);
  114. assert(
  115. bool_id &&
  116. "Precondition that condition value must be available is not satisfied");
  117. const bool from_to_edge_already_exists = bb_from->IsSuccessor(bb_to);
  118. auto successor = bb_from->terminator()->GetSingleWordInOperand(0);
  119. // Add the dead branch, by turning OpBranch into OpBranchConditional, and
  120. // ordering the targets depending on whether the given boolean corresponds to
  121. // true or false.
  122. bb_from->terminator()->SetOpcode(SpvOpBranchConditional);
  123. bb_from->terminator()->SetInOperands(
  124. {{SPV_OPERAND_TYPE_ID, {bool_id}},
  125. {SPV_OPERAND_TYPE_ID, {condition_value ? successor : bb_to->id()}},
  126. {SPV_OPERAND_TYPE_ID, {condition_value ? bb_to->id() : successor}}});
  127. // Update OpPhi instructions in the target block if this branch adds a
  128. // previously non-existent edge from source to target.
  129. if (!from_to_edge_already_exists) {
  130. uint32_t phi_index = 0;
  131. for (auto& inst : *bb_to) {
  132. if (inst.opcode() != SpvOpPhi) {
  133. break;
  134. }
  135. assert(phi_index < static_cast<uint32_t>(phi_ids.size()) &&
  136. "There should be at least one phi id per OpPhi instruction.");
  137. inst.AddOperand({SPV_OPERAND_TYPE_ID, {phi_ids[phi_index]}});
  138. inst.AddOperand({SPV_OPERAND_TYPE_ID, {bb_from->id()}});
  139. phi_index++;
  140. }
  141. }
  142. }
  143. bool BlockIsBackEdge(opt::IRContext* context, uint32_t block_id,
  144. uint32_t loop_header_id) {
  145. auto block = context->cfg()->block(block_id);
  146. auto loop_header = context->cfg()->block(loop_header_id);
  147. // |block| and |loop_header| must be defined, |loop_header| must be in fact
  148. // loop header and |block| must branch to it.
  149. if (!(block && loop_header && loop_header->IsLoopHeader() &&
  150. block->IsSuccessor(loop_header))) {
  151. return false;
  152. }
  153. // |block_id| must be reachable and be dominated by |loop_header|.
  154. opt::DominatorAnalysis* dominator_analysis =
  155. context->GetDominatorAnalysis(loop_header->GetParent());
  156. return dominator_analysis->IsReachable(block_id) &&
  157. dominator_analysis->Dominates(loop_header_id, block_id);
  158. }
  159. bool BlockIsInLoopContinueConstruct(opt::IRContext* context, uint32_t block_id,
  160. uint32_t maybe_loop_header_id) {
  161. // We deem a block to be part of a loop's continue construct if the loop's
  162. // continue target dominates the block.
  163. auto containing_construct_block = context->cfg()->block(maybe_loop_header_id);
  164. if (containing_construct_block->IsLoopHeader()) {
  165. auto continue_target = containing_construct_block->ContinueBlockId();
  166. if (context->GetDominatorAnalysis(containing_construct_block->GetParent())
  167. ->Dominates(continue_target, block_id)) {
  168. return true;
  169. }
  170. }
  171. return false;
  172. }
  173. opt::BasicBlock::iterator GetIteratorForInstruction(
  174. opt::BasicBlock* block, const opt::Instruction* inst) {
  175. for (auto inst_it = block->begin(); inst_it != block->end(); ++inst_it) {
  176. if (inst == &*inst_it) {
  177. return inst_it;
  178. }
  179. }
  180. return block->end();
  181. }
  182. bool BlockIsReachableInItsFunction(opt::IRContext* context,
  183. opt::BasicBlock* bb) {
  184. auto enclosing_function = bb->GetParent();
  185. return context->GetDominatorAnalysis(enclosing_function)
  186. ->Dominates(enclosing_function->entry().get(), bb);
  187. }
  188. bool CanInsertOpcodeBeforeInstruction(
  189. SpvOp opcode, const opt::BasicBlock::iterator& instruction_in_block) {
  190. if (instruction_in_block->PreviousNode() &&
  191. (instruction_in_block->PreviousNode()->opcode() == SpvOpLoopMerge ||
  192. instruction_in_block->PreviousNode()->opcode() == SpvOpSelectionMerge)) {
  193. // We cannot insert directly after a merge instruction.
  194. return false;
  195. }
  196. if (opcode != SpvOpVariable &&
  197. instruction_in_block->opcode() == SpvOpVariable) {
  198. // We cannot insert a non-OpVariable instruction directly before a
  199. // variable; variables in a function must be contiguous in the entry block.
  200. return false;
  201. }
  202. // We cannot insert a non-OpPhi instruction directly before an OpPhi, because
  203. // OpPhi instructions need to be contiguous at the start of a block.
  204. return opcode == SpvOpPhi || instruction_in_block->opcode() != SpvOpPhi;
  205. }
  206. bool CanMakeSynonymOf(opt::IRContext* ir_context, opt::Instruction* inst) {
  207. if (inst->opcode() == SpvOpSampledImage) {
  208. // The SPIR-V data rules say that only very specific instructions may
  209. // may consume the result id of an OpSampledImage, and this excludes the
  210. // instructions that are used for making synonyms.
  211. return false;
  212. }
  213. if (!inst->HasResultId()) {
  214. // We can only make a synonym of an instruction that generates an id.
  215. return false;
  216. }
  217. if (!inst->type_id()) {
  218. // We can only make a synonym of an instruction that has a type.
  219. return false;
  220. }
  221. auto type_inst = ir_context->get_def_use_mgr()->GetDef(inst->type_id());
  222. if (type_inst->opcode() == SpvOpTypePointer) {
  223. switch (inst->opcode()) {
  224. case SpvOpConstantNull:
  225. case SpvOpUndef:
  226. // We disallow making synonyms of null or undefined pointers. This is
  227. // to provide the property that if the original shader exhibited no bad
  228. // pointer accesses, the transformed shader will not either.
  229. return false;
  230. default:
  231. break;
  232. }
  233. }
  234. // We do not make synonyms of objects that have decorations: if the synonym is
  235. // not decorated analogously, using the original object vs. its synonymous
  236. // form may not be equivalent.
  237. return ir_context->get_decoration_mgr()
  238. ->GetDecorationsFor(inst->result_id(), true)
  239. .empty();
  240. }
  241. bool IsCompositeType(const opt::analysis::Type* type) {
  242. return type && (type->AsArray() || type->AsMatrix() || type->AsStruct() ||
  243. type->AsVector());
  244. }
  245. std::vector<uint32_t> RepeatedFieldToVector(
  246. const google::protobuf::RepeatedField<uint32_t>& repeated_field) {
  247. std::vector<uint32_t> result;
  248. for (auto i : repeated_field) {
  249. result.push_back(i);
  250. }
  251. return result;
  252. }
  253. uint32_t WalkOneCompositeTypeIndex(opt::IRContext* context,
  254. uint32_t base_object_type_id,
  255. uint32_t index) {
  256. auto should_be_composite_type =
  257. context->get_def_use_mgr()->GetDef(base_object_type_id);
  258. assert(should_be_composite_type && "The type should exist.");
  259. switch (should_be_composite_type->opcode()) {
  260. case SpvOpTypeArray: {
  261. auto array_length = GetArraySize(*should_be_composite_type, context);
  262. if (array_length == 0 || index >= array_length) {
  263. return 0;
  264. }
  265. return should_be_composite_type->GetSingleWordInOperand(0);
  266. }
  267. case SpvOpTypeMatrix:
  268. case SpvOpTypeVector: {
  269. auto count = should_be_composite_type->GetSingleWordInOperand(1);
  270. if (index >= count) {
  271. return 0;
  272. }
  273. return should_be_composite_type->GetSingleWordInOperand(0);
  274. }
  275. case SpvOpTypeStruct: {
  276. if (index >= GetNumberOfStructMembers(*should_be_composite_type)) {
  277. return 0;
  278. }
  279. return should_be_composite_type->GetSingleWordInOperand(index);
  280. }
  281. default:
  282. return 0;
  283. }
  284. }
  285. uint32_t WalkCompositeTypeIndices(
  286. opt::IRContext* context, uint32_t base_object_type_id,
  287. const google::protobuf::RepeatedField<google::protobuf::uint32>& indices) {
  288. uint32_t sub_object_type_id = base_object_type_id;
  289. for (auto index : indices) {
  290. sub_object_type_id =
  291. WalkOneCompositeTypeIndex(context, sub_object_type_id, index);
  292. if (!sub_object_type_id) {
  293. return 0;
  294. }
  295. }
  296. return sub_object_type_id;
  297. }
  298. uint32_t GetNumberOfStructMembers(
  299. const opt::Instruction& struct_type_instruction) {
  300. assert(struct_type_instruction.opcode() == SpvOpTypeStruct &&
  301. "An OpTypeStruct instruction is required here.");
  302. return struct_type_instruction.NumInOperands();
  303. }
  304. uint32_t GetArraySize(const opt::Instruction& array_type_instruction,
  305. opt::IRContext* context) {
  306. auto array_length_constant =
  307. context->get_constant_mgr()
  308. ->GetConstantFromInst(context->get_def_use_mgr()->GetDef(
  309. array_type_instruction.GetSingleWordInOperand(1)))
  310. ->AsIntConstant();
  311. if (array_length_constant->words().size() != 1) {
  312. return 0;
  313. }
  314. return array_length_constant->GetU32();
  315. }
  316. bool IsValid(opt::IRContext* context, spv_validator_options validator_options) {
  317. std::vector<uint32_t> binary;
  318. context->module()->ToBinary(&binary, false);
  319. SpirvTools tools(context->grammar().target_env());
  320. return tools.Validate(binary.data(), binary.size(), validator_options);
  321. }
  322. std::unique_ptr<opt::IRContext> CloneIRContext(opt::IRContext* context) {
  323. std::vector<uint32_t> binary;
  324. context->module()->ToBinary(&binary, false);
  325. return BuildModule(context->grammar().target_env(), nullptr, binary.data(),
  326. binary.size());
  327. }
  328. bool IsNonFunctionTypeId(opt::IRContext* ir_context, uint32_t id) {
  329. auto type = ir_context->get_type_mgr()->GetType(id);
  330. return type && !type->AsFunction();
  331. }
  332. bool IsMergeOrContinue(opt::IRContext* ir_context, uint32_t block_id) {
  333. bool result = false;
  334. ir_context->get_def_use_mgr()->WhileEachUse(
  335. block_id,
  336. [&result](const opt::Instruction* use_instruction,
  337. uint32_t /*unused*/) -> bool {
  338. switch (use_instruction->opcode()) {
  339. case SpvOpLoopMerge:
  340. case SpvOpSelectionMerge:
  341. result = true;
  342. return false;
  343. default:
  344. return true;
  345. }
  346. });
  347. return result;
  348. }
  349. uint32_t FindFunctionType(opt::IRContext* ir_context,
  350. const std::vector<uint32_t>& type_ids) {
  351. // Look through the existing types for a match.
  352. for (auto& type_or_value : ir_context->types_values()) {
  353. if (type_or_value.opcode() != SpvOpTypeFunction) {
  354. // We are only interested in function types.
  355. continue;
  356. }
  357. if (type_or_value.NumInOperands() != type_ids.size()) {
  358. // Not a match: different numbers of arguments.
  359. continue;
  360. }
  361. // Check whether the return type and argument types match.
  362. bool input_operands_match = true;
  363. for (uint32_t i = 0; i < type_or_value.NumInOperands(); i++) {
  364. if (type_ids[i] != type_or_value.GetSingleWordInOperand(i)) {
  365. input_operands_match = false;
  366. break;
  367. }
  368. }
  369. if (input_operands_match) {
  370. // Everything matches.
  371. return type_or_value.result_id();
  372. }
  373. }
  374. // No match was found.
  375. return 0;
  376. }
  377. opt::Instruction* GetFunctionType(opt::IRContext* context,
  378. const opt::Function* function) {
  379. uint32_t type_id = function->DefInst().GetSingleWordInOperand(1);
  380. return context->get_def_use_mgr()->GetDef(type_id);
  381. }
  382. opt::Function* FindFunction(opt::IRContext* ir_context, uint32_t function_id) {
  383. for (auto& function : *ir_context->module()) {
  384. if (function.result_id() == function_id) {
  385. return &function;
  386. }
  387. }
  388. return nullptr;
  389. }
  390. bool FunctionIsEntryPoint(opt::IRContext* context, uint32_t function_id) {
  391. for (auto& entry_point : context->module()->entry_points()) {
  392. if (entry_point.GetSingleWordInOperand(1) == function_id) {
  393. return true;
  394. }
  395. }
  396. return false;
  397. }
  398. bool IdIsAvailableAtUse(opt::IRContext* context,
  399. opt::Instruction* use_instruction,
  400. uint32_t use_input_operand_index, uint32_t id) {
  401. auto defining_instruction = context->get_def_use_mgr()->GetDef(id);
  402. auto enclosing_function =
  403. context->get_instr_block(use_instruction)->GetParent();
  404. // If the id a function parameter, it needs to be associated with the
  405. // function containing the use.
  406. if (defining_instruction->opcode() == SpvOpFunctionParameter) {
  407. return InstructionIsFunctionParameter(defining_instruction,
  408. enclosing_function);
  409. }
  410. if (!context->get_instr_block(id)) {
  411. // The id must be at global scope.
  412. return true;
  413. }
  414. if (defining_instruction == use_instruction) {
  415. // It is not OK for a definition to use itself.
  416. return false;
  417. }
  418. auto dominator_analysis = context->GetDominatorAnalysis(enclosing_function);
  419. if (use_instruction->opcode() == SpvOpPhi) {
  420. // In the case where the use is an operand to OpPhi, it is actually the
  421. // *parent* block associated with the operand that must be dominated by
  422. // the synonym.
  423. auto parent_block =
  424. use_instruction->GetSingleWordInOperand(use_input_operand_index + 1);
  425. return dominator_analysis->Dominates(
  426. context->get_instr_block(defining_instruction)->id(), parent_block);
  427. }
  428. return dominator_analysis->Dominates(defining_instruction, use_instruction);
  429. }
  430. bool IdIsAvailableBeforeInstruction(opt::IRContext* context,
  431. opt::Instruction* instruction,
  432. uint32_t id) {
  433. auto defining_instruction = context->get_def_use_mgr()->GetDef(id);
  434. auto enclosing_function = context->get_instr_block(instruction)->GetParent();
  435. // If the id a function parameter, it needs to be associated with the
  436. // function containing the instruction.
  437. if (defining_instruction->opcode() == SpvOpFunctionParameter) {
  438. return InstructionIsFunctionParameter(defining_instruction,
  439. enclosing_function);
  440. }
  441. if (!context->get_instr_block(id)) {
  442. // The id is at global scope.
  443. return true;
  444. }
  445. if (defining_instruction == instruction) {
  446. // The instruction is not available right before its own definition.
  447. return false;
  448. }
  449. return context->GetDominatorAnalysis(enclosing_function)
  450. ->Dominates(defining_instruction, instruction);
  451. }
  452. bool InstructionIsFunctionParameter(opt::Instruction* instruction,
  453. opt::Function* function) {
  454. if (instruction->opcode() != SpvOpFunctionParameter) {
  455. return false;
  456. }
  457. bool found_parameter = false;
  458. function->ForEachParam(
  459. [instruction, &found_parameter](opt::Instruction* param) {
  460. if (param == instruction) {
  461. found_parameter = true;
  462. }
  463. });
  464. return found_parameter;
  465. }
  466. uint32_t GetTypeId(opt::IRContext* context, uint32_t result_id) {
  467. return context->get_def_use_mgr()->GetDef(result_id)->type_id();
  468. }
  469. uint32_t GetPointeeTypeIdFromPointerType(opt::Instruction* pointer_type_inst) {
  470. assert(pointer_type_inst && pointer_type_inst->opcode() == SpvOpTypePointer &&
  471. "Precondition: |pointer_type_inst| must be OpTypePointer.");
  472. return pointer_type_inst->GetSingleWordInOperand(1);
  473. }
  474. uint32_t GetPointeeTypeIdFromPointerType(opt::IRContext* context,
  475. uint32_t pointer_type_id) {
  476. return GetPointeeTypeIdFromPointerType(
  477. context->get_def_use_mgr()->GetDef(pointer_type_id));
  478. }
  479. SpvStorageClass GetStorageClassFromPointerType(
  480. opt::Instruction* pointer_type_inst) {
  481. assert(pointer_type_inst && pointer_type_inst->opcode() == SpvOpTypePointer &&
  482. "Precondition: |pointer_type_inst| must be OpTypePointer.");
  483. return static_cast<SpvStorageClass>(
  484. pointer_type_inst->GetSingleWordInOperand(0));
  485. }
  486. SpvStorageClass GetStorageClassFromPointerType(opt::IRContext* context,
  487. uint32_t pointer_type_id) {
  488. return GetStorageClassFromPointerType(
  489. context->get_def_use_mgr()->GetDef(pointer_type_id));
  490. }
  491. uint32_t MaybeGetPointerType(opt::IRContext* context, uint32_t pointee_type_id,
  492. SpvStorageClass storage_class) {
  493. for (auto& inst : context->types_values()) {
  494. switch (inst.opcode()) {
  495. case SpvOpTypePointer:
  496. if (inst.GetSingleWordInOperand(0) == storage_class &&
  497. inst.GetSingleWordInOperand(1) == pointee_type_id) {
  498. return inst.result_id();
  499. }
  500. break;
  501. default:
  502. break;
  503. }
  504. }
  505. return 0;
  506. }
  507. uint32_t InOperandIndexFromOperandIndex(const opt::Instruction& inst,
  508. uint32_t absolute_index) {
  509. // Subtract the number of non-input operands from the index
  510. return absolute_index - inst.NumOperands() + inst.NumInOperands();
  511. }
  512. bool IsNullConstantSupported(const opt::analysis::Type& type) {
  513. return type.AsBool() || type.AsInteger() || type.AsFloat() ||
  514. type.AsMatrix() || type.AsVector() || type.AsArray() ||
  515. type.AsStruct() || type.AsPointer() || type.AsEvent() ||
  516. type.AsDeviceEvent() || type.AsReserveId() || type.AsQueue();
  517. }
  518. bool GlobalVariablesMustBeDeclaredInEntryPointInterfaces(
  519. const opt::IRContext* ir_context) {
  520. // TODO(afd): We capture the universal environments for which this requirement
  521. // holds. The check should be refined on demand for other target
  522. // environments.
  523. switch (ir_context->grammar().target_env()) {
  524. case SPV_ENV_UNIVERSAL_1_0:
  525. case SPV_ENV_UNIVERSAL_1_1:
  526. case SPV_ENV_UNIVERSAL_1_2:
  527. case SPV_ENV_UNIVERSAL_1_3:
  528. return false;
  529. default:
  530. return true;
  531. }
  532. }
  533. void AddVariableIdToEntryPointInterfaces(opt::IRContext* context, uint32_t id) {
  534. if (GlobalVariablesMustBeDeclaredInEntryPointInterfaces(context)) {
  535. // Conservatively add this global to the interface of every entry point in
  536. // the module. This means that the global is available for other
  537. // transformations to use.
  538. //
  539. // A downside of this is that the global will be in the interface even if it
  540. // ends up never being used.
  541. //
  542. // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3111) revisit
  543. // this if a more thorough approach to entry point interfaces is taken.
  544. for (auto& entry_point : context->module()->entry_points()) {
  545. entry_point.AddOperand({SPV_OPERAND_TYPE_ID, {id}});
  546. }
  547. }
  548. }
  549. void AddGlobalVariable(opt::IRContext* context, uint32_t result_id,
  550. uint32_t type_id, SpvStorageClass storage_class,
  551. uint32_t initializer_id) {
  552. // Check various preconditions.
  553. assert(result_id != 0 && "Result id can't be 0");
  554. assert((storage_class == SpvStorageClassPrivate ||
  555. storage_class == SpvStorageClassWorkgroup) &&
  556. "Variable's storage class must be either Private or Workgroup");
  557. auto* type_inst = context->get_def_use_mgr()->GetDef(type_id);
  558. (void)type_inst; // Variable becomes unused in release mode.
  559. assert(type_inst && type_inst->opcode() == SpvOpTypePointer &&
  560. GetStorageClassFromPointerType(type_inst) == storage_class &&
  561. "Variable's type is invalid");
  562. if (storage_class == SpvStorageClassWorkgroup) {
  563. assert(initializer_id == 0);
  564. }
  565. if (initializer_id != 0) {
  566. const auto* constant_inst =
  567. context->get_def_use_mgr()->GetDef(initializer_id);
  568. (void)constant_inst; // Variable becomes unused in release mode.
  569. assert(constant_inst && spvOpcodeIsConstant(constant_inst->opcode()) &&
  570. GetPointeeTypeIdFromPointerType(type_inst) ==
  571. constant_inst->type_id() &&
  572. "Initializer is invalid");
  573. }
  574. opt::Instruction::OperandList operands = {
  575. {SPV_OPERAND_TYPE_STORAGE_CLASS, {static_cast<uint32_t>(storage_class)}}};
  576. if (initializer_id) {
  577. operands.push_back({SPV_OPERAND_TYPE_ID, {initializer_id}});
  578. }
  579. context->module()->AddGlobalValue(MakeUnique<opt::Instruction>(
  580. context, SpvOpVariable, type_id, result_id, std::move(operands)));
  581. AddVariableIdToEntryPointInterfaces(context, result_id);
  582. UpdateModuleIdBound(context, result_id);
  583. }
  584. void AddLocalVariable(opt::IRContext* context, uint32_t result_id,
  585. uint32_t type_id, uint32_t function_id,
  586. uint32_t initializer_id) {
  587. // Check various preconditions.
  588. assert(result_id != 0 && "Result id can't be 0");
  589. auto* type_inst = context->get_def_use_mgr()->GetDef(type_id);
  590. (void)type_inst; // Variable becomes unused in release mode.
  591. assert(type_inst && type_inst->opcode() == SpvOpTypePointer &&
  592. GetStorageClassFromPointerType(type_inst) == SpvStorageClassFunction &&
  593. "Variable's type is invalid");
  594. const auto* constant_inst =
  595. context->get_def_use_mgr()->GetDef(initializer_id);
  596. (void)constant_inst; // Variable becomes unused in release mode.
  597. assert(constant_inst && spvOpcodeIsConstant(constant_inst->opcode()) &&
  598. GetPointeeTypeIdFromPointerType(type_inst) ==
  599. constant_inst->type_id() &&
  600. "Initializer is invalid");
  601. auto* function = FindFunction(context, function_id);
  602. assert(function && "Function id is invalid");
  603. function->begin()->begin()->InsertBefore(MakeUnique<opt::Instruction>(
  604. context, SpvOpVariable, type_id, result_id,
  605. opt::Instruction::OperandList{
  606. {SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}},
  607. {SPV_OPERAND_TYPE_ID, {initializer_id}}}));
  608. UpdateModuleIdBound(context, result_id);
  609. }
  610. bool HasDuplicates(const std::vector<uint32_t>& arr) {
  611. return std::unordered_set<uint32_t>(arr.begin(), arr.end()).size() !=
  612. arr.size();
  613. }
  614. bool IsPermutationOfRange(const std::vector<uint32_t>& arr, uint32_t lo,
  615. uint32_t hi) {
  616. if (arr.empty()) {
  617. return lo > hi;
  618. }
  619. if (HasDuplicates(arr)) {
  620. return false;
  621. }
  622. auto min_max = std::minmax_element(arr.begin(), arr.end());
  623. return arr.size() == hi - lo + 1 && *min_max.first == lo &&
  624. *min_max.second == hi;
  625. }
  626. std::vector<opt::Instruction*> GetParameters(opt::IRContext* ir_context,
  627. uint32_t function_id) {
  628. auto* function = FindFunction(ir_context, function_id);
  629. assert(function && "|function_id| is invalid");
  630. std::vector<opt::Instruction*> result;
  631. function->ForEachParam(
  632. [&result](opt::Instruction* inst) { result.push_back(inst); });
  633. return result;
  634. }
  635. void AddFunctionType(opt::IRContext* ir_context, uint32_t result_id,
  636. const std::vector<uint32_t>& type_ids) {
  637. assert(result_id != 0 && "Result id can't be 0");
  638. assert(!type_ids.empty() &&
  639. "OpTypeFunction always has at least one operand - function's return "
  640. "type");
  641. assert(IsNonFunctionTypeId(ir_context, type_ids[0]) &&
  642. "Return type must not be a function");
  643. for (size_t i = 1; i < type_ids.size(); ++i) {
  644. const auto* param_type = ir_context->get_type_mgr()->GetType(type_ids[i]);
  645. (void)param_type; // Make compiler happy in release mode.
  646. assert(param_type && !param_type->AsVoid() && !param_type->AsFunction() &&
  647. "Function parameter can't have a function or void type");
  648. }
  649. opt::Instruction::OperandList operands;
  650. operands.reserve(type_ids.size());
  651. for (auto id : type_ids) {
  652. operands.push_back({SPV_OPERAND_TYPE_ID, {id}});
  653. }
  654. ir_context->AddType(MakeUnique<opt::Instruction>(
  655. ir_context, SpvOpTypeFunction, 0, result_id, std::move(operands)));
  656. UpdateModuleIdBound(ir_context, result_id);
  657. }
  658. uint32_t FindOrCreateFunctionType(opt::IRContext* ir_context,
  659. uint32_t result_id,
  660. const std::vector<uint32_t>& type_ids) {
  661. if (auto existing_id = FindFunctionType(ir_context, type_ids)) {
  662. return existing_id;
  663. }
  664. AddFunctionType(ir_context, result_id, type_ids);
  665. return result_id;
  666. }
  667. uint32_t MaybeGetIntegerType(opt::IRContext* ir_context, uint32_t width,
  668. bool is_signed) {
  669. opt::analysis::Integer type(width, is_signed);
  670. return ir_context->get_type_mgr()->GetId(&type);
  671. }
  672. uint32_t MaybeGetFloatType(opt::IRContext* ir_context, uint32_t width) {
  673. opt::analysis::Float type(width);
  674. return ir_context->get_type_mgr()->GetId(&type);
  675. }
  676. uint32_t MaybeGetVectorType(opt::IRContext* ir_context,
  677. uint32_t component_type_id,
  678. uint32_t element_count) {
  679. const auto* component_type =
  680. ir_context->get_type_mgr()->GetType(component_type_id);
  681. assert(component_type &&
  682. (component_type->AsInteger() || component_type->AsFloat() ||
  683. component_type->AsBool()) &&
  684. "|component_type_id| is invalid");
  685. assert(element_count >= 2 && element_count <= 4 &&
  686. "Precondition: component count must be in range [2, 4].");
  687. opt::analysis::Vector type(component_type, element_count);
  688. return ir_context->get_type_mgr()->GetId(&type);
  689. }
  690. uint32_t MaybeGetStructType(opt::IRContext* ir_context,
  691. const std::vector<uint32_t>& component_type_ids) {
  692. std::vector<const opt::analysis::Type*> component_types;
  693. component_types.reserve(component_type_ids.size());
  694. for (auto type_id : component_type_ids) {
  695. const auto* component_type = ir_context->get_type_mgr()->GetType(type_id);
  696. assert(component_type && !component_type->AsFunction() &&
  697. "Component type is invalid");
  698. component_types.push_back(component_type);
  699. }
  700. opt::analysis::Struct type(component_types);
  701. return ir_context->get_type_mgr()->GetId(&type);
  702. }
  703. uint32_t MaybeGetZeroConstant(opt::IRContext* ir_context,
  704. uint32_t scalar_or_composite_type_id) {
  705. const auto* type =
  706. ir_context->get_type_mgr()->GetType(scalar_or_composite_type_id);
  707. assert(type && "|scalar_or_composite_type_id| is invalid");
  708. switch (type->kind()) {
  709. case opt::analysis::Type::kBool:
  710. return MaybeGetBoolConstant(ir_context, false);
  711. case opt::analysis::Type::kFloat:
  712. case opt::analysis::Type::kInteger: {
  713. std::vector<uint32_t> words = {0};
  714. if ((type->AsInteger() && type->AsInteger()->width() > 32) ||
  715. (type->AsFloat() && type->AsFloat()->width() > 32)) {
  716. words.push_back(0);
  717. }
  718. return MaybeGetScalarConstant(ir_context, words,
  719. scalar_or_composite_type_id);
  720. }
  721. case opt::analysis::Type::kStruct: {
  722. std::vector<uint32_t> component_ids;
  723. for (const auto* component_type : type->AsStruct()->element_types()) {
  724. auto component_type_id =
  725. ir_context->get_type_mgr()->GetId(component_type);
  726. assert(component_type_id && "Component type is invalid");
  727. auto component_id = MaybeGetZeroConstant(ir_context, component_type_id);
  728. if (component_id == 0) {
  729. return 0;
  730. }
  731. component_ids.push_back(component_id);
  732. }
  733. return MaybeGetCompositeConstant(ir_context, component_ids,
  734. scalar_or_composite_type_id);
  735. }
  736. case opt::analysis::Type::kMatrix:
  737. case opt::analysis::Type::kVector: {
  738. const auto* component_type = type->AsVector()
  739. ? type->AsVector()->element_type()
  740. : type->AsMatrix()->element_type();
  741. auto component_type_id =
  742. ir_context->get_type_mgr()->GetId(component_type);
  743. assert(component_type_id && "Component type is invalid");
  744. if (auto component_id =
  745. MaybeGetZeroConstant(ir_context, component_type_id)) {
  746. auto component_count = type->AsVector()
  747. ? type->AsVector()->element_count()
  748. : type->AsMatrix()->element_count();
  749. return MaybeGetCompositeConstant(
  750. ir_context, std::vector<uint32_t>(component_count, component_id),
  751. scalar_or_composite_type_id);
  752. }
  753. return 0;
  754. }
  755. case opt::analysis::Type::kArray: {
  756. auto component_type_id =
  757. ir_context->get_type_mgr()->GetId(type->AsArray()->element_type());
  758. assert(component_type_id && "Component type is invalid");
  759. if (auto component_id =
  760. MaybeGetZeroConstant(ir_context, component_type_id)) {
  761. auto type_id = ir_context->get_type_mgr()->GetId(type);
  762. assert(type_id && "|type| is invalid");
  763. const auto* type_inst = ir_context->get_def_use_mgr()->GetDef(type_id);
  764. assert(type_inst && "Array's type id is invalid");
  765. return MaybeGetCompositeConstant(
  766. ir_context,
  767. std::vector<uint32_t>(GetArraySize(*type_inst, ir_context),
  768. component_id),
  769. scalar_or_composite_type_id);
  770. }
  771. return 0;
  772. }
  773. default:
  774. assert(false && "Type is not supported");
  775. return 0;
  776. }
  777. }
  778. uint32_t MaybeGetScalarConstant(opt::IRContext* ir_context,
  779. const std::vector<uint32_t>& words,
  780. uint32_t scalar_type_id) {
  781. const auto* type = ir_context->get_type_mgr()->GetType(scalar_type_id);
  782. assert(type && "|scalar_type_id| is invalid");
  783. if (const auto* int_type = type->AsInteger()) {
  784. return MaybeGetIntegerConstant(ir_context, words, int_type->width(),
  785. int_type->IsSigned());
  786. } else if (const auto* float_type = type->AsFloat()) {
  787. return MaybeGetFloatConstant(ir_context, words, float_type->width());
  788. } else {
  789. assert(type->AsBool() && words.size() == 1 &&
  790. "|scalar_type_id| doesn't represent a scalar type");
  791. return MaybeGetBoolConstant(ir_context, words[0]);
  792. }
  793. }
  794. uint32_t MaybeGetCompositeConstant(opt::IRContext* ir_context,
  795. const std::vector<uint32_t>& component_ids,
  796. uint32_t composite_type_id) {
  797. std::vector<const opt::analysis::Constant*> constants;
  798. for (auto id : component_ids) {
  799. const auto* component_constant =
  800. ir_context->get_constant_mgr()->FindDeclaredConstant(id);
  801. assert(component_constant && "|id| is invalid");
  802. constants.push_back(component_constant);
  803. }
  804. const auto* type = ir_context->get_type_mgr()->GetType(composite_type_id);
  805. assert(type && "|composite_type_id| is invalid");
  806. std::unique_ptr<opt::analysis::Constant> composite_constant;
  807. switch (type->kind()) {
  808. case opt::analysis::Type::kStruct:
  809. composite_constant = MakeUnique<opt::analysis::StructConstant>(
  810. type->AsStruct(), std::move(constants));
  811. break;
  812. case opt::analysis::Type::kVector:
  813. composite_constant = MakeUnique<opt::analysis::VectorConstant>(
  814. type->AsVector(), std::move(constants));
  815. break;
  816. case opt::analysis::Type::kMatrix:
  817. composite_constant = MakeUnique<opt::analysis::MatrixConstant>(
  818. type->AsMatrix(), std::move(constants));
  819. break;
  820. case opt::analysis::Type::kArray:
  821. composite_constant = MakeUnique<opt::analysis::ArrayConstant>(
  822. type->AsArray(), std::move(constants));
  823. break;
  824. default:
  825. assert(false &&
  826. "|composite_type_id| is not a result id of a composite type");
  827. return 0;
  828. }
  829. return ir_context->get_constant_mgr()->FindDeclaredConstant(
  830. composite_constant.get(), composite_type_id);
  831. }
  832. uint32_t MaybeGetIntegerConstant(opt::IRContext* ir_context,
  833. const std::vector<uint32_t>& words,
  834. uint32_t width, bool is_signed) {
  835. auto type_id = MaybeGetIntegerType(ir_context, width, is_signed);
  836. if (!type_id) {
  837. return 0;
  838. }
  839. const auto* type = ir_context->get_type_mgr()->GetType(type_id);
  840. assert(type && "|type_id| is invalid");
  841. opt::analysis::IntConstant constant(type->AsInteger(), words);
  842. return ir_context->get_constant_mgr()->FindDeclaredConstant(&constant,
  843. type_id);
  844. }
  845. uint32_t MaybeGetFloatConstant(opt::IRContext* ir_context,
  846. const std::vector<uint32_t>& words,
  847. uint32_t width) {
  848. auto type_id = MaybeGetFloatType(ir_context, width);
  849. if (!type_id) {
  850. return 0;
  851. }
  852. const auto* type = ir_context->get_type_mgr()->GetType(type_id);
  853. assert(type && "|type_id| is invalid");
  854. opt::analysis::FloatConstant constant(type->AsFloat(), words);
  855. return ir_context->get_constant_mgr()->FindDeclaredConstant(&constant,
  856. type_id);
  857. }
  858. uint32_t MaybeGetBoolConstant(opt::IRContext* context, bool value) {
  859. opt::analysis::Bool bool_type;
  860. auto registered_bool_type =
  861. context->get_type_mgr()->GetRegisteredType(&bool_type);
  862. if (!registered_bool_type) {
  863. return 0;
  864. }
  865. opt::analysis::BoolConstant bool_constant(registered_bool_type->AsBool(),
  866. value);
  867. return context->get_constant_mgr()->FindDeclaredConstant(
  868. &bool_constant, context->get_type_mgr()->GetId(&bool_type));
  869. }
  870. void AddIntegerType(opt::IRContext* ir_context, uint32_t result_id,
  871. uint32_t width, bool is_signed) {
  872. ir_context->module()->AddType(MakeUnique<opt::Instruction>(
  873. ir_context, SpvOpTypeInt, 0, result_id,
  874. opt::Instruction::OperandList{
  875. {SPV_OPERAND_TYPE_LITERAL_INTEGER, {width}},
  876. {SPV_OPERAND_TYPE_LITERAL_INTEGER, {is_signed ? 1u : 0u}}}));
  877. UpdateModuleIdBound(ir_context, result_id);
  878. }
  879. void AddFloatType(opt::IRContext* ir_context, uint32_t result_id,
  880. uint32_t width) {
  881. ir_context->module()->AddType(MakeUnique<opt::Instruction>(
  882. ir_context, SpvOpTypeFloat, 0, result_id,
  883. opt::Instruction::OperandList{
  884. {SPV_OPERAND_TYPE_LITERAL_INTEGER, {width}}}));
  885. UpdateModuleIdBound(ir_context, result_id);
  886. }
  887. void AddVectorType(opt::IRContext* ir_context, uint32_t result_id,
  888. uint32_t component_type_id, uint32_t element_count) {
  889. const auto* component_type =
  890. ir_context->get_type_mgr()->GetType(component_type_id);
  891. (void)component_type; // Make compiler happy in release mode.
  892. assert(component_type &&
  893. (component_type->AsInteger() || component_type->AsFloat() ||
  894. component_type->AsBool()) &&
  895. "|component_type_id| is invalid");
  896. assert(element_count >= 2 && element_count <= 4 &&
  897. "Precondition: component count must be in range [2, 4].");
  898. ir_context->module()->AddType(MakeUnique<opt::Instruction>(
  899. ir_context, SpvOpTypeVector, 0, result_id,
  900. opt::Instruction::OperandList{
  901. {SPV_OPERAND_TYPE_ID, {component_type_id}},
  902. {SPV_OPERAND_TYPE_LITERAL_INTEGER, {element_count}}}));
  903. UpdateModuleIdBound(ir_context, result_id);
  904. }
  905. void AddStructType(opt::IRContext* ir_context, uint32_t result_id,
  906. const std::vector<uint32_t>& component_type_ids) {
  907. opt::Instruction::OperandList operands;
  908. operands.reserve(component_type_ids.size());
  909. for (auto type_id : component_type_ids) {
  910. const auto* type = ir_context->get_type_mgr()->GetType(type_id);
  911. (void)type; // Make compiler happy in release mode.
  912. assert(type && !type->AsFunction() && "Component's type id is invalid");
  913. operands.push_back({SPV_OPERAND_TYPE_ID, {type_id}});
  914. }
  915. ir_context->AddType(MakeUnique<opt::Instruction>(
  916. ir_context, SpvOpTypeStruct, 0, result_id, std::move(operands)));
  917. UpdateModuleIdBound(ir_context, result_id);
  918. }
  919. } // namespace fuzzerutil
  920. } // namespace fuzz
  921. } // namespace spvtools