| /* This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| |
| // A short test program with which to experiment with the assembler. |
| |
| //satisfies CPU(X86_64) |
| //#define WTF_CPU_X86_64 |
| |
| // satisfies ENABLE(ASSEMBLER) |
| #define ENABLE_ASSEMBLER 1 |
| |
| // satisfies ENABLE(JIT) |
| #define ENABLE_JIT 1 |
| |
| #define USE_SYSTEM_MALLOC 1 |
| // leads to FORCE_SYSTEM_MALLOC in wtf/FastMalloc.cpp |
| |
| #include <jit/ExecutableAllocator.h> |
| #include <assembler/LinkBuffer.h> |
| #include <assembler/CodeLocation.h> |
| #include <assembler/RepatchBuffer.h> |
| |
| #include <assembler/MacroAssembler.h> |
| |
| #include <stdio.h> |
| |
| ///////////////////////////////////////////////////////////////// |
| // Temporary scaffolding for selecting the arch |
| #undef ARCH_x86 |
| #undef ARCH_amd64 |
| #undef ARCH_arm |
| |
| #if defined(__APPLE__) && defined(__i386__) |
| # define ARCH_x86 1 |
| #elif defined(__APPLE__) && defined(__x86_64__) |
| # define ARCH_amd64 1 |
| #elif defined(__linux__) && defined(__i386__) |
| # define ARCH_x86 1 |
| #elif defined(__linux__) && defined(__x86_64__) |
| # define ARCH_amd64 1 |
| #elif defined(__linux__) && defined(__arm__) |
| # define ARCH_arm 1 |
| #elif defined(_MSC_VER) && defined(_M_IX86) |
| # define ARCH_x86 1 |
| #endif |
| ///////////////////////////////////////////////////////////////// |
| |
| // just somewhere convenient to put a breakpoint, before |
| // running gdb |
| #if WTF_COMPILER_GCC |
| __attribute__((noinline)) |
| #endif |
| void pre_run ( void ) { } |
| |
| ///////////////////////////////////////////////////////////////// |
| //// test1 (simple straight line code) |
| #if WTF_COMPILER_GCC |
| |
| void test1 ( void ) |
| { |
| printf("\n------------ Test 1 (straight line code) ------------\n\n" ); |
| |
| // Create new assembler |
| JSC::MacroAssembler* am = new JSC::MacroAssembler(); |
| |
| #if defined(ARCH_amd64) |
| JSC::X86Registers::RegisterID areg = JSC::X86Registers::r15; |
| // dump some instructions into it |
| // xor %r15,%r15 |
| // add $0x7b,%r15 |
| // add $0x141,%r15 |
| // retq |
| am->xorPtr(areg,areg); |
| am->addPtr(JSC::MacroAssembler::Imm32(123), areg); |
| am->addPtr(JSC::MacroAssembler::Imm32(321), areg); |
| am->ret(); |
| #endif |
| |
| #if defined(ARCH_x86) |
| JSC::X86Registers::RegisterID areg = JSC::X86Registers::edi; |
| // dump some instructions into it |
| // xor %edi,%edi |
| // add $0x7b,%edi |
| // add $0x141,%edi |
| // ret |
| am->xorPtr(areg,areg); |
| am->addPtr(JSC::MacroAssembler::Imm32(123), areg); |
| am->addPtr(JSC::MacroAssembler::Imm32(321), areg); |
| am->ret(); |
| #endif |
| |
| #if defined(ARCH_arm) |
| JSC::ARMRegisters::RegisterID areg = JSC::ARMRegisters::r8; |
| // eors r8, r8, r8 |
| // adds r8, r8, #123 ; 0x7b |
| // mov r3, #256 ; 0x100 |
| // orr r3, r3, #65 ; 0x41 |
| // adds r8, r8, r3 |
| // mov pc, lr |
| am->xorPtr(areg,areg); |
| am->addPtr(JSC::MacroAssembler::Imm32(123), areg); |
| am->addPtr(JSC::MacroAssembler::Imm32(321), areg); |
| am->ret(); |
| #endif |
| |
| // prepare a link buffer, into which we can copy the completed insns |
| JSC::ExecutableAllocator* eal = new JSC::ExecutableAllocator(); |
| |
| // intermediate step .. get the pool suited for the size of code in 'am' |
| //WTF::PassRefPtr<JSC::ExecutablePool> ep = eal->poolForSize( am->size() ); |
| JSC::ExecutablePool* ep = eal->poolForSize( am->size() ); |
| |
| // constructor for LinkBuffer asks ep to allocate r-x memory, |
| // then copies it there. |
| JSC::LinkBuffer patchBuffer(am, ep, JSC::METHOD_CODE); |
| |
| // finalize |
| JSC::MacroAssemblerCodeRef cr = patchBuffer.finalizeCode(); |
| |
| // cr now holds a pointer to the final runnable code. |
| void* entry = cr.m_code.executableAddress(); |
| |
| printf("disas %p %p\n", |
| entry, (char*)entry + cr.m_size); |
| pre_run(); |
| |
| unsigned long result = 0x55555555; |
| |
| #if defined(ARCH_amd64) |
| // call the generated piece of code. It puts its result in r15. |
| __asm__ __volatile__( |
| "callq *%1" "\n\t" |
| "movq %%r15, %0" "\n" |
| :/*out*/ "=r"(result) |
| :/*in*/ "r"(entry) |
| :/*trash*/ "r15","cc" |
| ); |
| #endif |
| #if defined(ARCH_x86) |
| // call the generated piece of code. It puts its result in edi. |
| __asm__ __volatile__( |
| "calll *%1" "\n\t" |
| "movl %%edi, %0" "\n" |
| :/*out*/ "=r"(result) |
| :/*in*/ "r"(entry) |
| :/*trash*/ "edi","cc" |
| ); |
| #endif |
| #if defined(ARCH_arm) |
| // call the generated piece of code. It puts its result in r8. |
| __asm__ __volatile__( |
| "blx %1" "\n\t" |
| "mov %0, %%r8" "\n" |
| :/*out*/ "=r"(result) |
| :/*in*/ "r"(entry) |
| :/*trash*/ "r8","cc" |
| ); |
| #endif |
| |
| printf("\n"); |
| printf("value computed is %lu (expected 444)\n", result); |
| printf("\n"); |
| |
| delete eal; |
| delete am; |
| } |
| |
| #endif /* WTF_COMPILER_GCC */ |
| |
| ///////////////////////////////////////////////////////////////// |
| //// test2 (a simple counting-down loop) |
| #if WTF_COMPILER_GCC |
| |
| void test2 ( void ) |
| { |
| printf("\n------------ Test 2 (mini loop) ------------\n\n" ); |
| |
| // Create new assembler |
| JSC::MacroAssembler* am = new JSC::MacroAssembler(); |
| |
| #if defined(ARCH_amd64) |
| JSC::X86Registers::RegisterID areg = JSC::X86Registers::r15; |
| // xor %r15,%r15 |
| // add $0x7b,%r15 |
| // add $0x141,%r15 |
| // sub $0x1,%r15 |
| // mov $0x0,%r11 |
| // cmp %r11,%r15 |
| // jne 0x7ff6d3e6a00e |
| // retq |
| // so r15 always winds up being zero |
| am->xorPtr(areg,areg); |
| am->addPtr(JSC::MacroAssembler::Imm32(123), areg); |
| am->addPtr(JSC::MacroAssembler::Imm32(321), areg); |
| |
| JSC::MacroAssembler::Label loopHeadLabel(am); |
| am->subPtr(JSC::MacroAssembler::Imm32(1), areg); |
| |
| JSC::MacroAssembler::Jump j |
| = am->branchPtr(JSC::MacroAssembler::NotEqual, |
| areg, JSC::MacroAssembler::ImmPtr(0)); |
| j.linkTo(loopHeadLabel, am); |
| |
| am->ret(); |
| #endif |
| |
| #if defined(ARCH_x86) |
| JSC::X86Registers::RegisterID areg = JSC::X86Registers::edi; |
| // xor %edi,%edi |
| // add $0x7b,%edi |
| // add $0x141,%edi |
| // sub $0x1,%edi |
| // test %edi,%edi |
| // jne 0xf7f9700b |
| // ret |
| // so edi always winds up being zero |
| am->xorPtr(areg,areg); |
| am->addPtr(JSC::MacroAssembler::Imm32(123), areg); |
| am->addPtr(JSC::MacroAssembler::Imm32(321), areg); |
| |
| JSC::MacroAssembler::Label loopHeadLabel(am); |
| am->subPtr(JSC::MacroAssembler::Imm32(1), areg); |
| |
| JSC::MacroAssembler::Jump j |
| = am->branchPtr(JSC::MacroAssembler::NotEqual, |
| areg, JSC::MacroAssembler::ImmPtr(0)); |
| j.linkTo(loopHeadLabel, am); |
| |
| am->ret(); |
| #endif |
| |
| #if defined(ARCH_arm) |
| JSC::ARMRegisters::RegisterID areg = JSC::ARMRegisters::r8; |
| // eors r8, r8, r8 |
| // adds r8, r8, #123 ; 0x7b |
| // mov r3, #256 ; 0x100 |
| // orr r3, r3, #65 ; 0x41 |
| // adds r8, r8, r3 |
| // subs r8, r8, #1 ; 0x1 |
| // ldr r3, [pc, #8] ; 0x40026028 |
| // cmp r8, r3 |
| // bne 0x40026014 |
| // mov pc, lr |
| // andeq r0, r0, r0 // DATA (0) |
| // andeq r0, r0, r4, lsl r0 // DATA (?? what's this for?) |
| // so r8 always winds up being zero |
| am->xorPtr(areg,areg); |
| am->addPtr(JSC::MacroAssembler::Imm32(123), areg); |
| am->addPtr(JSC::MacroAssembler::Imm32(321), areg); |
| |
| JSC::MacroAssembler::Label loopHeadLabel(am); |
| am->subPtr(JSC::MacroAssembler::Imm32(1), areg); |
| |
| JSC::MacroAssembler::Jump j |
| = am->branchPtr(JSC::MacroAssembler::NotEqual, |
| areg, JSC::MacroAssembler::ImmPtr(0)); |
| j.linkTo(loopHeadLabel, am); |
| |
| am->ret(); |
| #endif |
| |
| // prepare a link buffer, into which we can copy the completed insns |
| JSC::ExecutableAllocator* eal = new JSC::ExecutableAllocator(); |
| |
| // intermediate step .. get the pool suited for the size of code in 'am' |
| //WTF::PassRefPtr<JSC::ExecutablePool> ep = eal->poolForSize( am->size() ); |
| JSC::ExecutablePool* ep = eal->poolForSize( am->size() ); |
| |
| // constructor for LinkBuffer asks ep to allocate r-x memory, |
| // then copies it there. |
| JSC::LinkBuffer patchBuffer(am, ep, JSC::METHOD_CODE); |
| |
| // finalize |
| JSC::MacroAssemblerCodeRef cr = patchBuffer.finalizeCode(); |
| |
| // cr now holds a pointer to the final runnable code. |
| void* entry = cr.m_code.executableAddress(); |
| |
| printf("disas %p %p\n", |
| entry, (char*)entry + cr.m_size); |
| pre_run(); |
| |
| unsigned long result = 0x55555555; |
| |
| #if defined(ARCH_amd64) |
| // call the generated piece of code. It puts its result in r15. |
| __asm__ __volatile__( |
| "callq *%1" "\n\t" |
| "movq %%r15, %0" "\n" |
| :/*out*/ "=r"(result) |
| :/*in*/ "r"(entry) |
| :/*trash*/ "r15","cc" |
| ); |
| #endif |
| #if defined(ARCH_x86) |
| // call the generated piece of code. It puts its result in edi. |
| __asm__ __volatile__( |
| "calll *%1" "\n\t" |
| "movl %%edi, %0" "\n" |
| :/*out*/ "=r"(result) |
| :/*in*/ "r"(entry) |
| :/*trash*/ "edi","cc" |
| ); |
| #endif |
| #if defined(ARCH_arm) |
| // call the generated piece of code. It puts its result in r8. |
| __asm__ __volatile__( |
| "blx %1" "\n\t" |
| "mov %0, %%r8" "\n" |
| :/*out*/ "=r"(result) |
| :/*in*/ "r"(entry) |
| :/*trash*/ "r8","cc" |
| ); |
| #endif |
| |
| printf("\n"); |
| printf("value computed is %lu (expected 0)\n", result); |
| printf("\n"); |
| |
| delete eal; |
| delete am; |
| } |
| |
| #endif /* WTF_COMPILER_GCC */ |
| |
| ///////////////////////////////////////////////////////////////// |
| //// test3 (if-then-else) |
| #if WTF_COMPILER_GCC |
| |
| void test3 ( void ) |
| { |
| printf("\n------------ Test 3 (if-then-else) ------------\n\n" ); |
| |
| // Create new assembler |
| JSC::MacroAssembler* am = new JSC::MacroAssembler(); |
| |
| #if defined(ARCH_amd64) |
| JSC::X86Registers::RegisterID areg = JSC::X86Registers::r15; |
| // mov $0x64,%r15d |
| // mov $0x0,%r11 |
| // cmp %r11,%r15 |
| // jne 0x7ff6d3e6a024 |
| // mov $0x40,%r15d |
| // jmpq 0x7ff6d3e6a02a |
| // mov $0x4,%r15d |
| // retq |
| // so r15 ends up being 4 |
| |
| // put a value in reg |
| am->move(JSC::MacroAssembler::Imm32(100), areg); |
| |
| // test, and conditionally jump to 'else' branch |
| JSC::MacroAssembler::Jump jToElse |
| = am->branchPtr(JSC::MacroAssembler::NotEqual, |
| areg, JSC::MacroAssembler::ImmPtr(0)); |
| |
| // 'then' branch |
| am->move(JSC::MacroAssembler::Imm32(64), areg); |
| JSC::MacroAssembler::Jump jToAfter |
| = am->jump(); |
| |
| // 'else' branch |
| JSC::MacroAssembler::Label elseLbl(am); |
| am->move(JSC::MacroAssembler::Imm32(4), areg); |
| |
| // after |
| JSC::MacroAssembler::Label afterLbl(am); |
| |
| am->ret(); |
| #endif |
| |
| #if defined(ARCH_x86) |
| JSC::X86Registers::RegisterID areg = JSC::X86Registers::edi; |
| // mov $0x64,%edi |
| // test %edi,%edi |
| // jne 0xf7f22017 |
| // mov $0x40,%edi |
| // jmp 0xf7f2201c |
| // mov $0x4,%edi |
| // ret |
| // so edi ends up being 4 |
| |
| // put a value in reg |
| am->move(JSC::MacroAssembler::Imm32(100), areg); |
| |
| // test, and conditionally jump to 'else' branch |
| JSC::MacroAssembler::Jump jToElse |
| = am->branchPtr(JSC::MacroAssembler::NotEqual, |
| areg, JSC::MacroAssembler::ImmPtr(0)); |
| |
| // 'then' branch |
| am->move(JSC::MacroAssembler::Imm32(64), areg); |
| JSC::MacroAssembler::Jump jToAfter |
| = am->jump(); |
| |
| // 'else' branch |
| JSC::MacroAssembler::Label elseLbl(am); |
| am->move(JSC::MacroAssembler::Imm32(4), areg); |
| |
| // after |
| JSC::MacroAssembler::Label afterLbl(am); |
| |
| am->ret(); |
| #endif |
| |
| #if defined(ARCH_arm) |
| JSC::ARMRegisters::RegisterID areg = JSC::ARMRegisters::r8; |
| // mov r8, #100 ; 0x64 |
| // ldr r3, [pc, #20] ; 0x40026020 |
| // cmp r8, r3 |
| // bne 0x40026018 |
| // mov r8, #64 ; 0x40 |
| // b 0x4002601c |
| // mov r8, #4 ; 0x4 |
| // mov pc, lr |
| // andeq r0, r0, r0 // DATA |
| // andeq r0, r0, r8, lsl r0 // DATA |
| // andeq r0, r0, r12, lsl r0 // DATA |
| // ldr r3, [r3, -r3] // DATA |
| // so r8 ends up being 4 |
| |
| // put a value in reg |
| am->move(JSC::MacroAssembler::Imm32(100), areg); |
| |
| // test, and conditionally jump to 'else' branch |
| JSC::MacroAssembler::Jump jToElse |
| = am->branchPtr(JSC::MacroAssembler::NotEqual, |
| areg, JSC::MacroAssembler::ImmPtr(0)); |
| |
| // 'then' branch |
| am->move(JSC::MacroAssembler::Imm32(64), areg); |
| JSC::MacroAssembler::Jump jToAfter |
| = am->jump(); |
| |
| // 'else' branch |
| JSC::MacroAssembler::Label elseLbl(am); |
| am->move(JSC::MacroAssembler::Imm32(4), areg); |
| |
| // after |
| JSC::MacroAssembler::Label afterLbl(am); |
| |
| am->ret(); |
| #endif |
| |
| // set branch targets appropriately |
| jToElse.linkTo(elseLbl, am); |
| jToAfter.linkTo(afterLbl, am); |
| |
| // prepare a link buffer, into which we can copy the completed insns |
| JSC::ExecutableAllocator* eal = new JSC::ExecutableAllocator(); |
| |
| // intermediate step .. get the pool suited for the size of code in 'am' |
| //WTF::PassRefPtr<JSC::ExecutablePool> ep = eal->poolForSize( am->size() ); |
| JSC::ExecutablePool* ep = eal->poolForSize( am->size() ); |
| |
| // constructor for LinkBuffer asks ep to allocate r-x memory, |
| // then copies it there. |
| JSC::LinkBuffer patchBuffer(am, ep, JSC::METHOD_CODE); |
| |
| // finalize |
| JSC::MacroAssemblerCodeRef cr = patchBuffer.finalizeCode(); |
| |
| // cr now holds a pointer to the final runnable code. |
| void* entry = cr.m_code.executableAddress(); |
| |
| printf("disas %p %p\n", |
| entry, (char*)entry + cr.m_size); |
| pre_run(); |
| |
| unsigned long result = 0x55555555; |
| |
| #if defined(ARCH_amd64) |
| // call the generated piece of code. It puts its result in r15. |
| __asm__ __volatile__( |
| "callq *%1" "\n\t" |
| "movq %%r15, %0" "\n" |
| :/*out*/ "=r"(result) |
| :/*in*/ "r"(entry) |
| :/*trash*/ "r15","cc" |
| ); |
| #endif |
| #if defined(ARCH_x86) |
| // call the generated piece of code. It puts its result in edi. |
| __asm__ __volatile__( |
| "calll *%1" "\n\t" |
| "movl %%edi, %0" "\n" |
| :/*out*/ "=r"(result) |
| :/*in*/ "r"(entry) |
| :/*trash*/ "edi","cc" |
| ); |
| #endif |
| #if defined(ARCH_arm) |
| // call the generated piece of code. It puts its result in r8. |
| __asm__ __volatile__( |
| "blx %1" "\n\t" |
| "mov %0, %%r8" "\n" |
| :/*out*/ "=r"(result) |
| :/*in*/ "r"(entry) |
| :/*trash*/ "r8","cc" |
| ); |
| #endif |
| |
| printf("\n"); |
| printf("value computed is %lu (expected 4)\n", result); |
| printf("\n"); |
| |
| delete eal; |
| delete am; |
| } |
| |
| #endif /* WTF_COMPILER_GCC */ |
| |
| ///////////////////////////////////////////////////////////////// |
| //// test4 (callable function) |
| |
| void test4 ( void ) |
| { |
| printf("\n------------ Test 4 (callable fn) ------------\n\n" ); |
| |
| // Create new assembler |
| JSC::MacroAssembler* am = new JSC::MacroAssembler(); |
| |
| #if defined(ARCH_amd64) |
| // ADD FN PROLOGUE/EPILOGUE so as to make a mini-function |
| // push %rbp |
| // mov %rsp,%rbp |
| // push %rbx |
| // push %r12 |
| // push %r13 |
| // push %r14 |
| // push %r15 |
| // xor %rax,%rax |
| // add $0x7b,%rax |
| // add $0x141,%rax |
| // pop %r15 |
| // pop %r14 |
| // pop %r13 |
| // pop %r12 |
| // pop %rbx |
| // mov %rbp,%rsp |
| // pop %rbp |
| // retq |
| // callable as a normal function, returns 444 |
| |
| JSC::X86Registers::RegisterID rreg = JSC::X86Registers::eax; |
| am->push(JSC::X86Registers::ebp); |
| am->move(JSC::X86Registers::esp, JSC::X86Registers::ebp); |
| am->push(JSC::X86Registers::ebx); |
| am->push(JSC::X86Registers::r12); |
| am->push(JSC::X86Registers::r13); |
| am->push(JSC::X86Registers::r14); |
| am->push(JSC::X86Registers::r15); |
| |
| am->xorPtr(rreg,rreg); |
| am->addPtr(JSC::MacroAssembler::Imm32(123), rreg); |
| am->addPtr(JSC::MacroAssembler::Imm32(321), rreg); |
| |
| am->pop(JSC::X86Registers::r15); |
| am->pop(JSC::X86Registers::r14); |
| am->pop(JSC::X86Registers::r13); |
| am->pop(JSC::X86Registers::r12); |
| am->pop(JSC::X86Registers::ebx); |
| am->move(JSC::X86Registers::ebp, JSC::X86Registers::esp); |
| am->pop(JSC::X86Registers::ebp); |
| am->ret(); |
| #endif |
| |
| #if defined(ARCH_x86) |
| // ADD FN PROLOGUE/EPILOGUE so as to make a mini-function |
| // push %ebp |
| // mov %esp,%ebp |
| // push %ebx |
| // push %esi |
| // push %edi |
| // xor %eax,%eax |
| // add $0x7b,%eax |
| // add $0x141,%eax |
| // pop %edi |
| // pop %esi |
| // pop %ebx |
| // mov %ebp,%esp |
| // pop %ebp |
| // ret |
| // callable as a normal function, returns 444 |
| |
| JSC::X86Registers::RegisterID rreg = JSC::X86Registers::eax; |
| |
| am->push(JSC::X86Registers::ebp); |
| am->move(JSC::X86Registers::esp, JSC::X86Registers::ebp); |
| am->push(JSC::X86Registers::ebx); |
| am->push(JSC::X86Registers::esi); |
| am->push(JSC::X86Registers::edi); |
| |
| am->xorPtr(rreg,rreg); |
| am->addPtr(JSC::MacroAssembler::Imm32(123), rreg); |
| am->addPtr(JSC::MacroAssembler::Imm32(321), rreg); |
| |
| am->pop(JSC::X86Registers::edi); |
| am->pop(JSC::X86Registers::esi); |
| am->pop(JSC::X86Registers::ebx); |
| am->move(JSC::X86Registers::ebp, JSC::X86Registers::esp); |
| am->pop(JSC::X86Registers::ebp); |
| am->ret(); |
| #endif |
| |
| #if defined(ARCH_arm) |
| // ADD FN PROLOGUE/EPILOGUE so as to make a mini-function |
| // push {r4} ; (str r4, [sp, #-4]!) |
| // push {r5} ; (str r5, [sp, #-4]!) |
| // push {r6} ; (str r6, [sp, #-4]!) |
| // push {r7} ; (str r7, [sp, #-4]!) |
| // push {r8} ; (str r8, [sp, #-4]!) |
| // push {r9} ; (str r9, [sp, #-4]!) |
| // push {r10} ; (str r10, [sp, #-4]!) |
| // push {r11} ; (str r11, [sp, #-4]!) |
| // eors r0, r0, r0 |
| // adds r0, r0, #123 ; 0x7b |
| // mov r3, #256 ; 0x100 |
| // orr r3, r3, #65 ; 0x41 |
| // adds r0, r0, r3 |
| // pop {r11} ; (ldr r11, [sp], #4) |
| // pop {r10} ; (ldr r10, [sp], #4) |
| // pop {r9} ; (ldr r9, [sp], #4) |
| // pop {r8} ; (ldr r8, [sp], #4) |
| // pop {r7} ; (ldr r7, [sp], #4) |
| // pop {r6} ; (ldr r6, [sp], #4) |
| // pop {r5} ; (ldr r5, [sp], #4) |
| // pop {r4} ; (ldr r4, [sp], #4) |
| // mov pc, lr |
| // callable as a normal function, returns 444 |
| |
| JSC::ARMRegisters::RegisterID rreg = JSC::ARMRegisters::r0; |
| |
| am->push(JSC::ARMRegisters::r4); |
| am->push(JSC::ARMRegisters::r5); |
| am->push(JSC::ARMRegisters::r6); |
| am->push(JSC::ARMRegisters::r7); |
| am->push(JSC::ARMRegisters::r8); |
| am->push(JSC::ARMRegisters::r9); |
| am->push(JSC::ARMRegisters::r10); |
| am->push(JSC::ARMRegisters::r11); |
| |
| am->xorPtr(rreg,rreg); |
| am->addPtr(JSC::MacroAssembler::Imm32(123), rreg); |
| am->addPtr(JSC::MacroAssembler::Imm32(321), rreg); |
| |
| am->pop(JSC::ARMRegisters::r11); |
| am->pop(JSC::ARMRegisters::r10); |
| am->pop(JSC::ARMRegisters::r9); |
| am->pop(JSC::ARMRegisters::r8); |
| am->pop(JSC::ARMRegisters::r7); |
| am->pop(JSC::ARMRegisters::r6); |
| am->pop(JSC::ARMRegisters::r5); |
| am->pop(JSC::ARMRegisters::r4); |
| |
| am->ret(); |
| #endif |
| |
| // prepare a link buffer, into which we can copy the completed insns |
| JSC::ExecutableAllocator* eal = new JSC::ExecutableAllocator(); |
| |
| // intermediate step .. get the pool suited for the size of code in 'am' |
| //WTF::PassRefPtr<JSC::ExecutablePool> ep = eal->poolForSize( am->size() ); |
| JSC::ExecutablePool* ep = eal->poolForSize( am->size() ); |
| |
| // constructor for LinkBuffer asks ep to allocate r-x memory, |
| // then copies it there. |
| JSC::LinkBuffer patchBuffer(am, ep, JSC::METHOD_CODE); |
| |
| // now fix up any branches/calls |
| //JSC::FunctionPtr target = JSC::FunctionPtr::FunctionPtr( &cube ); |
| |
| // finalize |
| JSC::MacroAssemblerCodeRef cr = patchBuffer.finalizeCode(); |
| |
| // cr now holds a pointer to the final runnable code. |
| void* entry = cr.m_code.executableAddress(); |
| |
| printf("disas %p %p\n", |
| entry, (char*)entry + cr.m_size); |
| pre_run(); |
| |
| // call the function |
| unsigned long (*fn)(void) = (unsigned long (*)())entry; |
| unsigned long result = fn(); |
| |
| printf("\n"); |
| printf("value computed is %lu (expected 444)\n", result); |
| printf("\n"); |
| |
| delete eal; |
| delete am; |
| } |
| |
| |
| ///////////////////////////////////////////////////////////////// |
| //// test5 (call in, out, repatch) |
| |
| // a function which we will call from the JIT generated code |
| unsigned long cube ( unsigned long x ) { return x * x * x; } |
| unsigned long square ( unsigned long x ) { return x * x; } |
| |
| void test5 ( void ) |
| { |
| printf("\n--------- Test 5 (call in, out, repatch) ---------\n\n" ); |
| |
| // Create new assembler |
| JSC::MacroAssembler* am = new JSC::MacroAssembler(); |
| JSC::MacroAssembler::Call cl; |
| ptrdiff_t offset_of_call_insn; |
| |
| #if defined(ARCH_amd64) |
| // ADD FN PROLOGUE/EPILOGUE so as to make a mini-function |
| // and then call a non-JIT-generated helper from within |
| // this code |
| // push %rbp |
| // mov %rsp,%rbp |
| // push %rbx |
| // push %r12 |
| // push %r13 |
| // push %r14 |
| // push %r15 |
| // mov $0x9,%edi |
| // mov $0x40187e,%r11 |
| // callq *%r11 |
| // pop %r15 |
| // pop %r14 |
| // pop %r13 |
| // pop %r12 |
| // pop %rbx |
| // mov %rbp,%rsp |
| // pop %rbp |
| // retq |
| JSC::MacroAssembler::Label startOfFnLbl(am); |
| am->push(JSC::X86Registers::ebp); |
| am->move(JSC::X86Registers::esp, JSC::X86Registers::ebp); |
| am->push(JSC::X86Registers::ebx); |
| am->push(JSC::X86Registers::r12); |
| am->push(JSC::X86Registers::r13); |
| am->push(JSC::X86Registers::r14); |
| am->push(JSC::X86Registers::r15); |
| |
| // let's compute cube(9). Move $9 to the first arg reg. |
| am->move(JSC::MacroAssembler::Imm32(9), JSC::X86Registers::edi); |
| cl = am->JSC::MacroAssembler::call(); |
| |
| // result is now in %rax. Leave it ther and just return. |
| |
| am->pop(JSC::X86Registers::r15); |
| am->pop(JSC::X86Registers::r14); |
| am->pop(JSC::X86Registers::r13); |
| am->pop(JSC::X86Registers::r12); |
| am->pop(JSC::X86Registers::ebx); |
| am->move(JSC::X86Registers::ebp, JSC::X86Registers::esp); |
| am->pop(JSC::X86Registers::ebp); |
| am->ret(); |
| |
| offset_of_call_insn |
| = am->JSC::MacroAssembler::differenceBetween(startOfFnLbl, cl); |
| if (0) printf("XXXXXXXX offset = %lu\n", offset_of_call_insn); |
| #endif |
| |
| #if defined(ARCH_x86) |
| // ADD FN PROLOGUE/EPILOGUE so as to make a mini-function |
| // and then call a non-JIT-generated helper from within |
| // this code |
| // push %ebp |
| // mov %esp,%ebp |
| // push %ebx |
| // push %esi |
| // push %edi |
| // push $0x9 |
| // call 0x80490e9 <_Z4cubem> |
| // add $0x4,%esp |
| // pop %edi |
| // pop %esi |
| // pop %ebx |
| // mov %ebp,%esp |
| // pop %ebp |
| // ret |
| JSC::MacroAssembler::Label startOfFnLbl(am); |
| am->push(JSC::X86Registers::ebp); |
| am->move(JSC::X86Registers::esp, JSC::X86Registers::ebp); |
| am->push(JSC::X86Registers::ebx); |
| am->push(JSC::X86Registers::esi); |
| am->push(JSC::X86Registers::edi); |
| |
| // let's compute cube(9). Push $9 on the stack. |
| am->push(JSC::MacroAssembler::Imm32(9)); |
| cl = am->JSC::MacroAssembler::call(); |
| am->addPtr(JSC::MacroAssembler::Imm32(4), JSC::X86Registers::esp); |
| // result is now in %eax. Leave it there and just return. |
| |
| am->pop(JSC::X86Registers::edi); |
| am->pop(JSC::X86Registers::esi); |
| am->pop(JSC::X86Registers::ebx); |
| am->move(JSC::X86Registers::ebp, JSC::X86Registers::esp); |
| am->pop(JSC::X86Registers::ebp); |
| am->ret(); |
| |
| offset_of_call_insn |
| = am->JSC::MacroAssembler::differenceBetween(startOfFnLbl, cl); |
| if (0) printf("XXXXXXXX offset = %lu\n", |
| (unsigned long)offset_of_call_insn); |
| #endif |
| |
| #if defined(ARCH_arm) |
| // ADD FN PROLOGUE/EPILOGUE so as to make a mini-function |
| // push {r4} ; (str r4, [sp, #-4]!) |
| // push {r5} ; (str r5, [sp, #-4]!) |
| // push {r6} ; (str r6, [sp, #-4]!) |
| // push {r7} ; (str r7, [sp, #-4]!) |
| // push {r8} ; (str r8, [sp, #-4]!) |
| // push {r9} ; (str r9, [sp, #-4]!) |
| // push {r10} ; (str r10, [sp, #-4]!) |
| // push {r11} ; (str r11, [sp, #-4]!) |
| // eors r0, r0, r0 |
| // adds r0, r0, #123 ; 0x7b |
| // mov r3, #256 ; 0x100 |
| // orr r3, r3, #65 ; 0x41 |
| // adds r0, r0, r3 |
| // pop {r11} ; (ldr r11, [sp], #4) |
| // pop {r10} ; (ldr r10, [sp], #4) |
| // pop {r9} ; (ldr r9, [sp], #4) |
| // pop {r8} ; (ldr r8, [sp], #4) |
| // pop {r7} ; (ldr r7, [sp], #4) |
| // pop {r6} ; (ldr r6, [sp], #4) |
| // pop {r5} ; (ldr r5, [sp], #4) |
| // pop {r4} ; (ldr r4, [sp], #4) |
| // mov pc, lr |
| // callable as a normal function, returns 444 |
| JSC::MacroAssembler::Label startOfFnLbl(am); |
| am->push(JSC::ARMRegisters::r4); |
| am->push(JSC::ARMRegisters::r5); |
| am->push(JSC::ARMRegisters::r6); |
| am->push(JSC::ARMRegisters::r7); |
| am->push(JSC::ARMRegisters::r8); |
| am->push(JSC::ARMRegisters::r9); |
| am->push(JSC::ARMRegisters::r10); |
| am->push(JSC::ARMRegisters::r11); |
| am->push(JSC::ARMRegisters::lr); |
| |
| // let's compute cube(9). Get $9 into r0. |
| am->move(JSC::MacroAssembler::Imm32(9), JSC::ARMRegisters::r0); |
| cl = am->JSC::MacroAssembler::call(); |
| // result is now in r0. Leave it there and just return. |
| |
| am->pop(JSC::ARMRegisters::lr); |
| am->pop(JSC::ARMRegisters::r11); |
| am->pop(JSC::ARMRegisters::r10); |
| am->pop(JSC::ARMRegisters::r9); |
| am->pop(JSC::ARMRegisters::r8); |
| am->pop(JSC::ARMRegisters::r7); |
| am->pop(JSC::ARMRegisters::r6); |
| am->pop(JSC::ARMRegisters::r5); |
| am->pop(JSC::ARMRegisters::r4); |
| am->ret(); |
| |
| offset_of_call_insn |
| = am->JSC::MacroAssembler::differenceBetween(startOfFnLbl, cl); |
| if (0) printf("XXXXXXXX offset = %lu\n", |
| (unsigned long)offset_of_call_insn); |
| #endif |
| |
| // prepare a link buffer, into which we can copy the completed insns |
| JSC::ExecutableAllocator* eal = new JSC::ExecutableAllocator(); |
| |
| // intermediate step .. get the pool suited for the size of code in 'am' |
| //WTF::PassRefPtr<JSC::ExecutablePool> ep = eal->poolForSize( am->size() ); |
| JSC::ExecutablePool* ep = eal->poolForSize( am->size() ); |
| |
| // constructor for LinkBuffer asks ep to allocate r-x memory, |
| // then copies it there. |
| JSC::LinkBuffer patchBuffer(am, ep, JSC::METHOD_CODE); |
| |
| // now fix up any branches/calls |
| JSC::FunctionPtr target = JSC::FunctionPtr::FunctionPtr( &cube ); |
| patchBuffer.link(cl, target); |
| |
| JSC::MacroAssemblerCodeRef cr = patchBuffer.finalizeCode(); |
| |
| // cr now holds a pointer to the final runnable code. |
| void* entry = cr.m_code.executableAddress(); |
| |
| printf("disas %p %p\n", |
| entry, (char*)entry + cr.m_size); |
| |
| |
| pre_run(); |
| |
| printf("\n"); |
| |
| unsigned long (*fn)() = (unsigned long(*)())entry; |
| unsigned long result = fn(); |
| |
| printf("value computed is %lu (expected 729)\n", result); |
| printf("\n"); |
| |
| // now repatch the call in the JITted code to go elsewhere |
| JSC::JITCode jc = JSC::JITCode::JITCode(entry, cr.m_size); |
| JSC::CodeBlock cb = JSC::CodeBlock::CodeBlock(jc); |
| |
| // the address of the call insn, that we want to prod |
| JSC::MacroAssemblerCodePtr cp |
| = JSC::MacroAssemblerCodePtr( ((char*)entry) + offset_of_call_insn ); |
| |
| JSC::RepatchBuffer repatchBuffer(&cb); |
| repatchBuffer.relink( JSC::CodeLocationCall(cp), |
| JSC::FunctionPtr::FunctionPtr( &square )); |
| |
| result = fn(); |
| printf("value computed is %lu (expected 81)\n", result); |
| printf("\n\n"); |
| |
| delete eal; |
| delete am; |
| } |
| |
| ///////////////////////////////////////////////////////////////// |
| |
| int main ( void ) |
| { |
| #if WTF_COMPILER_GCC |
| test1(); |
| test2(); |
| test3(); |
| #endif |
| test4(); |
| test5(); |
| return 0; |
| } |