Commit c4e19c7d authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Lower JSCreate to an inline allocation in JSTypedLowering.

This adds initial support for fast inline allocations of JSObject
instances. It currently has exactly the same limitations as Crankshaft.

R=mstarzinger@chromium.org
BUG=v8:4544
LOG=n

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

Cr-Commit-Position: refs/heads/master@{#31957}
parent ee9020dd
......@@ -47,6 +47,16 @@ FieldAccess AccessBuilder::ForJSObjectElements() {
}
// static
FieldAccess AccessBuilder::ForJSObjectInObjectProperty(Handle<Map> map,
int index) {
int const offset = map->GetInObjectPropertyOffset(index);
FieldAccess access = {kTaggedBase, offset, MaybeHandle<Name>(),
Type::Tagged(), kMachAnyTagged};
return access;
}
// static
FieldAccess AccessBuilder::ForJSFunctionContext() {
FieldAccess access = {kTaggedBase, JSFunction::kContextOffset,
......
......@@ -31,6 +31,9 @@ class AccessBuilder final : public AllStatic {
// Provides access to JSObject::elements() field.
static FieldAccess ForJSObjectElements();
// Provides access to JSObject inobject property fields.
static FieldAccess ForJSObjectInObjectProperty(Handle<Map> map, int index);
// Provides access to JSFunction::context() field.
static FieldAccess ForJSFunctionContext();
......
......@@ -1314,6 +1314,75 @@ Reduction JSTypedLowering::ReduceJSConvertReceiver(Node* node) {
}
namespace {
// Maximum instance size for which allocations will be inlined.
const int kMaxInlineInstanceSize = 64 * kPointerSize;
// Checks whether allocation using the given constructor can be inlined.
bool IsAllocationInlineable(Handle<JSFunction> constructor) {
// TODO(bmeurer): Support inlining of class constructors.
if (IsClassConstructor(constructor->shared()->kind())) return false;
return constructor->has_initial_map() &&
constructor->initial_map()->instance_type() == JS_OBJECT_TYPE &&
constructor->initial_map()->instance_size() < kMaxInlineInstanceSize;
}
} // namespace
Reduction JSTypedLowering::ReduceJSCreate(Node* node) {
DCHECK_EQ(IrOpcode::kJSCreate, node->opcode());
Node* const target = NodeProperties::GetValueInput(node, 0);
Type* const target_type = NodeProperties::GetType(target);
Node* const new_target = NodeProperties::GetValueInput(node, 1);
Node* const effect = NodeProperties::GetEffectInput(node);
// TODO(turbofan): Add support for NewTarget passed to JSCreate.
if (target != new_target) return NoChange();
// Extract constructor function.
if (target_type->IsConstant() &&
target_type->AsConstant()->Value()->IsJSFunction()) {
Handle<JSFunction> constructor =
Handle<JSFunction>::cast(target_type->AsConstant()->Value());
// Force completion of inobject slack tracking before
// generating code to finalize the instance size.
if (constructor->IsInobjectSlackTrackingInProgress()) {
constructor->CompleteInobjectSlackTracking();
}
// TODO(bmeurer): We fall back to the runtime in case we cannot inline
// the allocation here, which is sort of expensive. We should think about
// a soft fallback to some NewObjectCodeStub.
if (IsAllocationInlineable(constructor)) {
// Compute instance size from initial map of {constructor}.
Handle<Map> initial_map(constructor->initial_map(), isolate());
int const instance_size = initial_map->instance_size();
// Add a dependency on the {initial_map} to make sure that this code is
// deoptimized whenever the {initial_map} of the {constructor} changes.
dependencies()->AssumeInitialMapCantChange(initial_map);
// Emit code to allocate the JSObject instance for the {constructor}.
AllocationBuilder a(jsgraph(), effect, graph()->start());
a.Allocate(instance_size);
a.Store(AccessBuilder::ForMap(), initial_map);
a.Store(AccessBuilder::ForJSObjectProperties(),
jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSObjectElements(),
jsgraph()->EmptyFixedArrayConstant());
for (int i = 0; i < initial_map->GetInObjectProperties(); ++i) {
a.Store(AccessBuilder::ForJSObjectInObjectProperty(initial_map, i),
jsgraph()->UndefinedConstant());
}
a.FinishAndChange(node);
return Changed(node);
}
}
return NoChange();
}
namespace {
// Retrieves the frame state holding actual argument values.
......@@ -2153,6 +2222,8 @@ Reduction JSTypedLowering::Reduce(Node* node) {
return ReduceJSStoreContext(node);
case IrOpcode::kJSConvertReceiver:
return ReduceJSConvertReceiver(node);
case IrOpcode::kJSCreate:
return ReduceJSCreate(node);
case IrOpcode::kJSCreateArguments:
return ReduceJSCreateArguments(node);
case IrOpcode::kJSCreateClosure:
......
......@@ -67,6 +67,7 @@ class JSTypedLowering final : public AdvancedReducer {
Reduction ReduceJSToString(Node* node);
Reduction ReduceJSToObject(Node* node);
Reduction ReduceJSConvertReceiver(Node* node);
Reduction ReduceJSCreate(Node* node);
Reduction ReduceJSCreateArguments(Node* node);
Reduction ReduceJSCreateClosure(Node* node);
Reduction ReduceJSCreateLiteralArray(Node* node);
......
......@@ -978,6 +978,27 @@ TEST_F(JSTypedLoweringTest, JSAddWithString) {
}
// -----------------------------------------------------------------------------
// JSCreate
TEST_F(JSTypedLoweringTest, JSCreate) {
Handle<JSFunction> function = isolate()->object_function();
Node* const target = Parameter(Type::Constant(function, graph()->zone()));
Node* const context = Parameter(Type::Any());
Node* const effect = graph()->start();
Reduction r = Reduce(graph()->NewNode(javascript()->Create(), target, target,
context, effect));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(
r.replacement(),
IsFinishRegion(
IsAllocate(IsNumberConstant(function->initial_map()->instance_size()),
IsBeginRegion(effect), _),
_));
}
// -----------------------------------------------------------------------------
// JSCreateArguments
......
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