| // Copyright 2014 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef V8_COMPILER_BACKEND_INSTRUCTION_SELECTOR_IMPL_H_ |
| #define V8_COMPILER_BACKEND_INSTRUCTION_SELECTOR_IMPL_H_ |
| |
| #include "src/codegen/macro-assembler.h" |
| #include "src/compiler/backend/instruction-selector.h" |
| #include "src/compiler/backend/instruction.h" |
| #include "src/compiler/common-operator.h" |
| #include "src/compiler/linkage.h" |
| #include "src/compiler/schedule.h" |
| |
| namespace v8 { |
| namespace internal { |
| namespace compiler { |
| |
| struct CaseInfo { |
| int32_t value; // The case value. |
| int32_t order; // The order for lowering to comparisons (less means earlier). |
| BasicBlock* branch; // The basic blocks corresponding to the case value. |
| }; |
| |
| inline bool operator<(const CaseInfo& l, const CaseInfo& r) { |
| return l.order < r.order; |
| } |
| |
| // Helper struct containing data about a table or lookup switch. |
| class SwitchInfo { |
| public: |
| SwitchInfo(ZoneVector<CaseInfo>& cases, // NOLINT(runtime/references) |
| int32_t min_value, int32_t max_value, BasicBlock* default_branch) |
| : cases_(cases), |
| min_value_(min_value), |
| max_value_(max_value), |
| default_branch_(default_branch) { |
| if (cases.size() != 0) { |
| DCHECK_LE(min_value, max_value); |
| // Note that {value_range} can be 0 if {min_value} is -2^31 and |
| // {max_value} is 2^31-1, so don't assume that it's non-zero below. |
| value_range_ = |
| 1u + bit_cast<uint32_t>(max_value) - bit_cast<uint32_t>(min_value); |
| } else { |
| value_range_ = 0; |
| } |
| } |
| |
| // Ensure that comparison order of if-cascades is preserved. |
| std::vector<CaseInfo> CasesSortedByOriginalOrder() const { |
| std::vector<CaseInfo> result(cases_.begin(), cases_.end()); |
| std::stable_sort(result.begin(), result.end()); |
| return result; |
| } |
| std::vector<CaseInfo> CasesSortedByValue() const { |
| std::vector<CaseInfo> result(cases_.begin(), cases_.end()); |
| std::stable_sort(result.begin(), result.end(), |
| [](CaseInfo a, CaseInfo b) { return a.value < b.value; }); |
| return result; |
| } |
| const ZoneVector<CaseInfo>& CasesUnsorted() const { return cases_; } |
| int32_t min_value() const { return min_value_; } |
| int32_t max_value() const { return max_value_; } |
| size_t value_range() const { return value_range_; } |
| size_t case_count() const { return cases_.size(); } |
| BasicBlock* default_branch() const { return default_branch_; } |
| |
| private: |
| const ZoneVector<CaseInfo>& cases_; |
| int32_t min_value_; // minimum value of {cases_} |
| int32_t max_value_; // maximum value of {cases_} |
| size_t value_range_; // |max_value - min_value| + 1 |
| BasicBlock* default_branch_; |
| }; |
| |
| // A helper class for the instruction selector that simplifies construction of |
| // Operands. This class implements a base for architecture-specific helpers. |
| class OperandGenerator { |
| public: |
| explicit OperandGenerator(InstructionSelector* selector) |
| : selector_(selector) {} |
| |
| InstructionOperand NoOutput() { |
| return InstructionOperand(); // Generates an invalid operand. |
| } |
| |
| InstructionOperand DefineAsRegister(Node* node) { |
| return Define(node, |
| UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER, |
| GetVReg(node))); |
| } |
| |
| InstructionOperand DefineSameAsFirst(Node* node) { |
| return Define(node, |
| UnallocatedOperand(UnallocatedOperand::SAME_AS_FIRST_INPUT, |
| GetVReg(node))); |
| } |
| |
| InstructionOperand DefineAsFixed(Node* node, Register reg) { |
| return Define(node, UnallocatedOperand(UnallocatedOperand::FIXED_REGISTER, |
| reg.code(), GetVReg(node))); |
| } |
| |
| template <typename FPRegType> |
| InstructionOperand DefineAsFixed(Node* node, FPRegType reg) { |
| return Define(node, |
| UnallocatedOperand(UnallocatedOperand::FIXED_FP_REGISTER, |
| reg.code(), GetVReg(node))); |
| } |
| |
| InstructionOperand DefineAsConstant(Node* node) { |
| selector()->MarkAsDefined(node); |
| int virtual_register = GetVReg(node); |
| sequence()->AddConstant(virtual_register, ToConstant(node)); |
| return ConstantOperand(virtual_register); |
| } |
| |
| InstructionOperand DefineAsLocation(Node* node, LinkageLocation location) { |
| return Define(node, ToUnallocatedOperand(location, GetVReg(node))); |
| } |
| |
| InstructionOperand DefineAsDualLocation(Node* node, |
| LinkageLocation primary_location, |
| LinkageLocation secondary_location) { |
| return Define(node, |
| ToDualLocationUnallocatedOperand( |
| primary_location, secondary_location, GetVReg(node))); |
| } |
| |
| InstructionOperand Use(Node* node) { |
| return Use(node, UnallocatedOperand(UnallocatedOperand::NONE, |
| UnallocatedOperand::USED_AT_START, |
| GetVReg(node))); |
| } |
| |
| InstructionOperand UseAnyAtEnd(Node* node) { |
| return Use(node, UnallocatedOperand(UnallocatedOperand::REGISTER_OR_SLOT, |
| UnallocatedOperand::USED_AT_END, |
| GetVReg(node))); |
| } |
| |
| InstructionOperand UseAny(Node* node) { |
| return Use(node, UnallocatedOperand(UnallocatedOperand::REGISTER_OR_SLOT, |
| UnallocatedOperand::USED_AT_START, |
| GetVReg(node))); |
| } |
| |
| InstructionOperand UseRegisterOrSlotOrConstant(Node* node) { |
| return Use(node, UnallocatedOperand( |
| UnallocatedOperand::REGISTER_OR_SLOT_OR_CONSTANT, |
| UnallocatedOperand::USED_AT_START, GetVReg(node))); |
| } |
| |
| InstructionOperand UseUniqueRegisterOrSlotOrConstant(Node* node) { |
| return Use(node, UnallocatedOperand( |
| UnallocatedOperand::REGISTER_OR_SLOT_OR_CONSTANT, |
| GetVReg(node))); |
| } |
| |
| InstructionOperand UseRegister(Node* node) { |
| return Use(node, UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER, |
| UnallocatedOperand::USED_AT_START, |
| GetVReg(node))); |
| } |
| |
| InstructionOperand UseUniqueSlot(Node* node) { |
| return Use(node, UnallocatedOperand(UnallocatedOperand::MUST_HAVE_SLOT, |
| GetVReg(node))); |
| } |
| |
| // Use register or operand for the node. If a register is chosen, it won't |
| // alias any temporary or output registers. |
| InstructionOperand UseUnique(Node* node) { |
| return Use(node, |
| UnallocatedOperand(UnallocatedOperand::NONE, GetVReg(node))); |
| } |
| |
| // Use a unique register for the node that does not alias any temporary or |
| // output registers. |
| InstructionOperand UseUniqueRegister(Node* node) { |
| return Use(node, UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER, |
| GetVReg(node))); |
| } |
| |
| InstructionOperand UseFixed(Node* node, Register reg) { |
| return Use(node, UnallocatedOperand(UnallocatedOperand::FIXED_REGISTER, |
| reg.code(), GetVReg(node))); |
| } |
| |
| template <typename FPRegType> |
| InstructionOperand UseFixed(Node* node, FPRegType reg) { |
| return Use(node, UnallocatedOperand(UnallocatedOperand::FIXED_FP_REGISTER, |
| reg.code(), GetVReg(node))); |
| } |
| |
| InstructionOperand UseExplicit(LinkageLocation location) { |
| MachineRepresentation rep = InstructionSequence::DefaultRepresentation(); |
| if (location.IsRegister()) { |
| return ExplicitOperand(LocationOperand::REGISTER, rep, |
| location.AsRegister()); |
| } else { |
| return ExplicitOperand(LocationOperand::STACK_SLOT, rep, |
| location.GetLocation()); |
| } |
| } |
| |
| InstructionOperand UseImmediate(int immediate) { |
| return sequence()->AddImmediate(Constant(immediate)); |
| } |
| |
| InstructionOperand UseImmediate(Node* node) { |
| return sequence()->AddImmediate(ToConstant(node)); |
| } |
| |
| InstructionOperand UseNegatedImmediate(Node* node) { |
| return sequence()->AddImmediate(ToNegatedConstant(node)); |
| } |
| |
| InstructionOperand UseLocation(Node* node, LinkageLocation location) { |
| return Use(node, ToUnallocatedOperand(location, GetVReg(node))); |
| } |
| |
| // Used to force gap moves from the from_location to the to_location |
| // immediately before an instruction. |
| InstructionOperand UsePointerLocation(LinkageLocation to_location, |
| LinkageLocation from_location) { |
| UnallocatedOperand casted_from_operand = |
| UnallocatedOperand::cast(TempLocation(from_location)); |
| selector_->Emit(kArchNop, casted_from_operand); |
| return ToUnallocatedOperand(to_location, |
| casted_from_operand.virtual_register()); |
| } |
| |
| InstructionOperand TempRegister() { |
| return UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER, |
| UnallocatedOperand::USED_AT_START, |
| sequence()->NextVirtualRegister()); |
| } |
| |
| int AllocateVirtualRegister() { return sequence()->NextVirtualRegister(); } |
| |
| InstructionOperand DefineSameAsFirstForVreg(int vreg) { |
| return UnallocatedOperand(UnallocatedOperand::SAME_AS_FIRST_INPUT, vreg); |
| } |
| |
| InstructionOperand DefineAsRegistertForVreg(int vreg) { |
| return UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER, vreg); |
| } |
| |
| InstructionOperand UseRegisterForVreg(int vreg) { |
| return UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER, |
| UnallocatedOperand::USED_AT_START, vreg); |
| } |
| |
| InstructionOperand TempDoubleRegister() { |
| UnallocatedOperand op = UnallocatedOperand( |
| UnallocatedOperand::MUST_HAVE_REGISTER, |
| UnallocatedOperand::USED_AT_START, sequence()->NextVirtualRegister()); |
| sequence()->MarkAsRepresentation(MachineRepresentation::kFloat64, |
| op.virtual_register()); |
| return op; |
| } |
| |
| InstructionOperand TempSimd128Register() { |
| UnallocatedOperand op = UnallocatedOperand( |
| UnallocatedOperand::MUST_HAVE_REGISTER, |
| UnallocatedOperand::USED_AT_START, sequence()->NextVirtualRegister()); |
| sequence()->MarkAsRepresentation(MachineRepresentation::kSimd128, |
| op.virtual_register()); |
| return op; |
| } |
| |
| InstructionOperand TempRegister(Register reg) { |
| return UnallocatedOperand(UnallocatedOperand::FIXED_REGISTER, reg.code(), |
| InstructionOperand::kInvalidVirtualRegister); |
| } |
| |
| InstructionOperand TempImmediate(int32_t imm) { |
| return sequence()->AddImmediate(Constant(imm)); |
| } |
| |
| InstructionOperand TempLocation(LinkageLocation location) { |
| return ToUnallocatedOperand(location, sequence()->NextVirtualRegister()); |
| } |
| |
| InstructionOperand Label(BasicBlock* block) { |
| return sequence()->AddImmediate( |
| Constant(RpoNumber::FromInt(block->rpo_number()))); |
| } |
| |
| protected: |
| InstructionSelector* selector() const { return selector_; } |
| InstructionSequence* sequence() const { return selector()->sequence(); } |
| Zone* zone() const { return selector()->instruction_zone(); } |
| |
| private: |
| int GetVReg(Node* node) const { return selector_->GetVirtualRegister(node); } |
| |
| static Constant ToConstant(const Node* node) { |
| switch (node->opcode()) { |
| case IrOpcode::kInt32Constant: |
| return Constant(OpParameter<int32_t>(node->op())); |
| case IrOpcode::kInt64Constant: |
| return Constant(OpParameter<int64_t>(node->op())); |
| case IrOpcode::kFloat32Constant: |
| return Constant(OpParameter<float>(node->op())); |
| case IrOpcode::kRelocatableInt32Constant: |
| case IrOpcode::kRelocatableInt64Constant: |
| return Constant(OpParameter<RelocatablePtrConstantInfo>(node->op())); |
| case IrOpcode::kFloat64Constant: |
| case IrOpcode::kNumberConstant: |
| return Constant(OpParameter<double>(node->op())); |
| case IrOpcode::kExternalConstant: |
| return Constant(OpParameter<ExternalReference>(node->op())); |
| case IrOpcode::kComment: { |
| // We cannot use {intptr_t} here, since the Constant constructor would |
| // be ambiguous on some architectures. |
| using ptrsize_int_t = |
| std::conditional<kSystemPointerSize == 8, int64_t, int32_t>::type; |
| return Constant(reinterpret_cast<ptrsize_int_t>( |
| OpParameter<const char*>(node->op()))); |
| } |
| case IrOpcode::kHeapConstant: |
| return Constant(HeapConstantOf(node->op())); |
| case IrOpcode::kCompressedHeapConstant: |
| return Constant(HeapConstantOf(node->op()), true); |
| case IrOpcode::kDelayedStringConstant: |
| return Constant(StringConstantBaseOf(node->op())); |
| case IrOpcode::kDeadValue: { |
| switch (DeadValueRepresentationOf(node->op())) { |
| case MachineRepresentation::kBit: |
| case MachineRepresentation::kWord32: |
| case MachineRepresentation::kTagged: |
| case MachineRepresentation::kTaggedSigned: |
| case MachineRepresentation::kTaggedPointer: |
| case MachineRepresentation::kCompressed: |
| case MachineRepresentation::kCompressedSigned: |
| case MachineRepresentation::kCompressedPointer: |
| return Constant(static_cast<int32_t>(0)); |
| case MachineRepresentation::kFloat64: |
| return Constant(static_cast<double>(0)); |
| case MachineRepresentation::kFloat32: |
| return Constant(static_cast<float>(0)); |
| default: |
| UNREACHABLE(); |
| } |
| break; |
| } |
| default: |
| break; |
| } |
| UNREACHABLE(); |
| } |
| |
| static Constant ToNegatedConstant(const Node* node) { |
| switch (node->opcode()) { |
| case IrOpcode::kInt32Constant: |
| return Constant(-OpParameter<int32_t>(node->op())); |
| case IrOpcode::kInt64Constant: |
| return Constant(-OpParameter<int64_t>(node->op())); |
| default: |
| break; |
| } |
| UNREACHABLE(); |
| } |
| |
| UnallocatedOperand Define(Node* node, UnallocatedOperand operand) { |
| DCHECK_NOT_NULL(node); |
| DCHECK_EQ(operand.virtual_register(), GetVReg(node)); |
| selector()->MarkAsDefined(node); |
| return operand; |
| } |
| |
| UnallocatedOperand Use(Node* node, UnallocatedOperand operand) { |
| DCHECK_NOT_NULL(node); |
| DCHECK_EQ(operand.virtual_register(), GetVReg(node)); |
| selector()->MarkAsUsed(node); |
| return operand; |
| } |
| |
| UnallocatedOperand ToDualLocationUnallocatedOperand( |
| LinkageLocation primary_location, LinkageLocation secondary_location, |
| int virtual_register) { |
| // We only support the primary location being a register and the secondary |
| // one a slot. |
| DCHECK(primary_location.IsRegister() && |
| secondary_location.IsCalleeFrameSlot()); |
| int reg_id = primary_location.AsRegister(); |
| int slot_id = secondary_location.AsCalleeFrameSlot(); |
| return UnallocatedOperand(reg_id, slot_id, virtual_register); |
| } |
| |
| UnallocatedOperand ToUnallocatedOperand(LinkageLocation location, |
| int virtual_register) { |
| if (location.IsAnyRegister()) { |
| // any machine register. |
| return UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER, |
| virtual_register); |
| } |
| if (location.IsCallerFrameSlot()) { |
| // a location on the caller frame. |
| return UnallocatedOperand(UnallocatedOperand::FIXED_SLOT, |
| location.AsCallerFrameSlot(), virtual_register); |
| } |
| if (location.IsCalleeFrameSlot()) { |
| // a spill location on this (callee) frame. |
| return UnallocatedOperand(UnallocatedOperand::FIXED_SLOT, |
| location.AsCalleeFrameSlot(), virtual_register); |
| } |
| // a fixed register. |
| if (IsFloatingPoint(location.GetType().representation())) { |
| return UnallocatedOperand(UnallocatedOperand::FIXED_FP_REGISTER, |
| location.AsRegister(), virtual_register); |
| } |
| return UnallocatedOperand(UnallocatedOperand::FIXED_REGISTER, |
| location.AsRegister(), virtual_register); |
| } |
| |
| InstructionSelector* selector_; |
| }; |
| |
| } // namespace compiler |
| } // namespace internal |
| } // namespace v8 |
| |
| #endif // V8_COMPILER_BACKEND_INSTRUCTION_SELECTOR_IMPL_H_ |