| // Copyright (c) 2019 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/fuzzer_pass_adjust_loop_controls.h" | |
| #include "source/fuzz/transformation_set_loop_control.h" | |
| namespace spvtools { | |
| namespace fuzz { | |
| FuzzerPassAdjustLoopControls::FuzzerPassAdjustLoopControls( | |
| opt::IRContext* ir_context, TransformationContext* transformation_context, | |
| FuzzerContext* fuzzer_context, | |
| protobufs::TransformationSequence* transformations, | |
| bool ignore_inapplicable_transformations) | |
| : FuzzerPass(ir_context, transformation_context, fuzzer_context, | |
| transformations, ignore_inapplicable_transformations) {} | |
| void FuzzerPassAdjustLoopControls::Apply() { | |
| // Consider every merge instruction in the module (via looking through all | |
| // functions and blocks). | |
| for (auto& function : *GetIRContext()->module()) { | |
| for (auto& block : function) { | |
| if (auto merge_inst = block.GetMergeInst()) { | |
| // Ignore the instruction if it is not a loop merge. | |
| if (merge_inst->opcode() != spv::Op::OpLoopMerge) { | |
| continue; | |
| } | |
| // Decide randomly whether to adjust this loop merge. | |
| if (!GetFuzzerContext()->ChoosePercentage( | |
| GetFuzzerContext()->GetChanceOfAdjustingLoopControl())) { | |
| continue; | |
| } | |
| uint32_t existing_mask = merge_inst->GetSingleWordOperand( | |
| TransformationSetLoopControl::kLoopControlMaskInOperandIndex); | |
| // First, set the new mask to one of None, Unroll or DontUnroll. | |
| std::vector<uint32_t> basic_masks = { | |
| uint32_t(spv::LoopControlMask::MaskNone), | |
| uint32_t(spv::LoopControlMask::Unroll), | |
| uint32_t(spv::LoopControlMask::DontUnroll)}; | |
| uint32_t new_mask = | |
| basic_masks[GetFuzzerContext()->RandomIndex(basic_masks)]; | |
| // For the loop controls that depend on guarantees about what the loop | |
| // does, check which of these were present in the existing mask and | |
| // randomly decide whether to keep them. They are just hints, so | |
| // removing them should not change the semantics of the module. | |
| for (auto mask_bit : {spv::LoopControlMask::DependencyInfinite, | |
| spv::LoopControlMask::DependencyLength, | |
| spv::LoopControlMask::MinIterations, | |
| spv::LoopControlMask::MaxIterations, | |
| spv::LoopControlMask::IterationMultiple}) { | |
| if ((existing_mask & uint32_t(mask_bit)) && | |
| GetFuzzerContext()->ChooseEven()) { | |
| // The mask bits we are considering are not available in all SPIR-V | |
| // versions. However, we only include a mask bit if it was present | |
| // in the original loop control mask, and we work under the | |
| // assumption that we are transforming a valid module, thus we don't | |
| // need to actually check whether the SPIR-V version being used | |
| // supports these loop control mask bits. | |
| new_mask |= uint32_t(mask_bit); | |
| } | |
| } | |
| // We use 0 for peel count and partial count in the case that we choose | |
| // not to set these controls. | |
| uint32_t peel_count = 0; | |
| uint32_t partial_count = 0; | |
| // PeelCount and PartialCount are not compatible with DontUnroll, so | |
| // we check whether DontUnroll is set. | |
| if (!(new_mask & uint32_t(spv::LoopControlMask::DontUnroll))) { | |
| // If PeelCount is supported by this SPIR-V version, randomly choose | |
| // whether to set it. If it was set in the original mask and is not | |
| // selected for setting here, that amounts to dropping it. | |
| if (TransformationSetLoopControl::PeelCountIsSupported( | |
| GetIRContext()) && | |
| GetFuzzerContext()->ChooseEven()) { | |
| new_mask |= uint32_t(spv::LoopControlMask::PeelCount); | |
| // The peel count is chosen randomly - if PeelCount was already set | |
| // this will overwrite whatever peel count was previously used. | |
| peel_count = GetFuzzerContext()->GetRandomLoopControlPeelCount(); | |
| } | |
| // Similar, but for PartialCount. | |
| if (TransformationSetLoopControl::PartialCountIsSupported( | |
| GetIRContext()) && | |
| GetFuzzerContext()->ChooseEven()) { | |
| new_mask |= uint32_t(spv::LoopControlMask::PartialCount); | |
| partial_count = | |
| GetFuzzerContext()->GetRandomLoopControlPartialCount(); | |
| } | |
| } | |
| // Apply the transformation and add it to the output transformation | |
| // sequence. | |
| TransformationSetLoopControl transformation(block.id(), new_mask, | |
| peel_count, partial_count); | |
| ApplyTransformation(transformation); | |
| } | |
| } | |
| } | |
| } | |
| } // namespace fuzz | |
| } // namespace spvtools |