• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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