[turbofan] Properly type field access to stable heap object maps.

Introduce new typing rules for LoadField[Map], which try to take into
account stable map information if the object either has type Constant or
type Class. If the map of the object is stable but can transition we
have to introduce a code dependency in the Typer to make sure that the
information (the Constant type we infer for LoadField[Map]) is valid
(and stays valid).

This also settles the policy for depending on map stability: The
definition can introduce any number of maps, without having to pay
attention to stability (i.e. you can always use Type::Class to introduce
a map that is propagated along the value edges), and the use site is
responsible for checking that the type information is valid before using
it. I.e. if you use stable map information, you'll have to add a
stability dependency (or make sure the map cannot transition).

Drive-by-improvement: Add ReferenceEqualTyper which takes input types
into account for improved constant folding.

Drive-by-fix: Apply policy mentioned above to JSNativeContextSpecialization.

R=jarin@chromium.org, rossberg@chromium.org
BUG=v8:4470
LOG=n

Review URL: https://codereview.chromium.org/1410953006

Cr-Commit-Position: refs/heads/master@{#31567}
This commit is contained in:
bmeurer 2015-10-26 07:04:00 -07:00 committed by Commit bot
parent 15f36b2b1e
commit 44b9122d9f
5 changed files with 98 additions and 16 deletions

View File

@ -109,7 +109,10 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadGlobal(Node* node) {
property_cell_value_type = Type::Intersect(
Type::Number(), Type::TaggedPointer(), graph()->zone());
} else {
property_cell_value_type = Type::Of(property_cell_value, graph()->zone());
Handle<Map> property_cell_value_map(
Handle<HeapObject>::cast(property_cell_value)->map(), isolate());
property_cell_value_type =
Type::Class(property_cell_value_map, graph()->zone());
}
Node* value = effect = graph()->NewNode(
simplified()->LoadField(
@ -407,17 +410,10 @@ bool JSNativeContextSpecialization::ComputePropertyAccessInfo(
// TODO(bmeurer): It would be awesome to make this saner in the
// runtime/GC interaction.
field_type = Type::TaggedPointer();
} else {
} else if (!Type::Any()->Is(field_type)) {
// Add proper code dependencies in case of stable field map(s).
if (field_type->NumClasses() > 0 && field_type->NowStable()) {
dependencies()->AssumeFieldType(
handle(map->FindFieldOwner(number), isolate()));
for (auto i = field_type->Classes(); !i.Done(); i.Advance()) {
dependencies()->AssumeMapStable(i.Current());
}
} else {
field_type = Type::TaggedPointer();
}
Handle<Map> field_owner_map(map->FindFieldOwner(number), isolate());
dependencies()->AssumeFieldType(field_owner_map);
}
DCHECK(field_type->Is(Type::TaggedPointer()));
}

View File

@ -1097,7 +1097,11 @@ Handle<Code> Pipeline::GenerateCode() {
base::SmartPointer<Typer> typer;
if (info()->is_typing_enabled()) {
// Type the graph.
typer.Reset(new Typer(isolate(), data.graph(), info()->function_type()));
typer.Reset(new Typer(isolate(), data.graph(),
info()->is_deoptimization_enabled()
? Typer::kDeoptimizationEnabled
: Typer::kNoFlags,
info()->dependencies(), info()->function_type()));
Run<TyperPhase>(typer.get());
RunPrintAndVerify("Typed");
}

View File

@ -7,6 +7,7 @@
#include "src/base/flags.h"
#include "src/base/lazy-instance.h"
#include "src/bootstrapper.h"
#include "src/compilation-dependencies.h"
#include "src/compiler/common-operator.h"
#include "src/compiler/graph-reducer.h"
#include "src/compiler/js-operator.h"
@ -37,9 +38,13 @@ class Typer::Decorator final : public GraphDecorator {
};
Typer::Typer(Isolate* isolate, Graph* graph, Type::FunctionType* function_type)
Typer::Typer(Isolate* isolate, Graph* graph, Flags flags,
CompilationDependencies* dependencies,
Type::FunctionType* function_type)
: isolate_(isolate),
graph_(graph),
flags_(flags),
dependencies_(dependencies),
function_type_(function_type),
decorator_(nullptr),
cache_(kCache.Get()) {
@ -204,6 +209,10 @@ class Typer::Visitor : public Reducer {
Zone* zone() { return typer_->zone(); }
Isolate* isolate() { return typer_->isolate(); }
Graph* graph() { return typer_->graph(); }
Typer::Flags flags() const { return typer_->flags(); }
CompilationDependencies* dependencies() const {
return typer_->dependencies();
}
void SetWeakened(NodeId node_id) { weakened_nodes_.insert(node_id); }
bool IsWeakened(NodeId node_id) {
@ -252,6 +261,8 @@ class Typer::Visitor : public Reducer {
static Type* JSLoadPropertyTyper(Type*, Type*, Typer*);
static Type* JSCallFunctionTyper(Type*, Typer*);
static Type* ReferenceEqualTyper(Type*, Type*, Typer*);
Reduction UpdateType(Node* node, Type* current) {
if (NodeProperties::IsTyped(node)) {
// Widen the type of a previously typed node.
@ -1575,8 +1586,17 @@ Type* Typer::Visitor::TypePlainPrimitiveToNumber(Node* node) {
}
// static
Type* Typer::Visitor::ReferenceEqualTyper(Type* lhs, Type* rhs, Typer* t) {
if (lhs->IsConstant() && rhs->Is(lhs)) {
return t->singleton_true_;
}
return Type::Boolean();
}
Type* Typer::Visitor::TypeReferenceEqual(Node* node) {
return Type::Boolean(zone());
return TypeBinaryOp(node, ReferenceEqualTyper);
}
@ -1666,8 +1686,53 @@ Type* Typer::Visitor::TypeChangeBitToBool(Node* node) {
Type* Typer::Visitor::TypeAllocate(Node* node) { return Type::TaggedPointer(); }
namespace {
MaybeHandle<Map> GetStableMapFromObjectType(Type* object_type) {
if (object_type->IsConstant() &&
object_type->AsConstant()->Value()->IsHeapObject()) {
Handle<Map> object_map(
Handle<HeapObject>::cast(object_type->AsConstant()->Value())->map());
if (object_map->is_stable()) return object_map;
} else if (object_type->IsClass()) {
Handle<Map> object_map = object_type->AsClass()->Map();
if (object_map->is_stable()) return object_map;
}
return MaybeHandle<Map>();
}
} // namespace
Type* Typer::Visitor::TypeLoadField(Node* node) {
return FieldAccessOf(node->op()).type;
FieldAccess const& access = FieldAccessOf(node->op());
if (access.base_is_tagged == kTaggedBase &&
access.offset == HeapObject::kMapOffset) {
// The type of LoadField[Map](o) is Constant(map) if map is stable and
// either
// (a) o has type Constant(object) and map == object->map, or
// (b) o has type Class(map),
// and either
// (1) map cannot transition further, or
// (2) deoptimization is enabled and we can add a code dependency on the
// stability of map (to guard the Constant type information).
Type* const object = Operand(node, 0);
if (object->Is(Type::None())) return Type::None();
Handle<Map> object_map;
if (GetStableMapFromObjectType(object).ToHandle(&object_map)) {
if (object_map->CanTransition()) {
if (flags() & kDeoptimizationEnabled) {
dependencies()->AssumeMapStable(object_map);
} else {
return access.type;
}
}
Type* object_map_type = Type::Constant(object_map, zone());
DCHECK(object_map_type->Is(access.type));
return object_map_type;
}
}
return access.type;
}

View File

@ -5,6 +5,7 @@
#ifndef V8_COMPILER_TYPER_H_
#define V8_COMPILER_TYPER_H_
#include "src/base/flags.h"
#include "src/compiler/graph.h"
#include "src/types.h"
@ -12,6 +13,7 @@ namespace v8 {
namespace internal {
// Forward declarations.
class CompilationDependencies;
class ZoneTypeCache;
namespace compiler {
@ -19,7 +21,15 @@ namespace compiler {
class Typer {
public:
Typer(Isolate* isolate, Graph* graph,
// Flags that control the mode of operation.
enum Flag {
kNoFlags = 0u,
kDeoptimizationEnabled = 1u << 0,
};
typedef base::Flags<Flag> Flags;
Typer(Isolate* isolate, Graph* graph, Flags flags = kNoFlags,
CompilationDependencies* dependencies = nullptr,
Type::FunctionType* function_type = nullptr);
~Typer();
@ -34,10 +44,14 @@ class Typer {
Graph* graph() const { return graph_; }
Zone* zone() const { return graph()->zone(); }
Isolate* isolate() const { return isolate_; }
Flags flags() const { return flags_; }
CompilationDependencies* dependencies() const { return dependencies_; }
Type::FunctionType* function_type() const { return function_type_; }
Isolate* const isolate_;
Graph* const graph_;
Flags const flags_;
CompilationDependencies* const dependencies_;
Type::FunctionType* function_type_;
Decorator* decorator_;
ZoneTypeCache const& cache_;
@ -52,6 +66,8 @@ class Typer {
DISALLOW_COPY_AND_ASSIGN(Typer);
};
DEFINE_OPERATORS_FOR_FLAGS(Typer::Flags)
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -176,6 +176,7 @@ class FunctionTester : public InitializedHandleScope {
Pipeline pipeline(&info);
Handle<Code> code = pipeline.GenerateCode();
CHECK(!code.is_null());
info.dependencies()->Commit(code);
info.context()->native_context()->AddOptimizedCode(*code);
function->ReplaceCode(*code);
return function;