Roll external/abseil_cpp/ 8f739d18b..917bfee46 (2 commits) (#5887)
[KhronosGroup/SPIRV-Tools.git] / source / fuzz / pass_management / repeated_pass_recommender_standard.cpp
blob6c61c0d4e4fe431df11e827f7b676b92501873b2
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.
15 #include "source/fuzz/pass_management/repeated_pass_recommender_standard.h"
17 #include <numeric>
19 namespace spvtools {
20 namespace fuzz {
22 RepeatedPassRecommenderStandard::RepeatedPassRecommenderStandard(
23 RepeatedPassInstances* pass_instances, FuzzerContext* fuzzer_context)
24 : pass_instances_(pass_instances), fuzzer_context_(fuzzer_context) {}
26 RepeatedPassRecommenderStandard::~RepeatedPassRecommenderStandard() = default;
28 std::vector<FuzzerPass*>
29 RepeatedPassRecommenderStandard::GetFuturePassRecommendations(
30 const FuzzerPass& pass) {
31 if (&pass == pass_instances_->GetAddAccessChains()) {
32 // - Adding access chains means there is more scope for loading and storing
33 // - It could be worth making more access chains from the recently-added
34 // access chains
35 return RandomOrderAndNonNull({pass_instances_->GetAddLoads(),
36 pass_instances_->GetAddStores(),
37 pass_instances_->GetAddAccessChains()});
39 if (&pass == pass_instances_->GetAddBitInstructionSynonyms()) {
40 // - Adding bit instruction synonyms creates opportunities to apply synonyms
41 return RandomOrderAndNonNull({pass_instances_->GetApplyIdSynonyms()});
43 if (&pass == pass_instances_->GetAddCompositeExtract()) {
44 // - This transformation can introduce synonyms to the fact manager.
45 return RandomOrderAndNonNull({pass_instances_->GetApplyIdSynonyms()});
47 if (&pass == pass_instances_->GetAddCompositeInserts()) {
48 // - Having added inserts we will have more vectors, so there is scope for
49 // vector shuffling
50 // - Adding inserts creates synonyms, which we should try to use
51 // - Vector inserts can be made dynamic
52 return RandomOrderAndNonNull(
53 {pass_instances_->GetAddVectorShuffleInstructions(),
54 pass_instances_->GetApplyIdSynonyms(),
55 pass_instances_->GetMakeVectorOperationsDynamic()});
57 if (&pass == pass_instances_->GetAddCompositeTypes()) {
58 // - More composite types gives more scope for constructing composites
59 return RandomOrderAndNonNull({pass_instances_->GetConstructComposites()});
61 if (&pass == pass_instances_->GetAddCopyMemory()) {
62 // - Recently-added copy memories could be replace with load-store pairs
63 return RandomOrderAndNonNull(
64 {pass_instances_->GetReplaceCopyMemoriesWithLoadsStores()});
66 if (&pass == pass_instances_->GetAddDeadBlocks()) {
67 // - Dead blocks are great for adding function calls
68 // - Dead blocks are also great for adding loads and stores
69 // - The guard associated with a dead block can be obfuscated
70 // - Branches from dead blocks may be replaced with exits
71 return RandomOrderAndNonNull(
72 {pass_instances_->GetAddFunctionCalls(), pass_instances_->GetAddLoads(),
73 pass_instances_->GetAddStores(),
74 pass_instances_->GetObfuscateConstants(),
75 pass_instances_->GetReplaceBranchesFromDeadBlocksWithExits()});
77 if (&pass == pass_instances_->GetAddDeadBreaks()) {
78 // - The guard of the dead break is a good candidate for obfuscation
79 return RandomOrderAndNonNull({pass_instances_->GetObfuscateConstants()});
81 if (&pass == pass_instances_->GetAddDeadContinues()) {
82 // - The guard of the dead continue is a good candidate for obfuscation
83 return RandomOrderAndNonNull({pass_instances_->GetObfuscateConstants()});
85 if (&pass == pass_instances_->GetAddEquationInstructions()) {
86 // - Equation instructions can create synonyms, which we can apply
87 // - Equation instructions collaborate with one another to make synonyms, so
88 // having added some it is worth adding more
89 return RandomOrderAndNonNull(
90 {pass_instances_->GetApplyIdSynonyms(),
91 pass_instances_->GetAddEquationInstructions()});
93 if (&pass == pass_instances_->GetAddFunctionCalls()) {
94 // - Called functions can be inlined
95 // - Irrelevant ids are created, so they can be replaced
96 return RandomOrderAndNonNull({pass_instances_->GetInlineFunctions(),
97 pass_instances_->GetReplaceIrrelevantIds()});
99 if (&pass == pass_instances_->GetAddGlobalVariables()) {
100 // - New globals provide new possibilities for making access chains
101 // - We can load from and store to new globals
102 return RandomOrderAndNonNull({pass_instances_->GetAddAccessChains(),
103 pass_instances_->GetAddLoads(),
104 pass_instances_->GetAddStores()});
106 if (&pass == pass_instances_->GetAddImageSampleUnusedComponents()) {
107 // - This introduces an unused component whose id is irrelevant and can be
108 // replaced
109 return RandomOrderAndNonNull({pass_instances_->GetReplaceIrrelevantIds()});
111 if (&pass == pass_instances_->GetAddLoads()) {
112 // - Loads might end up with corresponding stores, so that pairs can be
113 // replaced with memory copies
114 return RandomOrderAndNonNull(
115 {pass_instances_->GetReplaceLoadsStoresWithCopyMemories()});
117 if (&pass == pass_instances_->GetAddLocalVariables()) {
118 // - New locals provide new possibilities for making access chains
119 // - We can load from and store to new locals
120 return RandomOrderAndNonNull({pass_instances_->GetAddAccessChains(),
121 pass_instances_->GetAddLoads(),
122 pass_instances_->GetAddStores()});
124 if (&pass == pass_instances_->GetAddLoopPreheaders()) {
125 // - The loop preheader provides more scope for duplicating regions and
126 // outlining functions.
127 return RandomOrderAndNonNull(
128 {pass_instances_->GetDuplicateRegionsWithSelections(),
129 pass_instances_->GetOutlineFunctions(),
130 pass_instances_->GetWrapRegionsInSelections()});
132 if (&pass == pass_instances_->GetAddLoopsToCreateIntConstantSynonyms()) {
133 // - New synonyms can be applied
134 return RandomOrderAndNonNull({pass_instances_->GetApplyIdSynonyms()});
136 if (&pass == pass_instances_->GetAddOpPhiSynonyms()) {
137 // - New synonyms can be applied
138 // - If OpPhi synonyms are introduced for blocks with dead predecessors, the
139 // values consumed from dead predecessors can be replaced
140 return RandomOrderAndNonNull(
141 {pass_instances_->GetApplyIdSynonyms(),
142 pass_instances_->GetReplaceOpPhiIdsFromDeadPredecessors()});
144 if (&pass == pass_instances_->GetAddParameters()) {
145 // - We might be able to create interesting synonyms of new parameters.
146 // - This introduces irrelevant ids, which can be replaced
147 return RandomOrderAndNonNull({pass_instances_->GetAddSynonyms(),
148 pass_instances_->GetReplaceIrrelevantIds()});
150 if (&pass == pass_instances_->GetAddRelaxedDecorations()) {
151 // - No obvious follow-on passes
152 return {};
154 if (&pass == pass_instances_->GetAddStores()) {
155 // - Stores might end up with corresponding loads, so that pairs can be
156 // replaced with memory copies
157 return RandomOrderAndNonNull(
158 {pass_instances_->GetReplaceLoadsStoresWithCopyMemories()});
160 if (&pass == pass_instances_->GetAddSynonyms()) {
161 // - New synonyms can be applied
162 // - Synonym instructions use constants, which can be obfuscated
163 // - Synonym instructions use irrelevant ids, which can be replaced
164 // - Synonym instructions introduce addition/subtraction, which can be
165 // replaced with carrying/extended versions
166 return RandomOrderAndNonNull(
167 {pass_instances_->GetApplyIdSynonyms(),
168 pass_instances_->GetObfuscateConstants(),
169 pass_instances_->GetReplaceAddsSubsMulsWithCarryingExtended(),
170 pass_instances_->GetReplaceIrrelevantIds()});
172 if (&pass == pass_instances_->GetAddVectorShuffleInstructions()) {
173 // - Vector shuffles create synonyms that can be applied
174 // - TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3806) Extract
175 // from composites.
176 return RandomOrderAndNonNull({pass_instances_->GetApplyIdSynonyms()});
178 if (&pass == pass_instances_->GetApplyIdSynonyms()) {
179 // - No obvious follow-on passes
180 return {};
182 if (&pass == pass_instances_->GetConstructComposites()) {
183 // - TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3806): Extract
184 // from composites.
185 return RandomOrderAndNonNull({});
187 if (&pass == pass_instances_->GetCopyObjects()) {
188 // - Object copies create synonyms that can be applied
189 // - OpCopyObject can be replaced with a store/load pair
190 return RandomOrderAndNonNull(
191 {pass_instances_->GetApplyIdSynonyms(),
192 pass_instances_->GetReplaceCopyObjectsWithStoresLoads()});
194 if (&pass == pass_instances_->GetDonateModules()) {
195 // - New functions in the module can be called
196 // - Donated dead functions produce irrelevant ids, which can be replaced
197 // - Donated functions are good candidates for having their returns merged
198 // - Donated dead functions may allow branches to be replaced with exits
199 return RandomOrderAndNonNull(
200 {pass_instances_->GetAddFunctionCalls(),
201 pass_instances_->GetReplaceIrrelevantIds(),
202 pass_instances_->GetMergeFunctionReturns(),
203 pass_instances_->GetReplaceBranchesFromDeadBlocksWithExits()});
205 if (&pass == pass_instances_->GetDuplicateRegionsWithSelections()) {
206 // - Parts of duplicated regions can be outlined
207 return RandomOrderAndNonNull({pass_instances_->GetOutlineFunctions()});
209 if (&pass == pass_instances_->GetExpandVectorReductions()) {
210 // - Adding OpAny and OpAll synonyms creates opportunities to apply synonyms
211 return RandomOrderAndNonNull({pass_instances_->GetApplyIdSynonyms()});
213 if (&pass == pass_instances_->GetFlattenConditionalBranches()) {
214 // - Parts of flattened selections can be outlined
215 // - The flattening transformation introduces constants and irrelevant ids
216 // for enclosing hard-to-flatten operations; these can be obfuscated or
217 // replaced
218 return RandomOrderAndNonNull({pass_instances_->GetObfuscateConstants(),
219 pass_instances_->GetOutlineFunctions(),
220 pass_instances_->GetReplaceIrrelevantIds()});
222 if (&pass == pass_instances_->GetInlineFunctions()) {
223 // - Parts of inlined functions can be outlined again
224 return RandomOrderAndNonNull({pass_instances_->GetOutlineFunctions()});
226 if (&pass == pass_instances_->GetInvertComparisonOperators()) {
227 // - No obvious follow-on passes
228 return {};
230 if (&pass == pass_instances_->GetMakeVectorOperationsDynamic()) {
231 // - No obvious follow-on passes
232 return {};
234 if (&pass == pass_instances_->GetMergeBlocks()) {
235 // - Having merged some blocks it may be interesting to split them in a
236 // different way
237 return RandomOrderAndNonNull({pass_instances_->GetSplitBlocks()});
239 if (&pass == pass_instances_->GetMergeFunctionReturns()) {
240 // - Functions without early returns are more likely to be able to be
241 // inlined.
242 return RandomOrderAndNonNull({pass_instances_->GetInlineFunctions()});
244 if (&pass == pass_instances_->GetMutatePointers()) {
245 // - This creates irrelevant ids, which can be replaced
246 return RandomOrderAndNonNull({pass_instances_->GetReplaceIrrelevantIds()});
248 if (&pass == pass_instances_->GetObfuscateConstants()) {
249 // - No obvious follow-on passes
250 return {};
252 if (&pass == pass_instances_->GetOutlineFunctions()) {
253 // - This creates more functions, which can be called
254 // - Inlining the function for the region that was outlined might also be
255 // fruitful; it will be inlined in a different form
256 return RandomOrderAndNonNull({pass_instances_->GetAddFunctionCalls(),
257 pass_instances_->GetInlineFunctions()});
259 if (&pass == pass_instances_->GetPermuteBlocks()) {
260 // No obvious follow-on passes
261 return {};
263 if (&pass == pass_instances_->GetPermuteFunctionParameters()) {
264 // No obvious follow-on passes
265 return {};
267 if (&pass == pass_instances_->GetPermuteInstructions()) {
268 // No obvious follow-on passes
269 return {};
271 if (&pass == pass_instances_->GetPropagateInstructionsDown()) {
272 // - This fuzzer pass might create new synonyms that can later be applied.
273 // - This fuzzer pass might create irrelevant ids that can later be
274 // replaced.
275 return RandomOrderAndNonNull({pass_instances_->GetApplyIdSynonyms(),
276 pass_instances_->GetReplaceIrrelevantIds()});
278 if (&pass == pass_instances_->GetPropagateInstructionsUp()) {
279 // No obvious follow-on passes
280 return {};
282 if (&pass == pass_instances_->GetPushIdsThroughVariables()) {
283 // - This pass creates synonyms, so it is worth applying them
284 return RandomOrderAndNonNull({pass_instances_->GetApplyIdSynonyms()});
286 if (&pass == pass_instances_->GetReplaceAddsSubsMulsWithCarryingExtended()) {
287 // No obvious follow-on passes
288 return {};
290 if (&pass == pass_instances_->GetReplaceBranchesFromDeadBlocksWithExits()) {
291 // - Changing a branch to OpReturnValue introduces an irrelevant id, which
292 // can be replaced
293 return RandomOrderAndNonNull({pass_instances_->GetReplaceIrrelevantIds()});
295 if (&pass == pass_instances_->GetReplaceCopyMemoriesWithLoadsStores()) {
296 // No obvious follow-on passes
297 return {};
299 if (&pass == pass_instances_->GetReplaceCopyObjectsWithStoresLoads()) {
300 // - We may end up with load/store pairs that could be used to create memory
301 // copies
302 return RandomOrderAndNonNull(
303 {pass_instances_->GetReplaceLoadsStoresWithCopyMemories()});
305 if (&pass == pass_instances_->GetReplaceIrrelevantIds()) {
306 // No obvious follow-on passes
307 return {};
309 if (&pass == pass_instances_->GetReplaceLinearAlgebraInstructions()) {
310 // No obvious follow-on passes
311 return {};
313 if (&pass == pass_instances_->GetReplaceLoadsStoresWithCopyMemories()) {
314 // No obvious follow-on passes
315 return {};
317 if (&pass == pass_instances_->GetReplaceOpPhiIdsFromDeadPredecessors()) {
318 // No obvious follow-on passes
319 return {};
321 if (&pass == pass_instances_->GetReplaceOpSelectsWithConditionalBranches()) {
322 // No obvious follow-on passes
323 return {};
325 if (&pass == pass_instances_->GetReplaceParameterWithGlobal()) {
326 // No obvious follow-on passes
327 return {};
329 if (&pass == pass_instances_->GetReplaceParamsWithStruct()) {
330 // No obvious follow-on passes
331 return {};
333 if (&pass == pass_instances_->GetSplitBlocks()) {
334 // - More blocks means more chances for adding dead breaks/continues, and
335 // for adding dead blocks
336 return RandomOrderAndNonNull({pass_instances_->GetAddDeadBreaks(),
337 pass_instances_->GetAddDeadContinues(),
338 pass_instances_->GetAddDeadBlocks()});
340 if (&pass == pass_instances_->GetSwapBranchConditionalOperands()) {
341 // No obvious follow-on passes
342 return {};
344 if (&pass == pass_instances_->GetWrapRegionsInSelections()) {
345 // - This pass uses an irrelevant boolean constant - we can replace it with
346 // something more interesting.
347 // - We can obfuscate that very constant as well.
348 // - We can flatten created selection construct.
349 return RandomOrderAndNonNull(
350 {pass_instances_->GetObfuscateConstants(),
351 pass_instances_->GetReplaceIrrelevantIds(),
352 pass_instances_->GetFlattenConditionalBranches()});
354 if (&pass == pass_instances_->GetWrapVectorSynonym()) {
355 // This transformation introduces synonym facts and irrelevant ids.
356 return RandomOrderAndNonNull({pass_instances_->GetApplyIdSynonyms(),
357 pass_instances_->GetReplaceIrrelevantIds()});
360 assert(false && "Unreachable: every fuzzer pass should be dealt with.");
361 return {};
364 std::vector<FuzzerPass*> RepeatedPassRecommenderStandard::RandomOrderAndNonNull(
365 const std::vector<FuzzerPass*>& passes) {
366 std::vector<uint32_t> indices(passes.size());
367 std::iota(indices.begin(), indices.end(), 0);
368 std::vector<FuzzerPass*> result;
369 while (!indices.empty()) {
370 FuzzerPass* maybe_pass =
371 passes[fuzzer_context_->RemoveAtRandomIndex(&indices)];
372 if (maybe_pass != nullptr &&
373 fuzzer_context_->ChoosePercentage(
374 fuzzer_context_
375 ->GetChanceOfAcceptingRepeatedPassRecommendation())) {
376 result.push_back(maybe_pass);
379 return result;
382 } // namespace fuzz
383 } // namespace spvtools