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() == SpvOpBranchConditional;
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(SpvOpLogicalNot, iter)) {
57 // There might be a merge instruction before OpBranchConditional.
58 --iter;
59 }
60
61 assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLogicalNot, iter) &&
62 "We should now be able to insert SpvOpLogicalNot before |iter|");
63
64 // Get the instruction whose result is used as a condition for
65 // OpBranchConditional.
66 const auto* condition_inst = ir_context->get_def_use_mgr()->GetDef(
67 branch_inst->GetSingleWordInOperand(0));
68 assert(condition_inst);
69
70 // We are swapping the labels in OpBranchConditional. This means that we must
71 // invert the guard as well. We are using OpLogicalNot for that purpose here.
72 auto new_instruction = MakeUnique<opt::Instruction>(
73 ir_context, SpvOpLogicalNot, condition_inst->type_id(),
74 message_.fresh_id(),
75 opt::Instruction::OperandList{
76 {SPV_OPERAND_TYPE_ID, {condition_inst->result_id()}}});
77 auto new_instruction_ptr = new_instruction.get();
78 iter.InsertBefore(std::move(new_instruction));
79
80 fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
81
82 // Update OpBranchConditional condition operand.
83 branch_inst->GetInOperand(0).words[0] = message_.fresh_id();
84
85 // Swap label operands.
86 std::swap(branch_inst->GetInOperand(1), branch_inst->GetInOperand(2));
87
88 // Additionally, swap branch weights if present.
89 if (branch_inst->NumInOperands() > 3) {
90 std::swap(branch_inst->GetInOperand(3), branch_inst->GetInOperand(4));
91 }
92
93 ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
94 ir_context->set_instr_block(new_instruction_ptr, block);
95 ir_context->get_def_use_mgr()->EraseUseRecordsOfOperandIds(branch_inst);
96 ir_context->get_def_use_mgr()->AnalyzeInstUse(branch_inst);
97
98 // No analyses need to be invalidated since the transformation is local to a
99 // block and the def-use and instruction-to-block mappings have been updated.
100 }
101
102 protobufs::Transformation
ToMessage() const103 TransformationSwapConditionalBranchOperands::ToMessage() const {
104 protobufs::Transformation result;
105 *result.mutable_swap_conditional_branch_operands() = message_;
106 return result;
107 }
108
109 std::unordered_set<uint32_t>
GetFreshIds() const110 TransformationSwapConditionalBranchOperands::GetFreshIds() const {
111 return {message_.fresh_id()};
112 }
113
114 } // namespace fuzz
115 } // namespace spvtools
116