From: vegorov@chromium.org Date: Wed, 29 Feb 2012 12:05:58 +0000 (+0000) Subject: MIPS: Support fast case for-in in Crankshaft. X-Git-Tag: upstream/4.7.83~17236 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=b4a0a4ddfebbdd2430263026e2c2c2e86ddf15f1;p=platform%2Fupstream%2Fv8.git MIPS: Support fast case for-in in Crankshaft. Port r10794 (654fe910). Original commit message: Only JSObject enumerables with enum cache (fast case properties, no interceptors, no enumerable properties on the prototype) are supported. HLoadKeyedGeneric with keys produced by for-in enumeration are recognized and rewritten into direct property load by index. For this enum-cache was extended to store property indices in a separate array (see handles.cc). New hydrogen instructions: - HForInPrepareMap: checks for-in fast case preconditions and returns map that contains enum-cache; - HForInCacheArray: extracts enum-cache array from the map; - HCheckMapValue: map check with HValue map instead of immediate; - HLoadFieldByIndex: load fast property by it's index, positive indexes denote in-object properties, negative - out of object properties; Changed hydrogen instructions: - HLoadKeyedFastElement: added hole check suppression for loads from internal FixedArrays that are knows to have no holes inside. BUG= TEST= Review URL: https://chromiumcodereview.appspot.com/9453009 Patch from Daniel Kalmar . git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@10865 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/src/mips/full-codegen-mips.cc b/src/mips/full-codegen-mips.cc index d6e7a44..5559788 100644 --- a/src/mips/full-codegen-mips.cc +++ b/src/mips/full-codegen-mips.cc @@ -952,7 +952,8 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { 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. Label convert, done_convert; __ JumpIfSmi(a0, &convert); @@ -975,44 +976,7 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { // 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. - Label next; - // Preload a couple of values used in the loop. - Register empty_fixed_array_value = t2; - __ LoadRoot(empty_fixed_array_value, Heap::kEmptyFixedArrayRootIndex); - Register empty_descriptor_array_value = t3; - __ LoadRoot(empty_descriptor_array_value, - Heap::kEmptyDescriptorArrayRootIndex); - __ mov(a1, a0); - __ bind(&next); - - // Check that there are no elements. Register a1 contains the - // current JS object we've reached through the prototype chain. - __ lw(a2, FieldMemOperand(a1, JSObject::kElementsOffset)); - __ Branch(&call_runtime, ne, a2, Operand(empty_fixed_array_value)); - - // Check that instance descriptors are not empty so that we can - // check for an enum cache. Leave the map in a2 for the subsequent - // prototype load. - __ lw(a2, FieldMemOperand(a1, HeapObject::kMapOffset)); - __ lw(a3, FieldMemOperand(a2, Map::kInstanceDescriptorsOrBitField3Offset)); - __ JumpIfSmi(a3, &call_runtime); - - // Check that there is an enum cache in the non-empty instance - // descriptors (a3). This is the case if the next enumeration - // index field does not contain a smi. - __ lw(a3, FieldMemOperand(a3, DescriptorArray::kEnumerationIndexOffset)); - __ JumpIfSmi(a3, &call_runtime); - - // For all objects but the receiver, check that the cache is empty. - Label check_prototype; - __ Branch(&check_prototype, eq, a1, Operand(a0)); - __ lw(a3, FieldMemOperand(a3, DescriptorArray::kEnumCacheBridgeCacheOffset)); - __ Branch(&call_runtime, ne, a3, Operand(empty_fixed_array_value)); - - // Load the prototype from the map and loop if non-null. - __ bind(&check_prototype); - __ lw(a1, FieldMemOperand(a2, Map::kPrototypeOffset)); - __ Branch(&next, ne, a1, Operand(null_value)); + __ CheckEnumCache(null_value, &call_runtime); // The enum cache is valid. Load the map of the object being // iterated over and use the cache for the iteration. @@ -1064,6 +1028,7 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { __ Push(a1, a0); // Fixed array length (as smi) and initial index. // Generate code for doing the condition check. + PrepareForBailoutForId(stmt->BodyId(), NO_REGISTERS); __ bind(&loop); // Load the current count to a0, load the length to a1. __ lw(a0, MemOperand(sp, 0 * kPointerSize)); @@ -1108,7 +1073,7 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { __ mov(result_register(), a3); // Perform the assignment as if via '='. { EffectContext context(this); - EmitAssignment(stmt->each(), stmt->AssignmentId()); + EmitAssignment(stmt->each()); } // Generate code for the body of the loop. @@ -1129,6 +1094,7 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { __ Drop(5); // Exit and decrement the loop depth. + PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS); __ bind(&exit); decrement_loop_depth(); } @@ -1899,7 +1865,7 @@ void FullCodeGenerator::EmitBinaryOp(BinaryOperation* expr, } -void FullCodeGenerator::EmitAssignment(Expression* expr, int bailout_ast_id) { +void FullCodeGenerator::EmitAssignment(Expression* expr) { // Invalid left-hand sides are rewritten to have a 'throw // ReferenceError' on the left-hand side. if (!expr->IsValidLeftHandSide()) { @@ -1951,7 +1917,6 @@ void FullCodeGenerator::EmitAssignment(Expression* expr, int bailout_ast_id) { break; } } - PrepareForBailoutForId(bailout_ast_id, TOS_REG); context()->Plug(v0); } diff --git a/src/mips/lithium-codegen-mips.cc b/src/mips/lithium-codegen-mips.cc index 5550605..5650d42 100644 --- a/src/mips/lithium-codegen-mips.cc +++ b/src/mips/lithium-codegen-mips.cc @@ -4836,6 +4836,89 @@ void LCodeGen::DoOsrEntry(LOsrEntry* instr) { } +void LCodeGen::DoForInPrepareMap(LForInPrepareMap* instr) { + Register result = ToRegister(instr->result()); + Register object = ToRegister(instr->object()); + __ LoadRoot(at, Heap::kUndefinedValueRootIndex); + DeoptimizeIf(eq, instr->environment(), object, Operand(at)); + + Register null_value = t1; + __ LoadRoot(null_value, Heap::kNullValueRootIndex); + DeoptimizeIf(eq, instr->environment(), object, Operand(null_value)); + + __ And(at, object, kSmiTagMask); + DeoptimizeIf(eq, instr->environment(), at, Operand(zero_reg)); + + STATIC_ASSERT(FIRST_JS_PROXY_TYPE == FIRST_SPEC_OBJECT_TYPE); + __ GetObjectType(object, a1, a1); + DeoptimizeIf(le, instr->environment(), a1, Operand(LAST_JS_PROXY_TYPE)); + + Label use_cache, call_runtime; + ASSERT(object.is(a0)); + __ CheckEnumCache(null_value, &call_runtime); + + __ lw(result, FieldMemOperand(object, HeapObject::kMapOffset)); + __ Branch(&use_cache); + + // Get the set of properties to enumerate. + __ bind(&call_runtime); + __ push(object); + CallRuntime(Runtime::kGetPropertyNamesFast, 1, instr); + + __ lw(a1, FieldMemOperand(v0, HeapObject::kMapOffset)); + ASSERT(result.is(v0)); + __ LoadRoot(at, Heap::kMetaMapRootIndex); + DeoptimizeIf(ne, instr->environment(), a1, Operand(at)); + __ bind(&use_cache); +} + + +void LCodeGen::DoForInCacheArray(LForInCacheArray* instr) { + Register map = ToRegister(instr->map()); + Register result = ToRegister(instr->result()); + __ LoadInstanceDescriptors(map, result); + __ lw(result, + FieldMemOperand(result, DescriptorArray::kEnumerationIndexOffset)); + __ lw(result, + FieldMemOperand(result, FixedArray::SizeFor(instr->idx()))); + DeoptimizeIf(eq, instr->environment(), result, Operand(zero_reg)); +} + + +void LCodeGen::DoCheckMapValue(LCheckMapValue* instr) { + Register object = ToRegister(instr->value()); + Register map = ToRegister(instr->map()); + __ lw(scratch0(), FieldMemOperand(object, HeapObject::kMapOffset)); + DeoptimizeIf(ne, instr->environment(), map, Operand(scratch0())); +} + + +void LCodeGen::DoLoadFieldByIndex(LLoadFieldByIndex* instr) { + Register object = ToRegister(instr->object()); + Register index = ToRegister(instr->index()); + Register result = ToRegister(instr->result()); + Register scratch = scratch0(); + + Label out_of_object, done; + __ Branch(USE_DELAY_SLOT, &out_of_object, lt, index, Operand(zero_reg)); + __ sll(scratch, index, kPointerSizeLog2 - kSmiTagSize); // In delay slot. + + STATIC_ASSERT(kPointerSizeLog2 > kSmiTagSize); + __ Addu(scratch, object, scratch); + __ lw(result, FieldMemOperand(scratch, JSObject::kHeaderSize)); + + __ Branch(&done); + + __ bind(&out_of_object); + __ lw(result, FieldMemOperand(object, JSObject::kPropertiesOffset)); + // Index is equal to negated out of object property index plus 1. + __ Subu(scratch, result, scratch); + __ lw(result, FieldMemOperand(scratch, + FixedArray::kHeaderSize - kPointerSize)); + __ bind(&done); +} + + #undef __ } } // namespace v8::internal diff --git a/src/mips/lithium-mips.cc b/src/mips/lithium-mips.cc index c759ac8..4d86882 100644 --- a/src/mips/lithium-mips.cc +++ b/src/mips/lithium-mips.cc @@ -2308,4 +2308,32 @@ LInstruction* LChunkBuilder::DoIn(HIn* instr) { } +LInstruction* LChunkBuilder::DoForInPrepareMap(HForInPrepareMap* instr) { + LOperand* object = UseFixed(instr->enumerable(), a0); + LForInPrepareMap* result = new LForInPrepareMap(object); + return MarkAsCall(DefineFixed(result, v0), instr, CAN_DEOPTIMIZE_EAGERLY); +} + + +LInstruction* LChunkBuilder::DoForInCacheArray(HForInCacheArray* instr) { + LOperand* map = UseRegister(instr->map()); + return AssignEnvironment(DefineAsRegister( + new LForInCacheArray(map))); +} + + +LInstruction* LChunkBuilder::DoCheckMapValue(HCheckMapValue* instr) { + LOperand* value = UseRegisterAtStart(instr->value()); + LOperand* map = UseRegisterAtStart(instr->map()); + return AssignEnvironment(new LCheckMapValue(value, map)); +} + + +LInstruction* LChunkBuilder::DoLoadFieldByIndex(HLoadFieldByIndex* instr) { + LOperand* object = UseRegister(instr->object()); + LOperand* index = UseRegister(instr->index()); + return DefineAsRegister(new LLoadFieldByIndex(object, index)); +} + + } } // namespace v8::internal diff --git a/src/mips/lithium-mips.h b/src/mips/lithium-mips.h index fbeadd5..cc9d3b1 100644 --- a/src/mips/lithium-mips.h +++ b/src/mips/lithium-mips.h @@ -173,7 +173,11 @@ class LCodeGen; V(TypeofIsAndBranch) \ V(UnaryMathOperation) \ V(UnknownOSRValue) \ - V(ValueOf) + V(ValueOf) \ + V(ForInPrepareMap) \ + V(ForInCacheArray) \ + V(CheckMapValue) \ + V(LoadFieldByIndex) #define DECLARE_CONCRETE_INSTRUCTION(type, mnemonic) \ @@ -2072,6 +2076,62 @@ class LIn: public LTemplateInstruction<1, 2, 0> { }; +class LForInPrepareMap: public LTemplateInstruction<1, 1, 0> { + public: + explicit LForInPrepareMap(LOperand* object) { + inputs_[0] = object; + } + + LOperand* object() { return inputs_[0]; } + + DECLARE_CONCRETE_INSTRUCTION(ForInPrepareMap, "for-in-prepare-map") +}; + + +class LForInCacheArray: public LTemplateInstruction<1, 1, 0> { + public: + explicit LForInCacheArray(LOperand* map) { + inputs_[0] = map; + } + + LOperand* map() { return inputs_[0]; } + + DECLARE_CONCRETE_INSTRUCTION(ForInCacheArray, "for-in-cache-array") + + int idx() { + return HForInCacheArray::cast(this->hydrogen_value())->idx(); + } +}; + + +class LCheckMapValue: public LTemplateInstruction<0, 2, 0> { + public: + LCheckMapValue(LOperand* value, LOperand* map) { + inputs_[0] = value; + inputs_[1] = map; + } + + LOperand* value() { return inputs_[0]; } + LOperand* map() { return inputs_[1]; } + + DECLARE_CONCRETE_INSTRUCTION(CheckMapValue, "check-map-value") +}; + + +class LLoadFieldByIndex: public LTemplateInstruction<1, 2, 0> { + public: + LLoadFieldByIndex(LOperand* object, LOperand* index) { + inputs_[0] = object; + inputs_[1] = index; + } + + LOperand* object() { return inputs_[0]; } + LOperand* index() { return inputs_[1]; } + + DECLARE_CONCRETE_INSTRUCTION(LoadFieldByIndex, "load-field-by-index") +}; + + class LChunkBuilder; class LChunk: public ZoneObject { public: diff --git a/src/mips/macro-assembler-mips.cc b/src/mips/macro-assembler-mips.cc index 7a733bc..77d03b5 100644 --- a/src/mips/macro-assembler-mips.cc +++ b/src/mips/macro-assembler-mips.cc @@ -5093,6 +5093,48 @@ void MacroAssembler::LoadInstanceDescriptors(Register map, } +void MacroAssembler::CheckEnumCache(Register null_value, Label* call_runtime) { + Label next; + // Preload a couple of values used in the loop. + Register empty_fixed_array_value = t2; + LoadRoot(empty_fixed_array_value, Heap::kEmptyFixedArrayRootIndex); + Register empty_descriptor_array_value = t3; + LoadRoot(empty_descriptor_array_value, + Heap::kEmptyDescriptorArrayRootIndex); + mov(a1, a0); + bind(&next); + + // Check that there are no elements. Register a1 contains the + // current JS object we've reached through the prototype chain. + lw(a2, FieldMemOperand(a1, JSObject::kElementsOffset)); + Branch(call_runtime, ne, a2, Operand(empty_fixed_array_value)); + + // Check that instance descriptors are not empty so that we can + // check for an enum cache. Leave the map in a2 for the subsequent + // prototype load. + lw(a2, FieldMemOperand(a1, HeapObject::kMapOffset)); + lw(a3, FieldMemOperand(a2, Map::kInstanceDescriptorsOrBitField3Offset)); + JumpIfSmi(a3, call_runtime); + + // Check that there is an enum cache in the non-empty instance + // descriptors (a3). This is the case if the next enumeration + // index field does not contain a smi. + lw(a3, FieldMemOperand(a3, DescriptorArray::kEnumerationIndexOffset)); + JumpIfSmi(a3, call_runtime); + + // For all objects but the receiver, check that the cache is empty. + Label check_prototype; + Branch(&check_prototype, eq, a1, Operand(a0)); + lw(a3, FieldMemOperand(a3, DescriptorArray::kEnumCacheBridgeCacheOffset)); + Branch(call_runtime, ne, a3, Operand(empty_fixed_array_value)); + + // Load the prototype from the map and loop if non-null. + bind(&check_prototype); + lw(a1, FieldMemOperand(a2, Map::kPrototypeOffset)); + Branch(&next, ne, a1, Operand(null_value)); +} + + void MacroAssembler::ClampUint8(Register output_reg, Register input_reg) { ASSERT(!output_reg.is(input_reg)); Label done; diff --git a/src/mips/macro-assembler-mips.h b/src/mips/macro-assembler-mips.h index 56a3433..6ae8657 100644 --- a/src/mips/macro-assembler-mips.h +++ b/src/mips/macro-assembler-mips.h @@ -1353,6 +1353,10 @@ class MacroAssembler: public Assembler { Register value, Register scratch); + // 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); + private: void CallCFunctionHelper(Register function, int num_reg_arguments,