blob: 5598a0fe59f131e2dd25105c03f41ebd4a8e9f5e [file] [log] [blame]
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/compiler/frame-states.h"
#include "src/base/functional.h"
#include "src/codegen/callable.h"
#include "src/compiler/graph.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/node.h"
#include "src/handles/handles-inl.h"
#include "src/objects/objects-inl.h"
namespace v8 {
namespace internal {
namespace compiler {
// Guard equality of these constants. Ideally they should be merged at
// some point.
STATIC_ASSERT(kFrameStateOuterStateInput ==
FrameState::kFrameStateOuterStateInput);
size_t hash_value(OutputFrameStateCombine const& sc) {
return base::hash_value(sc.parameter_);
}
std::ostream& operator<<(std::ostream& os, OutputFrameStateCombine const& sc) {
if (sc.parameter_ == OutputFrameStateCombine::kInvalidIndex)
return os << "Ignore";
return os << "PokeAt(" << sc.parameter_ << ")";
}
bool operator==(FrameStateInfo const& lhs, FrameStateInfo const& rhs) {
return lhs.type() == rhs.type() && lhs.bailout_id() == rhs.bailout_id() &&
lhs.state_combine() == rhs.state_combine() &&
lhs.function_info() == rhs.function_info();
}
bool operator!=(FrameStateInfo const& lhs, FrameStateInfo const& rhs) {
return !(lhs == rhs);
}
size_t hash_value(FrameStateInfo const& info) {
return base::hash_combine(static_cast<int>(info.type()), info.bailout_id(),
info.state_combine());
}
std::ostream& operator<<(std::ostream& os, FrameStateType type) {
switch (type) {
case FrameStateType::kInterpretedFunction:
os << "INTERPRETED_FRAME";
break;
case FrameStateType::kArgumentsAdaptor:
os << "ARGUMENTS_ADAPTOR";
break;
case FrameStateType::kConstructStub:
os << "CONSTRUCT_STUB";
break;
case FrameStateType::kBuiltinContinuation:
os << "BUILTIN_CONTINUATION_FRAME";
break;
case FrameStateType::kJavaScriptBuiltinContinuation:
os << "JAVA_SCRIPT_BUILTIN_CONTINUATION_FRAME";
break;
case FrameStateType::kJavaScriptBuiltinContinuationWithCatch:
os << "JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH_FRAME";
break;
}
return os;
}
std::ostream& operator<<(std::ostream& os, FrameStateInfo const& info) {
os << info.type() << ", " << info.bailout_id() << ", "
<< info.state_combine();
Handle<SharedFunctionInfo> shared_info;
if (info.shared_info().ToHandle(&shared_info)) {
os << ", " << Brief(*shared_info);
}
return os;
}
namespace {
// Lazy deopt points where the frame state is assocated with a call get an
// additional parameter for the return result from the call. The return result
// is added by the deoptimizer and not explicitly specified in the frame state.
// Lazy deopt points which can catch exceptions further get an additional
// parameter, namely the exception thrown. The exception is also added by the
// deoptimizer.
uint8_t DeoptimizerParameterCountFor(ContinuationFrameStateMode mode) {
switch (mode) {
case ContinuationFrameStateMode::EAGER:
return 0;
case ContinuationFrameStateMode::LAZY:
return 1;
case ContinuationFrameStateMode::LAZY_WITH_CATCH:
return 2;
}
UNREACHABLE();
}
FrameState CreateBuiltinContinuationFrameStateCommon(
JSGraph* jsgraph, FrameStateType frame_type, Builtins::Name name,
Node* closure, Node* context, Node** parameters, int parameter_count,
Node* outer_frame_state,
Handle<SharedFunctionInfo> shared = Handle<SharedFunctionInfo>()) {
Graph* const graph = jsgraph->graph();
CommonOperatorBuilder* const common = jsgraph->common();
const Operator* op_param =
common->StateValues(parameter_count, SparseInputMask::Dense());
Node* params_node = graph->NewNode(op_param, parameter_count, parameters);
BailoutId bailout_id = Builtins::GetContinuationBailoutId(name);
const FrameStateFunctionInfo* state_info =
common->CreateFrameStateFunctionInfo(frame_type, parameter_count, 0,
shared);
const Operator* op = common->FrameState(
bailout_id, OutputFrameStateCombine::Ignore(), state_info);
return FrameState(graph->NewNode(op, params_node, jsgraph->EmptyStateValues(),
jsgraph->EmptyStateValues(), context,
closure, outer_frame_state));
}
} // namespace
FrameState CreateStubBuiltinContinuationFrameState(
JSGraph* jsgraph, Builtins::Name name, Node* context,
Node* const* parameters, int parameter_count, Node* outer_frame_state,
ContinuationFrameStateMode mode) {
Callable callable = Builtins::CallableFor(jsgraph->isolate(), name);
CallInterfaceDescriptor descriptor = callable.descriptor();
std::vector<Node*> actual_parameters;
// Stack parameters first. Depending on {mode}, final parameters are added
// by the deoptimizer and aren't explicitly passed in the frame state.
int stack_parameter_count =
descriptor.GetStackParameterCount() - DeoptimizerParameterCountFor(mode);
// Ensure the parameters added by the deoptimizer are passed on the stack.
// This check prevents using TFS builtins as continuations while doing the
// lazy deopt. Use TFC or TFJ builtin as a lazy deopt continuation which
// would pass the result parameter on the stack.
DCHECK_GE(stack_parameter_count, 0);
// Reserving space in the vector.
actual_parameters.reserve(stack_parameter_count +
descriptor.GetRegisterParameterCount());
for (int i = 0; i < stack_parameter_count; ++i) {
actual_parameters.push_back(
parameters[descriptor.GetRegisterParameterCount() + i]);
}
// Register parameters follow, context will be added by instruction selector
// during FrameState translation.
for (int i = 0; i < descriptor.GetRegisterParameterCount(); ++i) {
actual_parameters.push_back(parameters[i]);
}
return CreateBuiltinContinuationFrameStateCommon(
jsgraph, FrameStateType::kBuiltinContinuation, name,
jsgraph->UndefinedConstant(), context, actual_parameters.data(),
static_cast<int>(actual_parameters.size()), outer_frame_state);
}
FrameState CreateJavaScriptBuiltinContinuationFrameState(
JSGraph* jsgraph, const SharedFunctionInfoRef& shared, Builtins::Name name,
Node* target, Node* context, Node* const* stack_parameters,
int stack_parameter_count, Node* outer_frame_state,
ContinuationFrameStateMode mode) {
// Depending on {mode}, final parameters are added by the deoptimizer
// and aren't explicitly passed in the frame state.
DCHECK_EQ(Builtins::GetStackParameterCount(name) + 1, // add receiver
stack_parameter_count + DeoptimizerParameterCountFor(mode));
Node* argc = jsgraph->Constant(Builtins::GetStackParameterCount(name));
// Stack parameters first. They must be first because the receiver is expected
// to be the second value in the translation when creating stack crawls
// (e.g. Error.stack) of optimized JavaScript frames.
std::vector<Node*> actual_parameters;
for (int i = 0; i < stack_parameter_count; ++i) {
actual_parameters.push_back(stack_parameters[i]);
}
Node* new_target = jsgraph->UndefinedConstant();
// Register parameters follow stack parameters. The context will be added by
// instruction selector during FrameState translation.
actual_parameters.push_back(target); // kJavaScriptCallTargetRegister
actual_parameters.push_back(new_target); // kJavaScriptCallNewTargetRegister
actual_parameters.push_back(argc); // kJavaScriptCallArgCountRegister
return CreateBuiltinContinuationFrameStateCommon(
jsgraph,
mode == ContinuationFrameStateMode::LAZY_WITH_CATCH
? FrameStateType::kJavaScriptBuiltinContinuationWithCatch
: FrameStateType::kJavaScriptBuiltinContinuation,
name, target, context, &actual_parameters[0],
static_cast<int>(actual_parameters.size()), outer_frame_state,
shared.object());
}
FrameState CreateGenericLazyDeoptContinuationFrameState(
JSGraph* graph, const SharedFunctionInfoRef& shared, Node* target,
Node* context, Node* receiver, Node* outer_frame_state) {
Node* stack_parameters[]{receiver};
const int stack_parameter_count = arraysize(stack_parameters);
return CreateJavaScriptBuiltinContinuationFrameState(
graph, shared, Builtins::kGenericLazyDeoptContinuation, target, context,
stack_parameters, stack_parameter_count, outer_frame_state,
ContinuationFrameStateMode::LAZY);
}
} // namespace compiler
} // namespace internal
} // namespace v8