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