Commit 11d5d09c authored by mstarzinger's avatar mstarzinger Committed by Commit bot

[turbofan] Initial support for constructor call inlining.

This implements a first version of support for constructor call inlining
in the inlining machinery. For now we can only inline calls where the
actual constructor and the original constructor coincide (i.e. no super
constructor calls). Note that the target of a super constructor call is
loaded with a runtime call, so there is no way for it to be constant
promoted at the moment.

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

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

Cr-Commit-Position: refs/heads/master@{#31954}
parent dd0ba4d1
......@@ -3215,7 +3215,8 @@ Node* AstGraphBuilder::BuildNewTargetVariable(Variable* new_target_var) {
const Operator* op =
javascript()->CallRuntime(Runtime::kGetOriginalConstructor, 0);
Node* object = NewNode(op);
PrepareFrameState(object, BailoutId::None());
// TODO(4544): Bailout id only needed for JavaScriptFrame::Summarize.
PrepareFrameState(object, BailoutId::FunctionContext());
// Assign the object to the {new.target} variable. This should never lazy
// deopt, so it is fine to send invalid bailout id.
......
......@@ -551,6 +551,11 @@ void CodeGenerator::BuildTranslationForFrameStateDescriptor(
shared_info_id,
static_cast<unsigned int>(descriptor->parameters_count()));
break;
case FrameStateType::kConstructStub:
translation->BeginConstructStubFrame(
shared_info_id,
static_cast<unsigned int>(descriptor->parameters_count()));
break;
}
for (size_t i = 0; i < descriptor->GetSize(state_combine); i++) {
......
......@@ -54,6 +54,9 @@ std::ostream& operator<<(std::ostream& os, FrameStateType type) {
case FrameStateType::kArgumentsAdaptor:
os << "ARGUMENTS_ADAPTOR";
break;
case FrameStateType::kConstructStub:
os << "CONSTRUCT_STUB";
break;
}
return os;
}
......
......@@ -77,7 +77,8 @@ class OutputFrameStateCombine {
// The type of stack frame that a FrameState node represents.
enum class FrameStateType {
kJavaScriptFunction, // Represents an unoptimized JavaScriptFrame.
kArgumentsAdaptor // Represents an ArgumentsAdaptorFrame.
kArgumentsAdaptor, // Represents an ArgumentsAdaptorFrame.
kConstructStub // Represents a ConstructStubFrame.
};
......
......@@ -456,7 +456,12 @@ void JSGenericLowering::LowerJSLoadDynamic(Node* node) {
}
void JSGenericLowering::LowerJSCreate(Node* node) { UNIMPLEMENTED(); }
void JSGenericLowering::LowerJSCreate(Node* node) {
// TODO(4544): The duplication of the constructor function is only valid if
// actual constructor and original constructor coincide. Fix this!
node->InsertInput(zone(), 1, node->InputAt(0)); // Duplicate constructor.
ReplaceWithRuntimeCall(node, Runtime::kNewObject);
}
void JSGenericLowering::LowerJSCreateArguments(Node* node) {
......
......@@ -13,7 +13,7 @@ namespace internal {
namespace compiler {
Reduction JSInliningHeuristic::Reduce(Node* node) {
if (node->opcode() != IrOpcode::kJSCallFunction) return NoChange();
if (!IrOpcode::IsInlineeOpcode(node->opcode())) return NoChange();
// Check if we already saw that {node} before, and if so, just skip it.
if (seen_.find(node->id()) != seen_.end()) return NoChange();
......@@ -26,7 +26,7 @@ Reduction JSInliningHeuristic::Reduce(Node* node) {
// Functions marked with %SetForceInlineFlag are immediately inlined.
if (function->shared()->force_inline()) {
return inliner_.ReduceJSCallFunction(node, function);
return inliner_.ReduceJSCall(node, function);
}
// Handling of special inlining modes right away:
......@@ -36,7 +36,7 @@ Reduction JSInliningHeuristic::Reduce(Node* node) {
case kRestrictedInlining:
return NoChange();
case kStressInlining:
return inliner_.ReduceJSCallFunction(node, function);
return inliner_.ReduceJSCall(node, function);
case kGeneralInlining:
break;
}
......@@ -67,18 +67,21 @@ Reduction JSInliningHeuristic::Reduce(Node* node) {
// Stop inlinining once the maximum allowed level is reached.
int level = 0;
for (Node* frame_state = NodeProperties::GetFrameStateInput(node, 1);
for (Node* frame_state = NodeProperties::GetFrameStateInput(node, 0);
frame_state->opcode() == IrOpcode::kFrameState;
frame_state = NodeProperties::GetFrameStateInput(frame_state, 0)) {
if (++level > FLAG_max_inlining_levels) return NoChange();
}
// Gather feedback on how often this call site has been hit before.
CallFunctionParameters p = CallFunctionParametersOf(node->op());
int calls = -1; // Same default as CallICNexus::ExtractCallCount.
if (p.feedback().IsValid()) {
CallICNexus nexus(p.feedback().vector(), p.feedback().slot());
calls = nexus.ExtractCallCount();
// TODO(turbofan): We also want call counts for constructor calls.
if (node->opcode() == IrOpcode::kJSCallFunction) {
CallFunctionParameters p = CallFunctionParametersOf(node->op());
if (p.feedback().IsValid()) {
CallICNexus nexus(p.feedback().vector(), p.feedback().slot());
calls = nexus.ExtractCallCount();
}
}
// ---------------------------------------------------------------------------
......@@ -99,7 +102,7 @@ void JSInliningHeuristic::Finalize() {
if (cumulative_count_ > FLAG_max_inlined_nodes_cumulative) break;
auto i = candidates_.begin();
Candidate const& candidate = *i;
inliner_.ReduceJSCallFunction(candidate.node, candidate.function);
inliner_.ReduceJSCall(candidate.node, candidate.function);
cumulative_count_ += candidate.function->shared()->ast_node_count();
candidates_.erase(i);
}
......
This diff is collapsed.
......@@ -16,9 +16,6 @@ class CompilationInfo;
namespace compiler {
// Forward declarations.
class JSCallFunctionAccessor;
// The JSInliner provides the core graph inlining machinery. Note that this
// class only deals with the mechanics of how to inline one graph into another,
// heuristics that decide what and how much to inline are beyond its scope.
......@@ -36,15 +33,17 @@ class JSInliner final : public AdvancedReducer {
// Can be used by inlining heuristics or by testing code directly, without
// using the above generic reducer interface of the inlining machinery.
Reduction ReduceJSCallFunction(Node* node, Handle<JSFunction> function);
Reduction ReduceJSCall(Node* node, Handle<JSFunction> function);
private:
Zone* local_zone_;
CompilationInfo* info_;
JSGraph* jsgraph_;
Node* CreateArgumentsAdaptorFrameState(
JSCallFunctionAccessor* call, Handle<SharedFunctionInfo> shared_info);
Node* CreateArtificialFrameState(Node* node, Node* outer_frame_state,
int parameter_count,
FrameStateType frame_state_type,
Handle<SharedFunctionInfo> shared);
Reduction InlineCall(Node* call, Node* context, Node* frame_state,
Node* start, Node* end);
......
......@@ -363,7 +363,7 @@ const CreateClosureParameters& CreateClosureParametersOf(const Operator* op) {
V(ToName, Operator::kNoProperties, 1, 1) \
V(ToObject, Operator::kNoProperties, 1, 1) \
V(Yield, Operator::kNoProperties, 1, 1) \
V(Create, Operator::kEliminatable, 0, 1) \
V(Create, Operator::kEliminatable, 1, 1) \
V(HasProperty, Operator::kNoProperties, 2, 1) \
V(TypeOf, Operator::kEliminatable, 1, 1) \
V(InstanceOf, Operator::kNoProperties, 2, 1) \
......
......@@ -230,6 +230,7 @@ int Linkage::FrameStateInputCount(Runtime::FunctionId function) {
case Runtime::kNewClosure:
case Runtime::kNewClosure_Tenured:
case Runtime::kNewFunctionContext:
case Runtime::kNewObject:
case Runtime::kPushBlockContext:
case Runtime::kPushCatchContext:
case Runtime::kReThrow:
......@@ -244,6 +245,7 @@ int Linkage::FrameStateInputCount(Runtime::FunctionId function) {
case Runtime::kInlineDefaultConstructorCallSuper:
case Runtime::kInlineGetCallerJSFunction:
case Runtime::kInlineGetPrototype:
case Runtime::kInlineIsConstructCall:
case Runtime::kInlineRegExpExec:
case Runtime::kInlineSubString:
case Runtime::kInlineToInteger:
......
......@@ -373,6 +373,11 @@ class IrOpcode {
return kIfTrue <= value && value <= kIfDefault;
}
// Returns true if opcode can be inlined.
static bool IsInlineeOpcode(Value value) {
return value == kJSCallConstruct || value == kJSCallFunction;
}
// Returns true if opcode for comparison operator.
static bool IsComparisonOpcode(Value value) {
return (kJSEqual <= value && value <= kJSGreaterThanOrEqual) ||
......
......@@ -570,10 +570,18 @@ RUNTIME_FUNCTION(Runtime_GetOriginalConstructor) {
DCHECK(args.length() == 0);
JavaScriptFrameIterator it(isolate);
JavaScriptFrame* frame = it.frame();
// Currently we don't inline [[Construct]] calls.
return frame->IsConstructor() && !frame->HasInlinedFrames()
? frame->GetOriginalConstructor()
: isolate->heap()->undefined_value();
// TODO(4544): Currently we never inline any [[Construct]] calls where the
// actual constructor differs from the original constructor. Fix this soon!
if (frame->HasInlinedFrames()) {
HandleScope scope(isolate);
List<FrameSummary> frames(FLAG_max_inlining_levels + 1);
it.frame()->Summarize(&frames);
FrameSummary& summary = frames.last();
return summary.is_constructor() ? Object::cast(*summary.function())
: isolate->heap()->undefined_value();
}
return frame->IsConstructor() ? frame->GetOriginalConstructor()
: isolate->heap()->undefined_value();
}
......@@ -590,11 +598,13 @@ RUNTIME_FUNCTION(Runtime_ConvertReceiver) {
RUNTIME_FUNCTION(Runtime_IsConstructCall) {
SealHandleScope shs(isolate);
HandleScope scope(isolate);
DCHECK(args.length() == 0);
JavaScriptFrameIterator it(isolate);
JavaScriptFrame* frame = it.frame();
return isolate->heap()->ToBoolean(frame->IsConstructor());
List<FrameSummary> frames(FLAG_max_inlining_levels + 1);
it.frame()->Summarize(&frames);
FrameSummary& summary = frames.last();
return isolate->heap()->ToBoolean(summary.is_constructor());
}
......
......@@ -138,8 +138,14 @@
'debug-stepout-scope-part2': [PASS, NO_VARIANTS],
'debug-stepout-scope-part3': [PASS, NO_VARIANTS],
'es6/debug-evaluate-blockscopes': [PASS, NO_VARIANTS],
# issue 4055:
# Issue 4055: Scope chain length observed by debugger is off.
'es6/generators-debug-scopes': [PASS, NO_VARIANTS],
# Issue 4544: Stepping in inlined constructors is borked.
'es6/debug-promises/throw-in-constructor': [PASS, NO_VARIANTS],
'es6/debug-step-into-constructor': [PASS, NO_VARIANTS],
# TODO(4544): Investigate why assumptions about fast properties break.
'fast-prototype': [PASS, NO_VARIANTS],
# TODO(titzer): --always-opt incorrectly disables CrankShaft soft deopt points
'result-table-min': [PASS, NO_VARIANTS],
......
......@@ -78,7 +78,7 @@ const SharedOperator kSharedOperators[] = {
SHARED(ToName, Operator::kNoProperties, 1, 1, 1, 1, 1, 1, 2),
SHARED(ToObject, Operator::kNoProperties, 1, 1, 1, 1, 1, 1, 2),
SHARED(Yield, Operator::kNoProperties, 1, 0, 1, 1, 1, 1, 2),
SHARED(Create, Operator::kEliminatable, 0, 0, 1, 0, 1, 1, 0),
SHARED(Create, Operator::kEliminatable, 1, 0, 1, 0, 1, 1, 0),
SHARED(HasProperty, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
SHARED(TypeOf, Operator::kEliminatable, 1, 0, 1, 0, 1, 1, 0),
SHARED(InstanceOf, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
......
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