[for-in] Sanitize for-in optimizations and fix bailout points.

The PrepareId bailout location was used incorrectly in Crankshaft and,
as it turns out, is not required anyway (once you do it right). Also
there was some premature optimization going on with the CheckEnumCache
(trying to load null from roots only once), plus we can be smarter about
the null/undefined check anyway.

The idea behind this changes is to prepare unification of the two
different ForInPrepare implementations that we now have, with the end
result being that we only use the new implementation that was recently
added for the interpreter.

R=jarin@chromium.org
BUG=v8:3650
LOG=n

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

Cr-Commit-Position: refs/heads/master@{#33426}
This commit is contained in:
bmeurer 2016-01-21 00:43:43 -08:00 committed by Commit bot
parent 02e7906e39
commit f48bf12f5e
24 changed files with 112 additions and 220 deletions

View File

@ -3384,7 +3384,8 @@ void MacroAssembler::LoadAccessor(Register dst, Register holder,
}
void MacroAssembler::CheckEnumCache(Register null_value, Label* call_runtime) {
void MacroAssembler::CheckEnumCache(Label* call_runtime) {
Register null_value = r5;
Register empty_fixed_array_value = r6;
LoadRoot(empty_fixed_array_value, Heap::kEmptyFixedArrayRootIndex);
Label next, start;
@ -3398,6 +3399,7 @@ void MacroAssembler::CheckEnumCache(Register null_value, Label* call_runtime) {
cmp(r3, Operand(Smi::FromInt(kInvalidEnumCacheSentinel)));
b(eq, call_runtime);
LoadRoot(null_value, Heap::kNullValueRootIndex);
jmp(&start);
bind(&next);

View File

@ -1404,7 +1404,7 @@ class MacroAssembler: public Assembler {
// Expects object in r0 and returns map with validated enum cache
// in r0. Assumes that any other register can be used as a scratch.
void CheckEnumCache(Register null_value, Label* call_runtime);
void CheckEnumCache(Label* call_runtime);
// AllocationMemento support. Arrays may have an associated
// AllocationMemento object that can be checked for in order to pretransition

View File

@ -1488,18 +1488,15 @@ void MacroAssembler::LoadAccessor(Register dst, Register holder,
}
void MacroAssembler::CheckEnumCache(Register object,
Register null_value,
Register scratch0,
Register scratch1,
Register scratch2,
Register scratch3,
void MacroAssembler::CheckEnumCache(Register object, Register scratch0,
Register scratch1, Register scratch2,
Register scratch3, Register scratch4,
Label* call_runtime) {
DCHECK(!AreAliased(object, null_value, scratch0, scratch1, scratch2,
scratch3));
DCHECK(!AreAliased(object, scratch0, scratch1, scratch2, scratch3, scratch4));
Register empty_fixed_array_value = scratch0;
Register current_object = scratch1;
Register null_value = scratch4;
LoadRoot(empty_fixed_array_value, Heap::kEmptyFixedArrayRootIndex);
Label next, start;
@ -1516,6 +1513,7 @@ void MacroAssembler::CheckEnumCache(Register object,
Cmp(enum_length, kInvalidEnumCacheSentinel);
B(eq, call_runtime);
LoadRoot(null_value, Heap::kNullValueRootIndex);
B(&start);
Bind(&next);

View File

@ -1583,12 +1583,8 @@ class MacroAssembler : public Assembler {
void LeaveFrame(StackFrame::Type type);
// Returns map with validated enum cache in object register.
void CheckEnumCache(Register object,
Register null_value,
Register scratch0,
Register scratch1,
Register scratch2,
Register scratch3,
void CheckEnumCache(Register object, Register scratch0, Register scratch1,
Register scratch2, Register scratch3, Register scratch4,
Label* call_runtime);
// AllocationMemento support. Arrays may have an associated

View File

@ -862,13 +862,12 @@ class ForInStatement final : public ForEachStatement {
ForInType for_in_type() const { return for_in_type_; }
void set_for_in_type(ForInType type) { for_in_type_ = type; }
static int num_ids() { return parent_num_ids() + 6; }
static int num_ids() { return parent_num_ids() + 5; }
BailoutId BodyId() const { return BailoutId(local_id(0)); }
BailoutId PrepareId() const { return BailoutId(local_id(1)); }
BailoutId EnumId() const { return BailoutId(local_id(2)); }
BailoutId ToObjectId() const { return BailoutId(local_id(3)); }
BailoutId FilterId() const { return BailoutId(local_id(4)); }
BailoutId AssignmentId() const { return BailoutId(local_id(5)); }
BailoutId EnumId() const { return BailoutId(local_id(1)); }
BailoutId ToObjectId() const { return BailoutId(local_id(2)); }
BailoutId FilterId() const { return BailoutId(local_id(3)); }
BailoutId AssignmentId() const { return BailoutId(local_id(4)); }
BailoutId ContinueId() const override { return EntryId(); }
BailoutId StackCheckId() const override { return BodyId(); }

View File

@ -5451,17 +5451,8 @@ void LCodeGen::DoOsrEntry(LOsrEntry* instr) {
void LCodeGen::DoForInPrepareMap(LForInPrepareMap* instr) {
__ SmiTst(r0);
DeoptimizeIf(eq, instr, Deoptimizer::kSmi);
STATIC_ASSERT(JS_PROXY_TYPE == FIRST_JS_RECEIVER_TYPE);
__ CompareObjectType(r0, r1, r1, JS_PROXY_TYPE);
DeoptimizeIf(le, instr, Deoptimizer::kWrongInstanceType);
Label use_cache, call_runtime;
Register null_value = r5;
__ LoadRoot(null_value, Heap::kNullValueRootIndex);
__ CheckEnumCache(null_value, &call_runtime);
__ CheckEnumCache(&call_runtime);
__ ldr(r0, FieldMemOperand(r0, HeapObject::kMapOffset));
__ b(&use_cache);
@ -5470,11 +5461,6 @@ void LCodeGen::DoForInPrepareMap(LForInPrepareMap* instr) {
__ bind(&call_runtime);
__ push(r0);
CallRuntime(Runtime::kGetPropertyNamesFast, instr);
__ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset));
__ LoadRoot(ip, Heap::kMetaMapRootIndex);
__ cmp(r1, ip);
DeoptimizeIf(ne, instr, Deoptimizer::kWrongMap);
__ bind(&use_cache);
}

View File

@ -2716,20 +2716,12 @@ void LCodeGen::DoForInCacheArray(LForInCacheArray* instr) {
void LCodeGen::DoForInPrepareMap(LForInPrepareMap* instr) {
Register object = ToRegister(instr->object());
Register null_value = x5;
DCHECK(instr->IsMarkedAsCall());
DCHECK(object.Is(x0));
DeoptimizeIfSmi(object, instr, Deoptimizer::kSmi);
STATIC_ASSERT(JS_PROXY_TYPE == FIRST_JS_RECEIVER_TYPE);
__ CompareObjectType(object, x1, x1, JS_PROXY_TYPE);
DeoptimizeIf(le, instr, Deoptimizer::kNotAJavaScriptObject);
Label use_cache, call_runtime;
__ LoadRoot(null_value, Heap::kNullValueRootIndex);
__ CheckEnumCache(object, null_value, x1, x2, x3, x4, &call_runtime);
__ CheckEnumCache(object, x5, x1, x2, x3, x4, &call_runtime);
__ Ldr(object, FieldMemOperand(object, HeapObject::kMapOffset));
__ B(&use_cache);
@ -2738,11 +2730,6 @@ void LCodeGen::DoForInPrepareMap(LForInPrepareMap* instr) {
__ Bind(&call_runtime);
__ Push(object);
CallRuntime(Runtime::kGetPropertyNamesFast, instr);
__ Ldr(x1, FieldMemOperand(object, HeapObject::kMapOffset));
DeoptimizeIfNotRoot(x1, Heap::kMetaMapRootIndex, instr,
Deoptimizer::kWrongMap);
__ Bind(&use_cache);
}

View File

@ -5314,10 +5314,17 @@ void HOptimizedGraphBuilder::BuildForInBody(ForInStatement* stmt,
HInstruction* map;
HInstruction* array;
HInstruction* enum_length;
BuildCheckHeapObject(enumerable);
Add<HCheckInstanceType>(enumerable, HCheckInstanceType::IS_JS_RECEIVER);
Add<HSimulate>(stmt->ToObjectId());
bool fast = stmt->for_in_type() == ForInStatement::FAST_FOR_IN;
if (fast) {
map = Add<HForInPrepareMap>(enumerable);
Add<HSimulate>(stmt->PrepareId());
Push(map);
Add<HSimulate>(stmt->EnumId());
Drop(1);
Handle<Map> meta_map = isolate()->factory()->meta_map();
Add<HCheckMaps>(map, meta_map);
array = Add<HForInCacheArray>(enumerable, map,
DescriptorArray::kEnumCacheBridgeCacheIndex);
@ -5328,13 +5335,6 @@ void HOptimizedGraphBuilder::BuildForInBody(ForInStatement* stmt,
HForInCacheArray::cast(array)
->set_index_cache(HForInCacheArray::cast(index_cache));
} else {
Add<HSimulate>(stmt->PrepareId());
{
NoObservableSideEffectsScope no_effects(this);
BuildJSObjectCheck(enumerable, 0);
}
Add<HSimulate>(stmt->ToObjectId());
map = graph()->GetConstant1();
Runtime::FunctionId function_id = Runtime::kGetPropertyNamesFast;
Add<HPushArguments>(enumerable);

View File

@ -5329,12 +5329,6 @@ void LCodeGen::DoOsrEntry(LOsrEntry* instr) {
void LCodeGen::DoForInPrepareMap(LForInPrepareMap* instr) {
DCHECK(ToRegister(instr->context()).is(esi));
__ test(eax, Immediate(kSmiTagMask));
DeoptimizeIf(zero, instr, Deoptimizer::kSmi);
STATIC_ASSERT(JS_PROXY_TYPE == FIRST_JS_RECEIVER_TYPE);
__ CmpObjectType(eax, JS_PROXY_TYPE, ecx);
DeoptimizeIf(below_equal, instr, Deoptimizer::kWrongInstanceType);
Label use_cache, call_runtime;
__ CheckEnumCache(&call_runtime);
@ -5346,10 +5340,6 @@ void LCodeGen::DoForInPrepareMap(LForInPrepareMap* instr) {
__ bind(&call_runtime);
__ push(eax);
CallRuntime(Runtime::kGetPropertyNamesFast, instr);
__ cmp(FieldOperand(eax, HeapObject::kMapOffset),
isolate()->factory()->meta_map());
DeoptimizeIf(not_equal, instr, Deoptimizer::kWrongMap);
__ bind(&use_cache);
}

View File

@ -5470,19 +5470,10 @@ void LCodeGen::DoOsrEntry(LOsrEntry* instr) {
void LCodeGen::DoForInPrepareMap(LForInPrepareMap* instr) {
Register result = ToRegister(instr->result());
Register object = ToRegister(instr->object());
__ And(at, object, kSmiTagMask);
DeoptimizeIf(eq, instr, Deoptimizer::kSmi, at, Operand(zero_reg));
STATIC_ASSERT(JS_PROXY_TYPE == FIRST_JS_RECEIVER_TYPE);
__ GetObjectType(object, a1, a1);
DeoptimizeIf(le, instr, Deoptimizer::kNotAJavaScriptObject, a1,
Operand(JS_PROXY_TYPE));
Label use_cache, call_runtime;
DCHECK(object.is(a0));
Register null_value = t1;
__ LoadRoot(null_value, Heap::kNullValueRootIndex);
__ CheckEnumCache(null_value, &call_runtime);
__ CheckEnumCache(&call_runtime);
__ lw(result, FieldMemOperand(object, HeapObject::kMapOffset));
__ Branch(&use_cache);
@ -5491,11 +5482,6 @@ void LCodeGen::DoForInPrepareMap(LForInPrepareMap* instr) {
__ bind(&call_runtime);
__ push(object);
CallRuntime(Runtime::kGetPropertyNamesFast, instr);
__ lw(a1, FieldMemOperand(v0, HeapObject::kMapOffset));
DCHECK(result.is(v0));
__ LoadRoot(at, Heap::kMetaMapRootIndex);
DeoptimizeIf(ne, instr, Deoptimizer::kWrongMap, a1, Operand(at));
__ bind(&use_cache);
}

View File

@ -5690,19 +5690,9 @@ void LCodeGen::DoForInPrepareMap(LForInPrepareMap* instr) {
Register result = ToRegister(instr->result());
Register object = ToRegister(instr->object());
__ And(at, object, kSmiTagMask);
DeoptimizeIf(eq, instr, Deoptimizer::kSmi, at, Operand(zero_reg));
STATIC_ASSERT(JS_PROXY_TYPE == FIRST_JS_RECEIVER_TYPE);
__ GetObjectType(object, a1, a1);
DeoptimizeIf(le, instr, Deoptimizer::kNotAJavaScriptObject, a1,
Operand(JS_PROXY_TYPE));
Label use_cache, call_runtime;
DCHECK(object.is(a0));
Register null_value = a5;
__ LoadRoot(null_value, Heap::kNullValueRootIndex);
__ CheckEnumCache(null_value, &call_runtime);
__ CheckEnumCache(&call_runtime);
__ ld(result, FieldMemOperand(object, HeapObject::kMapOffset));
__ Branch(&use_cache);
@ -5711,11 +5701,6 @@ void LCodeGen::DoForInPrepareMap(LForInPrepareMap* instr) {
__ bind(&call_runtime);
__ push(object);
CallRuntime(Runtime::kGetPropertyNamesFast, instr);
__ ld(a1, FieldMemOperand(v0, HeapObject::kMapOffset));
DCHECK(result.is(v0));
__ LoadRoot(at, Heap::kMetaMapRootIndex);
DeoptimizeIf(ne, instr, Deoptimizer::kWrongMap, a1, Operand(at));
__ bind(&use_cache);
}

View File

@ -5520,17 +5520,8 @@ void LCodeGen::DoOsrEntry(LOsrEntry* instr) {
void LCodeGen::DoForInPrepareMap(LForInPrepareMap* instr) {
DCHECK(ToRegister(instr->context()).is(rsi));
Condition cc = masm()->CheckSmi(rax);
DeoptimizeIf(cc, instr, Deoptimizer::kSmi);
STATIC_ASSERT(JS_PROXY_TYPE == FIRST_JS_RECEIVER_TYPE);
__ CmpObjectType(rax, JS_PROXY_TYPE, rcx);
DeoptimizeIf(below_equal, instr, Deoptimizer::kWrongInstanceType);
Label use_cache, call_runtime;
Register null_value = rdi;
__ LoadRoot(null_value, Heap::kNullValueRootIndex);
__ CheckEnumCache(null_value, &call_runtime);
__ CheckEnumCache(&call_runtime);
__ movp(rax, FieldOperand(rax, HeapObject::kMapOffset));
__ jmp(&use_cache, Label::kNear);
@ -5539,10 +5530,6 @@ void LCodeGen::DoForInPrepareMap(LForInPrepareMap* instr) {
__ bind(&call_runtime);
__ Push(rax);
CallRuntime(Runtime::kGetPropertyNamesFast, instr);
__ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset),
Heap::kMetaMapRootIndex);
DeoptimizeIf(not_equal, instr, Deoptimizer::kWrongMap);
__ bind(&use_cache);
}

View File

@ -1041,25 +1041,20 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
ForIn loop_statement(this, stmt);
increment_loop_depth();
// Get the object to enumerate over. If the object is null or undefined, skip
// over the loop. See ECMA-262 version 5, section 12.6.4.
// Get the object to enumerate over.
SetExpressionAsStatementPosition(stmt->enumerable());
VisitForAccumulatorValue(stmt->enumerable());
__ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
__ cmp(r0, ip);
__ b(eq, &exit);
Register null_value = r5;
__ LoadRoot(null_value, Heap::kNullValueRootIndex);
__ cmp(r0, null_value);
__ b(eq, &exit);
PrepareForBailoutForId(stmt->PrepareId(), TOS_REG);
// Convert the object to a JS object.
// If the object is null or undefined, skip over the loop, otherwise convert
// it to a JS receiver. See ECMA-262 version 5, section 12.6.4.
Label convert, done_convert;
__ JumpIfSmi(r0, &convert);
__ CompareObjectType(r0, r1, r1, FIRST_JS_RECEIVER_TYPE);
__ b(ge, &done_convert);
__ CompareRoot(r0, Heap::kNullValueRootIndex);
__ b(eq, &exit);
__ CompareRoot(r0, Heap::kUndefinedValueRootIndex);
__ b(eq, &exit);
__ bind(&convert);
ToObjectStub stub(isolate());
__ CallStub(&stub);
@ -1067,16 +1062,14 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
PrepareForBailoutForId(stmt->ToObjectId(), TOS_REG);
__ push(r0);
// Check for proxies.
Label call_runtime;
__ CompareObjectType(r0, r1, r1, JS_PROXY_TYPE);
__ b(eq, &call_runtime);
// 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.
__ CheckEnumCache(null_value, &call_runtime);
// Note: Proxies never have an enum cache, so will always take the
// slow path.
Label call_runtime;
__ CheckEnumCache(&call_runtime);
// The enum cache is valid. Load the map of the object being
// iterated over and use the cache for the iteration.

View File

@ -1044,22 +1044,17 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
ForIn loop_statement(this, stmt);
increment_loop_depth();
// Get the object to enumerate over. If the object is null or undefined, skip
// over the loop. See ECMA-262 version 5, section 12.6.4.
// Get the object to enumerate over.
SetExpressionAsStatementPosition(stmt->enumerable());
VisitForAccumulatorValue(stmt->enumerable());
__ JumpIfRoot(x0, Heap::kUndefinedValueRootIndex, &exit);
Register null_value = x15;
__ LoadRoot(null_value, Heap::kNullValueRootIndex);
__ Cmp(x0, null_value);
__ B(eq, &exit);
PrepareForBailoutForId(stmt->PrepareId(), TOS_REG);
// Convert the object to a JS object.
// If the object is null or undefined, skip over the loop, otherwise convert
// it to a JS receiver. See ECMA-262 version 5, section 12.6.4.
Label convert, done_convert;
__ JumpIfSmi(x0, &convert);
__ JumpIfObjectType(x0, x10, x11, FIRST_JS_RECEIVER_TYPE, &done_convert, ge);
__ JumpIfRoot(x0, Heap::kNullValueRootIndex, &exit);
__ JumpIfRoot(x0, Heap::kUndefinedValueRootIndex, &exit);
__ Bind(&convert);
ToObjectStub stub(isolate());
__ CallStub(&stub);
@ -1067,15 +1062,14 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
PrepareForBailoutForId(stmt->ToObjectId(), TOS_REG);
__ Push(x0);
// Check for proxies.
Label call_runtime;
__ JumpIfObjectType(x0, x10, x11, JS_PROXY_TYPE, &call_runtime, eq);
// 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.
__ CheckEnumCache(x0, null_value, x10, x11, x12, x13, &call_runtime);
// Note: Proxies never have an enum cache, so will always take the
// slow path.
Label call_runtime;
__ CheckEnumCache(x0, x15, x10, x11, x12, x13, &call_runtime);
// The enum cache is valid. Load the map of the object being
// iterated over and use the cache for the iteration.

View File

@ -977,22 +977,20 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
ForIn loop_statement(this, stmt);
increment_loop_depth();
// Get the object to enumerate over. If the object is null or undefined, skip
// over the loop. See ECMA-262 version 5, section 12.6.4.
// Get the object to enumerate over.
SetExpressionAsStatementPosition(stmt->enumerable());
VisitForAccumulatorValue(stmt->enumerable());
__ cmp(eax, isolate()->factory()->undefined_value());
__ j(equal, &exit);
__ cmp(eax, isolate()->factory()->null_value());
__ j(equal, &exit);
PrepareForBailoutForId(stmt->PrepareId(), TOS_REG);
// Convert the object to a JS object.
// If the object is null or undefined, skip over the loop, otherwise convert
// it to a JS receiver. See ECMA-262 version 5, section 12.6.4.
Label convert, done_convert;
__ JumpIfSmi(eax, &convert, Label::kNear);
__ CmpObjectType(eax, FIRST_JS_RECEIVER_TYPE, ecx);
__ j(above_equal, &done_convert, Label::kNear);
__ cmp(eax, isolate()->factory()->undefined_value());
__ j(equal, &exit);
__ cmp(eax, isolate()->factory()->null_value());
__ j(equal, &exit);
__ bind(&convert);
ToObjectStub stub(isolate());
__ CallStub(&stub);
@ -1000,15 +998,13 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
PrepareForBailoutForId(stmt->ToObjectId(), TOS_REG);
__ push(eax);
// Check for proxies.
Label call_runtime, use_cache, fixed_array;
__ CmpObjectType(eax, JS_PROXY_TYPE, ecx);
__ j(equal, &call_runtime);
// 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.
// Note: Proxies never have an enum cache, so will always take the
// slow path.
Label call_runtime, use_cache, fixed_array;
__ CheckEnumCache(&call_runtime);
__ mov(eax, FieldOperand(eax, HeapObject::kMapOffset));

View File

@ -1037,23 +1037,22 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
ForIn loop_statement(this, stmt);
increment_loop_depth();
// Get the object to enumerate over. If the object is null or undefined, skip
// over the loop. See ECMA-262 version 5, section 12.6.4.
// Get the object to enumerate over.
SetExpressionAsStatementPosition(stmt->enumerable());
VisitForAccumulatorValue(stmt->enumerable());
__ mov(a0, result_register()); // Result as param to InvokeBuiltin below.
__ LoadRoot(at, Heap::kUndefinedValueRootIndex);
__ Branch(&exit, eq, a0, Operand(at));
Register null_value = t1;
__ LoadRoot(null_value, Heap::kNullValueRootIndex);
__ Branch(&exit, eq, a0, Operand(null_value));
PrepareForBailoutForId(stmt->PrepareId(), TOS_REG);
__ mov(a0, v0);
// Convert the object to a JS object.
__ mov(a0, result_register());
// If the object is null or undefined, skip over the loop, otherwise convert
// it to a JS receiver. See ECMA-262 version 5, section 12.6.4.
Label convert, done_convert;
__ JumpIfSmi(a0, &convert);
__ GetObjectType(a0, a1, a1);
__ Branch(&done_convert, ge, a1, Operand(FIRST_JS_RECEIVER_TYPE));
__ Branch(USE_DELAY_SLOT, &done_convert, ge, a1,
Operand(FIRST_JS_RECEIVER_TYPE));
__ LoadRoot(at, Heap::kNullValueRootIndex); // In delay slot.
__ Branch(USE_DELAY_SLOT, &exit, eq, a0, Operand(at));
__ LoadRoot(at, Heap::kUndefinedValueRootIndex); // In delay slot.
__ Branch(&exit, eq, a0, Operand(at));
__ bind(&convert);
ToObjectStub stub(isolate());
__ CallStub(&stub);
@ -1062,16 +1061,14 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
PrepareForBailoutForId(stmt->ToObjectId(), TOS_REG);
__ push(a0);
// Check for proxies.
Label call_runtime;
__ GetObjectType(a0, a1, a1);
__ Branch(&call_runtime, eq, a1, Operand(JS_PROXY_TYPE));
// 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.
__ CheckEnumCache(null_value, &call_runtime);
// Note: Proxies never have an enum cache, so will always take the
// slow path.
Label call_runtime;
__ CheckEnumCache(&call_runtime);
// The enum cache is valid. Load the map of the object being
// iterated over and use the cache for the iteration.

View File

@ -1040,19 +1040,19 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
// over the loop. See ECMA-262 version 5, section 12.6.4.
SetExpressionAsStatementPosition(stmt->enumerable());
VisitForAccumulatorValue(stmt->enumerable());
__ mov(a0, result_register()); // Result as param to InvokeBuiltin below.
__ LoadRoot(at, Heap::kUndefinedValueRootIndex);
__ Branch(&exit, eq, a0, Operand(at));
Register null_value = a5;
__ LoadRoot(null_value, Heap::kNullValueRootIndex);
__ Branch(&exit, eq, a0, Operand(null_value));
PrepareForBailoutForId(stmt->PrepareId(), TOS_REG);
__ mov(a0, v0);
// Convert the object to a JS object.
__ mov(a0, result_register());
// If the object is null or undefined, skip over the loop, otherwise convert
// it to a JS receiver. See ECMA-262 version 5, section 12.6.4.
Label convert, done_convert;
__ JumpIfSmi(a0, &convert);
__ GetObjectType(a0, a1, a1);
__ Branch(&done_convert, ge, a1, Operand(FIRST_JS_RECEIVER_TYPE));
__ Branch(USE_DELAY_SLOT, &done_convert, ge, a1,
Operand(FIRST_JS_RECEIVER_TYPE));
__ LoadRoot(at, Heap::kNullValueRootIndex); // In delay slot.
__ Branch(USE_DELAY_SLOT, &exit, eq, a0, Operand(at));
__ LoadRoot(at, Heap::kUndefinedValueRootIndex); // In delay slot.
__ Branch(&exit, eq, a0, Operand(at));
__ bind(&convert);
ToObjectStub stub(isolate());
__ CallStub(&stub);
@ -1061,16 +1061,14 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
PrepareForBailoutForId(stmt->ToObjectId(), TOS_REG);
__ push(a0);
// Check for proxies.
Label call_runtime;
__ GetObjectType(a0, a1, a1);
__ Branch(&call_runtime, eq, a1, Operand(JS_PROXY_TYPE));
// 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.
__ CheckEnumCache(null_value, &call_runtime);
// Note: Proxies never have an enum cache, so will always take the
// slow path.
Label call_runtime;
__ CheckEnumCache(&call_runtime);
// The enum cache is valid. Load the map of the object being
// iterated over and use the cache for the iteration.

View File

@ -990,24 +990,20 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
ForIn loop_statement(this, stmt);
increment_loop_depth();
// Get the object to enumerate over. If the object is null or undefined, skip
// over the loop. See ECMA-262 version 5, section 12.6.4.
// Get the object to enumerate over.
SetExpressionAsStatementPosition(stmt->enumerable());
VisitForAccumulatorValue(stmt->enumerable());
__ CompareRoot(rax, Heap::kUndefinedValueRootIndex);
__ j(equal, &exit);
Register null_value = rdi;
__ LoadRoot(null_value, Heap::kNullValueRootIndex);
__ cmpp(rax, null_value);
__ j(equal, &exit);
PrepareForBailoutForId(stmt->PrepareId(), TOS_REG);
// Convert the object to a JS object.
// If the object is null or undefined, skip over the loop, otherwise convert
// it to a JS receiver. See ECMA-262 version 5, section 12.6.4.
Label convert, done_convert;
__ JumpIfSmi(rax, &convert, Label::kNear);
__ CmpObjectType(rax, FIRST_JS_RECEIVER_TYPE, rcx);
__ j(above_equal, &done_convert, Label::kNear);
__ CompareRoot(rax, Heap::kNullValueRootIndex);
__ j(equal, &exit);
__ CompareRoot(rax, Heap::kUndefinedValueRootIndex);
__ j(equal, &exit);
__ bind(&convert);
ToObjectStub stub(isolate());
__ CallStub(&stub);
@ -1015,16 +1011,14 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
PrepareForBailoutForId(stmt->ToObjectId(), TOS_REG);
__ Push(rax);
// Check for proxies.
Label call_runtime;
__ CmpObjectType(rax, JS_PROXY_TYPE, rcx);
__ j(equal, &call_runtime);
// 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.
__ CheckEnumCache(null_value, &call_runtime);
// Note: Proxies never have an enum cache, so will always take the
// slow path.
Label call_runtime;
__ CheckEnumCache(&call_runtime);
// The enum cache is valid. Load the map of the object being
// iterated over and use the cache for the iteration.

View File

@ -5511,7 +5511,8 @@ void MacroAssembler::LoadAccessor(Register dst, Register holder,
}
void MacroAssembler::CheckEnumCache(Register null_value, Label* call_runtime) {
void MacroAssembler::CheckEnumCache(Label* call_runtime) {
Register null_value = t1;
Register empty_fixed_array_value = t2;
LoadRoot(empty_fixed_array_value, Heap::kEmptyFixedArrayRootIndex);
Label next, start;
@ -5525,6 +5526,7 @@ void MacroAssembler::CheckEnumCache(Register null_value, Label* call_runtime) {
Branch(
call_runtime, eq, a3, Operand(Smi::FromInt(kInvalidEnumCacheSentinel)));
LoadRoot(null_value, Heap::kNullValueRootIndex);
jmp(&start);
bind(&next);

View File

@ -1595,7 +1595,7 @@ const Operand& rt = Operand(zero_reg), BranchDelaySlot bd = PROTECT
// Expects object in a0 and returns map with validated enum cache
// in a0. Assumes that any other register can be used as a scratch.
void CheckEnumCache(Register null_value, Label* call_runtime);
void CheckEnumCache(Label* call_runtime);
// AllocationMemento support. Arrays may have an associated
// AllocationMemento object that can be checked for in order to pretransition

View File

@ -6239,7 +6239,8 @@ void MacroAssembler::LoadAccessor(Register dst, Register holder,
}
void MacroAssembler::CheckEnumCache(Register null_value, Label* call_runtime) {
void MacroAssembler::CheckEnumCache(Label* call_runtime) {
Register null_value = a5;
Register empty_fixed_array_value = a6;
LoadRoot(empty_fixed_array_value, Heap::kEmptyFixedArrayRootIndex);
Label next, start;
@ -6253,6 +6254,7 @@ void MacroAssembler::CheckEnumCache(Register null_value, Label* call_runtime) {
Branch(
call_runtime, eq, a3, Operand(Smi::FromInt(kInvalidEnumCacheSentinel)));
LoadRoot(null_value, Heap::kNullValueRootIndex);
jmp(&start);
bind(&next);

View File

@ -1744,7 +1744,7 @@ const Operand& rt = Operand(zero_reg), BranchDelaySlot bd = PROTECT
// Expects object in a0 and returns map with validated enum cache
// in a0. Assumes that any other register can be used as a scratch.
void CheckEnumCache(Register null_value, Label* call_runtime);
void CheckEnumCache(Label* call_runtime);
// AllocationMemento support. Arrays may have an associated
// AllocationMemento object that can be checked for in order to pretransition

View File

@ -5395,7 +5395,7 @@ void MacroAssembler::JumpIfWhite(Register value, Register bitmap_scratch,
}
void MacroAssembler::CheckEnumCache(Register null_value, Label* call_runtime) {
void MacroAssembler::CheckEnumCache(Label* call_runtime) {
Label next, start;
Register empty_fixed_array_value = r8;
LoadRoot(empty_fixed_array_value, Heap::kEmptyFixedArrayRootIndex);
@ -5436,10 +5436,11 @@ void MacroAssembler::CheckEnumCache(Register null_value, Label* call_runtime) {
bind(&no_elements);
movp(rcx, FieldOperand(rbx, Map::kPrototypeOffset));
cmpp(rcx, null_value);
CompareRoot(rcx, Heap::kNullValueRootIndex);
j(not_equal, &next);
}
void MacroAssembler::TestJSArrayForAllocationMemento(
Register receiver_reg,
Register scratch_reg,

View File

@ -1541,8 +1541,7 @@ class MacroAssembler: public Assembler {
// Expects object in rax and returns map with validated enum cache
// in rax. Assumes that any other register can be used as a scratch.
void CheckEnumCache(Register null_value,
Label* call_runtime);
void CheckEnumCache(Label* call_runtime);
// AllocationMemento support. Arrays may have an associated
// AllocationMemento object that can be checked for in order to pretransition