1 // Copyright (c) 2020 Vasyl Teliman
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.
14
15 #include "source/fuzz/transformation_swap_conditional_branch_operands.h"
16
17 #include "source/fuzz/fuzzer_util.h"
18 #include "source/fuzz/instruction_descriptor.h"
19
20 namespace spvtools {
21 namespace fuzz {
22
23 TransformationSwapConditionalBranchOperands::
TransformationSwapConditionalBranchOperands(const spvtools::fuzz::protobufs::TransformationSwapConditionalBranchOperands & message)24 TransformationSwapConditionalBranchOperands(
25 const spvtools::fuzz::protobufs::
26 TransformationSwapConditionalBranchOperands& message)
27 : message_(message) {}
28
29 TransformationSwapConditionalBranchOperands::
TransformationSwapConditionalBranchOperands(const protobufs::InstructionDescriptor & instruction_descriptor,uint32_t fresh_id)30 TransformationSwapConditionalBranchOperands(
31 const protobufs::InstructionDescriptor& instruction_descriptor,
32 uint32_t fresh_id) {
33 *message_.mutable_instruction_descriptor() = instruction_descriptor;
34 message_.set_fresh_id(fresh_id);
35 }
36
IsApplicable(opt::IRContext * ir_context,const TransformationContext &) const37 bool TransformationSwapConditionalBranchOperands::IsApplicable(
38 opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
39 const auto* inst =
40 FindInstruction(message_.instruction_descriptor(), ir_context);
41 return fuzzerutil::IsFreshId(ir_context, message_.fresh_id()) && inst &&
42 inst->opcode() == SpvOpBranchConditional;
43 }
44
Apply(opt::IRContext * ir_context,TransformationContext *) const45 void TransformationSwapConditionalBranchOperands::Apply(
46 opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
47 auto* branch_inst =
48 FindInstruction(message_.instruction_descriptor(), ir_context);
49 assert(branch_inst);
50
51 auto* block = ir_context->get_instr_block(branch_inst);
52 assert(block);
53
54 // Compute the last instruction in the |block| that allows us to insert
55 // OpLogicalNot above it.
56 auto iter = fuzzerutil::GetIteratorForInstruction(block, branch_inst);
57 if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLogicalNot, iter)) {
58 // There might be a merge instruction before OpBranchConditional.
59 --iter;
60 }
61
62 assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLogicalNot, iter) &&
63 "We should now be able to insert SpvOpLogicalNot before |iter|");
64
65 // Get the instruction whose result is used as a condition for
66 // OpBranchConditional.
67 const auto* condition_inst = ir_context->get_def_use_mgr()->GetDef(
68 branch_inst->GetSingleWordInOperand(0));
69 assert(condition_inst);
70
71 // We are swapping the labels in OpBranchConditional. This means that we must
72 // invert the guard as well. We are using OpLogicalNot for that purpose here.
73 iter.InsertBefore(MakeUnique<opt::Instruction>(
74 ir_context, SpvOpLogicalNot, condition_inst->type_id(),
75 message_.fresh_id(),
76 opt::Instruction::OperandList{
77 {SPV_OPERAND_TYPE_ID, {condition_inst->result_id()}}}));
78
79 fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
80
81 // Update OpBranchConditional condition operand.
82 branch_inst->GetInOperand(0).words[0] = message_.fresh_id();
83
84 // Swap label operands.
85 std::swap(branch_inst->GetInOperand(1), branch_inst->GetInOperand(2));
86
87 // Additionally, swap branch weights if present.
88 if (branch_inst->NumInOperands() > 3) {
89 std::swap(branch_inst->GetInOperand(3), branch_inst->GetInOperand(4));
90 }
91
92 // Make sure the changes are analyzed.
93 ir_context->InvalidateAnalysesExceptFor(
94 opt::IRContext::Analysis::kAnalysisNone);
95 }
96
97 protobufs::Transformation
ToMessage() const98 TransformationSwapConditionalBranchOperands::ToMessage() const {
99 protobufs::Transformation result;
100 *result.mutable_swap_conditional_branch_operands() = message_;
101 return result;
102 }
103
104 std::unordered_set<uint32_t>
GetFreshIds() const105 TransformationSwapConditionalBranchOperands::GetFreshIds() const {
106 return {message_.fresh_id()};
107 }
108
109 } // namespace fuzz
110 } // namespace spvtools
111