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