1 // Copyright 2015 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/compiler/frame-states.h"
6
7 #include "src/base/functional.h"
8 #include "src/codegen/callable.h"
9 #include "src/compiler/graph.h"
10 #include "src/compiler/js-graph.h"
11 #include "src/compiler/node.h"
12 #include "src/handles/handles-inl.h"
13 #include "src/objects/objects-inl.h"
14
15 namespace v8 {
16 namespace internal {
17 namespace compiler {
18
19 // Guard equality of these constants. Ideally they should be merged at
20 // some point.
21 STATIC_ASSERT(kFrameStateOuterStateInput ==
22 FrameState::kFrameStateOuterStateInput);
23
hash_value(OutputFrameStateCombine const & sc)24 size_t hash_value(OutputFrameStateCombine const& sc) {
25 return base::hash_value(sc.parameter_);
26 }
27
28
operator <<(std::ostream & os,OutputFrameStateCombine const & sc)29 std::ostream& operator<<(std::ostream& os, OutputFrameStateCombine const& sc) {
30 if (sc.parameter_ == OutputFrameStateCombine::kInvalidIndex)
31 return os << "Ignore";
32 return os << "PokeAt(" << sc.parameter_ << ")";
33 }
34
35
operator ==(FrameStateInfo const & lhs,FrameStateInfo const & rhs)36 bool operator==(FrameStateInfo const& lhs, FrameStateInfo const& rhs) {
37 return lhs.type() == rhs.type() && lhs.bailout_id() == rhs.bailout_id() &&
38 lhs.state_combine() == rhs.state_combine() &&
39 lhs.function_info() == rhs.function_info();
40 }
41
42
operator !=(FrameStateInfo const & lhs,FrameStateInfo const & rhs)43 bool operator!=(FrameStateInfo const& lhs, FrameStateInfo const& rhs) {
44 return !(lhs == rhs);
45 }
46
47
hash_value(FrameStateInfo const & info)48 size_t hash_value(FrameStateInfo const& info) {
49 return base::hash_combine(static_cast<int>(info.type()), info.bailout_id(),
50 info.state_combine());
51 }
52
53
operator <<(std::ostream & os,FrameStateType type)54 std::ostream& operator<<(std::ostream& os, FrameStateType type) {
55 switch (type) {
56 case FrameStateType::kInterpretedFunction:
57 os << "INTERPRETED_FRAME";
58 break;
59 case FrameStateType::kArgumentsAdaptor:
60 os << "ARGUMENTS_ADAPTOR";
61 break;
62 case FrameStateType::kConstructStub:
63 os << "CONSTRUCT_STUB";
64 break;
65 case FrameStateType::kBuiltinContinuation:
66 os << "BUILTIN_CONTINUATION_FRAME";
67 break;
68 case FrameStateType::kJavaScriptBuiltinContinuation:
69 os << "JAVA_SCRIPT_BUILTIN_CONTINUATION_FRAME";
70 break;
71 case FrameStateType::kJavaScriptBuiltinContinuationWithCatch:
72 os << "JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH_FRAME";
73 break;
74 }
75 return os;
76 }
77
78
operator <<(std::ostream & os,FrameStateInfo const & info)79 std::ostream& operator<<(std::ostream& os, FrameStateInfo const& info) {
80 os << info.type() << ", " << info.bailout_id() << ", "
81 << info.state_combine();
82 Handle<SharedFunctionInfo> shared_info;
83 if (info.shared_info().ToHandle(&shared_info)) {
84 os << ", " << Brief(*shared_info);
85 }
86 return os;
87 }
88
89 namespace {
90
91 // Lazy deopt points where the frame state is assocated with a call get an
92 // additional parameter for the return result from the call. The return result
93 // is added by the deoptimizer and not explicitly specified in the frame state.
94 // Lazy deopt points which can catch exceptions further get an additional
95 // parameter, namely the exception thrown. The exception is also added by the
96 // deoptimizer.
DeoptimizerParameterCountFor(ContinuationFrameStateMode mode)97 uint8_t DeoptimizerParameterCountFor(ContinuationFrameStateMode mode) {
98 switch (mode) {
99 case ContinuationFrameStateMode::EAGER:
100 return 0;
101 case ContinuationFrameStateMode::LAZY:
102 return 1;
103 case ContinuationFrameStateMode::LAZY_WITH_CATCH:
104 return 2;
105 }
106 UNREACHABLE();
107 }
108
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> ())109 FrameState CreateBuiltinContinuationFrameStateCommon(
110 JSGraph* jsgraph, FrameStateType frame_type, Builtins::Name name,
111 Node* closure, Node* context, Node** parameters, int parameter_count,
112 Node* outer_frame_state,
113 Handle<SharedFunctionInfo> shared = Handle<SharedFunctionInfo>()) {
114 Graph* const graph = jsgraph->graph();
115 CommonOperatorBuilder* const common = jsgraph->common();
116
117 const Operator* op_param =
118 common->StateValues(parameter_count, SparseInputMask::Dense());
119 Node* params_node = graph->NewNode(op_param, parameter_count, parameters);
120
121 BailoutId bailout_id = Builtins::GetContinuationBailoutId(name);
122 const FrameStateFunctionInfo* state_info =
123 common->CreateFrameStateFunctionInfo(frame_type, parameter_count, 0,
124 shared);
125 const Operator* op = common->FrameState(
126 bailout_id, OutputFrameStateCombine::Ignore(), state_info);
127 return FrameState(graph->NewNode(op, params_node, jsgraph->EmptyStateValues(),
128 jsgraph->EmptyStateValues(), context,
129 closure, outer_frame_state));
130 }
131
132 } // namespace
133
CreateStubBuiltinContinuationFrameState(JSGraph * jsgraph,Builtins::Name name,Node * context,Node * const * parameters,int parameter_count,Node * outer_frame_state,ContinuationFrameStateMode mode)134 FrameState CreateStubBuiltinContinuationFrameState(
135 JSGraph* jsgraph, Builtins::Name name, Node* context,
136 Node* const* parameters, int parameter_count, Node* outer_frame_state,
137 ContinuationFrameStateMode mode) {
138 Callable callable = Builtins::CallableFor(jsgraph->isolate(), name);
139 CallInterfaceDescriptor descriptor = callable.descriptor();
140
141 std::vector<Node*> actual_parameters;
142 // Stack parameters first. Depending on {mode}, final parameters are added
143 // by the deoptimizer and aren't explicitly passed in the frame state.
144 int stack_parameter_count =
145 descriptor.GetStackParameterCount() - DeoptimizerParameterCountFor(mode);
146
147 // Ensure the parameters added by the deoptimizer are passed on the stack.
148 // This check prevents using TFS builtins as continuations while doing the
149 // lazy deopt. Use TFC or TFJ builtin as a lazy deopt continuation which
150 // would pass the result parameter on the stack.
151 DCHECK_GE(stack_parameter_count, 0);
152
153 // Reserving space in the vector.
154 actual_parameters.reserve(stack_parameter_count +
155 descriptor.GetRegisterParameterCount());
156 for (int i = 0; i < stack_parameter_count; ++i) {
157 actual_parameters.push_back(
158 parameters[descriptor.GetRegisterParameterCount() + i]);
159 }
160 // Register parameters follow, context will be added by instruction selector
161 // during FrameState translation.
162 for (int i = 0; i < descriptor.GetRegisterParameterCount(); ++i) {
163 actual_parameters.push_back(parameters[i]);
164 }
165
166 return CreateBuiltinContinuationFrameStateCommon(
167 jsgraph, FrameStateType::kBuiltinContinuation, name,
168 jsgraph->UndefinedConstant(), context, actual_parameters.data(),
169 static_cast<int>(actual_parameters.size()), outer_frame_state);
170 }
171
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)172 FrameState CreateJavaScriptBuiltinContinuationFrameState(
173 JSGraph* jsgraph, const SharedFunctionInfoRef& shared, Builtins::Name name,
174 Node* target, Node* context, Node* const* stack_parameters,
175 int stack_parameter_count, Node* outer_frame_state,
176 ContinuationFrameStateMode mode) {
177 // Depending on {mode}, final parameters are added by the deoptimizer
178 // and aren't explicitly passed in the frame state.
179 DCHECK_EQ(Builtins::GetStackParameterCount(name) + 1, // add receiver
180 stack_parameter_count + DeoptimizerParameterCountFor(mode));
181
182 Node* argc = jsgraph->Constant(Builtins::GetStackParameterCount(name));
183
184 // Stack parameters first. They must be first because the receiver is expected
185 // to be the second value in the translation when creating stack crawls
186 // (e.g. Error.stack) of optimized JavaScript frames.
187 std::vector<Node*> actual_parameters;
188 for (int i = 0; i < stack_parameter_count; ++i) {
189 actual_parameters.push_back(stack_parameters[i]);
190 }
191
192 Node* new_target = jsgraph->UndefinedConstant();
193
194 // Register parameters follow stack parameters. The context will be added by
195 // instruction selector during FrameState translation.
196 actual_parameters.push_back(target); // kJavaScriptCallTargetRegister
197 actual_parameters.push_back(new_target); // kJavaScriptCallNewTargetRegister
198 actual_parameters.push_back(argc); // kJavaScriptCallArgCountRegister
199
200 return CreateBuiltinContinuationFrameStateCommon(
201 jsgraph,
202 mode == ContinuationFrameStateMode::LAZY_WITH_CATCH
203 ? FrameStateType::kJavaScriptBuiltinContinuationWithCatch
204 : FrameStateType::kJavaScriptBuiltinContinuation,
205 name, target, context, &actual_parameters[0],
206 static_cast<int>(actual_parameters.size()), outer_frame_state,
207 shared.object());
208 }
209
CreateGenericLazyDeoptContinuationFrameState(JSGraph * graph,const SharedFunctionInfoRef & shared,Node * target,Node * context,Node * receiver,Node * outer_frame_state)210 FrameState CreateGenericLazyDeoptContinuationFrameState(
211 JSGraph* graph, const SharedFunctionInfoRef& shared, Node* target,
212 Node* context, Node* receiver, Node* outer_frame_state) {
213 Node* stack_parameters[]{receiver};
214 const int stack_parameter_count = arraysize(stack_parameters);
215 return CreateJavaScriptBuiltinContinuationFrameState(
216 graph, shared, Builtins::kGenericLazyDeoptContinuation, target, context,
217 stack_parameters, stack_parameter_count, outer_frame_state,
218 ContinuationFrameStateMode::LAZY);
219 }
220
221 } // namespace compiler
222 } // namespace internal
223 } // namespace v8
224