Replace ICStub for array.length with hydrogen stub
BUG= Review URL: https://codereview.chromium.org/12700006 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14090 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
15ab3a0612
commit
e357ddc249
@ -4488,35 +4488,6 @@ void InstanceofStub::Generate(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
|
||||
void ArrayLengthStub::Generate(MacroAssembler* masm) {
|
||||
Label miss;
|
||||
Register receiver;
|
||||
if (kind() == Code::KEYED_LOAD_IC) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- lr : return address
|
||||
// -- r0 : key
|
||||
// -- r1 : receiver
|
||||
// -----------------------------------
|
||||
__ cmp(r0, Operand(masm->isolate()->factory()->length_string()));
|
||||
__ b(ne, &miss);
|
||||
receiver = r1;
|
||||
} else {
|
||||
ASSERT(kind() == Code::LOAD_IC);
|
||||
// ----------- S t a t e -------------
|
||||
// -- r2 : name
|
||||
// -- lr : return address
|
||||
// -- r0 : receiver
|
||||
// -- sp[0] : receiver
|
||||
// -----------------------------------
|
||||
receiver = r0;
|
||||
}
|
||||
|
||||
StubCompiler::GenerateLoadArrayLength(masm, receiver, r3, &miss);
|
||||
__ bind(&miss);
|
||||
StubCompiler::TailCallBuiltin(masm, StubCompiler::MissBuiltin(kind()));
|
||||
}
|
||||
|
||||
|
||||
void FunctionPrototypeStub::Generate(MacroAssembler* masm) {
|
||||
Label miss;
|
||||
Register receiver;
|
||||
|
@ -414,12 +414,9 @@ void Property::RecordTypeFeedback(TypeFeedbackOracle* oracle,
|
||||
is_monomorphic_ = oracle->LoadIsMonomorphicNormal(this);
|
||||
receiver_types_.Clear();
|
||||
if (key()->IsPropertyName()) {
|
||||
ArrayLengthStub array_stub(Code::LOAD_IC);
|
||||
FunctionPrototypeStub proto_stub(Code::LOAD_IC);
|
||||
StringLengthStub string_stub(Code::LOAD_IC, false);
|
||||
if (oracle->LoadIsStub(this, &array_stub)) {
|
||||
is_array_length_ = true;
|
||||
} else if (oracle->LoadIsStub(this, &string_stub)) {
|
||||
if (oracle->LoadIsStub(this, &string_stub)) {
|
||||
is_string_length_ = true;
|
||||
} else if (oracle->LoadIsStub(this, &proto_stub)) {
|
||||
is_function_prototype_ = true;
|
||||
|
@ -1488,7 +1488,6 @@ class Property: public Expression {
|
||||
virtual KeyedAccessStoreMode GetStoreMode() {
|
||||
return STANDARD_STORE;
|
||||
}
|
||||
bool IsArrayLength() { return is_array_length_; }
|
||||
bool IsUninitialized() { return is_uninitialized_; }
|
||||
TypeFeedbackId PropertyFeedbackId() { return reuse(id()); }
|
||||
|
||||
@ -1504,7 +1503,6 @@ class Property: public Expression {
|
||||
load_id_(GetNextId(isolate)),
|
||||
is_monomorphic_(false),
|
||||
is_uninitialized_(false),
|
||||
is_array_length_(false),
|
||||
is_string_length_(false),
|
||||
is_string_access_(false),
|
||||
is_function_prototype_(false) { }
|
||||
@ -1518,7 +1516,6 @@ class Property: public Expression {
|
||||
SmallMapList receiver_types_;
|
||||
bool is_monomorphic_ : 1;
|
||||
bool is_uninitialized_ : 1;
|
||||
bool is_array_length_ : 1;
|
||||
bool is_string_length_ : 1;
|
||||
bool is_string_access_ : 1;
|
||||
bool is_function_prototype_ : 1;
|
||||
|
@ -47,7 +47,6 @@ namespace internal {
|
||||
V(Compare) \
|
||||
V(CompareIC) \
|
||||
V(MathPow) \
|
||||
V(ArrayLength) \
|
||||
V(StringLength) \
|
||||
V(FunctionPrototype) \
|
||||
V(StoreArrayLength) \
|
||||
@ -597,16 +596,6 @@ class ICStub: public PlatformCodeStub {
|
||||
};
|
||||
|
||||
|
||||
class ArrayLengthStub: public ICStub {
|
||||
public:
|
||||
explicit ArrayLengthStub(Code::Kind kind) : ICStub(kind) { }
|
||||
virtual void Generate(MacroAssembler* masm);
|
||||
|
||||
private:
|
||||
virtual CodeStub::Major MajorKey() { return ArrayLength; }
|
||||
};
|
||||
|
||||
|
||||
class FunctionPrototypeStub: public ICStub {
|
||||
public:
|
||||
explicit FunctionPrototypeStub(Code::Kind kind) : ICStub(kind) { }
|
||||
|
@ -1235,6 +1235,13 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess(
|
||||
}
|
||||
|
||||
|
||||
HInstruction* HGraphBuilder::BuildFastArrayLengthLoad(HValue* object,
|
||||
HValue* typecheck) {
|
||||
Zone* zone = this->zone();
|
||||
return new (zone) HJSArrayLength(object, typecheck, HType::Smi());
|
||||
}
|
||||
|
||||
|
||||
HValue* HGraphBuilder::BuildAllocateElements(HValue* context,
|
||||
ElementsKind kind,
|
||||
HValue* capacity) {
|
||||
@ -6287,6 +6294,12 @@ static int ComputeLoadStoreFieldIndex(Handle<Map> type,
|
||||
}
|
||||
|
||||
|
||||
void HOptimizedGraphBuilder::AddCheckMap(HValue* object, Handle<Map> map) {
|
||||
AddInstruction(new(zone()) HCheckNonSmi(object));
|
||||
AddInstruction(new(zone()) HCheckMaps(object, map, zone()));
|
||||
}
|
||||
|
||||
|
||||
void HOptimizedGraphBuilder::AddCheckMapsWithTransitions(HValue* object,
|
||||
Handle<Map> map) {
|
||||
AddInstruction(new(zone()) HCheckNonSmi(object));
|
||||
@ -6398,15 +6411,39 @@ HInstruction* HOptimizedGraphBuilder::BuildStoreNamedMonomorphic(
|
||||
}
|
||||
|
||||
|
||||
void HOptimizedGraphBuilder::HandlePolymorphicLoadNamedField(
|
||||
bool HOptimizedGraphBuilder::HandlePolymorphicArrayLengthLoad(
|
||||
Property* expr,
|
||||
HValue* object,
|
||||
SmallMapList* types,
|
||||
Handle<String> name) {
|
||||
if (!name->Equals(isolate()->heap()->length_string())) return false;
|
||||
|
||||
for (int i = 0; i < types->length(); i++) {
|
||||
if (types->at(i)->instance_type() != JS_ARRAY_TYPE) return false;
|
||||
}
|
||||
|
||||
AddInstruction(new(zone()) HCheckNonSmi(object));
|
||||
HInstruction* typecheck =
|
||||
AddInstruction(HCheckInstanceType::NewIsJSArray(object, zone()));
|
||||
HInstruction* instr = BuildFastArrayLengthLoad(object, typecheck);
|
||||
instr->set_position(expr->position());
|
||||
ast_context()->ReturnInstruction(instr, expr->id());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void HOptimizedGraphBuilder::HandlePolymorphicLoadNamedField(Property* expr,
|
||||
HValue* object,
|
||||
SmallMapList* types,
|
||||
Handle<String> name) {
|
||||
int count = 0;
|
||||
int previous_field_offset = 0;
|
||||
bool previous_field_is_in_object = false;
|
||||
bool is_monomorphic_field = true;
|
||||
|
||||
if (HandlePolymorphicArrayLengthLoad(expr, object, types, name))
|
||||
return;
|
||||
|
||||
Handle<Map> map;
|
||||
LookupResult lookup(isolate());
|
||||
for (int i = 0; i < types->length() && count < kMaxLoadPolymorphism; ++i) {
|
||||
@ -7042,16 +7079,25 @@ HInstruction* HOptimizedGraphBuilder::BuildLoadNamedMonomorphic(
|
||||
Handle<Map> map) {
|
||||
// Handle a load from a known field.
|
||||
ASSERT(!map->is_dictionary_map());
|
||||
|
||||
// Handle access to various length properties
|
||||
if (name->Equals(isolate()->heap()->length_string())) {
|
||||
if (map->instance_type() == JS_ARRAY_TYPE) {
|
||||
AddCheckMapsWithTransitions(object, map);
|
||||
return BuildFastArrayLengthLoad(object, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
LookupResult lookup(isolate());
|
||||
map->LookupDescriptor(NULL, *name, &lookup);
|
||||
if (lookup.IsField()) {
|
||||
AddCheckMapsWithTransitions(object, map);
|
||||
AddCheckMap(object, map);
|
||||
return BuildLoadNamedField(object, map, &lookup);
|
||||
}
|
||||
|
||||
// Handle a load of a constant known function.
|
||||
if (lookup.IsConstantFunction()) {
|
||||
AddCheckMapsWithTransitions(object, map);
|
||||
AddCheckMap(object, map);
|
||||
Handle<JSFunction> function(lookup.GetConstantFunctionFromMap(*map));
|
||||
return new(zone()) HConstant(function, Representation::Tagged());
|
||||
}
|
||||
@ -7062,7 +7108,7 @@ HInstruction* HOptimizedGraphBuilder::BuildLoadNamedMonomorphic(
|
||||
Handle<JSObject> prototype(JSObject::cast(map->prototype()));
|
||||
Handle<JSObject> holder(lookup.holder());
|
||||
Handle<Map> holder_map(holder->map());
|
||||
AddCheckMapsWithTransitions(object, map);
|
||||
AddCheckMap(object, map);
|
||||
HInstruction* holder_value = AddInstruction(
|
||||
new(zone()) HCheckPrototypeMaps(prototype, holder, zone()));
|
||||
return BuildLoadNamedField(holder_value, holder_map, &lookup);
|
||||
@ -7073,7 +7119,7 @@ HInstruction* HOptimizedGraphBuilder::BuildLoadNamedMonomorphic(
|
||||
Handle<JSObject> prototype(JSObject::cast(map->prototype()));
|
||||
Handle<JSObject> holder(lookup.holder());
|
||||
Handle<Map> holder_map(holder->map());
|
||||
AddCheckMapsWithTransitions(object, map);
|
||||
AddCheckMap(object, map);
|
||||
AddInstruction(new(zone()) HCheckPrototypeMaps(prototype, holder, zone()));
|
||||
Handle<JSFunction> function(lookup.GetConstantFunctionFromMap(*holder_map));
|
||||
return new(zone()) HConstant(function, Representation::Tagged());
|
||||
@ -7533,13 +7579,7 @@ void HOptimizedGraphBuilder::VisitProperty(Property* expr) {
|
||||
CHECK_ALIVE(VisitForValue(expr->obj()));
|
||||
|
||||
HInstruction* instr = NULL;
|
||||
if (expr->AsProperty()->IsArrayLength()) {
|
||||
HValue* array = Pop();
|
||||
AddInstruction(new(zone()) HCheckNonSmi(array));
|
||||
HInstruction* mapcheck =
|
||||
AddInstruction(HCheckInstanceType::NewIsJSArray(array, zone()));
|
||||
instr = new(zone()) HJSArrayLength(array, mapcheck);
|
||||
} else if (expr->IsStringLength()) {
|
||||
if (expr->IsStringLength()) {
|
||||
HValue* string = Pop();
|
||||
AddInstruction(new(zone()) HCheckNonSmi(string));
|
||||
AddInstruction(HCheckInstanceType::NewIsString(string, zone()));
|
||||
|
@ -939,6 +939,8 @@ class HGraphBuilder {
|
||||
KeyedAccessStoreMode store_mode,
|
||||
Representation checked_index_representation = Representation::None());
|
||||
|
||||
HInstruction* BuildFastArrayLengthLoad(HValue* object, HValue* typecheck);
|
||||
|
||||
HInstruction* BuildStoreMap(HValue* object, HValue* map, BailoutId id);
|
||||
HInstruction* BuildStoreMap(HValue* object, Handle<Map> map, BailoutId id);
|
||||
|
||||
@ -1351,6 +1353,10 @@ class HOptimizedGraphBuilder: public HGraphBuilder, public AstVisitor {
|
||||
HValue* object,
|
||||
SmallMapList* types,
|
||||
Handle<String> name);
|
||||
bool HandlePolymorphicArrayLengthLoad(Property* expr,
|
||||
HValue* object,
|
||||
SmallMapList* types,
|
||||
Handle<String> name);
|
||||
void HandlePolymorphicStoreNamedField(Assignment* expr,
|
||||
HValue* object,
|
||||
HValue* value,
|
||||
@ -1425,6 +1431,8 @@ class HOptimizedGraphBuilder: public HGraphBuilder, public AstVisitor {
|
||||
Property* expr,
|
||||
Handle<Map> map);
|
||||
|
||||
void AddCheckMap(HValue* object, Handle<Map> map);
|
||||
|
||||
void AddCheckMapsWithTransitions(HValue* object,
|
||||
Handle<Map> map);
|
||||
|
||||
|
@ -3304,25 +3304,6 @@ void MathPowStub::Generate(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
|
||||
void ArrayLengthStub::Generate(MacroAssembler* masm) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- ecx : name
|
||||
// -- edx : receiver
|
||||
// -- esp[0] : return address
|
||||
// -----------------------------------
|
||||
Label miss;
|
||||
|
||||
if (kind() == Code::KEYED_LOAD_IC) {
|
||||
__ cmp(ecx, Immediate(masm->isolate()->factory()->length_string()));
|
||||
__ j(not_equal, &miss);
|
||||
}
|
||||
|
||||
StubCompiler::GenerateLoadArrayLength(masm, edx, eax, &miss);
|
||||
__ bind(&miss);
|
||||
StubCompiler::TailCallBuiltin(masm, StubCompiler::MissBuiltin(kind()));
|
||||
}
|
||||
|
||||
|
||||
void FunctionPrototypeStub::Generate(MacroAssembler* masm) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- ecx : name
|
||||
|
66
src/ic.cc
66
src/ic.cc
@ -857,28 +857,6 @@ MaybeObject* LoadIC::Load(State state,
|
||||
return Smi::FromInt(String::cast(*string)->length());
|
||||
}
|
||||
|
||||
// Use specialized code for getting the length of arrays.
|
||||
if (object->IsJSArray() &&
|
||||
name->Equals(isolate()->heap()->length_string())) {
|
||||
Handle<Code> stub;
|
||||
if (state == UNINITIALIZED) {
|
||||
stub = pre_monomorphic_stub();
|
||||
} else if (state == PREMONOMORPHIC) {
|
||||
ArrayLengthStub array_length_stub(kind());
|
||||
stub = array_length_stub.GetCode(isolate());
|
||||
} else if (state != MEGAMORPHIC) {
|
||||
ASSERT(state != GENERIC);
|
||||
stub = megamorphic_stub();
|
||||
}
|
||||
if (!stub.is_null()) {
|
||||
set_target(*stub);
|
||||
#ifdef DEBUG
|
||||
if (FLAG_trace_ic) PrintF("[LoadIC : +#length /array]\n");
|
||||
#endif
|
||||
}
|
||||
return JSArray::cast(*object)->length();
|
||||
}
|
||||
|
||||
// Use specialized code for getting prototype of functions.
|
||||
if (object->IsJSFunction() &&
|
||||
name->Equals(isolate()->heap()->prototype_string()) &&
|
||||
@ -1037,6 +1015,22 @@ void IC::CopyICToMegamorphicCache(Handle<String> name) {
|
||||
}
|
||||
|
||||
|
||||
bool IC::IsTransitionedMapOfMonomorphicTarget(Map* receiver_map) {
|
||||
AssertNoAllocation no_allocation;
|
||||
|
||||
Map* current_map = target()->FindFirstMap();
|
||||
ElementsKind receiver_elements_kind = receiver_map->elements_kind();
|
||||
bool more_general_transition =
|
||||
IsMoreGeneralElementsKindTransition(
|
||||
current_map->elements_kind(), receiver_elements_kind);
|
||||
Map* transitioned_map = more_general_transition
|
||||
? current_map->LookupElementsTransitionMap(receiver_elements_kind)
|
||||
: NULL;
|
||||
|
||||
return transitioned_map == receiver_map;
|
||||
}
|
||||
|
||||
|
||||
// Since GC may have been invoked, by the time PatchCache is called, |state| is
|
||||
// not necessarily equal to target()->state().
|
||||
void IC::PatchCache(State state,
|
||||
@ -1054,6 +1048,17 @@ void IC::PatchCache(State state,
|
||||
// Only move to megamorphic if the target changes.
|
||||
if (target() != *code) {
|
||||
if (target()->is_load_stub()) {
|
||||
bool is_same_handler = false;
|
||||
{
|
||||
AssertNoAllocation no_allocation;
|
||||
Code* old_handler = target()->FindFirstCode();
|
||||
is_same_handler = old_handler == *code;
|
||||
}
|
||||
if (is_same_handler
|
||||
&& IsTransitionedMapOfMonomorphicTarget(receiver->map())) {
|
||||
UpdateMonomorphicIC(receiver, code, name);
|
||||
break;
|
||||
}
|
||||
if (UpdatePolymorphicIC(state, strict_mode, receiver, name, code)) {
|
||||
break;
|
||||
}
|
||||
@ -1226,6 +1231,12 @@ Handle<Code> LoadIC::ComputeLoadHandler(LookupResult* lookup,
|
||||
if (!holder->HasFastProperties()) break;
|
||||
return isolate()->stub_cache()->ComputeLoadViaGetter(
|
||||
name, receiver, holder, Handle<JSFunction>::cast(getter));
|
||||
} else if (receiver->IsJSArray() &&
|
||||
name->Equals(isolate()->heap()->length_string())) {
|
||||
PropertyIndex lengthIndex =
|
||||
PropertyIndex::NewHeaderIndex(JSArray::kLengthOffset / kPointerSize);
|
||||
return isolate()->stub_cache()->ComputeLoadField(
|
||||
name, receiver, holder, lengthIndex);
|
||||
}
|
||||
// TODO(dcarney): Handle correctly.
|
||||
if (callback->IsDeclaredAccessorInfo()) break;
|
||||
@ -1705,16 +1716,7 @@ Handle<Code> KeyedStoreIC::StoreElementStub(Handle<JSObject> receiver,
|
||||
transitioned_receiver_map =
|
||||
ComputeTransitionedMap(receiver, store_mode);
|
||||
}
|
||||
ElementsKind transitioned_kind =
|
||||
transitioned_receiver_map->elements_kind();
|
||||
bool more_general_transition =
|
||||
IsMoreGeneralElementsKindTransition(
|
||||
previous_receiver_map->elements_kind(),
|
||||
transitioned_kind);
|
||||
Map* transitioned_previous_map = more_general_transition
|
||||
? previous_receiver_map->LookupElementsTransitionMap(transitioned_kind)
|
||||
: NULL;
|
||||
if (transitioned_previous_map == *transitioned_receiver_map) {
|
||||
if (IsTransitionedMapOfMonomorphicTarget(*transitioned_receiver_map)) {
|
||||
// Element family is the same, use the "worst" case map.
|
||||
store_mode = GetNonTransitioningStoreMode(store_mode);
|
||||
return isolate()->stub_cache()->ComputeKeyedStoreElement(
|
||||
|
1
src/ic.h
1
src/ic.h
@ -176,6 +176,7 @@ class IC {
|
||||
Handle<String> name,
|
||||
Handle<Code> code);
|
||||
void CopyICToMegamorphicCache(Handle<String> name);
|
||||
bool IsTransitionedMapOfMonomorphicTarget(Map* receiver_map);
|
||||
void PatchCache(State state,
|
||||
StrictModeFlag strict_mode,
|
||||
Handle<JSObject> receiver,
|
||||
|
@ -4555,35 +4555,6 @@ void InstanceofStub::Generate(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
|
||||
void ArrayLengthStub::Generate(MacroAssembler* masm) {
|
||||
Label miss;
|
||||
Register receiver;
|
||||
if (kind() == Code::KEYED_LOAD_IC) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- ra : return address
|
||||
// -- a0 : key
|
||||
// -- a1 : receiver
|
||||
// -----------------------------------
|
||||
__ Branch(&miss, ne, a0,
|
||||
Operand(masm->isolate()->factory()->length_string()));
|
||||
receiver = a1;
|
||||
} else {
|
||||
ASSERT(kind() == Code::LOAD_IC);
|
||||
// ----------- S t a t e -------------
|
||||
// -- a2 : name
|
||||
// -- ra : return address
|
||||
// -- a0 : receiver
|
||||
// -- sp[0] : receiver
|
||||
// -----------------------------------
|
||||
receiver = a0;
|
||||
}
|
||||
|
||||
StubCompiler::GenerateLoadArrayLength(masm, receiver, a3, &miss);
|
||||
__ bind(&miss);
|
||||
StubCompiler::TailCallBuiltin(masm, StubCompiler::MissBuiltin(kind()));
|
||||
}
|
||||
|
||||
|
||||
void FunctionPrototypeStub::Generate(MacroAssembler* masm) {
|
||||
Label miss;
|
||||
Register receiver;
|
||||
|
@ -2386,34 +2386,6 @@ void MathPowStub::Generate(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
|
||||
void ArrayLengthStub::Generate(MacroAssembler* masm) {
|
||||
Label miss;
|
||||
Register receiver;
|
||||
if (kind() == Code::KEYED_LOAD_IC) {
|
||||
// ----------- S t a t e -------------
|
||||
// -- rax : key
|
||||
// -- rdx : receiver
|
||||
// -- rsp[0] : return address
|
||||
// -----------------------------------
|
||||
__ Cmp(rax, masm->isolate()->factory()->length_string());
|
||||
__ j(not_equal, &miss);
|
||||
receiver = rdx;
|
||||
} else {
|
||||
ASSERT(kind() == Code::LOAD_IC);
|
||||
// ----------- S t a t e -------------
|
||||
// -- rax : receiver
|
||||
// -- rcx : name
|
||||
// -- rsp[0] : return address
|
||||
// -----------------------------------
|
||||
receiver = rax;
|
||||
}
|
||||
|
||||
StubCompiler::GenerateLoadArrayLength(masm, receiver, r8, &miss);
|
||||
__ bind(&miss);
|
||||
StubCompiler::TailCallBuiltin(masm, StubCompiler::MissBuiltin(kind()));
|
||||
}
|
||||
|
||||
|
||||
void FunctionPrototypeStub::Generate(MacroAssembler* masm) {
|
||||
Label miss;
|
||||
Register receiver;
|
||||
|
Loading…
Reference in New Issue
Block a user