transformation_propagate_instruction_down.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592
  1. // Copyright (c) 2020 Vasyl Teliman
  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/transformation_propagate_instruction_down.h"
  15. #include "source/fuzz/fuzzer_util.h"
  16. #include "source/fuzz/instruction_descriptor.h"
  17. namespace spvtools {
  18. namespace fuzz {
  19. TransformationPropagateInstructionDown::TransformationPropagateInstructionDown(
  20. protobufs::TransformationPropagateInstructionDown message)
  21. : message_(std::move(message)) {}
  22. TransformationPropagateInstructionDown::TransformationPropagateInstructionDown(
  23. uint32_t block_id, uint32_t phi_fresh_id,
  24. const std::map<uint32_t, uint32_t>& successor_id_to_fresh_id) {
  25. message_.set_block_id(block_id);
  26. message_.set_phi_fresh_id(phi_fresh_id);
  27. *message_.mutable_successor_id_to_fresh_id() =
  28. fuzzerutil::MapToRepeatedUInt32Pair(successor_id_to_fresh_id);
  29. }
  30. bool TransformationPropagateInstructionDown::IsApplicable(
  31. opt::IRContext* ir_context,
  32. const TransformationContext& transformation_context) const {
  33. // Check that we can apply this transformation to the |block_id|.
  34. if (!IsApplicableToBlock(ir_context, message_.block_id())) {
  35. return false;
  36. }
  37. const auto successor_id_to_fresh_id =
  38. fuzzerutil::RepeatedUInt32PairToMap(message_.successor_id_to_fresh_id());
  39. for (auto id : GetAcceptableSuccessors(ir_context, message_.block_id())) {
  40. // Each successor must have a fresh id in the |successor_id_to_fresh_id|
  41. // map, unless overflow ids are available.
  42. if (!successor_id_to_fresh_id.count(id) &&
  43. !transformation_context.GetOverflowIdSource()->HasOverflowIds()) {
  44. return false;
  45. }
  46. }
  47. std::vector<uint32_t> maybe_fresh_ids = {message_.phi_fresh_id()};
  48. maybe_fresh_ids.reserve(successor_id_to_fresh_id.size());
  49. for (const auto& entry : successor_id_to_fresh_id) {
  50. maybe_fresh_ids.push_back(entry.second);
  51. }
  52. // All ids must be unique and fresh.
  53. return !fuzzerutil::HasDuplicates(maybe_fresh_ids) &&
  54. std::all_of(maybe_fresh_ids.begin(), maybe_fresh_ids.end(),
  55. [ir_context](uint32_t id) {
  56. return fuzzerutil::IsFreshId(ir_context, id);
  57. });
  58. }
  59. void TransformationPropagateInstructionDown::Apply(
  60. opt::IRContext* ir_context,
  61. TransformationContext* transformation_context) const {
  62. // Get instruction to propagate down. There must be one.
  63. auto* inst_to_propagate =
  64. GetInstructionToPropagate(ir_context, message_.block_id());
  65. assert(inst_to_propagate && "There must be an instruction to propagate");
  66. auto successor_id_to_fresh_id =
  67. fuzzerutil::RepeatedUInt32PairToMap(message_.successor_id_to_fresh_id());
  68. std::vector<uint32_t> created_inst_ids;
  69. auto successor_ids = GetAcceptableSuccessors(ir_context, message_.block_id());
  70. // Clone |inst_to_propagate| into every successor.
  71. for (auto successor_id : successor_ids) {
  72. std::unique_ptr<opt::Instruction> clone(
  73. inst_to_propagate->Clone(ir_context));
  74. uint32_t new_result_id;
  75. if (successor_id_to_fresh_id.count(successor_id)) {
  76. new_result_id = successor_id_to_fresh_id.at(successor_id);
  77. } else {
  78. assert(transformation_context->GetOverflowIdSource()->HasOverflowIds() &&
  79. "Overflow ids must be available");
  80. new_result_id =
  81. transformation_context->GetOverflowIdSource()->GetNextOverflowId();
  82. successor_id_to_fresh_id[successor_id] = new_result_id;
  83. }
  84. clone->SetResultId(new_result_id);
  85. fuzzerutil::UpdateModuleIdBound(ir_context, new_result_id);
  86. auto* insert_before_inst = GetFirstInsertBeforeInstruction(
  87. ir_context, successor_id, clone->opcode());
  88. assert(insert_before_inst && "Can't insert into one of the successors");
  89. insert_before_inst->InsertBefore(std::move(clone));
  90. created_inst_ids.push_back(new_result_id);
  91. }
  92. // Add an OpPhi instruction into the module if possible.
  93. if (auto merge_block_id = GetOpPhiBlockId(
  94. ir_context, message_.block_id(), *inst_to_propagate, successor_ids)) {
  95. opt::Instruction::OperandList in_operands;
  96. std::unordered_set<uint32_t> visited_predecessors;
  97. for (auto predecessor_id : ir_context->cfg()->preds(merge_block_id)) {
  98. if (visited_predecessors.count(predecessor_id)) {
  99. // Merge block might have multiple identical predecessors.
  100. continue;
  101. }
  102. visited_predecessors.insert(predecessor_id);
  103. const auto* dominator_analysis = ir_context->GetDominatorAnalysis(
  104. ir_context->cfg()->block(message_.block_id())->GetParent());
  105. // Find the successor of |source_block| that dominates the predecessor of
  106. // the merge block |predecessor_id|.
  107. auto it = std::find_if(
  108. successor_ids.begin(), successor_ids.end(),
  109. [predecessor_id, dominator_analysis](uint32_t successor_id) {
  110. return dominator_analysis->Dominates(successor_id, predecessor_id);
  111. });
  112. // OpPhi requires a single operand pair for every predecessor of the
  113. // OpPhi's block.
  114. assert(it != successor_ids.end() && "Unable to insert OpPhi");
  115. in_operands.push_back(
  116. {SPV_OPERAND_TYPE_ID, {successor_id_to_fresh_id.at(*it)}});
  117. in_operands.push_back({SPV_OPERAND_TYPE_ID, {predecessor_id}});
  118. }
  119. ir_context->cfg()
  120. ->block(merge_block_id)
  121. ->begin()
  122. ->InsertBefore(MakeUnique<opt::Instruction>(
  123. ir_context, spv::Op::OpPhi, inst_to_propagate->type_id(),
  124. message_.phi_fresh_id(), std::move(in_operands)));
  125. fuzzerutil::UpdateModuleIdBound(ir_context, message_.phi_fresh_id());
  126. created_inst_ids.push_back(message_.phi_fresh_id());
  127. }
  128. // Make sure analyses are updated when we adjust users of |inst_to_propagate|.
  129. ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
  130. // Copy decorations from the original instructions to its propagated copies.
  131. for (auto id : created_inst_ids) {
  132. ir_context->get_decoration_mgr()->CloneDecorations(
  133. inst_to_propagate->result_id(), id);
  134. }
  135. // Remove all decorations from the original instruction.
  136. ir_context->get_decoration_mgr()->RemoveDecorationsFrom(
  137. inst_to_propagate->result_id());
  138. // Update every use of the |inst_to_propagate| with a result id of some of the
  139. // newly created instructions.
  140. ir_context->get_def_use_mgr()->ForEachUse(
  141. inst_to_propagate, [ir_context, &created_inst_ids](
  142. opt::Instruction* user, uint32_t operand_index) {
  143. assert(ir_context->get_instr_block(user) &&
  144. "All decorations should have already been adjusted");
  145. auto in_operand_index =
  146. fuzzerutil::InOperandIndexFromOperandIndex(*user, operand_index);
  147. for (auto id : created_inst_ids) {
  148. if (fuzzerutil::IdIsAvailableAtUse(ir_context, user, in_operand_index,
  149. id)) {
  150. user->SetInOperand(in_operand_index, {id});
  151. return;
  152. }
  153. }
  154. // Every user of |inst_to_propagate| must be updated since we will
  155. // remove that instruction from the module.
  156. assert(false && "Every user of |inst_to_propagate| must be updated");
  157. });
  158. // Add synonyms about newly created instructions.
  159. assert(inst_to_propagate->HasResultId() &&
  160. "Result id is required to add facts");
  161. if (transformation_context->GetFactManager()->IdIsIrrelevant(
  162. inst_to_propagate->result_id())) {
  163. for (auto id : created_inst_ids) {
  164. transformation_context->GetFactManager()->AddFactIdIsIrrelevant(id);
  165. }
  166. } else {
  167. std::vector<uint32_t> non_irrelevant_ids;
  168. for (auto id : created_inst_ids) {
  169. // |id| can be irrelevant implicitly (e.g. if we propagate it into a dead
  170. // block).
  171. if (!transformation_context->GetFactManager()->IdIsIrrelevant(id)) {
  172. non_irrelevant_ids.push_back(id);
  173. }
  174. }
  175. if (transformation_context->GetFactManager()->PointeeValueIsIrrelevant(
  176. inst_to_propagate->result_id())) {
  177. for (auto id : non_irrelevant_ids) {
  178. transformation_context->GetFactManager()
  179. ->AddFactValueOfPointeeIsIrrelevant(id);
  180. }
  181. }
  182. for (auto id : non_irrelevant_ids) {
  183. transformation_context->GetFactManager()->AddFactDataSynonym(
  184. MakeDataDescriptor(id, {}),
  185. MakeDataDescriptor(non_irrelevant_ids[0], {}));
  186. }
  187. }
  188. // Remove the propagated instruction from the module.
  189. ir_context->KillInst(inst_to_propagate);
  190. // We've adjusted all users - make sure these changes are analyzed.
  191. ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
  192. }
  193. protobufs::Transformation TransformationPropagateInstructionDown::ToMessage()
  194. const {
  195. protobufs::Transformation result;
  196. *result.mutable_propagate_instruction_down() = message_;
  197. return result;
  198. }
  199. bool TransformationPropagateInstructionDown::IsOpcodeSupported(spv::Op opcode) {
  200. // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3605):
  201. // We only support "simple" instructions that don't work with memory.
  202. // We should extend this so that we support the ones that modify the memory
  203. // too.
  204. switch (opcode) {
  205. case spv::Op::OpUndef:
  206. case spv::Op::OpAccessChain:
  207. case spv::Op::OpInBoundsAccessChain:
  208. case spv::Op::OpArrayLength:
  209. case spv::Op::OpVectorExtractDynamic:
  210. case spv::Op::OpVectorInsertDynamic:
  211. case spv::Op::OpVectorShuffle:
  212. case spv::Op::OpCompositeConstruct:
  213. case spv::Op::OpCompositeExtract:
  214. case spv::Op::OpCompositeInsert:
  215. case spv::Op::OpCopyObject:
  216. case spv::Op::OpTranspose:
  217. case spv::Op::OpConvertFToU:
  218. case spv::Op::OpConvertFToS:
  219. case spv::Op::OpConvertSToF:
  220. case spv::Op::OpConvertUToF:
  221. case spv::Op::OpUConvert:
  222. case spv::Op::OpSConvert:
  223. case spv::Op::OpFConvert:
  224. case spv::Op::OpQuantizeToF16:
  225. case spv::Op::OpSatConvertSToU:
  226. case spv::Op::OpSatConvertUToS:
  227. case spv::Op::OpBitcast:
  228. case spv::Op::OpSNegate:
  229. case spv::Op::OpFNegate:
  230. case spv::Op::OpIAdd:
  231. case spv::Op::OpFAdd:
  232. case spv::Op::OpISub:
  233. case spv::Op::OpFSub:
  234. case spv::Op::OpIMul:
  235. case spv::Op::OpFMul:
  236. case spv::Op::OpUDiv:
  237. case spv::Op::OpSDiv:
  238. case spv::Op::OpFDiv:
  239. case spv::Op::OpUMod:
  240. case spv::Op::OpSRem:
  241. case spv::Op::OpSMod:
  242. case spv::Op::OpFRem:
  243. case spv::Op::OpFMod:
  244. case spv::Op::OpVectorTimesScalar:
  245. case spv::Op::OpMatrixTimesScalar:
  246. case spv::Op::OpVectorTimesMatrix:
  247. case spv::Op::OpMatrixTimesVector:
  248. case spv::Op::OpMatrixTimesMatrix:
  249. case spv::Op::OpOuterProduct:
  250. case spv::Op::OpDot:
  251. case spv::Op::OpIAddCarry:
  252. case spv::Op::OpISubBorrow:
  253. case spv::Op::OpUMulExtended:
  254. case spv::Op::OpSMulExtended:
  255. case spv::Op::OpAny:
  256. case spv::Op::OpAll:
  257. case spv::Op::OpIsNan:
  258. case spv::Op::OpIsInf:
  259. case spv::Op::OpIsFinite:
  260. case spv::Op::OpIsNormal:
  261. case spv::Op::OpSignBitSet:
  262. case spv::Op::OpLessOrGreater:
  263. case spv::Op::OpOrdered:
  264. case spv::Op::OpUnordered:
  265. case spv::Op::OpLogicalEqual:
  266. case spv::Op::OpLogicalNotEqual:
  267. case spv::Op::OpLogicalOr:
  268. case spv::Op::OpLogicalAnd:
  269. case spv::Op::OpLogicalNot:
  270. case spv::Op::OpSelect:
  271. case spv::Op::OpIEqual:
  272. case spv::Op::OpINotEqual:
  273. case spv::Op::OpUGreaterThan:
  274. case spv::Op::OpSGreaterThan:
  275. case spv::Op::OpUGreaterThanEqual:
  276. case spv::Op::OpSGreaterThanEqual:
  277. case spv::Op::OpULessThan:
  278. case spv::Op::OpSLessThan:
  279. case spv::Op::OpULessThanEqual:
  280. case spv::Op::OpSLessThanEqual:
  281. case spv::Op::OpFOrdEqual:
  282. case spv::Op::OpFUnordEqual:
  283. case spv::Op::OpFOrdNotEqual:
  284. case spv::Op::OpFUnordNotEqual:
  285. case spv::Op::OpFOrdLessThan:
  286. case spv::Op::OpFUnordLessThan:
  287. case spv::Op::OpFOrdGreaterThan:
  288. case spv::Op::OpFUnordGreaterThan:
  289. case spv::Op::OpFOrdLessThanEqual:
  290. case spv::Op::OpFUnordLessThanEqual:
  291. case spv::Op::OpFOrdGreaterThanEqual:
  292. case spv::Op::OpFUnordGreaterThanEqual:
  293. case spv::Op::OpShiftRightLogical:
  294. case spv::Op::OpShiftRightArithmetic:
  295. case spv::Op::OpShiftLeftLogical:
  296. case spv::Op::OpBitwiseOr:
  297. case spv::Op::OpBitwiseXor:
  298. case spv::Op::OpBitwiseAnd:
  299. case spv::Op::OpNot:
  300. case spv::Op::OpBitFieldInsert:
  301. case spv::Op::OpBitFieldSExtract:
  302. case spv::Op::OpBitFieldUExtract:
  303. case spv::Op::OpBitReverse:
  304. case spv::Op::OpBitCount:
  305. case spv::Op::OpCopyLogical:
  306. case spv::Op::OpPtrEqual:
  307. case spv::Op::OpPtrNotEqual:
  308. return true;
  309. default:
  310. return false;
  311. }
  312. }
  313. opt::Instruction*
  314. TransformationPropagateInstructionDown::GetInstructionToPropagate(
  315. opt::IRContext* ir_context, uint32_t block_id) {
  316. auto* block = ir_context->cfg()->block(block_id);
  317. assert(block && "|block_id| is invalid");
  318. for (auto it = block->rbegin(); it != block->rend(); ++it) {
  319. if (!it->result_id() || !it->type_id() ||
  320. !IsOpcodeSupported(it->opcode())) {
  321. continue;
  322. }
  323. auto all_users_from_different_blocks =
  324. ir_context->get_def_use_mgr()->WhileEachUser(
  325. &*it, [ir_context, block](opt::Instruction* user) {
  326. return ir_context->get_instr_block(user) != block;
  327. });
  328. if (!all_users_from_different_blocks) {
  329. // We can't propagate an instruction if it's used in the same block.
  330. continue;
  331. }
  332. return &*it;
  333. }
  334. return nullptr;
  335. }
  336. bool TransformationPropagateInstructionDown::IsApplicableToBlock(
  337. opt::IRContext* ir_context, uint32_t block_id) {
  338. // Check that |block_id| is valid.
  339. const auto* block = fuzzerutil::MaybeFindBlock(ir_context, block_id);
  340. if (!block) {
  341. return false;
  342. }
  343. // |block| must be reachable.
  344. if (!ir_context->IsReachable(*block)) {
  345. return false;
  346. }
  347. // The block must have an instruction to propagate.
  348. const auto* inst_to_propagate =
  349. GetInstructionToPropagate(ir_context, block_id);
  350. if (!inst_to_propagate) {
  351. return false;
  352. }
  353. // Check that |block| has successors.
  354. auto successor_ids = GetAcceptableSuccessors(ir_context, block_id);
  355. if (successor_ids.empty()) {
  356. return false;
  357. }
  358. // Check that |successor_block| doesn't have any OpPhi instructions that
  359. // use |inst|.
  360. for (auto successor_id : successor_ids) {
  361. for (const auto& maybe_phi_inst : *ir_context->cfg()->block(successor_id)) {
  362. if (maybe_phi_inst.opcode() != spv::Op::OpPhi) {
  363. // OpPhis can be intermixed with OpLine and OpNoLine.
  364. continue;
  365. }
  366. for (uint32_t i = 0; i < maybe_phi_inst.NumInOperands(); i += 2) {
  367. if (maybe_phi_inst.GetSingleWordInOperand(i) ==
  368. inst_to_propagate->result_id()) {
  369. return false;
  370. }
  371. }
  372. }
  373. }
  374. // Get the result id of the block we will insert OpPhi instruction into.
  375. // This is either 0 or a result id of some merge block in the function.
  376. auto phi_block_id =
  377. GetOpPhiBlockId(ir_context, block_id, *inst_to_propagate, successor_ids);
  378. const auto* dominator_analysis =
  379. ir_context->GetDominatorAnalysis(block->GetParent());
  380. // Make sure we can adjust all users of the propagated instruction.
  381. return ir_context->get_def_use_mgr()->WhileEachUse(
  382. inst_to_propagate,
  383. [ir_context, &successor_ids, dominator_analysis, phi_block_id](
  384. opt::Instruction* user, uint32_t index) {
  385. const auto* user_block = ir_context->get_instr_block(user);
  386. if (!user_block) {
  387. // |user| might be a global instruction (e.g. OpDecorate).
  388. return true;
  389. }
  390. // Check that at least one of the ids in |successor_ids| or a
  391. // |phi_block_id| dominates |user|'s block (or its predecessor if the
  392. // user is an OpPhi). We can't use fuzzerutil::IdIsAvailableAtUse since
  393. // the id in question hasn't yet been created in the module.
  394. auto block_id_to_dominate = user->opcode() == spv::Op::OpPhi
  395. ? user->GetSingleWordOperand(index + 1)
  396. : user_block->id();
  397. if (phi_block_id != 0 &&
  398. dominator_analysis->Dominates(phi_block_id, block_id_to_dominate)) {
  399. return true;
  400. }
  401. return std::any_of(
  402. successor_ids.begin(), successor_ids.end(),
  403. [dominator_analysis, block_id_to_dominate](uint32_t id) {
  404. return dominator_analysis->Dominates(id, block_id_to_dominate);
  405. });
  406. });
  407. }
  408. opt::Instruction*
  409. TransformationPropagateInstructionDown::GetFirstInsertBeforeInstruction(
  410. opt::IRContext* ir_context, uint32_t block_id, spv::Op opcode) {
  411. auto* block = ir_context->cfg()->block(block_id);
  412. auto it = block->begin();
  413. while (it != block->end() &&
  414. !fuzzerutil::CanInsertOpcodeBeforeInstruction(opcode, it)) {
  415. ++it;
  416. }
  417. return it == block->end() ? nullptr : &*it;
  418. }
  419. std::unordered_set<uint32_t>
  420. TransformationPropagateInstructionDown::GetAcceptableSuccessors(
  421. opt::IRContext* ir_context, uint32_t block_id) {
  422. const auto* block = ir_context->cfg()->block(block_id);
  423. assert(block && "|block_id| is invalid");
  424. const auto* inst = GetInstructionToPropagate(ir_context, block_id);
  425. assert(inst && "The block must have an instruction to propagate");
  426. std::unordered_set<uint32_t> result;
  427. block->ForEachSuccessorLabel([ir_context, &result,
  428. inst](uint32_t successor_id) {
  429. if (result.count(successor_id)) {
  430. return;
  431. }
  432. auto* successor_block = ir_context->cfg()->block(successor_id);
  433. // We can't propagate |inst| into |successor_block| if the latter is not
  434. // dominated by the |inst|'s dependencies.
  435. if (!inst->WhileEachInId([ir_context, successor_block](const uint32_t* id) {
  436. return fuzzerutil::IdIsAvailableBeforeInstruction(
  437. ir_context, &*successor_block->begin(), *id);
  438. })) {
  439. return;
  440. }
  441. // We don't propagate any "special" instructions (e.g. OpSelectionMerge
  442. // etc), thus, insertion point must always exist if the module is valid.
  443. assert(GetFirstInsertBeforeInstruction(ir_context, successor_id,
  444. inst->opcode()) &&
  445. "There must exist an insertion point.");
  446. result.insert(successor_id);
  447. });
  448. return result;
  449. }
  450. uint32_t TransformationPropagateInstructionDown::GetOpPhiBlockId(
  451. opt::IRContext* ir_context, uint32_t block_id,
  452. const opt::Instruction& inst_to_propagate,
  453. const std::unordered_set<uint32_t>& successor_ids) {
  454. const auto* block = ir_context->cfg()->block(block_id);
  455. // |block_id| must belong to some construct.
  456. auto merge_block_id =
  457. block->GetMergeInst()
  458. ? block->GetMergeInst()->GetSingleWordInOperand(0)
  459. : ir_context->GetStructuredCFGAnalysis()->MergeBlock(block_id);
  460. if (!merge_block_id) {
  461. return 0;
  462. }
  463. const auto* dominator_analysis =
  464. ir_context->GetDominatorAnalysis(block->GetParent());
  465. // Check that |merge_block_id| is reachable in the CFG and |block_id|
  466. // dominates |merge_block_id|.
  467. if (!ir_context->IsReachable(*ir_context->cfg()->block(merge_block_id)) ||
  468. !dominator_analysis->Dominates(block_id, merge_block_id)) {
  469. return 0;
  470. }
  471. // We can't insert an OpPhi into |merge_block_id| if it's an acceptable
  472. // successor of |block_id|.
  473. if (successor_ids.count(merge_block_id)) {
  474. return 0;
  475. }
  476. // All predecessors of the merge block must be dominated by at least one
  477. // successor of the |block_id|.
  478. assert(!ir_context->cfg()->preds(merge_block_id).empty() &&
  479. "Merge block must be reachable");
  480. for (auto predecessor_id : ir_context->cfg()->preds(merge_block_id)) {
  481. if (std::none_of(
  482. successor_ids.begin(), successor_ids.end(),
  483. [dominator_analysis, predecessor_id](uint32_t successor_id) {
  484. return dominator_analysis->Dominates(successor_id,
  485. predecessor_id);
  486. })) {
  487. return 0;
  488. }
  489. }
  490. const auto* propagate_type =
  491. ir_context->get_type_mgr()->GetType(inst_to_propagate.type_id());
  492. assert(propagate_type && "|inst_to_propagate| must have a valid type");
  493. // VariablePointers capability implicitly declares
  494. // VariablePointersStorageBuffer. We need those capabilities since otherwise
  495. // OpPhi instructions cannot have operands of pointer types.
  496. if (propagate_type->AsPointer() &&
  497. !ir_context->get_feature_mgr()->HasCapability(
  498. spv::Capability::VariablePointersStorageBuffer)) {
  499. return 0;
  500. }
  501. return merge_block_id;
  502. }
  503. std::unordered_set<uint32_t>
  504. TransformationPropagateInstructionDown::GetFreshIds() const {
  505. std::unordered_set<uint32_t> result = {message_.phi_fresh_id()};
  506. for (const auto& pair : message_.successor_id_to_fresh_id()) {
  507. result.insert(pair.second());
  508. }
  509. return result;
  510. }
  511. } // namespace fuzz
  512. } // namespace spvtools