1 // Copyright (c) 2019 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/fuzzer_pass_split_blocks.h"
19 #include "source/fuzz/instruction_descriptor.h"
20 #include "source/fuzz/transformation_split_block.h"
25 FuzzerPassSplitBlocks::FuzzerPassSplitBlocks(
26 opt::IRContext
* ir_context
, TransformationContext
* transformation_context
,
27 FuzzerContext
* fuzzer_context
,
28 protobufs::TransformationSequence
* transformations
,
29 bool ignore_inapplicable_transformations
)
30 : FuzzerPass(ir_context
, transformation_context
, fuzzer_context
,
31 transformations
, ignore_inapplicable_transformations
) {}
33 void FuzzerPassSplitBlocks::Apply() {
34 // Gather up pointers to all the blocks in the module. We are then able to
35 // iterate over these pointers and split the blocks to which they point;
36 // we cannot safely split blocks while we iterate through the module.
37 std::vector
<opt::BasicBlock
*> blocks
;
38 for (auto& function
: *GetIRContext()->module()) {
39 for (auto& block
: function
) {
40 blocks
.push_back(&block
);
44 // Now go through all the block pointers that were gathered.
45 for (auto& block
: blocks
) {
46 // Probabilistically decide whether to try to split this block.
47 if (!GetFuzzerContext()->ChoosePercentage(
48 GetFuzzerContext()->GetChanceOfSplittingBlock())) {
49 // We are not going to try to split this block.
53 // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2964): consider
54 // taking a simpler approach to identifying the instruction before which
57 // We are going to try to split this block. We now need to choose where
58 // to split it. We describe the instruction before which we would like to
59 // split a block via an InstructionDescriptor, details of which are
60 // commented in the protobufs definition file.
61 std::vector
<protobufs::InstructionDescriptor
> instruction_descriptors
;
63 // The initial base instruction is the block label.
64 uint32_t base
= block
->id();
66 // Counts the number of times we have seen each opcode since we reset the
68 std::map
<spv::Op
, uint32_t> skip_count
;
70 // Consider every instruction in the block. The label is excluded: it is
71 // only necessary to consider it as a base in case the first instruction
72 // in the block does not have a result id.
73 for (auto& inst
: *block
) {
74 if (inst
.HasResultId()) {
75 // In the case that the instruction has a result id, we use the
76 // instruction as its own base, and clear the skip counts we have
78 base
= inst
.result_id();
81 const spv::Op opcode
= inst
.opcode();
82 instruction_descriptors
.emplace_back(MakeInstructionDescriptor(
83 base
, opcode
, skip_count
.count(opcode
) ? skip_count
.at(opcode
) : 0));
84 if (!inst
.HasResultId()) {
86 skip_count
.count(opcode
) ? skip_count
.at(opcode
) + 1 : 1;
89 // Having identified all the places we might be able to split the block,
90 // we choose one of them.
91 auto transformation
= TransformationSplitBlock(
92 instruction_descriptors
[GetFuzzerContext()->RandomIndex(
93 instruction_descriptors
)],
94 GetFuzzerContext()->GetFreshId());
95 // If the position we have chosen turns out to be a valid place to split
96 // the block, we apply the split. Otherwise the block just doesn't get
98 MaybeApplyTransformation(transformation
);
103 } // namespace spvtools