|  | // Copyright (c) 2023, the Dart project authors.  Please see the AUTHORS file | 
|  | // for details. All rights reserved. Use of this source code is governed by a | 
|  | // BSD-style license that can be found in the LICENSE file. | 
|  |  | 
|  | #ifndef RUNTIME_VM_COMPILER_BACKEND_PARALLEL_MOVE_RESOLVER_H_ | 
|  | #define RUNTIME_VM_COMPILER_BACKEND_PARALLEL_MOVE_RESOLVER_H_ | 
|  |  | 
|  | #if defined(DART_PRECOMPILED_RUNTIME) | 
|  | #error "AOT runtime should not use compiler sources (including header files)" | 
|  | #endif  // defined(DART_PRECOMPILED_RUNTIME) | 
|  |  | 
|  | #include "vm/allocation.h" | 
|  | #include "vm/compiler/backend/flow_graph_compiler.h" | 
|  | #include "vm/compiler/backend/locations.h" | 
|  | #include "vm/constants.h" | 
|  |  | 
|  | namespace dart { | 
|  |  | 
|  | class MoveOperands; | 
|  |  | 
|  | class ParallelMoveResolver : public ValueObject { | 
|  | public: | 
|  | ParallelMoveResolver(); | 
|  |  | 
|  | // Schedule moves specified by the given parallel move and store the | 
|  | // schedule on the parallel move itself. | 
|  | void Resolve(ParallelMoveInstr* parallel_move); | 
|  |  | 
|  | private: | 
|  | // Build the initial list of moves. | 
|  | void BuildInitialMoveList(ParallelMoveInstr* parallel_move); | 
|  |  | 
|  | // Perform the move at the moves_ index in question (possibly requiring | 
|  | // other moves to satisfy dependencies). | 
|  | void PerformMove(const InstructionSource& source, int index); | 
|  |  | 
|  | // Schedule a move and remove it from the move graph. | 
|  | void AddMoveToSchedule(int index); | 
|  |  | 
|  | // Schedule a swap of two operands. The move from | 
|  | // source to destination is removed from the move graph. | 
|  | void AddSwapToSchedule(int index); | 
|  |  | 
|  | FlowGraphCompiler* compiler_; | 
|  |  | 
|  | // List of moves not yet resolved. | 
|  | GrowableArray<MoveOperands> moves_; | 
|  |  | 
|  | enum class OpKind : uint8_t { | 
|  | kNop, | 
|  | kMove, | 
|  | kSwap, | 
|  | }; | 
|  |  | 
|  | struct Op { | 
|  | OpKind kind; | 
|  | MoveOperands operands; | 
|  | }; | 
|  |  | 
|  | GrowableArray<Op> scheduled_ops_; | 
|  |  | 
|  | friend class MoveSchedule; | 
|  | friend class ParallelMoveEmitter; | 
|  | friend class FlowGraphDeserializer; | 
|  | }; | 
|  |  | 
|  | class ParallelMoveEmitter : public ValueObject { | 
|  | public: | 
|  | ParallelMoveEmitter(FlowGraphCompiler* compiler, | 
|  | ParallelMoveInstr* parallel_move) | 
|  | : compiler_(compiler), parallel_move_(parallel_move) {} | 
|  |  | 
|  | void EmitNativeCode(); | 
|  |  | 
|  | private: | 
|  | class ScratchFpuRegisterScope : public ValueObject { | 
|  | public: | 
|  | ScratchFpuRegisterScope(ParallelMoveEmitter* emitter, FpuRegister blocked); | 
|  | ~ScratchFpuRegisterScope(); | 
|  |  | 
|  | FpuRegister reg() const { return reg_; } | 
|  |  | 
|  | private: | 
|  | ParallelMoveEmitter* const emitter_; | 
|  | FpuRegister reg_; | 
|  | bool spilled_; | 
|  | }; | 
|  |  | 
|  | class TemporaryAllocator : public TemporaryRegisterAllocator { | 
|  | public: | 
|  | TemporaryAllocator(ParallelMoveEmitter* emitter, Register blocked); | 
|  |  | 
|  | Register AllocateTemporary() override; | 
|  | void ReleaseTemporary() override; | 
|  | DEBUG_ONLY(bool DidAllocateTemporary() { return allocated_; }) | 
|  |  | 
|  | virtual ~TemporaryAllocator() { ASSERT(reg_ == kNoRegister); } | 
|  |  | 
|  | private: | 
|  | ParallelMoveEmitter* const emitter_; | 
|  | const Register blocked_; | 
|  | Register reg_; | 
|  | bool spilled_; | 
|  | DEBUG_ONLY(bool allocated_ = false); | 
|  | }; | 
|  |  | 
|  | class ScratchRegisterScope : public ValueObject { | 
|  | public: | 
|  | ScratchRegisterScope(ParallelMoveEmitter* emitter, Register blocked); | 
|  | ~ScratchRegisterScope(); | 
|  |  | 
|  | Register reg() const { return reg_; } | 
|  |  | 
|  | private: | 
|  | TemporaryAllocator allocator_; | 
|  | Register reg_; | 
|  | }; | 
|  |  | 
|  | bool IsScratchLocation(Location loc); | 
|  | intptr_t AllocateScratchRegister(Location::Kind kind, | 
|  | uword blocked_mask, | 
|  | intptr_t first_free_register, | 
|  | intptr_t last_free_register, | 
|  | bool* spilled); | 
|  |  | 
|  | void SpillScratch(Register reg); | 
|  | void RestoreScratch(Register reg); | 
|  | void SpillFpuScratch(FpuRegister reg); | 
|  | void RestoreFpuScratch(FpuRegister reg); | 
|  |  | 
|  | // Generate the code for a move from source to destination. | 
|  | void EmitMove(const MoveOperands& move); | 
|  |  | 
|  | void EmitSwap(const MoveOperands& swap); | 
|  |  | 
|  | // Verify the move list before performing moves. | 
|  | void Verify(); | 
|  |  | 
|  | // Helpers for non-trivial source-destination combinations that cannot | 
|  | // be handled by a single instruction. | 
|  | void MoveMemoryToMemory(const compiler::Address& dst, | 
|  | const compiler::Address& src); | 
|  | void Exchange(Register reg, const compiler::Address& mem); | 
|  | void Exchange(const compiler::Address& mem1, const compiler::Address& mem2); | 
|  | void Exchange(Register reg, Register base_reg, intptr_t stack_offset); | 
|  | void Exchange(Register base_reg1, | 
|  | intptr_t stack_offset1, | 
|  | Register base_reg2, | 
|  | intptr_t stack_offset2); | 
|  |  | 
|  | FlowGraphCompiler* const compiler_; | 
|  | ParallelMoveInstr* parallel_move_; | 
|  | intptr_t current_move_; | 
|  | }; | 
|  |  | 
|  | }  // namespace dart | 
|  |  | 
|  | #endif  // RUNTIME_VM_COMPILER_BACKEND_PARALLEL_MOVE_RESOLVER_H_ |