• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
15 #include "source/fuzz/fuzzer_pass_outline_functions.h"
16 
17 #include <vector>
18 
19 #include "source/fuzz/fuzzer_util.h"
20 #include "source/fuzz/instruction_descriptor.h"
21 #include "source/fuzz/transformation_outline_function.h"
22 #include "source/fuzz/transformation_split_block.h"
23 
24 namespace spvtools {
25 namespace fuzz {
26 
FuzzerPassOutlineFunctions(opt::IRContext * ir_context,TransformationContext * transformation_context,FuzzerContext * fuzzer_context,protobufs::TransformationSequence * transformations)27 FuzzerPassOutlineFunctions::FuzzerPassOutlineFunctions(
28     opt::IRContext* ir_context, TransformationContext* transformation_context,
29     FuzzerContext* fuzzer_context,
30     protobufs::TransformationSequence* transformations)
31     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
32                  transformations) {}
33 
Apply()34 void FuzzerPassOutlineFunctions::Apply() {
35   std::vector<opt::Function*> original_functions;
36   for (auto& function : *GetIRContext()->module()) {
37     original_functions.push_back(&function);
38   }
39   for (auto& function : original_functions) {
40     if (!GetFuzzerContext()->ChoosePercentage(
41             GetFuzzerContext()->GetChanceOfOutliningFunction())) {
42       continue;
43     }
44     std::vector<opt::BasicBlock*> blocks;
45     for (auto& block : *function) {
46       blocks.push_back(&block);
47     }
48     auto entry_block = MaybeGetEntryBlockSuitableForOutlining(
49         blocks[GetFuzzerContext()->RandomIndex(blocks)]);
50 
51     if (!entry_block) {
52       // The chosen block is not suitable to be the entry block of a region that
53       // will be outlined.
54       continue;
55     }
56 
57     auto dominator_analysis = GetIRContext()->GetDominatorAnalysis(function);
58     auto postdominator_analysis =
59         GetIRContext()->GetPostDominatorAnalysis(function);
60     std::vector<opt::BasicBlock*> candidate_exit_blocks;
61     for (auto postdominates_entry_block = entry_block;
62          postdominates_entry_block != nullptr;
63          postdominates_entry_block = postdominator_analysis->ImmediateDominator(
64              postdominates_entry_block)) {
65       // Consider the block if it is dominated by the entry block, ignore it if
66       // it is a continue target.
67       if (dominator_analysis->Dominates(entry_block,
68                                         postdominates_entry_block) &&
69           !GetIRContext()->GetStructuredCFGAnalysis()->IsContinueBlock(
70               postdominates_entry_block->id())) {
71         candidate_exit_blocks.push_back(postdominates_entry_block);
72       }
73     }
74     if (candidate_exit_blocks.empty()) {
75       continue;
76     }
77     auto exit_block = MaybeGetExitBlockSuitableForOutlining(
78         candidate_exit_blocks[GetFuzzerContext()->RandomIndex(
79             candidate_exit_blocks)]);
80 
81     if (!exit_block) {
82       // The block chosen is not suitable
83       continue;
84     }
85 
86     auto region_blocks = TransformationOutlineFunction::GetRegionBlocks(
87         GetIRContext(), entry_block, exit_block);
88     std::map<uint32_t, uint32_t> input_id_to_fresh_id;
89     for (auto id : TransformationOutlineFunction::GetRegionInputIds(
90              GetIRContext(), region_blocks, exit_block)) {
91       input_id_to_fresh_id[id] = GetFuzzerContext()->GetFreshId();
92     }
93     std::map<uint32_t, uint32_t> output_id_to_fresh_id;
94     for (auto id : TransformationOutlineFunction::GetRegionOutputIds(
95              GetIRContext(), region_blocks, exit_block)) {
96       output_id_to_fresh_id[id] = GetFuzzerContext()->GetFreshId();
97     }
98     TransformationOutlineFunction transformation(
99         entry_block->id(), exit_block->id(),
100         /*new_function_struct_return_type_id*/
101         GetFuzzerContext()->GetFreshId(),
102         /*new_function_type_id*/ GetFuzzerContext()->GetFreshId(),
103         /*new_function_id*/ GetFuzzerContext()->GetFreshId(),
104         /*new_function_region_entry_block*/
105         GetFuzzerContext()->GetFreshId(),
106         /*new_caller_result_id*/ GetFuzzerContext()->GetFreshId(),
107         /*new_callee_result_id*/ GetFuzzerContext()->GetFreshId(),
108         /*input_id_to_fresh_id*/ input_id_to_fresh_id,
109         /*output_id_to_fresh_id*/ output_id_to_fresh_id);
110     MaybeApplyTransformation(transformation);
111   }
112 }
113 
114 opt::BasicBlock*
MaybeGetEntryBlockSuitableForOutlining(opt::BasicBlock * entry_block)115 FuzzerPassOutlineFunctions::MaybeGetEntryBlockSuitableForOutlining(
116     opt::BasicBlock* entry_block) {
117   // If the entry block is a loop header, we need to get or create its
118   // preheader and make it the entry block, if possible.
119   if (entry_block->IsLoopHeader()) {
120     auto predecessors =
121         GetIRContext()->cfg()->preds(entry_block->GetLabel()->result_id());
122 
123     if (predecessors.size() < 2) {
124       // The header only has one predecessor (the back-edge block) and thus
125       // it is unreachable. The block cannot be adjusted to be suitable for
126       // outlining.
127       return nullptr;
128     }
129 
130     // Get or create a suitable preheader and make it become the entry block.
131     entry_block =
132         GetOrCreateSimpleLoopPreheader(entry_block->GetLabel()->result_id());
133   }
134 
135   assert(!entry_block->IsLoopHeader() &&
136          "The entry block cannot be a loop header at this point.");
137 
138   // If the entry block starts with OpPhi or OpVariable, try to split it.
139   if (entry_block->begin()->opcode() == SpvOpPhi ||
140       entry_block->begin()->opcode() == SpvOpVariable) {
141     // Find the first non-OpPhi and non-OpVariable instruction.
142     auto non_phi_or_var_inst = &*entry_block->begin();
143     while (non_phi_or_var_inst->opcode() == SpvOpPhi ||
144            non_phi_or_var_inst->opcode() == SpvOpVariable) {
145       non_phi_or_var_inst = non_phi_or_var_inst->NextNode();
146     }
147 
148     // Split the block.
149     uint32_t new_block_id = GetFuzzerContext()->GetFreshId();
150     ApplyTransformation(TransformationSplitBlock(
151         MakeInstructionDescriptor(GetIRContext(), non_phi_or_var_inst),
152         new_block_id));
153 
154     // The new entry block is the newly-created block.
155     entry_block = &*entry_block->GetParent()->FindBlock(new_block_id);
156   }
157 
158   return entry_block;
159 }
160 
161 opt::BasicBlock*
MaybeGetExitBlockSuitableForOutlining(opt::BasicBlock * exit_block)162 FuzzerPassOutlineFunctions::MaybeGetExitBlockSuitableForOutlining(
163     opt::BasicBlock* exit_block) {
164   // The exit block must not be a continue target.
165   assert(!GetIRContext()->GetStructuredCFGAnalysis()->IsContinueBlock(
166              exit_block->id()) &&
167          "A candidate exit block cannot be a continue target.");
168 
169   // If the exit block is a merge block, try to split it and return the second
170   // block in the pair as the exit block.
171   if (GetIRContext()->GetStructuredCFGAnalysis()->IsMergeBlock(
172           exit_block->id())) {
173     uint32_t new_block_id = GetFuzzerContext()->GetFreshId();
174 
175     // Find the first non-OpPhi instruction, after which to split.
176     auto split_before = &*exit_block->begin();
177     while (split_before->opcode() == SpvOpPhi) {
178       split_before = split_before->NextNode();
179     }
180 
181     if (!MaybeApplyTransformation(TransformationSplitBlock(
182             MakeInstructionDescriptor(GetIRContext(), split_before),
183             new_block_id))) {
184       return nullptr;
185     }
186 
187     return &*exit_block->GetParent()->FindBlock(new_block_id);
188   }
189 
190   return exit_block;
191 }
192 
193 }  // namespace fuzz
194 }  // namespace spvtools
195