transformation_duplicate_region_with_selection.cpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721
  1. // Copyright (c) 2020 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/transformation_duplicate_region_with_selection.h"
  15. #include "source/fuzz/fuzzer_util.h"
  16. namespace spvtools {
  17. namespace fuzz {
  18. TransformationDuplicateRegionWithSelection::
  19. TransformationDuplicateRegionWithSelection(
  20. protobufs::TransformationDuplicateRegionWithSelection message)
  21. : message_(std::move(message)) {}
  22. TransformationDuplicateRegionWithSelection::
  23. TransformationDuplicateRegionWithSelection(
  24. uint32_t new_entry_fresh_id, uint32_t condition_id,
  25. uint32_t merge_label_fresh_id, uint32_t entry_block_id,
  26. uint32_t exit_block_id,
  27. const std::map<uint32_t, uint32_t>& original_label_to_duplicate_label,
  28. const std::map<uint32_t, uint32_t>& original_id_to_duplicate_id,
  29. const std::map<uint32_t, uint32_t>& original_id_to_phi_id) {
  30. message_.set_new_entry_fresh_id(new_entry_fresh_id);
  31. message_.set_condition_id(condition_id);
  32. message_.set_merge_label_fresh_id(merge_label_fresh_id);
  33. message_.set_entry_block_id(entry_block_id);
  34. message_.set_exit_block_id(exit_block_id);
  35. *message_.mutable_original_label_to_duplicate_label() =
  36. fuzzerutil::MapToRepeatedUInt32Pair(original_label_to_duplicate_label);
  37. *message_.mutable_original_id_to_duplicate_id() =
  38. fuzzerutil::MapToRepeatedUInt32Pair(original_id_to_duplicate_id);
  39. *message_.mutable_original_id_to_phi_id() =
  40. fuzzerutil::MapToRepeatedUInt32Pair(original_id_to_phi_id);
  41. }
  42. bool TransformationDuplicateRegionWithSelection::IsApplicable(
  43. opt::IRContext* ir_context,
  44. const TransformationContext& transformation_context) const {
  45. // Instruction with the id |condition_id| must exist and must be of a bool
  46. // type.
  47. auto bool_instr =
  48. ir_context->get_def_use_mgr()->GetDef(message_.condition_id());
  49. if (bool_instr == nullptr || !bool_instr->type_id()) {
  50. return false;
  51. }
  52. if (!ir_context->get_type_mgr()->GetType(bool_instr->type_id())->AsBool()) {
  53. return false;
  54. }
  55. // The |new_entry_fresh_id| must be fresh and distinct.
  56. std::set<uint32_t> ids_used_by_this_transformation;
  57. if (!CheckIdIsFreshAndNotUsedByThisTransformation(
  58. message_.new_entry_fresh_id(), ir_context,
  59. &ids_used_by_this_transformation)) {
  60. return false;
  61. }
  62. // The |merge_label_fresh_id| must be fresh and distinct.
  63. if (!CheckIdIsFreshAndNotUsedByThisTransformation(
  64. message_.merge_label_fresh_id(), ir_context,
  65. &ids_used_by_this_transformation)) {
  66. return false;
  67. }
  68. // The entry and exit block ids must refer to blocks.
  69. for (auto block_id : {message_.entry_block_id(), message_.exit_block_id()}) {
  70. auto block_label = ir_context->get_def_use_mgr()->GetDef(block_id);
  71. if (!block_label || block_label->opcode() != spv::Op::OpLabel) {
  72. return false;
  73. }
  74. }
  75. auto entry_block = ir_context->cfg()->block(message_.entry_block_id());
  76. auto exit_block = ir_context->cfg()->block(message_.exit_block_id());
  77. // The |entry_block| and the |exit_block| must be in the same function.
  78. if (entry_block->GetParent() != exit_block->GetParent()) {
  79. return false;
  80. }
  81. // The |entry_block| must dominate the |exit_block|.
  82. auto dominator_analysis =
  83. ir_context->GetDominatorAnalysis(entry_block->GetParent());
  84. if (!dominator_analysis->Dominates(entry_block, exit_block)) {
  85. return false;
  86. }
  87. // The |exit_block| must post-dominate the |entry_block|.
  88. auto postdominator_analysis =
  89. ir_context->GetPostDominatorAnalysis(entry_block->GetParent());
  90. if (!postdominator_analysis->Dominates(exit_block, entry_block)) {
  91. return false;
  92. }
  93. auto enclosing_function = entry_block->GetParent();
  94. // |entry_block| cannot be the first block of the |enclosing_function|.
  95. if (&*enclosing_function->begin() == entry_block) {
  96. return false;
  97. }
  98. // To make the process of resolving OpPhi instructions easier, we require that
  99. // the entry block has only one predecessor.
  100. auto entry_block_preds = ir_context->cfg()->preds(entry_block->id());
  101. std::sort(entry_block_preds.begin(), entry_block_preds.end());
  102. entry_block_preds.erase(
  103. std::unique(entry_block_preds.begin(), entry_block_preds.end()),
  104. entry_block_preds.end());
  105. if (entry_block_preds.size() > 1) {
  106. return false;
  107. }
  108. // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3785):
  109. // The following code has been copied from TransformationOutlineFunction.
  110. // Consider refactoring to avoid duplication.
  111. auto region_set = GetRegionBlocks(ir_context, entry_block, exit_block);
  112. // Check whether |region_set| really is a single-entry single-exit region, and
  113. // also check whether structured control flow constructs and their merge
  114. // and continue constructs are either wholly in or wholly out of the region -
  115. // e.g. avoid the situation where the region contains the head of a loop but
  116. // not the loop's continue construct.
  117. //
  118. // This is achieved by going through every block in the |enclosing_function|
  119. for (auto& block : *enclosing_function) {
  120. if (&block == exit_block) {
  121. // It is not OK for the exit block to head a loop construct or a
  122. // conditional construct.
  123. if (block.GetMergeInst()) {
  124. return false;
  125. }
  126. continue;
  127. }
  128. if (region_set.count(&block) != 0) {
  129. // The block is in the region and is not the region's exit block. Let's
  130. // see whether all of the block's successors are in the region. If they
  131. // are not, the region is not single-entry single-exit.
  132. bool all_successors_in_region = true;
  133. block.WhileEachSuccessorLabel([&all_successors_in_region, ir_context,
  134. &region_set](uint32_t successor) -> bool {
  135. if (region_set.count(ir_context->cfg()->block(successor)) == 0) {
  136. all_successors_in_region = false;
  137. return false;
  138. }
  139. return true;
  140. });
  141. if (!all_successors_in_region) {
  142. return false;
  143. }
  144. }
  145. if (auto merge = block.GetMergeInst()) {
  146. // The block is a loop or selection header. The header and its
  147. // associated merge block must be both in the region or both be
  148. // outside the region.
  149. auto merge_block =
  150. ir_context->cfg()->block(merge->GetSingleWordOperand(0));
  151. if (region_set.count(&block) != region_set.count(merge_block)) {
  152. return false;
  153. }
  154. }
  155. if (auto loop_merge = block.GetLoopMergeInst()) {
  156. // The continue target of a loop must be within the region if and only if
  157. // the header of the loop is.
  158. auto continue_target =
  159. ir_context->cfg()->block(loop_merge->GetSingleWordOperand(1));
  160. // The continue target is a single-entry, single-exit region. Therefore,
  161. // if the continue target is the exit block, the region might not contain
  162. // the loop header. However, we would like to exclude this situation,
  163. // since it would be impossible for the modified exit block to branch to
  164. // the new selection merge block. In this scenario the exit block is
  165. // required to branch to the loop header.
  166. if (region_set.count(&block) != region_set.count(continue_target)) {
  167. return false;
  168. }
  169. }
  170. }
  171. // Get the maps from the protobuf.
  172. std::map<uint32_t, uint32_t> original_label_to_duplicate_label =
  173. fuzzerutil::RepeatedUInt32PairToMap(
  174. message_.original_label_to_duplicate_label());
  175. std::map<uint32_t, uint32_t> original_id_to_duplicate_id =
  176. fuzzerutil::RepeatedUInt32PairToMap(
  177. message_.original_id_to_duplicate_id());
  178. std::map<uint32_t, uint32_t> original_id_to_phi_id =
  179. fuzzerutil::RepeatedUInt32PairToMap(message_.original_id_to_phi_id());
  180. for (auto block : region_set) {
  181. // The label of every block in the region must be present in the map
  182. // |original_label_to_duplicate_label|, unless overflow ids are present.
  183. if (original_label_to_duplicate_label.count(block->id()) == 0) {
  184. if (!transformation_context.GetOverflowIdSource()->HasOverflowIds()) {
  185. return false;
  186. }
  187. } else {
  188. auto duplicate_label = original_label_to_duplicate_label.at(block->id());
  189. // Each id assigned to labels in the region must be distinct and fresh.
  190. if (!duplicate_label ||
  191. !CheckIdIsFreshAndNotUsedByThisTransformation(
  192. duplicate_label, ir_context, &ids_used_by_this_transformation)) {
  193. return false;
  194. }
  195. }
  196. for (auto& instr : *block) {
  197. if (!instr.HasResultId()) {
  198. continue;
  199. }
  200. // Every instruction with a result id in the region must be present in the
  201. // map |original_id_to_duplicate_id|, unless overflow ids are present.
  202. if (original_id_to_duplicate_id.count(instr.result_id()) == 0) {
  203. if (!transformation_context.GetOverflowIdSource()->HasOverflowIds()) {
  204. return false;
  205. }
  206. } else {
  207. auto duplicate_id = original_id_to_duplicate_id.at(instr.result_id());
  208. // Id assigned to this result id in the region must be distinct and
  209. // fresh.
  210. if (!duplicate_id ||
  211. !CheckIdIsFreshAndNotUsedByThisTransformation(
  212. duplicate_id, ir_context, &ids_used_by_this_transformation)) {
  213. return false;
  214. }
  215. }
  216. // If the instruction is available at the end of the region then we would
  217. // like to be able to add an OpPhi instruction at the merge point of the
  218. // duplicated region to capture the values computed by both duplicates of
  219. // the instruction, so that this is also available after the region. We
  220. // do this not just for instructions that are already used after the
  221. // region, but for all instructions so that the phi is available to future
  222. // transformations.
  223. if (AvailableAfterRegion(instr, exit_block, ir_context)) {
  224. if (!ValidOpPhiArgument(instr, ir_context)) {
  225. // The instruction cannot be used as an OpPhi argument. This is a
  226. // blocker if there are uses of the instruction after the region.
  227. // Otherwise we can simply avoid generating an OpPhi for this
  228. // instruction and its duplicate.
  229. if (!ir_context->get_def_use_mgr()->WhileEachUser(
  230. &instr,
  231. [ir_context,
  232. &region_set](opt::Instruction* use_instr) -> bool {
  233. opt::BasicBlock* use_block =
  234. ir_context->get_instr_block(use_instr);
  235. return use_block == nullptr ||
  236. region_set.count(use_block) > 0;
  237. })) {
  238. return false;
  239. }
  240. } else {
  241. // Every instruction with a result id available at the end of the
  242. // region must be present in the map |original_id_to_phi_id|, unless
  243. // overflow ids are present.
  244. if (original_id_to_phi_id.count(instr.result_id()) == 0) {
  245. if (!transformation_context.GetOverflowIdSource()
  246. ->HasOverflowIds()) {
  247. return false;
  248. }
  249. } else {
  250. auto phi_id = original_id_to_phi_id.at(instr.result_id());
  251. // Id assigned to this result id in the region must be distinct and
  252. // fresh.
  253. if (!phi_id ||
  254. !CheckIdIsFreshAndNotUsedByThisTransformation(
  255. phi_id, ir_context, &ids_used_by_this_transformation)) {
  256. return false;
  257. }
  258. }
  259. }
  260. }
  261. }
  262. }
  263. return true;
  264. }
  265. void TransformationDuplicateRegionWithSelection::Apply(
  266. opt::IRContext* ir_context,
  267. TransformationContext* transformation_context) const {
  268. fuzzerutil::UpdateModuleIdBound(ir_context, message_.new_entry_fresh_id());
  269. fuzzerutil::UpdateModuleIdBound(ir_context, message_.merge_label_fresh_id());
  270. // Create the new entry block containing the main conditional instruction. Set
  271. // its parent to the parent of the original entry block, since it is located
  272. // in the same function.
  273. std::unique_ptr<opt::BasicBlock> new_entry_block =
  274. MakeUnique<opt::BasicBlock>(MakeUnique<opt::Instruction>(
  275. ir_context, spv::Op::OpLabel, 0, message_.new_entry_fresh_id(),
  276. opt::Instruction::OperandList()));
  277. auto entry_block = ir_context->cfg()->block(message_.entry_block_id());
  278. auto enclosing_function = entry_block->GetParent();
  279. auto exit_block = ir_context->cfg()->block(message_.exit_block_id());
  280. // Get the blocks contained in the region.
  281. std::set<opt::BasicBlock*> region_blocks =
  282. GetRegionBlocks(ir_context, entry_block, exit_block);
  283. // Construct the merge block.
  284. std::unique_ptr<opt::BasicBlock> merge_block =
  285. MakeUnique<opt::BasicBlock>(MakeUnique<opt::Instruction>(
  286. ir_context, spv::Op::OpLabel, 0, message_.merge_label_fresh_id(),
  287. opt::Instruction::OperandList()));
  288. // Get the maps from the protobuf.
  289. std::map<uint32_t, uint32_t> original_label_to_duplicate_label =
  290. fuzzerutil::RepeatedUInt32PairToMap(
  291. message_.original_label_to_duplicate_label());
  292. std::map<uint32_t, uint32_t> original_id_to_duplicate_id =
  293. fuzzerutil::RepeatedUInt32PairToMap(
  294. message_.original_id_to_duplicate_id());
  295. std::map<uint32_t, uint32_t> original_id_to_phi_id =
  296. fuzzerutil::RepeatedUInt32PairToMap(message_.original_id_to_phi_id());
  297. // Use overflow ids to fill in any required ids that are missing from these
  298. // maps.
  299. for (auto block : region_blocks) {
  300. if (original_label_to_duplicate_label.count(block->id()) == 0) {
  301. original_label_to_duplicate_label.insert(
  302. {block->id(),
  303. transformation_context->GetOverflowIdSource()->GetNextOverflowId()});
  304. }
  305. for (auto& instr : *block) {
  306. if (!instr.HasResultId()) {
  307. continue;
  308. }
  309. if (original_id_to_duplicate_id.count(instr.result_id()) == 0) {
  310. original_id_to_duplicate_id.insert(
  311. {instr.result_id(), transformation_context->GetOverflowIdSource()
  312. ->GetNextOverflowId()});
  313. }
  314. if (AvailableAfterRegion(instr, exit_block, ir_context) &&
  315. ValidOpPhiArgument(instr, ir_context)) {
  316. if (original_id_to_phi_id.count(instr.result_id()) == 0) {
  317. original_id_to_phi_id.insert(
  318. {instr.result_id(), transformation_context->GetOverflowIdSource()
  319. ->GetNextOverflowId()});
  320. }
  321. }
  322. }
  323. }
  324. // Before adding duplicate blocks, we need to update the OpPhi instructions in
  325. // the successors of the |exit_block|. We know that the execution of the
  326. // transformed region will end in |merge_block|. Hence, we need to change all
  327. // occurrences of the label id of the |exit_block| to the label id of the
  328. // |merge_block|.
  329. exit_block->ForEachSuccessorLabel([this, ir_context](uint32_t label_id) {
  330. auto block = ir_context->cfg()->block(label_id);
  331. for (auto& instr : *block) {
  332. if (instr.opcode() == spv::Op::OpPhi) {
  333. instr.ForEachId([this](uint32_t* id) {
  334. if (*id == message_.exit_block_id()) {
  335. *id = message_.merge_label_fresh_id();
  336. }
  337. });
  338. }
  339. }
  340. });
  341. // Get vector of predecessors id of |entry_block|. Remove any duplicate
  342. // values.
  343. auto entry_block_preds = ir_context->cfg()->preds(entry_block->id());
  344. std::sort(entry_block_preds.begin(), entry_block_preds.end());
  345. entry_block_preds.erase(
  346. unique(entry_block_preds.begin(), entry_block_preds.end()),
  347. entry_block_preds.end());
  348. // We know that |entry_block| has only one predecessor, since the region is
  349. // single-entry, single-exit and its constructs and their merge blocks must be
  350. // either wholly within or wholly outside of the region.
  351. assert(entry_block_preds.size() == 1 &&
  352. "The entry of the region to be duplicated can have only one "
  353. "predecessor.");
  354. uint32_t entry_block_pred_id =
  355. ir_context->get_instr_block(entry_block_preds[0])->id();
  356. // Update all the OpPhi instructions in the |entry_block|. Change every
  357. // occurrence of |entry_block_pred_id| to the id of |new_entry|, because we
  358. // will insert |new_entry| before |entry_block|.
  359. for (auto& instr : *entry_block) {
  360. if (instr.opcode() == spv::Op::OpPhi) {
  361. instr.ForEachId([this, entry_block_pred_id](uint32_t* id) {
  362. if (*id == entry_block_pred_id) {
  363. *id = message_.new_entry_fresh_id();
  364. }
  365. });
  366. }
  367. }
  368. // Duplication of blocks will invalidate iterators. Store all the blocks from
  369. // the enclosing function.
  370. std::vector<opt::BasicBlock*> blocks;
  371. for (auto& block : *enclosing_function) {
  372. blocks.push_back(&block);
  373. }
  374. opt::BasicBlock* previous_block = nullptr;
  375. opt::BasicBlock* duplicated_exit_block = nullptr;
  376. // Iterate over all blocks of the function to duplicate blocks of the original
  377. // region and their instructions.
  378. for (auto& block : blocks) {
  379. // The block must be contained in the region.
  380. if (region_blocks.count(block) == 0) {
  381. continue;
  382. }
  383. fuzzerutil::UpdateModuleIdBound(
  384. ir_context, original_label_to_duplicate_label.at(block->id()));
  385. std::unique_ptr<opt::BasicBlock> duplicated_block =
  386. MakeUnique<opt::BasicBlock>(MakeUnique<opt::Instruction>(
  387. ir_context, spv::Op::OpLabel, 0,
  388. original_label_to_duplicate_label.at(block->id()),
  389. opt::Instruction::OperandList()));
  390. for (auto& instr : *block) {
  391. // Case where an instruction is the terminator of the exit block is
  392. // handled separately.
  393. if (block == exit_block && instr.IsBlockTerminator()) {
  394. switch (instr.opcode()) {
  395. case spv::Op::OpBranch:
  396. case spv::Op::OpBranchConditional:
  397. case spv::Op::OpReturn:
  398. case spv::Op::OpReturnValue:
  399. case spv::Op::OpUnreachable:
  400. case spv::Op::OpKill:
  401. continue;
  402. default:
  403. assert(false &&
  404. "Unexpected terminator for |exit_block| of the region.");
  405. }
  406. }
  407. // Duplicate the instruction.
  408. auto cloned_instr = instr.Clone(ir_context);
  409. duplicated_block->AddInstruction(
  410. std::unique_ptr<opt::Instruction>(cloned_instr));
  411. if (instr.HasResultId()) {
  412. fuzzerutil::UpdateModuleIdBound(
  413. ir_context, original_id_to_duplicate_id.at(instr.result_id()));
  414. }
  415. // If an id from the original region was used in this instruction,
  416. // replace it with the value from |original_id_to_duplicate_id|.
  417. // If a label from the original region was used in this instruction,
  418. // replace it with the value from |original_label_to_duplicate_label|.
  419. cloned_instr->ForEachId(
  420. [original_id_to_duplicate_id,
  421. original_label_to_duplicate_label](uint32_t* op) {
  422. if (original_id_to_duplicate_id.count(*op) != 0) {
  423. *op = original_id_to_duplicate_id.at(*op);
  424. } else if (original_label_to_duplicate_label.count(*op) != 0) {
  425. *op = original_label_to_duplicate_label.at(*op);
  426. }
  427. });
  428. }
  429. // If the block is the first duplicated block, insert it after the exit
  430. // block of the original region. Otherwise, insert it after the preceding
  431. // one.
  432. auto duplicated_block_ptr = duplicated_block.get();
  433. if (previous_block) {
  434. enclosing_function->InsertBasicBlockAfter(std::move(duplicated_block),
  435. previous_block);
  436. } else {
  437. enclosing_function->InsertBasicBlockAfter(std::move(duplicated_block),
  438. exit_block);
  439. }
  440. previous_block = duplicated_block_ptr;
  441. if (block == exit_block) {
  442. // After execution of the loop, this variable stores a pointer to the last
  443. // duplicated block.
  444. duplicated_exit_block = duplicated_block_ptr;
  445. }
  446. }
  447. for (auto& block : region_blocks) {
  448. for (auto& instr : *block) {
  449. if (instr.result_id() == 0) {
  450. continue;
  451. }
  452. if (AvailableAfterRegion(instr, exit_block, ir_context) &&
  453. ValidOpPhiArgument(instr, ir_context)) {
  454. // Add an OpPhi instruction for every result id that is available at
  455. // the end of the region, as long as the result id is valid for use
  456. // with OpPhi.
  457. merge_block->AddInstruction(MakeUnique<opt::Instruction>(
  458. ir_context, spv::Op::OpPhi, instr.type_id(),
  459. original_id_to_phi_id.at(instr.result_id()),
  460. opt::Instruction::OperandList({
  461. {SPV_OPERAND_TYPE_ID, {instr.result_id()}},
  462. {SPV_OPERAND_TYPE_ID, {exit_block->id()}},
  463. {SPV_OPERAND_TYPE_ID,
  464. {original_id_to_duplicate_id.at(instr.result_id())}},
  465. {SPV_OPERAND_TYPE_ID, {duplicated_exit_block->id()}},
  466. })));
  467. fuzzerutil::UpdateModuleIdBound(
  468. ir_context, original_id_to_phi_id.at(instr.result_id()));
  469. // If the instruction has been remapped by an OpPhi, look
  470. // for all its uses outside of the region and outside of the
  471. // merge block (to not overwrite just added instructions in
  472. // the merge block) and replace the original instruction id
  473. // with the id of the corresponding OpPhi instruction.
  474. ir_context->get_def_use_mgr()->ForEachUse(
  475. &instr,
  476. [ir_context, &instr, region_blocks, original_id_to_phi_id,
  477. &merge_block](opt::Instruction* user, uint32_t operand_index) {
  478. auto user_block = ir_context->get_instr_block(user);
  479. if ((region_blocks.find(user_block) != region_blocks.end()) ||
  480. user_block == merge_block.get()) {
  481. return;
  482. }
  483. user->SetOperand(operand_index,
  484. {original_id_to_phi_id.at(instr.result_id())});
  485. });
  486. }
  487. }
  488. }
  489. // Construct a conditional instruction in the |new_entry_block|.
  490. // If the condition is true, the execution proceeds in the
  491. // |entry_block| of the original region. If the condition is
  492. // false, the execution proceeds in the first block of the
  493. // duplicated region.
  494. new_entry_block->AddInstruction(MakeUnique<opt::Instruction>(
  495. ir_context, spv::Op::OpSelectionMerge, 0, 0,
  496. opt::Instruction::OperandList(
  497. {{SPV_OPERAND_TYPE_ID, {message_.merge_label_fresh_id()}},
  498. {SPV_OPERAND_TYPE_SELECTION_CONTROL,
  499. {uint32_t(spv::SelectionControlMask::MaskNone)}}})));
  500. new_entry_block->AddInstruction(MakeUnique<opt::Instruction>(
  501. ir_context, spv::Op::OpBranchConditional, 0, 0,
  502. opt::Instruction::OperandList(
  503. {{SPV_OPERAND_TYPE_ID, {message_.condition_id()}},
  504. {SPV_OPERAND_TYPE_ID, {message_.entry_block_id()}},
  505. {SPV_OPERAND_TYPE_ID,
  506. {original_label_to_duplicate_label.at(
  507. message_.entry_block_id())}}})));
  508. // Move the terminator of |exit_block| to the end of
  509. // |merge_block|.
  510. auto exit_block_terminator = exit_block->terminator();
  511. auto cloned_instr = exit_block_terminator->Clone(ir_context);
  512. merge_block->AddInstruction(std::unique_ptr<opt::Instruction>(cloned_instr));
  513. ir_context->KillInst(exit_block_terminator);
  514. // Add OpBranch instruction to the merge block at the end of
  515. // |exit_block| and at the end of |duplicated_exit_block|, so that
  516. // the execution proceeds in the |merge_block|.
  517. opt::Instruction merge_branch_instr = opt::Instruction(
  518. ir_context, spv::Op::OpBranch, 0, 0,
  519. opt::Instruction::OperandList(
  520. {{SPV_OPERAND_TYPE_ID, {message_.merge_label_fresh_id()}}}));
  521. exit_block->AddInstruction(MakeUnique<opt::Instruction>(merge_branch_instr));
  522. duplicated_exit_block->AddInstruction(
  523. std::unique_ptr<opt::Instruction>(merge_branch_instr.Clone(ir_context)));
  524. // Execution needs to start in the |new_entry_block|. Change all
  525. // the uses of |entry_block_label_instr| outside of the original
  526. // region to |message_.new_entry_fresh_id|.
  527. auto entry_block_label_instr =
  528. ir_context->get_def_use_mgr()->GetDef(message_.entry_block_id());
  529. ir_context->get_def_use_mgr()->ForEachUse(
  530. entry_block_label_instr,
  531. [this, ir_context, region_blocks](opt::Instruction* user,
  532. uint32_t operand_index) {
  533. auto user_block = ir_context->get_instr_block(user);
  534. if ((region_blocks.count(user_block) != 0)) {
  535. return;
  536. }
  537. switch (user->opcode()) {
  538. case spv::Op::OpSwitch:
  539. case spv::Op::OpBranch:
  540. case spv::Op::OpBranchConditional:
  541. case spv::Op::OpLoopMerge:
  542. case spv::Op::OpSelectionMerge: {
  543. user->SetOperand(operand_index, {message_.new_entry_fresh_id()});
  544. } break;
  545. case spv::Op::OpName:
  546. break;
  547. default:
  548. assert(false &&
  549. "The label id cannot be used by instructions "
  550. "other than "
  551. "OpSwitch, OpBranch, OpBranchConditional, "
  552. "OpLoopMerge, "
  553. "OpSelectionMerge");
  554. }
  555. });
  556. opt::Instruction* merge_block_terminator = merge_block->terminator();
  557. switch (merge_block_terminator->opcode()) {
  558. case spv::Op::OpReturnValue:
  559. case spv::Op::OpBranchConditional: {
  560. uint32_t operand = merge_block_terminator->GetSingleWordInOperand(0);
  561. if (original_id_to_phi_id.count(operand)) {
  562. merge_block_terminator->SetInOperand(
  563. 0, {original_id_to_phi_id.at(operand)});
  564. }
  565. break;
  566. }
  567. default:
  568. break;
  569. }
  570. // Insert the merge block after the |duplicated_exit_block| (the
  571. // last duplicated block).
  572. enclosing_function->InsertBasicBlockAfter(std::move(merge_block),
  573. duplicated_exit_block);
  574. // Insert the |new_entry_block| before the entry block of the
  575. // original region.
  576. enclosing_function->InsertBasicBlockBefore(std::move(new_entry_block),
  577. entry_block);
  578. // Since we have changed the module, most of the analysis are now
  579. // invalid. We can invalidate analyses now after all of the blocks
  580. // have been registered.
  581. ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
  582. }
  583. // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3785):
  584. // The following method has been copied from
  585. // TransformationOutlineFunction. Consider refactoring to avoid
  586. // duplication.
  587. std::set<opt::BasicBlock*>
  588. TransformationDuplicateRegionWithSelection::GetRegionBlocks(
  589. opt::IRContext* ir_context, opt::BasicBlock* entry_block,
  590. opt::BasicBlock* exit_block) {
  591. auto enclosing_function = entry_block->GetParent();
  592. auto dominator_analysis =
  593. ir_context->GetDominatorAnalysis(enclosing_function);
  594. auto postdominator_analysis =
  595. ir_context->GetPostDominatorAnalysis(enclosing_function);
  596. // A block belongs to a region between the entry block and the exit
  597. // block if and only if it is dominated by the entry block and
  598. // post-dominated by the exit block.
  599. std::set<opt::BasicBlock*> result;
  600. for (auto& block : *enclosing_function) {
  601. if (dominator_analysis->Dominates(entry_block, &block) &&
  602. postdominator_analysis->Dominates(exit_block, &block)) {
  603. result.insert(&block);
  604. }
  605. }
  606. return result;
  607. }
  608. protobufs::Transformation
  609. TransformationDuplicateRegionWithSelection::ToMessage() const {
  610. protobufs::Transformation result;
  611. *result.mutable_duplicate_region_with_selection() = message_;
  612. return result;
  613. }
  614. std::unordered_set<uint32_t>
  615. TransformationDuplicateRegionWithSelection::GetFreshIds() const {
  616. std::unordered_set<uint32_t> result = {message_.new_entry_fresh_id(),
  617. message_.merge_label_fresh_id()};
  618. for (auto& pair : message_.original_label_to_duplicate_label()) {
  619. result.insert(pair.second());
  620. }
  621. for (auto& pair : message_.original_id_to_duplicate_id()) {
  622. result.insert(pair.second());
  623. }
  624. for (auto& pair : message_.original_id_to_phi_id()) {
  625. result.insert(pair.second());
  626. }
  627. return result;
  628. }
  629. bool TransformationDuplicateRegionWithSelection::AvailableAfterRegion(
  630. const opt::Instruction& instr, opt::BasicBlock* exit_block,
  631. opt::IRContext* ir_context) {
  632. opt::Instruction* final_instruction_in_region = &*exit_block->tail();
  633. return &instr == final_instruction_in_region ||
  634. fuzzerutil::IdIsAvailableBeforeInstruction(
  635. ir_context, final_instruction_in_region, instr.result_id());
  636. }
  637. bool TransformationDuplicateRegionWithSelection::ValidOpPhiArgument(
  638. const opt::Instruction& instr, opt::IRContext* ir_context) {
  639. opt::Instruction* instr_type =
  640. ir_context->get_def_use_mgr()->GetDef(instr.type_id());
  641. // It is invalid to apply OpPhi to void-typed values.
  642. if (instr_type->opcode() == spv::Op::OpTypeVoid) {
  643. return false;
  644. }
  645. // Using pointers with OpPhi requires capability VariablePointers.
  646. if (instr_type->opcode() == spv::Op::OpTypePointer &&
  647. !ir_context->get_feature_mgr()->HasCapability(
  648. spv::Capability::VariablePointers)) {
  649. return false;
  650. }
  651. // OpTypeSampledImage cannot be the result type of an OpPhi instruction.
  652. if (instr_type->opcode() == spv::Op::OpTypeSampledImage) {
  653. return false;
  654. }
  655. return true;
  656. }
  657. } // namespace fuzz
  658. } // namespace spvtools