Files
sdk/runtime/vm/compiler/backend/parallel_move_resolver.h
T
Vyacheslav Egorov 27230ae008 [vm/compiler] Split ParallelMove codegen into scheduling and emission
This CL does not contain any changes to behaviour, but simply moves
ParallelMoveResolver to a separate file. Additionally instead of
immediately generating code we produce a move schedule which is
attached to the ParallelMoveInstr and later converted to the
native code.

This refactoring prepares the code for subsequent improvements, e.g.
we want to rework how temporaries used by move resolution are
allocated: instead of pushing/poping them around every move that needs
them we will allocate space for them in spill area.

Having ParallelMove scheduling separated from code emission also
allows to unit test it.

TEST=ci

Cq-Include-Trybots: luci.dart.try:vm-aot-linux-debug-x64c-try,vm-aot-linux-product-x64-try,vm-aot-android-release-arm64c-try,vm-aot-android-release-arm_x64-try,vm-aot-asan-linux-release-x64-try
Change-Id: Ifeb17940f4cfb3c0cc004cb3f74895f0d3c2b7bf
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/285840
Commit-Queue: Slava Egorov <vegorov@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
2023-02-28 12:21:41 +00:00

159 lines
4.7 KiB
C++

// 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_