Optimize array-length and fast element loads.

1. Separating out the instance-type check from the array-length operation.

2. I also changed the bounds-check on keyed loads to use the length property
for JS arrays (like we do for array stores).

The new pattern should use less registers and allow more checks to be eliminated.

Review URL: http://codereview.chromium.org/5961016

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@6125 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 24e560fe
......@@ -1666,19 +1666,15 @@ LInstruction* LChunkBuilder::DoClassOfTest(HClassOfTest* instr) {
}
LInstruction* LChunkBuilder::DoArrayLength(HArrayLength* instr) {
LOperand* array = NULL;
LOperand* temporary = NULL;
LInstruction* LChunkBuilder::DoJSArrayLength(HJSArrayLength* instr) {
LOperand* array = UseRegisterAtStart(instr->value());
return DefineAsRegister(new LJSArrayLength(array));
}
if (instr->value()->IsLoadElements()) {
array = UseRegisterAtStart(instr->value());
} else {
array = UseRegister(instr->value());
temporary = TempRegister();
}
LInstruction* result = new LArrayLength(array, temporary);
return AssignEnvironment(DefineAsRegister(result));
LInstruction* LChunkBuilder::DoFixedArrayLength(HFixedArrayLength* instr) {
LOperand* array = UseRegisterAtStart(instr->value());
return DefineAsRegister(new LFixedArrayLength(array));
}
......
......@@ -101,7 +101,8 @@ class Translation;
// LStoreNamedField
// LStoreNamedGeneric
// LUnaryOperation
// LArrayLength
// LJSArrayLength
// LFixedArrayLength
// LBitNotI
// LBranch
// LCallNew
......@@ -162,7 +163,6 @@ class Translation;
V(ArgumentsLength) \
V(ArithmeticD) \
V(ArithmeticT) \
V(ArrayLength) \
V(ArrayLiteral) \
V(BitI) \
V(BitNotI) \
......@@ -196,6 +196,7 @@ class Translation;
V(Deoptimize) \
V(DivI) \
V(DoubleToI) \
V(FixedArrayLength) \
V(FunctionLiteral) \
V(Gap) \
V(GlobalObject) \
......@@ -210,6 +211,7 @@ class Translation;
V(IsObjectAndBranch) \
V(IsSmi) \
V(IsSmiAndBranch) \
V(JSArrayLength) \
V(HasInstanceType) \
V(HasInstanceTypeAndBranch) \
V(HasCachedArrayIndex) \
......@@ -1143,18 +1145,21 @@ class LCmpMapAndBranch: public LUnaryOperation {
};
class LArrayLength: public LUnaryOperation {
class LJSArrayLength: public LUnaryOperation {
public:
LArrayLength(LOperand* input, LOperand* temporary)
: LUnaryOperation(input), temporary_(temporary) { }
explicit LJSArrayLength(LOperand* input) : LUnaryOperation(input) { }
LOperand* temporary() const { return temporary_; }
DECLARE_CONCRETE_INSTRUCTION(JSArrayLength, "js-array-length")
DECLARE_HYDROGEN_ACCESSOR(JSArrayLength)
};
DECLARE_CONCRETE_INSTRUCTION(ArrayLength, "array-length")
DECLARE_HYDROGEN_ACCESSOR(ArrayLength)
private:
LOperand* temporary_;
class LFixedArrayLength: public LUnaryOperation {
public:
explicit LFixedArrayLength(LOperand* input) : LUnaryOperation(input) { }
DECLARE_CONCRETE_INSTRUCTION(FixedArrayLength, "fixed-array-length")
DECLARE_HYDROGEN_ACCESSOR(FixedArrayLength)
};
......
......@@ -898,24 +898,19 @@ void LCodeGen::DoConstantT(LConstantT* instr) {
}
void LCodeGen::DoArrayLength(LArrayLength* instr) {
void LCodeGen::DoJSArrayLength(LJSArrayLength* instr) {
Register result = ToRegister(instr->result());
if (instr->hydrogen()->value()->IsLoadElements()) {
// We load the length directly from the elements array.
Register elements = ToRegister(instr->input());
__ ldr(result, FieldMemOperand(elements, FixedArray::kLengthOffset));
} else {
// Check that the receiver really is an array.
Register array = ToRegister(instr->input());
Register temporary = ToRegister(instr->temporary());
__ CompareObjectType(array, temporary, temporary, JS_ARRAY_TYPE);
DeoptimizeIf(ne, instr->environment());
// Load length directly from the array.
__ ldr(result, FieldMemOperand(array, JSArray::kLengthOffset));
}
Abort("DoArrayLength untested.");
Abort("DoJSArrayLength untested.");
}
void LCodeGen::DoFixedArrayLength(LFixedArrayLength* instr) {
Register result = ToRegister(instr->result());
Register array = ToRegister(instr->input());
__ ldr(result, FieldMemOperand(array, FixedArray::kLengthOffset));
Abort("DoFixedArrayLength untested.");
}
......
......@@ -118,7 +118,6 @@ class LChunkBuilder;
// HStoreKeyedFastElement
// HStoreKeyedGeneric
// HUnaryOperation
// HArrayLength
// HBitNot
// HChange
// HCheckFunction
......@@ -128,6 +127,8 @@ class LChunkBuilder;
// HCheckPrototypeMaps
// HCheckSmi
// HDeleteProperty
// HFixedArrayLength
// HJSArrayLength
// HLoadElements
// HTypeofIs
// HLoadNamedField
......@@ -171,7 +172,6 @@ class LChunkBuilder;
V(ArgumentsElements) \
V(ArgumentsLength) \
V(ArgumentsObject) \
V(ArrayLength) \
V(ArrayLiteral) \
V(BitAnd) \
V(BitNot) \
......@@ -204,6 +204,7 @@ class LChunkBuilder;
V(Deoptimize) \
V(Div) \
V(EnterInlined) \
V(FixedArrayLength) \
V(FunctionLiteral) \
V(GlobalObject) \
V(GlobalReceiver) \
......@@ -214,6 +215,7 @@ class LChunkBuilder;
V(IsSmi) \
V(HasInstanceType) \
V(HasCachedArrayIndex) \
V(JSArrayLength) \
V(ClassOfTest) \
V(LeaveInlined) \
V(LoadElements) \
......@@ -1339,9 +1341,9 @@ class HCallRuntime: public HCall {
};
class HArrayLength: public HUnaryOperation {
class HJSArrayLength: public HUnaryOperation {
public:
explicit HArrayLength(HValue* value) : HUnaryOperation(value) {
explicit HJSArrayLength(HValue* value) : HUnaryOperation(value) {
// The length of an array is stored as a tagged value in the array
// object. It is guaranteed to be 32 bit integer, but it can be
// represented as either a smi or heap number.
......@@ -1354,7 +1356,23 @@ class HArrayLength: public HUnaryOperation {
return Representation::Tagged();
}
DECLARE_CONCRETE_INSTRUCTION(ArrayLength, "array_length")
DECLARE_CONCRETE_INSTRUCTION(JSArrayLength, "js_array_length")
};
class HFixedArrayLength: public HUnaryOperation {
public:
explicit HFixedArrayLength(HValue* value) : HUnaryOperation(value) {
set_representation(Representation::Tagged());
SetFlag(kDependsOnArrayLengths);
SetFlag(kUseGVN);
}
virtual Representation RequiredInputRepresentation(int index) const {
return Representation::Tagged();
}
DECLARE_CONCRETE_INSTRUCTION(FixedArrayLength, "fixed_array_length")
};
......
......@@ -3643,9 +3643,18 @@ HInstruction* HGraphBuilder::BuildLoadKeyedFastElement(HValue* object,
Handle<Map> map = expr->GetMonomorphicReceiverType();
ASSERT(map->has_fast_elements());
AddInstruction(new HCheckMap(object, map));
HInstruction* elements = AddInstruction(new HLoadElements(object));
HInstruction* length = AddInstruction(new HArrayLength(elements));
bool is_array = (map->instance_type() == JS_ARRAY_TYPE);
HLoadElements* elements = new HLoadElements(object);
HInstruction* length = NULL;
if (is_array) {
length = AddInstruction(new HJSArrayLength(object));
AddInstruction(new HBoundsCheck(key, length));
AddInstruction(elements);
} else {
AddInstruction(elements);
length = AddInstruction(new HFixedArrayLength(elements));
AddInstruction(new HBoundsCheck(key, length));
}
return new HLoadKeyedFastElement(elements, key);
}
......@@ -3671,9 +3680,9 @@ HInstruction* HGraphBuilder::BuildStoreKeyedFastElement(HValue* object,
bool is_array = (map->instance_type() == JS_ARRAY_TYPE);
HInstruction* length = NULL;
if (is_array) {
length = AddInstruction(new HArrayLength(object));
length = AddInstruction(new HJSArrayLength(object));
} else {
length = AddInstruction(new HArrayLength(elements));
length = AddInstruction(new HFixedArrayLength(elements));
}
AddInstruction(new HBoundsCheck(key, length));
return new HStoreKeyedFastElement(elements, key, val);
......@@ -3720,7 +3729,8 @@ void HGraphBuilder::VisitProperty(Property* expr) {
if (expr->IsArrayLength()) {
HValue* array = Pop();
AddInstruction(new HCheckNonSmi(array));
instr = new HArrayLength(array);
AddInstruction(new HCheckInstanceType(array, JS_ARRAY_TYPE, JS_ARRAY_TYPE));
instr = new HJSArrayLength(array);
} else if (expr->IsFunctionPrototype()) {
HValue* function = Pop();
......@@ -4859,7 +4869,9 @@ void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
switch (op) {
case Token::EQ:
case Token::EQ_STRICT: {
AddInstruction(new HCheckNonSmi(left));
AddInstruction(HCheckInstanceType::NewIsJSObjectOrJSFunction(left));
AddInstruction(new HCheckNonSmi(right));
AddInstruction(HCheckInstanceType::NewIsJSObjectOrJSFunction(right));
instr = new HCompareJSObjectEq(left, right);
break;
......
......@@ -977,23 +977,17 @@ void LCodeGen::DoConstantT(LConstantT* instr) {
}
void LCodeGen::DoArrayLength(LArrayLength* instr) {
void LCodeGen::DoJSArrayLength(LJSArrayLength* instr) {
Register result = ToRegister(instr->result());
if (instr->hydrogen()->value()->IsLoadElements()) {
// We load the length directly from the elements array.
Register elements = ToRegister(instr->input());
__ mov(result, FieldOperand(elements, FixedArray::kLengthOffset));
} else {
// Check that the receiver really is an array.
Register array = ToRegister(instr->input());
Register temporary = ToRegister(instr->temporary());
__ CmpObjectType(array, JS_ARRAY_TYPE, temporary);
DeoptimizeIf(not_equal, instr->environment());
// Load length directly from the array.
__ mov(result, FieldOperand(array, JSArray::kLengthOffset));
}
}
void LCodeGen::DoFixedArrayLength(LFixedArrayLength* instr) {
Register result = ToRegister(instr->result());
Register array = ToRegister(instr->input());
__ mov(result, FieldOperand(array, FixedArray::kLengthOffset));
}
......@@ -2933,9 +2927,6 @@ void LCodeGen::DoCheckInstanceType(LCheckInstanceType* instr) {
InstanceType first = instr->hydrogen()->first();
InstanceType last = instr->hydrogen()->last();
__ test(input, Immediate(kSmiTagMask));
DeoptimizeIf(zero, instr->environment());
__ mov(temp, FieldOperand(input, HeapObject::kMapOffset));
__ cmpb(FieldOperand(temp, Map::kInstanceTypeOffset),
static_cast<int8_t>(first));
......
......@@ -1677,19 +1677,15 @@ LInstruction* LChunkBuilder::DoClassOfTest(HClassOfTest* instr) {
}
LInstruction* LChunkBuilder::DoArrayLength(HArrayLength* instr) {
LOperand* array = NULL;
LOperand* temporary = NULL;
LInstruction* LChunkBuilder::DoJSArrayLength(HJSArrayLength* instr) {
LOperand* array = UseRegisterAtStart(instr->value());
return DefineAsRegister(new LJSArrayLength(array));
}
if (instr->value()->IsLoadElements()) {
array = UseRegisterAtStart(instr->value());
} else {
array = UseRegister(instr->value());
temporary = TempRegister();
}
LInstruction* result = new LArrayLength(array, temporary);
return AssignEnvironment(DefineAsRegister(result));
LInstruction* LChunkBuilder::DoFixedArrayLength(HFixedArrayLength* instr) {
LOperand* array = UseRegisterAtStart(instr->value());
return DefineAsRegister(new LFixedArrayLength(array));
}
......
......@@ -104,7 +104,6 @@ class LGapNode;
// LStoreNamedField
// LStoreNamedGeneric
// LUnaryOperation
// LArrayLength
// LBitNotI
// LBranch
// LCallNew
......@@ -117,6 +116,7 @@ class LGapNode;
// LClassOfTestAndBranch
// LDeleteProperty
// LDoubleToI
// LFixedArrayLength
// LHasCachedArrayIndex
// LHasCachedArrayIndexAndBranch
// LHasInstanceType
......@@ -128,6 +128,7 @@ class LGapNode;
// LIsObjectAndBranch
// LIsSmi
// LIsSmiAndBranch
// LJSArrayLength
// LLoadNamedField
// LLoadNamedGeneric
// LLoadFunctionPrototype
......@@ -165,7 +166,6 @@ class LGapNode;
V(ArgumentsLength) \
V(ArithmeticD) \
V(ArithmeticT) \
V(ArrayLength) \
V(ArrayLiteral) \
V(BitI) \
V(BitNotI) \
......@@ -204,6 +204,7 @@ class LGapNode;
V(GlobalObject) \
V(GlobalReceiver) \
V(Goto) \
V(FixedArrayLength) \
V(InstanceOf) \
V(InstanceOfAndBranch) \
V(Integer32ToDouble) \
......@@ -213,6 +214,7 @@ class LGapNode;
V(IsObjectAndBranch) \
V(IsSmi) \
V(IsSmiAndBranch) \
V(JSArrayLength) \
V(HasInstanceType) \
V(HasInstanceTypeAndBranch) \
V(HasCachedArrayIndex) \
......@@ -1148,18 +1150,21 @@ class LCmpMapAndBranch: public LUnaryOperation {
};
class LArrayLength: public LUnaryOperation {
class LJSArrayLength: public LUnaryOperation {
public:
LArrayLength(LOperand* input, LOperand* temporary)
: LUnaryOperation(input), temporary_(temporary) { }
explicit LJSArrayLength(LOperand* input) : LUnaryOperation(input) { }
LOperand* temporary() const { return temporary_; }
DECLARE_CONCRETE_INSTRUCTION(JSArrayLength, "js-array-length")
DECLARE_HYDROGEN_ACCESSOR(JSArrayLength)
};
DECLARE_CONCRETE_INSTRUCTION(ArrayLength, "array-length")
DECLARE_HYDROGEN_ACCESSOR(ArrayLength)
private:
LOperand* temporary_;
class LFixedArrayLength: public LUnaryOperation {
public:
explicit LFixedArrayLength(LOperand* input) : LUnaryOperation(input) { }
DECLARE_CONCRETE_INSTRUCTION(FixedArrayLength, "fixed-array-length")
DECLARE_HYDROGEN_ACCESSOR(FixedArrayLength)
};
......
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