fuzzer_util.cpp 39 KB

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