Commit b4a0a4dd authored by vegorov@chromium.org's avatar vegorov@chromium.org

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 <kalmard@homejinni.com>.

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@10865 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 9f375ea8
......@@ -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);
}
......
......@@ -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
......@@ -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
......@@ -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:
......
......@@ -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;
......
......@@ -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,
......
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