transformation_split_block.cpp 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  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_split_block.h"
  15. #include <utility>
  16. #include "source/fuzz/fuzzer_util.h"
  17. #include "source/util/make_unique.h"
  18. namespace spvtools {
  19. namespace fuzz {
  20. TransformationSplitBlock::TransformationSplitBlock(
  21. const spvtools::fuzz::protobufs::TransformationSplitBlock& message)
  22. : message_(message) {}
  23. TransformationSplitBlock::TransformationSplitBlock(uint32_t base_instruction_id,
  24. uint32_t offset,
  25. uint32_t fresh_id) {
  26. message_.set_base_instruction_id(base_instruction_id);
  27. message_.set_offset(offset);
  28. message_.set_fresh_id(fresh_id);
  29. }
  30. bool TransformationSplitBlock::IsApplicable(
  31. opt::IRContext* context, const FactManager& /*unused*/) const {
  32. if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
  33. // We require the id for the new block to be unused.
  34. return false;
  35. }
  36. auto base_instruction =
  37. context->get_def_use_mgr()->GetDef(message_.base_instruction_id());
  38. if (!base_instruction) {
  39. // The instruction describing the block we should split does not exist.
  40. return false;
  41. }
  42. auto block_containing_base_instruction =
  43. context->get_instr_block(base_instruction);
  44. if (!block_containing_base_instruction) {
  45. // The instruction describing the block we should split is not contained in
  46. // a block.
  47. return false;
  48. }
  49. if (block_containing_base_instruction->IsLoopHeader()) {
  50. // We cannot split a loop header block: back-edges would become invalid.
  51. return false;
  52. }
  53. auto split_before = fuzzerutil::GetIteratorForBaseInstructionAndOffset(
  54. block_containing_base_instruction, base_instruction, message_.offset());
  55. if (split_before == block_containing_base_instruction->end()) {
  56. // The offset was inappropriate.
  57. return false;
  58. }
  59. if (split_before->PreviousNode() &&
  60. split_before->PreviousNode()->opcode() == SpvOpSelectionMerge) {
  61. // We cannot split directly after a selection merge: this would separate
  62. // the merge from its associated branch or switch operation.
  63. return false;
  64. }
  65. if (split_before->opcode() == SpvOpVariable) {
  66. // We cannot split directly after a variable; variables in a function
  67. // must be contiguous in the entry block.
  68. return false;
  69. }
  70. // We cannot split before an OpPhi unless the OpPhi has exactly one
  71. // associated incoming edge.
  72. return !(split_before->opcode() == SpvOpPhi &&
  73. split_before->NumInOperands() != 2);
  74. }
  75. void TransformationSplitBlock::Apply(opt::IRContext* context,
  76. FactManager* /*unused*/) const {
  77. auto base_instruction =
  78. context->get_def_use_mgr()->GetDef(message_.base_instruction_id());
  79. assert(base_instruction && "Base instruction must exist");
  80. auto block_containing_base_instruction =
  81. context->get_instr_block(base_instruction);
  82. assert(block_containing_base_instruction &&
  83. "Base instruction must be in a block");
  84. auto split_before = fuzzerutil::GetIteratorForBaseInstructionAndOffset(
  85. block_containing_base_instruction, base_instruction, message_.offset());
  86. assert(split_before != block_containing_base_instruction->end() &&
  87. "If the transformation is applicable, we should have an "
  88. "instruction to split on.");
  89. // We need to make sure the module's id bound is large enough to add the
  90. // fresh id.
  91. fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
  92. // Split the block.
  93. auto new_bb = block_containing_base_instruction->SplitBasicBlock(
  94. context, message_.fresh_id(), split_before);
  95. // The split does not automatically add a branch between the two parts of
  96. // the original block, so we add one.
  97. block_containing_base_instruction->AddInstruction(
  98. MakeUnique<opt::Instruction>(
  99. context, SpvOpBranch, 0, 0,
  100. std::initializer_list<opt::Operand>{
  101. opt::Operand(spv_operand_type_t::SPV_OPERAND_TYPE_ID,
  102. {message_.fresh_id()})}));
  103. // If we split before OpPhi instructions, we need to update their
  104. // predecessor operand so that the block they used to be inside is now the
  105. // predecessor.
  106. new_bb->ForEachPhiInst(
  107. [block_containing_base_instruction](opt::Instruction* phi_inst) {
  108. // The following assertion is a sanity check. It is guaranteed to hold
  109. // if IsApplicable holds.
  110. assert(phi_inst->NumInOperands() == 2 &&
  111. "We can only split a block before an OpPhi if block has exactly "
  112. "one predecessor.");
  113. phi_inst->SetInOperand(1, {block_containing_base_instruction->id()});
  114. });
  115. // Invalidate all analyses
  116. context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
  117. }
  118. protobufs::Transformation TransformationSplitBlock::ToMessage() const {
  119. protobufs::Transformation result;
  120. *result.mutable_split_block() = message_;
  121. return result;
  122. }
  123. } // namespace fuzz
  124. } // namespace spvtools