Commit 4926be6e authored by oth's avatar oth Committed by Commit bot

[Interpreter] Implement ForIn in bytecode graph builder.

A pre-requisite for this change was changing the interpreter to use
Runtime::ForInStep to bring the interpreter implementation closer
to the turbofan implementation. Also required to flatten out the
cache parameters into the interpreter frame for de-opt.

BUG=v8:4280
LOG=N

Review URL: https://codereview.chromium.org/1531693002

Cr-Commit-Position: refs/heads/master@{#32986}
parent 8bfb7189
...@@ -1394,15 +1394,15 @@ void BytecodeGraphBuilder::VisitToName( ...@@ -1394,15 +1394,15 @@ void BytecodeGraphBuilder::VisitToName(
} }
void BytecodeGraphBuilder::VisitToNumber( void BytecodeGraphBuilder::VisitToObject(
const interpreter::BytecodeArrayIterator& iterator) { const interpreter::BytecodeArrayIterator& iterator) {
BuildCastOperator(javascript()->ToNumber(), iterator); BuildCastOperator(javascript()->ToObject(), iterator);
} }
void BytecodeGraphBuilder::VisitToObject( void BytecodeGraphBuilder::VisitToNumber(
const interpreter::BytecodeArrayIterator& iterator) { const interpreter::BytecodeArrayIterator& iterator) {
BuildCastOperator(javascript()->ToObject(), iterator); BuildCastOperator(javascript()->ToNumber(), iterator);
} }
...@@ -1513,19 +1513,55 @@ void BytecodeGraphBuilder::VisitReturn( ...@@ -1513,19 +1513,55 @@ void BytecodeGraphBuilder::VisitReturn(
void BytecodeGraphBuilder::VisitForInPrepare( void BytecodeGraphBuilder::VisitForInPrepare(
const interpreter::BytecodeArrayIterator& iterator) { const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED(); Node* prepare = nullptr;
{
FrameStateBeforeAndAfter states(this, iterator);
Node* receiver = environment()->LookupAccumulator();
prepare = NewNode(javascript()->ForInPrepare(), receiver);
environment()->RecordAfterState(prepare, &states);
}
// Project cache_type, cache_array, cache_length into register
// operands 1, 2, 3.
for (int i = 0; i < 3; i++) {
environment()->BindRegister(iterator.GetRegisterOperand(i),
NewNode(common()->Projection(i), prepare));
}
}
void BytecodeGraphBuilder::VisitForInDone(
const interpreter::BytecodeArrayIterator& iterator) {
FrameStateBeforeAndAfter states(this, iterator);
Node* index = environment()->LookupRegister(iterator.GetRegisterOperand(0));
Node* cache_length =
environment()->LookupRegister(iterator.GetRegisterOperand(1));
Node* exit_cond = NewNode(javascript()->ForInDone(), index, cache_length);
environment()->BindAccumulator(exit_cond, &states);
} }
void BytecodeGraphBuilder::VisitForInNext( void BytecodeGraphBuilder::VisitForInNext(
const interpreter::BytecodeArrayIterator& iterator) { const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED(); FrameStateBeforeAndAfter states(this, iterator);
Node* receiver =
environment()->LookupRegister(iterator.GetRegisterOperand(0));
Node* cache_type =
environment()->LookupRegister(iterator.GetRegisterOperand(1));
Node* cache_array =
environment()->LookupRegister(iterator.GetRegisterOperand(2));
Node* index = environment()->LookupRegister(iterator.GetRegisterOperand(3));
Node* value = NewNode(javascript()->ForInNext(), receiver, cache_array,
cache_type, index);
environment()->BindAccumulator(value, &states);
} }
void BytecodeGraphBuilder::VisitForInDone( void BytecodeGraphBuilder::VisitForInStep(
const interpreter::BytecodeArrayIterator& iterator) { const interpreter::BytecodeArrayIterator& iterator) {
UNIMPLEMENTED(); FrameStateBeforeAndAfter states(this, iterator);
Node* index = environment()->LookupRegister(iterator.GetRegisterOperand(0));
index = NewNode(javascript()->ForInStep(), index);
environment()->BindAccumulator(index, &states);
} }
......
...@@ -71,6 +71,7 @@ BytecodeArrayBuilder::BytecodeArrayBuilder(Isolate* isolate, Zone* zone) ...@@ -71,6 +71,7 @@ BytecodeArrayBuilder::BytecodeArrayBuilder(Isolate* isolate, Zone* zone)
last_block_end_(0), last_block_end_(0),
last_bytecode_start_(~0), last_bytecode_start_(~0),
exit_seen_in_block_(false), exit_seen_in_block_(false),
unbound_jumps_(0),
constants_map_(isolate->heap(), zone), constants_map_(isolate->heap(), zone),
constants_(zone), constants_(zone),
parameter_count_(-1), parameter_count_(-1),
...@@ -80,6 +81,9 @@ BytecodeArrayBuilder::BytecodeArrayBuilder(Isolate* isolate, Zone* zone) ...@@ -80,6 +81,9 @@ BytecodeArrayBuilder::BytecodeArrayBuilder(Isolate* isolate, Zone* zone)
free_temporaries_(zone) {} free_temporaries_(zone) {}
BytecodeArrayBuilder::~BytecodeArrayBuilder() { DCHECK_EQ(0, unbound_jumps_); }
void BytecodeArrayBuilder::set_locals_count(int number_of_locals) { void BytecodeArrayBuilder::set_locals_count(int number_of_locals) {
local_register_count_ = number_of_locals; local_register_count_ = number_of_locals;
DCHECK_LE(context_register_count_, 0); DCHECK_LE(context_register_count_, 0);
...@@ -782,6 +786,7 @@ void BytecodeArrayBuilder::PatchJump( ...@@ -782,6 +786,7 @@ void BytecodeArrayBuilder::PatchJump(
UNIMPLEMENTED(); UNIMPLEMENTED();
} }
} }
unbound_jumps_--;
} }
...@@ -826,6 +831,7 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::OutputJump(Bytecode jump_bytecode, ...@@ -826,6 +831,7 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::OutputJump(Bytecode jump_bytecode,
// that will be patched when the label is bound. // that will be patched when the label is bound.
label->set_referrer(bytecodes()->size()); label->set_referrer(bytecodes()->size());
delta = 0; delta = 0;
unbound_jumps_++;
} }
if (FitsInImm8Operand(delta)) { if (FitsInImm8Operand(delta)) {
...@@ -884,21 +890,33 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::Return() { ...@@ -884,21 +890,33 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::Return() {
} }
BytecodeArrayBuilder& BytecodeArrayBuilder::ForInPrepare(Register receiver) { BytecodeArrayBuilder& BytecodeArrayBuilder::ForInPrepare(
Output(Bytecode::kForInPrepare, receiver.ToOperand()); Register cache_type, Register cache_array, Register cache_length) {
Output(Bytecode::kForInPrepare, cache_type.ToOperand(),
cache_array.ToOperand(), cache_length.ToOperand());
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::ForInDone(Register index,
Register cache_length) {
Output(Bytecode::kForInDone, index.ToOperand(), cache_length.ToOperand());
return *this; return *this;
} }
BytecodeArrayBuilder& BytecodeArrayBuilder::ForInNext(Register for_in_state, BytecodeArrayBuilder& BytecodeArrayBuilder::ForInNext(Register receiver,
Register cache_type,
Register cache_array,
Register index) { Register index) {
Output(Bytecode::kForInNext, for_in_state.ToOperand(), index.ToOperand()); Output(Bytecode::kForInNext, receiver.ToOperand(), cache_type.ToOperand(),
cache_array.ToOperand(), index.ToOperand());
return *this; return *this;
} }
BytecodeArrayBuilder& BytecodeArrayBuilder::ForInDone(Register for_in_state) { BytecodeArrayBuilder& BytecodeArrayBuilder::ForInStep(Register index) {
Output(Bytecode::kForInDone, for_in_state.ToOperand()); Output(Bytecode::kForInStep, index.ToOperand());
return *this; return *this;
} }
......
...@@ -27,9 +27,11 @@ class Register; ...@@ -27,9 +27,11 @@ class Register;
// when rest parameters implementation has settled down. // when rest parameters implementation has settled down.
enum class CreateArgumentsType { kMappedArguments, kUnmappedArguments }; enum class CreateArgumentsType { kMappedArguments, kUnmappedArguments };
class BytecodeArrayBuilder { class BytecodeArrayBuilder final {
public: public:
BytecodeArrayBuilder(Isolate* isolate, Zone* zone); BytecodeArrayBuilder(Isolate* isolate, Zone* zone);
~BytecodeArrayBuilder();
Handle<BytecodeArray> ToBytecodeArray(); Handle<BytecodeArray> ToBytecodeArray();
// Set the number of parameters expected by function. // Set the number of parameters expected by function.
...@@ -211,9 +213,12 @@ class BytecodeArrayBuilder { ...@@ -211,9 +213,12 @@ class BytecodeArrayBuilder {
BytecodeArrayBuilder& Return(); BytecodeArrayBuilder& Return();
// Complex flow control. // Complex flow control.
BytecodeArrayBuilder& ForInPrepare(Register receiver); BytecodeArrayBuilder& ForInPrepare(Register cache_type, Register cache_array,
BytecodeArrayBuilder& ForInNext(Register for_in_state, Register index); Register cache_length);
BytecodeArrayBuilder& ForInDone(Register for_in_state); BytecodeArrayBuilder& ForInDone(Register index, Register cache_length);
BytecodeArrayBuilder& ForInNext(Register receiver, Register cache_type,
Register cache_array, Register index);
BytecodeArrayBuilder& ForInStep(Register index);
// Accessors // Accessors
Zone* zone() const { return zone_; } Zone* zone() const { return zone_; }
...@@ -288,6 +293,7 @@ class BytecodeArrayBuilder { ...@@ -288,6 +293,7 @@ class BytecodeArrayBuilder {
size_t last_block_end_; size_t last_block_end_;
size_t last_bytecode_start_; size_t last_bytecode_start_;
bool exit_seen_in_block_; bool exit_seen_in_block_;
int unbound_jumps_;
IdentityMap<size_t> constants_map_; IdentityMap<size_t> constants_map_;
ZoneVector<Handle<Object>> constants_; ZoneVector<Handle<Object>> constants_;
......
...@@ -855,7 +855,6 @@ void BytecodeGenerator::VisitForInAssignment(Expression* expr, ...@@ -855,7 +855,6 @@ void BytecodeGenerator::VisitForInAssignment(Expression* expr,
void BytecodeGenerator::VisitForInStatement(ForInStatement* stmt) { void BytecodeGenerator::VisitForInStatement(ForInStatement* stmt) {
EffectResultScope statement_result_scope(this); EffectResultScope statement_result_scope(this);
if (stmt->subject()->IsNullLiteral() || if (stmt->subject()->IsNullLiteral() ||
stmt->subject()->IsUndefinedLiteral(isolate())) { stmt->subject()->IsUndefinedLiteral(isolate())) {
// ForIn generates lots of code, skip if it wouldn't produce any effects. // ForIn generates lots of code, skip if it wouldn't produce any effects.
...@@ -864,41 +863,43 @@ void BytecodeGenerator::VisitForInStatement(ForInStatement* stmt) { ...@@ -864,41 +863,43 @@ void BytecodeGenerator::VisitForInStatement(ForInStatement* stmt) {
LoopBuilder loop_builder(builder()); LoopBuilder loop_builder(builder());
ControlScopeForIteration control_scope(this, stmt, &loop_builder); ControlScopeForIteration control_scope(this, stmt, &loop_builder);
BytecodeLabel subject_null_label, subject_undefined_label, not_object_label;
// Prepare the state for executing ForIn. // Prepare the state for executing ForIn.
VisitForAccumulatorValue(stmt->subject()); VisitForAccumulatorValue(stmt->subject());
loop_builder.BreakIfUndefined(); builder()->JumpIfUndefined(&subject_undefined_label);
loop_builder.BreakIfNull(); builder()->JumpIfNull(&subject_null_label);
Register receiver = execution_result()->NewRegister(); Register receiver = execution_result()->NewRegister();
builder()->CastAccumulatorToJSObject(); builder()->CastAccumulatorToJSObject();
builder()->JumpIfNull(&not_object_label);
builder()->StoreAccumulatorInRegister(receiver); builder()->StoreAccumulatorInRegister(receiver);
builder()->CallRuntime(Runtime::kGetPropertyNamesFast, receiver, 1); Register cache_type = execution_result()->NewRegister();
builder()->ForInPrepare(receiver); Register cache_array = execution_result()->NewRegister();
loop_builder.BreakIfUndefined(); Register cache_length = execution_result()->NewRegister();
builder()->ForInPrepare(cache_type, cache_array, cache_length);
Register for_in_state = execution_result()->NewRegister();
builder()->StoreAccumulatorInRegister(for_in_state);
// Check loop termination (accumulator holds index). // Set up loop counter
Register index = receiver; // Re-using register as receiver no longer used. Register index = execution_result()->NewRegister();
builder()->LoadLiteral(Smi::FromInt(0)); builder()->LoadLiteral(Smi::FromInt(0));
builder()->StoreAccumulatorInRegister(index);
// The loop
loop_builder.LoopHeader(); loop_builder.LoopHeader();
loop_builder.Condition(); loop_builder.Condition();
builder()->StoreAccumulatorInRegister(index).ForInDone(for_in_state); builder()->ForInDone(index, cache_length);
loop_builder.BreakIfTrue(); loop_builder.BreakIfTrue();
builder()->ForInNext(for_in_state, index); builder()->ForInNext(receiver, cache_type, cache_array, index);
loop_builder.ContinueIfUndefined(); loop_builder.ContinueIfUndefined();
VisitForInAssignment(stmt->each(), stmt->EachFeedbackSlot()); VisitForInAssignment(stmt->each(), stmt->EachFeedbackSlot());
Visit(stmt->body()); Visit(stmt->body());
// TODO(oth): replace CountOperation here with ForInStep.
loop_builder.Next(); loop_builder.Next();
builder()->LoadAccumulatorWithRegister(index).CountOperation( builder()->ForInStep(index);
Token::Value::ADD, language_mode_strength()); builder()->StoreAccumulatorInRegister(index);
loop_builder.JumpToHeader(); loop_builder.JumpToHeader();
loop_builder.EndLoop(); loop_builder.EndLoop();
builder()->Bind(&not_object_label);
builder()->Bind(&subject_null_label);
builder()->Bind(&subject_undefined_label);
} }
......
...@@ -199,9 +199,11 @@ namespace interpreter { ...@@ -199,9 +199,11 @@ namespace interpreter {
V(JumpIfUndefinedConstant, OperandType::kIdx8) \ V(JumpIfUndefinedConstant, OperandType::kIdx8) \
\ \
/* Complex flow control For..in */ \ /* Complex flow control For..in */ \
V(ForInPrepare, OperandType::kReg8) \ V(ForInPrepare, OperandType::kReg8, OperandType::kReg8, OperandType::kReg8) \
V(ForInNext, OperandType::kReg8, OperandType::kReg8) \ V(ForInDone, OperandType::kReg8, OperandType::kReg8) \
V(ForInDone, OperandType::kReg8) \ V(ForInNext, OperandType::kReg8, OperandType::kReg8, OperandType::kReg8, \
OperandType::kReg8) \
V(ForInStep, OperandType::kReg8) \
\ \
/* Non-local flow control */ \ /* Non-local flow control */ \
V(Throw, OperandType::kNone) \ V(Throw, OperandType::kNone) \
......
...@@ -90,6 +90,16 @@ void BlockBuilder::EndBlock() { ...@@ -90,6 +90,16 @@ void BlockBuilder::EndBlock() {
LoopBuilder::~LoopBuilder() { DCHECK(continue_sites_.empty()); } LoopBuilder::~LoopBuilder() { DCHECK(continue_sites_.empty()); }
void LoopBuilder::LoopHeader() {
// Jumps from before the loop header into the loop violate ordering
// requirements of bytecode basic blocks. The only entry into a loop
// must be the loop header. Surely breaks is okay? Not if nested
// and misplaced between the headers.
DCHECK(break_sites_.empty() && continue_sites_.empty());
builder()->Bind(&loop_header_);
}
void LoopBuilder::EndLoop() { void LoopBuilder::EndLoop() {
// Loop must have closed form, i.e. all loop elements are within the loop, // Loop must have closed form, i.e. all loop elements are within the loop,
// the loop header precedes the body and next elements in the loop. // the loop header precedes the body and next elements in the loop.
......
...@@ -60,7 +60,6 @@ class BreakableControlFlowBuilder : public ControlFlowBuilder { ...@@ -60,7 +60,6 @@ class BreakableControlFlowBuilder : public ControlFlowBuilder {
void BindLabels(const BytecodeLabel& target, ZoneVector<BytecodeLabel>* site); void BindLabels(const BytecodeLabel& target, ZoneVector<BytecodeLabel>* site);
private:
// Unbound labels that identify jumps for break statements in the code. // Unbound labels that identify jumps for break statements in the code.
ZoneVector<BytecodeLabel> break_sites_; ZoneVector<BytecodeLabel> break_sites_;
}; };
...@@ -88,7 +87,7 @@ class LoopBuilder final : public BreakableControlFlowBuilder { ...@@ -88,7 +87,7 @@ class LoopBuilder final : public BreakableControlFlowBuilder {
continue_sites_(builder->zone()) {} continue_sites_(builder->zone()) {}
~LoopBuilder(); ~LoopBuilder();
void LoopHeader() { builder()->Bind(&loop_header_); } void LoopHeader();
void Condition() { builder()->Bind(&condition_); } void Condition() { builder()->Bind(&condition_); }
void Next() { builder()->Bind(&next_); } void Next() { builder()->Bind(&next_); }
void JumpToHeader() { builder()->Jump(&loop_header_); } void JumpToHeader() { builder()->Jump(&loop_header_); }
......
...@@ -1516,33 +1516,36 @@ void Interpreter::DoReturn(compiler::InterpreterAssembler* assembler) { ...@@ -1516,33 +1516,36 @@ void Interpreter::DoReturn(compiler::InterpreterAssembler* assembler) {
} }
// ForInPrepare <receiver> // ForInPrepare <cache_type> <cache_array> <cache_length>
// //
// Returns state for for..in loop execution based on the |receiver| and // Returns state for for..in loop execution based on the object in the
// the property names in the accumulator. // accumulator. The registers |cache_type|, |cache_array|, and
// |cache_length| represent output parameters.
void Interpreter::DoForInPrepare(compiler::InterpreterAssembler* assembler) { void Interpreter::DoForInPrepare(compiler::InterpreterAssembler* assembler) {
Node* receiver_reg = __ BytecodeOperandReg(0); Node* object = __ GetAccumulator();
Node* receiver = __ LoadRegister(receiver_reg); Node* result = __ CallRuntime(Runtime::kInterpreterForInPrepare, object);
Node* property_names = __ GetAccumulator(); for (int i = 0; i < 3; i++) {
Node* result = __ CallRuntime(Runtime::kInterpreterForInPrepare, receiver, // 0 == cache_type, 1 == cache_array, 2 == cache_length
property_names); Node* cache_info = __ LoadFixedArrayElement(result, i);
Node* cache_info_reg = __ BytecodeOperandReg(i);
__ StoreRegister(cache_info, cache_info_reg);
}
__ SetAccumulator(result); __ SetAccumulator(result);
__ Dispatch(); __ Dispatch();
} }
// ForInNext <for_in_state> <index> // ForInNext <receiver> <cache_type> <cache_array> <index>
// //
// Returns the next key in a for..in loop. The state associated with the // Returns the next enumerable property in the the accumulator.
// iteration is contained in |for_in_state| and |index| is the current
// zero-based iteration count.
void Interpreter::DoForInNext(compiler::InterpreterAssembler* assembler) { void Interpreter::DoForInNext(compiler::InterpreterAssembler* assembler) {
Node* for_in_state_reg = __ BytecodeOperandReg(0); Node* receiver_reg = __ BytecodeOperandReg(0);
Node* for_in_state = __ LoadRegister(for_in_state_reg); Node* receiver = __ LoadRegister(receiver_reg);
Node* receiver = __ LoadFixedArrayElement(for_in_state, 0); Node* cache_type_reg = __ BytecodeOperandReg(1);
Node* cache_array = __ LoadFixedArrayElement(for_in_state, 1); Node* cache_type = __ LoadRegister(cache_type_reg);
Node* cache_type = __ LoadFixedArrayElement(for_in_state, 2); Node* cache_array_reg = __ BytecodeOperandReg(2);
Node* index_reg = __ BytecodeOperandReg(1); Node* cache_array = __ LoadRegister(cache_array_reg);
Node* index_reg = __ BytecodeOperandReg(3);
Node* index = __ LoadRegister(index_reg); Node* index = __ LoadRegister(index_reg);
Node* result = __ CallRuntime(Runtime::kForInNext, receiver, cache_array, Node* result = __ CallRuntime(Runtime::kForInNext, receiver, cache_array,
cache_type, index); cache_type, index);
...@@ -1551,22 +1554,34 @@ void Interpreter::DoForInNext(compiler::InterpreterAssembler* assembler) { ...@@ -1551,22 +1554,34 @@ void Interpreter::DoForInNext(compiler::InterpreterAssembler* assembler) {
} }
// ForInDone <for_in_state> // ForInDone <index> <cache_length>
// //
// Returns the next key in a for..in loop. The accumulator contains the current // Returns true if the end of the enumerable properties has been reached.
// zero-based iteration count and |for_in_state| is the state returned by an
// earlier invocation of ForInPrepare.
void Interpreter::DoForInDone(compiler::InterpreterAssembler* assembler) { void Interpreter::DoForInDone(compiler::InterpreterAssembler* assembler) {
Node* index = __ GetAccumulator(); // TODO(oth): Implement directly rather than making a runtime call.
Node* for_in_state_reg = __ BytecodeOperandReg(0); Node* index_reg = __ BytecodeOperandReg(0);
Node* for_in_state = __ LoadRegister(for_in_state_reg); Node* index = __ LoadRegister(index_reg);
Node* cache_length = __ LoadFixedArrayElement(for_in_state, 3); Node* cache_length_reg = __ BytecodeOperandReg(1);
Node* cache_length = __ LoadRegister(cache_length_reg);
Node* result = __ CallRuntime(Runtime::kForInDone, index, cache_length); Node* result = __ CallRuntime(Runtime::kForInDone, index, cache_length);
__ SetAccumulator(result); __ SetAccumulator(result);
__ Dispatch(); __ Dispatch();
} }
// ForInStep <index>
//
// Increments the loop counter in register |index| and stores the result
// in the accumulator.
void Interpreter::DoForInStep(compiler::InterpreterAssembler* assembler) {
// TODO(oth): Implement directly rather than making a runtime call.
Node* index_reg = __ BytecodeOperandReg(0);
Node* index = __ LoadRegister(index_reg);
Node* result = __ CallRuntime(Runtime::kForInStep, index);
__ SetAccumulator(result);
__ Dispatch();
}
} // namespace interpreter } // namespace interpreter
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -150,18 +150,24 @@ RUNTIME_FUNCTION(Runtime_InterpreterNewClosure) { ...@@ -150,18 +150,24 @@ RUNTIME_FUNCTION(Runtime_InterpreterNewClosure) {
RUNTIME_FUNCTION(Runtime_InterpreterForInPrepare) { RUNTIME_FUNCTION(Runtime_InterpreterForInPrepare) {
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK_EQ(2, args.length()); DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSReceiver, receiver, 0); CONVERT_ARG_HANDLE_CHECKED(JSReceiver, receiver, 0);
CONVERT_ARG_HANDLE_CHECKED(HeapObject, property_names, 1);
Handle<Object> cache_type = property_names; Object* property_names = Runtime_GetPropertyNamesFast(
Handle<Map> cache_type_map = handle(property_names->map(), isolate); 1, Handle<Object>::cast(receiver).location(), isolate);
Handle<Map> receiver_map = handle(receiver->map(), isolate); if (isolate->has_pending_exception()) {
return property_names;
}
Handle<Object> cache_type(property_names, isolate);
Handle<FixedArray> cache_array; Handle<FixedArray> cache_array;
int cache_length; int cache_length;
if (cache_type_map.is_identical_to(isolate->factory()->meta_map())) { Handle<Map> receiver_map = handle(receiver->map(), isolate);
if (cache_type->IsMap()) {
Handle<Map> cache_type_map =
handle(Handle<Map>::cast(cache_type)->map(), isolate);
DCHECK(cache_type_map.is_identical_to(isolate->factory()->meta_map()));
int enum_length = cache_type_map->EnumLength(); int enum_length = cache_type_map->EnumLength();
DescriptorArray* descriptors = receiver_map->instance_descriptors(); DescriptorArray* descriptors = receiver_map->instance_descriptors();
if (enum_length > 0 && descriptors->HasEnumCache()) { if (enum_length > 0 && descriptors->HasEnumCache()) {
...@@ -185,14 +191,12 @@ RUNTIME_FUNCTION(Runtime_InterpreterForInPrepare) { ...@@ -185,14 +191,12 @@ RUNTIME_FUNCTION(Runtime_InterpreterForInPrepare) {
} }
} }
Handle<FixedArray> result = isolate->factory()->NewFixedArray(4); Handle<FixedArray> result = isolate->factory()->NewFixedArray(3);
result->set(0, *receiver); result->set(0, *cache_type);
result->set(1, *cache_array); result->set(1, *cache_array);
result->set(2, *cache_type); result->set(2, Smi::FromInt(cache_length));
result->set(3, Smi::FromInt(cache_length));
return *result; return *result;
} }
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -1880,6 +1880,79 @@ TEST(BytecodeGraphBuilderFor) { ...@@ -1880,6 +1880,79 @@ TEST(BytecodeGraphBuilderFor) {
} }
} }
TEST(BytecodeGraphBuilderForIn) {
HandleAndZoneScope scope;
Isolate* isolate = scope.main_isolate();
Zone* zone = scope.main_zone();
Factory* factory = isolate->factory();
ExpectedSnippet<0> snippets[] = {
{"var sum = 0;\n"
"var empty = null;\n"
"for (var x in empty) { sum++; }\n"
"return sum;",
{factory->NewNumberFromInt(0)}},
{"var sum = 100;\n"
"var empty = 1;\n"
"for (var x in empty) { sum++; }\n"
"return sum;",
{factory->NewNumberFromInt(100)}},
{"for (var x in [ 10, 20, 30 ]) {}\n"
"return 2;",
{factory->NewNumberFromInt(2)}},
{"var last = 0;\n"
"for (var x in [ 10, 20, 30 ]) {\n"
" last = x;\n"
"}\n"
"return +last;",
{factory->NewNumberFromInt(2)}},
{"var first = -1;\n"
"for (var x in [ 10, 20, 30 ]) {\n"
" first = +x;\n"
" if (first > 0) break;\n"
"}\n"
"return first;",
{factory->NewNumberFromInt(1)}},
{"var first = -1;\n"
"for (var x in [ 10, 20, 30 ]) {\n"
" if (first >= 0) continue;\n"
" first = x;\n"
"}\n"
"return +first;",
{factory->NewNumberFromInt(0)}},
{"var sum = 0;\n"
"for (var x in [ 10, 20, 30 ]) {\n"
" for (var y in [ 11, 22, 33, 44, 55, 66, 77 ]) {\n"
" sum += 1;\n"
" }\n"
"}\n"
"return sum;",
{factory->NewNumberFromInt(21)}},
{"var sum = 0;\n"
"for (var x in [ 10, 20, 30 ]) {\n"
" for (var y in [ 11, 22, 33, 44, 55, 66, 77 ]) {\n"
" if (sum == 7) break;\n"
" if (sum == 6) continue;\n"
" sum += 1;\n"
" }\n"
"}\n"
"return sum;",
{factory->NewNumberFromInt(6)}},
};
for (size_t i = 0; i < arraysize(snippets); i++) {
ScopedVector<char> script(1024);
SNPrintF(script, "function %s() { %s }\n%s();", kFunctionName,
snippets[i].code_snippet, kFunctionName);
BytecodeGraphTester tester(isolate, zone, script.start());
auto callable = tester.GetCallable<>();
Handle<Object> return_value = callable().ToHandleChecked();
CHECK(return_value->SameValue(*snippets[i].return_value()));
}
}
} // namespace compiler } // namespace compiler
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -216,7 +216,10 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) { ...@@ -216,7 +216,10 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
.Throw() .Throw()
.Bind(&after_throw); .Bind(&after_throw);
builder.ForInPrepare(reg).ForInDone(reg).ForInNext(reg, reg); builder.ForInPrepare(reg, reg, reg)
.ForInDone(reg, reg)
.ForInNext(reg, reg, reg, reg)
.ForInStep(reg);
// Wide constant pool loads // Wide constant pool loads
for (int i = 0; i < 256; i++) { for (int i = 0; i < 256; i++) {
......
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