transformation_add_dead_block.cpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  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/transformation_add_dead_block.h"
  15. #include "source/fuzz/fuzzer_util.h"
  16. namespace spvtools {
  17. namespace fuzz {
  18. TransformationAddDeadBlock::TransformationAddDeadBlock(
  19. protobufs::TransformationAddDeadBlock message)
  20. : message_(std::move(message)) {}
  21. TransformationAddDeadBlock::TransformationAddDeadBlock(uint32_t fresh_id,
  22. uint32_t existing_block,
  23. bool condition_value) {
  24. message_.set_fresh_id(fresh_id);
  25. message_.set_existing_block(existing_block);
  26. message_.set_condition_value(condition_value);
  27. }
  28. bool TransformationAddDeadBlock::IsApplicable(
  29. opt::IRContext* ir_context,
  30. const TransformationContext& transformation_context) const {
  31. // The new block's id must be fresh.
  32. if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
  33. return false;
  34. }
  35. // First, we check that a constant with the same value as
  36. // |message_.condition_value| is present.
  37. if (!fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context,
  38. message_.condition_value(), false)) {
  39. // The required constant is not present, so the transformation cannot be
  40. // applied.
  41. return false;
  42. }
  43. // The existing block must indeed exist.
  44. auto existing_block =
  45. fuzzerutil::MaybeFindBlock(ir_context, message_.existing_block());
  46. if (!existing_block) {
  47. return false;
  48. }
  49. // It must not head a loop.
  50. if (existing_block->IsLoopHeader()) {
  51. return false;
  52. }
  53. // It must end with OpBranch.
  54. if (existing_block->terminator()->opcode() != spv::Op::OpBranch) {
  55. return false;
  56. }
  57. // Its successor must not be a merge block nor continue target.
  58. auto successor_block_id =
  59. existing_block->terminator()->GetSingleWordInOperand(0);
  60. if (fuzzerutil::IsMergeOrContinue(ir_context, successor_block_id)) {
  61. return false;
  62. }
  63. // The successor must not be a loop header (i.e., |message_.existing_block|
  64. // must not be a back-edge block.
  65. if (ir_context->cfg()->block(successor_block_id)->IsLoopHeader()) {
  66. return false;
  67. }
  68. // |existing_block| must be reachable.
  69. if (!ir_context->IsReachable(*existing_block)) {
  70. return false;
  71. }
  72. assert(existing_block->id() != successor_block_id &&
  73. "|existing_block| must be different from |successor_block_id|");
  74. // Even though we know |successor_block_id| is not a merge block, it might
  75. // still have multiple predecessors because divergent control flow is allowed
  76. // to converge early (before the merge block). In this case, when we create
  77. // the selection construct, its header |existing_block| will not dominate the
  78. // merge block |successor_block_id|, which is invalid. Thus, |existing_block|
  79. // must dominate |successor_block_id|.
  80. opt::DominatorAnalysis* dominator_analysis =
  81. ir_context->GetDominatorAnalysis(existing_block->GetParent());
  82. if (!dominator_analysis->Dominates(existing_block->id(),
  83. successor_block_id)) {
  84. return false;
  85. }
  86. return true;
  87. }
  88. void TransformationAddDeadBlock::Apply(
  89. opt::IRContext* ir_context,
  90. TransformationContext* transformation_context) const {
  91. // Update the module id bound so that it is at least the id of the new block.
  92. fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
  93. // Get the existing block and its successor.
  94. auto existing_block = ir_context->cfg()->block(message_.existing_block());
  95. auto successor_block_id =
  96. existing_block->terminator()->GetSingleWordInOperand(0);
  97. // Get the id of the boolean value that will be used as the branch condition.
  98. auto bool_id = fuzzerutil::MaybeGetBoolConstant(
  99. ir_context, *transformation_context, message_.condition_value(), false);
  100. // Make a new block that unconditionally branches to the original successor
  101. // block.
  102. auto enclosing_function = existing_block->GetParent();
  103. std::unique_ptr<opt::BasicBlock> new_block =
  104. MakeUnique<opt::BasicBlock>(MakeUnique<opt::Instruction>(
  105. ir_context, spv::Op::OpLabel, 0, message_.fresh_id(),
  106. opt::Instruction::OperandList()));
  107. new_block->AddInstruction(MakeUnique<opt::Instruction>(
  108. ir_context, spv::Op::OpBranch, 0, 0,
  109. opt::Instruction::OperandList(
  110. {{SPV_OPERAND_TYPE_ID, {successor_block_id}}})));
  111. // Turn the original block into a selection merge, with its original successor
  112. // as the merge block.
  113. existing_block->terminator()->InsertBefore(MakeUnique<opt::Instruction>(
  114. ir_context, spv::Op::OpSelectionMerge, 0, 0,
  115. opt::Instruction::OperandList(
  116. {{SPV_OPERAND_TYPE_ID, {successor_block_id}},
  117. {SPV_OPERAND_TYPE_SELECTION_CONTROL,
  118. {uint32_t(spv::SelectionControlMask::MaskNone)}}})));
  119. // Change the original block's terminator to be a conditional branch on the
  120. // given boolean, with the original successor and the new successor as branch
  121. // targets, and such that at runtime control will always transfer to the
  122. // original successor.
  123. existing_block->terminator()->SetOpcode(spv::Op::OpBranchConditional);
  124. existing_block->terminator()->SetInOperands(
  125. {{SPV_OPERAND_TYPE_ID, {bool_id}},
  126. {SPV_OPERAND_TYPE_ID,
  127. {message_.condition_value() ? successor_block_id
  128. : message_.fresh_id()}},
  129. {SPV_OPERAND_TYPE_ID,
  130. {message_.condition_value() ? message_.fresh_id()
  131. : successor_block_id}}});
  132. // Add the new block to the enclosing function.
  133. enclosing_function->InsertBasicBlockAfter(std::move(new_block),
  134. existing_block);
  135. // Fix up OpPhi instructions in the successor block, so that the values they
  136. // yield when control has transferred from the new block are the same as if
  137. // control had transferred from |message_.existing_block|. This is guaranteed
  138. // to be valid since |message_.existing_block| dominates the new block by
  139. // construction. Other transformations can change these phi operands to more
  140. // interesting values.
  141. ir_context->cfg()
  142. ->block(successor_block_id)
  143. ->ForEachPhiInst([this](opt::Instruction* phi_inst) {
  144. // Copy the operand that provides the phi value for the first of any
  145. // existing predecessors.
  146. opt::Operand copy_of_existing_operand = phi_inst->GetInOperand(0);
  147. // Use this as the value associated with the new predecessor.
  148. phi_inst->AddOperand(std::move(copy_of_existing_operand));
  149. phi_inst->AddOperand({SPV_OPERAND_TYPE_ID, {message_.fresh_id()}});
  150. });
  151. // Do not rely on any existing analysis results since the control flow graph
  152. // of the module has changed.
  153. ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
  154. // Record the fact that the new block is dead.
  155. transformation_context->GetFactManager()->AddFactBlockIsDead(
  156. message_.fresh_id());
  157. }
  158. protobufs::Transformation TransformationAddDeadBlock::ToMessage() const {
  159. protobufs::Transformation result;
  160. *result.mutable_add_dead_block() = message_;
  161. return result;
  162. }
  163. std::unordered_set<uint32_t> TransformationAddDeadBlock::GetFreshIds() const {
  164. return {message_.fresh_id()};
  165. }
  166. } // namespace fuzz
  167. } // namespace spvtools