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

Side by Side Diff: runtime/vm/assembler_x64.cc

Issue 12212151: Revert SSE assembler changes (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 10 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 | « runtime/vm/assembler_x64.h ('k') | runtime/vm/assembler_x64_test.cc » ('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 (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 #include "vm/globals.h" 5 #include "vm/globals.h"
6 #if defined(TARGET_ARCH_X64) 6 #if defined(TARGET_ARCH_X64)
7 7
8 #include "vm/assembler.h" 8 #include "vm/assembler.h"
9 #include "vm/heap.h" 9 #include "vm/heap.h"
10 #include "vm/memory_region.h" 10 #include "vm/memory_region.h"
(...skipping 511 matching lines...) Expand 10 before | Expand all | Expand 10 after
522 ASSERT(src <= XMM15); 522 ASSERT(src <= XMM15);
523 ASSERT(dst <= XMM15); 523 ASSERT(dst <= XMM15);
524 AssemblerBuffer::EnsureCapacity ensured(&buffer_); 524 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
525 EmitREX_RB(dst, src); 525 EmitREX_RB(dst, src);
526 EmitUint8(0x0F); 526 EmitUint8(0x0F);
527 EmitUint8(0x28); 527 EmitUint8(0x28);
528 EmitXmmRegisterOperand(dst & 7, src); 528 EmitXmmRegisterOperand(dst & 7, src);
529 } 529 }
530 530
531 531
532 void Assembler::movups(XmmRegister dst, const Address& src) {
533 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
534 EmitREX_RB(dst, src);
535 EmitUint8(0x0F);
536 EmitUint8(0x10);
537 EmitOperand(dst & 7, src);
538 }
539
540
541 void Assembler::movups(const Address& dst, XmmRegister src) {
542 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
543 EmitREX_RB(src, dst);
544 EmitUint8(0x0F);
545 EmitUint8(0x11);
546 EmitOperand(src & 7, dst);
547 }
548
549
550 void Assembler::addsd(XmmRegister dst, XmmRegister src) { 532 void Assembler::addsd(XmmRegister dst, XmmRegister src) {
551 ASSERT(src <= XMM15); 533 ASSERT(src <= XMM15);
552 ASSERT(dst <= XMM15); 534 ASSERT(dst <= XMM15);
553 AssemblerBuffer::EnsureCapacity ensured(&buffer_); 535 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
554 EmitUint8(0xF2); 536 EmitUint8(0xF2);
555 EmitREX_RB(dst, src); 537 EmitREX_RB(dst, src);
556 EmitUint8(0x0F); 538 EmitUint8(0x0F);
557 EmitUint8(0x58); 539 EmitUint8(0x58);
558 EmitXmmRegisterOperand(dst & 7, src); 540 EmitXmmRegisterOperand(dst & 7, src);
559 } 541 }
(...skipping 27 matching lines...) Expand all
587 ASSERT(src <= XMM15); 569 ASSERT(src <= XMM15);
588 ASSERT(dst <= XMM15); 570 ASSERT(dst <= XMM15);
589 AssemblerBuffer::EnsureCapacity ensured(&buffer_); 571 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
590 EmitUint8(0xF2); 572 EmitUint8(0xF2);
591 EmitREX_RB(dst, src); 573 EmitREX_RB(dst, src);
592 EmitUint8(0x0F); 574 EmitUint8(0x0F);
593 EmitUint8(0x5E); 575 EmitUint8(0x5E);
594 EmitXmmRegisterOperand(dst & 7, src); 576 EmitXmmRegisterOperand(dst & 7, src);
595 } 577 }
596 578
597 void Assembler::addps(XmmRegister dst, XmmRegister src) {
598 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
599 EmitREX_RB(dst, src);
600 EmitUint8(0x0F);
601 EmitUint8(0x58);
602 EmitXmmRegisterOperand(dst & 7, src);
603 }
604
605
606 void Assembler::subps(XmmRegister dst, XmmRegister src) {
607 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
608 EmitREX_RB(dst, src);
609 EmitUint8(0x0F);
610 EmitUint8(0x5C);
611 EmitXmmRegisterOperand(dst & 7, src);
612 }
613
614
615 void Assembler::divps(XmmRegister dst, XmmRegister src) {
616 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
617 EmitREX_RB(dst, src);
618 EmitUint8(0x0F);
619 EmitUint8(0x5E);
620 EmitXmmRegisterOperand(dst & 7, src);
621 }
622
623
624 void Assembler::mulps(XmmRegister dst, XmmRegister src) {
625 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
626 EmitREX_RB(dst, src);
627 EmitUint8(0x0F);
628 EmitUint8(0x59);
629 EmitXmmRegisterOperand(dst & 7, src);
630 }
631
632
633 void Assembler::minps(XmmRegister dst, XmmRegister src) {
634 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
635 EmitREX_RB(dst, src);
636 EmitUint8(0x0F);
637 EmitUint8(0x5D);
638 EmitXmmRegisterOperand(dst & 7, src);
639 }
640
641
642 void Assembler::maxps(XmmRegister dst, XmmRegister src) {
643 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
644 EmitREX_RB(dst, src);
645 EmitUint8(0x0F);
646 EmitUint8(0x5F);
647 EmitXmmRegisterOperand(dst & 7, src);
648 }
649
650
651 void Assembler::andps(XmmRegister dst, XmmRegister src) {
652 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
653 EmitREX_RB(dst, src);
654 EmitUint8(0x0F);
655 EmitUint8(0x54);
656 EmitXmmRegisterOperand(dst & 7, src);
657 }
658
659
660 void Assembler::andps(XmmRegister dst, const Address& src) {
661 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
662 EmitREX_RB(dst, src);
663 EmitUint8(0x0F);
664 EmitUint8(0x54);
665 EmitOperand(dst & 7, src);
666 }
667
668
669 void Assembler::orps(XmmRegister dst, XmmRegister src) {
670 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
671 EmitREX_RB(dst, src);
672 EmitUint8(0x0F);
673 EmitUint8(0x56);
674 EmitXmmRegisterOperand(dst & 7, src);
675 }
676
677 void Assembler::notps(XmmRegister dst) {
678 static const struct ALIGN16 {
679 uint32_t a;
680 uint32_t b;
681 uint32_t c;
682 uint32_t d;
683 } float_not_constant =
684 { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
685 movq(TMP, Immediate(reinterpret_cast<intptr_t>(&float_not_constant)));
686 xorps(dst, Address(TMP, 0));
687 }
688
689
690 void Assembler::negateps(XmmRegister dst) {
691 static const struct ALIGN16 {
692 uint32_t a;
693 uint32_t b;
694 uint32_t c;
695 uint32_t d;
696 } float_negate_constant =
697 { 0x80000000, 0x80000000, 0x80000000, 0x80000000 };
698 movq(TMP, Immediate(reinterpret_cast<intptr_t>(&float_negate_constant)));
699 xorps(dst, Address(TMP, 0));
700 }
701
702
703 void Assembler::absps(XmmRegister dst) {
704 static const struct ALIGN16 {
705 uint32_t a;
706 uint32_t b;
707 uint32_t c;
708 uint32_t d;
709 } float_absolute_constant =
710 { 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF };
711 movq(TMP, Immediate(reinterpret_cast<intptr_t>(&float_absolute_constant)));
712 andps(dst, Address(TMP, 0));
713 }
714
715
716 void Assembler::zerowps(XmmRegister dst) {
717 static const struct ALIGN16 {
718 uint32_t a;
719 uint32_t b;
720 uint32_t c;
721 uint32_t d;
722 } float_zerow_constant =
723 { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000 };
724 movq(TMP, Immediate(reinterpret_cast<intptr_t>(&float_zerow_constant)));
725 andps(dst, Address(TMP, 0));
726 }
727
728
729 void Assembler::cmppseq(XmmRegister dst, XmmRegister src) {
730 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
731 EmitREX_RB(dst, src);
732 EmitUint8(0x0F);
733 EmitUint8(0xC2);
734 EmitXmmRegisterOperand(dst & 7, src);
735 EmitUint8(0x0);
736 }
737
738
739 void Assembler::cmppsneq(XmmRegister dst, XmmRegister src) {
740 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
741 EmitREX_RB(dst, src);
742 EmitUint8(0x0F);
743 EmitUint8(0xC2);
744 EmitXmmRegisterOperand(dst & 7, src);
745 EmitUint8(0x4);
746 }
747
748
749 void Assembler::cmppslt(XmmRegister dst, XmmRegister src) {
750 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
751 EmitREX_RB(dst, src);
752 EmitUint8(0x0F);
753 EmitUint8(0xC2);
754 EmitXmmRegisterOperand(dst & 7, src);
755 EmitUint8(0x1);
756 }
757
758
759 void Assembler::cmppsle(XmmRegister dst, XmmRegister src) {
760 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
761 EmitREX_RB(dst, src);
762 EmitUint8(0x0F);
763 EmitUint8(0xC2);
764 EmitXmmRegisterOperand(dst & 7, src);
765 EmitUint8(0x2);
766 }
767
768
769 void Assembler::cmppsnlt(XmmRegister dst, XmmRegister src) {
770 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
771 EmitREX_RB(dst, src);
772 EmitUint8(0x0F);
773 EmitUint8(0xC2);
774 EmitXmmRegisterOperand(dst & 7, src);
775 EmitUint8(0x5);
776 }
777
778
779 void Assembler::cmppsnle(XmmRegister dst, XmmRegister src) {
780 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
781 EmitREX_RB(dst, src);
782 EmitUint8(0x0F);
783 EmitUint8(0xC2);
784 EmitXmmRegisterOperand(dst & 7, src);
785 EmitUint8(0x6);
786 }
787
788
789 void Assembler::sqrtps(XmmRegister dst) {
790 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
791 EmitREX_RB(dst, dst);
792 EmitUint8(0x0F);
793 EmitUint8(0x51);
794 EmitXmmRegisterOperand(dst & 7, dst);
795 }
796
797
798 void Assembler::rsqrtps(XmmRegister dst) {
799 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
800 EmitREX_RB(dst, dst);
801 EmitUint8(0x0F);
802 EmitUint8(0x52);
803 EmitXmmRegisterOperand(dst & 7, dst);
804 }
805
806
807 void Assembler::reciprocalps(XmmRegister dst) {
808 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
809 EmitREX_RB(dst, dst);
810 EmitUint8(0x0F);
811 EmitUint8(0x53);
812 EmitXmmRegisterOperand(dst & 7, dst);
813 }
814
815
816 void Assembler::set1ps(XmmRegister dst, Register tmp1, const Immediate& imm) {
817 // Load 32-bit immediate value into tmp1.
818 movl(tmp1, imm);
819 // Move value from tmp1 into dst.
820 movd(dst, tmp1);
821 // Broadcast low lane into other three lanes.
822 shufps(dst, dst, Immediate(0x0));
823 }
824
825
826 void Assembler::shufps(XmmRegister dst, XmmRegister src, const Immediate& imm) {
827 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
828 EmitREX_RB(dst, src);
829 EmitUint8(0x0F);
830 EmitUint8(0xC6);
831 EmitXmmRegisterOperand(dst & 7, src);
832 ASSERT(imm.is_uint8());
833 EmitUint8(imm.value());
834 }
835
836 579
837 void Assembler::comisd(XmmRegister a, XmmRegister b) { 580 void Assembler::comisd(XmmRegister a, XmmRegister b) {
838 ASSERT(a <= XMM15); 581 ASSERT(a <= XMM15);
839 ASSERT(b <= XMM15); 582 ASSERT(b <= XMM15);
840 AssemblerBuffer::EnsureCapacity ensured(&buffer_); 583 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
841 EmitUint8(0x66); 584 EmitUint8(0x66);
842 EmitREX_RB(a, b); 585 EmitREX_RB(a, b);
843 EmitUint8(0x0F); 586 EmitUint8(0x0F);
844 EmitUint8(0x2F); 587 EmitUint8(0x2F);
845 EmitXmmRegisterOperand(a & 7, b); 588 EmitXmmRegisterOperand(a & 7, b);
846 } 589 }
847 590
848 591
849 void Assembler::movmskpd(Register dst, XmmRegister src) { 592 void Assembler::movmskpd(Register dst, XmmRegister src) {
850 ASSERT(src <= XMM15); 593 ASSERT(src <= XMM15);
851 AssemblerBuffer::EnsureCapacity ensured(&buffer_); 594 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
852 EmitUint8(0x66); 595 EmitUint8(0x66);
853 EmitREX_RB(dst, src); 596 EmitREX_RB(dst, src);
854 EmitUint8(0x0F); 597 EmitUint8(0x0F);
855 EmitUint8(0x50); 598 EmitUint8(0x50);
856 EmitXmmRegisterOperand(dst & 7, src); 599 EmitXmmRegisterOperand(dst & 7, src);
857 } 600 }
858 601
859 602
860 void Assembler::movmskps(Register dst, XmmRegister src) {
861 ASSERT(src <= XMM15);
862 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
863 EmitREX_RB(dst, src);
864 EmitUint8(0x0F);
865 EmitUint8(0x50);
866 EmitXmmRegisterOperand(dst & 7, src);
867 }
868
869
870 void Assembler::sqrtsd(XmmRegister dst, XmmRegister src) { 603 void Assembler::sqrtsd(XmmRegister dst, XmmRegister src) {
871 ASSERT(dst <= XMM15); 604 ASSERT(dst <= XMM15);
872 ASSERT(src <= XMM15); 605 ASSERT(src <= XMM15);
873 AssemblerBuffer::EnsureCapacity ensured(&buffer_); 606 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
874 EmitUint8(0xF2); 607 EmitUint8(0xF2);
875 EmitREX_RB(dst, src); 608 EmitREX_RB(dst, src);
876 EmitUint8(0x0F); 609 EmitUint8(0x0F);
877 EmitUint8(0x51); 610 EmitUint8(0x51);
878 EmitXmmRegisterOperand(dst & 7, src); 611 EmitXmmRegisterOperand(dst & 7, src);
879 } 612 }
(...skipping 15 matching lines...) Expand all
895 ASSERT(src <= XMM15); 628 ASSERT(src <= XMM15);
896 AssemblerBuffer::EnsureCapacity ensured(&buffer_); 629 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
897 EmitUint8(0x66); 630 EmitUint8(0x66);
898 EmitREX_RB(dst, src); 631 EmitREX_RB(dst, src);
899 EmitUint8(0x0F); 632 EmitUint8(0x0F);
900 EmitUint8(0x57); 633 EmitUint8(0x57);
901 EmitXmmRegisterOperand(dst & 7, src); 634 EmitXmmRegisterOperand(dst & 7, src);
902 } 635 }
903 636
904 637
905 void Assembler::xorps(XmmRegister dst, const Address& src) {
906 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
907 EmitREX_RB(dst, src);
908 EmitUint8(0x0F);
909 EmitUint8(0x57);
910 EmitOperand(dst & 7, src);
911 }
912
913
914 void Assembler::xorps(XmmRegister dst, XmmRegister src) {
915 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
916 EmitREX_RB(dst, src);
917 EmitUint8(0x0F);
918 EmitUint8(0x57);
919 EmitXmmRegisterOperand(dst & 7, src);
920 }
921
922 void Assembler::andpd(XmmRegister dst, const Address& src) { 638 void Assembler::andpd(XmmRegister dst, const Address& src) {
923 ASSERT(dst <= XMM15); 639 ASSERT(dst <= XMM15);
924 AssemblerBuffer::EnsureCapacity ensured(&buffer_); 640 AssemblerBuffer::EnsureCapacity ensured(&buffer_);
925 EmitUint8(0x66); 641 EmitUint8(0x66);
926 EmitOperandREX(dst, src, REX_NONE); 642 EmitOperandREX(dst, src, REX_NONE);
927 EmitUint8(0x0F); 643 EmitUint8(0x0F);
928 EmitUint8(0x54); 644 EmitUint8(0x54);
929 EmitOperand(dst & 7, src); 645 EmitOperand(dst & 7, src);
930 } 646 }
931 647
(...skipping 1268 matching lines...) Expand 10 before | Expand all | Expand 10 after
2200 1916
2201 void Assembler::EnterCallRuntimeFrame(intptr_t frame_space) { 1917 void Assembler::EnterCallRuntimeFrame(intptr_t frame_space) {
2202 EnterFrame(0); 1918 EnterFrame(0);
2203 1919
2204 // Preserve volatile CPU registers. 1920 // Preserve volatile CPU registers.
2205 for (intptr_t i = 0; i < kNumberOfVolatileCpuRegisters; i++) { 1921 for (intptr_t i = 0; i < kNumberOfVolatileCpuRegisters; i++) {
2206 pushq(volatile_cpu_registers[i]); 1922 pushq(volatile_cpu_registers[i]);
2207 } 1923 }
2208 1924
2209 // Preserve all XMM registers except XMM0 1925 // Preserve all XMM registers except XMM0
2210 subq(RSP, Immediate((kNumberOfXmmRegisters - 1) * kFpuRegisterSize)); 1926 subq(RSP, Immediate((kNumberOfXmmRegisters - 1) * kDoubleSize));
2211 // Store XMM registers with the lowest register number at the lowest 1927 // Store XMM registers with the lowest register number at the lowest
2212 // address. 1928 // address.
2213 intptr_t offset = 0; 1929 intptr_t offset = 0;
2214 for (intptr_t reg_idx = 1; reg_idx < kNumberOfXmmRegisters; ++reg_idx) { 1930 for (intptr_t reg_idx = 1; reg_idx < kNumberOfXmmRegisters; ++reg_idx) {
2215 XmmRegister xmm_reg = static_cast<XmmRegister>(reg_idx); 1931 XmmRegister xmm_reg = static_cast<XmmRegister>(reg_idx);
2216 movups(Address(RSP, offset), xmm_reg); 1932 movsd(Address(RSP, offset), xmm_reg);
2217 offset += kFpuRegisterSize; 1933 offset += kDoubleSize;
2218 } 1934 }
2219 1935
2220 ReserveAlignedFrameSpace(frame_space); 1936 ReserveAlignedFrameSpace(frame_space);
2221 } 1937 }
2222 1938
2223 1939
2224 void Assembler::LeaveCallRuntimeFrame() { 1940 void Assembler::LeaveCallRuntimeFrame() {
2225 // RSP might have been modified to reserve space for arguments 1941 // RSP might have been modified to reserve space for arguments
2226 // and ensure proper alignment of the stack frame. 1942 // and ensure proper alignment of the stack frame.
2227 // We need to restore it before restoring registers. 1943 // We need to restore it before restoring registers.
2228 const intptr_t kPushedRegistersSize = 1944 const intptr_t kPushedRegistersSize =
2229 kNumberOfVolatileCpuRegisters * kWordSize + 1945 kNumberOfVolatileCpuRegisters * kWordSize +
2230 kNumberOfVolatileXmmRegisters * kFpuRegisterSize; 1946 kNumberOfVolatileXmmRegisters * kDoubleSize;
2231 leaq(RSP, Address(RBP, -kPushedRegistersSize)); 1947 leaq(RSP, Address(RBP, -kPushedRegistersSize));
2232 1948
2233 // Restore all XMM registers except XMM0 1949 // Restore all XMM registers except XMM0
2234 // XMM registers have the lowest register number at the lowest address. 1950 // XMM registers have the lowest register number at the lowest address.
2235 intptr_t offset = 0; 1951 intptr_t offset = 0;
2236 for (intptr_t reg_idx = 1; reg_idx < kNumberOfXmmRegisters; ++reg_idx) { 1952 for (intptr_t reg_idx = 1; reg_idx < kNumberOfXmmRegisters; ++reg_idx) {
2237 XmmRegister xmm_reg = static_cast<XmmRegister>(reg_idx); 1953 XmmRegister xmm_reg = static_cast<XmmRegister>(reg_idx);
2238 movups(xmm_reg, Address(RSP, offset)); 1954 movsd(xmm_reg, Address(RSP, offset));
2239 offset += kFpuRegisterSize; 1955 offset += kDoubleSize;
2240 } 1956 }
2241 addq(RSP, Immediate(offset)); 1957 addq(RSP, Immediate(offset));
2242 1958
2243 // Restore volatile CPU registers. 1959 // Restore volatile CPU registers.
2244 for (intptr_t i = kNumberOfVolatileCpuRegisters - 1; i >= 0; i--) { 1960 for (intptr_t i = kNumberOfVolatileCpuRegisters - 1; i >= 0; i--) {
2245 popq(volatile_cpu_registers[i]); 1961 popq(volatile_cpu_registers[i]);
2246 } 1962 }
2247 1963
2248 leave(); 1964 leave();
2249 } 1965 }
(...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after
2441 2157
2442 const char* Assembler::FpuRegisterName(FpuRegister reg) { 2158 const char* Assembler::FpuRegisterName(FpuRegister reg) {
2443 ASSERT((0 <= reg) && (reg < kNumberOfXmmRegisters)); 2159 ASSERT((0 <= reg) && (reg < kNumberOfXmmRegisters));
2444 return xmm_reg_names[reg]; 2160 return xmm_reg_names[reg];
2445 } 2161 }
2446 2162
2447 2163
2448 } // namespace dart 2164 } // namespace dart
2449 2165
2450 #endif // defined TARGET_ARCH_X64 2166 #endif // defined TARGET_ARCH_X64
OLDNEW
« no previous file with comments | « runtime/vm/assembler_x64.h ('k') | runtime/vm/assembler_x64_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698