• 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() != 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