Commit 3fd2428c authored by Leszek Swirski's avatar Leszek Swirski Committed by V8 LUCI CQ

[maglev] Remove LiveNodeInfo, store its data directly on ValueNode

LiveNodeInfo was stored in a Node->LiveNodeInfo map, which was never
copied or merged alongside control flow. Lookups in this map were a bit
part of register allocation time, and it was mostly duplicating data
that was already in the ValueNode.

So, instead of a separate LiveNodeInfo, store the current register
allocation state directly on the ValueNode. This involves a bit of
clobbering of state (in particular, we have to clobber the next_use id),
but that doesn't matter since regalloc is a forward pass and with this
change, it's less memory and zero map lookups.

Measuring on a (very large) function, this reduces compile time from
300ms to 200ms.

Bug: v8:7700
Change-Id: I02620f1a795bd308d1de03d694c102cb5ea3ff50
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3500617Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/main@{#79339}
parent 23b178b5
......@@ -9,6 +9,7 @@
#include "src/base/macros.h"
#include "src/base/small-vector.h"
#include "src/base/threaded-list.h"
#include "src/codegen/reglist.h"
#include "src/common/globals.h"
#include "src/compiler/backend/instruction.h"
#include "src/compiler/heap-refs.h"
......@@ -431,6 +432,8 @@ class NodeBase : public ZoneObject {
}
uint32_t bit_field_;
private:
NodeIdT id_ = kInvalidNodeId;
union {
......@@ -445,7 +448,6 @@ class NodeBase : public ZoneObject {
} kTemporariesState = kUnset;
#endif // DEBUG
private:
NodeBase() = delete;
NodeBase(const NodeBase&) = delete;
NodeBase(NodeBase&&) = delete;
......@@ -554,6 +556,33 @@ class ValueNode : public Node {
LiveRange live_range() const { return {start_id(), end_id_}; }
NodeIdT next_use() const { return next_use_; }
// The following metods should only be used during register allocation, to
// mark the _current_ state of this Node according to the register allocator.
void set_next_use(NodeIdT use) { next_use_ = use; }
// A node is dead once it has no more upcoming uses.
bool is_dead() const { return next_use_ == kInvalidNodeId; }
void AddRegister(Register reg) {
registers_with_result_ =
CombineRegLists(registers_with_result_, Register::ListOf(reg));
}
void RemoveRegister(Register reg) { reg.RemoveFrom(&registers_with_result_); }
RegList ClearRegisters() {
return std::exchange(registers_with_result_, kEmptyRegList);
}
bool has_register() const { return registers_with_result_ != kEmptyRegList; }
compiler::AllocatedOperand allocation() const {
if (has_register()) {
return compiler::AllocatedOperand(
compiler::LocationOperand::REGISTER, MachineRepresentation::kTagged,
Register::AnyOf(registers_with_result_).code());
}
DCHECK(is_spilled());
return compiler::AllocatedOperand::cast(spill_or_hint_);
}
protected:
explicit ValueNode(Opcode opcode, size_t input_count)
: Node(opcode, input_count),
......@@ -571,6 +600,7 @@ class ValueNode : public Node {
NodeIdT end_id_ = kInvalidNodeId;
NodeIdT next_use_ = kInvalidNodeId;
ValueLocation result_;
RegList registers_with_result_ = kEmptyRegList;
union {
// Pointer to the current last use's next_use_id field. Most of the time
// this will be a pointer to an Input's next_use_id_ field, but it's
......
......@@ -13,7 +13,7 @@ namespace v8 {
namespace internal {
namespace maglev {
struct LiveNodeInfo;
class ValueNode;
#define COUNT(V) +1
static constexpr int kAllocatableGeneralRegisterCount =
......@@ -78,7 +78,7 @@ struct RegisterMerge {
}
compiler::AllocatedOperand& operand(size_t i) { return operands()[i]; }
LiveNodeInfo* node;
ValueNode* node;
};
inline bool LoadMergeState(RegisterState state, RegisterMerge** merge) {
......@@ -91,14 +91,14 @@ inline bool LoadMergeState(RegisterState state, RegisterMerge** merge) {
return false;
}
inline bool LoadMergeState(RegisterState state, LiveNodeInfo** node,
inline bool LoadMergeState(RegisterState state, ValueNode** node,
RegisterMerge** merge) {
DCHECK(state.GetPayload().is_initialized);
if (LoadMergeState(state, merge)) {
*node = (*merge)->node;
return true;
}
*node = static_cast<LiveNodeInfo*>(state.GetPointer());
*node = static_cast<ValueNode*>(state.GetPointer());
return false;
}
......
This diff is collapsed.
......@@ -18,25 +18,6 @@ namespace maglev {
class MaglevPrintingVisitor;
struct LiveNodeInfo {
ValueNode* node;
uint32_t last_use = 0;
uint32_t next_use = 0;
compiler::InstructionOperand stack_slot = compiler::InstructionOperand();
RegList registers = kEmptyRegList;
bool has_register() const { return registers != kEmptyRegList; }
compiler::AllocatedOperand allocation() const {
if (has_register()) {
return compiler::AllocatedOperand(compiler::LocationOperand::REGISTER,
MachineRepresentation::kTagged,
Register::AnyOf(registers).code());
}
return compiler::AllocatedOperand::cast(stack_slot);
}
};
class StraightForwardRegisterAllocator {
public:
StraightForwardRegisterAllocator(MaglevCompilationUnit* compilation_unit,
......@@ -48,12 +29,8 @@ class StraightForwardRegisterAllocator {
private:
std::vector<int> future_register_uses_[kAllocatableGeneralRegisterCount];
// Currently live values.
using LiveNodeInfoMap = std::map<ValueNode*, LiveNodeInfo>;
LiveNodeInfoMap values_;
#define N(V) nullptr,
LiveNodeInfo* register_values_[kAllocatableGeneralRegisterCount] = {
ValueNode* register_values_[kAllocatableGeneralRegisterCount] = {
ALWAYS_ALLOCATABLE_GENERAL_REGISTERS(N)};
#undef N
......@@ -63,13 +40,6 @@ class StraightForwardRegisterAllocator {
uint8_t free_registers_[kAllocatableGeneralRegisterCount];
std::vector<uint32_t> free_slots_;
LiveNodeInfo* MakeLive(ValueNode* node) {
uint32_t last_use = node->live_range().end;
// TODO(verwaest): We don't currently have next_use info...
uint32_t next_use = node->next_use();
return &(values_[node] = {node, last_use, next_use});
}
void ComputePostDominatingHoles(Graph* graph);
void AllocateRegisters(Graph* graph);
......@@ -82,11 +52,12 @@ class StraightForwardRegisterAllocator {
void AllocateNodeResult(ValueNode* node);
void AssignInput(Input& input);
void AssignTemporaries(NodeBase* node);
void TryAllocateToInput(LiveNodeInfo* info, Phi* phi);
void TryAllocateToInput(Phi* phi);
void FreeRegisters(RegList* list) {
while (*list != kEmptyRegList) {
Register reg = Register::TakeAny(list);
void FreeRegisters(ValueNode* node) {
RegList list = node->ClearRegisters();
while (list != kEmptyRegList) {
Register reg = Register::TakeAny(&list);
FreeRegister(MapRegisterToIndex(reg));
}
}
......@@ -95,20 +66,20 @@ class StraightForwardRegisterAllocator {
void AddMoveBeforeCurrentNode(compiler::AllocatedOperand source,
compiler::AllocatedOperand target);
void AllocateSpillSlot(LiveNodeInfo* info);
void Spill(LiveNodeInfo* info);
void AllocateSpillSlot(ValueNode* node);
void Spill(ValueNode* node);
void SpillAndClearRegisters();
void SpillRegisters();
compiler::AllocatedOperand AllocateRegister(LiveNodeInfo* info);
compiler::AllocatedOperand AllocateRegister(ValueNode* node);
compiler::AllocatedOperand ForceAllocate(const Register& reg,
LiveNodeInfo* info);
void SetRegister(Register reg, LiveNodeInfo* info);
ValueNode* node);
void SetRegister(Register reg, ValueNode* node);
void Free(const Register& reg);
compiler::InstructionOperand TryAllocateRegister(LiveNodeInfo* info);
compiler::InstructionOperand TryAllocateRegister(ValueNode* node);
void InitializeRegisterValues(RegisterState* target_state);
void EnsureInRegister(RegisterState* target_state, LiveNodeInfo* incoming);
void EnsureInRegister(RegisterState* target_state, ValueNode* incoming);
void InitializeBranchTargetRegisterValues(ControlNode* source,
BasicBlock* target);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment