From f568fc6d13b1995390a70cb6b00fc6dc7cb57fbd Mon Sep 17 00:00:00 2001 From: "ager@chromium.org" Date: Thu, 3 Dec 2009 10:24:16 +0000 Subject: [PATCH] Move for-in cache validity check to generated code. Review URL: http://codereview.chromium.org/464002 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3407 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/codegen-arm.cc | 68 +++++++++++++++++++++++++++++++++++++--- src/ia32/codegen-ia32.cc | 58 +++++++++++++++++++++++++++++++--- src/x64/codegen-x64.cc | 53 +++++++++++++++++++++++++++++-- 3 files changed, 168 insertions(+), 11 deletions(-) diff --git a/src/arm/codegen-arm.cc b/src/arm/codegen-arm.cc index c95b2c4dbf..e9611fae20 100644 --- a/src/arm/codegen-arm.cc +++ b/src/arm/codegen-arm.cc @@ -1775,19 +1775,77 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) { jsobject.Bind(); // Get the set of properties (as a FixedArray or Map). - frame_->EmitPush(r0); // duplicate the object being enumerated - frame_->EmitPush(r0); + // r0: value to be iterated over + frame_->EmitPush(r0); // Push the object being iterated over. + + // Check cache validity in generated code. This is a fast case for + // the JSObject::IsSimpleEnum cache validity checks. If we cannot + // guarantee cache validity, call the runtime system to check cache + // validity or get the property names in a fixed array. + JumpTarget call_runtime; + JumpTarget loop(JumpTarget::BIDIRECTIONAL); + JumpTarget check_prototype; + JumpTarget use_cache; + __ mov(r1, Operand(r0)); + loop.Bind(); + // Check that there are no elements. + __ ldr(r2, FieldMemOperand(r1, JSObject::kElementsOffset)); + __ LoadRoot(r4, Heap::kEmptyFixedArrayRootIndex); + __ cmp(r2, r4); + call_runtime.Branch(ne); + // Check that instance descriptors are not empty so that we can + // check for an enum cache. Leave the map in r3 for the subsequent + // prototype load. + __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset)); + __ ldr(r2, FieldMemOperand(r3, Map::kInstanceDescriptorsOffset)); + __ LoadRoot(ip, Heap::kEmptyDescriptorArrayRootIndex); + __ cmp(r2, ip); + call_runtime.Branch(eq); + // Check that there in an enum cache in the non-empty instance + // descriptors. This is the case if the next enumeration index + // field does not contain a smi. + __ ldr(r2, FieldMemOperand(r2, DescriptorArray::kEnumerationIndexOffset)); + __ tst(r2, Operand(kSmiTagMask)); + call_runtime.Branch(eq); + // For all objects but the receiver, check that the cache is empty. + // r4: empty fixed array root. + __ cmp(r1, r0); + check_prototype.Branch(eq); + __ ldr(r2, FieldMemOperand(r2, DescriptorArray::kEnumCacheBridgeCacheOffset)); + __ cmp(r2, r4); + call_runtime.Branch(ne); + check_prototype.Bind(); + // Load the prototype from the map and loop if non-null. + __ ldr(r1, FieldMemOperand(r3, Map::kPrototypeOffset)); + __ LoadRoot(ip, Heap::kNullValueRootIndex); + __ cmp(r1, ip); + loop.Branch(ne); + // The enum cache is valid. Load the map of the object being + // iterated over and use the cache for the iteration. + __ ldr(r0, FieldMemOperand(r0, HeapObject::kMapOffset)); + use_cache.Jump(); + + call_runtime.Bind(); + // Call the runtime to get the property names for the object. + frame_->EmitPush(r0); // push the object (slot 4) for the runtime call frame_->CallRuntime(Runtime::kGetPropertyNamesFast, 1); - // If we got a Map, we can do a fast modification check. - // Otherwise, we got a FixedArray, and we have to do a slow check. + // If we got a map from the runtime call, we can do a fast + // modification check. Otherwise, we got a fixed array, and we have + // to do a slow check. + // r0: map or fixed array (result from call to + // Runtime::kGetPropertyNamesFast) __ mov(r2, Operand(r0)); __ ldr(r1, FieldMemOperand(r2, HeapObject::kMapOffset)); __ LoadRoot(ip, Heap::kMetaMapRootIndex); __ cmp(r1, ip); fixed_array.Branch(ne); + use_cache.Bind(); // Get enum cache + // r0: map (either the result from a call to + // Runtime::kGetPropertyNamesFast or has been fetched directly from + // the object) __ mov(r1, Operand(r0)); __ ldr(r1, FieldMemOperand(r1, Map::kInstanceDescriptorsOffset)); __ ldr(r1, FieldMemOperand(r1, DescriptorArray::kEnumerationIndexOffset)); @@ -6396,7 +6454,7 @@ void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) { __ b(eq, &adaptor); // Check index against formal parameters count limit passed in - // through register eax. Use unsigned comparison to get negative + // through register r0. Use unsigned comparison to get negative // check for free. __ cmp(r1, r0); __ b(cs, &slow); diff --git a/src/ia32/codegen-ia32.cc b/src/ia32/codegen-ia32.cc index cc2eed7f3c..78f66e291a 100644 --- a/src/ia32/codegen-ia32.cc +++ b/src/ia32/codegen-ia32.cc @@ -3056,13 +3056,59 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) { jsobject.Bind(); // Get the set of properties (as a FixedArray or Map). // eax: value to be iterated over - frame_->EmitPush(eax); // push the object being iterated over (slot 4) + frame_->EmitPush(eax); // Push the object being iterated over. + // Check cache validity in generated code. This is a fast case for + // the JSObject::IsSimpleEnum cache validity checks. If we cannot + // guarantee cache validity, call the runtime system to check cache + // validity or get the property names in a fixed array. + JumpTarget call_runtime; + JumpTarget loop(JumpTarget::BIDIRECTIONAL); + JumpTarget check_prototype; + JumpTarget use_cache; + __ mov(ecx, eax); + loop.Bind(); + // Check that there are no elements. + __ mov(edx, FieldOperand(ecx, JSObject::kElementsOffset)); + __ cmp(Operand(edx), Immediate(Factory::empty_fixed_array())); + call_runtime.Branch(not_equal); + // Check that instance descriptors are not empty so that we can + // check for an enum cache. Leave the map in ebx for the subsequent + // prototype load. + __ mov(ebx, FieldOperand(ecx, HeapObject::kMapOffset)); + __ mov(edx, FieldOperand(ebx, Map::kInstanceDescriptorsOffset)); + __ cmp(Operand(edx), Immediate(Factory::empty_descriptor_array())); + call_runtime.Branch(equal); + // Check that there in an enum cache in the non-empty instance + // descriptors. This is the case if the next enumeration index + // field does not contain a smi. + __ mov(edx, FieldOperand(edx, DescriptorArray::kEnumerationIndexOffset)); + __ test(edx, Immediate(kSmiTagMask)); + call_runtime.Branch(zero); + // For all objects but the receiver, check that the cache is empty. + __ cmp(ecx, Operand(eax)); + check_prototype.Branch(equal); + __ mov(edx, FieldOperand(edx, DescriptorArray::kEnumCacheBridgeCacheOffset)); + __ cmp(Operand(edx), Immediate(Factory::empty_fixed_array())); + call_runtime.Branch(not_equal); + check_prototype.Bind(); + // Load the prototype from the map and loop if non-null. + __ mov(ecx, FieldOperand(ebx, Map::kPrototypeOffset)); + __ cmp(Operand(ecx), Immediate(Factory::null_value())); + loop.Branch(not_equal); + // The enum cache is valid. Load the map of the object being + // iterated over and use the cache for the iteration. + __ mov(eax, FieldOperand(eax, HeapObject::kMapOffset)); + use_cache.Jump(); + + call_runtime.Bind(); + // Call the runtime to get the property names for the object. frame_->EmitPush(eax); // push the Object (slot 4) for the runtime call frame_->CallRuntime(Runtime::kGetPropertyNamesFast, 1); - // If we got a Map, we can do a fast modification check. - // Otherwise, we got a FixedArray, and we have to do a slow check. + // If we got a map from the runtime call, we can do a fast + // modification check. Otherwise, we got a fixed array, and we have + // to do a slow check. // eax: map or fixed array (result from call to // Runtime::kGetPropertyNamesFast) __ mov(edx, Operand(eax)); @@ -3070,9 +3116,13 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) { __ cmp(ecx, Factory::meta_map()); fixed_array.Branch(not_equal); + use_cache.Bind(); // Get enum cache - // eax: map (result from call to Runtime::kGetPropertyNamesFast) + // eax: map (either the result from a call to + // Runtime::kGetPropertyNamesFast or has been fetched directly from + // the object) __ mov(ecx, Operand(eax)); + __ mov(ecx, FieldOperand(ecx, Map::kInstanceDescriptorsOffset)); // Get the bridge array held in the enumeration index field. __ mov(ecx, FieldOperand(ecx, DescriptorArray::kEnumerationIndexOffset)); diff --git a/src/x64/codegen-x64.cc b/src/x64/codegen-x64.cc index ec0fa12fbe..004b6bf69c 100644 --- a/src/x64/codegen-x64.cc +++ b/src/x64/codegen-x64.cc @@ -1662,8 +1662,54 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) { jsobject.Bind(); // Get the set of properties (as a FixedArray or Map). // rax: value to be iterated over - frame_->EmitPush(rax); // push the object being iterated over (slot 4) + frame_->EmitPush(rax); // Push the object being iterated over. + + // Check cache validity in generated code. This is a fast case for + // the JSObject::IsSimpleEnum cache validity checks. If we cannot + // guarantee cache validity, call the runtime system to check cache + // validity or get the property names in a fixed array. + JumpTarget call_runtime; + JumpTarget loop(JumpTarget::BIDIRECTIONAL); + JumpTarget check_prototype; + JumpTarget use_cache; + __ movq(rcx, rax); + loop.Bind(); + // Check that there are no elements. + __ movq(rdx, FieldOperand(rcx, JSObject::kElementsOffset)); + __ CompareRoot(rdx, Heap::kEmptyFixedArrayRootIndex); + call_runtime.Branch(not_equal); + // Check that instance descriptors are not empty so that we can + // check for an enum cache. Leave the map in ebx for the subsequent + // prototype load. + __ movq(rbx, FieldOperand(rcx, HeapObject::kMapOffset)); + __ movq(rdx, FieldOperand(rbx, Map::kInstanceDescriptorsOffset)); + __ CompareRoot(rdx, Heap::kEmptyDescriptorArrayRootIndex); + call_runtime.Branch(equal); + // Check that there in an enum cache in the non-empty instance + // descriptors. This is the case if the next enumeration index + // field does not contain a smi. + __ movq(rdx, FieldOperand(rdx, DescriptorArray::kEnumerationIndexOffset)); + is_smi = masm_->CheckSmi(rdx); + call_runtime.Branch(is_smi); + // For all objects but the receiver, check that the cache is empty. + __ cmpq(rcx, rax); + check_prototype.Branch(equal); + __ movq(rdx, FieldOperand(rdx, DescriptorArray::kEnumCacheBridgeCacheOffset)); + __ CompareRoot(rdx, Heap::kEmptyFixedArrayRootIndex); + call_runtime.Branch(not_equal); + check_prototype.Bind(); + // Load the prototype from the map and loop if non-null. + __ movq(rcx, FieldOperand(rbx, Map::kPrototypeOffset)); + __ CompareRoot(rcx, Heap::kNullValueRootIndex); + loop.Branch(not_equal); + // The enum cache is valid. Load the map of the object being + // iterated over and use the cache for the iteration. + __ movq(rax, FieldOperand(rax, HeapObject::kMapOffset)); + use_cache.Jump(); + + call_runtime.Bind(); + // Call the runtime to get the property names for the object. frame_->EmitPush(rax); // push the Object (slot 4) for the runtime call frame_->CallRuntime(Runtime::kGetPropertyNamesFast, 1); @@ -1676,8 +1722,11 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) { __ CompareRoot(rcx, Heap::kMetaMapRootIndex); fixed_array.Branch(not_equal); + use_cache.Bind(); // Get enum cache - // rax: map (result from call to Runtime::kGetPropertyNamesFast) + // rax: map (either the result from a call to + // Runtime::kGetPropertyNamesFast or has been fetched directly from + // the object) __ movq(rcx, rax); __ movq(rcx, FieldOperand(rcx, Map::kInstanceDescriptorsOffset)); // Get the bridge array held in the enumeration index field.