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(protobufs::TransformationSwapConditionalBranchOperands message)24 TransformationSwapConditionalBranchOperands(
25 protobufs::TransformationSwapConditionalBranchOperands message)
26 : message_(std::move(message)) {}
27
28 TransformationSwapConditionalBranchOperands::
TransformationSwapConditionalBranchOperands(const protobufs::InstructionDescriptor & instruction_descriptor,uint32_t fresh_id)29 TransformationSwapConditionalBranchOperands(
30 const protobufs::InstructionDescriptor& instruction_descriptor,
31 uint32_t fresh_id) {
32 *message_.mutable_instruction_descriptor() = instruction_descriptor;
33 message_.set_fresh_id(fresh_id);
34 }
35
IsApplicable(opt::IRContext * ir_context,const TransformationContext &) const36 bool TransformationSwapConditionalBranchOperands::IsApplicable(
37 opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
38 const auto* inst =
39 FindInstruction(message_.instruction_descriptor(), ir_context);
40 return fuzzerutil::IsFreshId(ir_context, message_.fresh_id()) && inst &&
41 inst->opcode() == spv::Op::OpBranchConditional;
42 }
43
Apply(opt::IRContext * ir_context,TransformationContext *) const44 void TransformationSwapConditionalBranchOperands::Apply(
45 opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
46 auto* branch_inst =
47 FindInstruction(message_.instruction_descriptor(), ir_context);
48 assert(branch_inst);
49
50 auto* block = ir_context->get_instr_block(branch_inst);
51 assert(block);
52
53 // Compute the last instruction in the |block| that allows us to insert
54 // OpLogicalNot above it.
55 auto iter = fuzzerutil::GetIteratorForInstruction(block, branch_inst);
56 if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpLogicalNot,
57 iter)) {
58 // There might be a merge instruction before OpBranchConditional.
59 --iter;
60 }
61
62 assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpLogicalNot,
63 iter) &&
64 "We should now be able to insert spv::Op::OpLogicalNot before |iter|");
65
66 // Get the instruction whose result is used as a condition for
67 // OpBranchConditional.
68 const auto* condition_inst = ir_context->get_def_use_mgr()->GetDef(
69 branch_inst->GetSingleWordInOperand(0));
70 assert(condition_inst);
71
72 // We are swapping the labels in OpBranchConditional. This means that we must
73 // invert the guard as well. We are using OpLogicalNot for that purpose here.
74 auto new_instruction = MakeUnique<opt::Instruction>(
75 ir_context, spv::Op::OpLogicalNot, condition_inst->type_id(),
76 message_.fresh_id(),
77 opt::Instruction::OperandList{
78 {SPV_OPERAND_TYPE_ID, {condition_inst->result_id()}}});
79 auto new_instruction_ptr = new_instruction.get();
80 iter.InsertBefore(std::move(new_instruction));
81
82 fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
83
84 // Update OpBranchConditional condition operand.
85 branch_inst->GetInOperand(0).words[0] = message_.fresh_id();
86
87 // Swap label operands.
88 std::swap(branch_inst->GetInOperand(1), branch_inst->GetInOperand(2));
89
90 // Additionally, swap branch weights if present.
91 if (branch_inst->NumInOperands() > 3) {
92 std::swap(branch_inst->GetInOperand(3), branch_inst->GetInOperand(4));
93 }
94
95 ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
96 ir_context->set_instr_block(new_instruction_ptr, block);
97 ir_context->get_def_use_mgr()->EraseUseRecordsOfOperandIds(branch_inst);
98 ir_context->get_def_use_mgr()->AnalyzeInstUse(branch_inst);
99
100 // No analyses need to be invalidated since the transformation is local to a
101 // block and the def-use and instruction-to-block mappings have been updated.
102 }
103
104 protobufs::Transformation
ToMessage() const105 TransformationSwapConditionalBranchOperands::ToMessage() const {
106 protobufs::Transformation result;
107 *result.mutable_swap_conditional_branch_operands() = message_;
108 return result;
109 }
110
111 std::unordered_set<uint32_t>
GetFreshIds() const112 TransformationSwapConditionalBranchOperands::GetFreshIds() const {
113 return {message_.fresh_id()};
114 }
115
116 } // namespace fuzz
117 } // namespace spvtools
118