Index: src/interpreter/interpreter.cc |
diff --git a/src/interpreter/interpreter.cc b/src/interpreter/interpreter.cc |
index 4830f1dc72efa1f90dfece7c047bee278d960916..b461264c22863fcf3c73c3768da8ad0d43696e82 100644 |
--- a/src/interpreter/interpreter.cc |
+++ b/src/interpreter/interpreter.cc |
@@ -1840,6 +1840,17 @@ void Interpreter::DoDebugger(InterpreterAssembler* assembler) { |
DEBUG_BREAK_BYTECODE_LIST(DEBUG_BREAK); |
#undef DEBUG_BREAK |
+void Interpreter::BuildForInPrepareResult(Node* output_register, |
+ Node* cache_type, Node* cache_array, |
+ Node* cache_length, |
+ InterpreterAssembler* assembler) { |
+ __ StoreRegister(cache_type, output_register); |
+ output_register = __ NextRegister(output_register); |
+ __ StoreRegister(cache_array, output_register); |
+ output_register = __ NextRegister(output_register); |
+ __ StoreRegister(cache_length, output_register); |
+} |
+ |
// ForInPrepare <cache_info_triple> |
// |
// Returns state for for..in loop execution based on the object in the |
@@ -1849,17 +1860,92 @@ DEBUG_BREAK_BYTECODE_LIST(DEBUG_BREAK); |
void Interpreter::DoForInPrepare(InterpreterAssembler* assembler) { |
Node* object = __ GetAccumulator(); |
Node* context = __ GetContext(); |
- Node* result_triple = __ CallRuntime(Runtime::kForInPrepare, context, object); |
- |
- // Set output registers: |
- // 0 == cache_type, 1 == cache_array, 2 == cache_length |
- Node* output_register = __ BytecodeOperandReg(0); |
- for (int i = 0; i < 3; i++) { |
- Node* cache_info = __ Projection(i, result_triple); |
- __ StoreRegister(cache_info, output_register); |
- output_register = __ NextRegister(output_register); |
+ Node* const zero_smi = __ SmiConstant(Smi::FromInt(0)); |
+ |
+ Label test_if_null(assembler), test_if_undefined(assembler), |
+ nothing_to_iterate(assembler, Label::kDeferred), |
+ convert_to_receiver(assembler, Label::kDeferred), |
+ already_receiver(assembler), check_enum_cache(assembler); |
+ |
+ Variable receiver(assembler, MachineRepresentation::kTagged); |
+ |
+ // Test if object is already a receiver, no conversion necessary if so. |
+ Node* instance_type = __ LoadInstanceType(object); |
+ Node* first_receiver_type = __ Int32Constant(FIRST_JS_RECEIVER_TYPE); |
+ __ BranchIfInt32GreaterThanOrEqual(instance_type, first_receiver_type, |
+ &already_receiver, &test_if_null); |
+ |
+ __ Bind(&test_if_null); |
+ { |
+ __ BranchIfWordEqual(object, assembler->NullConstant(), ¬hing_to_iterate, |
+ &test_if_undefined); |
+ } |
+ |
+ __ Bind(&test_if_undefined); |
+ { |
+ __ BranchIfWordEqual(object, assembler->UndefinedConstant(), |
+ ¬hing_to_iterate, &convert_to_receiver); |
+ } |
+ |
+ __ Bind(&convert_to_receiver); |
+ { |
+ Callable callable = CodeFactory::ToObject(assembler->isolate()); |
+ Node* target = __ HeapConstant(callable.code()); |
+ Node* result = __ CallStub(callable.descriptor(), target, context, object); |
+ receiver.Bind(result); |
+ __ Goto(&check_enum_cache); |
+ } |
+ |
+ __ Bind(&already_receiver); |
+ { |
+ receiver.Bind(object); |
+ __ Goto(&check_enum_cache); |
+ } |
+ |
+ Label use_enum_cache(assembler), use_runtime(assembler, Label::kDeferred); |
+ __ Bind(&check_enum_cache); |
+ { __ CheckEnumCache(receiver.value(), &use_enum_cache, &use_runtime); } |
+ |
+ __ Bind(&use_enum_cache); |
+ { |
+ // The enum cache is valid. Load the map of the object being |
+ // iterated over and use the cache for the iteration. |
+ Node* cache_type = __ LoadMap(receiver.value()); |
+ Node* cache_length = __ EnumLength(cache_type); |
+ __ GotoIf(assembler->WordEqual(cache_length, zero_smi), |
+ ¬hing_to_iterate); |
+ Node* descriptors = __ LoadMapDescriptors(cache_type); |
+ Node* cache_offset = |
+ __ LoadObjectField(descriptors, DescriptorArray::kEnumCacheOffset); |
+ Node* cache_array = __ LoadObjectField( |
+ cache_offset, DescriptorArray::kEnumCacheBridgeCacheOffset); |
+ Node* output_register = __ BytecodeOperandReg(0); |
+ BuildForInPrepareResult(output_register, cache_type, cache_array, |
+ cache_length, assembler); |
+ __ Dispatch(); |
+ } |
+ |
+ __ Bind(&use_runtime); |
+ { |
+ Node* result_triple = |
+ __ CallRuntime(Runtime::kForInPrepare, context, object); |
+ Node* cache_type = __ Projection(0, result_triple); |
+ Node* cache_array = __ Projection(1, result_triple); |
+ Node* cache_length = __ Projection(2, result_triple); |
+ Node* output_register = __ BytecodeOperandReg(0); |
+ BuildForInPrepareResult(output_register, cache_type, cache_array, |
+ cache_length, assembler); |
+ __ Dispatch(); |
+ } |
+ |
+ __ Bind(¬hing_to_iterate); |
+ { |
+ // Receiver is null or undefined or descriptors are zero length. |
+ Node* output_register = __ BytecodeOperandReg(0); |
+ BuildForInPrepareResult(output_register, zero_smi, zero_smi, zero_smi, |
+ assembler); |
+ __ Dispatch(); |
} |
- __ Dispatch(); |
} |
// ForInNext <receiver> <index> <cache_info_pair> |
@@ -1882,8 +1968,7 @@ void Interpreter::DoForInNext(InterpreterAssembler* assembler) { |
// Check if we can use the for-in fast path potentially using the enum cache. |
Label if_fast(assembler), if_slow(assembler, Label::kDeferred); |
Node* receiver_map = __ LoadObjectField(receiver, HeapObject::kMapOffset); |
- Node* condition = __ WordEqual(receiver_map, cache_type); |
- __ BranchIf(condition, &if_fast, &if_slow); |
+ __ BranchIfWordEqual(receiver_map, cache_type, &if_fast, &if_slow); |
__ Bind(&if_fast); |
{ |
// Enum cache in use for {receiver}, the {key} is definitely valid. |