11cb0ef41Sopenharmony_ci// Copyright 2014 the V8 project authors. All rights reserved.
21cb0ef41Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be
31cb0ef41Sopenharmony_ci// found in the LICENSE file.
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_ci#include "src/compiler/backend/move-optimizer.h"
61cb0ef41Sopenharmony_ci
71cb0ef41Sopenharmony_ci#include "src/codegen/register-configuration.h"
81cb0ef41Sopenharmony_ci
91cb0ef41Sopenharmony_cinamespace v8 {
101cb0ef41Sopenharmony_cinamespace internal {
111cb0ef41Sopenharmony_cinamespace compiler {
121cb0ef41Sopenharmony_ci
131cb0ef41Sopenharmony_cinamespace {
141cb0ef41Sopenharmony_ci
151cb0ef41Sopenharmony_cistruct MoveKey {
161cb0ef41Sopenharmony_ci  InstructionOperand source;
171cb0ef41Sopenharmony_ci  InstructionOperand destination;
181cb0ef41Sopenharmony_ci};
191cb0ef41Sopenharmony_ci
201cb0ef41Sopenharmony_cistruct MoveKeyCompare {
211cb0ef41Sopenharmony_ci  bool operator()(const MoveKey& a, const MoveKey& b) const {
221cb0ef41Sopenharmony_ci    if (a.source.EqualsCanonicalized(b.source)) {
231cb0ef41Sopenharmony_ci      return a.destination.CompareCanonicalized(b.destination);
241cb0ef41Sopenharmony_ci    }
251cb0ef41Sopenharmony_ci    return a.source.CompareCanonicalized(b.source);
261cb0ef41Sopenharmony_ci  }
271cb0ef41Sopenharmony_ci};
281cb0ef41Sopenharmony_ci
291cb0ef41Sopenharmony_ciusing MoveMap = ZoneMap<MoveKey, unsigned, MoveKeyCompare>;
301cb0ef41Sopenharmony_ci
311cb0ef41Sopenharmony_ciclass OperandSet {
321cb0ef41Sopenharmony_ci public:
331cb0ef41Sopenharmony_ci  explicit OperandSet(ZoneVector<InstructionOperand>* buffer)
341cb0ef41Sopenharmony_ci      : set_(buffer), fp_reps_(0) {
351cb0ef41Sopenharmony_ci    buffer->clear();
361cb0ef41Sopenharmony_ci  }
371cb0ef41Sopenharmony_ci
381cb0ef41Sopenharmony_ci  void InsertOp(const InstructionOperand& op) {
391cb0ef41Sopenharmony_ci    set_->push_back(op);
401cb0ef41Sopenharmony_ci
411cb0ef41Sopenharmony_ci    if (kFPAliasing == AliasingKind::kCombine && op.IsFPRegister())
421cb0ef41Sopenharmony_ci      fp_reps_ |= RepresentationBit(LocationOperand::cast(op).representation());
431cb0ef41Sopenharmony_ci  }
441cb0ef41Sopenharmony_ci
451cb0ef41Sopenharmony_ci  bool Contains(const InstructionOperand& op) const {
461cb0ef41Sopenharmony_ci    for (const InstructionOperand& elem : *set_) {
471cb0ef41Sopenharmony_ci      if (elem.EqualsCanonicalized(op)) return true;
481cb0ef41Sopenharmony_ci    }
491cb0ef41Sopenharmony_ci    return false;
501cb0ef41Sopenharmony_ci  }
511cb0ef41Sopenharmony_ci
521cb0ef41Sopenharmony_ci  bool ContainsOpOrAlias(const InstructionOperand& op) const {
531cb0ef41Sopenharmony_ci    if (Contains(op)) return true;
541cb0ef41Sopenharmony_ci
551cb0ef41Sopenharmony_ci    if (kFPAliasing == AliasingKind::kCombine && op.IsFPRegister()) {
561cb0ef41Sopenharmony_ci      // Platforms where FP registers have complex aliasing need extra checks.
571cb0ef41Sopenharmony_ci      const LocationOperand& loc = LocationOperand::cast(op);
581cb0ef41Sopenharmony_ci      MachineRepresentation rep = loc.representation();
591cb0ef41Sopenharmony_ci      // If haven't encountered mixed rep FP registers, skip the extra checks.
601cb0ef41Sopenharmony_ci      if (!HasMixedFPReps(fp_reps_ | RepresentationBit(rep))) return false;
611cb0ef41Sopenharmony_ci
621cb0ef41Sopenharmony_ci      // Check register against aliasing registers of other FP representations.
631cb0ef41Sopenharmony_ci      MachineRepresentation other_rep1, other_rep2;
641cb0ef41Sopenharmony_ci      switch (rep) {
651cb0ef41Sopenharmony_ci        case MachineRepresentation::kFloat32:
661cb0ef41Sopenharmony_ci          other_rep1 = MachineRepresentation::kFloat64;
671cb0ef41Sopenharmony_ci          other_rep2 = MachineRepresentation::kSimd128;
681cb0ef41Sopenharmony_ci          break;
691cb0ef41Sopenharmony_ci        case MachineRepresentation::kFloat64:
701cb0ef41Sopenharmony_ci          other_rep1 = MachineRepresentation::kFloat32;
711cb0ef41Sopenharmony_ci          other_rep2 = MachineRepresentation::kSimd128;
721cb0ef41Sopenharmony_ci          break;
731cb0ef41Sopenharmony_ci        case MachineRepresentation::kSimd128:
741cb0ef41Sopenharmony_ci          other_rep1 = MachineRepresentation::kFloat32;
751cb0ef41Sopenharmony_ci          other_rep2 = MachineRepresentation::kFloat64;
761cb0ef41Sopenharmony_ci          break;
771cb0ef41Sopenharmony_ci        default:
781cb0ef41Sopenharmony_ci          UNREACHABLE();
791cb0ef41Sopenharmony_ci      }
801cb0ef41Sopenharmony_ci      const RegisterConfiguration* config = RegisterConfiguration::Default();
811cb0ef41Sopenharmony_ci      int base = -1;
821cb0ef41Sopenharmony_ci      int aliases =
831cb0ef41Sopenharmony_ci          config->GetAliases(rep, loc.register_code(), other_rep1, &base);
841cb0ef41Sopenharmony_ci      DCHECK(aliases > 0 || (aliases == 0 && base == -1));
851cb0ef41Sopenharmony_ci      while (aliases--) {
861cb0ef41Sopenharmony_ci        if (Contains(AllocatedOperand(LocationOperand::REGISTER, other_rep1,
871cb0ef41Sopenharmony_ci                                      base + aliases))) {
881cb0ef41Sopenharmony_ci          return true;
891cb0ef41Sopenharmony_ci        }
901cb0ef41Sopenharmony_ci      }
911cb0ef41Sopenharmony_ci      aliases = config->GetAliases(rep, loc.register_code(), other_rep2, &base);
921cb0ef41Sopenharmony_ci      DCHECK(aliases > 0 || (aliases == 0 && base == -1));
931cb0ef41Sopenharmony_ci      while (aliases--) {
941cb0ef41Sopenharmony_ci        if (Contains(AllocatedOperand(LocationOperand::REGISTER, other_rep2,
951cb0ef41Sopenharmony_ci                                      base + aliases))) {
961cb0ef41Sopenharmony_ci          return true;
971cb0ef41Sopenharmony_ci        }
981cb0ef41Sopenharmony_ci      }
991cb0ef41Sopenharmony_ci    }
1001cb0ef41Sopenharmony_ci    return false;
1011cb0ef41Sopenharmony_ci  }
1021cb0ef41Sopenharmony_ci
1031cb0ef41Sopenharmony_ci private:
1041cb0ef41Sopenharmony_ci  static bool HasMixedFPReps(int reps) {
1051cb0ef41Sopenharmony_ci    return reps && !base::bits::IsPowerOfTwo(reps);
1061cb0ef41Sopenharmony_ci  }
1071cb0ef41Sopenharmony_ci
1081cb0ef41Sopenharmony_ci  ZoneVector<InstructionOperand>* set_;
1091cb0ef41Sopenharmony_ci  int fp_reps_;
1101cb0ef41Sopenharmony_ci};
1111cb0ef41Sopenharmony_ci
1121cb0ef41Sopenharmony_ciint FindFirstNonEmptySlot(const Instruction* instr) {
1131cb0ef41Sopenharmony_ci  int i = Instruction::FIRST_GAP_POSITION;
1141cb0ef41Sopenharmony_ci  for (; i <= Instruction::LAST_GAP_POSITION; i++) {
1151cb0ef41Sopenharmony_ci    ParallelMove* moves = instr->parallel_moves()[i];
1161cb0ef41Sopenharmony_ci    if (moves == nullptr) continue;
1171cb0ef41Sopenharmony_ci    for (MoveOperands* move : *moves) {
1181cb0ef41Sopenharmony_ci      if (!move->IsRedundant()) return i;
1191cb0ef41Sopenharmony_ci      move->Eliminate();
1201cb0ef41Sopenharmony_ci    }
1211cb0ef41Sopenharmony_ci    moves->clear();  // Clear this redundant move.
1221cb0ef41Sopenharmony_ci  }
1231cb0ef41Sopenharmony_ci  return i;
1241cb0ef41Sopenharmony_ci}
1251cb0ef41Sopenharmony_ci
1261cb0ef41Sopenharmony_ci}  // namespace
1271cb0ef41Sopenharmony_ci
1281cb0ef41Sopenharmony_ciMoveOptimizer::MoveOptimizer(Zone* local_zone, InstructionSequence* code)
1291cb0ef41Sopenharmony_ci    : local_zone_(local_zone),
1301cb0ef41Sopenharmony_ci      code_(code),
1311cb0ef41Sopenharmony_ci      local_vector_(local_zone),
1321cb0ef41Sopenharmony_ci      operand_buffer1(local_zone),
1331cb0ef41Sopenharmony_ci      operand_buffer2(local_zone) {}
1341cb0ef41Sopenharmony_ci
1351cb0ef41Sopenharmony_civoid MoveOptimizer::Run() {
1361cb0ef41Sopenharmony_ci  for (Instruction* instruction : code()->instructions()) {
1371cb0ef41Sopenharmony_ci    CompressGaps(instruction);
1381cb0ef41Sopenharmony_ci  }
1391cb0ef41Sopenharmony_ci  for (InstructionBlock* block : code()->instruction_blocks()) {
1401cb0ef41Sopenharmony_ci    CompressBlock(block);
1411cb0ef41Sopenharmony_ci  }
1421cb0ef41Sopenharmony_ci  for (InstructionBlock* block : code()->instruction_blocks()) {
1431cb0ef41Sopenharmony_ci    if (block->PredecessorCount() <= 1) continue;
1441cb0ef41Sopenharmony_ci    if (!block->IsDeferred()) {
1451cb0ef41Sopenharmony_ci      bool has_only_deferred = true;
1461cb0ef41Sopenharmony_ci      for (RpoNumber& pred_id : block->predecessors()) {
1471cb0ef41Sopenharmony_ci        if (!code()->InstructionBlockAt(pred_id)->IsDeferred()) {
1481cb0ef41Sopenharmony_ci          has_only_deferred = false;
1491cb0ef41Sopenharmony_ci          break;
1501cb0ef41Sopenharmony_ci        }
1511cb0ef41Sopenharmony_ci      }
1521cb0ef41Sopenharmony_ci      // This would pull down common moves. If the moves occur in deferred
1531cb0ef41Sopenharmony_ci      // blocks, and the closest common successor is not deferred, we lose the
1541cb0ef41Sopenharmony_ci      // optimization of just spilling/filling in deferred blocks, when the
1551cb0ef41Sopenharmony_ci      // current block is not deferred.
1561cb0ef41Sopenharmony_ci      if (has_only_deferred) continue;
1571cb0ef41Sopenharmony_ci    }
1581cb0ef41Sopenharmony_ci    OptimizeMerge(block);
1591cb0ef41Sopenharmony_ci  }
1601cb0ef41Sopenharmony_ci  for (Instruction* gap : code()->instructions()) {
1611cb0ef41Sopenharmony_ci    FinalizeMoves(gap);
1621cb0ef41Sopenharmony_ci  }
1631cb0ef41Sopenharmony_ci}
1641cb0ef41Sopenharmony_ci
1651cb0ef41Sopenharmony_civoid MoveOptimizer::RemoveClobberedDestinations(Instruction* instruction) {
1661cb0ef41Sopenharmony_ci  if (instruction->IsCall()) return;
1671cb0ef41Sopenharmony_ci  ParallelMove* moves = instruction->parallel_moves()[0];
1681cb0ef41Sopenharmony_ci  if (moves == nullptr) return;
1691cb0ef41Sopenharmony_ci
1701cb0ef41Sopenharmony_ci  DCHECK(instruction->parallel_moves()[1] == nullptr ||
1711cb0ef41Sopenharmony_ci         instruction->parallel_moves()[1]->empty());
1721cb0ef41Sopenharmony_ci
1731cb0ef41Sopenharmony_ci  OperandSet outputs(&operand_buffer1);
1741cb0ef41Sopenharmony_ci  OperandSet inputs(&operand_buffer2);
1751cb0ef41Sopenharmony_ci
1761cb0ef41Sopenharmony_ci  // Outputs and temps are treated together as potentially clobbering a
1771cb0ef41Sopenharmony_ci  // destination operand.
1781cb0ef41Sopenharmony_ci  for (size_t i = 0; i < instruction->OutputCount(); ++i) {
1791cb0ef41Sopenharmony_ci    outputs.InsertOp(*instruction->OutputAt(i));
1801cb0ef41Sopenharmony_ci  }
1811cb0ef41Sopenharmony_ci  for (size_t i = 0; i < instruction->TempCount(); ++i) {
1821cb0ef41Sopenharmony_ci    outputs.InsertOp(*instruction->TempAt(i));
1831cb0ef41Sopenharmony_ci  }
1841cb0ef41Sopenharmony_ci
1851cb0ef41Sopenharmony_ci  // Input operands block elisions.
1861cb0ef41Sopenharmony_ci  for (size_t i = 0; i < instruction->InputCount(); ++i) {
1871cb0ef41Sopenharmony_ci    inputs.InsertOp(*instruction->InputAt(i));
1881cb0ef41Sopenharmony_ci  }
1891cb0ef41Sopenharmony_ci
1901cb0ef41Sopenharmony_ci  // Elide moves made redundant by the instruction.
1911cb0ef41Sopenharmony_ci  for (MoveOperands* move : *moves) {
1921cb0ef41Sopenharmony_ci    if (outputs.ContainsOpOrAlias(move->destination()) &&
1931cb0ef41Sopenharmony_ci        !inputs.ContainsOpOrAlias(move->destination())) {
1941cb0ef41Sopenharmony_ci      move->Eliminate();
1951cb0ef41Sopenharmony_ci    }
1961cb0ef41Sopenharmony_ci  }
1971cb0ef41Sopenharmony_ci
1981cb0ef41Sopenharmony_ci  // The ret instruction makes any assignment before it unnecessary, except for
1991cb0ef41Sopenharmony_ci  // the one for its input.
2001cb0ef41Sopenharmony_ci  if (instruction->IsRet() || instruction->IsTailCall()) {
2011cb0ef41Sopenharmony_ci    for (MoveOperands* move : *moves) {
2021cb0ef41Sopenharmony_ci      if (!inputs.ContainsOpOrAlias(move->destination())) {
2031cb0ef41Sopenharmony_ci        move->Eliminate();
2041cb0ef41Sopenharmony_ci      }
2051cb0ef41Sopenharmony_ci    }
2061cb0ef41Sopenharmony_ci  }
2071cb0ef41Sopenharmony_ci}
2081cb0ef41Sopenharmony_ci
2091cb0ef41Sopenharmony_civoid MoveOptimizer::MigrateMoves(Instruction* to, Instruction* from) {
2101cb0ef41Sopenharmony_ci  if (from->IsCall()) return;
2111cb0ef41Sopenharmony_ci
2121cb0ef41Sopenharmony_ci  ParallelMove* from_moves = from->parallel_moves()[0];
2131cb0ef41Sopenharmony_ci  if (from_moves == nullptr || from_moves->empty()) return;
2141cb0ef41Sopenharmony_ci
2151cb0ef41Sopenharmony_ci  OperandSet dst_cant_be(&operand_buffer1);
2161cb0ef41Sopenharmony_ci  OperandSet src_cant_be(&operand_buffer2);
2171cb0ef41Sopenharmony_ci
2181cb0ef41Sopenharmony_ci  // If an operand is an input to the instruction, we cannot move assignments
2191cb0ef41Sopenharmony_ci  // where it appears on the LHS.
2201cb0ef41Sopenharmony_ci  for (size_t i = 0; i < from->InputCount(); ++i) {
2211cb0ef41Sopenharmony_ci    dst_cant_be.InsertOp(*from->InputAt(i));
2221cb0ef41Sopenharmony_ci  }
2231cb0ef41Sopenharmony_ci  // If an operand is output to the instruction, we cannot move assignments
2241cb0ef41Sopenharmony_ci  // where it appears on the RHS, because we would lose its value before the
2251cb0ef41Sopenharmony_ci  // instruction.
2261cb0ef41Sopenharmony_ci  // Same for temp operands.
2271cb0ef41Sopenharmony_ci  // The output can't appear on the LHS because we performed
2281cb0ef41Sopenharmony_ci  // RemoveClobberedDestinations for the "from" instruction.
2291cb0ef41Sopenharmony_ci  for (size_t i = 0; i < from->OutputCount(); ++i) {
2301cb0ef41Sopenharmony_ci    src_cant_be.InsertOp(*from->OutputAt(i));
2311cb0ef41Sopenharmony_ci  }
2321cb0ef41Sopenharmony_ci  for (size_t i = 0; i < from->TempCount(); ++i) {
2331cb0ef41Sopenharmony_ci    src_cant_be.InsertOp(*from->TempAt(i));
2341cb0ef41Sopenharmony_ci  }
2351cb0ef41Sopenharmony_ci  for (MoveOperands* move : *from_moves) {
2361cb0ef41Sopenharmony_ci    if (move->IsRedundant()) continue;
2371cb0ef41Sopenharmony_ci    // Assume dest has a value "V". If we have a "dest = y" move, then we can't
2381cb0ef41Sopenharmony_ci    // move "z = dest", because z would become y rather than "V".
2391cb0ef41Sopenharmony_ci    // We assume CompressMoves has happened before this, which means we don't
2401cb0ef41Sopenharmony_ci    // have more than one assignment to dest.
2411cb0ef41Sopenharmony_ci    src_cant_be.InsertOp(move->destination());
2421cb0ef41Sopenharmony_ci  }
2431cb0ef41Sopenharmony_ci
2441cb0ef41Sopenharmony_ci  ZoneSet<MoveKey, MoveKeyCompare> move_candidates(local_zone());
2451cb0ef41Sopenharmony_ci  // We start with all the moves that don't have conflicting source or
2461cb0ef41Sopenharmony_ci  // destination operands are eligible for being moved down.
2471cb0ef41Sopenharmony_ci  for (MoveOperands* move : *from_moves) {
2481cb0ef41Sopenharmony_ci    if (move->IsRedundant()) continue;
2491cb0ef41Sopenharmony_ci    if (!dst_cant_be.ContainsOpOrAlias(move->destination())) {
2501cb0ef41Sopenharmony_ci      MoveKey key = {move->source(), move->destination()};
2511cb0ef41Sopenharmony_ci      move_candidates.insert(key);
2521cb0ef41Sopenharmony_ci    }
2531cb0ef41Sopenharmony_ci  }
2541cb0ef41Sopenharmony_ci  if (move_candidates.empty()) return;
2551cb0ef41Sopenharmony_ci
2561cb0ef41Sopenharmony_ci  // Stabilize the candidate set.
2571cb0ef41Sopenharmony_ci  bool changed = false;
2581cb0ef41Sopenharmony_ci  do {
2591cb0ef41Sopenharmony_ci    changed = false;
2601cb0ef41Sopenharmony_ci    for (auto iter = move_candidates.begin(); iter != move_candidates.end();) {
2611cb0ef41Sopenharmony_ci      auto current = iter;
2621cb0ef41Sopenharmony_ci      ++iter;
2631cb0ef41Sopenharmony_ci      InstructionOperand src = current->source;
2641cb0ef41Sopenharmony_ci      if (src_cant_be.ContainsOpOrAlias(src)) {
2651cb0ef41Sopenharmony_ci        src_cant_be.InsertOp(current->destination);
2661cb0ef41Sopenharmony_ci        move_candidates.erase(current);
2671cb0ef41Sopenharmony_ci        changed = true;
2681cb0ef41Sopenharmony_ci      }
2691cb0ef41Sopenharmony_ci    }
2701cb0ef41Sopenharmony_ci  } while (changed);
2711cb0ef41Sopenharmony_ci
2721cb0ef41Sopenharmony_ci  ParallelMove to_move(local_zone());
2731cb0ef41Sopenharmony_ci  for (MoveOperands* move : *from_moves) {
2741cb0ef41Sopenharmony_ci    if (move->IsRedundant()) continue;
2751cb0ef41Sopenharmony_ci    MoveKey key = {move->source(), move->destination()};
2761cb0ef41Sopenharmony_ci    if (move_candidates.find(key) != move_candidates.end()) {
2771cb0ef41Sopenharmony_ci      to_move.AddMove(move->source(), move->destination(), code_zone());
2781cb0ef41Sopenharmony_ci      move->Eliminate();
2791cb0ef41Sopenharmony_ci    }
2801cb0ef41Sopenharmony_ci  }
2811cb0ef41Sopenharmony_ci  if (to_move.empty()) return;
2821cb0ef41Sopenharmony_ci
2831cb0ef41Sopenharmony_ci  ParallelMove* dest =
2841cb0ef41Sopenharmony_ci      to->GetOrCreateParallelMove(Instruction::GapPosition::START, code_zone());
2851cb0ef41Sopenharmony_ci
2861cb0ef41Sopenharmony_ci  CompressMoves(&to_move, dest);
2871cb0ef41Sopenharmony_ci  DCHECK(dest->empty());
2881cb0ef41Sopenharmony_ci  for (MoveOperands* m : to_move) {
2891cb0ef41Sopenharmony_ci    dest->push_back(m);
2901cb0ef41Sopenharmony_ci  }
2911cb0ef41Sopenharmony_ci}
2921cb0ef41Sopenharmony_ci
2931cb0ef41Sopenharmony_civoid MoveOptimizer::CompressMoves(ParallelMove* left, MoveOpVector* right) {
2941cb0ef41Sopenharmony_ci  if (right == nullptr) return;
2951cb0ef41Sopenharmony_ci
2961cb0ef41Sopenharmony_ci  MoveOpVector& eliminated = local_vector();
2971cb0ef41Sopenharmony_ci  DCHECK(eliminated.empty());
2981cb0ef41Sopenharmony_ci
2991cb0ef41Sopenharmony_ci  if (!left->empty()) {
3001cb0ef41Sopenharmony_ci    // Modify the right moves in place and collect moves that will be killed by
3011cb0ef41Sopenharmony_ci    // merging the two gaps.
3021cb0ef41Sopenharmony_ci    for (MoveOperands* move : *right) {
3031cb0ef41Sopenharmony_ci      if (move->IsRedundant()) continue;
3041cb0ef41Sopenharmony_ci      left->PrepareInsertAfter(move, &eliminated);
3051cb0ef41Sopenharmony_ci    }
3061cb0ef41Sopenharmony_ci    // Eliminate dead moves.
3071cb0ef41Sopenharmony_ci    for (MoveOperands* to_eliminate : eliminated) {
3081cb0ef41Sopenharmony_ci      to_eliminate->Eliminate();
3091cb0ef41Sopenharmony_ci    }
3101cb0ef41Sopenharmony_ci    eliminated.clear();
3111cb0ef41Sopenharmony_ci  }
3121cb0ef41Sopenharmony_ci  // Add all possibly modified moves from right side.
3131cb0ef41Sopenharmony_ci  for (MoveOperands* move : *right) {
3141cb0ef41Sopenharmony_ci    if (move->IsRedundant()) continue;
3151cb0ef41Sopenharmony_ci    left->push_back(move);
3161cb0ef41Sopenharmony_ci  }
3171cb0ef41Sopenharmony_ci  // Nuke right.
3181cb0ef41Sopenharmony_ci  right->clear();
3191cb0ef41Sopenharmony_ci  DCHECK(eliminated.empty());
3201cb0ef41Sopenharmony_ci}
3211cb0ef41Sopenharmony_ci
3221cb0ef41Sopenharmony_civoid MoveOptimizer::CompressGaps(Instruction* instruction) {
3231cb0ef41Sopenharmony_ci  int i = FindFirstNonEmptySlot(instruction);
3241cb0ef41Sopenharmony_ci  bool has_moves = i <= Instruction::LAST_GAP_POSITION;
3251cb0ef41Sopenharmony_ci  USE(has_moves);
3261cb0ef41Sopenharmony_ci
3271cb0ef41Sopenharmony_ci  if (i == Instruction::LAST_GAP_POSITION) {
3281cb0ef41Sopenharmony_ci    std::swap(instruction->parallel_moves()[Instruction::FIRST_GAP_POSITION],
3291cb0ef41Sopenharmony_ci              instruction->parallel_moves()[Instruction::LAST_GAP_POSITION]);
3301cb0ef41Sopenharmony_ci  } else if (i == Instruction::FIRST_GAP_POSITION) {
3311cb0ef41Sopenharmony_ci    CompressMoves(
3321cb0ef41Sopenharmony_ci        instruction->parallel_moves()[Instruction::FIRST_GAP_POSITION],
3331cb0ef41Sopenharmony_ci        instruction->parallel_moves()[Instruction::LAST_GAP_POSITION]);
3341cb0ef41Sopenharmony_ci  }
3351cb0ef41Sopenharmony_ci  // We either have no moves, or, after swapping or compressing, we have
3361cb0ef41Sopenharmony_ci  // all the moves in the first gap position, and none in the second/end gap
3371cb0ef41Sopenharmony_ci  // position.
3381cb0ef41Sopenharmony_ci  ParallelMove* first =
3391cb0ef41Sopenharmony_ci      instruction->parallel_moves()[Instruction::FIRST_GAP_POSITION];
3401cb0ef41Sopenharmony_ci  ParallelMove* last =
3411cb0ef41Sopenharmony_ci      instruction->parallel_moves()[Instruction::LAST_GAP_POSITION];
3421cb0ef41Sopenharmony_ci  USE(first);
3431cb0ef41Sopenharmony_ci  USE(last);
3441cb0ef41Sopenharmony_ci
3451cb0ef41Sopenharmony_ci  DCHECK(!has_moves ||
3461cb0ef41Sopenharmony_ci         (first != nullptr && (last == nullptr || last->empty())));
3471cb0ef41Sopenharmony_ci}
3481cb0ef41Sopenharmony_ci
3491cb0ef41Sopenharmony_civoid MoveOptimizer::CompressBlock(InstructionBlock* block) {
3501cb0ef41Sopenharmony_ci  int first_instr_index = block->first_instruction_index();
3511cb0ef41Sopenharmony_ci  int last_instr_index = block->last_instruction_index();
3521cb0ef41Sopenharmony_ci
3531cb0ef41Sopenharmony_ci  // Start by removing gap assignments where the output of the subsequent
3541cb0ef41Sopenharmony_ci  // instruction appears on LHS, as long as they are not needed by its input.
3551cb0ef41Sopenharmony_ci  Instruction* prev_instr = code()->instructions()[first_instr_index];
3561cb0ef41Sopenharmony_ci  RemoveClobberedDestinations(prev_instr);
3571cb0ef41Sopenharmony_ci
3581cb0ef41Sopenharmony_ci  for (int index = first_instr_index + 1; index <= last_instr_index; ++index) {
3591cb0ef41Sopenharmony_ci    Instruction* instr = code()->instructions()[index];
3601cb0ef41Sopenharmony_ci    // Migrate to the gap of prev_instr eligible moves from instr.
3611cb0ef41Sopenharmony_ci    MigrateMoves(instr, prev_instr);
3621cb0ef41Sopenharmony_ci    // Remove gap assignments clobbered by instr's output.
3631cb0ef41Sopenharmony_ci    RemoveClobberedDestinations(instr);
3641cb0ef41Sopenharmony_ci    prev_instr = instr;
3651cb0ef41Sopenharmony_ci  }
3661cb0ef41Sopenharmony_ci}
3671cb0ef41Sopenharmony_ci
3681cb0ef41Sopenharmony_ciconst Instruction* MoveOptimizer::LastInstruction(
3691cb0ef41Sopenharmony_ci    const InstructionBlock* block) const {
3701cb0ef41Sopenharmony_ci  return code()->instructions()[block->last_instruction_index()];
3711cb0ef41Sopenharmony_ci}
3721cb0ef41Sopenharmony_ci
3731cb0ef41Sopenharmony_civoid MoveOptimizer::OptimizeMerge(InstructionBlock* block) {
3741cb0ef41Sopenharmony_ci  DCHECK_LT(1, block->PredecessorCount());
3751cb0ef41Sopenharmony_ci  // Ensure that the last instruction in all incoming blocks don't contain
3761cb0ef41Sopenharmony_ci  // things that would prevent moving gap moves across them.
3771cb0ef41Sopenharmony_ci  for (RpoNumber& pred_index : block->predecessors()) {
3781cb0ef41Sopenharmony_ci    const InstructionBlock* pred = code()->InstructionBlockAt(pred_index);
3791cb0ef41Sopenharmony_ci
3801cb0ef41Sopenharmony_ci    // If the predecessor has more than one successor, we shouldn't attempt to
3811cb0ef41Sopenharmony_ci    // move down to this block (one of the successors) any of the gap moves,
3821cb0ef41Sopenharmony_ci    // because their effect may be necessary to the other successors.
3831cb0ef41Sopenharmony_ci    if (pred->SuccessorCount() > 1) return;
3841cb0ef41Sopenharmony_ci
3851cb0ef41Sopenharmony_ci    const Instruction* last_instr =
3861cb0ef41Sopenharmony_ci        code()->instructions()[pred->last_instruction_index()];
3871cb0ef41Sopenharmony_ci    if (last_instr->IsCall()) return;
3881cb0ef41Sopenharmony_ci    if (last_instr->TempCount() != 0) return;
3891cb0ef41Sopenharmony_ci    if (last_instr->OutputCount() != 0) return;
3901cb0ef41Sopenharmony_ci    for (size_t i = 0; i < last_instr->InputCount(); ++i) {
3911cb0ef41Sopenharmony_ci      const InstructionOperand* op = last_instr->InputAt(i);
3921cb0ef41Sopenharmony_ci      if (!op->IsConstant() && !op->IsImmediate()) return;
3931cb0ef41Sopenharmony_ci    }
3941cb0ef41Sopenharmony_ci  }
3951cb0ef41Sopenharmony_ci  // TODO(dcarney): pass a ZoneStats down for this?
3961cb0ef41Sopenharmony_ci  MoveMap move_map(local_zone());
3971cb0ef41Sopenharmony_ci  size_t correct_counts = 0;
3981cb0ef41Sopenharmony_ci  // Accumulate set of shared moves.
3991cb0ef41Sopenharmony_ci  for (RpoNumber& pred_index : block->predecessors()) {
4001cb0ef41Sopenharmony_ci    const InstructionBlock* pred = code()->InstructionBlockAt(pred_index);
4011cb0ef41Sopenharmony_ci    const Instruction* instr = LastInstruction(pred);
4021cb0ef41Sopenharmony_ci    if (instr->parallel_moves()[0] == nullptr ||
4031cb0ef41Sopenharmony_ci        instr->parallel_moves()[0]->empty()) {
4041cb0ef41Sopenharmony_ci      return;
4051cb0ef41Sopenharmony_ci    }
4061cb0ef41Sopenharmony_ci    for (const MoveOperands* move : *instr->parallel_moves()[0]) {
4071cb0ef41Sopenharmony_ci      if (move->IsRedundant()) continue;
4081cb0ef41Sopenharmony_ci      InstructionOperand src = move->source();
4091cb0ef41Sopenharmony_ci      InstructionOperand dst = move->destination();
4101cb0ef41Sopenharmony_ci      MoveKey key = {src, dst};
4111cb0ef41Sopenharmony_ci      auto res = move_map.insert(std::make_pair(key, 1));
4121cb0ef41Sopenharmony_ci      if (!res.second) {
4131cb0ef41Sopenharmony_ci        res.first->second++;
4141cb0ef41Sopenharmony_ci        if (res.first->second == block->PredecessorCount()) {
4151cb0ef41Sopenharmony_ci          correct_counts++;
4161cb0ef41Sopenharmony_ci        }
4171cb0ef41Sopenharmony_ci      }
4181cb0ef41Sopenharmony_ci    }
4191cb0ef41Sopenharmony_ci  }
4201cb0ef41Sopenharmony_ci  if (move_map.empty() || correct_counts == 0) return;
4211cb0ef41Sopenharmony_ci
4221cb0ef41Sopenharmony_ci  // Find insertion point.
4231cb0ef41Sopenharmony_ci  Instruction* instr = code()->instructions()[block->first_instruction_index()];
4241cb0ef41Sopenharmony_ci
4251cb0ef41Sopenharmony_ci  if (correct_counts != move_map.size()) {
4261cb0ef41Sopenharmony_ci    // Moves that are unique to each predecessor won't be pushed to the common
4271cb0ef41Sopenharmony_ci    // successor.
4281cb0ef41Sopenharmony_ci    OperandSet conflicting_srcs(&operand_buffer1);
4291cb0ef41Sopenharmony_ci    for (auto iter = move_map.begin(), end = move_map.end(); iter != end;) {
4301cb0ef41Sopenharmony_ci      auto current = iter;
4311cb0ef41Sopenharmony_ci      ++iter;
4321cb0ef41Sopenharmony_ci      if (current->second != block->PredecessorCount()) {
4331cb0ef41Sopenharmony_ci        InstructionOperand dest = current->first.destination;
4341cb0ef41Sopenharmony_ci        // Not all the moves in all the gaps are the same. Maybe some are. If
4351cb0ef41Sopenharmony_ci        // there are such moves, we could move them, but the destination of the
4361cb0ef41Sopenharmony_ci        // moves staying behind can't appear as a source of a common move,
4371cb0ef41Sopenharmony_ci        // because the move staying behind will clobber this destination.
4381cb0ef41Sopenharmony_ci        conflicting_srcs.InsertOp(dest);
4391cb0ef41Sopenharmony_ci        move_map.erase(current);
4401cb0ef41Sopenharmony_ci      }
4411cb0ef41Sopenharmony_ci    }
4421cb0ef41Sopenharmony_ci
4431cb0ef41Sopenharmony_ci    bool changed = false;
4441cb0ef41Sopenharmony_ci    do {
4451cb0ef41Sopenharmony_ci      // If a common move can't be pushed to the common successor, then its
4461cb0ef41Sopenharmony_ci      // destination also can't appear as source to any move being pushed.
4471cb0ef41Sopenharmony_ci      changed = false;
4481cb0ef41Sopenharmony_ci      for (auto iter = move_map.begin(), end = move_map.end(); iter != end;) {
4491cb0ef41Sopenharmony_ci        auto current = iter;
4501cb0ef41Sopenharmony_ci        ++iter;
4511cb0ef41Sopenharmony_ci        DCHECK_EQ(block->PredecessorCount(), current->second);
4521cb0ef41Sopenharmony_ci        if (conflicting_srcs.ContainsOpOrAlias(current->first.source)) {
4531cb0ef41Sopenharmony_ci          conflicting_srcs.InsertOp(current->first.destination);
4541cb0ef41Sopenharmony_ci          move_map.erase(current);
4551cb0ef41Sopenharmony_ci          changed = true;
4561cb0ef41Sopenharmony_ci        }
4571cb0ef41Sopenharmony_ci      }
4581cb0ef41Sopenharmony_ci    } while (changed);
4591cb0ef41Sopenharmony_ci  }
4601cb0ef41Sopenharmony_ci
4611cb0ef41Sopenharmony_ci  if (move_map.empty()) return;
4621cb0ef41Sopenharmony_ci
4631cb0ef41Sopenharmony_ci  DCHECK_NOT_NULL(instr);
4641cb0ef41Sopenharmony_ci  bool gap_initialized = true;
4651cb0ef41Sopenharmony_ci  if (instr->parallel_moves()[0] != nullptr &&
4661cb0ef41Sopenharmony_ci      !instr->parallel_moves()[0]->empty()) {
4671cb0ef41Sopenharmony_ci    // Will compress after insertion.
4681cb0ef41Sopenharmony_ci    gap_initialized = false;
4691cb0ef41Sopenharmony_ci    std::swap(instr->parallel_moves()[0], instr->parallel_moves()[1]);
4701cb0ef41Sopenharmony_ci  }
4711cb0ef41Sopenharmony_ci  ParallelMove* moves = instr->GetOrCreateParallelMove(
4721cb0ef41Sopenharmony_ci      static_cast<Instruction::GapPosition>(0), code_zone());
4731cb0ef41Sopenharmony_ci  // Delete relevant entries in predecessors and move everything to block.
4741cb0ef41Sopenharmony_ci  bool first_iteration = true;
4751cb0ef41Sopenharmony_ci  for (RpoNumber& pred_index : block->predecessors()) {
4761cb0ef41Sopenharmony_ci    const InstructionBlock* pred = code()->InstructionBlockAt(pred_index);
4771cb0ef41Sopenharmony_ci    for (MoveOperands* move : *LastInstruction(pred)->parallel_moves()[0]) {
4781cb0ef41Sopenharmony_ci      if (move->IsRedundant()) continue;
4791cb0ef41Sopenharmony_ci      MoveKey key = {move->source(), move->destination()};
4801cb0ef41Sopenharmony_ci      auto it = move_map.find(key);
4811cb0ef41Sopenharmony_ci      if (it != move_map.end()) {
4821cb0ef41Sopenharmony_ci        if (first_iteration) {
4831cb0ef41Sopenharmony_ci          moves->AddMove(move->source(), move->destination());
4841cb0ef41Sopenharmony_ci        }
4851cb0ef41Sopenharmony_ci        move->Eliminate();
4861cb0ef41Sopenharmony_ci      }
4871cb0ef41Sopenharmony_ci    }
4881cb0ef41Sopenharmony_ci    first_iteration = false;
4891cb0ef41Sopenharmony_ci  }
4901cb0ef41Sopenharmony_ci  // Compress.
4911cb0ef41Sopenharmony_ci  if (!gap_initialized) {
4921cb0ef41Sopenharmony_ci    CompressMoves(instr->parallel_moves()[0], instr->parallel_moves()[1]);
4931cb0ef41Sopenharmony_ci  }
4941cb0ef41Sopenharmony_ci  CompressBlock(block);
4951cb0ef41Sopenharmony_ci}
4961cb0ef41Sopenharmony_ci
4971cb0ef41Sopenharmony_cinamespace {
4981cb0ef41Sopenharmony_ci
4991cb0ef41Sopenharmony_cibool IsSlot(const InstructionOperand& op) {
5001cb0ef41Sopenharmony_ci  return op.IsStackSlot() || op.IsFPStackSlot();
5011cb0ef41Sopenharmony_ci}
5021cb0ef41Sopenharmony_ci
5031cb0ef41Sopenharmony_cibool LoadCompare(const MoveOperands* a, const MoveOperands* b) {
5041cb0ef41Sopenharmony_ci  if (!a->source().EqualsCanonicalized(b->source())) {
5051cb0ef41Sopenharmony_ci    return a->source().CompareCanonicalized(b->source());
5061cb0ef41Sopenharmony_ci  }
5071cb0ef41Sopenharmony_ci  if (IsSlot(a->destination()) && !IsSlot(b->destination())) return false;
5081cb0ef41Sopenharmony_ci  if (!IsSlot(a->destination()) && IsSlot(b->destination())) return true;
5091cb0ef41Sopenharmony_ci  return a->destination().CompareCanonicalized(b->destination());
5101cb0ef41Sopenharmony_ci}
5111cb0ef41Sopenharmony_ci
5121cb0ef41Sopenharmony_ci}  // namespace
5131cb0ef41Sopenharmony_ci
5141cb0ef41Sopenharmony_ci// Split multiple loads of the same constant or stack slot off into the second
5151cb0ef41Sopenharmony_ci// slot and keep remaining moves in the first slot.
5161cb0ef41Sopenharmony_civoid MoveOptimizer::FinalizeMoves(Instruction* instr) {
5171cb0ef41Sopenharmony_ci  MoveOpVector& loads = local_vector();
5181cb0ef41Sopenharmony_ci  DCHECK(loads.empty());
5191cb0ef41Sopenharmony_ci
5201cb0ef41Sopenharmony_ci  ParallelMove* parallel_moves = instr->parallel_moves()[0];
5211cb0ef41Sopenharmony_ci  if (parallel_moves == nullptr) return;
5221cb0ef41Sopenharmony_ci  // Find all the loads.
5231cb0ef41Sopenharmony_ci  for (MoveOperands* move : *parallel_moves) {
5241cb0ef41Sopenharmony_ci    if (move->IsRedundant()) continue;
5251cb0ef41Sopenharmony_ci    if (move->source().IsConstant() || IsSlot(move->source())) {
5261cb0ef41Sopenharmony_ci      loads.push_back(move);
5271cb0ef41Sopenharmony_ci    }
5281cb0ef41Sopenharmony_ci  }
5291cb0ef41Sopenharmony_ci  if (loads.empty()) return;
5301cb0ef41Sopenharmony_ci  // Group the loads by source, moving the preferred destination to the
5311cb0ef41Sopenharmony_ci  // beginning of the group.
5321cb0ef41Sopenharmony_ci  std::sort(loads.begin(), loads.end(), LoadCompare);
5331cb0ef41Sopenharmony_ci  MoveOperands* group_begin = nullptr;
5341cb0ef41Sopenharmony_ci  for (MoveOperands* load : loads) {
5351cb0ef41Sopenharmony_ci    // New group.
5361cb0ef41Sopenharmony_ci    if (group_begin == nullptr ||
5371cb0ef41Sopenharmony_ci        !load->source().EqualsCanonicalized(group_begin->source())) {
5381cb0ef41Sopenharmony_ci      group_begin = load;
5391cb0ef41Sopenharmony_ci      continue;
5401cb0ef41Sopenharmony_ci    }
5411cb0ef41Sopenharmony_ci    // Nothing to be gained from splitting here.
5421cb0ef41Sopenharmony_ci    if (IsSlot(group_begin->destination())) continue;
5431cb0ef41Sopenharmony_ci    // Insert new move into slot 1.
5441cb0ef41Sopenharmony_ci    ParallelMove* slot_1 = instr->GetOrCreateParallelMove(
5451cb0ef41Sopenharmony_ci        static_cast<Instruction::GapPosition>(1), code_zone());
5461cb0ef41Sopenharmony_ci    slot_1->AddMove(group_begin->destination(), load->destination());
5471cb0ef41Sopenharmony_ci    load->Eliminate();
5481cb0ef41Sopenharmony_ci  }
5491cb0ef41Sopenharmony_ci  loads.clear();
5501cb0ef41Sopenharmony_ci}
5511cb0ef41Sopenharmony_ci
5521cb0ef41Sopenharmony_ci}  // namespace compiler
5531cb0ef41Sopenharmony_ci}  // namespace internal
5541cb0ef41Sopenharmony_ci}  // namespace v8
555