[maglev] Use free list for iterating used registers

Don't rely on register_values[index] == nullptr for checking if a
register is free, but instead re-use the free register list, and iterate
the allocatable_register & ~free_register list when iterating used
registers.

This also changes the indexing of register_values to be by register
code, not allocatable register index. The register state stored on the
InterpreterFrameState, however, stays compact (allocatable register
count). A new wrapper class + iterator keeps iteration over it and the
register_values array in sync.

Bug: v8:7700
Change-Id: I7815aa2d4a1f7b7ebafaaafe0727219adcc4dcfe
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3512792
Auto-Submit: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Toon Verwaest <verwaest@chromium.org>
Commit-Queue: Toon Verwaest <verwaest@chromium.org>
Cr-Commit-Position: refs/heads/main@{#79434}
This commit is contained in:
Leszek Swirski 2022-03-09 17:50:20 +01:00 committed by V8 LUCI CQ
parent 0f5f6024ff
commit 0504331b78
10 changed files with 212 additions and 152 deletions

View File

@ -243,6 +243,19 @@ class Register : public CPURegister {
return Register::Create(code, kXRegSizeInBits);
}
// Copied from RegisterBase since there's no CPURegister::from_code.
static constexpr Register FirstOf(RegList list) {
DCHECK_NE(kEmptyRegList, list);
return from_code(base::bits::CountTrailingZerosNonZero(list));
}
static constexpr Register TakeFirst(RegList* list) {
RegList value = *list;
Register result = FirstOf(value);
result.RemoveFrom(list);
return result;
}
static const char* GetSpecialRegisterName(int code) {
return (code == kSPRegInternalCode) ? "sp" : "UNKNOWN";
}

View File

@ -55,25 +55,27 @@ class RegisterBase {
return is_valid() ? RegList{1} << code() : RegList{};
}
static constexpr SubType AnyOf(RegList list) {
static constexpr SubType FirstOf(RegList list) {
DCHECK_NE(kEmptyRegList, list);
return from_code(base::bits::CountTrailingZeros(list));
return SubType::from_code(base::bits::CountTrailingZerosNonZero(list));
}
static constexpr SubType TakeAny(RegList* list) {
static constexpr SubType TakeFirst(RegList* list) {
RegList value = *list;
SubType result = AnyOf(value);
SubType result = FirstOf(value);
result.RemoveFrom(list);
return result;
}
constexpr bool IsIn(RegList list) const { return list & bit(); }
constexpr void InsertInto(RegList* list) const {
DCHECK_NE(bit(), (*list) & bit());
DCHECK(!IsIn(*list));
*list |= bit();
}
constexpr void RemoveFrom(RegList* list) const {
DCHECK_EQ(bit(), (*list) & bit());
DCHECK(IsIn(*list));
*list ^= bit();
}

View File

@ -69,6 +69,29 @@ inline constexpr bool AreAliased(RegType first_reg, RegTypes... regs) {
}
#endif
class RegListIterator {
public:
class Iterator {
public:
explicit Iterator(RegList list) : list_(list) {}
Register operator*() { return Register::FirstOf(list_); }
void operator++() { Register::FirstOf(list_).RemoveFrom(&list_); }
bool operator!=(const Iterator& other) const {
return list_ != other.list_;
}
private:
RegList list_;
};
explicit RegListIterator(RegList list) : list_(list) {}
Iterator begin() const { return Iterator(list_); }
Iterator end() const { return Iterator(kEmptyRegList); }
private:
RegList list_;
};
} // namespace internal
} // namespace v8

View File

@ -249,11 +249,11 @@ class MaglevCodeGeneratingNodeProcessor {
__ RecordComment("-- Gap moves:");
for (int index = 0; index < kAllocatableGeneralRegisterCount; ++index) {
for (auto entry : target->state()->register_state()) {
RegisterMerge* merge;
if (LoadMergeState(target->state()->register_state()[index], &merge)) {
if (LoadMergeState(entry.state, &merge)) {
compiler::AllocatedOperand source = merge->operand(predecessor_id);
Register target_reg = MapIndexToRegister(index);
Register target_reg = entry.reg;
if (FLAG_code_comments) {
std::stringstream ss;

View File

@ -67,6 +67,46 @@ class InterpreterFrameState {
RegisterFrameArray<ValueNode*> frame_;
};
class MergePointRegisterState {
public:
class Iterator {
public:
struct Entry {
RegisterState& state;
Register reg;
};
explicit Iterator(RegisterState* value_pointer,
RegListIterator::Iterator reg_iterator)
: current_value_(value_pointer), reg_iterator_(reg_iterator) {}
Entry operator*() { return {*current_value_, *reg_iterator_}; }
void operator++() {
++current_value_;
++reg_iterator_;
}
bool operator!=(const Iterator& other) const {
return current_value_ != other.current_value_;
}
private:
RegisterState* current_value_;
RegListIterator::Iterator reg_iterator_;
};
bool is_initialized() const { return values_[0].GetPayload().is_initialized; }
Iterator begin() {
return Iterator(values_,
RegListIterator::Iterator(kAllocatableGeneralRegisters));
}
Iterator end() {
return Iterator(values_ + kAllocatableGeneralRegisterCount,
RegListIterator::Iterator(kEmptyRegList));
}
private:
RegisterState values_[kAllocatableGeneralRegisterCount] = {{}};
};
class MergePointInterpreterFrameState {
public:
void CheckIsLoopPhiIfNeeded(const MaglevCompilationUnit& compilation_unit,
@ -161,7 +201,7 @@ class MergePointInterpreterFrameState {
DCHECK_LE(predecessors_so_far_, predecessor_count_);
}
RegisterState* register_state() { return register_values_; }
MergePointRegisterState& register_state() { return register_state_; }
// Merges an unmerged framestate with a possibly merged framestate into |this|
// framestate.
@ -340,10 +380,7 @@ class MergePointInterpreterFrameState {
const compiler::BytecodeLivenessState* liveness_ = nullptr;
BasicBlock** predecessors_;
#define N(V) RegisterState{nullptr},
RegisterState register_values_[kAllocatableGeneralRegisterCount] = {
ALLOCATABLE_GENERAL_REGISTERS(N)};
#undef N
MergePointRegisterState register_state_;
};
void InterpreterFrameState::CopyFrom(

View File

@ -507,7 +507,7 @@ void CheckMaps::GenerateCode(MaglevCodeGenState* code_gen_state,
const ProcessingState& state) {
Register object = ToRegister(actual_map_input());
RegList temps = temporaries();
Register map_tmp = Register::TakeAny(&temps);
Register map_tmp = Register::TakeFirst(&temps);
__ LoadMap(map_tmp, object);
__ Cmp(map_tmp, map().object());

View File

@ -567,10 +567,7 @@ class ValueNode : public Node {
// 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 AddRegister(Register reg) { reg.InsertInto(&registers_with_result_); }
void RemoveRegister(Register reg) { reg.RemoveFrom(&registers_with_result_); }
RegList ClearRegisters() {
return std::exchange(registers_with_result_, kEmptyRegList);
@ -581,7 +578,7 @@ class ValueNode : public Node {
if (has_register()) {
return compiler::AllocatedOperand(
compiler::LocationOperand::REGISTER, MachineRepresentation::kTagged,
Register::AnyOf(registers_with_result_).code());
Register::FirstOf(registers_with_result_).code());
}
DCHECK(is_spilled());
return compiler::AllocatedOperand::cast(spill_or_hint_);

View File

@ -20,32 +20,6 @@ static constexpr int kAllocatableGeneralRegisterCount =
ALLOCATABLE_GENERAL_REGISTERS(COUNT);
#undef COUNT
constexpr uint8_t MapRegisterToIndex(Register r) {
uint8_t count = 0;
#define EMIT_BRANCH(V) \
if (r == V) return count; \
count++;
ALLOCATABLE_GENERAL_REGISTERS(EMIT_BRANCH)
#undef EMIT_BRANCH
// TODO(v8:7700): Re-enable UNREACHABLE once we figure out how to to avoid
// the gcc error 'call to non-constexpr function'.
// UNREACHABLE();
return 255;
}
constexpr Register MapIndexToRegister(int i) {
uint8_t count = 0;
#define EMIT_BRANCH(V) \
if (i == count) return V; \
count++;
ALLOCATABLE_GENERAL_REGISTERS(EMIT_BRANCH)
#undef EMIT_BRANCH
// TODO(v8:7700): Re-enable UNREACHABLE once we figure out how to to avoid
// the gcc error 'call to non-constexpr function'.
// UNREACHABLE();
return no_reg;
}
struct RegisterStateFlags {
// TODO(v8:7700): Use the good old Flags mechanism.
static constexpr int kIsMergeShift = 0;

View File

@ -6,6 +6,7 @@
#include "src/base/bits.h"
#include "src/base/logging.h"
#include "src/codegen/register.h"
#include "src/codegen/reglist.h"
#include "src/compiler/backend/instruction.h"
#include "src/maglev/maglev-compilation-data.h"
@ -58,7 +59,7 @@ ControlNode* NearestPostDominatingHole(ControlNode* node) {
}
bool IsLiveAtTarget(ValueNode* node, ControlNode* source, BasicBlock* target) {
if (!node) return false;
DCHECK_NOT_NULL(node);
// TODO(leszeks): We shouldn't have any dead nodes passed into here.
if (node->is_dead()) return false;
@ -197,15 +198,14 @@ void StraightForwardRegisterAllocator::ComputePostDominatingHoles(
void StraightForwardRegisterAllocator::PrintLiveRegs() const {
bool first = true;
for (int i = 0; i < kAllocatableGeneralRegisterCount; i++) {
ValueNode* node = register_values_[i];
if (!node) continue;
for (Register reg : RegListIterator(used_registers())) {
ValueNode* node = GetUsedRegister(reg);
if (first) {
first = false;
} else {
printing_visitor_->os() << ", ";
}
printing_visitor_->os() << MapIndexToRegister(i) << "=v" << node->id();
printing_visitor_->os() << reg << "=v" << node->id();
}
}
@ -410,27 +410,21 @@ void StraightForwardRegisterAllocator::AllocateNodeResult(ValueNode* node) {
}
}
void StraightForwardRegisterAllocator::Free(const Register& reg) {
int index = MapRegisterToIndex(reg);
ValueNode* node = register_values_[index];
void StraightForwardRegisterAllocator::DropRegisterValue(Register reg) {
// The register should not already be free.
DCHECK(!reg.IsIn(free_registers_));
// If the register is already free, return.
if (!node) return;
ValueNode* node = GetUsedRegister(reg);
// Free the register without adding it to the list.
register_values_[index] = nullptr;
// Remove the register from the list.
// Remove the register from the node's list.
node->RemoveRegister(reg);
// Return if the removed value already has another register.
if (node->has_register()) return;
// If the value is already spilled, return.
if (node->is_spilled()) return;
// Return if the removed value already has another register or is spilled.
if (node->has_register() || node->is_spilled()) return;
// Try to move the value to another register.
if (free_registers_ != kEmptyRegList) {
Register target_reg = Register::TakeAny(&free_registers_);
Register target_reg = Register::TakeFirst(&free_registers_);
SetRegister(target_reg, node);
// Emit a gapmove.
compiler::AllocatedOperand source(compiler::LocationOperand::REGISTER,
@ -467,10 +461,14 @@ void StraightForwardRegisterAllocator::InitializeConditionalBranchRegisters(
}
// Clear dead fall-through registers.
DCHECK_EQ(control_node->id() + 1, target->first_id());
for (int i = 0; i < kAllocatableGeneralRegisterCount; i++) {
ValueNode* node = register_values_[i];
if (node != nullptr && !IsLiveAtTarget(node, control_node, target)) {
RegList registers = used_registers();
while (registers != kEmptyRegList) {
Register reg = Register::TakeFirst(&registers);
ValueNode* node = GetUsedRegister(reg);
if (!IsLiveAtTarget(node, control_node, target)) {
FreeRegisters(node);
// Update the registers we're visiting to avoid revisiting this node.
registers &= !free_registers_;
}
}
}
@ -526,8 +524,7 @@ void StraightForwardRegisterAllocator::TryAllocateToInput(Phi* phi) {
for (Input& input : *phi) {
if (input.operand().IsRegister()) {
Register reg = input.AssignedRegister();
int index = MapRegisterToIndex(reg);
if (register_values_[index] == nullptr) {
if (reg.IsIn(free_registers_)) {
phi->result().SetAllocated(ForceAllocate(reg, phi));
if (FLAG_trace_maglev_regalloc) {
printing_visitor_->Process(
@ -614,24 +611,19 @@ void StraightForwardRegisterAllocator::AssignInput(Input& input) {
}
void StraightForwardRegisterAllocator::SpillRegisters() {
for (int i = 0; i < kAllocatableGeneralRegisterCount; i++) {
ValueNode* node = register_values_[i];
if (!node) continue;
for (Register reg : RegListIterator(used_registers())) {
ValueNode* node = GetUsedRegister(reg);
Spill(node);
}
}
void StraightForwardRegisterAllocator::FreeRegister(int i) {
register_values_[i] = nullptr;
MapIndexToRegister(i).InsertInto(&free_registers_);
}
void StraightForwardRegisterAllocator::SpillAndClearRegisters() {
for (int i = 0; i < kAllocatableGeneralRegisterCount; i++) {
ValueNode* node = register_values_[i];
if (!node) continue;
while (used_registers() != kEmptyRegList) {
Register reg = Register::FirstOf(used_registers());
ValueNode* node = GetUsedRegister(reg);
Spill(node);
FreeRegisters(node);
DCHECK(!reg.IsIn(used_registers()));
}
}
@ -651,16 +643,16 @@ void StraightForwardRegisterAllocator::AllocateSpillSlot(ValueNode* node) {
void StraightForwardRegisterAllocator::FreeSomeRegister() {
int furthest_use = 0;
int longest = -1;
for (int i = 0; i < kAllocatableGeneralRegisterCount; i++) {
if (!register_values_[i]) continue;
int use = register_values_[i]->next_use();
Register best = Register::no_reg();
for (Register reg : RegListIterator(used_registers())) {
int use = GetUsedRegister(reg)->next_use();
if (use > furthest_use) {
furthest_use = use;
longest = i;
best = reg;
}
}
FreeRegister(longest);
DCHECK(best.is_valid());
FreeRegister(best);
}
compiler::AllocatedOperand StraightForwardRegisterAllocator::AllocateRegister(
@ -672,21 +664,19 @@ compiler::AllocatedOperand StraightForwardRegisterAllocator::AllocateRegister(
}
compiler::AllocatedOperand StraightForwardRegisterAllocator::ForceAllocate(
const Register& reg, ValueNode* node) {
if (register_values_[MapRegisterToIndex(reg)] == nullptr) {
Register reg, ValueNode* node) {
if (reg.IsIn(free_registers_)) {
// If it's already free, remove it from the free list.
reg.RemoveFrom(&free_registers_);
} else if (register_values_[MapRegisterToIndex(reg)] == node) {
} else if (GetUsedRegister(reg) == node) {
return compiler::AllocatedOperand(compiler::LocationOperand::REGISTER,
MachineRepresentation::kTagged,
reg.code());
} else {
Free(reg);
DCHECK_NULL(register_values_[MapRegisterToIndex(reg)]);
DropRegisterValue(reg);
}
#ifdef DEBUG
DCHECK_NE(free_registers_,
CombineRegLists(free_registers_, Register::ListOf(reg)));
DCHECK(!reg.IsIn(free_registers_));
#endif
SetRegister(reg, node);
return compiler::AllocatedOperand(compiler::LocationOperand::REGISTER,
@ -695,17 +685,15 @@ compiler::AllocatedOperand StraightForwardRegisterAllocator::ForceAllocate(
void StraightForwardRegisterAllocator::SetRegister(Register reg,
ValueNode* node) {
int index = MapRegisterToIndex(reg);
DCHECK_IMPLIES(register_values_[index] != node,
register_values_[index] == nullptr);
register_values_[index] = node;
DCHECK(!reg.IsIn(free_registers_));
register_values_[reg.code()] = node;
node->AddRegister(reg);
}
compiler::InstructionOperand
StraightForwardRegisterAllocator::TryAllocateRegister(ValueNode* node) {
if (free_registers_ == kEmptyRegList) return compiler::InstructionOperand();
Register reg = Register::TakeAny(&free_registers_);
Register reg = Register::TakeFirst(&free_registers_);
// Allocation succeeded. This might have found an existing allocation.
// Simply update the state anyway.
@ -729,79 +717,91 @@ void StraightForwardRegisterAllocator::AssignTemporaries(NodeBase* node) {
}
void StraightForwardRegisterAllocator::InitializeRegisterValues(
RegisterState* target_state) {
MergePointRegisterState& target_state) {
// First clear the register state.
// TODO(verwaest): We could loop over the list of allocated registers by
// deducing it from the free registers.
for (int i = 0; i < kAllocatableGeneralRegisterCount; i++) {
ValueNode* node = register_values_[i];
if (!node) continue;
node->ClearRegisters();
while (used_registers() != kEmptyRegList) {
Register reg = Register::FirstOf(used_registers());
ValueNode* node = GetUsedRegister(reg);
FreeRegisters(node);
DCHECK(!reg.IsIn(used_registers()));
}
// Mark no register as free.
free_registers_ = kEmptyRegList;
// All registers should be free by now.
DCHECK_EQ(free_registers_, kAllocatableGeneralRegisters);
// Then fill it in with target information.
for (int i = 0; i < kAllocatableGeneralRegisterCount; i++) {
for (auto entry : target_state) {
Register reg = entry.reg;
ValueNode* node;
RegisterMerge* merge;
LoadMergeState(target_state[i], &node, &merge);
if (node == nullptr) {
DCHECK(!target_state[i].GetPayload().is_merge);
FreeRegister(i);
LoadMergeState(entry.state, &node, &merge);
if (node != nullptr) {
reg.RemoveFrom(&free_registers_);
SetRegister(reg, node);
} else {
SetRegister(MapIndexToRegister(i), node);
DCHECK(!entry.state.GetPayload().is_merge);
}
}
}
void StraightForwardRegisterAllocator::EnsureInRegister(
RegisterState* target_state, ValueNode* incoming) {
MergePointRegisterState& target_state, ValueNode* incoming) {
#ifdef DEBUG
int i;
for (i = 0; i < kAllocatableGeneralRegisterCount; i++) {
bool found = false;
for (auto entry : target_state) {
ValueNode* node;
RegisterMerge* merge;
LoadMergeState(target_state[i], &node, &merge);
if (node == incoming) break;
LoadMergeState(entry.state, &node, &merge);
if (node == incoming) found = true;
}
CHECK_NE(kAllocatableGeneralRegisterCount, i);
DCHECK(found);
#endif
}
void StraightForwardRegisterAllocator::InitializeBranchTargetRegisterValues(
ControlNode* source, BasicBlock* target) {
RegisterState* target_state = target->state()->register_state();
DCHECK(!target_state[0].GetPayload().is_initialized);
for (int i = 0; i < kAllocatableGeneralRegisterCount; i++) {
ValueNode* node = register_values_[i];
if (!IsLiveAtTarget(node, source, target)) node = nullptr;
target_state[i] = {node, initialized_node};
MergePointRegisterState& target_state = target->state()->register_state();
DCHECK(!target_state.is_initialized());
for (auto entry : target_state) {
Register reg = entry.reg;
ValueNode* node = nullptr;
if (!reg.IsIn(free_registers_)) {
node = GetUsedRegister(reg);
if (!IsLiveAtTarget(node, source, target)) node = nullptr;
}
entry.state = {node, initialized_node};
}
}
void StraightForwardRegisterAllocator::MergeRegisterValues(ControlNode* control,
BasicBlock* target,
int predecessor_id) {
RegisterState* target_state = target->state()->register_state();
if (!target_state[0].GetPayload().is_initialized) {
MergePointRegisterState& target_state = target->state()->register_state();
if (!target_state.is_initialized()) {
// This is the first block we're merging, initialize the values.
return InitializeBranchTargetRegisterValues(control, target);
}
int predecessor_count = target->state()->predecessor_count();
for (int i = 0; i < kAllocatableGeneralRegisterCount; i++) {
for (auto entry : target_state) {
Register reg = entry.reg;
ValueNode* node;
RegisterMerge* merge;
LoadMergeState(target_state[i], &node, &merge);
LoadMergeState(entry.state, &node, &merge);
compiler::AllocatedOperand register_info = {
compiler::LocationOperand::REGISTER, MachineRepresentation::kTagged,
MapIndexToRegister(i).code()};
reg.code()};
ValueNode* incoming = register_values_[i];
if (!IsLiveAtTarget(incoming, control, target)) incoming = nullptr;
ValueNode* incoming = nullptr;
if (!reg.IsIn(free_registers_)) {
incoming = GetUsedRegister(reg);
if (!IsLiveAtTarget(incoming, control, target)) {
incoming = nullptr;
}
}
if (incoming == node) {
// We're using the same register as the target already has. If registers
@ -849,8 +849,8 @@ void StraightForwardRegisterAllocator::MergeRegisterValues(ControlNode* control,
// Initialize the entire array with info_so_far since we don't know in
// which order we've seen the predecessors so far. Predecessors we
// haven't seen yet will simply overwrite their entry later.
for (int j = 0; j < predecessor_count; j++) {
merge->operand(j) = info_so_far;
for (int i = 0; i < predecessor_count; i++) {
merge->operand(i) = info_so_far;
}
// If the register is unallocated at the merge point, fill in the
// incoming value. Otherwise find the merge-point node in the incoming
@ -860,7 +860,7 @@ void StraightForwardRegisterAllocator::MergeRegisterValues(ControlNode* control,
} else {
merge->operand(predecessor_id) = node->allocation();
}
target_state[i] = {merge, initialized_merge};
entry.state = {merge, initialized_merge};
}
}

View File

@ -17,6 +17,7 @@ namespace internal {
namespace maglev {
class MaglevPrintingVisitor;
class MergePointRegisterState;
class StraightForwardRegisterAllocator {
public:
@ -27,17 +28,19 @@ class StraightForwardRegisterAllocator {
int stack_slots() const { return top_of_stack_; }
private:
std::vector<int> future_register_uses_[kAllocatableGeneralRegisterCount];
#define N(V) nullptr,
ValueNode* register_values_[kAllocatableGeneralRegisterCount] = {
ALLOCATABLE_GENERAL_REGISTERS(N)};
#undef N
std::vector<int> future_register_uses_[Register::kNumRegisters];
ValueNode* register_values_[Register::kNumRegisters];
int top_of_stack_ = 0;
RegList free_registers_ = kAllocatableGeneralRegisters;
std::vector<uint32_t> free_slots_;
RegList used_registers() const {
// Only allocatable registers should be free.
DCHECK_EQ(free_registers_, free_registers_ & kAllocatableGeneralRegisters);
return kAllocatableGeneralRegisters ^ free_registers_;
}
void ComputePostDominatingHoles(Graph* graph);
void AllocateRegisters(Graph* graph);
@ -54,12 +57,23 @@ class StraightForwardRegisterAllocator {
void FreeRegisters(ValueNode* node) {
RegList list = node->ClearRegisters();
while (list != kEmptyRegList) {
Register reg = Register::TakeAny(&list);
FreeRegister(MapRegisterToIndex(reg));
}
DCHECK_EQ(free_registers_ & list, kEmptyRegList);
free_registers_ |= list;
}
void FreeRegister(int i);
void FreeRegister(Register reg) { reg.InsertInto(&free_registers_); }
ValueNode* GetUsedRegister(Register reg) const {
DCHECK(!reg.IsIn(free_registers_));
ValueNode* node = register_values_[reg.code()];
DCHECK_NOT_NULL(node);
return node;
}
ValueNode* GetMaybeUsedRegister(Register reg) const {
if (!reg.IsIn(free_registers_)) return nullptr;
return GetUsedRegister(reg);
}
void FreeSomeRegister();
void AddMoveBeforeCurrentNode(compiler::AllocatedOperand source,
compiler::AllocatedOperand target);
@ -70,14 +84,14 @@ class StraightForwardRegisterAllocator {
void SpillRegisters();
compiler::AllocatedOperand AllocateRegister(ValueNode* node);
compiler::AllocatedOperand ForceAllocate(const Register& reg,
ValueNode* node);
compiler::AllocatedOperand ForceAllocate(Register reg, ValueNode* node);
void SetRegister(Register reg, ValueNode* node);
void Free(const Register& reg);
void DropRegisterValue(Register reg);
compiler::InstructionOperand TryAllocateRegister(ValueNode* node);
void InitializeRegisterValues(RegisterState* target_state);
void EnsureInRegister(RegisterState* target_state, ValueNode* incoming);
void InitializeRegisterValues(MergePointRegisterState& target_state);
void EnsureInRegister(MergePointRegisterState& target_state,
ValueNode* incoming);
void InitializeBranchTargetRegisterValues(ControlNode* source,
BasicBlock* target);