OLD | NEW |
---|---|
1 // Copyright 2015 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/interpreter/bytecode-generator.h" | 5 #include "src/interpreter/bytecode-generator.h" |
6 | 6 |
7 #include "src/ast/scopes.h" | 7 #include "src/ast/scopes.h" |
8 #include "src/compiler.h" | 8 #include "src/compiler.h" |
9 #include "src/interpreter/bytecode-register-allocator.h" | 9 #include "src/interpreter/bytecode-register-allocator.h" |
10 #include "src/interpreter/control-flow-builders.h" | 10 #include "src/interpreter/control-flow-builders.h" |
(...skipping 547 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
558 : isolate_(isolate), | 558 : isolate_(isolate), |
559 zone_(zone), | 559 zone_(zone), |
560 builder_(nullptr), | 560 builder_(nullptr), |
561 info_(nullptr), | 561 info_(nullptr), |
562 scope_(nullptr), | 562 scope_(nullptr), |
563 globals_(0, zone), | 563 globals_(0, zone), |
564 execution_control_(nullptr), | 564 execution_control_(nullptr), |
565 execution_context_(nullptr), | 565 execution_context_(nullptr), |
566 execution_result_(nullptr), | 566 execution_result_(nullptr), |
567 register_allocator_(nullptr), | 567 register_allocator_(nullptr), |
568 resume_points_(0, zone), | |
568 try_catch_nesting_level_(0), | 569 try_catch_nesting_level_(0), |
569 try_finally_nesting_level_(0) { | 570 try_finally_nesting_level_(0), |
571 yield_count_(0) { | |
570 InitializeAstVisitor(isolate); | 572 InitializeAstVisitor(isolate); |
571 } | 573 } |
572 | 574 |
573 Handle<BytecodeArray> BytecodeGenerator::MakeBytecode(CompilationInfo* info) { | 575 Handle<BytecodeArray> BytecodeGenerator::MakeBytecode(CompilationInfo* info) { |
574 set_info(info); | 576 set_info(info); |
575 set_scope(info->scope()); | 577 set_scope(info->scope()); |
576 | 578 |
577 // Initialize bytecode array builder. | 579 // Initialize bytecode array builder. |
578 set_builder(new (zone()) BytecodeArrayBuilder( | 580 set_builder(new (zone()) BytecodeArrayBuilder( |
579 isolate(), zone(), info->num_parameters_including_this(), | 581 isolate(), zone(), info->num_parameters_including_this(), |
580 scope()->MaxNestedContextChainLength(), scope()->num_stack_slots(), | 582 scope()->MaxNestedContextChainLength(), scope()->num_stack_slots(), |
581 info->literal())); | 583 info->literal())); |
582 | 584 |
583 // Initialize the incoming context. | 585 // Initialize the incoming context. |
584 ContextScope incoming_context(this, scope(), false); | 586 ContextScope incoming_context(this, scope(), false); |
585 | 587 |
586 // Initialize control scope. | 588 // Initialize control scope. |
587 ControlScopeForTopLevel control(this); | 589 ControlScopeForTopLevel control(this); |
588 | 590 |
591 if (IsGeneratorFunction(info->literal()->kind())) { | |
592 BuildGeneratorPrologue(); | |
593 } | |
594 | |
589 // Build function context only if there are context allocated variables. | 595 // Build function context only if there are context allocated variables. |
590 if (scope()->NeedsContext()) { | 596 if (scope()->NeedsContext()) { |
591 // Push a new inner context scope for the function. | 597 // Push a new inner context scope for the function. |
592 VisitNewLocalFunctionContext(); | 598 VisitNewLocalFunctionContext(); |
593 ContextScope local_function_context(this, scope(), false); | 599 ContextScope local_function_context(this, scope(), false); |
594 VisitBuildLocalActivationContext(); | 600 VisitBuildLocalActivationContext(); |
595 MakeBytecodeBody(); | 601 MakeBytecodeBody(); |
596 } else { | 602 } else { |
597 MakeBytecodeBody(); | 603 MakeBytecodeBody(); |
598 } | 604 } |
(...skipping 28 matching lines...) Expand all Loading... | |
627 // Visit declarations within the function scope. | 633 // Visit declarations within the function scope. |
628 VisitDeclarations(scope()->declarations()); | 634 VisitDeclarations(scope()->declarations()); |
629 | 635 |
630 // Perform a stack-check before the body. | 636 // Perform a stack-check before the body. |
631 builder()->StackCheck(); | 637 builder()->StackCheck(); |
632 | 638 |
633 // Visit statements in the function body. | 639 // Visit statements in the function body. |
634 VisitStatements(info()->literal()->body()); | 640 VisitStatements(info()->literal()->body()); |
635 } | 641 } |
636 | 642 |
643 void BytecodeGenerator::BuildGeneratorPrologue() { | |
644 resume_points_.clear(); | |
rmcilroy
2016/04/15 13:17:46
Do we need the clear? it should be empty when we r
neis
2016/04/18 08:04:17
MakeBytecode could be called several times on the
rmcilroy
2016/04/18 10:01:57
No it's only ever used for a single compile info.
neis
2016/04/18 14:07:52
Ok. After your change I can probably initialize it
| |
645 resume_points_.resize(info()->literal()->yield_count()); | |
646 | |
647 BytecodeLabel regular_call; | |
648 builder() | |
649 ->LoadAccumulatorWithRegister(Register::new_target()) | |
650 .JumpIfUndefined(®ular_call); | |
651 | |
652 // This is a resume call. Restore registers and perform state dispatch. | |
653 // (The current context has already been restored by the trampoline.) | |
654 { | |
655 RegisterAllocationScope register_scope(this); | |
656 Register state = register_allocator()->NewRegister(); | |
657 builder() | |
658 ->CallRuntime(Runtime::kResumeIgnitionGenerator, Register::new_target(), | |
659 1) | |
660 .StoreAccumulatorInRegister(state); | |
661 | |
662 // TODO(neis): Optimize this by using a proper jump table. | |
663 for (size_t i = 0; i < resume_points_.size(); ++i) { | |
664 builder() | |
665 ->LoadLiteral(Smi::FromInt(static_cast<int>(i))) | |
666 .CompareOperation(Token::Value::EQ_STRICT, state) | |
667 .JumpIfTrue(&(resume_points_[i])); | |
668 } | |
669 builder()->Illegal(); // Should never get here. | |
670 } | |
671 | |
672 builder()->Bind(®ular_call); | |
673 // This is a regular call. Fall through to the ordinary function prologue, | |
674 // after which we will run into the generator object creation and the initial | |
675 // yield (both inserted by the parser). | |
676 } | |
637 | 677 |
638 void BytecodeGenerator::VisitBlock(Block* stmt) { | 678 void BytecodeGenerator::VisitBlock(Block* stmt) { |
639 // Visit declarations and statements. | 679 // Visit declarations and statements. |
640 if (stmt->scope() != nullptr && stmt->scope()->NeedsContext()) { | 680 if (stmt->scope() != nullptr && stmt->scope()->NeedsContext()) { |
641 VisitNewLocalBlockContext(stmt->scope()); | 681 VisitNewLocalBlockContext(stmt->scope()); |
642 ContextScope scope(this, stmt->scope()); | 682 ContextScope scope(this, stmt->scope()); |
643 VisitBlockDeclarationsAndStatements(stmt); | 683 VisitBlockDeclarationsAndStatements(stmt); |
644 } else { | 684 } else { |
645 VisitBlockDeclarationsAndStatements(stmt); | 685 VisitBlockDeclarationsAndStatements(stmt); |
646 } | 686 } |
(...skipping 1575 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2222 } | 2262 } |
2223 case KEYED_SUPER_PROPERTY: { | 2263 case KEYED_SUPER_PROPERTY: { |
2224 builder()->StoreAccumulatorInRegister(value); | 2264 builder()->StoreAccumulatorInRegister(value); |
2225 BuildKeyedSuperPropertyStore(object, home_object, key, value); | 2265 BuildKeyedSuperPropertyStore(object, home_object, key, value); |
2226 break; | 2266 break; |
2227 } | 2267 } |
2228 } | 2268 } |
2229 execution_result()->SetResultInAccumulator(); | 2269 execution_result()->SetResultInAccumulator(); |
2230 } | 2270 } |
2231 | 2271 |
2272 void BytecodeGenerator::VisitYield(Yield* expr) { | |
2273 int id = yield_count_++; | |
rmcilroy
2016/04/15 13:17:46
Rather than doing this id naming implicitly, would
neis
2016/04/18 08:04:17
I don't think that would be safer and I don't see
rmcilroy
2016/04/18 10:01:57
OK. I'm fine with this for this CL, but maybe it's
neis
2016/04/18 14:07:52
OK. Also, I probably need to move them into array
| |
2232 | 2274 |
2233 void BytecodeGenerator::VisitYield(Yield* expr) { UNIMPLEMENTED(); } | 2275 builder()->SetExpressionPosition(expr); |
2276 Register value = VisitForRegisterValue(expr->expression()); | |
rmcilroy
2016/04/15 13:17:46
nit - maybe scope the pre-yield, and post-resume p
neis
2016/04/18 08:04:17
Thanks, I didn't think of that. I added a scope f
| |
2234 | 2277 |
2278 register_allocator()->PrepareForConsecutiveAllocations(2); | |
2279 Register generator = register_allocator()->NextConsecutiveRegister(); | |
2280 Register state = register_allocator()->NextConsecutiveRegister(); | |
2281 | |
2282 // Save context, registers, and state. Then return. | |
2283 VisitForRegisterValue(expr->generator_object(), generator); | |
2284 builder() | |
2285 ->LoadLiteral(Smi::FromInt(id)) | |
2286 .StoreAccumulatorInRegister(state) | |
2287 .CallRuntime(Runtime::kSuspendIgnitionGenerator, generator, 2) | |
2288 .LoadAccumulatorWithRegister(value) | |
2289 .Return(); // Hard return (ignore any finally blocks). | |
2290 | |
2291 builder()->Bind(&(resume_points_[id])); | |
2292 // Upon resume, we continue here. | |
2293 | |
2294 Register input = register_allocator()->NewRegister(); | |
2295 builder() | |
2296 ->CallRuntime(Runtime::kGeneratorGetInput, generator, 1) | |
2297 .StoreAccumulatorInRegister(input); | |
2298 | |
2299 Register resume_mode = register_allocator()->NewRegister(); | |
2300 builder() | |
2301 ->CallRuntime(Runtime::kGeneratorGetResumeMode, generator, 1) | |
rmcilroy
2016/04/15 13:17:46
Maybe you could have a runtime function which retu
neis
2016/04/18 08:04:17
Let's keep it as is for now. I think we want to g
rmcilroy
2016/04/18 10:01:57
Acknowledged.
| |
2302 .StoreAccumulatorInRegister(resume_mode); | |
2303 | |
2304 // Dispatch on resume mode. | |
2305 { | |
2306 BytecodeLabel resume_with_next; | |
2307 BytecodeLabel resume_with_return; | |
2308 BytecodeLabel resume_with_throw; | |
2309 | |
2310 builder() | |
2311 ->LoadLiteral(Smi::FromInt(JSGeneratorObject::kReturn)) | |
2312 .CompareOperation(Token::EQ_STRICT, resume_mode) | |
2313 .JumpIfTrue(&resume_with_return) | |
2314 .LoadLiteral(Smi::FromInt(JSGeneratorObject::kThrow)) | |
2315 .CompareOperation(Token::EQ_STRICT, resume_mode) | |
2316 .JumpIfTrue(&resume_with_throw) | |
2317 .Jump(&resume_with_next); | |
rmcilroy
2016/04/15 13:17:46
(micro optimization) - presumably JSGeneratorObjec
neis
2016/04/18 08:04:17
Done.
| |
2318 | |
2319 builder()->Bind(&resume_with_return); | |
2320 { | |
2321 register_allocator()->PrepareForConsecutiveAllocations(2); | |
2322 Register value = register_allocator()->NextConsecutiveRegister(); | |
2323 Register done = register_allocator()->NextConsecutiveRegister(); | |
2324 builder() | |
2325 ->MoveRegister(input, value) | |
2326 .LoadTrue() | |
2327 .StoreAccumulatorInRegister(done) | |
2328 .CallRuntime(Runtime::kCreateIterResultObject, value, 2); | |
2329 execution_control()->ReturnAccumulator(); | |
2330 } | |
2331 | |
2332 builder()->Bind(&resume_with_throw); | |
2333 builder() | |
2334 ->LoadAccumulatorWithRegister(input) | |
2335 .Throw(); | |
2336 | |
2337 builder()->Bind(&resume_with_next); | |
2338 builder()->LoadAccumulatorWithRegister(input); | |
2339 execution_result()->SetResultInAccumulator(); | |
2340 } | |
2341 } | |
2235 | 2342 |
2236 void BytecodeGenerator::VisitThrow(Throw* expr) { | 2343 void BytecodeGenerator::VisitThrow(Throw* expr) { |
2237 VisitForAccumulatorValue(expr->exception()); | 2344 VisitForAccumulatorValue(expr->exception()); |
2238 builder()->SetExpressionPosition(expr); | 2345 builder()->SetExpressionPosition(expr); |
2239 builder()->Throw(); | 2346 builder()->Throw(); |
2240 // Throw statments are modeled as expression instead of statments. These are | 2347 // Throw statements are modeled as expressions instead of statements. These |
2241 // converted from assignment statements in Rewriter::ReWrite pass. An | 2348 // are converted from assignment statements in Rewriter::ReWrite pass. An |
rmcilroy
2016/04/15 13:17:46
Thanks!
| |
2242 // assignment statement expects a value in the accumulator. This is a hack to | 2349 // assignment statement expects a value in the accumulator. This is a hack to |
2243 // avoid DCHECK fails assert accumulator has been set. | 2350 // avoid DCHECK fails assert accumulator has been set. |
2244 execution_result()->SetResultInAccumulator(); | 2351 execution_result()->SetResultInAccumulator(); |
2245 } | 2352 } |
2246 | 2353 |
2247 | 2354 |
2248 void BytecodeGenerator::VisitPropertyLoad(Register obj, Property* expr) { | 2355 void BytecodeGenerator::VisitPropertyLoad(Register obj, Property* expr) { |
2249 LhsKind property_kind = Property::GetAssignType(expr); | 2356 LhsKind property_kind = Property::GetAssignType(expr); |
2250 FeedbackVectorSlot slot = expr->PropertyFeedbackSlot(); | 2357 FeedbackVectorSlot slot = expr->PropertyFeedbackSlot(); |
2251 builder()->SetExpressionPosition(expr); | 2358 builder()->SetExpressionPosition(expr); |
(...skipping 907 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
3159 } | 3266 } |
3160 | 3267 |
3161 | 3268 |
3162 int BytecodeGenerator::feedback_index(FeedbackVectorSlot slot) const { | 3269 int BytecodeGenerator::feedback_index(FeedbackVectorSlot slot) const { |
3163 return info()->shared_info()->feedback_vector()->GetIndex(slot); | 3270 return info()->shared_info()->feedback_vector()->GetIndex(slot); |
3164 } | 3271 } |
3165 | 3272 |
3166 } // namespace interpreter | 3273 } // namespace interpreter |
3167 } // namespace internal | 3274 } // namespace internal |
3168 } // namespace v8 | 3275 } // namespace v8 |
OLD | NEW |