Move function prototype handling into a special handler rather than IC
Adjust hydrogen handling of function.prototype to be based on map feedback. Handle non-instance prototype loading using an IC rather than in the hydrogen instruction. In the future, remove the special instruction and replace by multiple hydrogen instructions. BUG= R=mvstanton@chromium.org Review URL: https://codereview.chromium.org/408193002 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@22526 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
9cabfc4a65
commit
9b3f0cbcb5
@ -3143,17 +3143,6 @@ void LCodeGen::DoLoadFunctionPrototype(LLoadFunctionPrototype* instr) {
|
||||
Register function = ToRegister(instr->function());
|
||||
Register result = ToRegister(instr->result());
|
||||
|
||||
// Check that the function really is a function. Load map into the
|
||||
// result register.
|
||||
__ CompareObjectType(function, result, scratch, JS_FUNCTION_TYPE);
|
||||
DeoptimizeIf(ne, instr->environment());
|
||||
|
||||
// Make sure that the function has an instance prototype.
|
||||
Label non_instance;
|
||||
__ ldrb(scratch, FieldMemOperand(result, Map::kBitFieldOffset));
|
||||
__ tst(scratch, Operand(1 << Map::kHasNonInstancePrototype));
|
||||
__ b(ne, &non_instance);
|
||||
|
||||
// Get the prototype or initial map from the function.
|
||||
__ ldr(result,
|
||||
FieldMemOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
|
||||
@ -3170,12 +3159,6 @@ void LCodeGen::DoLoadFunctionPrototype(LLoadFunctionPrototype* instr) {
|
||||
|
||||
// Get the prototype from the initial map.
|
||||
__ ldr(result, FieldMemOperand(result, Map::kPrototypeOffset));
|
||||
__ jmp(&done);
|
||||
|
||||
// Non-instance prototype: Fetch prototype from constructor field
|
||||
// in initial map.
|
||||
__ bind(&non_instance);
|
||||
__ ldr(result, FieldMemOperand(result, Map::kConstructorOffset));
|
||||
|
||||
// All done.
|
||||
__ bind(&done);
|
||||
|
@ -3332,16 +3332,6 @@ void LCodeGen::DoLoadFunctionPrototype(LLoadFunctionPrototype* instr) {
|
||||
Register result = ToRegister(instr->result());
|
||||
Register temp = ToRegister(instr->temp());
|
||||
|
||||
// Check that the function really is a function. Leaves map in the result
|
||||
// register.
|
||||
__ CompareObjectType(function, result, temp, JS_FUNCTION_TYPE);
|
||||
DeoptimizeIf(ne, instr->environment());
|
||||
|
||||
// Make sure that the function has an instance prototype.
|
||||
Label non_instance;
|
||||
__ Ldrb(temp, FieldMemOperand(result, Map::kBitFieldOffset));
|
||||
__ Tbnz(temp, Map::kHasNonInstancePrototype, &non_instance);
|
||||
|
||||
// Get the prototype or initial map from the function.
|
||||
__ Ldr(result, FieldMemOperand(function,
|
||||
JSFunction::kPrototypeOrInitialMapOffset));
|
||||
@ -3357,12 +3347,6 @@ void LCodeGen::DoLoadFunctionPrototype(LLoadFunctionPrototype* instr) {
|
||||
|
||||
// Get the prototype from the initial map.
|
||||
__ Ldr(result, FieldMemOperand(result, Map::kPrototypeOffset));
|
||||
__ B(&done);
|
||||
|
||||
// Non-instance prototype: fetch prototype from constructor field in initial
|
||||
// map.
|
||||
__ Bind(&non_instance);
|
||||
__ Ldr(result, FieldMemOperand(result, Map::kConstructorOffset));
|
||||
|
||||
// All done.
|
||||
__ Bind(&done);
|
||||
|
11
src/ast.h
11
src/ast.h
@ -1684,7 +1684,6 @@ class Property V8_FINAL : public Expression, public FeedbackSlotInterface {
|
||||
BailoutId LoadId() const { return load_id_; }
|
||||
|
||||
bool IsStringAccess() const { return is_string_access_; }
|
||||
bool IsFunctionPrototype() const { return is_function_prototype_; }
|
||||
|
||||
// Type feedback information.
|
||||
virtual bool IsMonomorphic() V8_OVERRIDE {
|
||||
@ -1702,7 +1701,6 @@ class Property V8_FINAL : public Expression, public FeedbackSlotInterface {
|
||||
}
|
||||
void set_is_uninitialized(bool b) { is_uninitialized_ = b; }
|
||||
void set_is_string_access(bool b) { is_string_access_ = b; }
|
||||
void set_is_function_prototype(bool b) { is_function_prototype_ = b; }
|
||||
void mark_for_call() { is_for_call_ = true; }
|
||||
bool IsForCall() { return is_for_call_; }
|
||||
|
||||
@ -1716,10 +1714,7 @@ class Property V8_FINAL : public Expression, public FeedbackSlotInterface {
|
||||
int PropertyFeedbackSlot() const { return property_feedback_slot_; }
|
||||
|
||||
protected:
|
||||
Property(Zone* zone,
|
||||
Expression* obj,
|
||||
Expression* key,
|
||||
int pos)
|
||||
Property(Zone* zone, Expression* obj, Expression* key, int pos)
|
||||
: Expression(zone, pos),
|
||||
obj_(obj),
|
||||
key_(key),
|
||||
@ -1727,8 +1722,7 @@ class Property V8_FINAL : public Expression, public FeedbackSlotInterface {
|
||||
property_feedback_slot_(kInvalidFeedbackSlot),
|
||||
is_for_call_(false),
|
||||
is_uninitialized_(false),
|
||||
is_string_access_(false),
|
||||
is_function_prototype_(false) { }
|
||||
is_string_access_(false) {}
|
||||
|
||||
private:
|
||||
Expression* obj_;
|
||||
@ -1740,7 +1734,6 @@ class Property V8_FINAL : public Expression, public FeedbackSlotInterface {
|
||||
bool is_for_call_ : 1;
|
||||
bool is_uninitialized_ : 1;
|
||||
bool is_string_access_ : 1;
|
||||
bool is_function_prototype_ : 1;
|
||||
};
|
||||
|
||||
|
||||
|
@ -899,14 +899,26 @@ class CallIC_ArrayStub: public CallICStub {
|
||||
};
|
||||
|
||||
|
||||
class FunctionPrototypeStub: public ICStub {
|
||||
// TODO(verwaest): Translate to hydrogen code stub.
|
||||
class FunctionPrototypeStub : public PlatformCodeStub {
|
||||
public:
|
||||
FunctionPrototypeStub(Isolate* isolate, Code::Kind kind)
|
||||
: ICStub(isolate, kind) { }
|
||||
: PlatformCodeStub(isolate) {
|
||||
bit_field_ = KindBits::encode(kind);
|
||||
}
|
||||
virtual void Generate(MacroAssembler* masm);
|
||||
virtual Code::Kind GetCodeKind() const { return Code::HANDLER; }
|
||||
virtual InlineCacheState GetICState() { return MONOMORPHIC; }
|
||||
virtual ExtraICState GetExtraICState() const { return kind(); }
|
||||
|
||||
protected:
|
||||
class KindBits : public BitField<Code::Kind, 0, 4> {};
|
||||
virtual Code::Kind kind() const { return KindBits::decode(bit_field_); }
|
||||
|
||||
private:
|
||||
virtual CodeStub::Major MajorKey() const { return FunctionPrototype; }
|
||||
virtual int MinorKey() const { return bit_field_; }
|
||||
int bit_field_;
|
||||
};
|
||||
|
||||
|
||||
|
@ -6070,6 +6070,11 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::LookupInPrototypes() {
|
||||
bool HOptimizedGraphBuilder::PropertyAccessInfo::CanAccessMonomorphic() {
|
||||
if (!CanInlinePropertyAccess(type_)) return false;
|
||||
if (IsJSObjectFieldAccessor()) return IsLoad();
|
||||
if (this->map()->function_with_prototype() &&
|
||||
!this->map()->has_non_instance_prototype() &&
|
||||
name_.is_identical_to(isolate()->factory()->prototype_string())) {
|
||||
return IsLoad();
|
||||
}
|
||||
if (!LookupDescriptor()) return false;
|
||||
if (lookup_.IsFound()) {
|
||||
if (IsLoad()) return true;
|
||||
@ -6161,6 +6166,12 @@ HInstruction* HOptimizedGraphBuilder::BuildMonomorphicAccess(
|
||||
return New<HLoadNamedField>(object, checked_object, access);
|
||||
}
|
||||
|
||||
if (info->name().is_identical_to(isolate()->factory()->prototype_string()) &&
|
||||
info->map()->function_with_prototype()) {
|
||||
ASSERT(!info->map()->has_non_instance_prototype());
|
||||
return New<HLoadFunctionPrototype>(checked_object);
|
||||
}
|
||||
|
||||
HValue* checked_holder = checked_object;
|
||||
if (info->has_holder()) {
|
||||
Handle<JSObject> prototype(JSObject::cast(info->map()->prototype()));
|
||||
@ -6575,8 +6586,7 @@ void HOptimizedGraphBuilder::HandleCompoundAssignment(Assignment* expr) {
|
||||
CHECK_ALIVE(VisitForValue(prop->obj()));
|
||||
HValue* object = Top();
|
||||
HValue* key = NULL;
|
||||
if ((!prop->IsFunctionPrototype() && !prop->key()->IsPropertyName()) ||
|
||||
prop->IsStringAccess()) {
|
||||
if (!prop->key()->IsPropertyName() || prop->IsStringAccess()) {
|
||||
CHECK_ALIVE(VisitForValue(prop->key()));
|
||||
key = Top();
|
||||
}
|
||||
@ -7286,11 +7296,6 @@ void HOptimizedGraphBuilder::BuildLoad(Property* expr,
|
||||
AddInstruction(char_code);
|
||||
instr = NewUncasted<HStringCharFromCode>(char_code);
|
||||
|
||||
} else if (expr->IsFunctionPrototype()) {
|
||||
HValue* function = Pop();
|
||||
BuildCheckHeapObject(function);
|
||||
instr = New<HLoadFunctionPrototype>(function);
|
||||
|
||||
} else if (expr->key()->IsPropertyName()) {
|
||||
Handle<String> name = expr->key()->AsLiteral()->AsPropertyName();
|
||||
HValue* object = Pop();
|
||||
@ -7330,8 +7335,7 @@ void HOptimizedGraphBuilder::VisitProperty(Property* expr) {
|
||||
if (TryArgumentsAccess(expr)) return;
|
||||
|
||||
CHECK_ALIVE(VisitForValue(expr->obj()));
|
||||
if ((!expr->IsFunctionPrototype() && !expr->key()->IsPropertyName()) ||
|
||||
expr->IsStringAccess()) {
|
||||
if (!expr->key()->IsPropertyName() || expr->IsStringAccess()) {
|
||||
CHECK_ALIVE(VisitForValue(expr->key()));
|
||||
}
|
||||
|
||||
@ -10025,8 +10029,7 @@ void HOptimizedGraphBuilder::VisitCountOperation(CountOperation* expr) {
|
||||
HValue* object = Top();
|
||||
|
||||
HValue* key = NULL;
|
||||
if ((!prop->IsFunctionPrototype() && !prop->key()->IsPropertyName()) ||
|
||||
prop->IsStringAccess()) {
|
||||
if (!prop->key()->IsPropertyName() || prop->IsStringAccess()) {
|
||||
CHECK_ALIVE(VisitForValue(prop->key()));
|
||||
key = Top();
|
||||
}
|
||||
|
@ -2998,16 +2998,6 @@ void LCodeGen::DoLoadFunctionPrototype(LLoadFunctionPrototype* instr) {
|
||||
Register temp = ToRegister(instr->temp());
|
||||
Register result = ToRegister(instr->result());
|
||||
|
||||
// Check that the function really is a function.
|
||||
__ CmpObjectType(function, JS_FUNCTION_TYPE, result);
|
||||
DeoptimizeIf(not_equal, instr->environment());
|
||||
|
||||
// Check whether the function has an instance prototype.
|
||||
Label non_instance;
|
||||
__ test_b(FieldOperand(result, Map::kBitFieldOffset),
|
||||
1 << Map::kHasNonInstancePrototype);
|
||||
__ j(not_zero, &non_instance, Label::kNear);
|
||||
|
||||
// Get the prototype or initial map from the function.
|
||||
__ mov(result,
|
||||
FieldOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
|
||||
@ -3023,12 +3013,6 @@ void LCodeGen::DoLoadFunctionPrototype(LLoadFunctionPrototype* instr) {
|
||||
|
||||
// Get the prototype from the initial map.
|
||||
__ mov(result, FieldOperand(result, Map::kPrototypeOffset));
|
||||
__ jmp(&done, Label::kNear);
|
||||
|
||||
// Non-instance prototype: Fetch prototype from constructor field
|
||||
// in the function's map.
|
||||
__ bind(&non_instance);
|
||||
__ mov(result, FieldOperand(result, Map::kConstructorOffset));
|
||||
|
||||
// All done.
|
||||
__ bind(&done);
|
||||
|
34
src/ic.cc
34
src/ic.cc
@ -558,31 +558,6 @@ MaybeHandle<Object> LoadIC::Load(Handle<Object> object, Handle<String> name) {
|
||||
return TypeError("non_object_property_load", object, name);
|
||||
}
|
||||
|
||||
if (FLAG_use_ic) {
|
||||
// Use specialized code for getting prototype of functions.
|
||||
if (object->IsJSFunction() &&
|
||||
String::Equals(isolate()->factory()->prototype_string(), name) &&
|
||||
Handle<JSFunction>::cast(object)->should_have_prototype()) {
|
||||
Handle<Code> stub;
|
||||
if (state() == UNINITIALIZED) {
|
||||
stub = pre_monomorphic_stub();
|
||||
} else if (state() == PREMONOMORPHIC) {
|
||||
FunctionPrototypeStub function_prototype_stub(isolate(), kind());
|
||||
stub = function_prototype_stub.GetCode();
|
||||
} else if (!FLAG_compiled_keyed_generic_loads && state() != MEGAMORPHIC) {
|
||||
ASSERT(state() != GENERIC);
|
||||
stub = megamorphic_stub();
|
||||
} else if (FLAG_compiled_keyed_generic_loads && state() != GENERIC) {
|
||||
stub = generic_stub();
|
||||
}
|
||||
if (!stub.is_null()) {
|
||||
set_target(*stub);
|
||||
if (FLAG_trace_ic) PrintF("[LoadIC : +#prototype /function]\n");
|
||||
}
|
||||
return Accessors::FunctionGetPrototype(Handle<JSFunction>::cast(object));
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the name is trivially convertible to an index and get
|
||||
// the element or char if so.
|
||||
uint32_t index;
|
||||
@ -959,6 +934,15 @@ Handle<Code> LoadIC::CompileHandler(LookupResult* lookup, Handle<Object> object,
|
||||
}
|
||||
}
|
||||
|
||||
// Use specialized code for getting prototype of functions.
|
||||
if (object->IsJSFunction() &&
|
||||
String::Equals(isolate()->factory()->prototype_string(), name) &&
|
||||
Handle<JSFunction>::cast(object)->should_have_prototype()) {
|
||||
Handle<Code> stub;
|
||||
FunctionPrototypeStub function_prototype_stub(isolate(), kind());
|
||||
return function_prototype_stub.GetCode();
|
||||
}
|
||||
|
||||
Handle<HeapType> type = receiver_type();
|
||||
Handle<JSObject> holder(lookup->holder());
|
||||
bool receiver_is_holder = object.is_identical_to(holder);
|
||||
|
@ -42,11 +42,10 @@ static Code::Flags CommonStubCacheChecks(Name* name, Map* map,
|
||||
ASSERT(!heap->InNewSpace(name));
|
||||
ASSERT(name->IsUniqueName());
|
||||
|
||||
// The state bits are not important to the hash function because
|
||||
// the stub cache only contains monomorphic stubs. Make sure that
|
||||
// the bits are the least significant so they will be the ones
|
||||
// masked out.
|
||||
ASSERT(Code::ExtractICStateFromFlags(flags) == MONOMORPHIC);
|
||||
// The state bits are not important to the hash function because the stub
|
||||
// cache only contains handlers. Make sure that the bits are the least
|
||||
// significant so they will be the ones masked out.
|
||||
ASSERT_EQ(Code::HANDLER, Code::ExtractKindFromFlags(flags));
|
||||
STATIC_ASSERT((Code::ICStateField::kMask & 1) == 1);
|
||||
|
||||
// Make sure that the code type and cache holder are not included in the hash.
|
||||
|
@ -175,16 +175,6 @@ bool TypeFeedbackOracle::LoadIsBuiltin(
|
||||
}
|
||||
|
||||
|
||||
bool TypeFeedbackOracle::LoadIsStub(TypeFeedbackId id, ICStub* stub) {
|
||||
Handle<Object> object = GetInfo(id);
|
||||
if (!object->IsCode()) return false;
|
||||
Handle<Code> code = Handle<Code>::cast(object);
|
||||
if (!code->is_load_stub()) return false;
|
||||
if (code->ic_state() != MONOMORPHIC) return false;
|
||||
return stub->Describes(*code);
|
||||
}
|
||||
|
||||
|
||||
void TypeFeedbackOracle::CompareType(TypeFeedbackId id,
|
||||
Type** left_type,
|
||||
Type** right_type,
|
||||
@ -264,16 +254,12 @@ Type* TypeFeedbackOracle::CountType(TypeFeedbackId id) {
|
||||
}
|
||||
|
||||
|
||||
void TypeFeedbackOracle::PropertyReceiverTypes(
|
||||
TypeFeedbackId id, Handle<String> name,
|
||||
SmallMapList* receiver_types, bool* is_prototype) {
|
||||
void TypeFeedbackOracle::PropertyReceiverTypes(TypeFeedbackId id,
|
||||
Handle<String> name,
|
||||
SmallMapList* receiver_types) {
|
||||
receiver_types->Clear();
|
||||
FunctionPrototypeStub proto_stub(isolate(), Code::LOAD_IC);
|
||||
*is_prototype = LoadIsStub(id, &proto_stub);
|
||||
if (!*is_prototype) {
|
||||
Code::Flags flags = Code::ComputeHandlerFlags(Code::LOAD_IC);
|
||||
CollectReceiverTypes(id, name, flags, receiver_types);
|
||||
}
|
||||
Code::Flags flags = Code::ComputeHandlerFlags(Code::LOAD_IC);
|
||||
CollectReceiverTypes(id, name, flags, receiver_types);
|
||||
}
|
||||
|
||||
|
||||
|
@ -41,10 +41,8 @@ class TypeFeedbackOracle: public ZoneObject {
|
||||
|
||||
KeyedAccessStoreMode GetStoreMode(TypeFeedbackId id);
|
||||
|
||||
void PropertyReceiverTypes(TypeFeedbackId id,
|
||||
Handle<String> name,
|
||||
SmallMapList* receiver_types,
|
||||
bool* is_prototype);
|
||||
void PropertyReceiverTypes(TypeFeedbackId id, Handle<String> name,
|
||||
SmallMapList* receiver_types);
|
||||
void KeyedPropertyReceiverTypes(TypeFeedbackId id,
|
||||
SmallMapList* receiver_types,
|
||||
bool* is_string);
|
||||
@ -70,7 +68,6 @@ class TypeFeedbackOracle: public ZoneObject {
|
||||
Handle<AllocationSite> GetCallNewAllocationSite(int slot);
|
||||
|
||||
bool LoadIsBuiltin(TypeFeedbackId id, Builtins::Name builtin_id);
|
||||
bool LoadIsStub(TypeFeedbackId id, ICStub* stub);
|
||||
|
||||
// TODO(1571) We can't use ToBooleanStub::Types as the return value because
|
||||
// of various cycles in our headers. Death to tons of implementations in
|
||||
|
@ -485,10 +485,7 @@ void AstTyper::VisitProperty(Property* expr) {
|
||||
Literal* lit_key = expr->key()->AsLiteral();
|
||||
ASSERT(lit_key != NULL && lit_key->value()->IsString());
|
||||
Handle<String> name = Handle<String>::cast(lit_key->value());
|
||||
bool is_prototype;
|
||||
oracle()->PropertyReceiverTypes(
|
||||
id, name, expr->GetReceiverTypes(), &is_prototype);
|
||||
expr->set_is_function_prototype(is_prototype);
|
||||
oracle()->PropertyReceiverTypes(id, name, expr->GetReceiverTypes());
|
||||
} else {
|
||||
bool is_string;
|
||||
oracle()->KeyedPropertyReceiverTypes(
|
||||
|
@ -3018,16 +3018,6 @@ void LCodeGen::DoLoadFunctionPrototype(LLoadFunctionPrototype* instr) {
|
||||
Register function = ToRegister(instr->function());
|
||||
Register result = ToRegister(instr->result());
|
||||
|
||||
// Check that the function really is a function.
|
||||
__ CmpObjectType(function, JS_FUNCTION_TYPE, result);
|
||||
DeoptimizeIf(not_equal, instr->environment());
|
||||
|
||||
// Check whether the function has an instance prototype.
|
||||
Label non_instance;
|
||||
__ testb(FieldOperand(result, Map::kBitFieldOffset),
|
||||
Immediate(1 << Map::kHasNonInstancePrototype));
|
||||
__ j(not_zero, &non_instance, Label::kNear);
|
||||
|
||||
// Get the prototype or initial map from the function.
|
||||
__ movp(result,
|
||||
FieldOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
|
||||
@ -3043,12 +3033,6 @@ void LCodeGen::DoLoadFunctionPrototype(LLoadFunctionPrototype* instr) {
|
||||
|
||||
// Get the prototype from the initial map.
|
||||
__ movp(result, FieldOperand(result, Map::kPrototypeOffset));
|
||||
__ jmp(&done, Label::kNear);
|
||||
|
||||
// Non-instance prototype: Fetch prototype from constructor field
|
||||
// in the function's map.
|
||||
__ bind(&non_instance);
|
||||
__ movp(result, FieldOperand(result, Map::kConstructorOffset));
|
||||
|
||||
// All done.
|
||||
__ bind(&done);
|
||||
|
Loading…
Reference in New Issue
Block a user