fuzzer_util.cpp 44 KB

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