1 // Copyright (c) 2019 Google LLC
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_add_dead_continue.h"
16
17 #include "source/fuzz/fuzzer_util.h"
18
19 namespace spvtools {
20 namespace fuzz {
21
TransformationAddDeadContinue(protobufs::TransformationAddDeadContinue message)22 TransformationAddDeadContinue::TransformationAddDeadContinue(
23 protobufs::TransformationAddDeadContinue message)
24 : message_(std::move(message)) {}
25
TransformationAddDeadContinue(uint32_t from_block,bool continue_condition_value,std::vector<uint32_t> phi_id)26 TransformationAddDeadContinue::TransformationAddDeadContinue(
27 uint32_t from_block, bool continue_condition_value,
28 std::vector<uint32_t> phi_id) {
29 message_.set_from_block(from_block);
30 message_.set_continue_condition_value(continue_condition_value);
31 for (auto id : phi_id) {
32 message_.add_phi_id(id);
33 }
34 }
35
IsApplicable(opt::IRContext * ir_context,const TransformationContext & transformation_context) const36 bool TransformationAddDeadContinue::IsApplicable(
37 opt::IRContext* ir_context,
38 const TransformationContext& transformation_context) const {
39 // First, we check that a constant with the same value as
40 // |message_.continue_condition_value| is present.
41 const auto bool_id = fuzzerutil::MaybeGetBoolConstant(
42 ir_context, transformation_context, message_.continue_condition_value(),
43 false);
44 if (!bool_id) {
45 // The required constant is not present, so the transformation cannot be
46 // applied.
47 return false;
48 }
49
50 // Check that |message_.from_block| really is a block id.
51 opt::BasicBlock* bb_from =
52 fuzzerutil::MaybeFindBlock(ir_context, message_.from_block());
53 if (bb_from == nullptr) {
54 return false;
55 }
56
57 // Check that |message_.from_block| ends with an unconditional branch.
58 if (bb_from->terminator()->opcode() != spv::Op::OpBranch) {
59 // The block associated with the id does not end with an unconditional
60 // branch.
61 return false;
62 }
63
64 assert(bb_from != nullptr &&
65 "We should have found a block if this line of code is reached.");
66 assert(
67 bb_from->id() == message_.from_block() &&
68 "The id of the block we found should match the source id for the break.");
69
70 // Get the header for the innermost loop containing |message_.from_block|.
71 // Because the structured CFG analysis does not regard a loop header as part
72 // of the loop it heads, we check first whether bb_from is a loop header
73 // before using the structured CFG analysis.
74 auto loop_header =
75 bb_from->IsLoopHeader()
76 ? message_.from_block()
77 : ir_context->GetStructuredCFGAnalysis()->ContainingLoop(
78 message_.from_block());
79 if (!loop_header) {
80 return false;
81 }
82
83 auto continue_block =
84 ir_context->cfg()->block(loop_header)->ContinueBlockId();
85
86 if (!ir_context->IsReachable(*ir_context->cfg()->block(continue_block))) {
87 // If the loop's continue block is unreachable, we conservatively do not
88 // allow adding a dead continue, to avoid the compilations that arise due to
89 // the lack of sensible dominance information for unreachable blocks.
90 return false;
91 }
92
93 if (fuzzerutil::BlockIsInLoopContinueConstruct(
94 ir_context, message_.from_block(), loop_header)) {
95 // We cannot jump to the continue target from the continue construct.
96 return false;
97 }
98
99 if (ir_context->GetStructuredCFGAnalysis()->IsMergeBlock(continue_block)) {
100 // A branch straight to the continue target that is also a merge block might
101 // break the property that a construct header must dominate its merge block
102 // (if the merge block is reachable).
103 return false;
104 }
105
106 // Check whether the data passed to extend OpPhi instructions is appropriate.
107 if (!fuzzerutil::PhiIdsOkForNewEdge(ir_context, bb_from,
108 ir_context->cfg()->block(continue_block),
109 message_.phi_id())) {
110 return false;
111 }
112
113 // Adding the dead break is only valid if SPIR-V rules related to dominance
114 // hold.
115 return fuzzerutil::NewTerminatorPreservesDominationRules(
116 ir_context, message_.from_block(),
117 fuzzerutil::CreateUnreachableEdgeInstruction(
118 ir_context, message_.from_block(), continue_block, bool_id));
119 }
120
Apply(opt::IRContext * ir_context,TransformationContext * transformation_context) const121 void TransformationAddDeadContinue::Apply(
122 opt::IRContext* ir_context,
123 TransformationContext* transformation_context) const {
124 auto bb_from = ir_context->cfg()->block(message_.from_block());
125 auto continue_block =
126 bb_from->IsLoopHeader()
127 ? bb_from->ContinueBlockId()
128 : ir_context->GetStructuredCFGAnalysis()->LoopContinueBlock(
129 message_.from_block());
130 assert(continue_block && "message_.from_block must be in a loop.");
131 fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis(
132 ir_context, bb_from, ir_context->cfg()->block(continue_block),
133 fuzzerutil::MaybeGetBoolConstant(ir_context, *transformation_context,
134 message_.continue_condition_value(),
135 false),
136 message_.phi_id());
137
138 // Invalidate all analyses
139 ir_context->InvalidateAnalysesExceptFor(
140 opt::IRContext::Analysis::kAnalysisNone);
141 }
142
ToMessage() const143 protobufs::Transformation TransformationAddDeadContinue::ToMessage() const {
144 protobufs::Transformation result;
145 *result.mutable_add_dead_continue() = message_;
146 return result;
147 }
148
GetFreshIds() const149 std::unordered_set<uint32_t> TransformationAddDeadContinue::GetFreshIds()
150 const {
151 return std::unordered_set<uint32_t>();
152 }
153
154 } // namespace fuzz
155 } // namespace spvtools
156