1 // Copyright (c) 2020 Google LLC
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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"
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
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
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
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
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
176 return RandomOrderAndNonNull({pass_instances_
->GetApplyIdSynonyms()});
178 if (&pass
== pass_instances_
->GetApplyIdSynonyms()) {
179 // - No obvious follow-on passes
182 if (&pass
== pass_instances_
->GetConstructComposites()) {
183 // - TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3806): Extract
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
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
230 if (&pass
== pass_instances_
->GetMakeVectorOperationsDynamic()) {
231 // - No obvious follow-on passes
234 if (&pass
== pass_instances_
->GetMergeBlocks()) {
235 // - Having merged some blocks it may be interesting to split them in a
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
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
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
263 if (&pass
== pass_instances_
->GetPermuteFunctionParameters()) {
264 // No obvious follow-on passes
267 if (&pass
== pass_instances_
->GetPermuteInstructions()) {
268 // No obvious follow-on passes
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
275 return RandomOrderAndNonNull({pass_instances_
->GetApplyIdSynonyms(),
276 pass_instances_
->GetReplaceIrrelevantIds()});
278 if (&pass
== pass_instances_
->GetPropagateInstructionsUp()) {
279 // No obvious follow-on passes
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
290 if (&pass
== pass_instances_
->GetReplaceBranchesFromDeadBlocksWithExits()) {
291 // - Changing a branch to OpReturnValue introduces an irrelevant id, which
293 return RandomOrderAndNonNull({pass_instances_
->GetReplaceIrrelevantIds()});
295 if (&pass
== pass_instances_
->GetReplaceCopyMemoriesWithLoadsStores()) {
296 // No obvious follow-on passes
299 if (&pass
== pass_instances_
->GetReplaceCopyObjectsWithStoresLoads()) {
300 // - We may end up with load/store pairs that could be used to create memory
302 return RandomOrderAndNonNull(
303 {pass_instances_
->GetReplaceLoadsStoresWithCopyMemories()});
305 if (&pass
== pass_instances_
->GetReplaceIrrelevantIds()) {
306 // No obvious follow-on passes
309 if (&pass
== pass_instances_
->GetReplaceLinearAlgebraInstructions()) {
310 // No obvious follow-on passes
313 if (&pass
== pass_instances_
->GetReplaceLoadsStoresWithCopyMemories()) {
314 // No obvious follow-on passes
317 if (&pass
== pass_instances_
->GetReplaceOpPhiIdsFromDeadPredecessors()) {
318 // No obvious follow-on passes
321 if (&pass
== pass_instances_
->GetReplaceOpSelectsWithConditionalBranches()) {
322 // No obvious follow-on passes
325 if (&pass
== pass_instances_
->GetReplaceParameterWithGlobal()) {
326 // No obvious follow-on passes
329 if (&pass
== pass_instances_
->GetReplaceParamsWithStruct()) {
330 // No obvious follow-on passes
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
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.");
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(
375 ->GetChanceOfAcceptingRepeatedPassRecommendation())) {
376 result
.push_back(maybe_pass
);
383 } // namespace spvtools