Commit bb5b3926 authored by jarin@chromium.org's avatar jarin@chromium.org

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
parent 73733bb3
......@@ -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<Object> 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);
}
}
......
......@@ -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();
......
......@@ -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<int>(node));
case IrOpcode::kPhi:
return OpParameter<MachineType>(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<LoadRepresentation>(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<MachineType>* types, Node* state_values) {
DCHECK(state_values->opcode() == IrOpcode::kStateValues);
int count = OpParameter<int>(state_values);
types->reserve(static_cast<size_t>(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<FrameStateCallInfo>(state);
int parameters = OpParameter<int>(state->InputAt(0));
int locals = OpParameter<int>(state->InputAt(1));
int stack = OpParameter<int>(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<int>(descriptor->locals_count()), locals->InputCount());
DCHECK_EQ(static_cast<int>(descriptor->stack_count()), stack->InputCount());
ZoneVector<MachineType> types(instruction_zone());
types.reserve(descriptor->GetSize());
OperandGenerator g(this);
size_t value_index = 0;
for (int i = 0; i < static_cast<int>(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<int>(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<int>(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());
}
......
......@@ -145,8 +145,11 @@ class InstructionSelector FINAL {
bool call_address_immediate);
FrameStateDescriptor* GetFrameStateDescriptor(Node* node);
void FillTypeVectorFromStateValues(ZoneVector<MachineType>* parameters,
Node* state_values);
void AddFrameStateInputs(Node* state, InstructionOperandVector* inputs,
FrameStateDescriptor* descriptor);
MachineType GetMachineType(Node* node);
// ===========================================================================
// ============= Architecture-specific graph covering methods. ===============
......
......@@ -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];
......
......@@ -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> 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<MachineType> types_;
FrameStateDescriptor* outer_state_;
MaybeHandle<JSFunction> jsfunction_;
};
......
......@@ -39,6 +39,7 @@ enum MachineType {
// Machine types.
kMachNone = 0,
kMachBool = kRepBit | kTypeBool,
kMachFloat32 = kRepFloat32 | kTypeNumber,
kMachFloat64 = kRepFloat64 | kTypeNumber,
kMachInt8 = kRepWord8 | kTypeInt32,
......
......@@ -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;
......
......@@ -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());
......
......@@ -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<HeapObject> 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());
......
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