OLD | NEW |
---|---|
(Empty) | |
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/bytecode-graph-builder.h" | |
6 | |
7 #include "src/compiler/operator-properties.h" | |
8 | |
9 namespace v8 { | |
10 namespace internal { | |
11 namespace compiler { | |
12 | |
13 // Issues: | |
14 // - Need to deal with FrameState / FrameStateBeforeAndAfter / StateValue. | |
15 // - Scopes - intimately tied to AST. Need to eval what is needed. | |
16 // - Need story for context parameter, closure parameter, this. | |
17 // * GetFunctionClosureForContext() | |
18 // * GetFunctionClosure() | |
19 // * GetFunctionContext() | |
20 // | |
21 // NB Nodes talk slides: http://shortn/_fmVf0TjCC4 | |
rmcilroy
2015/09/01 14:00:06
nit - probably remove the internal talk link
oth
2015/09/01 15:25:57
Done.
| |
22 | |
23 BytecodeGraphBuilder::Environment::Environment(BytecodeGraphBuilder* builder, | |
24 int locals_count, | |
25 Node* control_dependency) | |
26 : builder_(builder), | |
27 locals_count_(locals_count), | |
28 control_dependency_(control_dependency), | |
29 effect_dependency_(control_dependency), | |
30 values_(builder->local_zone()) { | |
31 // | |
32 // values_ layout | |
33 // | |
34 // [parameters] [receiver] [registers] [accumulator] [old registers] | |
35 // | |
36 // The accumulator is treated as a bytecode register. As values are | |
37 // overwritten, the old values are pushed to the back of the values | |
38 // array. | |
39 // | |
40 // TODO(oth): add support for parameters | |
41 // | |
42 register_base_ = values()->size(); | |
43 Node* undefined_constant = builder->jsgraph()->UndefinedConstant(); | |
44 | |
45 values()->insert(values()->end(), locals_count, undefined_constant); | |
46 values()->push_back(undefined_constant); | |
rmcilroy
2015/09/01 14:00:06
nit - comment that this is the accumulator
oth
2015/09/01 15:25:57
Moved accumulator out of values per later review c
| |
47 } | |
48 | |
49 | |
50 void BytecodeGraphBuilder::Environment::BindRegister(size_t register_index, | |
51 Node* node) { | |
52 size_t values_index = register_index + register_base(); | |
53 values()->push_back(values()->at(values_index)); | |
rmcilroy
2015/09/01 14:00:06
Do we need to keep the old register values around
oth
2015/09/01 15:25:57
Done.
| |
54 values()->at(values_index) = node; | |
55 } | |
56 | |
57 | |
58 Node* BytecodeGraphBuilder::Environment::LookupRegister( | |
59 size_t register_index) const { | |
60 size_t values_index = register_index + register_base(); | |
61 return values()->at(values_index); | |
62 } | |
63 | |
64 | |
65 void BytecodeGraphBuilder::Environment::BindAccumulator(Node* node) { | |
66 BindRegister(accumulator_pseudo_register(), node); | |
rmcilroy
2015/09/01 14:00:06
nit - could we just have a separate Node* accumula
oth
2015/09/01 15:25:57
Done.
| |
67 } | |
68 | |
69 | |
70 Node* BytecodeGraphBuilder::Environment::LookupAccumulator() const { | |
71 return LookupRegister(accumulator_pseudo_register()); | |
72 } | |
73 | |
74 | |
75 bool BytecodeGraphBuilder::Environment::IsMarkedAsUnreachable() const { | |
76 return GetControlDependency()->opcode() == IrOpcode::kDead; | |
77 } | |
78 | |
79 | |
80 void BytecodeGraphBuilder::Environment::MarkAsUnreachable() { | |
81 UpdateControlDependency(builder()->jsgraph()->Dead()); | |
82 } | |
83 | |
84 | |
85 BytecodeGraphBuilder::BytecodeGraphBuilder(Zone* local_zone, | |
86 CompilationInfo* compilation_info, | |
87 JSGraph* jsgraph) | |
88 : local_zone_(local_zone), | |
89 info_(compilation_info), | |
90 jsgraph_(jsgraph), | |
91 exit_controls_(local_zone) { | |
92 bytecode_array_ = handle(info()->shared_info()->bytecode_array()); | |
93 } | |
94 | |
95 | |
96 BytecodeGraphBuilder::BytecodeGraphBuilder(Zone* local_zone, | |
97 Handle<BytecodeArray> bytecode_array, | |
98 JSGraph* jsgraph) | |
99 : local_zone_(local_zone), | |
100 info_(nullptr), | |
101 jsgraph_(jsgraph), | |
102 bytecode_array_(bytecode_array), | |
103 input_buffer_size_(0), | |
104 input_buffer_(nullptr), | |
105 exit_controls_(local_zone) {} | |
106 | |
107 | |
108 bool BytecodeGraphBuilder::CreateGraph(bool stack_check) { | |
109 // Set up the basic structure of the graph. Outputs for {Start} are | |
110 // the formal parameters (including the receiver) plus context and | |
111 // closure. | |
112 | |
113 int actual_parameter_count = info()->num_parameters_including_this() + 2; | |
114 graph()->SetStart(graph()->NewNode(common()->Start(actual_parameter_count))); | |
115 | |
116 // Locals count are the explicitly named interpreter registers. | |
117 int locals_count = bytecode_array()->frame_size() / kPointerSize; | |
rmcilroy
2015/09/01 14:00:06
optional nit - you could add a locals_count() help
oth
2015/09/01 15:25:57
Done.
| |
118 Environment env(this, locals_count, graph()->start()); | |
119 set_environment(&env); | |
120 | |
121 // Build function context only if there are context allocated variables. | |
122 if (info()->num_heap_slots() > 0) { | |
123 UNIMPLEMENTED(); // write ast-graph-builder equivalent. | |
124 } else { | |
125 // Simply use the outer function context in building the graph. | |
126 CreateGraphBody(stack_check); | |
127 } | |
128 | |
129 // Finish the basic structure of the graph. | |
130 DCHECK_NE(0u, exit_controls_.size()); | |
131 int const input_count = static_cast<int>(exit_controls_.size()); | |
132 Node** const inputs = &exit_controls_.front(); | |
133 Node* end = graph()->NewNode(common()->End(input_count), input_count, inputs); | |
134 graph()->SetEnd(end); | |
135 | |
136 return true; | |
137 } | |
138 | |
139 | |
140 bool BytecodeGraphBuilder::CreateGraphForTest(bool stack_check) { | |
141 // Set up the basic structure of the graph. Outputs for {Start} are | |
142 // the formal parameters (including the receiver) plus context and | |
143 // closure. | |
144 int actual_parameter_count = | |
145 /* info()->num_parameters_including_this() */ 0 + 2; | |
146 graph()->SetStart(graph()->NewNode(common()->Start(actual_parameter_count))); | |
147 | |
148 // Locals count are the explicitly named interpreter registers. | |
149 int locals_count = bytecode_array()->frame_size() / kPointerSize; | |
150 Environment env(this, locals_count, graph()->start()); | |
151 set_environment(&env); | |
152 | |
153 // Build function context only if there are context allocated variables. | |
154 // - Assume no context allocated variables. | |
155 // Simply use the outer function context in building the graph. | |
156 CreateGraphBody(stack_check); | |
157 | |
158 // Finish the basic structure of the graph. | |
159 DCHECK_NE(0u, exit_controls_.size()); | |
160 int const input_count = static_cast<int>(exit_controls_.size()); | |
161 Node** const inputs = &exit_controls_.front(); | |
162 Node* end = graph()->NewNode(common()->End(input_count), input_count, inputs); | |
163 graph()->SetEnd(end); | |
164 | |
165 return true; | |
166 } | |
167 | |
168 | |
169 void BytecodeGraphBuilder::CreateGraphBody(bool stack_check) { | |
170 // TODO(oth): write ast-graph-builder equivalent. | |
rmcilroy
2015/09/01 14:00:06
not sure what this TODO means, could you add more
oth
2015/09/01 15:25:57
Done.
| |
171 VisitBytecodes(); | |
172 } | |
173 | |
174 | |
175 void BytecodeGraphBuilder::VisitBytecodes() { | |
176 int last_bytecode_length = 0; | |
177 for (int i = 0; i < bytecode_array()->length(); i += last_bytecode_length) { | |
178 interpreter::Bytecode bytecode = bytecode_at(i); | |
179 int operand_offset = i + 1; | |
180 switch (bytecode) { | |
181 #define BYTECODE_CASE(name, ...) \ | |
182 case interpreter::Bytecode::k##name: \ | |
183 Visit##name(operand_offset); \ | |
184 break; | |
185 BYTECODE_LIST(BYTECODE_CASE) | |
186 #undef BYTECODE_CODE | |
187 } | |
188 last_bytecode_length = interpreter::Bytecodes::Size(bytecode); | |
189 } | |
190 } | |
191 | |
192 | |
193 void BytecodeGraphBuilder::VisitLdaZero(int operand_offset) { | |
194 Node* node = jsgraph()->ZeroConstant(); | |
195 environment()->BindAccumulator(node); | |
196 } | |
197 | |
198 | |
199 void BytecodeGraphBuilder::VisitLdaSmi8(int operand_offset) { | |
200 Node* node = jsgraph()->Int32Constant(smi8_at(operand_offset)); | |
201 environment()->BindAccumulator(node); | |
202 } | |
203 | |
204 | |
205 void BytecodeGraphBuilder::VisitLdaConstant(int operand_offset) { | |
206 Handle<FixedArray> constants = handle(bytecode_array()->constant_pool()); | |
207 Handle<Object> constant = FixedArray::get(constants, operand_offset); | |
rmcilroy
2015/09/01 14:00:06
nit - create a helper for getting a given constant
oth
2015/09/01 15:25:57
Done. Noticed this code is broken anyway - it need
| |
208 Node* node = jsgraph()->Constant(constant); | |
209 environment()->BindAccumulator(node); | |
210 } | |
211 | |
212 | |
213 void BytecodeGraphBuilder::VisitLdaUndefined(int operand_offset) { | |
214 Node* node = jsgraph()->UndefinedConstant(); | |
215 environment()->BindAccumulator(node); | |
216 } | |
217 | |
218 | |
219 void BytecodeGraphBuilder::VisitLdaNull(int operand_offset) { | |
220 Node* node = jsgraph()->NullConstant(); | |
221 environment()->BindAccumulator(node); | |
222 } | |
223 | |
224 | |
225 void BytecodeGraphBuilder::VisitLdaTheHole(int operand_offset) { | |
226 Node* node = jsgraph()->TheHoleConstant(); | |
227 environment()->BindAccumulator(node); | |
228 } | |
229 | |
230 | |
231 void BytecodeGraphBuilder::VisitLdaTrue(int operand_offset) { | |
232 Node* node = jsgraph()->TrueConstant(); | |
233 environment()->BindAccumulator(node); | |
234 } | |
235 | |
236 | |
237 void BytecodeGraphBuilder::VisitLdaFalse(int operand_offset) { | |
238 Node* node = jsgraph()->FalseConstant(); | |
239 environment()->BindAccumulator(node); | |
240 } | |
241 | |
242 | |
243 void BytecodeGraphBuilder::VisitLdar(int operand_offset) { | |
244 Node* value = environment()->LookupRegister(register_at(operand_offset)); | |
245 environment()->BindAccumulator(value); | |
246 } | |
247 | |
248 | |
249 void BytecodeGraphBuilder::VisitStar(int operand_offset) { | |
250 Node* value = environment()->LookupAccumulator(); | |
251 environment()->BindRegister(register_at(operand_offset), value); | |
252 } | |
253 | |
254 | |
255 void BytecodeGraphBuilder::FinishBinaryOperation(Node* node) { | |
256 // What is the minimal work here? | |
257 // In the initial absence of: | |
258 // - FrameStateBeforeAndAfter / BailOutId | |
259 // - Environment check pointing | |
260 environment()->BindAccumulator(node); | |
261 } | |
262 | |
263 | |
264 void BytecodeGraphBuilder::VisitAdd(int operand_offset) { | |
265 const Operator* js_op = javascript()->Add(language_mode()); | |
266 Node* left = environment()->LookupRegister(register_at(operand_offset)); | |
267 Node* right = environment()->LookupAccumulator(); | |
268 Node* node = NewNode(js_op, left, right); | |
269 FinishBinaryOperation(node); | |
270 } | |
271 | |
272 | |
273 void BytecodeGraphBuilder::VisitSub(int operand_offset) { | |
274 const Operator* js_op = javascript()->Subtract(language_mode()); | |
275 Node* left = environment()->LookupRegister(register_at(operand_offset)); | |
276 Node* right = environment()->LookupAccumulator(); | |
277 Node* node = NewNode(js_op, left, right); | |
278 FinishBinaryOperation(node); | |
279 } | |
280 | |
281 | |
282 void BytecodeGraphBuilder::VisitMul(int operand_offset) { | |
283 const Operator* js_op = javascript()->Multiply(language_mode()); | |
284 Node* left = environment()->LookupRegister(register_at(operand_offset)); | |
285 Node* right = environment()->LookupAccumulator(); | |
286 Node* node = NewNode(js_op, left, right); | |
287 FinishBinaryOperation(node); | |
288 } | |
289 | |
290 | |
291 void BytecodeGraphBuilder::VisitDiv(int operand_offset) { | |
292 const Operator* js_op = javascript()->Divide(language_mode()); | |
293 Node* left = environment()->LookupRegister(register_at(operand_offset)); | |
294 Node* right = environment()->LookupAccumulator(); | |
295 Node* node = NewNode(js_op, left, right); | |
296 FinishBinaryOperation(node); | |
297 } | |
298 | |
299 | |
300 void BytecodeGraphBuilder::VisitMod(int operand_offset) { | |
301 const Operator* js_op = javascript()->Modulus(language_mode()); | |
302 Node* left = environment()->LookupRegister(register_at(operand_offset)); | |
303 Node* right = environment()->LookupAccumulator(); | |
304 Node* node = NewNode(js_op, left, right); | |
305 FinishBinaryOperation(node); | |
306 } | |
307 | |
308 | |
309 void BytecodeGraphBuilder::VisitReturn(int operand_offset) { | |
310 Node* control = | |
311 NewNode(common()->Return(), environment()->LookupAccumulator()); | |
312 UpdateControlDependencyToLeaveFunction(control); | |
313 } | |
314 | |
315 | |
316 Node** BytecodeGraphBuilder::EnsureInputBufferSize(int size) { | |
317 if (size > input_buffer_size_) { | |
318 size = size + kInputBufferSizeIncrement + input_buffer_size_; | |
319 input_buffer_ = local_zone()->NewArray<Node*>(size); | |
320 input_buffer_size_ = size; | |
321 } | |
322 return input_buffer_; | |
323 } | |
324 | |
325 | |
326 Node* BytecodeGraphBuilder::MakeNode(const Operator* op, int value_input_count, | |
327 Node** value_inputs, bool incomplete) { | |
328 DCHECK_EQ(op->ValueInputCount(), value_input_count); | |
329 | |
330 bool has_context = OperatorProperties::HasContextInput(op); | |
331 int frame_state_count = OperatorProperties::GetFrameStateInputCount(op); | |
332 bool has_control = op->ControlInputCount() == 1; | |
333 bool has_effect = op->EffectInputCount() == 1; | |
334 | |
335 DCHECK_LT(op->ControlInputCount(), 2); | |
336 DCHECK_LT(op->EffectInputCount(), 2); | |
337 | |
338 Node* result = NULL; | |
339 if (!has_context && frame_state_count == 0 && !has_control && !has_effect) { | |
340 result = graph()->NewNode(op, value_input_count, value_inputs, incomplete); | |
341 } else { | |
342 int input_count_with_deps = value_input_count; | |
343 if (has_context) ++input_count_with_deps; | |
344 input_count_with_deps += frame_state_count; | |
345 if (has_control) ++input_count_with_deps; | |
346 if (has_effect) ++input_count_with_deps; | |
347 Node** buffer = EnsureInputBufferSize(input_count_with_deps); | |
348 memcpy(buffer, value_inputs, kPointerSize * value_input_count); | |
349 Node** current_input = buffer + value_input_count; | |
350 if (has_context) { | |
351 // TODO(oth): need context. | |
352 *current_input++ = jsgraph()->Dead(); | |
353 } | |
354 for (int i = 0; i < frame_state_count; i++) { | |
355 // The frame state will be inserted later. Here we misuse | |
356 // the {Dead} node as a sentinel to be later overwritten | |
357 // with the real frame state. | |
358 *current_input++ = jsgraph()->Dead(); | |
359 } | |
360 if (has_effect) { | |
361 *current_input++ = environment()->GetEffectDependency(); | |
362 } | |
363 if (has_control) { | |
364 *current_input++ = environment()->GetControlDependency(); | |
365 } | |
366 result = graph()->NewNode(op, input_count_with_deps, buffer, incomplete); | |
367 if (!environment()->IsMarkedAsUnreachable()) { | |
368 // Update the current control dependency for control-producing nodes. | |
369 if (NodeProperties::IsControl(result)) { | |
370 environment()->UpdateControlDependency(result); | |
371 } | |
372 // Update the current effect dependency for effect-producing nodes. | |
373 if (result->op()->EffectOutputCount() > 0) { | |
374 environment()->UpdateEffectDependency(result); | |
375 } | |
376 // Add implicit success continuation for throwing nodes. | |
377 if (!result->op()->HasProperty(Operator::kNoThrow)) { | |
378 const Operator* op = common()->IfSuccess(); | |
379 Node* on_success = graph()->NewNode(op, result); | |
380 environment_->UpdateControlDependency(on_success); | |
381 } | |
382 } | |
383 } | |
384 | |
385 return result; | |
386 } | |
387 | |
388 | |
389 Node* BytecodeGraphBuilder::MergeControl(Node* control, Node* other) { | |
390 int inputs = control->op()->ControlInputCount() + 1; | |
391 if (control->opcode() == IrOpcode::kLoop) { | |
392 // Control node for loop exists, add input. | |
393 const Operator* op = common()->Loop(inputs); | |
394 control->AppendInput(graph_zone(), other); | |
395 control->set_op(op); | |
396 } else if (control->opcode() == IrOpcode::kMerge) { | |
397 // Control node for merge exists, add input. | |
398 const Operator* op = common()->Merge(inputs); | |
399 control->AppendInput(graph_zone(), other); | |
400 control->set_op(op); | |
401 } else { | |
402 // Control node is a singleton, introduce a merge. | |
403 const Operator* op = common()->Merge(inputs); | |
404 Node* inputs[] = {control, other}; | |
405 control = graph()->NewNode(op, arraysize(inputs), inputs, true); | |
406 } | |
407 return control; | |
408 } | |
409 | |
410 | |
411 void BytecodeGraphBuilder::UpdateControlDependencyToLeaveFunction(Node* exit) { | |
412 if (environment()->IsMarkedAsUnreachable()) return; | |
413 environment()->MarkAsUnreachable(); | |
414 exit_controls_.push_back(exit); | |
415 } | |
416 | |
417 | |
418 interpreter::Bytecode BytecodeGraphBuilder::bytecode_at(int offset) const { | |
419 return interpreter::Bytecodes::FromByte(bytecode_array()->get(offset)); | |
420 } | |
421 | |
422 | |
423 int8_t BytecodeGraphBuilder::smi8_at(int offset) const { | |
424 return static_cast<int8_t>(bytecode_array()->get(offset)); | |
425 } | |
426 | |
427 | |
428 int8_t BytecodeGraphBuilder::register_at(int offset) const { | |
429 return static_cast<int8_t>(-bytecode_array()->get(offset)); | |
430 } | |
431 | |
432 } // namespace compiler | |
433 } // namespace internal | |
434 } // namespace v8 | |
OLD | NEW |