// Copyright 2012 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #if V8_TARGET_ARCH_IA32 #include "src/codegen.h" #include "src/heap/factory-inl.h" #include "src/heap/heap.h" #include "src/isolate.h" #include "src/macro-assembler.h" namespace v8 { namespace internal { #define __ masm. UnaryMathFunctionWithIsolate CreateSqrtFunction(Isolate* isolate) { size_t allocated = 0; byte* buffer = AllocatePage(isolate->heap()->GetRandomMmapAddr(), &allocated); if (buffer == nullptr) return nullptr; MacroAssembler masm(isolate, buffer, static_cast(allocated), CodeObjectRequired::kNo); // esp[1 * kPointerSize]: raw double input // esp[0 * kPointerSize]: return address // Move double input into registers. { __ movsd(xmm0, Operand(esp, 1 * kPointerSize)); __ sqrtsd(xmm0, xmm0); __ movsd(Operand(esp, 1 * kPointerSize), xmm0); // Load result into floating point register as return value. __ fld_d(Operand(esp, 1 * kPointerSize)); __ Ret(); } CodeDesc desc; masm.GetCode(isolate, &desc); DCHECK(!RelocInfo::RequiresRelocationAfterCodegen(desc)); Assembler::FlushICache(buffer, allocated); CHECK(SetPermissions(buffer, allocated, PageAllocator::kReadExecute)); return FUNCTION_CAST(buffer); } // Helper functions for CreateMemMoveFunction. #undef __ #define __ ACCESS_MASM(masm) enum Direction { FORWARD, BACKWARD }; enum Alignment { MOVE_ALIGNED, MOVE_UNALIGNED }; // Expects registers: // esi - source, aligned if alignment == ALIGNED // edi - destination, always aligned // ecx - count (copy size in bytes) // edx - loop count (number of 64 byte chunks) void MemMoveEmitMainLoop(MacroAssembler* masm, Label* move_last_15, Direction direction, Alignment alignment) { Register src = esi; Register dst = edi; Register count = ecx; Register loop_count = edx; Label loop, move_last_31, move_last_63; __ cmp(loop_count, 0); __ j(equal, &move_last_63); __ bind(&loop); // Main loop. Copy in 64 byte chunks. if (direction == BACKWARD) __ sub(src, Immediate(0x40)); __ movdq(alignment == MOVE_ALIGNED, xmm0, Operand(src, 0x00)); __ movdq(alignment == MOVE_ALIGNED, xmm1, Operand(src, 0x10)); __ movdq(alignment == MOVE_ALIGNED, xmm2, Operand(src, 0x20)); __ movdq(alignment == MOVE_ALIGNED, xmm3, Operand(src, 0x30)); if (direction == FORWARD) __ add(src, Immediate(0x40)); if (direction == BACKWARD) __ sub(dst, Immediate(0x40)); __ movdqa(Operand(dst, 0x00), xmm0); __ movdqa(Operand(dst, 0x10), xmm1); __ movdqa(Operand(dst, 0x20), xmm2); __ movdqa(Operand(dst, 0x30), xmm3); if (direction == FORWARD) __ add(dst, Immediate(0x40)); __ dec(loop_count); __ j(not_zero, &loop); // At most 63 bytes left to copy. __ bind(&move_last_63); __ test(count, Immediate(0x20)); __ j(zero, &move_last_31); if (direction == BACKWARD) __ sub(src, Immediate(0x20)); __ movdq(alignment == MOVE_ALIGNED, xmm0, Operand(src, 0x00)); __ movdq(alignment == MOVE_ALIGNED, xmm1, Operand(src, 0x10)); if (direction == FORWARD) __ add(src, Immediate(0x20)); if (direction == BACKWARD) __ sub(dst, Immediate(0x20)); __ movdqa(Operand(dst, 0x00), xmm0); __ movdqa(Operand(dst, 0x10), xmm1); if (direction == FORWARD) __ add(dst, Immediate(0x20)); // At most 31 bytes left to copy. __ bind(&move_last_31); __ test(count, Immediate(0x10)); __ j(zero, move_last_15); if (direction == BACKWARD) __ sub(src, Immediate(0x10)); __ movdq(alignment == MOVE_ALIGNED, xmm0, Operand(src, 0)); if (direction == FORWARD) __ add(src, Immediate(0x10)); if (direction == BACKWARD) __ sub(dst, Immediate(0x10)); __ movdqa(Operand(dst, 0), xmm0); if (direction == FORWARD) __ add(dst, Immediate(0x10)); } void MemMoveEmitPopAndReturn(MacroAssembler* masm) { __ pop(esi); __ pop(edi); __ ret(0); } #undef __ #define __ masm. class LabelConverter { public: explicit LabelConverter(byte* buffer) : buffer_(buffer) {} int32_t address(Label* l) const { return reinterpret_cast(buffer_) + l->pos(); } private: byte* buffer_; }; MemMoveFunction CreateMemMoveFunction(Isolate* isolate) { size_t allocated = 0; byte* buffer = AllocatePage(isolate->heap()->GetRandomMmapAddr(), &allocated); if (buffer == nullptr) return nullptr; MacroAssembler masm(isolate, buffer, static_cast(allocated), CodeObjectRequired::kNo); LabelConverter conv(buffer); // Generated code is put into a fixed, unmovable buffer, and not into // the V8 heap. We can't, and don't, refer to any relocatable addresses // (e.g. the JavaScript nan-object). // 32-bit C declaration function calls pass arguments on stack. // Stack layout: // esp[12]: Third argument, size. // esp[8]: Second argument, source pointer. // esp[4]: First argument, destination pointer. // esp[0]: return address const int kDestinationOffset = 1 * kPointerSize; const int kSourceOffset = 2 * kPointerSize; const int kSizeOffset = 3 * kPointerSize; // When copying up to this many bytes, use special "small" handlers. const size_t kSmallCopySize = 8; // When copying up to this many bytes, use special "medium" handlers. const size_t kMediumCopySize = 63; // When non-overlapping region of src and dst is less than this, // use a more careful implementation (slightly slower). const size_t kMinMoveDistance = 16; // Note that these values are dictated by the implementation below, // do not just change them and hope things will work! int stack_offset = 0; // Update if we change the stack height. Label backward, backward_much_overlap; Label forward_much_overlap, small_size, medium_size, pop_and_return; __ push(edi); __ push(esi); stack_offset += 2 * kPointerSize; Register dst = edi; Register src = esi; Register count = ecx; Register loop_count = edx; __ mov(dst, Operand(esp, stack_offset + kDestinationOffset)); __ mov(src, Operand(esp, stack_offset + kSourceOffset)); __ mov(count, Operand(esp, stack_offset + kSizeOffset)); __ cmp(dst, src); __ j(equal, &pop_and_return); __ prefetch(Operand(src, 0), 1); __ cmp(count, kSmallCopySize); __ j(below_equal, &small_size); __ cmp(count, kMediumCopySize); __ j(below_equal, &medium_size); __ cmp(dst, src); __ j(above, &backward); { // |dst| is a lower address than |src|. Copy front-to-back. Label unaligned_source, move_last_15, skip_last_move; __ mov(eax, src); __ sub(eax, dst); __ cmp(eax, kMinMoveDistance); __ j(below, &forward_much_overlap); // Copy first 16 bytes. __ movdqu(xmm0, Operand(src, 0)); __ movdqu(Operand(dst, 0), xmm0); // Determine distance to alignment: 16 - (dst & 0xF). __ mov(edx, dst); __ and_(edx, 0xF); __ neg(edx); __ add(edx, Immediate(16)); __ add(dst, edx); __ add(src, edx); __ sub(count, edx); // dst is now aligned. Main copy loop. __ mov(loop_count, count); __ shr(loop_count, 6); // Check if src is also aligned. __ test(src, Immediate(0xF)); __ j(not_zero, &unaligned_source); // Copy loop for aligned source and destination. MemMoveEmitMainLoop(&masm, &move_last_15, FORWARD, MOVE_ALIGNED); // At most 15 bytes to copy. Copy 16 bytes at end of string. __ bind(&move_last_15); __ and_(count, 0xF); __ j(zero, &skip_last_move, Label::kNear); __ movdqu(xmm0, Operand(src, count, times_1, -0x10)); __ movdqu(Operand(dst, count, times_1, -0x10), xmm0); __ bind(&skip_last_move); MemMoveEmitPopAndReturn(&masm); // Copy loop for unaligned source and aligned destination. __ bind(&unaligned_source); MemMoveEmitMainLoop(&masm, &move_last_15, FORWARD, MOVE_UNALIGNED); __ jmp(&move_last_15); // Less than kMinMoveDistance offset between dst and src. Label loop_until_aligned, last_15_much_overlap; __ bind(&loop_until_aligned); __ mov_b(eax, Operand(src, 0)); __ inc(src); __ mov_b(Operand(dst, 0), eax); __ inc(dst); __ dec(count); __ bind(&forward_much_overlap); // Entry point into this block. __ test(dst, Immediate(0xF)); __ j(not_zero, &loop_until_aligned); // dst is now aligned, src can't be. Main copy loop. __ mov(loop_count, count); __ shr(loop_count, 6); MemMoveEmitMainLoop(&masm, &last_15_much_overlap, FORWARD, MOVE_UNALIGNED); __ bind(&last_15_much_overlap); __ and_(count, 0xF); __ j(zero, &pop_and_return); __ cmp(count, kSmallCopySize); __ j(below_equal, &small_size); __ jmp(&medium_size); } { // |dst| is a higher address than |src|. Copy backwards. Label unaligned_source, move_first_15, skip_last_move; __ bind(&backward); // |dst| and |src| always point to the end of what's left to copy. __ add(dst, count); __ add(src, count); __ mov(eax, dst); __ sub(eax, src); __ cmp(eax, kMinMoveDistance); __ j(below, &backward_much_overlap); // Copy last 16 bytes. __ movdqu(xmm0, Operand(src, -0x10)); __ movdqu(Operand(dst, -0x10), xmm0); // Find distance to alignment: dst & 0xF __ mov(edx, dst); __ and_(edx, 0xF); __ sub(dst, edx); __ sub(src, edx); __ sub(count, edx); // dst is now aligned. Main copy loop. __ mov(loop_count, count); __ shr(loop_count, 6); // Check if src is also aligned. __ test(src, Immediate(0xF)); __ j(not_zero, &unaligned_source); // Copy loop for aligned source and destination. MemMoveEmitMainLoop(&masm, &move_first_15, BACKWARD, MOVE_ALIGNED); // At most 15 bytes to copy. Copy 16 bytes at beginning of string. __ bind(&move_first_15); __ and_(count, 0xF); __ j(zero, &skip_last_move, Label::kNear); __ sub(src, count); __ sub(dst, count); __ movdqu(xmm0, Operand(src, 0)); __ movdqu(Operand(dst, 0), xmm0); __ bind(&skip_last_move); MemMoveEmitPopAndReturn(&masm); // Copy loop for unaligned source and aligned destination. __ bind(&unaligned_source); MemMoveEmitMainLoop(&masm, &move_first_15, BACKWARD, MOVE_UNALIGNED); __ jmp(&move_first_15); // Less than kMinMoveDistance offset between dst and src. Label loop_until_aligned, first_15_much_overlap; __ bind(&loop_until_aligned); __ dec(src); __ dec(dst); __ mov_b(eax, Operand(src, 0)); __ mov_b(Operand(dst, 0), eax); __ dec(count); __ bind(&backward_much_overlap); // Entry point into this block. __ test(dst, Immediate(0xF)); __ j(not_zero, &loop_until_aligned); // dst is now aligned, src can't be. Main copy loop. __ mov(loop_count, count); __ shr(loop_count, 6); MemMoveEmitMainLoop(&masm, &first_15_much_overlap, BACKWARD, MOVE_UNALIGNED); __ bind(&first_15_much_overlap); __ and_(count, 0xF); __ j(zero, &pop_and_return); // Small/medium handlers expect dst/src to point to the beginning. __ sub(dst, count); __ sub(src, count); __ cmp(count, kSmallCopySize); __ j(below_equal, &small_size); __ jmp(&medium_size); } { // Special handlers for 9 <= copy_size < 64. No assumptions about // alignment or move distance, so all reads must be unaligned and // must happen before any writes. Label medium_handlers, f9_16, f17_32, f33_48, f49_63; __ bind(&f9_16); __ movsd(xmm0, Operand(src, 0)); __ movsd(xmm1, Operand(src, count, times_1, -8)); __ movsd(Operand(dst, 0), xmm0); __ movsd(Operand(dst, count, times_1, -8), xmm1); MemMoveEmitPopAndReturn(&masm); __ bind(&f17_32); __ movdqu(xmm0, Operand(src, 0)); __ movdqu(xmm1, Operand(src, count, times_1, -0x10)); __ movdqu(Operand(dst, 0x00), xmm0); __ movdqu(Operand(dst, count, times_1, -0x10), xmm1); MemMoveEmitPopAndReturn(&masm); __ bind(&f33_48); __ movdqu(xmm0, Operand(src, 0x00)); __ movdqu(xmm1, Operand(src, 0x10)); __ movdqu(xmm2, Operand(src, count, times_1, -0x10)); __ movdqu(Operand(dst, 0x00), xmm0); __ movdqu(Operand(dst, 0x10), xmm1); __ movdqu(Operand(dst, count, times_1, -0x10), xmm2); MemMoveEmitPopAndReturn(&masm); __ bind(&f49_63); __ movdqu(xmm0, Operand(src, 0x00)); __ movdqu(xmm1, Operand(src, 0x10)); __ movdqu(xmm2, Operand(src, 0x20)); __ movdqu(xmm3, Operand(src, count, times_1, -0x10)); __ movdqu(Operand(dst, 0x00), xmm0); __ movdqu(Operand(dst, 0x10), xmm1); __ movdqu(Operand(dst, 0x20), xmm2); __ movdqu(Operand(dst, count, times_1, -0x10), xmm3); MemMoveEmitPopAndReturn(&masm); __ bind(&medium_handlers); __ dd(conv.address(&f9_16)); __ dd(conv.address(&f17_32)); __ dd(conv.address(&f33_48)); __ dd(conv.address(&f49_63)); __ bind(&medium_size); // Entry point into this block. __ mov(eax, count); __ dec(eax); __ shr(eax, 4); if (FLAG_debug_code) { Label ok; __ cmp(eax, 3); __ j(below_equal, &ok); __ int3(); __ bind(&ok); } __ mov(eax, Operand(eax, times_4, conv.address(&medium_handlers))); __ jmp(eax); } { // Specialized copiers for copy_size <= 8 bytes. Label small_handlers, f0, f1, f2, f3, f4, f5_8; __ bind(&f0); MemMoveEmitPopAndReturn(&masm); __ bind(&f1); __ mov_b(eax, Operand(src, 0)); __ mov_b(Operand(dst, 0), eax); MemMoveEmitPopAndReturn(&masm); __ bind(&f2); __ mov_w(eax, Operand(src, 0)); __ mov_w(Operand(dst, 0), eax); MemMoveEmitPopAndReturn(&masm); __ bind(&f3); __ mov_w(eax, Operand(src, 0)); __ mov_b(edx, Operand(src, 2)); __ mov_w(Operand(dst, 0), eax); __ mov_b(Operand(dst, 2), edx); MemMoveEmitPopAndReturn(&masm); __ bind(&f4); __ mov(eax, Operand(src, 0)); __ mov(Operand(dst, 0), eax); MemMoveEmitPopAndReturn(&masm); __ bind(&f5_8); __ mov(eax, Operand(src, 0)); __ mov(edx, Operand(src, count, times_1, -4)); __ mov(Operand(dst, 0), eax); __ mov(Operand(dst, count, times_1, -4), edx); MemMoveEmitPopAndReturn(&masm); __ bind(&small_handlers); __ dd(conv.address(&f0)); __ dd(conv.address(&f1)); __ dd(conv.address(&f2)); __ dd(conv.address(&f3)); __ dd(conv.address(&f4)); __ dd(conv.address(&f5_8)); __ dd(conv.address(&f5_8)); __ dd(conv.address(&f5_8)); __ dd(conv.address(&f5_8)); __ bind(&small_size); // Entry point into this block. if (FLAG_debug_code) { Label ok; __ cmp(count, 8); __ j(below_equal, &ok); __ int3(); __ bind(&ok); } __ mov(eax, Operand(count, times_4, conv.address(&small_handlers))); __ jmp(eax); } __ bind(&pop_and_return); MemMoveEmitPopAndReturn(&masm); CodeDesc desc; masm.GetCode(isolate, &desc); DCHECK(!RelocInfo::RequiresRelocationAfterCodegen(desc)); Assembler::FlushICache(buffer, allocated); CHECK(SetPermissions(buffer, allocated, PageAllocator::kReadExecute)); // TODO(jkummerow): It would be nice to register this code creation event // with the PROFILE / GDBJIT system. return FUNCTION_CAST(buffer); } #undef __ } // namespace internal } // namespace v8 #endif // V8_TARGET_ARCH_IA32