1 // Copyright (c) 2018 Google Inc.
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/opt/workaround1209.h"
16 
17 #include <list>
18 #include <memory>
19 #include <stack>
20 #include <utility>
21 
22 namespace spvtools {
23 namespace opt {
24 
Process()25 Pass::Status Workaround1209::Process() {
26   bool modified = false;
27   modified = RemoveOpUnreachableInLoops();
28   return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange);
29 }
30 
RemoveOpUnreachableInLoops()31 bool Workaround1209::RemoveOpUnreachableInLoops() {
32   bool modified = false;
33   for (auto& func : *get_module()) {
34     std::list<BasicBlock*> structured_order;
35     cfg()->ComputeStructuredOrder(&func, &*func.begin(), &structured_order);
36 
37     // Keep track of the loop merges.  The top of the stack will always be the
38     // loop merge for the loop that immediately contains the basic block being
39     // processed.
40     std::stack<uint32_t> loop_merges;
41     for (BasicBlock* bb : structured_order) {
42       if (!loop_merges.empty() && bb->id() == loop_merges.top()) {
43         loop_merges.pop();
44       }
45 
46       if (bb->tail()->opcode() == SpvOpUnreachable) {
47         if (!loop_merges.empty()) {
48           // We found an OpUnreachable inside a loop.
49           // Replace it with an unconditional branch to the loop merge.
50           context()->KillInst(&*bb->tail());
51           std::unique_ptr<Instruction> new_branch(
52               new Instruction(context(), SpvOpBranch, 0, 0,
53                               {{spv_operand_type_t::SPV_OPERAND_TYPE_ID,
54                                 {loop_merges.top()}}}));
55           context()->AnalyzeDefUse(&*new_branch);
56           bb->AddInstruction(std::move(new_branch));
57           modified = true;
58         }
59       } else {
60         if (bb->GetLoopMergeInst()) {
61           loop_merges.push(bb->MergeBlockIdIfAny());
62         }
63       }
64     }
65   }
66   return modified;
67 }
68 }  // namespace opt
69 }  // namespace spvtools
70