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:
verwaest@chromium.org 2014-07-22 14:27:53 +00:00
parent 9cabfc4a65
commit 9b3f0cbcb5
12 changed files with 51 additions and 145 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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;
};

View File

@ -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_;
};

View File

@ -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();
}

View File

@ -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);

View File

@ -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);

View File

@ -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.

View File

@ -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);
}

View File

@ -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

View File

@ -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(

View File

@ -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);