Commit 04131452 authored by vogelheim's avatar vogelheim Committed by Commit bot

Speedup access to global_proxy.* attributes/accessors.

Using a global proxy (e.g. 'window.f', 'w.f' or 'this.f') is considerably slower than evaluating just 'f'. This CL aims to perform the necessary checks at compile time and inline the accesses.

This is a follow-on CL to crrev.com/2369933005:
- The initial upload is crrev.com/2369933005 + a rebase.
- The remaining issues are the fixes requested by the reviewers on that CL.

BUG=chromium:634276, chromium:654716

Committed: https://crrev.com/8f43d748272536117008aa6a1b53ea52126261c1
Review-Url: https://codereview.chromium.org/2403003002
Cr-Original-Commit-Position: refs/heads/master@{#40153}
Cr-Commit-Position: refs/heads/master@{#40365}
parent dd144bbb
......@@ -5264,12 +5264,16 @@ void HOptimizedGraphBuilder::VisitConditional(Conditional* expr) {
}
}
bool HOptimizedGraphBuilder::CanInlineGlobalPropertyAccess(
Variable* var, LookupIterator* it, PropertyAccessType access_type) {
if (var->is_this()) return false;
return CanInlineGlobalPropertyAccess(it, access_type);
}
HOptimizedGraphBuilder::GlobalPropertyAccess
HOptimizedGraphBuilder::LookupGlobalProperty(Variable* var, LookupIterator* it,
PropertyAccessType access_type) {
if (var->is_this() || !current_info()->has_global_object()) {
return kUseGeneric;
bool HOptimizedGraphBuilder::CanInlineGlobalPropertyAccess(
LookupIterator* it, PropertyAccessType access_type) {
if (!current_info()->has_global_object()) {
return false;
}
switch (it->state()) {
......@@ -5278,17 +5282,17 @@ HOptimizedGraphBuilder::LookupGlobalProperty(Variable* var, LookupIterator* it,
case LookupIterator::INTERCEPTOR:
case LookupIterator::INTEGER_INDEXED_EXOTIC:
case LookupIterator::NOT_FOUND:
return kUseGeneric;
return false;
case LookupIterator::DATA:
if (access_type == STORE && it->IsReadOnly()) return kUseGeneric;
if (!it->GetHolder<JSObject>()->IsJSGlobalObject()) return kUseGeneric;
return kUseCell;
if (access_type == STORE && it->IsReadOnly()) return false;
if (!it->GetHolder<JSObject>()->IsJSGlobalObject()) return false;
return true;
case LookupIterator::JSPROXY:
case LookupIterator::TRANSITION:
UNREACHABLE();
}
UNREACHABLE();
return kUseGeneric;
return false;
}
......@@ -5304,6 +5308,55 @@ HValue* HOptimizedGraphBuilder::BuildContextChainWalk(Variable* var) {
return context;
}
void HOptimizedGraphBuilder::InlineGlobalPropertyLoad(LookupIterator* it,
BailoutId ast_id) {
Handle<PropertyCell> cell = it->GetPropertyCell();
top_info()->dependencies()->AssumePropertyCell(cell);
auto cell_type = it->property_details().cell_type();
if (cell_type == PropertyCellType::kConstant ||
cell_type == PropertyCellType::kUndefined) {
Handle<Object> constant_object(cell->value(), isolate());
if (constant_object->IsConsString()) {
constant_object = String::Flatten(Handle<String>::cast(constant_object));
}
HConstant* constant = New<HConstant>(constant_object);
return ast_context()->ReturnInstruction(constant, ast_id);
} else {
auto access = HObjectAccess::ForPropertyCellValue();
UniqueSet<Map>* field_maps = nullptr;
if (cell_type == PropertyCellType::kConstantType) {
switch (cell->GetConstantType()) {
case PropertyCellConstantType::kSmi:
access = access.WithRepresentation(Representation::Smi());
break;
case PropertyCellConstantType::kStableMap: {
// Check that the map really is stable. The heap object could
// have mutated without the cell updating state. In that case,
// make no promises about the loaded value except that it's a
// heap object.
access = access.WithRepresentation(Representation::HeapObject());
Handle<Map> map(HeapObject::cast(cell->value())->map());
if (map->is_stable()) {
field_maps = new (zone())
UniqueSet<Map>(Unique<Map>::CreateImmovable(map), zone());
}
break;
}
}
}
HConstant* cell_constant = Add<HConstant>(cell);
HLoadNamedField* instr;
if (field_maps == nullptr) {
instr = New<HLoadNamedField>(cell_constant, nullptr, access);
} else {
instr = New<HLoadNamedField>(cell_constant, nullptr, access, field_maps,
HType::HeapObject());
}
instr->ClearDependsOnFlag(kInobjectFields);
instr->SetDependsOnFlag(kGlobalVars);
return ast_context()->ReturnInstruction(instr, ast_id);
}
}
void HOptimizedGraphBuilder::VisitVariableProxy(VariableProxy* expr) {
DCHECK(!HasStackOverflow());
......@@ -5352,57 +5405,9 @@ void HOptimizedGraphBuilder::VisitVariableProxy(VariableProxy* expr) {
}
LookupIterator it(global, variable->name(), LookupIterator::OWN);
GlobalPropertyAccess type = LookupGlobalProperty(variable, &it, LOAD);
if (type == kUseCell) {
Handle<PropertyCell> cell = it.GetPropertyCell();
top_info()->dependencies()->AssumePropertyCell(cell);
auto cell_type = it.property_details().cell_type();
if (cell_type == PropertyCellType::kConstant ||
cell_type == PropertyCellType::kUndefined) {
Handle<Object> constant_object(cell->value(), isolate());
if (constant_object->IsConsString()) {
constant_object =
String::Flatten(Handle<String>::cast(constant_object));
}
HConstant* constant = New<HConstant>(constant_object);
return ast_context()->ReturnInstruction(constant, expr->id());
} else {
auto access = HObjectAccess::ForPropertyCellValue();
UniqueSet<Map>* field_maps = nullptr;
if (cell_type == PropertyCellType::kConstantType) {
switch (cell->GetConstantType()) {
case PropertyCellConstantType::kSmi:
access = access.WithRepresentation(Representation::Smi());
break;
case PropertyCellConstantType::kStableMap: {
// Check that the map really is stable. The heap object could
// have mutated without the cell updating state. In that case,
// make no promises about the loaded value except that it's a
// heap object.
access =
access.WithRepresentation(Representation::HeapObject());
Handle<Map> map(HeapObject::cast(cell->value())->map());
if (map->is_stable()) {
field_maps = new (zone())
UniqueSet<Map>(Unique<Map>::CreateImmovable(map), zone());
}
break;
}
}
}
HConstant* cell_constant = Add<HConstant>(cell);
HLoadNamedField* instr;
if (field_maps == nullptr) {
instr = New<HLoadNamedField>(cell_constant, nullptr, access);
} else {
instr = New<HLoadNamedField>(cell_constant, nullptr, access,
field_maps, HType::HeapObject());
}
instr->ClearDependsOnFlag(kInobjectFields);
instr->SetDependsOnFlag(kGlobalVars);
return ast_context()->ReturnInstruction(instr, expr->id());
}
if (CanInlineGlobalPropertyAccess(variable, &it, LOAD)) {
InlineGlobalPropertyLoad(&it, expr->id());
return;
} else {
Handle<TypeFeedbackVector> vector(current_feedback_vector(), isolate());
......@@ -6506,51 +6511,11 @@ void HOptimizedGraphBuilder::HandlePropertyAssignment(Assignment* expr) {
expr->AssignmentId(), expr->IsUninitialized());
}
// Because not every expression has a position and there is not common
// superclass of Assignment and CountOperation, we cannot just pass the
// owning expression instead of position and ast_id separately.
void HOptimizedGraphBuilder::HandleGlobalVariableAssignment(
Variable* var, HValue* value, FeedbackVectorSlot slot, BailoutId ast_id) {
Handle<JSGlobalObject> global(current_info()->global_object());
// Lookup in script contexts.
{
Handle<ScriptContextTable> script_contexts(
global->native_context()->script_context_table());
ScriptContextTable::LookupResult lookup;
if (ScriptContextTable::Lookup(script_contexts, var->name(), &lookup)) {
if (lookup.mode == CONST) {
return Bailout(kNonInitializerAssignmentToConst);
}
Handle<Context> script_context =
ScriptContextTable::GetContext(script_contexts, lookup.context_index);
Handle<Object> current_value =
FixedArray::get(*script_context, lookup.slot_index, isolate());
// If the values is not the hole, it will stay initialized,
// so no need to generate a check.
if (current_value->IsTheHole(isolate())) {
return Bailout(kReferenceToUninitializedVariable);
}
HStoreNamedField* instr = Add<HStoreNamedField>(
Add<HConstant>(script_context),
HObjectAccess::ForContextSlot(lookup.slot_index), value);
USE(instr);
DCHECK(instr->HasObservableSideEffects());
Add<HSimulate>(ast_id, REMOVABLE_SIMULATE);
return;
}
}
LookupIterator it(global, var->name(), LookupIterator::OWN);
GlobalPropertyAccess type = LookupGlobalProperty(var, &it, STORE);
if (type == kUseCell) {
Handle<PropertyCell> cell = it.GetPropertyCell();
HInstruction* HOptimizedGraphBuilder::InlineGlobalPropertyStore(
LookupIterator* it, HValue* value, BailoutId ast_id) {
Handle<PropertyCell> cell = it->GetPropertyCell();
top_info()->dependencies()->AssumePropertyCell(cell);
auto cell_type = it.property_details().cell_type();
auto cell_type = it->property_details().cell_type();
if (cell_type == PropertyCellType::kConstant ||
cell_type == PropertyCellType::kUndefined) {
Handle<Object> constant(cell->value(), isolate());
......@@ -6593,9 +6558,54 @@ void HOptimizedGraphBuilder::HandleGlobalVariableAssignment(
}
}
}
HInstruction* instr = Add<HStoreNamedField>(cell_constant, access, value);
HInstruction* instr = New<HStoreNamedField>(cell_constant, access, value);
instr->ClearChangesFlag(kInobjectFields);
instr->SetChangesFlag(kGlobalVars);
return instr;
}
// Because not every expression has a position and there is not common
// superclass of Assignment and CountOperation, we cannot just pass the
// owning expression instead of position and ast_id separately.
void HOptimizedGraphBuilder::HandleGlobalVariableAssignment(
Variable* var, HValue* value, FeedbackVectorSlot slot, BailoutId ast_id) {
Handle<JSGlobalObject> global(current_info()->global_object());
// Lookup in script contexts.
{
Handle<ScriptContextTable> script_contexts(
global->native_context()->script_context_table());
ScriptContextTable::LookupResult lookup;
if (ScriptContextTable::Lookup(script_contexts, var->name(), &lookup)) {
if (lookup.mode == CONST) {
return Bailout(kNonInitializerAssignmentToConst);
}
Handle<Context> script_context =
ScriptContextTable::GetContext(script_contexts, lookup.context_index);
Handle<Object> current_value =
FixedArray::get(*script_context, lookup.slot_index, isolate());
// If the values is not the hole, it will stay initialized,
// so no need to generate a check.
if (current_value->IsTheHole(isolate())) {
return Bailout(kReferenceToUninitializedVariable);
}
HStoreNamedField* instr = Add<HStoreNamedField>(
Add<HConstant>(script_context),
HObjectAccess::ForContextSlot(lookup.slot_index), value);
USE(instr);
DCHECK(instr->HasObservableSideEffects());
Add<HSimulate>(ast_id, REMOVABLE_SIMULATE);
return;
}
}
LookupIterator it(global, var->name(), LookupIterator::OWN);
if (CanInlineGlobalPropertyAccess(var, &it, STORE)) {
HInstruction* instr = InlineGlobalPropertyStore(&it, value, ast_id);
AddInstruction(instr);
if (instr->HasObservableSideEffects()) {
Add<HSimulate>(ast_id, REMOVABLE_SIMULATE);
}
......@@ -7518,6 +7528,37 @@ HValue* HOptimizedGraphBuilder::BuildNamedAccess(
DCHECK(maps != NULL);
if (maps->length() > 0) {
Handle<JSGlobalObject> global_object(current_info()->global_object());
Handle<Context> current_context(current_info()->context());
Handle<JSObject> global_proxy(current_context->global_proxy());
// Check for special case: Access via a single map to the global proxy
// can also be handled monomorphically.
Handle<Object> map_constructor =
handle(maps->first()->GetConstructor(), isolate());
if (map_constructor->IsJSFunction()) {
Handle<Context> map_context =
handle(Handle<JSFunction>::cast(map_constructor)->context());
bool is_global_proxy_access =
maps->length() == 1 && // More than one map, fallback to polymorphic?
maps->first()->IsJSGlobalProxyMap() &&
isolate()->MayAccess(map_context, global_proxy);
if (is_global_proxy_access) {
LookupIterator it(global_object, name, LookupIterator::OWN);
if (CanInlineGlobalPropertyAccess(&it, access)) {
BuildCheckHeapObject(object);
Add<HCheckMaps>(object, maps);
if (access == LOAD) {
InlineGlobalPropertyLoad(&it, expr->id());
return nullptr;
} else {
return InlineGlobalPropertyStore(&it, value, expr->id());
}
}
}
}
PropertyAccessInfo info(this, access, maps->first(), name);
if (!info.CanAccessAsMonomorphic(maps)) {
HandlePolymorphicNamedFieldAccess(access, expr, slot, ast_id, return_id,
......
......@@ -2342,14 +2342,16 @@ class HOptimizedGraphBuilder : public HGraphBuilder,
#undef DECLARE_VISIT
private:
// Helpers for flow graph construction.
enum GlobalPropertyAccess {
kUseCell,
kUseGeneric
};
GlobalPropertyAccess LookupGlobalProperty(Variable* var, LookupIterator* it,
bool CanInlineGlobalPropertyAccess(Variable* var, LookupIterator* it,
PropertyAccessType access_type);
bool CanInlineGlobalPropertyAccess(LookupIterator* it,
PropertyAccessType access_type);
void InlineGlobalPropertyLoad(LookupIterator* it, BailoutId ast_id);
HInstruction* InlineGlobalPropertyStore(LookupIterator* it, HValue* value,
BailoutId ast_id);
void EnsureArgumentsArePushedForAccess();
bool TryArgumentsAccess(Property* expr);
......
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