tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

InstructionReordering.cpp (8524B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "jit/InstructionReordering.h"
      8 
      9 #include "jit/MIRGenerator.h"
     10 #include "jit/MIRGraph.h"
     11 
     12 using namespace js;
     13 using namespace js::jit;
     14 
     15 static void MoveBefore(MBasicBlock* block, MInstruction* at,
     16                       MInstruction* ins) {
     17  if (at == ins) {
     18    return;
     19  }
     20 
     21  // Update instruction numbers.
     22  for (MInstructionIterator iter(block->begin(at)); *iter != ins; iter++) {
     23    MOZ_ASSERT(iter->id() < ins->id());
     24    iter->setId(iter->id() + 1);
     25  }
     26  ins->setId(at->id() - 1);
     27  block->moveBefore(at, ins);
     28 }
     29 
     30 static bool IsLastUse(MDefinition* ins, MDefinition* input,
     31                      MBasicBlock* loopHeader) {
     32  // If we are in a loop, this cannot be the last use of any definitions from
     33  // outside the loop, as those definitions can be used in future iterations.
     34  if (loopHeader && input->block()->id() < loopHeader->id()) {
     35    return false;
     36  }
     37  for (MUseDefIterator iter(input); iter; iter++) {
     38    // Watch for uses defined in blocks which ReorderInstructions hasn't
     39    // processed yet. These nodes have not had their ids set yet.
     40    if (iter.def()->block()->id() > ins->block()->id()) {
     41      return false;
     42    }
     43    if (iter.def()->id() > ins->id()) {
     44      return false;
     45    }
     46  }
     47  return true;
     48 }
     49 
     50 static void MoveConstantsToStart(MBasicBlock* block,
     51                                 MInstruction* insertionPoint) {
     52  // Move constants with a single use in the current block to the start of the
     53  // block. Constants won't be reordered by ReorderInstructions, as they have no
     54  // inputs. Moving them up as high as possible can allow their use to be moved
     55  // up further, though, and has no cost if the constant is emitted at its use.
     56 
     57  MInstructionIterator iter(block->begin(insertionPoint));
     58  while (iter != block->end()) {
     59    MInstruction* ins = *iter;
     60    iter++;
     61 
     62    if (!ins->isConstant() || !ins->hasOneUse() ||
     63        ins->usesBegin()->consumer()->block() != block ||
     64        IsFloatingPointType(ins->type())) {
     65      continue;
     66    }
     67 
     68    MOZ_ASSERT(ins->isMovable());
     69    MOZ_ASSERT(insertionPoint != ins);
     70 
     71    // Note: we don't need to use MoveBefore here because MoveConstantsToStart
     72    // is called right before we renumber all instructions in this block.
     73    block->moveBefore(insertionPoint, ins);
     74  }
     75 }
     76 
     77 bool jit::ReorderInstructions(MIRGenerator* mir, MIRGraph& graph) {
     78  // Renumber all instructions in the graph as we go.
     79  size_t nextId = 0;
     80 
     81  // List of the headers of any loops we are in.
     82  Vector<MBasicBlock*, 4, SystemAllocPolicy> loopHeaders;
     83 
     84  for (ReversePostorderIterator block(graph.rpoBegin());
     85       block != graph.rpoEnd(); block++) {
     86    if (mir->shouldCancel("ReorderInstructions (block loop)")) {
     87      return false;
     88    }
     89 
     90    // Don't reorder instructions within entry blocks, which have special
     91    // requirements.
     92    bool isEntryBlock =
     93        *block == graph.entryBlock() || *block == graph.osrBlock();
     94 
     95    MInstruction* insertionPoint = nullptr;
     96    if (!isEntryBlock) {
     97      // Move constants to the start of the block before renumbering all
     98      // instructions.
     99      insertionPoint = block->safeInsertTop();
    100      MoveConstantsToStart(*block, insertionPoint);
    101    }
    102 
    103    // Renumber all definitions inside the basic blocks.
    104    for (MPhiIterator iter(block->phisBegin()); iter != block->phisEnd();
    105         iter++) {
    106      iter->setId(nextId++);
    107    }
    108 
    109    for (MInstructionIterator iter(block->begin()); iter != block->end();
    110         iter++) {
    111      iter->setId(nextId++);
    112    }
    113 
    114    if (isEntryBlock) {
    115      continue;
    116    }
    117 
    118    if (block->isLoopHeader()) {
    119      if (!loopHeaders.append(*block)) {
    120        return false;
    121      }
    122    }
    123 
    124    MBasicBlock* innerLoop = loopHeaders.empty() ? nullptr : loopHeaders.back();
    125 
    126    MInstructionReverseIterator rtop = ++block->rbegin(insertionPoint);
    127    for (MInstructionIterator iter(block->begin(insertionPoint));
    128         iter != block->end();) {
    129      if (mir->shouldCancel("ReorderInstructions (instruction loop)")) {
    130        return false;
    131      }
    132 
    133      MInstruction* ins = *iter;
    134 
    135      // Filter out some instructions which are never reordered.
    136      if (ins->isEffectful() || !ins->isMovable() || ins->resumePoint() ||
    137          ins == block->lastIns()) {
    138        iter++;
    139        continue;
    140      }
    141 
    142      // Look for inputs where this instruction is the last use of that
    143      // input. If we move this instruction up, the input's lifetime will
    144      // be shortened, modulo resume point uses (which don't need to be
    145      // stored in a register, and can be handled by the register
    146      // allocator by just spilling at some point with no reload).
    147      Vector<MDefinition*, 4, SystemAllocPolicy> lastUsedInputs;
    148      for (size_t i = 0; i < ins->numOperands(); i++) {
    149        MDefinition* input = ins->getOperand(i);
    150        if (!input->isConstant() && IsLastUse(ins, input, innerLoop)) {
    151          if (!lastUsedInputs.append(input)) {
    152            return false;
    153          }
    154        }
    155      }
    156 
    157      // Don't try to move instructions which aren't the last use of any
    158      // of their inputs (we really ought to move these down instead).
    159      if (lastUsedInputs.length() < 2) {
    160        iter++;
    161        continue;
    162      }
    163 
    164      MInstruction* target = ins;
    165      MInstruction* postCallTarget = nullptr;
    166      for (MInstructionReverseIterator riter = ++block->rbegin(ins);
    167           riter != rtop; riter++) {
    168        MInstruction* prev = *riter;
    169        if (prev->isInterruptCheck()) {
    170          break;
    171        }
    172        if (prev->isSetInitializedLength()) {
    173          break;
    174        }
    175 
    176        // The instruction can't be moved before any of its uses.
    177        bool isUse = false;
    178        for (size_t i = 0; i < ins->numOperands(); i++) {
    179          if (ins->getOperand(i) == prev) {
    180            isUse = true;
    181            break;
    182          }
    183        }
    184        if (isUse) {
    185          break;
    186        }
    187 
    188        // The instruction can't be moved before an instruction that
    189        // stores to a location read by the instruction.
    190        if (prev->isEffectful() &&
    191            (ins->getAliasSet().flags() & prev->getAliasSet().flags()) &&
    192            ins->mightAlias(prev) != MDefinition::AliasType::NoAlias) {
    193          break;
    194        }
    195 
    196        // Make sure the instruction will still be the last use of one
    197        // of its inputs when moved up this far.
    198        for (size_t i = 0; i < lastUsedInputs.length();) {
    199          bool found = false;
    200          for (size_t j = 0; j < prev->numOperands(); j++) {
    201            if (prev->getOperand(j) == lastUsedInputs[i]) {
    202              found = true;
    203              break;
    204            }
    205          }
    206          if (found) {
    207            lastUsedInputs[i] = lastUsedInputs.back();
    208            lastUsedInputs.popBack();
    209          } else {
    210            i++;
    211          }
    212        }
    213        if (lastUsedInputs.length() < 2) {
    214          break;
    215        }
    216 
    217        // If we see a captured call result, either move the instruction before
    218        // the corresponding call or don't move it at all.
    219        if (prev->isCallResultCapture()) {
    220          if (!postCallTarget) {
    221            postCallTarget = target;
    222          }
    223        } else if (postCallTarget) {
    224          MOZ_ASSERT(MWasmCallBase::IsWasmCall(prev) ||
    225                     prev->isIonToWasmCall());
    226          postCallTarget = nullptr;
    227        }
    228 
    229        // We can move the instruction before this one.
    230        target = prev;
    231      }
    232 
    233      if (postCallTarget) {
    234        // We would have plonked this instruction between a call and its
    235        // captured return value.  Instead put it after the last corresponding
    236        // return value.
    237        target = postCallTarget;
    238      }
    239 
    240      iter++;
    241      MoveBefore(*block, target, ins);
    242 
    243      // Instruction reordering can move a bailing instruction up past a call
    244      // that throws an exception, causing spurious bailouts. This should rarely
    245      // be an issue in practice, so we only update the bailout kind if we don't
    246      // have anything more specific.
    247      if (ins->bailoutKind() == BailoutKind::TranspiledCacheIR) {
    248        ins->setBailoutKind(BailoutKind::InstructionReordering);
    249      }
    250    }
    251 
    252    if (block->isLoopBackedge()) {
    253      loopHeaders.popBack();
    254    }
    255  }
    256 
    257  return true;
    258 }