Title: [144043] trunk/Source
Revision
144043
Author
[email protected]
Date
2013-02-26 05:04:28 -0800 (Tue, 26 Feb 2013)

Log Message

Implement JIT on Windows 64 bits
https://bugs.webkit.org/show_bug.cgi?id=107965

Reviewed by Simon Hausmann.

Source/_javascript_Core:

1. MSVC doesn't support inline assembly for 64 bits, implements the trampoline in a separate ASM file.

2. Windows 64 bits has a different calling convention than other OSes following the AMD64 ABI.
Differences that we have to handle here:
- Registers passed parameters are RCX, RDX, R8 and R9 instead of RDI, RSI, RDX, RCX, R8 and R9
- RDI and RSI must be preserved by callee
- Only return values <= 8 bytes can be returned by register (RDX can't be used to return a second word)
- There is no red-zone after RIP on the stack, but instead 4 reserved words before it

* Target.pri:
* jit/JITStubs.cpp:
* jit/JITStubs.h:
(JSC):
(JITStackFrame):
(JSC::JITStackFrame::returnAddressSlot):
* jit/JITStubsMSVC64.asm: Added.
* jit/JSInterfaceJIT.h:
(JSInterfaceJIT):
* jit/ThunkGenerators.cpp:
(JSC::nativeForGenerator):
* yarr/YarrJIT.cpp:
(YarrGenerator):
(JSC::Yarr::YarrGenerator::generateEnter):
(JSC::Yarr::YarrGenerator::generateReturn):

Source/WTF:

* wtf/Platform.h:

Modified Paths

Added Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (144042 => 144043)


--- trunk/Source/_javascript_Core/ChangeLog	2013-02-26 13:01:59 UTC (rev 144042)
+++ trunk/Source/_javascript_Core/ChangeLog	2013-02-26 13:04:28 UTC (rev 144043)
@@ -1,3 +1,35 @@
+2013-02-26  Jocelyn Turcotte  <[email protected]>
+
+        Implement JIT on Windows 64 bits
+        https://bugs.webkit.org/show_bug.cgi?id=107965
+
+        Reviewed by Simon Hausmann.
+
+        1. MSVC doesn't support inline assembly for 64 bits, implements the trampoline in a separate ASM file.
+
+        2. Windows 64 bits has a different calling convention than other OSes following the AMD64 ABI.
+        Differences that we have to handle here:
+        - Registers passed parameters are RCX, RDX, R8 and R9 instead of RDI, RSI, RDX, RCX, R8 and R9
+        - RDI and RSI must be preserved by callee
+        - Only return values <= 8 bytes can be returned by register (RDX can't be used to return a second word)
+        - There is no red-zone after RIP on the stack, but instead 4 reserved words before it
+
+        * Target.pri:
+        * jit/JITStubs.cpp:
+        * jit/JITStubs.h:
+        (JSC):
+        (JITStackFrame):
+        (JSC::JITStackFrame::returnAddressSlot):
+        * jit/JITStubsMSVC64.asm: Added.
+        * jit/JSInterfaceJIT.h:
+        (JSInterfaceJIT):
+        * jit/ThunkGenerators.cpp:
+        (JSC::nativeForGenerator):
+        * yarr/YarrJIT.cpp:
+        (YarrGenerator):
+        (JSC::Yarr::YarrGenerator::generateEnter):
+        (JSC::Yarr::YarrGenerator::generateReturn):
+
 2013-02-26  Oliver Hunt  <[email protected]>
 
         Kill another analyzer warning in _javascript_core

Modified: trunk/Source/_javascript_Core/Target.pri (144042 => 144043)


--- trunk/Source/_javascript_Core/Target.pri	2013-02-26 13:01:59 UTC (rev 144042)
+++ trunk/Source/_javascript_Core/Target.pri	2013-02-26 13:04:28 UTC (rev 144043)
@@ -335,6 +335,19 @@
         disassembler/udis86/udis86_syn.c \
 }
 
+win32:!win32-g++*:isEqual(QT_ARCH, "x86_64"):{
+    asm_compiler.commands = ml64 /c
+    asm_compiler.commands +=  /Fo ${QMAKE_FILE_OUT} ${QMAKE_FILE_IN}
+    asm_compiler.output = ${QMAKE_VAR_OBJECTS_DIR}${QMAKE_FILE_BASE}$${first(QMAKE_EXT_OBJ)}
+    asm_compiler.input = ASM_SOURCES
+    asm_compiler.variable_out = OBJECTS
+    asm_compiler.name = compiling[asm] ${QMAKE_FILE_IN}
+    silent:asm_compiler.commands = @echo compiling[asm] ${QMAKE_FILE_IN} && $$asm_compiler.commands
+    QMAKE_EXTRA_COMPILERS += asm_compiler
+
+    ASM_SOURCES += jit/JITStubsMSVC64.asm
+}
+
 HEADERS += $$files(*.h, true)
 
 *sh4* {

Modified: trunk/Source/_javascript_Core/jit/JITStubs.cpp (144042 => 144043)


--- trunk/Source/_javascript_Core/jit/JITStubs.cpp	2013-02-26 13:01:59 UTC (rev 144042)
+++ trunk/Source/_javascript_Core/jit/JITStubs.cpp	2013-02-26 13:04:28 UTC (rev 144043)
@@ -437,6 +437,13 @@
     "ret" "\n"
 );
 
+#elif COMPILER(MSVC) && CPU(X86_64)
+
+// These ASSERTs remind you that, if you change the layout of JITStackFrame, you
+// need to change the assembly trampolines in JITStubsMSVC64.asm to match.
+COMPILE_ASSERT(offsetof(struct JITStackFrame, code) % 16 == 0x0, JITStackFrame_maintains_16byte_stack_alignment);
+COMPILE_ASSERT(offsetof(struct JITStackFrame, savedRBX) == 0x58, JITStackFrame_stub_argument_space_matches_ctiTrampoline);
+
 #else
     #error "JIT not supported on this platform."
 #endif

Modified: trunk/Source/_javascript_Core/jit/JITStubs.h (144042 => 144043)


--- trunk/Source/_javascript_Core/jit/JITStubs.h	2013-02-26 13:01:59 UTC (rev 144042)
+++ trunk/Source/_javascript_Core/jit/JITStubs.h	2013-02-26 13:04:28 UTC (rev 144043)
@@ -87,7 +87,7 @@
     ArrayAllocationProfile* arrayAllocationProfile() { return static_cast<ArrayAllocationProfile*>(asPointer); }
 };
     
-#if CPU(X86_64)
+#if !OS(WINDOWS) && CPU(X86_64)
 struct JITStackFrame {
     void* reserved; // Unused
     JITStubArg args[6];
@@ -111,6 +111,34 @@
     // When JIT code makes a call, it pushes its return address just below the rest of the stack.
     ReturnAddressPtr* returnAddressSlot() { return reinterpret_cast<ReturnAddressPtr*>(this) - 1; }
 };
+#elif OS(WINDOWS) && CPU(X86_64)
+struct JITStackFrame {
+    void* shadow[4]; // Shadow space reserved for a callee's parameters home addresses
+    void* reserved; // Unused, also maintains the 16-bytes stack alignment
+    JITStubArg args[6];
+
+    void* savedRBX;
+    void* savedR15;
+    void* savedR14;
+    void* savedR13;
+    void* savedR12;
+    void* savedRBP;
+    void* savedRIP;
+
+    // Home addresses for our register passed parameters
+    // http://msdn.microsoft.com/en-us/library/ew5tede7.aspx
+    void* code;
+    JSStack* stack;
+    CallFrame* callFrame;
+    void* unused1;
+
+    // Passed on the stack
+    void* unused2;
+    JSGlobalData* globalData;
+
+    // When JIT code makes a call, it pushes its return address just below the rest of the stack.
+    ReturnAddressPtr* returnAddressSlot() { return reinterpret_cast<ReturnAddressPtr*>(this) - 1; }
+};
 #elif CPU(X86)
 #if COMPILER(MSVC) || (OS(WINDOWS) && COMPILER(GCC))
 #pragma pack(push)

Added: trunk/Source/_javascript_Core/jit/JITStubsMSVC64.asm (0 => 144043)


--- trunk/Source/_javascript_Core/jit/JITStubsMSVC64.asm	                        (rev 0)
+++ trunk/Source/_javascript_Core/jit/JITStubsMSVC64.asm	2013-02-26 13:04:28 UTC (rev 144043)
@@ -0,0 +1,84 @@
+;/*
+; Copyright (C) 2013 Digia Plc. and/or its subsidiary(-ies)
+;
+; Redistribution and use in source and binary forms, with or without
+; modification, are permitted provided that the following conditions
+; are met:
+; 1. Redistributions of source code must retain the above copyright
+;    notice, this list of conditions and the following disclaimer.
+; 2. Redistributions in binary form must reproduce the above copyright
+;    notice, this list of conditions and the following disclaimer in the
+;    documentation and/or other materials provided with the distribution.
+;
+; THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+; PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+; EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+; PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+; OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+; OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+;*/
+
+EXTERN cti_vm_throw : near
+PUBLIC ctiTrampoline
+PUBLIC ctiVMThrowTrampoline
+PUBLIC ctiOpThrowNotCaught
+
+_TEXT   SEGMENT
+
+ctiTrampoline PROC
+    ; Dump register parameters to their home address
+    mov qword ptr[rsp+20h], r9
+    mov qword ptr[rsp+18h], r8
+    mov qword ptr[rsp+10h], rdx
+    mov qword ptr[rsp+8h], rcx
+
+    push rbp
+    mov rbp, rsp
+    push r12
+    push r13
+    push r14
+    push r15
+    push rbx
+
+    ; Decrease rsp to point to the start of our JITStackFrame
+    sub rsp, 58h
+    mov r12, 512
+    mov r14, 0FFFF000000000000h
+    mov r15, 0FFFF000000000002h
+    mov r13, r8
+    call rcx
+    add rsp, 58h
+    pop rbx
+    pop r15
+    pop r14
+    pop r13
+    pop r12
+    pop rbp
+    ret
+ctiTrampoline ENDP
+
+ctiVMThrowTrampoline PROC
+    mov rcx, rsp
+    call cti_vm_throw
+    int 3
+ctiVMThrowTrampoline ENDP
+
+ctiOpThrowNotCaught PROC
+    add rsp, 58h
+    pop rbx
+    pop r15
+    pop r14
+    pop r13
+    pop r12
+    pop rbp
+    ret
+ctiOpThrowNotCaught ENDP
+
+_TEXT   ENDS
+
+END
\ No newline at end of file

Modified: trunk/Source/_javascript_Core/jit/JSInterfaceJIT.h (144042 => 144043)


--- trunk/Source/_javascript_Core/jit/JSInterfaceJIT.h	2013-02-26 13:01:59 UTC (rev 144042)
+++ trunk/Source/_javascript_Core/jit/JSInterfaceJIT.h	2013-02-26 13:04:28 UTC (rev 144043)
@@ -57,22 +57,26 @@
 #if CPU(X86_64)
         static const RegisterID returnValueRegister = X86Registers::eax;
         static const RegisterID cachedResultRegister = X86Registers::eax;
+#if !OS(WINDOWS)
         static const RegisterID firstArgumentRegister = X86Registers::edi;
-        
+#else
+        static const RegisterID firstArgumentRegister = X86Registers::ecx;
+#endif
+
 #if ENABLE(VALUE_PROFILER)
         static const RegisterID bucketCounterRegister = X86Registers::r10;
 #endif
-        
+
         static const RegisterID timeoutCheckRegister = X86Registers::r12;
         static const RegisterID callFrameRegister = X86Registers::r13;
         static const RegisterID tagTypeNumberRegister = X86Registers::r14;
         static const RegisterID tagMaskRegister = X86Registers::r15;
-        
+
         static const RegisterID regT0 = X86Registers::eax;
         static const RegisterID regT1 = X86Registers::edx;
         static const RegisterID regT2 = X86Registers::ecx;
         static const RegisterID regT3 = X86Registers::ebx;
-        
+
         static const FPRegisterID fpRegT0 = X86Registers::xmm0;
         static const FPRegisterID fpRegT1 = X86Registers::xmm1;
         static const FPRegisterID fpRegT2 = X86Registers::xmm2;

Modified: trunk/Source/_javascript_Core/jit/ThunkGenerators.cpp (144042 => 144043)


--- trunk/Source/_javascript_Core/jit/ThunkGenerators.cpp	2013-02-26 13:01:59 UTC (rev 144042)
+++ trunk/Source/_javascript_Core/jit/ThunkGenerators.cpp	2013-02-26 13:04:28 UTC (rev 144043)
@@ -264,6 +264,7 @@
     jit.peek(JSInterfaceJIT::regT1);
     jit.emitPutToCallFrameHeader(JSInterfaceJIT::regT1, JSStack::ReturnPC);
 
+#if !OS(WINDOWS)
     // Calling convention:      f(edi, esi, edx, ecx, ...);
     // Host function signature: f(ExecState*);
     jit.move(JSInterfaceJIT::callFrameRegister, X86Registers::edi);
@@ -276,7 +277,22 @@
     jit.call(JSInterfaceJIT::Address(X86Registers::r9, executableOffsetToFunction));
 
     jit.addPtr(JSInterfaceJIT::TrustedImm32(16 - sizeof(int64_t)), JSInterfaceJIT::stackPointerRegister);
+#else
+    // Calling convention:      f(ecx, edx, r8, r9, ...);
+    // Host function signature: f(ExecState*);
+    jit.move(JSInterfaceJIT::callFrameRegister, X86Registers::ecx);
 
+    // Leave space for the callee parameter home addresses and align the stack.
+    jit.subPtr(JSInterfaceJIT::TrustedImm32(4 * sizeof(int64_t) + 16 - sizeof(int64_t)), JSInterfaceJIT::stackPointerRegister);
+
+    jit.emitGetFromCallFrameHeaderPtr(JSStack::Callee, X86Registers::edx);
+    jit.loadPtr(JSInterfaceJIT::Address(X86Registers::edx, JSFunction::offsetOfExecutable()), X86Registers::r9);
+    jit.move(JSInterfaceJIT::regT0, JSInterfaceJIT::callFrameRegister); // Eagerly restore caller frame register to avoid loading from stack.
+    jit.call(JSInterfaceJIT::Address(X86Registers::r9, executableOffsetToFunction));
+
+    jit.addPtr(JSInterfaceJIT::TrustedImm32(4 * sizeof(int64_t) + 16 - sizeof(int64_t)), JSInterfaceJIT::stackPointerRegister);
+#endif
+
 #elif CPU(ARM)
     // Load caller frame's scope chain into this callframe so that whatever we call can
     // get to its global data.

Modified: trunk/Source/_javascript_Core/yarr/YarrJIT.cpp (144042 => 144043)


--- trunk/Source/_javascript_Core/yarr/YarrJIT.cpp	2013-02-26 13:01:59 UTC (rev 144042)
+++ trunk/Source/_javascript_Core/yarr/YarrJIT.cpp	2013-02-26 13:04:28 UTC (rev 144043)
@@ -87,10 +87,20 @@
     static const RegisterID returnRegister = X86Registers::eax;
     static const RegisterID returnRegister2 = X86Registers::edx;
 #elif CPU(X86_64)
+#if !OS(WINDOWS)
     static const RegisterID input = X86Registers::edi;
     static const RegisterID index = X86Registers::esi;
     static const RegisterID length = X86Registers::edx;
     static const RegisterID output = X86Registers::ecx;
+#else
+    // If the return value doesn't fit in 64bits, its destination is pointed by rcx and the parameters are shifted.
+    // http://msdn.microsoft.com/en-us/library/7572ztz4.aspx
+    COMPILE_ASSERT(sizeof(MatchResult) > sizeof(void*), MatchResult_does_not_fit_in_64bits);
+    static const RegisterID input = X86Registers::edx;
+    static const RegisterID index = X86Registers::r8;
+    static const RegisterID length = X86Registers::r9;
+    static const RegisterID output = X86Registers::r10;
+#endif
 
     static const RegisterID regT0 = X86Registers::eax;
     static const RegisterID regT1 = X86Registers::ebx;
@@ -2514,6 +2524,10 @@
         push(X86Registers::ebp);
         move(stackPointerRegister, X86Registers::ebp);
         push(X86Registers::ebx);
+#if OS(WINDOWS)
+        if (compileMode == IncludeSubpatterns)
+            loadPtr(Address(X86Registers::ebp, 6 * sizeof(void*)), output);
+#endif
 #elif CPU(X86)
         push(X86Registers::ebp);
         move(stackPointerRegister, X86Registers::ebp);
@@ -2552,6 +2566,12 @@
     void generateReturn()
     {
 #if CPU(X86_64)
+#if OS(WINDOWS)
+        // Store the return value in the allocated space pointed by rcx.
+        store64(returnRegister, Address(X86Registers::ecx));
+        store64(returnRegister2, Address(X86Registers::ecx, sizeof(void*)));
+        move(X86Registers::ecx, returnRegister);
+#endif
         pop(X86Registers::ebx);
         pop(X86Registers::ebp);
 #elif CPU(X86)

Modified: trunk/Source/WTF/ChangeLog (144042 => 144043)


--- trunk/Source/WTF/ChangeLog	2013-02-26 13:01:59 UTC (rev 144042)
+++ trunk/Source/WTF/ChangeLog	2013-02-26 13:04:28 UTC (rev 144043)
@@ -1,3 +1,12 @@
+2013-02-26  Jocelyn Turcotte  <[email protected]>
+
+        Implement JIT on Windows 64 bits
+        https://bugs.webkit.org/show_bug.cgi?id=107965
+
+        Reviewed by Simon Hausmann.
+
+        * wtf/Platform.h:
+
 2013-02-26  Oliver Hunt  <[email protected]>
 
         Appease static analyzer warning about null string passed to strlen

Modified: trunk/Source/WTF/wtf/Platform.h (144042 => 144043)


--- trunk/Source/WTF/wtf/Platform.h	2013-02-26 13:01:59 UTC (rev 144042)
+++ trunk/Source/WTF/wtf/Platform.h	2013-02-26 13:04:28 UTC (rev 144043)
@@ -756,12 +756,6 @@
 #define ENABLE_JIT 0
 #endif
 
-/* JIT is not implemented for Windows 64-bit */
-#if !defined(ENABLE_JIT) && OS(WINDOWS) && CPU(X86_64)
-#define ENABLE_JIT 0
-#define ENABLE_YARR_JIT 0
-#endif
-
 #if !defined(ENABLE_JIT) && CPU(SH4) && PLATFORM(QT)
 #define ENABLE_JIT 1
 #endif
@@ -906,7 +900,7 @@
 /* Pick which allocator to use; we only need an executable allocator if the assembler is compiled in.
    On x86-64 we use a single fixed mmap, on other platforms we mmap on demand. */
 #if ENABLE(ASSEMBLER)
-#if CPU(X86_64) || PLATFORM(IOS)
+#if CPU(X86_64) && !OS(WINDOWS) || PLATFORM(IOS)
 #define ENABLE_EXECUTABLE_ALLOCATOR_FIXED 1
 #else
 #define ENABLE_EXECUTABLE_ALLOCATOR_DEMAND 1
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to