Commit ab7e89f1 authored by Ross McIlroy's avatar Ross McIlroy Committed by Commit Bot

[TurboProp] Add PendingOperand for use by fast register allocator.

Adds a pending operand type for use with the fast register allocator.
These operands chain together multiple operands together, enabling
the allocator to keep track of multiple pending operands, then
replace them all with the allocated operand in one go.

BUG=v8:9684

Change-Id: I5d8150f3f26549a747a2e89e32e31135e89dff9c
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2292302
Commit-Queue: Ross McIlroy <rmcilroy@chromium.org>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#69019}
parent 9414d539
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "src/compiler/backend/instruction.h" #include "src/compiler/backend/instruction.h"
#include <cstddef>
#include <iomanip> #include <iomanip>
#include "src/codegen/interface-descriptors.h" #include "src/codegen/interface-descriptors.h"
...@@ -169,6 +170,8 @@ std::ostream& operator<<(std::ostream& os, const InstructionOperand& op) { ...@@ -169,6 +170,8 @@ std::ostream& operator<<(std::ostream& os, const InstructionOperand& op) {
return os << "[immediate:" << imm.indexed_value() << "]"; return os << "[immediate:" << imm.indexed_value() << "]";
} }
} }
case InstructionOperand::PENDING:
return os << "[pending: " << PendingOperand::cast(op).next() << "]";
case InstructionOperand::ALLOCATED: { case InstructionOperand::ALLOCATED: {
LocationOperand allocated = LocationOperand::cast(op); LocationOperand allocated = LocationOperand::cast(op);
if (op.IsStackSlot()) { if (op.IsStackSlot()) {
...@@ -296,6 +299,9 @@ Instruction::Instruction(InstructionCode opcode) ...@@ -296,6 +299,9 @@ Instruction::Instruction(InstructionCode opcode)
block_(nullptr) { block_(nullptr) {
parallel_moves_[0] = nullptr; parallel_moves_[0] = nullptr;
parallel_moves_[1] = nullptr; parallel_moves_[1] = nullptr;
// PendingOperands are required to be 8 byte aligned.
STATIC_ASSERT(offsetof(Instruction, operands_) % 8 == 0);
} }
Instruction::Instruction(InstructionCode opcode, size_t output_count, Instruction::Instruction(InstructionCode opcode, size_t output_count,
......
...@@ -33,7 +33,7 @@ namespace compiler { ...@@ -33,7 +33,7 @@ namespace compiler {
class Schedule; class Schedule;
class SourcePositionTable; class SourcePositionTable;
class V8_EXPORT_PRIVATE InstructionOperand { class V8_EXPORT_PRIVATE alignas(8) InstructionOperand {
public: public:
static const int kInvalidVirtualRegister = -1; static const int kInvalidVirtualRegister = -1;
...@@ -42,6 +42,7 @@ class V8_EXPORT_PRIVATE InstructionOperand { ...@@ -42,6 +42,7 @@ class V8_EXPORT_PRIVATE InstructionOperand {
UNALLOCATED, UNALLOCATED,
CONSTANT, CONSTANT,
IMMEDIATE, IMMEDIATE,
PENDING,
// Location operand kinds. // Location operand kinds.
ALLOCATED, ALLOCATED,
FIRST_LOCATION_OPERAND_KIND = ALLOCATED FIRST_LOCATION_OPERAND_KIND = ALLOCATED
...@@ -67,6 +68,10 @@ class V8_EXPORT_PRIVATE InstructionOperand { ...@@ -67,6 +68,10 @@ class V8_EXPORT_PRIVATE InstructionOperand {
// embedded directly in instructions, e.g. small integers and on some // embedded directly in instructions, e.g. small integers and on some
// platforms Objects. // platforms Objects.
INSTRUCTION_OPERAND_PREDICATE(Immediate, IMMEDIATE) INSTRUCTION_OPERAND_PREDICATE(Immediate, IMMEDIATE)
// PendingOperands are pending allocation during register allocation and
// shouldn't be seen elsewhere. They chain together multiple operators that
// will be replaced together with the same value when finalized.
INSTRUCTION_OPERAND_PREDICATE(Pending, PENDING)
// AllocatedOperands are registers or stack slots that are assigned by the // AllocatedOperands are registers or stack slots that are assigned by the
// register allocator and are always associated with a virtual register. // register allocator and are always associated with a virtual register.
INSTRUCTION_OPERAND_PREDICATE(Allocated, ALLOCATED) INSTRUCTION_OPERAND_PREDICATE(Allocated, ALLOCATED)
...@@ -99,6 +104,10 @@ class V8_EXPORT_PRIVATE InstructionOperand { ...@@ -99,6 +104,10 @@ class V8_EXPORT_PRIVATE InstructionOperand {
} }
bool Equals(const InstructionOperand& that) const { bool Equals(const InstructionOperand& that) const {
if (IsPending()) {
// Pending operands are only equal if they are the same operand.
return this == &that;
}
return this->value_ == that.value_; return this->value_ == that.value_;
} }
...@@ -107,10 +116,15 @@ class V8_EXPORT_PRIVATE InstructionOperand { ...@@ -107,10 +116,15 @@ class V8_EXPORT_PRIVATE InstructionOperand {
} }
bool EqualsCanonicalized(const InstructionOperand& that) const { bool EqualsCanonicalized(const InstructionOperand& that) const {
if (IsPending()) {
// Pending operands can't be canonicalized, so just compare for equality.
return Equals(that);
}
return this->GetCanonicalizedValue() == that.GetCanonicalizedValue(); return this->GetCanonicalizedValue() == that.GetCanonicalizedValue();
} }
bool CompareCanonicalized(const InstructionOperand& that) const { bool CompareCanonicalized(const InstructionOperand& that) const {
DCHECK(!IsPending());
return this->GetCanonicalizedValue() < that.GetCanonicalizedValue(); return this->GetCanonicalizedValue() < that.GetCanonicalizedValue();
} }
...@@ -404,6 +418,44 @@ class ImmediateOperand : public InstructionOperand { ...@@ -404,6 +418,44 @@ class ImmediateOperand : public InstructionOperand {
using ValueField = base::BitField64<int32_t, 32, 32>; using ValueField = base::BitField64<int32_t, 32, 32>;
}; };
class PendingOperand : public InstructionOperand {
public:
PendingOperand() : InstructionOperand(PENDING) {}
explicit PendingOperand(PendingOperand* next_operand) : PendingOperand() {
set_next(next_operand);
}
void set_next(PendingOperand* next) {
DCHECK_NULL(this->next());
uintptr_t shifted_value =
reinterpret_cast<uintptr_t>(next) >> kPointerShift;
DCHECK_EQ(reinterpret_cast<uintptr_t>(next),
shifted_value << kPointerShift);
value_ |= NextOperandField::encode(static_cast<uint64_t>(shifted_value));
}
PendingOperand* next() const {
uintptr_t shifted_value =
static_cast<uint64_t>(NextOperandField::decode(value_));
return reinterpret_cast<PendingOperand*>(shifted_value << kPointerShift);
}
static PendingOperand* New(Zone* zone, PendingOperand* previous_operand) {
return InstructionOperand::New(zone, PendingOperand(previous_operand));
}
INSTRUCTION_OPERAND_CASTS(PendingOperand, PENDING)
private:
// Operands are uint64_t values and so are aligned to 8 byte boundaries,
// therefore we can shift off the bottom three zeros without losing data.
static const uint64_t kPointerShift = 3;
STATIC_ASSERT(alignof(InstructionOperand) >= (1 << kPointerShift));
STATIC_ASSERT(KindField::kSize == 3);
using NextOperandField = base::BitField64<uint64_t, 3, 61>;
};
class LocationOperand : public InstructionOperand { class LocationOperand : public InstructionOperand {
public: public:
enum LocationKind { REGISTER, STACK_SLOT }; enum LocationKind { REGISTER, STACK_SLOT };
......
...@@ -284,6 +284,7 @@ UsePositionHintType UsePosition::HintTypeForOperand( ...@@ -284,6 +284,7 @@ UsePositionHintType UsePosition::HintTypeForOperand(
DCHECK(op.IsStackSlot() || op.IsFPStackSlot()); DCHECK(op.IsStackSlot() || op.IsFPStackSlot());
return UsePositionHintType::kNone; return UsePositionHintType::kNone;
} }
case InstructionOperand::PENDING:
case InstructionOperand::INVALID: case InstructionOperand::INVALID:
break; break;
} }
......
...@@ -1211,6 +1211,7 @@ std::ostream& operator<<(std::ostream& os, const InstructionOperandAsJSON& o) { ...@@ -1211,6 +1211,7 @@ std::ostream& operator<<(std::ostream& os, const InstructionOperandAsJSON& o) {
<< MachineReprToString(allocated->representation()) << "\""; << MachineReprToString(allocated->representation()) << "\"";
break; break;
} }
case InstructionOperand::PENDING:
case InstructionOperand::INVALID: case InstructionOperand::INVALID:
UNREACHABLE(); UNREACHABLE();
} }
......
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