Commit b3d748a2 authored by Jakob Gruber's avatar Jakob Gruber Committed by Commit Bot

[regalloc] Use an adaptive data structure for live sets

Live sets represent sets of live virtual registers at block entry and
exit points. They are usually sparsely populated; for example, a sample
taken from Octane2 shows 80% of sampled live sets with a fill ratio of
10% or less.

Prior to this CL, live sets were implemented as a statically-sized bit
vector. This is fine for low-ish virtual register counts, but becomes
wasteful at higher numbers.

This CL attempts to address this issue through an adaptive
implementation. Small live sets remain bit vectors, while larger sets
switch to a PersistentMap-based implementation. PersistentMap has very
memory-efficient add/remove/copy operations.

Of course, with adaptive data structures we enter the territory of
parameter fiddling. In this case, two parameters are used:
kMaxSmallSetSize controls when to switch implementations, and
kMaxDeletionsBeforePrune controls when pruning (= managing the # of
deleted entries in the map) sets in.

On the (degenerate) test case from the linked bug, the register
allocation zone shrinks from 1008MB to 475MB. For more realistic cases
I expect savings on the order of 10s of KB.

Bug: v8:9574
Change-Id: Id903bbe23f030b418e8d887ef4839c8d65126c52
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1891693Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Reviewed-by: 's avatarThibaud Michaud <thibaudm@chromium.org>
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#64872}
parent 1d403ce7
This diff is collapsed.
......@@ -21,6 +21,8 @@ class TickCounter;
namespace compiler {
class LiveSet;
static const int32_t kUnassignedRegister = RegisterConfiguration::kMaxRegisters;
enum RegisterKind { GENERAL_REGISTERS, FP_REGISTERS };
......@@ -277,8 +279,8 @@ class RegisterAllocationData final : public ZoneObject {
const ZoneVector<TopLevelLiveRange*>& fixed_simd128_live_ranges() const {
return fixed_simd128_live_ranges_;
}
ZoneVector<BitVector*>& live_in_sets() { return live_in_sets_; }
ZoneVector<BitVector*>& live_out_sets() { return live_out_sets_; }
ZoneVector<LiveSet*>& live_in_sets() { return live_in_sets_; }
ZoneVector<LiveSet*>& live_out_sets() { return live_out_sets_; }
ZoneVector<SpillRange*>& spill_ranges() { return spill_ranges_; }
DelayedReferences& delayed_references() { return delayed_references_; }
InstructionSequence* code() const { return code_; }
......@@ -352,8 +354,8 @@ class RegisterAllocationData final : public ZoneObject {
const char* const debug_name_;
const RegisterConfiguration* const config_;
PhiMap phi_map_;
ZoneVector<BitVector*> live_in_sets_;
ZoneVector<BitVector*> live_out_sets_;
ZoneVector<LiveSet*> live_in_sets_;
ZoneVector<LiveSet*> live_out_sets_;
ZoneVector<TopLevelLiveRange*> live_ranges_;
ZoneVector<TopLevelLiveRange*> fixed_live_ranges_;
ZoneVector<TopLevelLiveRange*> fixed_float_live_ranges_;
......@@ -1103,8 +1105,8 @@ class LiveRangeBuilder final : public ZoneObject {
// Phase 3: compute liveness of all virtual register.
void BuildLiveRanges();
static BitVector* ComputeLiveOut(const InstructionBlock* block,
RegisterAllocationData* data);
static LiveSet* ComputeLiveOut(const InstructionBlock* block,
RegisterAllocationData* data);
private:
using SpillMode = RegisterAllocationData::SpillMode;
......@@ -1116,9 +1118,7 @@ class LiveRangeBuilder final : public ZoneObject {
Zone* allocation_zone() const { return data()->allocation_zone(); }
Zone* code_zone() const { return code()->zone(); }
const RegisterConfiguration* config() const { return data()->config(); }
ZoneVector<BitVector*>& live_in_sets() const {
return data()->live_in_sets();
}
ZoneVector<LiveSet*>& live_in_sets() const { return data()->live_in_sets(); }
// Verification.
void Verify() const;
......@@ -1128,10 +1128,10 @@ class LiveRangeBuilder final : public ZoneObject {
bool NextIntervalStartsInDifferentBlocks(const UseInterval* interval) const;
// Liveness analysis support.
void AddInitialIntervals(const InstructionBlock* block, BitVector* live_out);
void ProcessInstructions(const InstructionBlock* block, BitVector* live);
void ProcessPhis(const InstructionBlock* block, BitVector* live);
void ProcessLoopHeader(const InstructionBlock* block, BitVector* live);
void AddInitialIntervals(const InstructionBlock* block, LiveSet* live_out);
void ProcessInstructions(const InstructionBlock* block, LiveSet* live);
void ProcessPhis(const InstructionBlock* block, LiveSet* live);
void ProcessLoopHeader(const InstructionBlock* block, LiveSet* live);
static int FixedLiveRangeID(int index) { return -index - 1; }
int FixedFPLiveRangeID(int index, MachineRepresentation rep);
......
......@@ -88,6 +88,8 @@ class PersistentMap {
return !(*this == other);
}
Zone* zone() const { return zone_; }
// The iterator produces key-value pairs in the lexicographical order of
// hash value and key. It produces exactly the key-value pairs where the value
// is not the default value.
......
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