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() != SpvOpBranch) {
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 (!fuzzerutil::BlockIsReachableInItsFunction(
87 ir_context, ir_context->cfg()->block(continue_block))) {
88 // If the loop's continue block is unreachable, we conservatively do not
89 // allow adding a dead continue, to avoid the compilations that arise due to
90 // the lack of sensible dominance information for unreachable blocks.
91 return false;
92 }
93
94 if (fuzzerutil::BlockIsInLoopContinueConstruct(
95 ir_context, message_.from_block(), loop_header)) {
96 // We cannot jump to the continue target from the continue construct.
97 return false;
98 }
99
100 if (ir_context->GetStructuredCFGAnalysis()->IsMergeBlock(continue_block)) {
101 // A branch straight to the continue target that is also a merge block might
102 // break the property that a construct header must dominate its merge block
103 // (if the merge block is reachable).
104 return false;
105 }
106
107 // Check whether the data passed to extend OpPhi instructions is appropriate.
108 if (!fuzzerutil::PhiIdsOkForNewEdge(ir_context, bb_from,
109 ir_context->cfg()->block(continue_block),
110 message_.phi_id())) {
111 return false;
112 }
113
114 // Adding the dead break is only valid if SPIR-V rules related to dominance
115 // hold.
116 return fuzzerutil::NewTerminatorPreservesDominationRules(
117 ir_context, message_.from_block(),
118 fuzzerutil::CreateUnreachableEdgeInstruction(
119 ir_context, message_.from_block(), continue_block, bool_id));
120 }
121
Apply(opt::IRContext * ir_context,TransformationContext * transformation_context) const122 void TransformationAddDeadContinue::Apply(
123 opt::IRContext* ir_context,
124 TransformationContext* transformation_context) const {
125 auto bb_from = ir_context->cfg()->block(message_.from_block());
126 auto continue_block =
127 bb_from->IsLoopHeader()
128 ? bb_from->ContinueBlockId()
129 : ir_context->GetStructuredCFGAnalysis()->LoopContinueBlock(
130 message_.from_block());
131 assert(continue_block && "message_.from_block must be in a loop.");
132 fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis(
133 ir_context, bb_from, ir_context->cfg()->block(continue_block),
134 fuzzerutil::MaybeGetBoolConstant(ir_context, *transformation_context,
135 message_.continue_condition_value(),
136 false),
137 message_.phi_id());
138
139 // Invalidate all analyses
140 ir_context->InvalidateAnalysesExceptFor(
141 opt::IRContext::Analysis::kAnalysisNone);
142 }
143
ToMessage() const144 protobufs::Transformation TransformationAddDeadContinue::ToMessage() const {
145 protobufs::Transformation result;
146 *result.mutable_add_dead_continue() = message_;
147 return result;
148 }
149
GetFreshIds() const150 std::unordered_set<uint32_t> TransformationAddDeadContinue::GetFreshIds()
151 const {
152 return std::unordered_set<uint32_t>();
153 }
154
155 } // namespace fuzz
156 } // namespace spvtools
157