• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
15 #include "source/fuzz/transformation_replace_opselect_with_conditional_branch.h"
16 
17 #include "source/fuzz/fuzzer_util.h"
18 
19 namespace spvtools {
20 namespace fuzz {
21 TransformationReplaceOpSelectWithConditionalBranch::
TransformationReplaceOpSelectWithConditionalBranch(protobufs::TransformationReplaceOpSelectWithConditionalBranch message)22     TransformationReplaceOpSelectWithConditionalBranch(
23         protobufs::TransformationReplaceOpSelectWithConditionalBranch message)
24     : message_(std::move(message)) {}
25 
26 TransformationReplaceOpSelectWithConditionalBranch::
TransformationReplaceOpSelectWithConditionalBranch(uint32_t select_id,uint32_t true_block_id,uint32_t false_block_id)27     TransformationReplaceOpSelectWithConditionalBranch(
28         uint32_t select_id, uint32_t true_block_id, uint32_t false_block_id) {
29   message_.set_select_id(select_id);
30   message_.set_true_block_id(true_block_id);
31   message_.set_false_block_id(false_block_id);
32 }
33 
IsApplicable(opt::IRContext * ir_context,const TransformationContext &) const34 bool TransformationReplaceOpSelectWithConditionalBranch::IsApplicable(
35     opt::IRContext* ir_context,
36     const TransformationContext& /* unused */) const {
37   assert((message_.true_block_id() || message_.false_block_id()) &&
38          "At least one of the ids must be non-zero.");
39 
40   // Check that the non-zero ids are fresh.
41   std::set<uint32_t> used_ids;
42   for (uint32_t id : {message_.true_block_id(), message_.false_block_id()}) {
43     if (id && !CheckIdIsFreshAndNotUsedByThisTransformation(id, ir_context,
44                                                             &used_ids)) {
45       return false;
46     }
47   }
48 
49   auto instruction =
50       ir_context->get_def_use_mgr()->GetDef(message_.select_id());
51 
52   // The instruction must exist and it must be an OpSelect instruction.
53   if (!instruction || instruction->opcode() != SpvOpSelect) {
54     return false;
55   }
56 
57   // Check that the condition is a scalar boolean.
58   auto condition = ir_context->get_def_use_mgr()->GetDef(
59       instruction->GetSingleWordInOperand(0));
60   assert(condition && "The condition should always exist in a valid module.");
61 
62   auto condition_type =
63       ir_context->get_type_mgr()->GetType(condition->type_id());
64   if (!condition_type->AsBool()) {
65     return false;
66   }
67 
68   auto block = ir_context->get_instr_block(instruction);
69   assert(block && "The block containing the instruction must be found");
70 
71   // The instruction must be the first in its block.
72   if (instruction->unique_id() != block->begin()->unique_id()) {
73     return false;
74   }
75 
76   // The block must not be a merge block.
77   if (ir_context->GetStructuredCFGAnalysis()->IsMergeBlock(block->id())) {
78     return false;
79   }
80 
81   // The block must have exactly one predecessor.
82   auto predecessors = ir_context->cfg()->preds(block->id());
83   if (predecessors.size() != 1) {
84     return false;
85   }
86 
87   uint32_t pred_id = predecessors[0];
88   auto predecessor = ir_context->get_instr_block(pred_id);
89 
90   // The predecessor must not be the header of a construct and it must end with
91   // OpBranch.
92   if (predecessor->GetMergeInst() != nullptr ||
93       predecessor->terminator()->opcode() != SpvOpBranch) {
94     return false;
95   }
96 
97   return true;
98 }
99 
Apply(opt::IRContext * ir_context,TransformationContext *) const100 void TransformationReplaceOpSelectWithConditionalBranch::Apply(
101     opt::IRContext* ir_context, TransformationContext* /* unused */) const {
102   auto instruction =
103       ir_context->get_def_use_mgr()->GetDef(message_.select_id());
104 
105   auto block = ir_context->get_instr_block(instruction);
106 
107   auto predecessor =
108       ir_context->get_instr_block(ir_context->cfg()->preds(block->id())[0]);
109 
110   // Create a new block for each non-zero id in {|message_.true_branch_id|,
111   // |message_.false_branch_id|}. Make each newly-created block branch
112   // unconditionally to the instruction block.
113   for (uint32_t id : {message_.true_block_id(), message_.false_block_id()}) {
114     if (id) {
115       fuzzerutil::UpdateModuleIdBound(ir_context, id);
116 
117       // Create the new block.
118       auto new_block = MakeUnique<opt::BasicBlock>(MakeUnique<opt::Instruction>(
119           ir_context, SpvOpLabel, 0, id, opt::Instruction::OperandList{}));
120 
121       // Add an unconditional branch from the new block to the instruction
122       // block.
123       new_block->AddInstruction(MakeUnique<opt::Instruction>(
124           ir_context, SpvOpBranch, 0, 0,
125           opt::Instruction::OperandList{{SPV_OPERAND_TYPE_ID, {block->id()}}}));
126 
127       // Insert the new block right after the predecessor of the instruction
128       // block.
129       block->GetParent()->InsertBasicBlockBefore(std::move(new_block), block);
130     }
131   }
132 
133   // Delete the OpBranch instruction from the predecessor.
134   ir_context->KillInst(predecessor->terminator());
135 
136   // Add an OpSelectionMerge instruction to the predecessor block, where the
137   // merge block is the instruction block.
138   predecessor->AddInstruction(MakeUnique<opt::Instruction>(
139       ir_context, SpvOpSelectionMerge, 0, 0,
140       opt::Instruction::OperandList{{SPV_OPERAND_TYPE_ID, {block->id()}},
141                                     {SPV_OPERAND_TYPE_SELECTION_CONTROL,
142                                      {SpvSelectionControlMaskNone}}}));
143 
144   // |if_block| will be the true block, if it has been created, the instruction
145   // block otherwise.
146   uint32_t if_block =
147       message_.true_block_id() ? message_.true_block_id() : block->id();
148 
149   // |else_block| will be the false block, if it has been created, the
150   // instruction block otherwise.
151   uint32_t else_block =
152       message_.false_block_id() ? message_.false_block_id() : block->id();
153 
154   assert(if_block != else_block &&
155          "|if_block| and |else_block| should always be different, if the "
156          "transformation is applicable.");
157 
158   // Add a conditional branching instruction to the predecessor, branching to
159   // |if_block| if the condition is true and to |if_false| otherwise.
160   predecessor->AddInstruction(MakeUnique<opt::Instruction>(
161       ir_context, SpvOpBranchConditional, 0, 0,
162       opt::Instruction::OperandList{
163           {SPV_OPERAND_TYPE_ID, {instruction->GetSingleWordInOperand(0)}},
164           {SPV_OPERAND_TYPE_ID, {if_block}},
165           {SPV_OPERAND_TYPE_ID, {else_block}}}));
166 
167   // |if_pred| will be the true block, if it has been created, the existing
168   // predecessor otherwise.
169   uint32_t if_pred =
170       message_.true_block_id() ? message_.true_block_id() : predecessor->id();
171 
172   // |else_pred| will be the false block, if it has been created, the existing
173   // predecessor otherwise.
174   uint32_t else_pred =
175       message_.false_block_id() ? message_.false_block_id() : predecessor->id();
176 
177   // Replace the OpSelect instruction in the merge block with an OpPhi.
178   // This:          OpSelect %type %cond %if %else
179   // will become:   OpPhi %type %if %if_pred %else %else_pred
180   instruction->SetOpcode(SpvOpPhi);
181   std::vector<opt::Operand> operands;
182 
183   operands.emplace_back(instruction->GetInOperand(1));
184   operands.emplace_back(opt::Operand{SPV_OPERAND_TYPE_ID, {if_pred}});
185 
186   operands.emplace_back(instruction->GetInOperand(2));
187   operands.emplace_back(opt::Operand{SPV_OPERAND_TYPE_ID, {else_pred}});
188 
189   instruction->SetInOperands(std::move(operands));
190 
191   // Invalidate all analyses, since the structure of the module was changed.
192   ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
193 }
194 
195 protobufs::Transformation
ToMessage() const196 TransformationReplaceOpSelectWithConditionalBranch::ToMessage() const {
197   protobufs::Transformation result;
198   *result.mutable_replace_opselect_with_conditional_branch() = message_;
199   return result;
200 }
201 
202 std::unordered_set<uint32_t>
GetFreshIds() const203 TransformationReplaceOpSelectWithConditionalBranch::GetFreshIds() const {
204   return {message_.true_block_id(), message_.false_block_id()};
205 }
206 
207 }  // namespace fuzz
208 }  // namespace spvtools
209