| Index: src/crankshaft/hydrogen.cc
|
| diff --git a/src/crankshaft/hydrogen.cc b/src/crankshaft/hydrogen.cc
|
| index da0ea4c318da50cde89c07f7e86b34a4b7ccf9f8..5d4e0d35064ce66b60b52c71b593486a95225f53 100644
|
| --- a/src/crankshaft/hydrogen.cc
|
| +++ b/src/crankshaft/hydrogen.cc
|
| @@ -5448,12 +5448,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()) {
|
| @@ -5462,17 +5466,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;
|
| }
|
|
|
|
|
| @@ -5488,6 +5492,74 @@ HValue* HOptimizedGraphBuilder::BuildContextChainWalk(Variable* var) {
|
| return context;
|
| }
|
|
|
| +LookupIterator TryReadingFromCache(Isolate* isolate, LookupIterator* it) {
|
| + if (it->state() != LookupIterator::ACCESSOR) return *it;
|
| +
|
| + Handle<Object> accessors = it->GetAccessors();
|
| + if (!accessors->IsAccessorPair()) return *it;
|
| +
|
| + Handle<Object> getter(Handle<AccessorPair>::cast(accessors)->getter(),
|
| + isolate);
|
| +
|
| + MaybeHandle<Name> maybe_name =
|
| + FunctionTemplateInfo::TryGetCachePropertyName(isolate, getter);
|
| + if (maybe_name.is_null()) return *it;
|
| +
|
| + Handle<Name> name = maybe_name.ToHandleChecked();
|
| + LookupIterator cache_it(it->GetReceiver(), name, it->GetHolder<JSObject>());
|
| + CHECK_EQ(cache_it.state(), LookupIterator::DATA);
|
| + return cache_it;
|
| +}
|
| +
|
| +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());
|
| @@ -5535,58 +5607,11 @@ 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());
|
| - }
|
| + LookupIterator default_it(global, variable->name(), LookupIterator::OWN);
|
| + LookupIterator it = TryReadingFromCache(isolate(), &default_it);
|
| + if (CanInlineGlobalPropertyAccess(variable, &it, LOAD)) {
|
| + InlineGlobalPropertyLoad(&it, expr->id());
|
| + return;
|
| } else {
|
| Handle<TypeFeedbackVector> vector(current_feedback_vector(), isolate());
|
| HLoadGlobalGeneric* instr = New<HLoadGlobalGeneric>(
|
| @@ -6387,6 +6412,17 @@ HValue* HOptimizedGraphBuilder::BuildMonomorphicAccess(
|
| }
|
|
|
| if (info->IsAccessorConstant()) {
|
| + MaybeHandle<Name> maybe_name =
|
| + FunctionTemplateInfo::TryGetCachePropertyName(isolate(),
|
| + info->accessor());
|
| + if (!maybe_name.is_null()) {
|
| + Handle<Name> name = maybe_name.ToHandleChecked();
|
| + PropertyAccessInfo cache_info(this, LOAD, info->map(), name);
|
| + // Load new target.
|
| + if (cache_info.CanAccessMonomorphic())
|
| + return BuildLoadNamedField(&cache_info, checked_object);
|
| + }
|
| +
|
| Push(checked_object);
|
| int argument_count = 1;
|
| if (!info->IsLoad()) {
|
| @@ -6682,6 +6718,61 @@ void HOptimizedGraphBuilder::HandlePropertyAssignment(Assignment* expr) {
|
| expr->AssignmentId(), expr->IsUninitialized());
|
| }
|
|
|
| +void 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();
|
| + if (cell_type == PropertyCellType::kConstant ||
|
| + cell_type == PropertyCellType::kUndefined) {
|
| + Handle<Object> constant(cell->value(), isolate());
|
| + if (value->IsConstant()) {
|
| + HConstant* c_value = HConstant::cast(value);
|
| + if (!constant.is_identical_to(c_value->handle(isolate()))) {
|
| + Add<HDeoptimize>(DeoptimizeReason::kConstantGlobalVariableAssignment,
|
| + Deoptimizer::EAGER);
|
| + }
|
| + } else {
|
| + HValue* c_constant = Add<HConstant>(constant);
|
| + IfBuilder builder(this);
|
| + if (constant->IsNumber()) {
|
| + builder.If<HCompareNumericAndBranch>(value, c_constant, Token::EQ);
|
| + } else {
|
| + builder.If<HCompareObjectEqAndBranch>(value, c_constant);
|
| + }
|
| + builder.Then();
|
| + builder.Else();
|
| + Add<HDeoptimize>(DeoptimizeReason::kConstantGlobalVariableAssignment,
|
| + Deoptimizer::EAGER);
|
| + builder.End();
|
| + }
|
| + }
|
| + HConstant* cell_constant = Add<HConstant>(cell);
|
| + auto access = HObjectAccess::ForPropertyCellValue();
|
| + if (cell_type == PropertyCellType::kConstantType) {
|
| + switch (cell->GetConstantType()) {
|
| + case PropertyCellConstantType::kSmi:
|
| + access = access.WithRepresentation(Representation::Smi());
|
| + break;
|
| + case PropertyCellConstantType::kStableMap: {
|
| + // The map may no longer be stable, deopt if it's ever different from
|
| + // what is currently there, which will allow for restablization.
|
| + Handle<Map> map(HeapObject::cast(cell->value())->map());
|
| + Add<HCheckHeapObject>(value);
|
| + value = Add<HCheckMaps>(value, map);
|
| + access = access.WithRepresentation(Representation::HeapObject());
|
| + break;
|
| + }
|
| + }
|
| + }
|
| + HInstruction* instr = Add<HStoreNamedField>(cell_constant, access, value);
|
| + instr->ClearChangesFlag(kInobjectFields);
|
| + instr->SetChangesFlag(kGlobalVars);
|
| + if (instr->HasObservableSideEffects()) {
|
| + Add<HSimulate>(ast_id, REMOVABLE_SIMULATE);
|
| + }
|
| +}
|
|
|
| // Because not every expression has a position and there is not common
|
| // superclass of Assignment and CountOperation, we cannot just pass the
|
| @@ -6722,59 +6813,8 @@ void HOptimizedGraphBuilder::HandleGlobalVariableAssignment(
|
| }
|
|
|
| LookupIterator it(global, var->name(), LookupIterator::OWN);
|
| - GlobalPropertyAccess type = LookupGlobalProperty(var, &it, STORE);
|
| - 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(cell->value(), isolate());
|
| - if (value->IsConstant()) {
|
| - HConstant* c_value = HConstant::cast(value);
|
| - if (!constant.is_identical_to(c_value->handle(isolate()))) {
|
| - Add<HDeoptimize>(DeoptimizeReason::kConstantGlobalVariableAssignment,
|
| - Deoptimizer::EAGER);
|
| - }
|
| - } else {
|
| - HValue* c_constant = Add<HConstant>(constant);
|
| - IfBuilder builder(this);
|
| - if (constant->IsNumber()) {
|
| - builder.If<HCompareNumericAndBranch>(value, c_constant, Token::EQ);
|
| - } else {
|
| - builder.If<HCompareObjectEqAndBranch>(value, c_constant);
|
| - }
|
| - builder.Then();
|
| - builder.Else();
|
| - Add<HDeoptimize>(DeoptimizeReason::kConstantGlobalVariableAssignment,
|
| - Deoptimizer::EAGER);
|
| - builder.End();
|
| - }
|
| - }
|
| - HConstant* cell_constant = Add<HConstant>(cell);
|
| - auto access = HObjectAccess::ForPropertyCellValue();
|
| - if (cell_type == PropertyCellType::kConstantType) {
|
| - switch (cell->GetConstantType()) {
|
| - case PropertyCellConstantType::kSmi:
|
| - access = access.WithRepresentation(Representation::Smi());
|
| - break;
|
| - case PropertyCellConstantType::kStableMap: {
|
| - // The map may no longer be stable, deopt if it's ever different from
|
| - // what is currently there, which will allow for restablization.
|
| - Handle<Map> map(HeapObject::cast(cell->value())->map());
|
| - Add<HCheckHeapObject>(value);
|
| - value = Add<HCheckMaps>(value, map);
|
| - access = access.WithRepresentation(Representation::HeapObject());
|
| - break;
|
| - }
|
| - }
|
| - }
|
| - HInstruction* instr = Add<HStoreNamedField>(cell_constant, access, value);
|
| - instr->ClearChangesFlag(kInobjectFields);
|
| - instr->SetChangesFlag(kGlobalVars);
|
| - if (instr->HasObservableSideEffects()) {
|
| - Add<HSimulate>(ast_id, REMOVABLE_SIMULATE);
|
| - }
|
| + if (CanInlineGlobalPropertyAccess(var, &it, STORE)) {
|
| + InlineGlobalPropertyStore(&it, value, ast_id);
|
| } else {
|
| HValue* global_object = Add<HLoadNamedField>(
|
| BuildGetNativeContext(), nullptr,
|
| @@ -7690,6 +7730,42 @@ 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 default_it(global_object, name, LookupIterator::OWN);
|
| + // TODO(peterssen): Inline LOADS only?.
|
| + LookupIterator it = (access == LOAD)
|
| + ? TryReadingFromCache(isolate(), &default_it)
|
| + : default_it;
|
| +
|
| + if (CanInlineGlobalPropertyAccess(&it, access)) {
|
| + BuildCheckHeapObject(object);
|
| + Add<HCheckMaps>(object, maps);
|
| + if (access == LOAD) {
|
| + InlineGlobalPropertyLoad(&it, expr->id());
|
| + } else {
|
| + InlineGlobalPropertyStore(&it, value, expr->id());
|
| + }
|
| + return nullptr;
|
| + }
|
| + }
|
| + }
|
| +
|
| PropertyAccessInfo info(this, access, maps->first(), name);
|
| if (!info.CanAccessAsMonomorphic(maps)) {
|
| HandlePolymorphicNamedFieldAccess(access, expr, slot, ast_id, return_id,
|
|
|