Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(5)

Side by Side Diff: src/arm/stub-cache-arm.cc

Issue 8139027: Version 3.6.5 (Closed) Base URL: http://v8.googlecode.com/svn/trunk/
Patch Set: '' Created 9 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « src/arm/simulator-arm.cc ('k') | src/array.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2011 the V8 project authors. All rights reserved. 1 // Copyright 2011 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
(...skipping 413 matching lines...) Expand 10 before | Expand all | Expand 10 after
424 if (index < 0) { 424 if (index < 0) {
425 // Set the property straight into the object. 425 // Set the property straight into the object.
426 int offset = object->map()->instance_size() + (index * kPointerSize); 426 int offset = object->map()->instance_size() + (index * kPointerSize);
427 __ str(r0, FieldMemOperand(receiver_reg, offset)); 427 __ str(r0, FieldMemOperand(receiver_reg, offset));
428 428
429 // Skip updating write barrier if storing a smi. 429 // Skip updating write barrier if storing a smi.
430 __ JumpIfSmi(r0, &exit); 430 __ JumpIfSmi(r0, &exit);
431 431
432 // Update the write barrier for the array address. 432 // Update the write barrier for the array address.
433 // Pass the now unused name_reg as a scratch register. 433 // Pass the now unused name_reg as a scratch register.
434 __ RecordWrite(receiver_reg, Operand(offset), name_reg, scratch); 434 __ mov(name_reg, r0);
435 __ RecordWriteField(receiver_reg,
436 offset,
437 name_reg,
438 scratch,
439 kLRHasNotBeenSaved,
440 kDontSaveFPRegs);
435 } else { 441 } else {
436 // Write to the properties array. 442 // Write to the properties array.
437 int offset = index * kPointerSize + FixedArray::kHeaderSize; 443 int offset = index * kPointerSize + FixedArray::kHeaderSize;
438 // Get the properties array 444 // Get the properties array
439 __ ldr(scratch, FieldMemOperand(receiver_reg, JSObject::kPropertiesOffset)); 445 __ ldr(scratch, FieldMemOperand(receiver_reg, JSObject::kPropertiesOffset));
440 __ str(r0, FieldMemOperand(scratch, offset)); 446 __ str(r0, FieldMemOperand(scratch, offset));
441 447
442 // Skip updating write barrier if storing a smi. 448 // Skip updating write barrier if storing a smi.
443 __ JumpIfSmi(r0, &exit); 449 __ JumpIfSmi(r0, &exit);
444 450
445 // Update the write barrier for the array address. 451 // Update the write barrier for the array address.
446 // Ok to clobber receiver_reg and name_reg, since we return. 452 // Ok to clobber receiver_reg and name_reg, since we return.
447 __ RecordWrite(scratch, Operand(offset), name_reg, receiver_reg); 453 __ mov(name_reg, r0);
454 __ RecordWriteField(scratch,
455 offset,
456 name_reg,
457 receiver_reg,
458 kLRHasNotBeenSaved,
459 kDontSaveFPRegs);
448 } 460 }
449 461
450 // Return the value (register r0). 462 // Return the value (register r0).
451 __ bind(&exit); 463 __ bind(&exit);
452 __ Ret(); 464 __ Ret();
453 } 465 }
454 466
455 467
456 void StubCompiler::GenerateLoadMiss(MacroAssembler* masm, Code::Kind kind) { 468 void StubCompiler::GenerateLoadMiss(MacroAssembler* masm, Code::Kind kind) {
457 ASSERT(kind == Code::LOAD_IC || kind == Code::KEYED_LOAD_IC); 469 ASSERT(kind == Code::LOAD_IC || kind == Code::KEYED_LOAD_IC);
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after
546 } 558 }
547 } 559 }
548 560
549 561
550 // Undoes the effects of ReserveSpaceForFastApiCall. 562 // Undoes the effects of ReserveSpaceForFastApiCall.
551 static void FreeSpaceForFastApiCall(MacroAssembler* masm) { 563 static void FreeSpaceForFastApiCall(MacroAssembler* masm) {
552 __ Drop(kFastApiCallArguments); 564 __ Drop(kFastApiCallArguments);
553 } 565 }
554 566
555 567
556 static MaybeObject* GenerateFastApiDirectCall(MacroAssembler* masm, 568 static MaybeObject* GenerateFastApiDirectCall(
557 const CallOptimization& optimization, 569 MacroAssembler* masm,
558 int argc) { 570 const CallOptimization& optimization,
571 int argc) {
559 // ----------- S t a t e ------------- 572 // ----------- S t a t e -------------
560 // -- sp[0] : holder (set by CheckPrototypes) 573 // -- sp[0] : holder (set by CheckPrototypes)
561 // -- sp[4] : callee js function 574 // -- sp[4] : callee js function
562 // -- sp[8] : call data 575 // -- sp[8] : call data
563 // -- sp[12] : last js argument 576 // -- sp[12] : last js argument
564 // -- ... 577 // -- ...
565 // -- sp[(argc + 3) * 4] : first js argument 578 // -- sp[(argc + 3) * 4] : first js argument
566 // -- sp[(argc + 4) * 4] : receiver 579 // -- sp[(argc + 4) * 4] : receiver
567 // ----------------------------------- 580 // -----------------------------------
568 // Get the function and setup the context. 581 // Get the function and setup the context.
(...skipping 15 matching lines...) Expand all
584 597
585 // r2 points to call data as expected by Arguments 598 // r2 points to call data as expected by Arguments
586 // (refer to layout above). 599 // (refer to layout above).
587 __ add(r2, sp, Operand(2 * kPointerSize)); 600 __ add(r2, sp, Operand(2 * kPointerSize));
588 601
589 Object* callback = optimization.api_call_info()->callback(); 602 Object* callback = optimization.api_call_info()->callback();
590 Address api_function_address = v8::ToCData<Address>(callback); 603 Address api_function_address = v8::ToCData<Address>(callback);
591 ApiFunction fun(api_function_address); 604 ApiFunction fun(api_function_address);
592 605
593 const int kApiStackSpace = 4; 606 const int kApiStackSpace = 4;
607
608 FrameScope frame_scope(masm, StackFrame::MANUAL);
594 __ EnterExitFrame(false, kApiStackSpace); 609 __ EnterExitFrame(false, kApiStackSpace);
595 610
596 // r0 = v8::Arguments& 611 // r0 = v8::Arguments&
597 // Arguments is after the return address. 612 // Arguments is after the return address.
598 __ add(r0, sp, Operand(1 * kPointerSize)); 613 __ add(r0, sp, Operand(1 * kPointerSize));
599 // v8::Arguments::implicit_args = data 614 // v8::Arguments::implicit_args = data
600 __ str(r2, MemOperand(r0, 0 * kPointerSize)); 615 __ str(r2, MemOperand(r0, 0 * kPointerSize));
601 // v8::Arguments::values = last argument 616 // v8::Arguments::values = last argument
602 __ add(ip, r2, Operand(argc * kPointerSize)); 617 __ add(ip, r2, Operand(argc * kPointerSize));
603 __ str(ip, MemOperand(r0, 1 * kPointerSize)); 618 __ str(ip, MemOperand(r0, 1 * kPointerSize));
604 // v8::Arguments::length_ = argc 619 // v8::Arguments::length_ = argc
605 __ mov(ip, Operand(argc)); 620 __ mov(ip, Operand(argc));
606 __ str(ip, MemOperand(r0, 2 * kPointerSize)); 621 __ str(ip, MemOperand(r0, 2 * kPointerSize));
607 // v8::Arguments::is_construct_call = 0 622 // v8::Arguments::is_construct_call = 0
608 __ mov(ip, Operand(0)); 623 __ mov(ip, Operand(0));
609 __ str(ip, MemOperand(r0, 3 * kPointerSize)); 624 __ str(ip, MemOperand(r0, 3 * kPointerSize));
610 625
611 // Emitting a stub call may try to allocate (if the code is not 626 // Emitting a stub call may try to allocate (if the code is not
612 // already generated). Do not allow the assembler to perform a 627 // already generated). Do not allow the assembler to perform a
613 // garbage collection but instead return the allocation failure 628 // garbage collection but instead return the allocation failure
614 // object. 629 // object.
615 const int kStackUnwindSpace = argc + kFastApiCallArguments + 1; 630 const int kStackUnwindSpace = argc + kFastApiCallArguments + 1;
616 ExternalReference ref = ExternalReference(&fun, 631 ExternalReference ref = ExternalReference(&fun,
617 ExternalReference::DIRECT_API_CALL, 632 ExternalReference::DIRECT_API_CALL,
618 masm->isolate()); 633 masm->isolate());
634 AllowExternalCallThatCantCauseGC scope(masm);
619 return masm->TryCallApiFunctionAndReturn(ref, kStackUnwindSpace); 635 return masm->TryCallApiFunctionAndReturn(ref, kStackUnwindSpace);
620 } 636 }
621 637
638
622 class CallInterceptorCompiler BASE_EMBEDDED { 639 class CallInterceptorCompiler BASE_EMBEDDED {
623 public: 640 public:
624 CallInterceptorCompiler(StubCompiler* stub_compiler, 641 CallInterceptorCompiler(StubCompiler* stub_compiler,
625 const ParameterCount& arguments, 642 const ParameterCount& arguments,
626 Register name, 643 Register name,
627 Code::ExtraICState extra_ic_state) 644 Code::ExtraICState extra_ic_state)
628 : stub_compiler_(stub_compiler), 645 : stub_compiler_(stub_compiler),
629 arguments_(arguments), 646 arguments_(arguments),
630 name_(name), 647 name_(name),
631 extra_ic_state_(extra_ic_state) {} 648 extra_ic_state_(extra_ic_state) {}
(...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after
787 Register scratch3, 804 Register scratch3,
788 String* name, 805 String* name,
789 JSObject* interceptor_holder, 806 JSObject* interceptor_holder,
790 Label* miss_label) { 807 Label* miss_label) {
791 Register holder = 808 Register holder =
792 stub_compiler_->CheckPrototypes(object, receiver, interceptor_holder, 809 stub_compiler_->CheckPrototypes(object, receiver, interceptor_holder,
793 scratch1, scratch2, scratch3, name, 810 scratch1, scratch2, scratch3, name,
794 miss_label); 811 miss_label);
795 812
796 // Call a runtime function to load the interceptor property. 813 // Call a runtime function to load the interceptor property.
797 __ EnterInternalFrame(); 814 FrameScope scope(masm, StackFrame::INTERNAL);
798 // Save the name_ register across the call. 815 // Save the name_ register across the call.
799 __ push(name_); 816 __ push(name_);
800 817
801 PushInterceptorArguments(masm, 818 PushInterceptorArguments(masm,
802 receiver, 819 receiver,
803 holder, 820 holder,
804 name_, 821 name_,
805 interceptor_holder); 822 interceptor_holder);
806 823
807 __ CallExternalReference( 824 __ CallExternalReference(
808 ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorForCall), 825 ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorForCall),
809 masm->isolate()), 826 masm->isolate()),
810 5); 827 5);
811 828
812 // Restore the name_ register. 829 // Restore the name_ register.
813 __ pop(name_); 830 __ pop(name_);
814 __ LeaveInternalFrame(); 831
832 // Leave the internal frame.
815 } 833 }
816 834
817 void LoadWithInterceptor(MacroAssembler* masm, 835 void LoadWithInterceptor(MacroAssembler* masm,
818 Register receiver, 836 Register receiver,
819 Register holder, 837 Register holder,
820 JSObject* holder_obj, 838 JSObject* holder_obj,
821 Register scratch, 839 Register scratch,
822 Label* interceptor_succeeded) { 840 Label* interceptor_succeeded) {
823 __ EnterInternalFrame(); 841 {
824 __ Push(holder, name_); 842 FrameScope scope(masm, StackFrame::INTERNAL);
843 __ Push(holder, name_);
825 844
826 CompileCallLoadPropertyWithInterceptor(masm, 845 CompileCallLoadPropertyWithInterceptor(masm,
827 receiver, 846 receiver,
828 holder, 847 holder,
829 name_, 848 name_,
830 holder_obj); 849 holder_obj);
831 850
832 __ pop(name_); // Restore the name. 851 __ pop(name_); // Restore the name.
833 __ pop(receiver); // Restore the holder. 852 __ pop(receiver); // Restore the holder.
834 __ LeaveInternalFrame(); 853 }
835 854
836 // If interceptor returns no-result sentinel, call the constant function. 855 // If interceptor returns no-result sentinel, call the constant function.
837 __ LoadRoot(scratch, Heap::kNoInterceptorResultSentinelRootIndex); 856 __ LoadRoot(scratch, Heap::kNoInterceptorResultSentinelRootIndex);
838 __ cmp(r0, scratch); 857 __ cmp(r0, scratch);
839 __ b(ne, interceptor_succeeded); 858 __ b(ne, interceptor_succeeded);
840 } 859 }
841 860
842 StubCompiler* stub_compiler_; 861 StubCompiler* stub_compiler_;
843 const ParameterCount& arguments_; 862 const ParameterCount& arguments_;
844 Register name_; 863 Register name_;
(...skipping 376 matching lines...) Expand 10 before | Expand all | Expand 10 after
1221 } else { 1240 } else {
1222 __ Move(scratch3, Handle<Object>(callback_handle->data())); 1241 __ Move(scratch3, Handle<Object>(callback_handle->data()));
1223 } 1242 }
1224 __ Push(reg, scratch3, name_reg); 1243 __ Push(reg, scratch3, name_reg);
1225 __ mov(r0, sp); // r0 = Handle<String> 1244 __ mov(r0, sp); // r0 = Handle<String>
1226 1245
1227 Address getter_address = v8::ToCData<Address>(callback->getter()); 1246 Address getter_address = v8::ToCData<Address>(callback->getter());
1228 ApiFunction fun(getter_address); 1247 ApiFunction fun(getter_address);
1229 1248
1230 const int kApiStackSpace = 1; 1249 const int kApiStackSpace = 1;
1250
1251 FrameScope frame_scope(masm(), StackFrame::MANUAL);
1231 __ EnterExitFrame(false, kApiStackSpace); 1252 __ EnterExitFrame(false, kApiStackSpace);
1253
1232 // Create AccessorInfo instance on the stack above the exit frame with 1254 // Create AccessorInfo instance on the stack above the exit frame with
1233 // scratch2 (internal::Object **args_) as the data. 1255 // scratch2 (internal::Object **args_) as the data.
1234 __ str(scratch2, MemOperand(sp, 1 * kPointerSize)); 1256 __ str(scratch2, MemOperand(sp, 1 * kPointerSize));
1235 __ add(r1, sp, Operand(1 * kPointerSize)); // r1 = AccessorInfo& 1257 __ add(r1, sp, Operand(1 * kPointerSize)); // r1 = AccessorInfo&
1236 1258
1237 // Emitting a stub call may try to allocate (if the code is not 1259 // Emitting a stub call may try to allocate (if the code is not
1238 // already generated). Do not allow the assembler to perform a 1260 // already generated). Do not allow the assembler to perform a
1239 // garbage collection but instead return the allocation failure 1261 // garbage collection but instead return the allocation failure
1240 // object. 1262 // object.
1241 const int kStackUnwindSpace = 4; 1263 const int kStackUnwindSpace = 4;
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
1281 // Compile the interceptor call, followed by inline code to load the 1303 // Compile the interceptor call, followed by inline code to load the
1282 // property from further up the prototype chain if the call fails. 1304 // property from further up the prototype chain if the call fails.
1283 // Check that the maps haven't changed. 1305 // Check that the maps haven't changed.
1284 Register holder_reg = CheckPrototypes(object, receiver, interceptor_holder, 1306 Register holder_reg = CheckPrototypes(object, receiver, interceptor_holder,
1285 scratch1, scratch2, scratch3, 1307 scratch1, scratch2, scratch3,
1286 name, miss); 1308 name, miss);
1287 ASSERT(holder_reg.is(receiver) || holder_reg.is(scratch1)); 1309 ASSERT(holder_reg.is(receiver) || holder_reg.is(scratch1));
1288 1310
1289 // Save necessary data before invoking an interceptor. 1311 // Save necessary data before invoking an interceptor.
1290 // Requires a frame to make GC aware of pushed pointers. 1312 // Requires a frame to make GC aware of pushed pointers.
1291 __ EnterInternalFrame(); 1313 {
1314 FrameScope frame_scope(masm(), StackFrame::INTERNAL);
1292 1315
1293 if (lookup->type() == CALLBACKS && !receiver.is(holder_reg)) { 1316 if (lookup->type() == CALLBACKS && !receiver.is(holder_reg)) {
1294 // CALLBACKS case needs a receiver to be passed into C++ callback. 1317 // CALLBACKS case needs a receiver to be passed into C++ callback.
1295 __ Push(receiver, holder_reg, name_reg); 1318 __ Push(receiver, holder_reg, name_reg);
1296 } else { 1319 } else {
1297 __ Push(holder_reg, name_reg); 1320 __ Push(holder_reg, name_reg);
1321 }
1322
1323 // Invoke an interceptor. Note: map checks from receiver to
1324 // interceptor's holder has been compiled before (see a caller
1325 // of this method.)
1326 CompileCallLoadPropertyWithInterceptor(masm(),
1327 receiver,
1328 holder_reg,
1329 name_reg,
1330 interceptor_holder);
1331
1332 // Check if interceptor provided a value for property. If it's
1333 // the case, return immediately.
1334 Label interceptor_failed;
1335 __ LoadRoot(scratch1, Heap::kNoInterceptorResultSentinelRootIndex);
1336 __ cmp(r0, scratch1);
1337 __ b(eq, &interceptor_failed);
1338 frame_scope.GenerateLeaveFrame();
1339 __ Ret();
1340
1341 __ bind(&interceptor_failed);
1342 __ pop(name_reg);
1343 __ pop(holder_reg);
1344 if (lookup->type() == CALLBACKS && !receiver.is(holder_reg)) {
1345 __ pop(receiver);
1346 }
1347
1348 // Leave the internal frame.
1298 } 1349 }
1299 1350
1300 // Invoke an interceptor. Note: map checks from receiver to
1301 // interceptor's holder has been compiled before (see a caller
1302 // of this method.)
1303 CompileCallLoadPropertyWithInterceptor(masm(),
1304 receiver,
1305 holder_reg,
1306 name_reg,
1307 interceptor_holder);
1308
1309 // Check if interceptor provided a value for property. If it's
1310 // the case, return immediately.
1311 Label interceptor_failed;
1312 __ LoadRoot(scratch1, Heap::kNoInterceptorResultSentinelRootIndex);
1313 __ cmp(r0, scratch1);
1314 __ b(eq, &interceptor_failed);
1315 __ LeaveInternalFrame();
1316 __ Ret();
1317
1318 __ bind(&interceptor_failed);
1319 __ pop(name_reg);
1320 __ pop(holder_reg);
1321 if (lookup->type() == CALLBACKS && !receiver.is(holder_reg)) {
1322 __ pop(receiver);
1323 }
1324
1325 __ LeaveInternalFrame();
1326
1327 // Check that the maps from interceptor's holder to lookup's holder 1351 // Check that the maps from interceptor's holder to lookup's holder
1328 // haven't changed. And load lookup's holder into |holder| register. 1352 // haven't changed. And load lookup's holder into |holder| register.
1329 if (interceptor_holder != lookup->holder()) { 1353 if (interceptor_holder != lookup->holder()) {
1330 holder_reg = CheckPrototypes(interceptor_holder, 1354 holder_reg = CheckPrototypes(interceptor_holder,
1331 holder_reg, 1355 holder_reg,
1332 lookup->holder(), 1356 lookup->holder(),
1333 scratch1, 1357 scratch1,
1334 scratch2, 1358 scratch2,
1335 scratch3, 1359 scratch3,
1336 name, 1360 name,
(...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after
1549 __ ldr(elements, FieldMemOperand(receiver, JSArray::kElementsOffset)); 1573 __ ldr(elements, FieldMemOperand(receiver, JSArray::kElementsOffset));
1550 1574
1551 // Check that the elements are in fast mode and writable. 1575 // Check that the elements are in fast mode and writable.
1552 __ CheckMap(elements, 1576 __ CheckMap(elements,
1553 r0, 1577 r0,
1554 Heap::kFixedArrayMapRootIndex, 1578 Heap::kFixedArrayMapRootIndex,
1555 &call_builtin, 1579 &call_builtin,
1556 DONT_DO_SMI_CHECK); 1580 DONT_DO_SMI_CHECK);
1557 1581
1558 if (argc == 1) { // Otherwise fall through to call the builtin. 1582 if (argc == 1) { // Otherwise fall through to call the builtin.
1559 Label exit, with_write_barrier, attempt_to_grow_elements; 1583 Label attempt_to_grow_elements;
1560 1584
1561 // Get the array's length into r0 and calculate new length. 1585 // Get the array's length into r0 and calculate new length.
1562 __ ldr(r0, FieldMemOperand(receiver, JSArray::kLengthOffset)); 1586 __ ldr(r0, FieldMemOperand(receiver, JSArray::kLengthOffset));
1563 STATIC_ASSERT(kSmiTagSize == 1); 1587 STATIC_ASSERT(kSmiTagSize == 1);
1564 STATIC_ASSERT(kSmiTag == 0); 1588 STATIC_ASSERT(kSmiTag == 0);
1565 __ add(r0, r0, Operand(Smi::FromInt(argc))); 1589 __ add(r0, r0, Operand(Smi::FromInt(argc)));
1566 1590
1567 // Get the element's length. 1591 // Get the element's length.
1568 __ ldr(r4, FieldMemOperand(elements, FixedArray::kLengthOffset)); 1592 __ ldr(r4, FieldMemOperand(elements, FixedArray::kLengthOffset));
1569 1593
1570 // Check if we could survive without allocation. 1594 // Check if we could survive without allocation.
1571 __ cmp(r0, r4); 1595 __ cmp(r0, r4);
1572 __ b(gt, &attempt_to_grow_elements); 1596 __ b(gt, &attempt_to_grow_elements);
1573 1597
1598 // Check if value is a smi.
1599 Label with_write_barrier;
1600 __ ldr(r4, MemOperand(sp, (argc - 1) * kPointerSize));
1601 __ JumpIfNotSmi(r4, &with_write_barrier);
1602
1574 // Save new length. 1603 // Save new length.
1575 __ str(r0, FieldMemOperand(receiver, JSArray::kLengthOffset)); 1604 __ str(r0, FieldMemOperand(receiver, JSArray::kLengthOffset));
1576 1605
1577 // Push the element. 1606 // Push the element.
1578 __ ldr(r4, MemOperand(sp, (argc - 1) * kPointerSize));
1579 // We may need a register containing the address end_elements below, 1607 // We may need a register containing the address end_elements below,
1580 // so write back the value in end_elements. 1608 // so write back the value in end_elements.
1581 __ add(end_elements, elements, 1609 __ add(end_elements, elements,
1582 Operand(r0, LSL, kPointerSizeLog2 - kSmiTagSize)); 1610 Operand(r0, LSL, kPointerSizeLog2 - kSmiTagSize));
1583 const int kEndElementsOffset = 1611 const int kEndElementsOffset =
1584 FixedArray::kHeaderSize - kHeapObjectTag - argc * kPointerSize; 1612 FixedArray::kHeaderSize - kHeapObjectTag - argc * kPointerSize;
1585 __ str(r4, MemOperand(end_elements, kEndElementsOffset, PreIndex)); 1613 __ str(r4, MemOperand(end_elements, kEndElementsOffset, PreIndex));
1586 1614
1587 // Check for a smi. 1615 // Check for a smi.
1588 __ JumpIfNotSmi(r4, &with_write_barrier);
1589 __ bind(&exit);
1590 __ Drop(argc + 1); 1616 __ Drop(argc + 1);
1591 __ Ret(); 1617 __ Ret();
1592 1618
1593 __ bind(&with_write_barrier); 1619 __ bind(&with_write_barrier);
1594 __ InNewSpace(elements, r4, eq, &exit); 1620
1595 __ RecordWriteHelper(elements, end_elements, r4); 1621 if (FLAG_smi_only_arrays) {
1622 __ ldr(r6, FieldMemOperand(receiver, HeapObject::kMapOffset));
1623 __ CheckFastSmiOnlyElements(r6, r6, &call_builtin);
1624 }
1625
1626 // Save new length.
1627 __ str(r0, FieldMemOperand(receiver, JSArray::kLengthOffset));
1628
1629 // Push the element.
1630 // We may need a register containing the address end_elements below,
1631 // so write back the value in end_elements.
1632 __ add(end_elements, elements,
1633 Operand(r0, LSL, kPointerSizeLog2 - kSmiTagSize));
1634 __ str(r4, MemOperand(end_elements, kEndElementsOffset, PreIndex));
1635
1636 __ RecordWrite(elements,
1637 end_elements,
1638 r4,
1639 kLRHasNotBeenSaved,
1640 kDontSaveFPRegs,
1641 EMIT_REMEMBERED_SET,
1642 OMIT_SMI_CHECK);
1596 __ Drop(argc + 1); 1643 __ Drop(argc + 1);
1597 __ Ret(); 1644 __ Ret();
1598 1645
1599 __ bind(&attempt_to_grow_elements); 1646 __ bind(&attempt_to_grow_elements);
1600 // r0: array's length + 1. 1647 // r0: array's length + 1.
1601 // r4: elements' length. 1648 // r4: elements' length.
1602 1649
1603 if (!FLAG_inline_new) { 1650 if (!FLAG_inline_new) {
1604 __ b(&call_builtin); 1651 __ b(&call_builtin);
1605 } 1652 }
1606 1653
1654 __ ldr(r2, MemOperand(sp, (argc - 1) * kPointerSize));
1655 if (FLAG_smi_only_arrays) {
1656 // Growing elements that are SMI-only requires special handling in case
1657 // the new element is non-Smi. For now, delegate to the builtin.
1658 Label no_fast_elements_check;
1659 __ JumpIfSmi(r2, &no_fast_elements_check);
1660 __ ldr(r7, FieldMemOperand(receiver, HeapObject::kMapOffset));
1661 __ CheckFastObjectElements(r7, r7, &call_builtin);
1662 __ bind(&no_fast_elements_check);
1663 }
1664
1607 Isolate* isolate = masm()->isolate(); 1665 Isolate* isolate = masm()->isolate();
1608 ExternalReference new_space_allocation_top = 1666 ExternalReference new_space_allocation_top =
1609 ExternalReference::new_space_allocation_top_address(isolate); 1667 ExternalReference::new_space_allocation_top_address(isolate);
1610 ExternalReference new_space_allocation_limit = 1668 ExternalReference new_space_allocation_limit =
1611 ExternalReference::new_space_allocation_limit_address(isolate); 1669 ExternalReference::new_space_allocation_limit_address(isolate);
1612 1670
1613 const int kAllocationDelta = 4; 1671 const int kAllocationDelta = 4;
1614 // Load top and check if it is the end of elements. 1672 // Load top and check if it is the end of elements.
1615 __ add(end_elements, elements, 1673 __ add(end_elements, elements,
1616 Operand(r0, LSL, kPointerSizeLog2 - kSmiTagSize)); 1674 Operand(r0, LSL, kPointerSizeLog2 - kSmiTagSize));
1617 __ add(end_elements, end_elements, Operand(kEndElementsOffset)); 1675 __ add(end_elements, end_elements, Operand(kEndElementsOffset));
1618 __ mov(r7, Operand(new_space_allocation_top)); 1676 __ mov(r7, Operand(new_space_allocation_top));
1619 __ ldr(r6, MemOperand(r7)); 1677 __ ldr(r6, MemOperand(r7));
1620 __ cmp(end_elements, r6); 1678 __ cmp(end_elements, r6);
1621 __ b(ne, &call_builtin); 1679 __ b(ne, &call_builtin);
1622 1680
1623 __ mov(r9, Operand(new_space_allocation_limit)); 1681 __ mov(r9, Operand(new_space_allocation_limit));
1624 __ ldr(r9, MemOperand(r9)); 1682 __ ldr(r9, MemOperand(r9));
1625 __ add(r6, r6, Operand(kAllocationDelta * kPointerSize)); 1683 __ add(r6, r6, Operand(kAllocationDelta * kPointerSize));
1626 __ cmp(r6, r9); 1684 __ cmp(r6, r9);
1627 __ b(hi, &call_builtin); 1685 __ b(hi, &call_builtin);
1628 1686
1629 // We fit and could grow elements. 1687 // We fit and could grow elements.
1630 // Update new_space_allocation_top. 1688 // Update new_space_allocation_top.
1631 __ str(r6, MemOperand(r7)); 1689 __ str(r6, MemOperand(r7));
1632 // Push the argument. 1690 // Push the argument.
1633 __ ldr(r6, MemOperand(sp, (argc - 1) * kPointerSize)); 1691 __ str(r2, MemOperand(end_elements));
1634 __ str(r6, MemOperand(end_elements));
1635 // Fill the rest with holes. 1692 // Fill the rest with holes.
1636 __ LoadRoot(r6, Heap::kTheHoleValueRootIndex); 1693 __ LoadRoot(r6, Heap::kTheHoleValueRootIndex);
1637 for (int i = 1; i < kAllocationDelta; i++) { 1694 for (int i = 1; i < kAllocationDelta; i++) {
1638 __ str(r6, MemOperand(end_elements, i * kPointerSize)); 1695 __ str(r6, MemOperand(end_elements, i * kPointerSize));
1639 } 1696 }
1640 1697
1641 // Update elements' and array's sizes. 1698 // Update elements' and array's sizes.
1642 __ str(r0, FieldMemOperand(receiver, JSArray::kLengthOffset)); 1699 __ str(r0, FieldMemOperand(receiver, JSArray::kLengthOffset));
1643 __ add(r4, r4, Operand(Smi::FromInt(kAllocationDelta))); 1700 __ add(r4, r4, Operand(Smi::FromInt(kAllocationDelta)));
1644 __ str(r4, FieldMemOperand(elements, FixedArray::kLengthOffset)); 1701 __ str(r4, FieldMemOperand(elements, FixedArray::kLengthOffset));
(...skipping 1061 matching lines...) Expand 10 before | Expand all | Expand 10 after
2706 // global object. We bail out to the runtime system to do that. 2763 // global object. We bail out to the runtime system to do that.
2707 __ mov(r4, Operand(Handle<JSGlobalPropertyCell>(cell))); 2764 __ mov(r4, Operand(Handle<JSGlobalPropertyCell>(cell)));
2708 __ LoadRoot(r5, Heap::kTheHoleValueRootIndex); 2765 __ LoadRoot(r5, Heap::kTheHoleValueRootIndex);
2709 __ ldr(r6, FieldMemOperand(r4, JSGlobalPropertyCell::kValueOffset)); 2766 __ ldr(r6, FieldMemOperand(r4, JSGlobalPropertyCell::kValueOffset));
2710 __ cmp(r5, r6); 2767 __ cmp(r5, r6);
2711 __ b(eq, &miss); 2768 __ b(eq, &miss);
2712 2769
2713 // Store the value in the cell. 2770 // Store the value in the cell.
2714 __ str(r0, FieldMemOperand(r4, JSGlobalPropertyCell::kValueOffset)); 2771 __ str(r0, FieldMemOperand(r4, JSGlobalPropertyCell::kValueOffset));
2715 2772
2773 __ mov(r1, r0);
2774 __ RecordWriteField(r4,
2775 JSGlobalPropertyCell::kValueOffset,
2776 r1,
2777 r2,
2778 kLRHasNotBeenSaved,
2779 kDontSaveFPRegs,
2780 OMIT_REMEMBERED_SET);
2781
2716 Counters* counters = masm()->isolate()->counters(); 2782 Counters* counters = masm()->isolate()->counters();
2717 __ IncrementCounter(counters->named_store_global_inline(), 1, r4, r3); 2783 __ IncrementCounter(counters->named_store_global_inline(), 1, r4, r3);
2718 __ Ret(); 2784 __ Ret();
2719 2785
2720 // Handle store cache miss. 2786 // Handle store cache miss.
2721 __ bind(&miss); 2787 __ bind(&miss);
2722 __ IncrementCounter(counters->named_store_global_inline_miss(), 1, r4, r3); 2788 __ IncrementCounter(counters->named_store_global_inline_miss(), 1, r4, r3);
2723 Handle<Code> ic = masm()->isolate()->builtins()->StoreIC_Miss(); 2789 Handle<Code> ic = masm()->isolate()->builtins()->StoreIC_Miss();
2724 __ Jump(ic, RelocInfo::CODE_TARGET); 2790 __ Jump(ic, RelocInfo::CODE_TARGET);
2725 2791
(...skipping 721 matching lines...) Expand 10 before | Expand all | Expand 10 after
3447 3513
3448 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: 3514 case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
3449 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: 3515 case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
3450 case EXTERNAL_UNSIGNED_INT_ELEMENTS: 3516 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
3451 case EXTERNAL_PIXEL_ELEMENTS: 3517 case EXTERNAL_PIXEL_ELEMENTS:
3452 return false; 3518 return false;
3453 3519
3454 case EXTERNAL_FLOAT_ELEMENTS: 3520 case EXTERNAL_FLOAT_ELEMENTS:
3455 case EXTERNAL_DOUBLE_ELEMENTS: 3521 case EXTERNAL_DOUBLE_ELEMENTS:
3456 case FAST_ELEMENTS: 3522 case FAST_ELEMENTS:
3523 case FAST_SMI_ONLY_ELEMENTS:
3457 case FAST_DOUBLE_ELEMENTS: 3524 case FAST_DOUBLE_ELEMENTS:
3458 case DICTIONARY_ELEMENTS: 3525 case DICTIONARY_ELEMENTS:
3459 case NON_STRICT_ARGUMENTS_ELEMENTS: 3526 case NON_STRICT_ARGUMENTS_ELEMENTS:
3460 UNREACHABLE(); 3527 UNREACHABLE();
3461 return false; 3528 return false;
3462 } 3529 }
3463 return false; 3530 return false;
3464 } 3531 }
3465 3532
3466 3533
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
3533 __ add(r2, r3, Operand(key, LSL, 2)); 3600 __ add(r2, r3, Operand(key, LSL, 2));
3534 __ vldr(d0, r2, 0); 3601 __ vldr(d0, r2, 0);
3535 } else { 3602 } else {
3536 __ add(r4, r3, Operand(key, LSL, 2)); 3603 __ add(r4, r3, Operand(key, LSL, 2));
3537 // r4: pointer to the beginning of the double we want to load. 3604 // r4: pointer to the beginning of the double we want to load.
3538 __ ldr(r2, MemOperand(r4, 0)); 3605 __ ldr(r2, MemOperand(r4, 0));
3539 __ ldr(r3, MemOperand(r4, Register::kSizeInBytes)); 3606 __ ldr(r3, MemOperand(r4, Register::kSizeInBytes));
3540 } 3607 }
3541 break; 3608 break;
3542 case FAST_ELEMENTS: 3609 case FAST_ELEMENTS:
3610 case FAST_SMI_ONLY_ELEMENTS:
3543 case FAST_DOUBLE_ELEMENTS: 3611 case FAST_DOUBLE_ELEMENTS:
3544 case DICTIONARY_ELEMENTS: 3612 case DICTIONARY_ELEMENTS:
3545 case NON_STRICT_ARGUMENTS_ELEMENTS: 3613 case NON_STRICT_ARGUMENTS_ELEMENTS:
3546 UNREACHABLE(); 3614 UNREACHABLE();
3547 break; 3615 break;
3548 } 3616 }
3549 3617
3550 // For integer array types: 3618 // For integer array types:
3551 // r2: value 3619 // r2: value
3552 // For float array type: 3620 // For float array type:
(...skipping 320 matching lines...) Expand 10 before | Expand all | Expand 10 after
3873 r4, s2); // These are: scratch2, single_scratch. 3941 r4, s2); // These are: scratch2, single_scratch.
3874 if (destination == FloatingPointHelper::kVFPRegisters) { 3942 if (destination == FloatingPointHelper::kVFPRegisters) {
3875 CpuFeatures::Scope scope(VFP3); 3943 CpuFeatures::Scope scope(VFP3);
3876 __ vstr(d0, r3, 0); 3944 __ vstr(d0, r3, 0);
3877 } else { 3945 } else {
3878 __ str(r6, MemOperand(r3, 0)); 3946 __ str(r6, MemOperand(r3, 0));
3879 __ str(r7, MemOperand(r3, Register::kSizeInBytes)); 3947 __ str(r7, MemOperand(r3, Register::kSizeInBytes));
3880 } 3948 }
3881 break; 3949 break;
3882 case FAST_ELEMENTS: 3950 case FAST_ELEMENTS:
3951 case FAST_SMI_ONLY_ELEMENTS:
3883 case FAST_DOUBLE_ELEMENTS: 3952 case FAST_DOUBLE_ELEMENTS:
3884 case DICTIONARY_ELEMENTS: 3953 case DICTIONARY_ELEMENTS:
3885 case NON_STRICT_ARGUMENTS_ELEMENTS: 3954 case NON_STRICT_ARGUMENTS_ELEMENTS:
3886 UNREACHABLE(); 3955 UNREACHABLE();
3887 break; 3956 break;
3888 } 3957 }
3889 3958
3890 // Entry registers are intact, r0 holds the value which is the return value. 3959 // Entry registers are intact, r0 holds the value which is the return value.
3891 __ Ret(); 3960 __ Ret();
3892 3961
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
3936 __ strh(r5, MemOperand(r3, key, LSL, 0)); 4005 __ strh(r5, MemOperand(r3, key, LSL, 0));
3937 break; 4006 break;
3938 case EXTERNAL_INT_ELEMENTS: 4007 case EXTERNAL_INT_ELEMENTS:
3939 case EXTERNAL_UNSIGNED_INT_ELEMENTS: 4008 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
3940 __ str(r5, MemOperand(r3, key, LSL, 1)); 4009 __ str(r5, MemOperand(r3, key, LSL, 1));
3941 break; 4010 break;
3942 case EXTERNAL_PIXEL_ELEMENTS: 4011 case EXTERNAL_PIXEL_ELEMENTS:
3943 case EXTERNAL_FLOAT_ELEMENTS: 4012 case EXTERNAL_FLOAT_ELEMENTS:
3944 case EXTERNAL_DOUBLE_ELEMENTS: 4013 case EXTERNAL_DOUBLE_ELEMENTS:
3945 case FAST_ELEMENTS: 4014 case FAST_ELEMENTS:
4015 case FAST_SMI_ONLY_ELEMENTS:
3946 case FAST_DOUBLE_ELEMENTS: 4016 case FAST_DOUBLE_ELEMENTS:
3947 case DICTIONARY_ELEMENTS: 4017 case DICTIONARY_ELEMENTS:
3948 case NON_STRICT_ARGUMENTS_ELEMENTS: 4018 case NON_STRICT_ARGUMENTS_ELEMENTS:
3949 UNREACHABLE(); 4019 UNREACHABLE();
3950 break; 4020 break;
3951 } 4021 }
3952 } 4022 }
3953 4023
3954 // Entry registers are intact, r0 holds the value which is the return 4024 // Entry registers are intact, r0 holds the value which is the return
3955 // value. 4025 // value.
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after
4075 __ strh(r5, MemOperand(r3, key, LSL, 0)); 4145 __ strh(r5, MemOperand(r3, key, LSL, 0));
4076 break; 4146 break;
4077 case EXTERNAL_INT_ELEMENTS: 4147 case EXTERNAL_INT_ELEMENTS:
4078 case EXTERNAL_UNSIGNED_INT_ELEMENTS: 4148 case EXTERNAL_UNSIGNED_INT_ELEMENTS:
4079 __ str(r5, MemOperand(r3, key, LSL, 1)); 4149 __ str(r5, MemOperand(r3, key, LSL, 1));
4080 break; 4150 break;
4081 case EXTERNAL_PIXEL_ELEMENTS: 4151 case EXTERNAL_PIXEL_ELEMENTS:
4082 case EXTERNAL_FLOAT_ELEMENTS: 4152 case EXTERNAL_FLOAT_ELEMENTS:
4083 case EXTERNAL_DOUBLE_ELEMENTS: 4153 case EXTERNAL_DOUBLE_ELEMENTS:
4084 case FAST_ELEMENTS: 4154 case FAST_ELEMENTS:
4155 case FAST_SMI_ONLY_ELEMENTS:
4085 case FAST_DOUBLE_ELEMENTS: 4156 case FAST_DOUBLE_ELEMENTS:
4086 case DICTIONARY_ELEMENTS: 4157 case DICTIONARY_ELEMENTS:
4087 case NON_STRICT_ARGUMENTS_ELEMENTS: 4158 case NON_STRICT_ARGUMENTS_ELEMENTS:
4088 UNREACHABLE(); 4159 UNREACHABLE();
4089 break; 4160 break;
4090 } 4161 }
4091 } 4162 }
4092 } 4163 }
4093 } 4164 }
4094 4165
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after
4227 masm->isolate()->builtins()->KeyedLoadIC_Slow(); 4298 masm->isolate()->builtins()->KeyedLoadIC_Slow();
4228 __ Jump(slow_ic, RelocInfo::CODE_TARGET); 4299 __ Jump(slow_ic, RelocInfo::CODE_TARGET);
4229 4300
4230 __ bind(&miss_force_generic); 4301 __ bind(&miss_force_generic);
4231 Handle<Code> miss_ic = 4302 Handle<Code> miss_ic =
4232 masm->isolate()->builtins()->KeyedLoadIC_MissForceGeneric(); 4303 masm->isolate()->builtins()->KeyedLoadIC_MissForceGeneric();
4233 __ Jump(miss_ic, RelocInfo::CODE_TARGET); 4304 __ Jump(miss_ic, RelocInfo::CODE_TARGET);
4234 } 4305 }
4235 4306
4236 4307
4237 void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm, 4308 void KeyedStoreStubCompiler::GenerateStoreFastElement(
4238 bool is_js_array) { 4309 MacroAssembler* masm,
4310 bool is_js_array,
4311 ElementsKind elements_kind) {
4239 // ----------- S t a t e ------------- 4312 // ----------- S t a t e -------------
4240 // -- r0 : value 4313 // -- r0 : value
4241 // -- r1 : key 4314 // -- r1 : key
4242 // -- r2 : receiver 4315 // -- r2 : receiver
4243 // -- lr : return address 4316 // -- lr : return address
4244 // -- r3 : scratch 4317 // -- r3 : scratch
4245 // -- r4 : scratch (elements) 4318 // -- r4 : scratch (elements)
4246 // ----------------------------------- 4319 // -----------------------------------
4247 Label miss_force_generic; 4320 Label miss_force_generic;
4248 4321
(...skipping 21 matching lines...) Expand all
4270 // Check that the key is within bounds. 4343 // Check that the key is within bounds.
4271 if (is_js_array) { 4344 if (is_js_array) {
4272 __ ldr(scratch, FieldMemOperand(receiver_reg, JSArray::kLengthOffset)); 4345 __ ldr(scratch, FieldMemOperand(receiver_reg, JSArray::kLengthOffset));
4273 } else { 4346 } else {
4274 __ ldr(scratch, FieldMemOperand(elements_reg, FixedArray::kLengthOffset)); 4347 __ ldr(scratch, FieldMemOperand(elements_reg, FixedArray::kLengthOffset));
4275 } 4348 }
4276 // Compare smis. 4349 // Compare smis.
4277 __ cmp(key_reg, scratch); 4350 __ cmp(key_reg, scratch);
4278 __ b(hs, &miss_force_generic); 4351 __ b(hs, &miss_force_generic);
4279 4352
4280 __ add(scratch, 4353 if (elements_kind == FAST_SMI_ONLY_ELEMENTS) {
4281 elements_reg, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); 4354 __ JumpIfNotSmi(value_reg, &miss_force_generic);
4282 STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize < kPointerSizeLog2); 4355 __ add(scratch,
4283 __ str(value_reg, 4356 elements_reg,
4284 MemOperand(scratch, key_reg, LSL, kPointerSizeLog2 - kSmiTagSize)); 4357 Operand(FixedArray::kHeaderSize - kHeapObjectTag));
4285 __ RecordWrite(scratch, 4358 STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize < kPointerSizeLog2);
4286 Operand(key_reg, LSL, kPointerSizeLog2 - kSmiTagSize), 4359 __ add(scratch,
4287 receiver_reg , elements_reg); 4360 scratch,
4288 4361 Operand(key_reg, LSL, kPointerSizeLog2 - kSmiTagSize));
4362 __ str(value_reg, MemOperand(scratch));
4363 } else {
4364 ASSERT(elements_kind == FAST_ELEMENTS);
4365 __ add(scratch,
4366 elements_reg,
4367 Operand(FixedArray::kHeaderSize - kHeapObjectTag));
4368 STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize < kPointerSizeLog2);
4369 __ add(scratch,
4370 scratch,
4371 Operand(key_reg, LSL, kPointerSizeLog2 - kSmiTagSize));
4372 __ str(value_reg, MemOperand(scratch));
4373 __ mov(receiver_reg, value_reg);
4374 __ RecordWrite(elements_reg, // Object.
4375 scratch, // Address.
4376 receiver_reg, // Value.
4377 kLRHasNotBeenSaved,
4378 kDontSaveFPRegs);
4379 }
4289 // value_reg (r0) is preserved. 4380 // value_reg (r0) is preserved.
4290 // Done. 4381 // Done.
4291 __ Ret(); 4382 __ Ret();
4292 4383
4293 __ bind(&miss_force_generic); 4384 __ bind(&miss_force_generic);
4294 Handle<Code> ic = 4385 Handle<Code> ic =
4295 masm->isolate()->builtins()->KeyedStoreIC_MissForceGeneric(); 4386 masm->isolate()->builtins()->KeyedStoreIC_MissForceGeneric();
4296 __ Jump(ic, RelocInfo::CODE_TARGET); 4387 __ Jump(ic, RelocInfo::CODE_TARGET);
4297 } 4388 }
4298 4389
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after
4421 masm->isolate()->builtins()->KeyedStoreIC_MissForceGeneric(); 4512 masm->isolate()->builtins()->KeyedStoreIC_MissForceGeneric();
4422 __ Jump(ic, RelocInfo::CODE_TARGET); 4513 __ Jump(ic, RelocInfo::CODE_TARGET);
4423 } 4514 }
4424 4515
4425 4516
4426 #undef __ 4517 #undef __
4427 4518
4428 } } // namespace v8::internal 4519 } } // namespace v8::internal
4429 4520
4430 #endif // V8_TARGET_ARCH_ARM 4521 #endif // V8_TARGET_ARCH_ARM
OLDNEW
« no previous file with comments | « src/arm/simulator-arm.cc ('k') | src/array.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698