From bb5b39260b139ae910e9c79a6a54ba7d5f8be0e7 Mon Sep 17 00:00:00 2001 From: "jarin@chromium.org" Date: Wed, 8 Oct 2014 08:47:29 +0000 Subject: [PATCH] Relax representation requirement in FrameStates. This change enables non-tagged representations in FrameStates. That allows us to run zlib with deoptimization support and have almost the same performance of the generated code (as the code with no deoptimization). Unfortunately, the frame states seem to confuse typer. As a consequence, we generate more representation changes, which in turn causes the scheduler to take a lot more time and memory (>4x). The added compiler time makes zlib with deopt be about 50% slower. BUG= R=titzer@chromium.org Review URL: https://codereview.chromium.org/614713002 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24454 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/compiler/code-generator.cc | 59 ++++++-- src/compiler/code-generator.h | 2 +- src/compiler/instruction-selector.cc | 154 ++++++++++++++++++++- src/compiler/instruction-selector.h | 3 + src/compiler/instruction.cc | 72 ++++++++++ src/compiler/instruction.h | 62 ++------- src/compiler/machine-type.h | 1 + src/compiler/simplified-lowering.cc | 29 ++-- .../compiler/instruction-selector-unittest.cc | 55 +++++--- .../compiler/instruction-selector-unittest.h | 8 ++ 10 files changed, 349 insertions(+), 96 deletions(-) diff --git a/src/compiler/code-generator.cc b/src/compiler/code-generator.cc index d68c92c..fc62119 100644 --- a/src/compiler/code-generator.cc +++ b/src/compiler/code-generator.cc @@ -270,7 +270,7 @@ void CodeGenerator::AddSafepointAndDeopt(Instruction* instr) { // by calls.) for (size_t i = 0; i < descriptor->GetSize(); i++) { InstructionOperand* op = instr->InputAt(frame_state_offset + 1 + i); - CHECK(op->IsStackSlot() || op->IsImmediate()); + CHECK(op->IsStackSlot() || op->IsDoubleStackSlot() || op->IsImmediate()); } #endif safepoints()->RecordLazyDeoptimizationIndex(deopt_state_id); @@ -296,7 +296,15 @@ FrameStateDescriptor* CodeGenerator::GetFrameStateDescriptor( return code()->GetFrameStateDescriptor(state_id); } -static InstructionOperand* OperandForFrameState( +struct OperandAndType { + OperandAndType(InstructionOperand* operand, MachineType type) + : operand_(operand), type_(type) {} + + InstructionOperand* operand_; + MachineType type_; +}; + +static OperandAndType TypedOperandForFrameState( FrameStateDescriptor* descriptor, Instruction* instr, size_t frame_state_offset, size_t index, OutputFrameStateCombine combine) { DCHECK(index < descriptor->GetSize(combine)); @@ -307,7 +315,8 @@ static InstructionOperand* OperandForFrameState( descriptor->GetSize(OutputFrameStateCombine::Ignore()); // If the index is past the existing stack items, return the output. if (index >= size_without_output) { - return instr->OutputAt(index - size_without_output); + return OperandAndType(instr->OutputAt(index - size_without_output), + kMachAnyTagged); } break; } @@ -316,11 +325,13 @@ static InstructionOperand* OperandForFrameState( descriptor->GetSize(combine) - 1 - combine.GetOffsetToPokeAt(); if (index >= index_from_top && index < index_from_top + instr->OutputCount()) { - return instr->OutputAt(index - index_from_top); + return OperandAndType(instr->OutputAt(index - index_from_top), + kMachAnyTagged); } break; } - return instr->InputAt(frame_state_offset + index); + return OperandAndType(instr->InputAt(frame_state_offset + index), + descriptor->GetType(index)); } @@ -356,9 +367,9 @@ void CodeGenerator::BuildTranslationForFrameStateDescriptor( frame_state_offset += descriptor->outer_state()->GetTotalSize(); for (size_t i = 0; i < descriptor->GetSize(state_combine); i++) { - InstructionOperand* op = OperandForFrameState( + OperandAndType op = TypedOperandForFrameState( descriptor, instr, frame_state_offset, i, state_combine); - AddTranslationForOperand(translation, instr, op); + AddTranslationForOperand(translation, instr, op.operand_, op.type_); } } @@ -387,15 +398,36 @@ int CodeGenerator::BuildTranslation(Instruction* instr, int pc_offset, void CodeGenerator::AddTranslationForOperand(Translation* translation, Instruction* instr, - InstructionOperand* op) { + InstructionOperand* op, + MachineType type) { if (op->IsStackSlot()) { - translation->StoreStackSlot(op->index()); + if (type == kMachBool || type == kMachInt32 || type == kMachInt8 || + type == kMachInt16) { + translation->StoreInt32StackSlot(op->index()); + } else if (type == kMachUint32) { + translation->StoreUint32StackSlot(op->index()); + } else if ((type & kRepMask) == kRepTagged) { + translation->StoreStackSlot(op->index()); + } else { + CHECK(false); + } } else if (op->IsDoubleStackSlot()) { + DCHECK((type & (kRepFloat32 | kRepFloat64)) != 0); translation->StoreDoubleStackSlot(op->index()); } else if (op->IsRegister()) { InstructionOperandConverter converter(this, instr); - translation->StoreRegister(converter.ToRegister(op)); + if (type == kMachBool || type == kMachInt32 || type == kMachInt8 || + type == kMachInt16) { + translation->StoreInt32Register(converter.ToRegister(op)); + } else if (type == kMachUint32) { + translation->StoreUint32Register(converter.ToRegister(op)); + } else if ((type & kRepMask) == kRepTagged) { + translation->StoreRegister(converter.ToRegister(op)); + } else { + CHECK(false); + } } else if (op->IsDoubleRegister()) { + DCHECK((type & (kRepFloat32 | kRepFloat64)) != 0); InstructionOperandConverter converter(this, instr); translation->StoreDoubleRegister(converter.ToDoubleRegister(op)); } else if (op->IsImmediate()) { @@ -404,22 +436,25 @@ void CodeGenerator::AddTranslationForOperand(Translation* translation, Handle constant_object; switch (constant.type()) { case Constant::kInt32: + DCHECK(type == kMachInt32 || type == kMachUint32); constant_object = isolate()->factory()->NewNumberFromInt(constant.ToInt32()); break; case Constant::kFloat64: + DCHECK(type == kMachFloat64 || type == kMachAnyTagged); constant_object = isolate()->factory()->NewNumber(constant.ToFloat64()); break; case Constant::kHeapObject: + DCHECK((type & kRepMask) == kRepTagged); constant_object = constant.ToHeapObject(); break; default: - UNREACHABLE(); + CHECK(false); } int literal_id = DefineDeoptimizationLiteral(constant_object); translation->StoreLiteral(literal_id); } else { - UNREACHABLE(); + CHECK(false); } } diff --git a/src/compiler/code-generator.h b/src/compiler/code-generator.h index ae93551..6bf96ac 100644 --- a/src/compiler/code-generator.h +++ b/src/compiler/code-generator.h @@ -96,7 +96,7 @@ class CodeGenerator FINAL : public GapResolver::Assembler { Translation* translation, size_t frame_state_offset, OutputFrameStateCombine state_combine); void AddTranslationForOperand(Translation* translation, Instruction* instr, - InstructionOperand* op); + InstructionOperand* op, MachineType type); void AddNopForSmiCodeInlining(); void EnsureSpaceForLazyDeopt(); void MarkLazyDeoptSite(); diff --git a/src/compiler/instruction-selector.cc b/src/compiler/instruction-selector.cc index c141259..791aa29 100644 --- a/src/compiler/instruction-selector.cc +++ b/src/compiler/instruction-selector.cc @@ -440,6 +440,126 @@ void InstructionSelector::VisitControl(BasicBlock* block) { } +MachineType InstructionSelector::GetMachineType(Node* node) { + DCHECK_NOT_NULL(schedule()->block(node)); // should only use scheduled nodes. + switch (node->opcode()) { + case IrOpcode::kStart: + case IrOpcode::kLoop: + case IrOpcode::kEnd: + case IrOpcode::kBranch: + case IrOpcode::kIfTrue: + case IrOpcode::kIfFalse: + case IrOpcode::kEffectPhi: + case IrOpcode::kMerge: + // No code needed for these graph artifacts. + return kMachNone; + case IrOpcode::kFinish: + return kMachAnyTagged; + case IrOpcode::kParameter: + return linkage()->GetParameterType(OpParameter(node)); + case IrOpcode::kPhi: + return OpParameter(node); + case IrOpcode::kProjection: + // TODO(jarin) Really project from outputs. + return kMachAnyTagged; + case IrOpcode::kInt32Constant: + return kMachInt32; + case IrOpcode::kInt64Constant: + return kMachInt64; + case IrOpcode::kExternalConstant: + return kMachPtr; + case IrOpcode::kFloat64Constant: + return kMachFloat64; + case IrOpcode::kHeapConstant: + case IrOpcode::kNumberConstant: + return kMachAnyTagged; + case IrOpcode::kCall: + return kMachAnyTagged; + case IrOpcode::kFrameState: + case IrOpcode::kStateValues: + return kMachNone; + case IrOpcode::kLoad: + return OpParameter(node); + case IrOpcode::kStore: + return kMachNone; + case IrOpcode::kWord32And: + case IrOpcode::kWord32Or: + case IrOpcode::kWord32Xor: + case IrOpcode::kWord32Shl: + case IrOpcode::kWord32Shr: + case IrOpcode::kWord32Sar: + case IrOpcode::kWord32Ror: + return kMachInt32; + case IrOpcode::kWord32Equal: + return kMachBool; + case IrOpcode::kWord64And: + case IrOpcode::kWord64Or: + case IrOpcode::kWord64Xor: + case IrOpcode::kWord64Shl: + case IrOpcode::kWord64Shr: + case IrOpcode::kWord64Sar: + case IrOpcode::kWord64Ror: + return kMachInt64; + case IrOpcode::kWord64Equal: + return kMachBool; + case IrOpcode::kInt32Add: + case IrOpcode::kInt32AddWithOverflow: + case IrOpcode::kInt32Sub: + case IrOpcode::kInt32SubWithOverflow: + case IrOpcode::kInt32Mul: + case IrOpcode::kInt32Div: + case IrOpcode::kInt32Mod: + return kMachInt32; + case IrOpcode::kInt32LessThan: + case IrOpcode::kInt32LessThanOrEqual: + case IrOpcode::kUint32LessThan: + case IrOpcode::kUint32LessThanOrEqual: + return kMachBool; + case IrOpcode::kInt64Add: + case IrOpcode::kInt64Sub: + case IrOpcode::kInt64Mul: + case IrOpcode::kInt64Div: + case IrOpcode::kInt64Mod: + return kMachInt64; + case IrOpcode::kInt64LessThan: + case IrOpcode::kInt64LessThanOrEqual: + return kMachBool; + case IrOpcode::kChangeFloat32ToFloat64: + case IrOpcode::kChangeInt32ToFloat64: + case IrOpcode::kChangeUint32ToFloat64: + return kMachFloat64; + case IrOpcode::kChangeFloat64ToInt32: + return kMachInt32; + case IrOpcode::kChangeFloat64ToUint32: + return kMachUint32; + case IrOpcode::kChangeInt32ToInt64: + return kMachInt64; + case IrOpcode::kChangeUint32ToUint64: + return kMachUint64; + case IrOpcode::kTruncateFloat64ToFloat32: + return kMachFloat32; + case IrOpcode::kTruncateFloat64ToInt32: + case IrOpcode::kTruncateInt64ToInt32: + return kMachInt32; + case IrOpcode::kFloat64Add: + case IrOpcode::kFloat64Sub: + case IrOpcode::kFloat64Mul: + case IrOpcode::kFloat64Div: + case IrOpcode::kFloat64Mod: + case IrOpcode::kFloat64Sqrt: + return kMachFloat64; + case IrOpcode::kFloat64Equal: + case IrOpcode::kFloat64LessThan: + case IrOpcode::kFloat64LessThanOrEqual: + return kMachBool; + default: + V8_Fatal(__FILE__, __LINE__, "Unexpected operator #%d:%s @ node #%d", + node->opcode(), node->op()->mnemonic(), node->id()); + } + return kMachNone; +} + + void InstructionSelector::VisitNode(Node* node) { DCHECK_NOT_NULL(schedule()->block(node)); // should only use scheduled nodes. SourcePosition source_position = source_positions_->GetSourcePosition(node); @@ -1010,11 +1130,23 @@ void InstructionSelector::VisitThrow(Node* value) { } +void InstructionSelector::FillTypeVectorFromStateValues( + ZoneVector* types, Node* state_values) { + DCHECK(state_values->opcode() == IrOpcode::kStateValues); + int count = OpParameter(state_values); + types->reserve(static_cast(count)); + for (int i = 0; i < count; i++) { + types->push_back(GetMachineType(state_values->InputAt(i))); + } +} + + FrameStateDescriptor* InstructionSelector::GetFrameStateDescriptor( Node* state) { DCHECK(state->opcode() == IrOpcode::kFrameState); DCHECK_EQ(5, state->InputCount()); FrameStateCallInfo state_info = OpParameter(state); + int parameters = OpParameter(state->InputAt(0)); int locals = OpParameter(state->InputAt(1)); int stack = OpParameter(state->InputAt(2)); @@ -1025,8 +1157,8 @@ FrameStateDescriptor* InstructionSelector::GetFrameStateDescriptor( outer_state = GetFrameStateDescriptor(outer_node); } - return new (instruction_zone()) - FrameStateDescriptor(state_info, parameters, locals, stack, outer_state); + return new (instruction_zone()) FrameStateDescriptor( + instruction_zone(), state_info, parameters, locals, stack, outer_state); } @@ -1066,19 +1198,31 @@ void InstructionSelector::AddFrameStateInputs( DCHECK_EQ(static_cast(descriptor->locals_count()), locals->InputCount()); DCHECK_EQ(static_cast(descriptor->stack_count()), stack->InputCount()); + ZoneVector types(instruction_zone()); + types.reserve(descriptor->GetSize()); + OperandGenerator g(this); + size_t value_index = 0; for (int i = 0; i < static_cast(descriptor->parameters_count()); i++) { - inputs->push_back(UseOrImmediate(&g, parameters->InputAt(i))); + Node* input_node = parameters->InputAt(i); + inputs->push_back(UseOrImmediate(&g, input_node)); + descriptor->SetType(value_index++, GetMachineType(input_node)); } if (descriptor->HasContext()) { inputs->push_back(UseOrImmediate(&g, context)); + descriptor->SetType(value_index++, kMachAnyTagged); } for (int i = 0; i < static_cast(descriptor->locals_count()); i++) { - inputs->push_back(UseOrImmediate(&g, locals->InputAt(i))); + Node* input_node = locals->InputAt(i); + inputs->push_back(UseOrImmediate(&g, input_node)); + descriptor->SetType(value_index++, GetMachineType(input_node)); } for (int i = 0; i < static_cast(descriptor->stack_count()); i++) { - inputs->push_back(UseOrImmediate(&g, stack->InputAt(i))); + Node* input_node = stack->InputAt(i); + inputs->push_back(UseOrImmediate(&g, input_node)); + descriptor->SetType(value_index++, GetMachineType(input_node)); } + DCHECK(value_index == descriptor->GetSize()); } diff --git a/src/compiler/instruction-selector.h b/src/compiler/instruction-selector.h index 264f737..9178262 100644 --- a/src/compiler/instruction-selector.h +++ b/src/compiler/instruction-selector.h @@ -145,8 +145,11 @@ class InstructionSelector FINAL { bool call_address_immediate); FrameStateDescriptor* GetFrameStateDescriptor(Node* node); + void FillTypeVectorFromStateValues(ZoneVector* parameters, + Node* state_values); void AddFrameStateInputs(Node* state, InstructionOperandVector* inputs, FrameStateDescriptor* descriptor); + MachineType GetMachineType(Node* node); // =========================================================================== // ============= Architecture-specific graph covering methods. =============== diff --git a/src/compiler/instruction.cc b/src/compiler/instruction.cc index e19a905..635a6bd 100644 --- a/src/compiler/instruction.cc +++ b/src/compiler/instruction.cc @@ -445,6 +445,78 @@ int InstructionSequence::GetFrameStateDescriptorCount() { } +FrameStateDescriptor::FrameStateDescriptor( + Zone* zone, const FrameStateCallInfo& state_info, size_t parameters_count, + size_t locals_count, size_t stack_count, FrameStateDescriptor* outer_state) + : type_(state_info.type()), + bailout_id_(state_info.bailout_id()), + frame_state_combine_(state_info.state_combine()), + parameters_count_(parameters_count), + locals_count_(locals_count), + stack_count_(stack_count), + types_(zone), + outer_state_(outer_state), + jsfunction_(state_info.jsfunction()) { + types_.resize(GetSize(), kMachNone); +} + +size_t FrameStateDescriptor::GetSize(OutputFrameStateCombine combine) const { + size_t size = parameters_count() + locals_count() + stack_count() + + (HasContext() ? 1 : 0); + switch (combine.kind()) { + case OutputFrameStateCombine::kPushOutput: + size += combine.GetPushCount(); + break; + case OutputFrameStateCombine::kPokeAt: + break; + } + return size; +} + + +size_t FrameStateDescriptor::GetTotalSize() const { + size_t total_size = 0; + for (const FrameStateDescriptor* iter = this; iter != NULL; + iter = iter->outer_state_) { + total_size += iter->GetSize(); + } + return total_size; +} + + +size_t FrameStateDescriptor::GetFrameCount() const { + size_t count = 0; + for (const FrameStateDescriptor* iter = this; iter != NULL; + iter = iter->outer_state_) { + ++count; + } + return count; +} + + +size_t FrameStateDescriptor::GetJSFrameCount() const { + size_t count = 0; + for (const FrameStateDescriptor* iter = this; iter != NULL; + iter = iter->outer_state_) { + if (iter->type_ == JS_FRAME) { + ++count; + } + } + return count; +} + + +MachineType FrameStateDescriptor::GetType(size_t index) const { + return types_[index]; +} + + +void FrameStateDescriptor::SetType(size_t index, MachineType type) { + DCHECK(index < GetSize()); + types_[index] = type; +} + + std::ostream& operator<<(std::ostream& os, const InstructionSequence& code) { for (size_t i = 0; i < code.immediates_.size(); ++i) { Constant constant = code.immediates_[i]; diff --git a/src/compiler/instruction.h b/src/compiler/instruction.h index 0fe0b0e..b769a1d 100644 --- a/src/compiler/instruction.h +++ b/src/compiler/instruction.h @@ -711,18 +711,10 @@ class Constant FINAL { class FrameStateDescriptor : public ZoneObject { public: - FrameStateDescriptor(const FrameStateCallInfo& state_info, + FrameStateDescriptor(Zone* zone, const FrameStateCallInfo& state_info, size_t parameters_count, size_t locals_count, size_t stack_count, - FrameStateDescriptor* outer_state = NULL) - : type_(state_info.type()), - bailout_id_(state_info.bailout_id()), - frame_state_combine_(state_info.state_combine()), - parameters_count_(parameters_count), - locals_count_(locals_count), - stack_count_(stack_count), - outer_state_(outer_state), - jsfunction_(state_info.jsfunction()) {} + FrameStateDescriptor* outer_state = NULL); FrameStateType type() const { return type_; } BailoutId bailout_id() const { return bailout_id_; } @@ -732,51 +724,16 @@ class FrameStateDescriptor : public ZoneObject { size_t stack_count() const { return stack_count_; } FrameStateDescriptor* outer_state() const { return outer_state_; } MaybeHandle jsfunction() const { return jsfunction_; } + bool HasContext() const { return type_ == JS_FRAME; } size_t GetSize(OutputFrameStateCombine combine = - OutputFrameStateCombine::Ignore()) const { - size_t size = parameters_count_ + locals_count_ + stack_count_ + - (HasContext() ? 1 : 0); - switch (combine.kind()) { - case OutputFrameStateCombine::kPushOutput: - size += combine.GetPushCount(); - break; - case OutputFrameStateCombine::kPokeAt: - break; - } - return size; - } + OutputFrameStateCombine::Ignore()) const; + size_t GetTotalSize() const; + size_t GetFrameCount() const; + size_t GetJSFrameCount() const; - size_t GetTotalSize() const { - size_t total_size = 0; - for (const FrameStateDescriptor* iter = this; iter != NULL; - iter = iter->outer_state_) { - total_size += iter->GetSize(); - } - return total_size; - } - - size_t GetFrameCount() const { - size_t count = 0; - for (const FrameStateDescriptor* iter = this; iter != NULL; - iter = iter->outer_state_) { - ++count; - } - return count; - } - - size_t GetJSFrameCount() const { - size_t count = 0; - for (const FrameStateDescriptor* iter = this; iter != NULL; - iter = iter->outer_state_) { - if (iter->type_ == JS_FRAME) { - ++count; - } - } - return count; - } - - bool HasContext() const { return type_ == JS_FRAME; } + MachineType GetType(size_t index) const; + void SetType(size_t index, MachineType type); private: FrameStateType type_; @@ -785,6 +742,7 @@ class FrameStateDescriptor : public ZoneObject { size_t parameters_count_; size_t locals_count_; size_t stack_count_; + ZoneVector types_; FrameStateDescriptor* outer_state_; MaybeHandle jsfunction_; }; diff --git a/src/compiler/machine-type.h b/src/compiler/machine-type.h index 059b058..12fdd11 100644 --- a/src/compiler/machine-type.h +++ b/src/compiler/machine-type.h @@ -39,6 +39,7 @@ enum MachineType { // Machine types. kMachNone = 0, + kMachBool = kRepBit | kTypeBool, kMachFloat32 = kRepFloat32 | kTypeNumber, kMachFloat64 = kRepFloat64 | kTypeNumber, kMachInt8 = kRepWord8 | kTypeInt32, diff --git a/src/compiler/simplified-lowering.cc b/src/compiler/simplified-lowering.cc index 74463a9..4619d25 100644 --- a/src/compiler/simplified-lowering.cc +++ b/src/compiler/simplified-lowering.cc @@ -284,7 +284,8 @@ class RepresentationSelector { // Phis adapt to whatever output representation their uses demand, // pushing representation changes to their inputs. MachineTypeUnion use_rep = GetUseInfo(node) & kRepMask; - MachineTypeUnion use_type = GetUseInfo(node) & kTypeMask; + Type* upper = NodeProperties::GetBounds(node).upper; + MachineTypeUnion phi_type = changer_->TypeFromUpperBound(upper); MachineTypeUnion rep = 0; if (use_rep & kRepTagged) { rep = kRepTagged; // Tagged overrides everything. @@ -295,29 +296,31 @@ class RepresentationSelector { } else if (use_rep & kRepWord64) { rep = kRepWord64; } else if (use_rep & kRepWord32) { - rep = kRepWord32; + if (phi_type & kTypeNumber) { + rep = kRepFloat64; + } else { + rep = kRepWord32; + } } else if (use_rep & kRepBit) { rep = kRepBit; } else { // There was no representation associated with any of the uses. - // TODO(titzer): Select the best rep using phi's type, not the usage type? - if (use_type & kTypeAny) { + if (phi_type & kTypeAny) { rep = kRepTagged; - } else if (use_type & kTypeNumber) { + } else if (phi_type & kTypeNumber) { rep = kRepFloat64; - } else if (use_type & kTypeInt64 || use_type & kTypeUint64) { + } else if (phi_type & kTypeInt64 || phi_type & kTypeUint64) { rep = kRepWord64; - } else if (use_type & kTypeInt32 || use_type & kTypeUint32) { + } else if (phi_type & kTypeInt32 || phi_type & kTypeUint32) { rep = kRepWord32; - } else if (use_type & kTypeBool) { + } else if (phi_type & kTypeBool) { rep = kRepBit; } else { UNREACHABLE(); // should have at least a usage type! } } // Preserve the usage type, but set the representation. - Type* upper = NodeProperties::GetBounds(node).upper; - MachineTypeUnion output_type = rep | changer_->TypeFromUpperBound(upper); + MachineTypeUnion output_type = rep | phi_type; SetOutput(node, output_type); if (lower()) { @@ -728,6 +731,12 @@ class RepresentationSelector { return VisitFloat64Cmp(node); case IrOpcode::kLoadStackPointer: return VisitLeaf(node, kMachPtr); + case IrOpcode::kStateValues: + for (int i = 0; i < node->InputCount(); i++) { + ProcessInput(node, i, kTypeAny); + } + SetOutput(node, kMachAnyTagged); + break; default: VisitInputs(node); break; diff --git a/test/unittests/compiler/instruction-selector-unittest.cc b/test/unittests/compiler/instruction-selector-unittest.cc index 8b759de..5bbf0ce 100644 --- a/test/unittests/compiler/instruction-selector-unittest.cc +++ b/test/unittests/compiler/instruction-selector-unittest.cc @@ -375,8 +375,8 @@ TARGET_TEST_F(InstructionSelectorTest, CallFunctionStubWithDeopt) { // Build frame state for the state before the call. Node* parameters = m.NewNode(m.common()->StateValues(1), m.Int32Constant(43)); - Node* locals = m.NewNode(m.common()->StateValues(1), m.Int32Constant(44)); - Node* stack = m.NewNode(m.common()->StateValues(1), m.Int32Constant(45)); + Node* locals = m.NewNode(m.common()->StateValues(1), m.Float64Constant(0.5)); + Node* stack = m.NewNode(m.common()->StateValues(1), m.UndefinedConstant()); Node* context_sentinel = m.Int32Constant(0); Node* frame_state_before = m.NewNode( @@ -425,9 +425,15 @@ TARGET_TEST_F(InstructionSelectorTest, CallFunctionStubWithDeopt) { EXPECT_EQ(1u, desc_before->locals_count()); EXPECT_EQ(1u, desc_before->stack_count()); EXPECT_EQ(43, s.ToInt32(call_instr->InputAt(2))); - EXPECT_EQ(0, s.ToInt32(call_instr->InputAt(3))); - EXPECT_EQ(44, s.ToInt32(call_instr->InputAt(4))); - EXPECT_EQ(45, s.ToInt32(call_instr->InputAt(5))); + EXPECT_EQ(0, s.ToInt32(call_instr->InputAt(3))); // This should be a context. + // We inserted 0 here. + EXPECT_EQ(0.5, s.ToFloat64(call_instr->InputAt(4))); + EXPECT_TRUE(s.ToHeapObject(call_instr->InputAt(5))->IsUndefined()); + EXPECT_EQ(kMachInt32, desc_before->GetType(0)); + EXPECT_EQ(kMachAnyTagged, desc_before->GetType(1)); // context is always + // tagged/any. + EXPECT_EQ(kMachFloat64, desc_before->GetType(2)); + EXPECT_EQ(kMachAnyTagged, desc_before->GetType(3)); // Function. EXPECT_EQ(s.ToVreg(function_node), s.ToVreg(call_instr->InputAt(6))); @@ -465,8 +471,10 @@ TARGET_TEST_F(InstructionSelectorTest, Node* context2 = m.Int32Constant(46); Node* parameters2 = m.NewNode(m.common()->StateValues(1), m.Int32Constant(43)); - Node* locals2 = m.NewNode(m.common()->StateValues(1), m.Int32Constant(44)); - Node* stack2 = m.NewNode(m.common()->StateValues(1), m.Int32Constant(45)); + Node* locals2 = + m.NewNode(m.common()->StateValues(1), m.Float64Constant(0.25)); + Node* stack2 = m.NewNode(m.common()->StateValues(2), m.Int32Constant(44), + m.Int32Constant(45)); Node* frame_state_before = m.NewNode(m.common()->FrameState(JS_FRAME, bailout_id_before, OutputFrameStateCombine::Push()), @@ -494,7 +502,7 @@ TARGET_TEST_F(InstructionSelectorTest, size_t num_operands = 1 + // Code object. 1 + // Frame state deopt id - 4 + // One input for each value in frame state + context. + 5 + // One input for each value in frame state + context. 4 + // One input for each value in the parent frame state + context. 1 + // Function. 1; // Context. @@ -506,25 +514,40 @@ TARGET_TEST_F(InstructionSelectorTest, int32_t deopt_id_before = s.ToInt32(call_instr->InputAt(1)); FrameStateDescriptor* desc_before = s.GetFrameStateDescriptor(deopt_id_before); + FrameStateDescriptor* desc_before_outer = desc_before->outer_state(); EXPECT_EQ(bailout_id_before, desc_before->bailout_id()); - EXPECT_EQ(1u, desc_before->parameters_count()); - EXPECT_EQ(1u, desc_before->locals_count()); - EXPECT_EQ(1u, desc_before->stack_count()); + EXPECT_EQ(1u, desc_before_outer->parameters_count()); + EXPECT_EQ(1u, desc_before_outer->locals_count()); + EXPECT_EQ(1u, desc_before_outer->stack_count()); + // Values from parent environment. EXPECT_EQ(63, s.ToInt32(call_instr->InputAt(2))); + EXPECT_EQ(kMachInt32, desc_before_outer->GetType(0)); // Context: EXPECT_EQ(66, s.ToInt32(call_instr->InputAt(3))); + EXPECT_EQ(kMachAnyTagged, desc_before_outer->GetType(1)); EXPECT_EQ(64, s.ToInt32(call_instr->InputAt(4))); + EXPECT_EQ(kMachInt32, desc_before_outer->GetType(2)); EXPECT_EQ(65, s.ToInt32(call_instr->InputAt(5))); - // Values from parent environment should follow. + EXPECT_EQ(kMachInt32, desc_before_outer->GetType(3)); + // Values from the nested frame. + EXPECT_EQ(1u, desc_before->parameters_count()); + EXPECT_EQ(1u, desc_before->locals_count()); + EXPECT_EQ(2u, desc_before->stack_count()); EXPECT_EQ(43, s.ToInt32(call_instr->InputAt(6))); + EXPECT_EQ(kMachInt32, desc_before->GetType(0)); EXPECT_EQ(46, s.ToInt32(call_instr->InputAt(7))); - EXPECT_EQ(44, s.ToInt32(call_instr->InputAt(8))); - EXPECT_EQ(45, s.ToInt32(call_instr->InputAt(9))); + EXPECT_EQ(kMachAnyTagged, desc_before->GetType(1)); + EXPECT_EQ(0.25, s.ToFloat64(call_instr->InputAt(8))); + EXPECT_EQ(kMachFloat64, desc_before->GetType(2)); + EXPECT_EQ(44, s.ToInt32(call_instr->InputAt(9))); + EXPECT_EQ(kMachInt32, desc_before->GetType(3)); + EXPECT_EQ(45, s.ToInt32(call_instr->InputAt(10))); + EXPECT_EQ(kMachInt32, desc_before->GetType(4)); // Function. - EXPECT_EQ(s.ToVreg(function_node), s.ToVreg(call_instr->InputAt(10))); + EXPECT_EQ(s.ToVreg(function_node), s.ToVreg(call_instr->InputAt(11))); // Context. - EXPECT_EQ(s.ToVreg(context2), s.ToVreg(call_instr->InputAt(11))); + EXPECT_EQ(s.ToVreg(context2), s.ToVreg(call_instr->InputAt(12))); // Continuation. EXPECT_EQ(kArchRet, s[index++]->arch_opcode()); diff --git a/test/unittests/compiler/instruction-selector-unittest.h b/test/unittests/compiler/instruction-selector-unittest.h index d48df12..ee5f610 100644 --- a/test/unittests/compiler/instruction-selector-unittest.h +++ b/test/unittests/compiler/instruction-selector-unittest.h @@ -146,6 +146,10 @@ class InstructionSelectorTest : public TestWithContext, public TestWithZone { return ToConstant(operand).ToFloat32(); } + double ToFloat64(const InstructionOperand* operand) const { + return ToConstant(operand).ToFloat64(); + } + int32_t ToInt32(const InstructionOperand* operand) const { return ToConstant(operand).ToInt32(); } @@ -154,6 +158,10 @@ class InstructionSelectorTest : public TestWithContext, public TestWithZone { return ToConstant(operand).ToInt64(); } + Handle ToHeapObject(const InstructionOperand* operand) const { + return ToConstant(operand).ToHeapObject(); + } + int ToVreg(const InstructionOperand* operand) const { if (operand->IsConstant()) return operand->index(); EXPECT_EQ(InstructionOperand::UNALLOCATED, operand->kind()); -- 2.7.4