Home | History | Annotate | Download | only in opt
      1 // Copyright (c) 2017 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/propagator.h"
     16 
     17 namespace spvtools {
     18 namespace opt {
     19 
     20 void SSAPropagator::AddControlEdge(const Edge& edge) {
     21   BasicBlock* dest_bb = edge.dest;
     22 
     23   // Refuse to add the exit block to the work list.
     24   if (dest_bb == ctx_->cfg()->pseudo_exit_block()) {
     25     return;
     26   }
     27 
     28   // Try to mark the edge executable.  If it was already in the set of
     29   // executable edges, do nothing.
     30   if (!MarkEdgeExecutable(edge)) {
     31     return;
     32   }
     33 
     34   // If the edge had not already been marked executable, add the destination
     35   // basic block to the work list.
     36   blocks_.push(dest_bb);
     37 }
     38 
     39 void SSAPropagator::AddSSAEdges(Instruction* instr) {
     40   // Ignore instructions that produce no result.
     41   if (instr->result_id() == 0) {
     42     return;
     43   }
     44 
     45   get_def_use_mgr()->ForEachUser(
     46       instr->result_id(), [this](Instruction* use_instr) {
     47         // If the basic block for |use_instr| has not been simulated yet, do
     48         // nothing.  The instruction |use_instr| will be simulated next time the
     49         // block is scheduled.
     50         if (!BlockHasBeenSimulated(ctx_->get_instr_block(use_instr))) {
     51           return;
     52         }
     53 
     54         if (ShouldSimulateAgain(use_instr)) {
     55           ssa_edge_uses_.push(use_instr);
     56         }
     57       });
     58 }
     59 
     60 bool SSAPropagator::IsPhiArgExecutable(Instruction* phi, uint32_t i) const {
     61   BasicBlock* phi_bb = ctx_->get_instr_block(phi);
     62 
     63   uint32_t in_label_id = phi->GetSingleWordOperand(i + 1);
     64   Instruction* in_label_instr = get_def_use_mgr()->GetDef(in_label_id);
     65   BasicBlock* in_bb = ctx_->get_instr_block(in_label_instr);
     66 
     67   return IsEdgeExecutable(Edge(in_bb, phi_bb));
     68 }
     69 
     70 bool SSAPropagator::SetStatus(Instruction* inst, PropStatus status) {
     71   bool has_old_status = false;
     72   PropStatus old_status = kVarying;
     73   if (HasStatus(inst)) {
     74     has_old_status = true;
     75     old_status = Status(inst);
     76   }
     77 
     78   assert((!has_old_status || old_status <= status) &&
     79          "Invalid lattice transition");
     80 
     81   bool status_changed = !has_old_status || (old_status != status);
     82   if (status_changed) statuses_[inst] = status;
     83 
     84   return status_changed;
     85 }
     86 
     87 bool SSAPropagator::Simulate(Instruction* instr) {
     88   bool changed = false;
     89 
     90   // Don't bother visiting instructions that should not be simulated again.
     91   if (!ShouldSimulateAgain(instr)) {
     92     return changed;
     93   }
     94 
     95   BasicBlock* dest_bb = nullptr;
     96   PropStatus status = visit_fn_(instr, &dest_bb);
     97   bool status_changed = SetStatus(instr, status);
     98 
     99   if (status == kVarying) {
    100     // The statement produces a varying result, add it to the list of statements
    101     // not to simulate anymore and add its SSA def-use edges for simulation.
    102     DontSimulateAgain(instr);
    103     if (status_changed) {
    104       AddSSAEdges(instr);
    105     }
    106 
    107     // If |instr| is a block terminator, add all the control edges out of its
    108     // block.
    109     if (instr->IsBlockTerminator()) {
    110       BasicBlock* block = ctx_->get_instr_block(instr);
    111       for (const auto& e : bb_succs_.at(block)) {
    112         AddControlEdge(e);
    113       }
    114     }
    115     return false;
    116   } else if (status == kInteresting) {
    117     // Add the SSA edges coming out of this instruction if the propagation
    118     // status has changed.
    119     if (status_changed) {
    120       AddSSAEdges(instr);
    121     }
    122 
    123     // If there are multiple outgoing control flow edges and we know which one
    124     // will be taken, add the destination block to the CFG work list.
    125     if (dest_bb) {
    126       AddControlEdge(Edge(ctx_->get_instr_block(instr), dest_bb));
    127     }
    128     changed = true;
    129   }
    130 
    131   // At this point, we are dealing with instructions that are in status
    132   // kInteresting or kNotInteresting.  To decide whether this instruction should
    133   // be simulated again, we examine its operands.  If at least one operand O is
    134   // defined at an instruction D that should be simulated again, then the output
    135   // of D might affect |instr|, so we should simulate |instr| again.
    136   bool has_operands_to_simulate = false;
    137   if (instr->opcode() == SpvOpPhi) {
    138     // For Phi instructions, an operand causes the Phi to be simulated again if
    139     // the operand comes from an edge that has not yet been traversed or if its
    140     // definition should be simulated again.
    141     for (uint32_t i = 2; i < instr->NumOperands(); i += 2) {
    142       // Phi arguments come in pairs. Index 'i' contains the
    143       // variable id, index 'i + 1' is the originating block id.
    144       assert(i % 2 == 0 && i < instr->NumOperands() - 1 &&
    145              "malformed Phi arguments");
    146 
    147       uint32_t arg_id = instr->GetSingleWordOperand(i);
    148       Instruction* arg_def_instr = get_def_use_mgr()->GetDef(arg_id);
    149       if (!IsPhiArgExecutable(instr, i) || ShouldSimulateAgain(arg_def_instr)) {
    150         has_operands_to_simulate = true;
    151         break;
    152       }
    153     }
    154   } else {
    155     // For regular instructions, check if the defining instruction of each
    156     // operand needs to be simulated again.  If so, then this instruction should
    157     // also be simulated again.
    158     has_operands_to_simulate =
    159         !instr->WhileEachInId([this](const uint32_t* use) {
    160           Instruction* def_instr = get_def_use_mgr()->GetDef(*use);
    161           if (ShouldSimulateAgain(def_instr)) {
    162             return false;
    163           }
    164           return true;
    165         });
    166   }
    167 
    168   if (!has_operands_to_simulate) {
    169     DontSimulateAgain(instr);
    170   }
    171 
    172   return changed;
    173 }
    174 
    175 bool SSAPropagator::Simulate(BasicBlock* block) {
    176   if (block == ctx_->cfg()->pseudo_exit_block()) {
    177     return false;
    178   }
    179 
    180   // Always simulate Phi instructions, even if we have simulated this block
    181   // before. We do this because Phi instructions receive their inputs from
    182   // incoming edges. When those edges are marked executable, the corresponding
    183   // operand can be simulated.
    184   bool changed = false;
    185   block->ForEachPhiInst(
    186       [&changed, this](Instruction* instr) { changed |= Simulate(instr); });
    187 
    188   // If this is the first time this block is being simulated, simulate every
    189   // statement in it.
    190   if (!BlockHasBeenSimulated(block)) {
    191     block->ForEachInst([this, &changed](Instruction* instr) {
    192       if (instr->opcode() != SpvOpPhi) {
    193         changed |= Simulate(instr);
    194       }
    195     });
    196 
    197     MarkBlockSimulated(block);
    198 
    199     // If this block has exactly one successor, mark the edge to its successor
    200     // as executable.
    201     if (bb_succs_.at(block).size() == 1) {
    202       AddControlEdge(bb_succs_.at(block).at(0));
    203     }
    204   }
    205 
    206   return changed;
    207 }
    208 
    209 void SSAPropagator::Initialize(Function* fn) {
    210   // Compute predecessor and successor blocks for every block in |fn|'s CFG.
    211   // TODO(dnovillo): Move this to CFG and always build them. Alternately,
    212   // move it to IRContext and build CFG preds/succs on-demand.
    213   bb_succs_[ctx_->cfg()->pseudo_entry_block()].push_back(
    214       Edge(ctx_->cfg()->pseudo_entry_block(), fn->entry().get()));
    215 
    216   for (auto& block : *fn) {
    217     const auto& const_block = block;
    218     const_block.ForEachSuccessorLabel([this, &block](const uint32_t label_id) {
    219       BasicBlock* succ_bb =
    220           ctx_->get_instr_block(get_def_use_mgr()->GetDef(label_id));
    221       bb_succs_[&block].push_back(Edge(&block, succ_bb));
    222       bb_preds_[succ_bb].push_back(Edge(succ_bb, &block));
    223     });
    224     if (block.IsReturnOrAbort()) {
    225       bb_succs_[&block].push_back(
    226           Edge(&block, ctx_->cfg()->pseudo_exit_block()));
    227       bb_preds_[ctx_->cfg()->pseudo_exit_block()].push_back(
    228           Edge(ctx_->cfg()->pseudo_exit_block(), &block));
    229     }
    230   }
    231 
    232   // Add the edges out of the entry block to seed the propagator.
    233   const auto& entry_succs = bb_succs_[ctx_->cfg()->pseudo_entry_block()];
    234   for (const auto& e : entry_succs) {
    235     AddControlEdge(e);
    236   }
    237 }
    238 
    239 bool SSAPropagator::Run(Function* fn) {
    240   Initialize(fn);
    241 
    242   bool changed = false;
    243   while (!blocks_.empty() || !ssa_edge_uses_.empty()) {
    244     // Simulate all blocks first. Simulating blocks will add SSA edges to
    245     // follow after all the blocks have been simulated.
    246     if (!blocks_.empty()) {
    247       auto block = blocks_.front();
    248       changed |= Simulate(block);
    249       blocks_.pop();
    250       continue;
    251     }
    252 
    253     // Simulate edges from the SSA queue.
    254     if (!ssa_edge_uses_.empty()) {
    255       Instruction* instr = ssa_edge_uses_.front();
    256       changed |= Simulate(instr);
    257       ssa_edge_uses_.pop();
    258     }
    259   }
    260 
    261 #ifndef NDEBUG
    262   // Verify all visited values have settled. No value that has been simulated
    263   // should end on not interesting.
    264   fn->ForEachInst([this](Instruction* inst) {
    265     assert(
    266         (!HasStatus(inst) || Status(inst) != SSAPropagator::kNotInteresting) &&
    267         "Unsettled value");
    268   });
    269 #endif
    270 
    271   return changed;
    272 }
    273 
    274 std::ostream& operator<<(std::ostream& str,
    275                          const SSAPropagator::PropStatus& status) {
    276   switch (status) {
    277     case SSAPropagator::kVarying:
    278       str << "Varying";
    279       break;
    280     case SSAPropagator::kInteresting:
    281       str << "Interesting";
    282       break;
    283     default:
    284       str << "Not interesting";
    285       break;
    286   }
    287   return str;
    288 }
    289 
    290 }  // namespace opt
    291 }  // namespace spvtools
    292