OLD | NEW |
1 // Copyright 2006-2009 the V8 project authors. All rights reserved. | 1 // Copyright 2010 the V8 project authors. All rights reserved. |
2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
4 // met: | 4 // met: |
5 // | 5 // |
6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
11 // with the distribution. | 11 // with the distribution. |
(...skipping 620 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
632 } | 632 } |
633 } | 633 } |
634 if (!skip_arguments) { | 634 if (!skip_arguments) { |
635 StoreToSlot(arguments->slot(), NOT_CONST_INIT); | 635 StoreToSlot(arguments->slot(), NOT_CONST_INIT); |
636 if (mode == LAZY_ARGUMENTS_ALLOCATION) done.Bind(); | 636 if (mode == LAZY_ARGUMENTS_ALLOCATION) done.Bind(); |
637 } | 637 } |
638 StoreToSlot(shadow->slot(), NOT_CONST_INIT); | 638 StoreToSlot(shadow->slot(), NOT_CONST_INIT); |
639 return frame_->Pop(); | 639 return frame_->Pop(); |
640 } | 640 } |
641 | 641 |
| 642 //------------------------------------------------------------------------------ |
| 643 // CodeGenerator implementation of variables, lookups, and stores. |
642 | 644 |
643 Reference::Reference(CodeGenerator* cgen, Expression* expression) | 645 Reference::Reference(CodeGenerator* cgen, |
644 : cgen_(cgen), expression_(expression), type_(ILLEGAL) { | 646 Expression* expression, |
| 647 bool persist_after_get) |
| 648 : cgen_(cgen), |
| 649 expression_(expression), |
| 650 type_(ILLEGAL), |
| 651 persist_after_get_(persist_after_get) { |
645 cgen->LoadReference(this); | 652 cgen->LoadReference(this); |
646 } | 653 } |
647 | 654 |
648 | 655 |
649 Reference::~Reference() { | 656 Reference::~Reference() { |
650 cgen_->UnloadReference(this); | 657 ASSERT(is_unloaded() || is_illegal()); |
651 } | 658 } |
652 | 659 |
653 | 660 |
654 void CodeGenerator::LoadReference(Reference* ref) { | 661 void CodeGenerator::LoadReference(Reference* ref) { |
655 // References are loaded from both spilled and unspilled code. Set the | 662 // References are loaded from both spilled and unspilled code. Set the |
656 // state to unspilled to allow that (and explicitly spill after | 663 // state to unspilled to allow that (and explicitly spill after |
657 // construction at the construction sites). | 664 // construction at the construction sites). |
658 bool was_in_spilled_code = in_spilled_code_; | 665 bool was_in_spilled_code = in_spilled_code_; |
659 in_spilled_code_ = false; | 666 in_spilled_code_ = false; |
660 | 667 |
(...skipping 29 matching lines...) Expand all Loading... |
690 } | 697 } |
691 | 698 |
692 in_spilled_code_ = was_in_spilled_code; | 699 in_spilled_code_ = was_in_spilled_code; |
693 } | 700 } |
694 | 701 |
695 | 702 |
696 void CodeGenerator::UnloadReference(Reference* ref) { | 703 void CodeGenerator::UnloadReference(Reference* ref) { |
697 // Pop a reference from the stack while preserving TOS. | 704 // Pop a reference from the stack while preserving TOS. |
698 Comment cmnt(masm_, "[ UnloadReference"); | 705 Comment cmnt(masm_, "[ UnloadReference"); |
699 frame_->Nip(ref->size()); | 706 frame_->Nip(ref->size()); |
| 707 ref->set_unloaded(); |
700 } | 708 } |
701 | 709 |
702 | 710 |
703 // ECMA-262, section 9.2, page 30: ToBoolean(). Pop the top of stack and | 711 // ECMA-262, section 9.2, page 30: ToBoolean(). Pop the top of stack and |
704 // convert it to a boolean in the condition code register or jump to | 712 // convert it to a boolean in the condition code register or jump to |
705 // 'false_target'/'true_target' as appropriate. | 713 // 'false_target'/'true_target' as appropriate. |
706 void CodeGenerator::ToBoolean(ControlDestination* dest) { | 714 void CodeGenerator::ToBoolean(ControlDestination* dest) { |
707 Comment cmnt(masm_, "[ ToBoolean"); | 715 Comment cmnt(masm_, "[ ToBoolean"); |
708 | 716 |
709 // The value to convert should be popped from the frame. | 717 // The value to convert should be popped from the frame. |
(...skipping 1580 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2290 InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP; | 2298 InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP; |
2291 CallFunctionStub call_function(arg_count, in_loop, flags); | 2299 CallFunctionStub call_function(arg_count, in_loop, flags); |
2292 Result answer = frame_->CallStub(&call_function, arg_count + 1); | 2300 Result answer = frame_->CallStub(&call_function, arg_count + 1); |
2293 // Restore context and replace function on the stack with the | 2301 // Restore context and replace function on the stack with the |
2294 // result of the stub invocation. | 2302 // result of the stub invocation. |
2295 frame_->RestoreContextRegister(); | 2303 frame_->RestoreContextRegister(); |
2296 frame_->SetElementAt(0, &answer); | 2304 frame_->SetElementAt(0, &answer); |
2297 } | 2305 } |
2298 | 2306 |
2299 | 2307 |
2300 void CodeGenerator::CallApplyLazy(Property* apply, | 2308 void CodeGenerator::CallApplyLazy(Expression* applicand, |
2301 Expression* receiver, | 2309 Expression* receiver, |
2302 VariableProxy* arguments, | 2310 VariableProxy* arguments, |
2303 int position) { | 2311 int position) { |
| 2312 // An optimized implementation of expressions of the form |
| 2313 // x.apply(y, arguments). |
| 2314 // If the arguments object of the scope has not been allocated, |
| 2315 // and x.apply is Function.prototype.apply, this optimization |
| 2316 // just copies y and the arguments of the current function on the |
| 2317 // stack, as receiver and arguments, and calls x. |
| 2318 // In the implementation comments, we call x the applicand |
| 2319 // and y the receiver. |
2304 ASSERT(ArgumentsMode() == LAZY_ARGUMENTS_ALLOCATION); | 2320 ASSERT(ArgumentsMode() == LAZY_ARGUMENTS_ALLOCATION); |
2305 ASSERT(arguments->IsArguments()); | 2321 ASSERT(arguments->IsArguments()); |
2306 | 2322 |
2307 JumpTarget slow, done; | 2323 // Load applicand.apply onto the stack. This will usually |
2308 | |
2309 // Load the apply function onto the stack. This will usually | |
2310 // give us a megamorphic load site. Not super, but it works. | 2324 // give us a megamorphic load site. Not super, but it works. |
2311 Reference ref(this, apply); | 2325 Load(applicand); |
2312 ref.GetValue(); | 2326 Handle<String> name = Factory::LookupAsciiSymbol("apply"); |
2313 ASSERT(ref.type() == Reference::NAMED); | 2327 frame()->Push(name); |
| 2328 Result answer = frame()->CallLoadIC(RelocInfo::CODE_TARGET); |
| 2329 __ nop(); |
| 2330 frame()->Push(&answer); |
2314 | 2331 |
2315 // Load the receiver and the existing arguments object onto the | 2332 // Load the receiver and the existing arguments object onto the |
2316 // expression stack. Avoid allocating the arguments object here. | 2333 // expression stack. Avoid allocating the arguments object here. |
2317 Load(receiver); | 2334 Load(receiver); |
2318 LoadFromSlot(scope_->arguments()->var()->slot(), NOT_INSIDE_TYPEOF); | 2335 LoadFromSlot(scope_->arguments()->var()->slot(), NOT_INSIDE_TYPEOF); |
2319 | 2336 |
2320 // Emit the source position information after having loaded the | 2337 // Emit the source position information after having loaded the |
2321 // receiver and the arguments. | 2338 // receiver and the arguments. |
2322 CodeForSourcePosition(position); | 2339 CodeForSourcePosition(position); |
| 2340 // Contents of frame at this point: |
| 2341 // Frame[0]: arguments object of the current function or the hole. |
| 2342 // Frame[1]: receiver |
| 2343 // Frame[2]: applicand.apply |
| 2344 // Frame[3]: applicand. |
2323 | 2345 |
2324 // Check if the arguments object has been lazily allocated | 2346 // Check if the arguments object has been lazily allocated |
2325 // already. If so, just use that instead of copying the arguments | 2347 // already. If so, just use that instead of copying the arguments |
2326 // from the stack. This also deals with cases where a local variable | 2348 // from the stack. This also deals with cases where a local variable |
2327 // named 'arguments' has been introduced. | 2349 // named 'arguments' has been introduced. |
2328 frame_->Dup(); | 2350 frame_->Dup(); |
2329 Result probe = frame_->Pop(); | 2351 Result probe = frame_->Pop(); |
2330 bool try_lazy = true; | 2352 { VirtualFrame::SpilledScope spilled_scope; |
2331 if (probe.is_constant()) { | 2353 Label slow, done; |
2332 try_lazy = probe.handle()->IsTheHole(); | 2354 bool try_lazy = true; |
2333 } else { | 2355 if (probe.is_constant()) { |
2334 __ cmp(Operand(probe.reg()), Immediate(Factory::the_hole_value())); | 2356 try_lazy = probe.handle()->IsTheHole(); |
2335 probe.Unuse(); | 2357 } else { |
2336 slow.Branch(not_equal); | 2358 __ cmp(Operand(probe.reg()), Immediate(Factory::the_hole_value())); |
2337 } | 2359 probe.Unuse(); |
| 2360 __ j(not_equal, &slow); |
| 2361 } |
2338 | 2362 |
2339 if (try_lazy) { | 2363 if (try_lazy) { |
2340 JumpTarget build_args; | 2364 Label build_args; |
| 2365 // Get rid of the arguments object probe. |
| 2366 frame_->Drop(); // Can be called on a spilled frame. |
| 2367 // Stack now has 3 elements on it. |
| 2368 // Contents of stack at this point: |
| 2369 // esp[0]: receiver |
| 2370 // esp[1]: applicand.apply |
| 2371 // esp[2]: applicand. |
2341 | 2372 |
2342 // Get rid of the arguments object probe. | 2373 // Check that the receiver really is a JavaScript object. |
2343 frame_->Drop(); | 2374 __ mov(eax, Operand(esp, 0)); |
2344 | 2375 __ test(eax, Immediate(kSmiTagMask)); |
2345 // Before messing with the execution stack, we sync all | 2376 __ j(zero, &build_args); |
2346 // elements. This is bound to happen anyway because we're | |
2347 // about to call a function. | |
2348 frame_->SyncRange(0, frame_->element_count() - 1); | |
2349 | |
2350 // Check that the receiver really is a JavaScript object. | |
2351 { frame_->PushElementAt(0); | |
2352 Result receiver = frame_->Pop(); | |
2353 receiver.ToRegister(); | |
2354 __ test(receiver.reg(), Immediate(kSmiTagMask)); | |
2355 build_args.Branch(zero); | |
2356 Result tmp = allocator_->Allocate(); | |
2357 // We allow all JSObjects including JSFunctions. As long as | 2377 // We allow all JSObjects including JSFunctions. As long as |
2358 // JS_FUNCTION_TYPE is the last instance type and it is right | 2378 // JS_FUNCTION_TYPE is the last instance type and it is right |
2359 // after LAST_JS_OBJECT_TYPE, we do not have to check the upper | 2379 // after LAST_JS_OBJECT_TYPE, we do not have to check the upper |
2360 // bound. | 2380 // bound. |
2361 ASSERT(LAST_TYPE == JS_FUNCTION_TYPE); | 2381 ASSERT(LAST_TYPE == JS_FUNCTION_TYPE); |
2362 ASSERT(JS_FUNCTION_TYPE == LAST_JS_OBJECT_TYPE + 1); | 2382 ASSERT(JS_FUNCTION_TYPE == LAST_JS_OBJECT_TYPE + 1); |
2363 __ CmpObjectType(receiver.reg(), FIRST_JS_OBJECT_TYPE, tmp.reg()); | 2383 __ CmpObjectType(eax, FIRST_JS_OBJECT_TYPE, ecx); |
2364 build_args.Branch(less); | 2384 __ j(below, &build_args); |
| 2385 |
| 2386 // Check that applicand.apply is Function.prototype.apply. |
| 2387 __ mov(eax, Operand(esp, kPointerSize)); |
| 2388 __ test(eax, Immediate(kSmiTagMask)); |
| 2389 __ j(zero, &build_args); |
| 2390 __ CmpObjectType(eax, JS_FUNCTION_TYPE, ecx); |
| 2391 __ j(not_equal, &build_args); |
| 2392 __ mov(ecx, FieldOperand(eax, JSFunction::kSharedFunctionInfoOffset)); |
| 2393 Handle<Code> apply_code(Builtins::builtin(Builtins::FunctionApply)); |
| 2394 __ cmp(FieldOperand(ecx, SharedFunctionInfo::kCodeOffset), |
| 2395 Immediate(apply_code)); |
| 2396 __ j(not_equal, &build_args); |
| 2397 |
| 2398 // Check that applicand is a function. |
| 2399 __ mov(edi, Operand(esp, 2 * kPointerSize)); |
| 2400 __ test(edi, Immediate(kSmiTagMask)); |
| 2401 __ j(zero, &build_args); |
| 2402 __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx); |
| 2403 __ j(not_equal, &build_args); |
| 2404 |
| 2405 // Copy the arguments to this function possibly from the |
| 2406 // adaptor frame below it. |
| 2407 Label invoke, adapted; |
| 2408 __ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset)); |
| 2409 __ mov(ecx, Operand(edx, StandardFrameConstants::kContextOffset)); |
| 2410 __ cmp(Operand(ecx), |
| 2411 Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))); |
| 2412 __ j(equal, &adapted); |
| 2413 |
| 2414 // No arguments adaptor frame. Copy fixed number of arguments. |
| 2415 __ mov(eax, Immediate(scope_->num_parameters())); |
| 2416 for (int i = 0; i < scope_->num_parameters(); i++) { |
| 2417 __ push(frame_->ParameterAt(i)); |
| 2418 } |
| 2419 __ jmp(&invoke); |
| 2420 |
| 2421 // Arguments adaptor frame present. Copy arguments from there, but |
| 2422 // avoid copying too many arguments to avoid stack overflows. |
| 2423 __ bind(&adapted); |
| 2424 static const uint32_t kArgumentsLimit = 1 * KB; |
| 2425 __ mov(eax, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset)); |
| 2426 __ SmiUntag(eax); |
| 2427 __ mov(ecx, Operand(eax)); |
| 2428 __ cmp(eax, kArgumentsLimit); |
| 2429 __ j(above, &build_args); |
| 2430 |
| 2431 // Loop through the arguments pushing them onto the execution |
| 2432 // stack. We don't inform the virtual frame of the push, so we don't |
| 2433 // have to worry about getting rid of the elements from the virtual |
| 2434 // frame. |
| 2435 Label loop; |
| 2436 // ecx is a small non-negative integer, due to the test above. |
| 2437 __ test(ecx, Operand(ecx)); |
| 2438 __ j(zero, &invoke); |
| 2439 __ bind(&loop); |
| 2440 __ push(Operand(edx, ecx, times_pointer_size, 1 * kPointerSize)); |
| 2441 __ dec(ecx); |
| 2442 __ j(not_zero, &loop); |
| 2443 |
| 2444 // Invoke the function. |
| 2445 __ bind(&invoke); |
| 2446 ParameterCount actual(eax); |
| 2447 __ InvokeFunction(edi, actual, CALL_FUNCTION); |
| 2448 // Drop applicand.apply and applicand from the stack, and push |
| 2449 // the result of the function call, but leave the spilled frame |
| 2450 // unchanged, with 3 elements, so it is correct when we compile the |
| 2451 // slow-case code. |
| 2452 __ add(Operand(esp), Immediate(2 * kPointerSize)); |
| 2453 __ push(eax); |
| 2454 // Stack now has 1 element: |
| 2455 // esp[0]: result |
| 2456 __ jmp(&done); |
| 2457 |
| 2458 // Slow-case: Allocate the arguments object since we know it isn't |
| 2459 // there, and fall-through to the slow-case where we call |
| 2460 // applicand.apply. |
| 2461 __ bind(&build_args); |
| 2462 // Stack now has 3 elements, because we have jumped from where: |
| 2463 // esp[0]: receiver |
| 2464 // esp[1]: applicand.apply |
| 2465 // esp[2]: applicand. |
| 2466 |
| 2467 // StoreArgumentsObject requires a correct frame, and may modify it. |
| 2468 Result arguments_object = StoreArgumentsObject(false); |
| 2469 frame_->SpillAll(); |
| 2470 arguments_object.ToRegister(); |
| 2471 frame_->EmitPush(arguments_object.reg()); |
| 2472 arguments_object.Unuse(); |
| 2473 // Stack and frame now have 4 elements. |
| 2474 __ bind(&slow); |
2365 } | 2475 } |
2366 | 2476 |
2367 // Verify that we're invoking Function.prototype.apply. | 2477 // Generic computation of x.apply(y, args) with no special optimization. |
2368 { frame_->PushElementAt(1); | 2478 // Flip applicand.apply and applicand on the stack, so |
2369 Result apply = frame_->Pop(); | 2479 // applicand looks like the receiver of the applicand.apply call. |
2370 apply.ToRegister(); | 2480 // Then process it as a normal function call. |
2371 __ test(apply.reg(), Immediate(kSmiTagMask)); | 2481 __ mov(eax, Operand(esp, 3 * kPointerSize)); |
2372 build_args.Branch(zero); | 2482 __ mov(ebx, Operand(esp, 2 * kPointerSize)); |
2373 Result tmp = allocator_->Allocate(); | 2483 __ mov(Operand(esp, 2 * kPointerSize), eax); |
2374 __ CmpObjectType(apply.reg(), JS_FUNCTION_TYPE, tmp.reg()); | 2484 __ mov(Operand(esp, 3 * kPointerSize), ebx); |
2375 build_args.Branch(not_equal); | |
2376 __ mov(tmp.reg(), | |
2377 FieldOperand(apply.reg(), JSFunction::kSharedFunctionInfoOffset)); | |
2378 Handle<Code> apply_code(Builtins::builtin(Builtins::FunctionApply)); | |
2379 __ cmp(FieldOperand(tmp.reg(), SharedFunctionInfo::kCodeOffset), | |
2380 Immediate(apply_code)); | |
2381 build_args.Branch(not_equal); | |
2382 } | |
2383 | 2485 |
2384 // Get the function receiver from the stack. Check that it | 2486 CallFunctionStub call_function(2, NOT_IN_LOOP, NO_CALL_FUNCTION_FLAGS); |
2385 // really is a function. | 2487 Result res = frame_->CallStub(&call_function, 3); |
2386 __ mov(edi, Operand(esp, 2 * kPointerSize)); | 2488 // The function and its two arguments have been dropped. |
2387 __ test(edi, Immediate(kSmiTagMask)); | 2489 frame_->Drop(1); // Drop the receiver as well. |
2388 build_args.Branch(zero); | 2490 res.ToRegister(); |
2389 __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx); | 2491 frame_->EmitPush(res.reg()); |
2390 build_args.Branch(not_equal); | 2492 // Stack now has 1 element: |
2391 | 2493 // esp[0]: result |
2392 // Copy the arguments to this function possibly from the | 2494 if (try_lazy) __ bind(&done); |
2393 // adaptor frame below it. | 2495 } // End of spilled scope. |
2394 Label invoke, adapted; | 2496 // Restore the context register after a call. |
2395 __ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset)); | |
2396 __ mov(ecx, Operand(edx, StandardFrameConstants::kContextOffset)); | |
2397 __ cmp(Operand(ecx), | |
2398 Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))); | |
2399 __ j(equal, &adapted); | |
2400 | |
2401 // No arguments adaptor frame. Copy fixed number of arguments. | |
2402 __ mov(eax, Immediate(scope_->num_parameters())); | |
2403 for (int i = 0; i < scope_->num_parameters(); i++) { | |
2404 __ push(frame_->ParameterAt(i)); | |
2405 } | |
2406 __ jmp(&invoke); | |
2407 | |
2408 // Arguments adaptor frame present. Copy arguments from there, but | |
2409 // avoid copying too many arguments to avoid stack overflows. | |
2410 __ bind(&adapted); | |
2411 static const uint32_t kArgumentsLimit = 1 * KB; | |
2412 __ mov(eax, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset)); | |
2413 __ SmiUntag(eax); | |
2414 __ mov(ecx, Operand(eax)); | |
2415 __ cmp(eax, kArgumentsLimit); | |
2416 build_args.Branch(above); | |
2417 | |
2418 // Loop through the arguments pushing them onto the execution | |
2419 // stack. We don't inform the virtual frame of the push, so we don't | |
2420 // have to worry about getting rid of the elements from the virtual | |
2421 // frame. | |
2422 Label loop; | |
2423 __ bind(&loop); | |
2424 __ test(ecx, Operand(ecx)); | |
2425 __ j(zero, &invoke); | |
2426 __ push(Operand(edx, ecx, times_4, 1 * kPointerSize)); | |
2427 __ dec(ecx); | |
2428 __ jmp(&loop); | |
2429 | |
2430 // Invoke the function. The virtual frame knows about the receiver | |
2431 // so make sure to forget that explicitly. | |
2432 __ bind(&invoke); | |
2433 ParameterCount actual(eax); | |
2434 __ InvokeFunction(edi, actual, CALL_FUNCTION); | |
2435 frame_->Forget(1); | |
2436 Result result = allocator()->Allocate(eax); | |
2437 frame_->SetElementAt(0, &result); | |
2438 done.Jump(); | |
2439 | |
2440 // Slow-case: Allocate the arguments object since we know it isn't | |
2441 // there, and fall-through to the slow-case where we call | |
2442 // Function.prototype.apply. | |
2443 build_args.Bind(); | |
2444 Result arguments_object = StoreArgumentsObject(false); | |
2445 frame_->Push(&arguments_object); | |
2446 slow.Bind(); | |
2447 } | |
2448 | |
2449 // Flip the apply function and the function to call on the stack, so | |
2450 // the function looks like the receiver of the apply call. This way, | |
2451 // the generic Function.prototype.apply implementation can deal with | |
2452 // the call like it usually does. | |
2453 Result a2 = frame_->Pop(); | |
2454 Result a1 = frame_->Pop(); | |
2455 Result ap = frame_->Pop(); | |
2456 Result fn = frame_->Pop(); | |
2457 frame_->Push(&ap); | |
2458 frame_->Push(&fn); | |
2459 frame_->Push(&a1); | |
2460 frame_->Push(&a2); | |
2461 CallFunctionStub call_function(2, NOT_IN_LOOP, NO_CALL_FUNCTION_FLAGS); | |
2462 Result res = frame_->CallStub(&call_function, 3); | |
2463 frame_->Push(&res); | |
2464 | |
2465 // All done. Restore context register after call. | |
2466 if (try_lazy) done.Bind(); | |
2467 frame_->RestoreContextRegister(); | 2497 frame_->RestoreContextRegister(); |
2468 } | 2498 } |
2469 | 2499 |
2470 | 2500 |
2471 class DeferredStackCheck: public DeferredCode { | 2501 class DeferredStackCheck: public DeferredCode { |
2472 public: | 2502 public: |
2473 DeferredStackCheck() { | 2503 DeferredStackCheck() { |
2474 set_comment("[ DeferredStackCheck"); | 2504 set_comment("[ DeferredStackCheck"); |
2475 } | 2505 } |
2476 | 2506 |
(...skipping 1019 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3496 end_del_check.Bind(); | 3526 end_del_check.Bind(); |
3497 // Store the entry in the 'each' expression and take another spin in the | 3527 // Store the entry in the 'each' expression and take another spin in the |
3498 // loop. edx: i'th entry of the enum cache (or string there of) | 3528 // loop. edx: i'th entry of the enum cache (or string there of) |
3499 frame_->EmitPush(ebx); | 3529 frame_->EmitPush(ebx); |
3500 { Reference each(this, node->each()); | 3530 { Reference each(this, node->each()); |
3501 // Loading a reference may leave the frame in an unspilled state. | 3531 // Loading a reference may leave the frame in an unspilled state. |
3502 frame_->SpillAll(); | 3532 frame_->SpillAll(); |
3503 if (!each.is_illegal()) { | 3533 if (!each.is_illegal()) { |
3504 if (each.size() > 0) { | 3534 if (each.size() > 0) { |
3505 frame_->EmitPush(frame_->ElementAt(each.size())); | 3535 frame_->EmitPush(frame_->ElementAt(each.size())); |
3506 } | 3536 each.SetValue(NOT_CONST_INIT); |
3507 // If the reference was to a slot we rely on the convenient property | 3537 frame_->Drop(2); |
3508 // that it doesn't matter whether a value (eg, ebx pushed above) is | 3538 } else { |
3509 // right on top of or right underneath a zero-sized reference. | 3539 // If the reference was to a slot we rely on the convenient property |
3510 each.SetValue(NOT_CONST_INIT); | 3540 // that it doesn't matter whether a value (eg, ebx pushed above) is |
3511 if (each.size() > 0) { | 3541 // right on top of or right underneath a zero-sized reference. |
3512 // It's safe to pop the value lying on top of the reference before | 3542 each.SetValue(NOT_CONST_INIT); |
3513 // unloading the reference itself (which preserves the top of stack, | |
3514 // ie, now the topmost value of the non-zero sized reference), since | |
3515 // we will discard the top of stack after unloading the reference | |
3516 // anyway. | |
3517 frame_->Drop(); | 3543 frame_->Drop(); |
3518 } | 3544 } |
3519 } | 3545 } |
3520 } | 3546 } |
3521 // Unloading a reference may leave the frame in an unspilled state. | 3547 // Unloading a reference may leave the frame in an unspilled state. |
3522 frame_->SpillAll(); | 3548 frame_->SpillAll(); |
3523 | 3549 |
3524 // Discard the i'th entry pushed above or else the remainder of the | |
3525 // reference, whichever is currently on top of the stack. | |
3526 frame_->Drop(); | |
3527 | |
3528 // Body. | 3550 // Body. |
3529 CheckStack(); // TODO(1222600): ignore if body contains calls. | 3551 CheckStack(); // TODO(1222600): ignore if body contains calls. |
3530 VisitAndSpill(node->body()); | 3552 VisitAndSpill(node->body()); |
3531 | 3553 |
3532 // Next. Reestablish a spilled frame in case we are coming here via | 3554 // Next. Reestablish a spilled frame in case we are coming here via |
3533 // a continue in the body. | 3555 // a continue in the body. |
3534 node->continue_target()->Bind(); | 3556 node->continue_target()->Bind(); |
3535 frame_->SpillAll(); | 3557 frame_->SpillAll(); |
3536 frame_->EmitPop(eax); | 3558 frame_->EmitPop(eax); |
3537 __ add(Operand(eax), Immediate(Smi::FromInt(1))); | 3559 __ add(Operand(eax), Immediate(Smi::FromInt(1))); |
(...skipping 1029 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4567 Comment cmnt(masm_, "[ CatchExtensionObject"); | 4589 Comment cmnt(masm_, "[ CatchExtensionObject"); |
4568 Load(node->key()); | 4590 Load(node->key()); |
4569 Load(node->value()); | 4591 Load(node->value()); |
4570 Result result = | 4592 Result result = |
4571 frame_->CallRuntime(Runtime::kCreateCatchExtensionObject, 2); | 4593 frame_->CallRuntime(Runtime::kCreateCatchExtensionObject, 2); |
4572 frame_->Push(&result); | 4594 frame_->Push(&result); |
4573 } | 4595 } |
4574 | 4596 |
4575 | 4597 |
4576 void CodeGenerator::VisitAssignment(Assignment* node) { | 4598 void CodeGenerator::VisitAssignment(Assignment* node) { |
| 4599 #ifdef DEBUG |
| 4600 int original_height = frame_->height(); |
| 4601 #endif |
4577 Comment cmnt(masm_, "[ Assignment"); | 4602 Comment cmnt(masm_, "[ Assignment"); |
4578 | 4603 |
4579 { Reference target(this, node->target()); | 4604 { Reference target(this, node->target(), node->is_compound()); |
4580 if (target.is_illegal()) { | 4605 if (target.is_illegal()) { |
4581 // Fool the virtual frame into thinking that we left the assignment's | 4606 // Fool the virtual frame into thinking that we left the assignment's |
4582 // value on the frame. | 4607 // value on the frame. |
4583 frame_->Push(Smi::FromInt(0)); | 4608 frame_->Push(Smi::FromInt(0)); |
4584 return; | 4609 return; |
4585 } | 4610 } |
4586 Variable* var = node->target()->AsVariableProxy()->AsVariable(); | 4611 Variable* var = node->target()->AsVariableProxy()->AsVariable(); |
4587 | 4612 |
4588 if (node->starts_initialization_block()) { | 4613 if (node->starts_initialization_block()) { |
4589 ASSERT(target.type() == Reference::NAMED || | 4614 ASSERT(target.type() == Reference::NAMED || |
4590 target.type() == Reference::KEYED); | 4615 target.type() == Reference::KEYED); |
4591 // Change to slow case in the beginning of an initialization | 4616 // Change to slow case in the beginning of an initialization |
4592 // block to avoid the quadratic behavior of repeatedly adding | 4617 // block to avoid the quadratic behavior of repeatedly adding |
4593 // fast properties. | 4618 // fast properties. |
4594 | 4619 |
4595 // The receiver is the argument to the runtime call. It is the | 4620 // The receiver is the argument to the runtime call. It is the |
4596 // first value pushed when the reference was loaded to the | 4621 // first value pushed when the reference was loaded to the |
4597 // frame. | 4622 // frame. |
4598 frame_->PushElementAt(target.size() - 1); | 4623 frame_->PushElementAt(target.size() - 1); |
4599 Result ignored = frame_->CallRuntime(Runtime::kToSlowProperties, 1); | 4624 Result ignored = frame_->CallRuntime(Runtime::kToSlowProperties, 1); |
4600 } | 4625 } |
| 4626 if (node->ends_initialization_block()) { |
| 4627 // Add an extra copy of the receiver to the frame, so that it can be |
| 4628 // converted back to fast case after the assignment. |
| 4629 ASSERT(target.type() == Reference::NAMED || |
| 4630 target.type() == Reference::KEYED); |
| 4631 if (target.type() == Reference::NAMED) { |
| 4632 frame_->Dup(); |
| 4633 // Dup target receiver on stack. |
| 4634 } else { |
| 4635 ASSERT(target.type() == Reference::KEYED); |
| 4636 Result temp = frame_->Pop(); |
| 4637 frame_->Dup(); |
| 4638 frame_->Push(&temp); |
| 4639 } |
| 4640 } |
4601 if (node->op() == Token::ASSIGN || | 4641 if (node->op() == Token::ASSIGN || |
4602 node->op() == Token::INIT_VAR || | 4642 node->op() == Token::INIT_VAR || |
4603 node->op() == Token::INIT_CONST) { | 4643 node->op() == Token::INIT_CONST) { |
4604 Load(node->value()); | 4644 Load(node->value()); |
4605 | 4645 |
4606 } else { | 4646 } else { // Assignment is a compound assignment. |
4607 Literal* literal = node->value()->AsLiteral(); | 4647 Literal* literal = node->value()->AsLiteral(); |
4608 bool overwrite_value = | 4648 bool overwrite_value = |
4609 (node->value()->AsBinaryOperation() != NULL && | 4649 (node->value()->AsBinaryOperation() != NULL && |
4610 node->value()->AsBinaryOperation()->ResultOverwriteAllowed()); | 4650 node->value()->AsBinaryOperation()->ResultOverwriteAllowed()); |
4611 Variable* right_var = node->value()->AsVariableProxy()->AsVariable(); | 4651 Variable* right_var = node->value()->AsVariableProxy()->AsVariable(); |
4612 // There are two cases where the target is not read in the right hand | 4652 // There are two cases where the target is not read in the right hand |
4613 // side, that are easy to test for: the right hand side is a literal, | 4653 // side, that are easy to test for: the right hand side is a literal, |
4614 // or the right hand side is a different variable. TakeValue invalidates | 4654 // or the right hand side is a different variable. TakeValue invalidates |
4615 // the target, with an implicit promise that it will be written to again | 4655 // the target, with an implicit promise that it will be written to again |
4616 // before it is read. | 4656 // before it is read. |
4617 if (literal != NULL || (right_var != NULL && right_var != var)) { | 4657 if (literal != NULL || (right_var != NULL && right_var != var)) { |
4618 target.TakeValue(); | 4658 target.TakeValue(); |
4619 } else { | 4659 } else { |
4620 target.GetValue(); | 4660 target.GetValue(); |
4621 } | 4661 } |
4622 Load(node->value()); | 4662 Load(node->value()); |
4623 GenericBinaryOperation(node->binary_op(), | 4663 GenericBinaryOperation(node->binary_op(), |
4624 node->type(), | 4664 node->type(), |
4625 overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE); | 4665 overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE); |
4626 } | 4666 } |
4627 | 4667 |
4628 if (var != NULL && | 4668 if (var != NULL && |
4629 var->mode() == Variable::CONST && | 4669 var->mode() == Variable::CONST && |
4630 node->op() != Token::INIT_VAR && node->op() != Token::INIT_CONST) { | 4670 node->op() != Token::INIT_VAR && node->op() != Token::INIT_CONST) { |
4631 // Assignment ignored - leave the value on the stack. | 4671 // Assignment ignored - leave the value on the stack. |
| 4672 UnloadReference(&target); |
4632 } else { | 4673 } else { |
4633 CodeForSourcePosition(node->position()); | 4674 CodeForSourcePosition(node->position()); |
4634 if (node->op() == Token::INIT_CONST) { | 4675 if (node->op() == Token::INIT_CONST) { |
4635 // Dynamic constant initializations must use the function context | 4676 // Dynamic constant initializations must use the function context |
4636 // and initialize the actual constant declared. Dynamic variable | 4677 // and initialize the actual constant declared. Dynamic variable |
4637 // initializations are simply assignments and use SetValue. | 4678 // initializations are simply assignments and use SetValue. |
4638 target.SetValue(CONST_INIT); | 4679 target.SetValue(CONST_INIT); |
4639 } else { | 4680 } else { |
4640 target.SetValue(NOT_CONST_INIT); | 4681 target.SetValue(NOT_CONST_INIT); |
4641 } | 4682 } |
4642 if (node->ends_initialization_block()) { | 4683 if (node->ends_initialization_block()) { |
4643 ASSERT(target.type() == Reference::NAMED || | 4684 ASSERT(target.type() == Reference::UNLOADED); |
4644 target.type() == Reference::KEYED); | |
4645 // End of initialization block. Revert to fast case. The | 4685 // End of initialization block. Revert to fast case. The |
4646 // argument to the runtime call is the receiver, which is the | 4686 // argument to the runtime call is the extra copy of the receiver, |
4647 // first value pushed as part of the reference, which is below | 4687 // which is below the value of the assignment. |
4648 // the lhs value. | 4688 // Swap the receiver and the value of the assignment expression. |
4649 frame_->PushElementAt(target.size()); | 4689 Result lhs = frame_->Pop(); |
| 4690 Result receiver = frame_->Pop(); |
| 4691 frame_->Push(&lhs); |
| 4692 frame_->Push(&receiver); |
4650 Result ignored = frame_->CallRuntime(Runtime::kToFastProperties, 1); | 4693 Result ignored = frame_->CallRuntime(Runtime::kToFastProperties, 1); |
4651 } | 4694 } |
4652 } | 4695 } |
4653 } | 4696 } |
| 4697 ASSERT(frame_->height() == original_height + 1); |
4654 } | 4698 } |
4655 | 4699 |
4656 | 4700 |
4657 void CodeGenerator::VisitThrow(Throw* node) { | 4701 void CodeGenerator::VisitThrow(Throw* node) { |
4658 Comment cmnt(masm_, "[ Throw"); | 4702 Comment cmnt(masm_, "[ Throw"); |
4659 Load(node->exception()); | 4703 Load(node->exception()); |
4660 Result result = frame_->CallRuntime(Runtime::kThrow, 1); | 4704 Result result = frame_->CallRuntime(Runtime::kThrow, 1); |
4661 frame_->Push(&result); | 4705 frame_->Push(&result); |
4662 } | 4706 } |
4663 | 4707 |
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4806 | 4850 |
4807 Handle<String> name = Handle<String>::cast(literal->handle()); | 4851 Handle<String> name = Handle<String>::cast(literal->handle()); |
4808 | 4852 |
4809 if (ArgumentsMode() == LAZY_ARGUMENTS_ALLOCATION && | 4853 if (ArgumentsMode() == LAZY_ARGUMENTS_ALLOCATION && |
4810 name->IsEqualTo(CStrVector("apply")) && | 4854 name->IsEqualTo(CStrVector("apply")) && |
4811 args->length() == 2 && | 4855 args->length() == 2 && |
4812 args->at(1)->AsVariableProxy() != NULL && | 4856 args->at(1)->AsVariableProxy() != NULL && |
4813 args->at(1)->AsVariableProxy()->IsArguments()) { | 4857 args->at(1)->AsVariableProxy()->IsArguments()) { |
4814 // Use the optimized Function.prototype.apply that avoids | 4858 // Use the optimized Function.prototype.apply that avoids |
4815 // allocating lazily allocated arguments objects. | 4859 // allocating lazily allocated arguments objects. |
4816 CallApplyLazy(property, | 4860 CallApplyLazy(property->obj(), |
4817 args->at(0), | 4861 args->at(0), |
4818 args->at(1)->AsVariableProxy(), | 4862 args->at(1)->AsVariableProxy(), |
4819 node->position()); | 4863 node->position()); |
4820 | 4864 |
4821 } else { | 4865 } else { |
4822 // Push the receiver onto the frame. | 4866 // Push the receiver onto the frame. |
4823 Load(property->obj()); | 4867 Load(property->obj()); |
4824 | 4868 |
4825 // Load the arguments. | 4869 // Load the arguments. |
4826 int arg_count = args->length(); | 4870 int arg_count = args->length(); |
(...skipping 12 matching lines...) Expand all Loading... |
4839 frame_->RestoreContextRegister(); | 4883 frame_->RestoreContextRegister(); |
4840 frame_->Push(&result); | 4884 frame_->Push(&result); |
4841 } | 4885 } |
4842 | 4886 |
4843 } else { | 4887 } else { |
4844 // ------------------------------------------- | 4888 // ------------------------------------------- |
4845 // JavaScript example: 'array[index](1, 2, 3)' | 4889 // JavaScript example: 'array[index](1, 2, 3)' |
4846 // ------------------------------------------- | 4890 // ------------------------------------------- |
4847 | 4891 |
4848 // Load the function to call from the property through a reference. | 4892 // Load the function to call from the property through a reference. |
4849 Reference ref(this, property); | |
4850 ref.GetValue(); | |
4851 | 4893 |
4852 // Pass receiver to called function. | 4894 // Pass receiver to called function. |
4853 if (property->is_synthetic()) { | 4895 if (property->is_synthetic()) { |
| 4896 Reference ref(this, property); |
| 4897 ref.GetValue(); |
4854 // Use global object as receiver. | 4898 // Use global object as receiver. |
4855 LoadGlobalReceiver(); | 4899 LoadGlobalReceiver(); |
4856 } else { | 4900 } else { |
4857 // The reference's size is non-negative. | 4901 Load(property->obj()); |
4858 frame_->PushElementAt(ref.size()); | 4902 Load(property->key()); |
| 4903 Result function = EmitKeyedLoad(false); |
| 4904 frame_->Drop(); // Key. |
| 4905 Result receiver = frame_->Pop(); |
| 4906 frame_->Push(&function); |
| 4907 frame_->Push(&receiver); |
4859 } | 4908 } |
4860 | 4909 |
4861 // Call the function. | 4910 // Call the function. |
4862 CallWithArguments(args, RECEIVER_MIGHT_BE_VALUE, node->position()); | 4911 CallWithArguments(args, RECEIVER_MIGHT_BE_VALUE, node->position()); |
4863 } | 4912 } |
4864 | 4913 |
4865 } else { | 4914 } else { |
4866 // ---------------------------------- | 4915 // ---------------------------------- |
4867 // JavaScript example: 'foo(1, 2, 3)' // foo is not global | 4916 // JavaScript example: 'foo(1, 2, 3)' // foo is not global |
4868 // ---------------------------------- | 4917 // ---------------------------------- |
(...skipping 890 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
5759 | 5808 |
5760 Variable* var = node->expression()->AsVariableProxy()->AsVariable(); | 5809 Variable* var = node->expression()->AsVariableProxy()->AsVariable(); |
5761 bool is_const = (var != NULL && var->mode() == Variable::CONST); | 5810 bool is_const = (var != NULL && var->mode() == Variable::CONST); |
5762 | 5811 |
5763 // Postfix operations need a stack slot under the reference to hold | 5812 // Postfix operations need a stack slot under the reference to hold |
5764 // the old value while the new value is being stored. This is so that | 5813 // the old value while the new value is being stored. This is so that |
5765 // in the case that storing the new value requires a call, the old | 5814 // in the case that storing the new value requires a call, the old |
5766 // value will be in the frame to be spilled. | 5815 // value will be in the frame to be spilled. |
5767 if (is_postfix) frame_->Push(Smi::FromInt(0)); | 5816 if (is_postfix) frame_->Push(Smi::FromInt(0)); |
5768 | 5817 |
5769 { Reference target(this, node->expression()); | 5818 // A constant reference is not saved to, so a constant reference is not a |
| 5819 // compound assignment reference. |
| 5820 { Reference target(this, node->expression(), !is_const); |
5770 if (target.is_illegal()) { | 5821 if (target.is_illegal()) { |
5771 // Spoof the virtual frame to have the expected height (one higher | 5822 // Spoof the virtual frame to have the expected height (one higher |
5772 // than on entry). | 5823 // than on entry). |
5773 if (!is_postfix) frame_->Push(Smi::FromInt(0)); | 5824 if (!is_postfix) frame_->Push(Smi::FromInt(0)); |
5774 return; | 5825 return; |
5775 } | 5826 } |
5776 target.TakeValue(); | 5827 target.TakeValue(); |
5777 | 5828 |
5778 Result new_value = frame_->Pop(); | 5829 Result new_value = frame_->Pop(); |
5779 new_value.ToRegister(); | 5830 new_value.ToRegister(); |
(...skipping 582 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
6362 // instruction that gets patched and coverage code gets in the way. | 6413 // instruction that gets patched and coverage code gets in the way. |
6363 masm_->test(eax, Immediate(-delta_to_patch_site)); | 6414 masm_->test(eax, Immediate(-delta_to_patch_site)); |
6364 // Restore value (returned from store IC), key and receiver | 6415 // Restore value (returned from store IC), key and receiver |
6365 // registers. | 6416 // registers. |
6366 if (!value_.is(eax)) __ mov(value_, eax); | 6417 if (!value_.is(eax)) __ mov(value_, eax); |
6367 __ pop(key_); | 6418 __ pop(key_); |
6368 __ pop(receiver_); | 6419 __ pop(receiver_); |
6369 } | 6420 } |
6370 | 6421 |
6371 | 6422 |
| 6423 Result CodeGenerator::EmitKeyedLoad(bool is_global) { |
| 6424 Comment cmnt(masm_, "[ Load from keyed Property"); |
| 6425 // Inline array load code if inside of a loop. We do not know |
| 6426 // the receiver map yet, so we initially generate the code with |
| 6427 // a check against an invalid map. In the inline cache code, we |
| 6428 // patch the map check if appropriate. |
| 6429 if (loop_nesting() > 0) { |
| 6430 Comment cmnt(masm_, "[ Inlined load from keyed Property"); |
| 6431 |
| 6432 Result key = frame_->Pop(); |
| 6433 Result receiver = frame_->Pop(); |
| 6434 key.ToRegister(); |
| 6435 receiver.ToRegister(); |
| 6436 |
| 6437 // Use a fresh temporary to load the elements without destroying |
| 6438 // the receiver which is needed for the deferred slow case. |
| 6439 Result elements = allocator()->Allocate(); |
| 6440 ASSERT(elements.is_valid()); |
| 6441 |
| 6442 // Use a fresh temporary for the index and later the loaded |
| 6443 // value. |
| 6444 Result index = allocator()->Allocate(); |
| 6445 ASSERT(index.is_valid()); |
| 6446 |
| 6447 DeferredReferenceGetKeyedValue* deferred = |
| 6448 new DeferredReferenceGetKeyedValue(index.reg(), |
| 6449 receiver.reg(), |
| 6450 key.reg(), |
| 6451 is_global); |
| 6452 |
| 6453 // Check that the receiver is not a smi (only needed if this |
| 6454 // is not a load from the global context) and that it has the |
| 6455 // expected map. |
| 6456 if (!is_global) { |
| 6457 __ test(receiver.reg(), Immediate(kSmiTagMask)); |
| 6458 deferred->Branch(zero); |
| 6459 } |
| 6460 |
| 6461 // Initially, use an invalid map. The map is patched in the IC |
| 6462 // initialization code. |
| 6463 __ bind(deferred->patch_site()); |
| 6464 // Use masm-> here instead of the double underscore macro since extra |
| 6465 // coverage code can interfere with the patching. |
| 6466 masm_->cmp(FieldOperand(receiver.reg(), HeapObject::kMapOffset), |
| 6467 Immediate(Factory::null_value())); |
| 6468 deferred->Branch(not_equal); |
| 6469 |
| 6470 // Check that the key is a smi. |
| 6471 __ test(key.reg(), Immediate(kSmiTagMask)); |
| 6472 deferred->Branch(not_zero); |
| 6473 |
| 6474 // Get the elements array from the receiver and check that it |
| 6475 // is not a dictionary. |
| 6476 __ mov(elements.reg(), |
| 6477 FieldOperand(receiver.reg(), JSObject::kElementsOffset)); |
| 6478 __ cmp(FieldOperand(elements.reg(), HeapObject::kMapOffset), |
| 6479 Immediate(Factory::fixed_array_map())); |
| 6480 deferred->Branch(not_equal); |
| 6481 |
| 6482 // Shift the key to get the actual index value and check that |
| 6483 // it is within bounds. |
| 6484 __ mov(index.reg(), key.reg()); |
| 6485 __ SmiUntag(index.reg()); |
| 6486 __ cmp(index.reg(), |
| 6487 FieldOperand(elements.reg(), FixedArray::kLengthOffset)); |
| 6488 deferred->Branch(above_equal); |
| 6489 |
| 6490 // Load and check that the result is not the hole. We could |
| 6491 // reuse the index or elements register for the value. |
| 6492 // |
| 6493 // TODO(206): Consider whether it makes sense to try some |
| 6494 // heuristic about which register to reuse. For example, if |
| 6495 // one is eax, the we can reuse that one because the value |
| 6496 // coming from the deferred code will be in eax. |
| 6497 Result value = index; |
| 6498 __ mov(value.reg(), Operand(elements.reg(), |
| 6499 index.reg(), |
| 6500 times_4, |
| 6501 FixedArray::kHeaderSize - kHeapObjectTag)); |
| 6502 elements.Unuse(); |
| 6503 index.Unuse(); |
| 6504 __ cmp(Operand(value.reg()), Immediate(Factory::the_hole_value())); |
| 6505 deferred->Branch(equal); |
| 6506 __ IncrementCounter(&Counters::keyed_load_inline, 1); |
| 6507 |
| 6508 deferred->BindExit(); |
| 6509 // Restore the receiver and key to the frame and push the |
| 6510 // result on top of it. |
| 6511 frame_->Push(&receiver); |
| 6512 frame_->Push(&key); |
| 6513 return value; |
| 6514 } else { |
| 6515 Comment cmnt(masm_, "[ Load from keyed Property"); |
| 6516 RelocInfo::Mode mode = is_global |
| 6517 ? RelocInfo::CODE_TARGET_CONTEXT |
| 6518 : RelocInfo::CODE_TARGET; |
| 6519 Result answer = frame_->CallKeyedLoadIC(mode); |
| 6520 // Make sure that we do not have a test instruction after the |
| 6521 // call. A test instruction after the call is used to |
| 6522 // indicate that we have generated an inline version of the |
| 6523 // keyed load. The explicit nop instruction is here because |
| 6524 // the push that follows might be peep-hole optimized away. |
| 6525 __ nop(); |
| 6526 return answer; |
| 6527 } |
| 6528 } |
| 6529 |
| 6530 |
6372 #undef __ | 6531 #undef __ |
6373 #define __ ACCESS_MASM(masm) | 6532 #define __ ACCESS_MASM(masm) |
6374 | 6533 |
6375 | 6534 |
6376 Handle<String> Reference::GetName() { | 6535 Handle<String> Reference::GetName() { |
6377 ASSERT(type_ == NAMED); | 6536 ASSERT(type_ == NAMED); |
6378 Property* property = expression_->AsProperty(); | 6537 Property* property = expression_->AsProperty(); |
6379 if (property == NULL) { | 6538 if (property == NULL) { |
6380 // Global variable reference treated as a named property reference. | 6539 // Global variable reference treated as a named property reference. |
6381 VariableProxy* proxy = expression_->AsVariableProxy(); | 6540 VariableProxy* proxy = expression_->AsVariableProxy(); |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
6474 | 6633 |
6475 __ IncrementCounter(&Counters::named_load_inline, 1); | 6634 __ IncrementCounter(&Counters::named_load_inline, 1); |
6476 deferred->BindExit(); | 6635 deferred->BindExit(); |
6477 cgen_->frame()->Push(&receiver); | 6636 cgen_->frame()->Push(&receiver); |
6478 cgen_->frame()->Push(&value); | 6637 cgen_->frame()->Push(&value); |
6479 } | 6638 } |
6480 break; | 6639 break; |
6481 } | 6640 } |
6482 | 6641 |
6483 case KEYED: { | 6642 case KEYED: { |
6484 Comment cmnt(masm, "[ Load from keyed Property"); | |
6485 Variable* var = expression_->AsVariableProxy()->AsVariable(); | 6643 Variable* var = expression_->AsVariableProxy()->AsVariable(); |
6486 bool is_global = var != NULL; | 6644 bool is_global = var != NULL; |
6487 ASSERT(!is_global || var->is_global()); | 6645 ASSERT(!is_global || var->is_global()); |
6488 | 6646 Result value = cgen_->EmitKeyedLoad(is_global); |
6489 // Inline array load code if inside of a loop. We do not know | 6647 cgen_->frame()->Push(&value); |
6490 // the receiver map yet, so we initially generate the code with | |
6491 // a check against an invalid map. In the inline cache code, we | |
6492 // patch the map check if appropriate. | |
6493 if (cgen_->loop_nesting() > 0) { | |
6494 Comment cmnt(masm, "[ Inlined load from keyed Property"); | |
6495 | |
6496 Result key = cgen_->frame()->Pop(); | |
6497 Result receiver = cgen_->frame()->Pop(); | |
6498 key.ToRegister(); | |
6499 receiver.ToRegister(); | |
6500 | |
6501 // Use a fresh temporary to load the elements without destroying | |
6502 // the receiver which is needed for the deferred slow case. | |
6503 Result elements = cgen_->allocator()->Allocate(); | |
6504 ASSERT(elements.is_valid()); | |
6505 | |
6506 // Use a fresh temporary for the index and later the loaded | |
6507 // value. | |
6508 Result index = cgen_->allocator()->Allocate(); | |
6509 ASSERT(index.is_valid()); | |
6510 | |
6511 DeferredReferenceGetKeyedValue* deferred = | |
6512 new DeferredReferenceGetKeyedValue(index.reg(), | |
6513 receiver.reg(), | |
6514 key.reg(), | |
6515 is_global); | |
6516 | |
6517 // Check that the receiver is not a smi (only needed if this | |
6518 // is not a load from the global context) and that it has the | |
6519 // expected map. | |
6520 if (!is_global) { | |
6521 __ test(receiver.reg(), Immediate(kSmiTagMask)); | |
6522 deferred->Branch(zero); | |
6523 } | |
6524 | |
6525 // Initially, use an invalid map. The map is patched in the IC | |
6526 // initialization code. | |
6527 __ bind(deferred->patch_site()); | |
6528 // Use masm-> here instead of the double underscore macro since extra | |
6529 // coverage code can interfere with the patching. | |
6530 masm->cmp(FieldOperand(receiver.reg(), HeapObject::kMapOffset), | |
6531 Immediate(Factory::null_value())); | |
6532 deferred->Branch(not_equal); | |
6533 | |
6534 // Check that the key is a smi. | |
6535 __ test(key.reg(), Immediate(kSmiTagMask)); | |
6536 deferred->Branch(not_zero); | |
6537 | |
6538 // Get the elements array from the receiver and check that it | |
6539 // is not a dictionary. | |
6540 __ mov(elements.reg(), | |
6541 FieldOperand(receiver.reg(), JSObject::kElementsOffset)); | |
6542 __ cmp(FieldOperand(elements.reg(), HeapObject::kMapOffset), | |
6543 Immediate(Factory::fixed_array_map())); | |
6544 deferred->Branch(not_equal); | |
6545 | |
6546 // Shift the key to get the actual index value and check that | |
6547 // it is within bounds. | |
6548 __ mov(index.reg(), key.reg()); | |
6549 __ SmiUntag(index.reg()); | |
6550 __ cmp(index.reg(), | |
6551 FieldOperand(elements.reg(), FixedArray::kLengthOffset)); | |
6552 deferred->Branch(above_equal); | |
6553 | |
6554 // Load and check that the result is not the hole. We could | |
6555 // reuse the index or elements register for the value. | |
6556 // | |
6557 // TODO(206): Consider whether it makes sense to try some | |
6558 // heuristic about which register to reuse. For example, if | |
6559 // one is eax, the we can reuse that one because the value | |
6560 // coming from the deferred code will be in eax. | |
6561 Result value = index; | |
6562 __ mov(value.reg(), Operand(elements.reg(), | |
6563 index.reg(), | |
6564 times_4, | |
6565 FixedArray::kHeaderSize - kHeapObjectTag)); | |
6566 elements.Unuse(); | |
6567 index.Unuse(); | |
6568 __ cmp(Operand(value.reg()), Immediate(Factory::the_hole_value())); | |
6569 deferred->Branch(equal); | |
6570 __ IncrementCounter(&Counters::keyed_load_inline, 1); | |
6571 | |
6572 deferred->BindExit(); | |
6573 // Restore the receiver and key to the frame and push the | |
6574 // result on top of it. | |
6575 cgen_->frame()->Push(&receiver); | |
6576 cgen_->frame()->Push(&key); | |
6577 cgen_->frame()->Push(&value); | |
6578 | |
6579 } else { | |
6580 Comment cmnt(masm, "[ Load from keyed Property"); | |
6581 RelocInfo::Mode mode = is_global | |
6582 ? RelocInfo::CODE_TARGET_CONTEXT | |
6583 : RelocInfo::CODE_TARGET; | |
6584 Result answer = cgen_->frame()->CallKeyedLoadIC(mode); | |
6585 // Make sure that we do not have a test instruction after the | |
6586 // call. A test instruction after the call is used to | |
6587 // indicate that we have generated an inline version of the | |
6588 // keyed load. The explicit nop instruction is here because | |
6589 // the push that follows might be peep-hole optimized away. | |
6590 __ nop(); | |
6591 cgen_->frame()->Push(&answer); | |
6592 } | |
6593 break; | 6648 break; |
6594 } | 6649 } |
6595 | 6650 |
6596 default: | 6651 default: |
6597 UNREACHABLE(); | 6652 UNREACHABLE(); |
6598 } | 6653 } |
| 6654 |
| 6655 if (!persist_after_get_) { |
| 6656 cgen_->UnloadReference(this); |
| 6657 } |
6599 } | 6658 } |
6600 | 6659 |
6601 | 6660 |
6602 void Reference::TakeValue() { | 6661 void Reference::TakeValue() { |
6603 // For non-constant frame-allocated slots, we invalidate the value in the | 6662 // For non-constant frame-allocated slots, we invalidate the value in the |
6604 // slot. For all others, we fall back on GetValue. | 6663 // slot. For all others, we fall back on GetValue. |
6605 ASSERT(!cgen_->in_spilled_code()); | 6664 ASSERT(!cgen_->in_spilled_code()); |
6606 ASSERT(!is_illegal()); | 6665 ASSERT(!is_illegal()); |
6607 if (type_ != SLOT) { | 6666 if (type_ != SLOT) { |
6608 GetValue(); | 6667 GetValue(); |
(...skipping 13 matching lines...) Expand all Loading... |
6622 // Only non-constant, frame-allocated parameters and locals can | 6681 // Only non-constant, frame-allocated parameters and locals can |
6623 // reach here. Be careful not to use the optimizations for arguments | 6682 // reach here. Be careful not to use the optimizations for arguments |
6624 // object access since it may not have been initialized yet. | 6683 // object access since it may not have been initialized yet. |
6625 ASSERT(!slot->is_arguments()); | 6684 ASSERT(!slot->is_arguments()); |
6626 if (slot->type() == Slot::PARAMETER) { | 6685 if (slot->type() == Slot::PARAMETER) { |
6627 cgen_->frame()->TakeParameterAt(slot->index()); | 6686 cgen_->frame()->TakeParameterAt(slot->index()); |
6628 } else { | 6687 } else { |
6629 ASSERT(slot->type() == Slot::LOCAL); | 6688 ASSERT(slot->type() == Slot::LOCAL); |
6630 cgen_->frame()->TakeLocalAt(slot->index()); | 6689 cgen_->frame()->TakeLocalAt(slot->index()); |
6631 } | 6690 } |
| 6691 |
| 6692 ASSERT(persist_after_get_); |
| 6693 // Do not unload the reference, because it is used in SetValue. |
6632 } | 6694 } |
6633 | 6695 |
6634 | 6696 |
6635 void Reference::SetValue(InitState init_state) { | 6697 void Reference::SetValue(InitState init_state) { |
6636 ASSERT(cgen_->HasValidEntryRegisters()); | 6698 ASSERT(cgen_->HasValidEntryRegisters()); |
6637 ASSERT(!is_illegal()); | 6699 ASSERT(!is_illegal()); |
6638 MacroAssembler* masm = cgen_->masm(); | 6700 MacroAssembler* masm = cgen_->masm(); |
6639 switch (type_) { | 6701 switch (type_) { |
6640 case SLOT: { | 6702 case SLOT: { |
6641 Comment cmnt(masm, "[ Store to Slot"); | 6703 Comment cmnt(masm, "[ Store to Slot"); |
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
6751 // keyed store. | 6813 // keyed store. |
6752 __ nop(); | 6814 __ nop(); |
6753 cgen_->frame()->Push(&answer); | 6815 cgen_->frame()->Push(&answer); |
6754 } | 6816 } |
6755 break; | 6817 break; |
6756 } | 6818 } |
6757 | 6819 |
6758 default: | 6820 default: |
6759 UNREACHABLE(); | 6821 UNREACHABLE(); |
6760 } | 6822 } |
| 6823 cgen_->UnloadReference(this); |
6761 } | 6824 } |
6762 | 6825 |
6763 | 6826 |
6764 void FastNewClosureStub::Generate(MacroAssembler* masm) { | 6827 void FastNewClosureStub::Generate(MacroAssembler* masm) { |
6765 // Clone the boilerplate in new space. Set the context to the | 6828 // Clone the boilerplate in new space. Set the context to the |
6766 // current context in esi. | 6829 // current context in esi. |
6767 Label gc; | 6830 Label gc; |
6768 __ AllocateInNewSpace(JSFunction::kSize, eax, ebx, ecx, &gc, TAG_OBJECT); | 6831 __ AllocateInNewSpace(JSFunction::kSize, eax, ebx, ecx, &gc, TAG_OBJECT); |
6769 | 6832 |
6770 // Get the boilerplate function from the stack. | 6833 // Get the boilerplate function from the stack. |
(...skipping 3256 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
10027 | 10090 |
10028 // Call the runtime; it returns -1 (less), 0 (equal), or 1 (greater) | 10091 // Call the runtime; it returns -1 (less), 0 (equal), or 1 (greater) |
10029 // tagged as a small integer. | 10092 // tagged as a small integer. |
10030 __ bind(&runtime); | 10093 __ bind(&runtime); |
10031 __ TailCallRuntime(ExternalReference(Runtime::kStringCompare), 2, 1); | 10094 __ TailCallRuntime(ExternalReference(Runtime::kStringCompare), 2, 1); |
10032 } | 10095 } |
10033 | 10096 |
10034 #undef __ | 10097 #undef __ |
10035 | 10098 |
10036 } } // namespace v8::internal | 10099 } } // namespace v8::internal |
OLD | NEW |