From: twisti Date: Thu, 11 Jan 2007 14:19:48 +0000 (+0000) Subject: * configure.ac [ENABLE_STATICVM] (AC_CHECK_LIB(dl)): Only perform the X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=commitdiff_plain;h=e124e9a34bd8f6e16de7650f092eb941112d5f1a;p=cacao.git * configure.ac [ENABLE_STATICVM] (AC_CHECK_LIB(dl)): Only perform the check if we want a static VM. (AC_CONFIG_FILES): Added src/vm/jit/arm/Makefile and src/vm/jit/arm/linux/Makefile. * src/vm/jit/Makefile.am (DIST_SUBDIRS): Added arm. --- diff --git a/configure.ac b/configure.ac index db0e9e436..3d5348993 100644 --- a/configure.ac +++ b/configure.ac @@ -22,7 +22,7 @@ dnl along with this program; if not, write to the Free Software dnl Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA dnl 02110-1301, USA. dnl -dnl $Id: configure.ac 6283 2007-01-08 23:49:11Z twisti $ +dnl $Id: configure.ac 6595 2007-01-11 14:19:48Z twisti $ dnl Process this file with autoconf to produce a configure script. @@ -976,7 +976,9 @@ if test x"${WITH_STATIC_CLASSPATH}" = "xyes"; then AM_ICONV_LINK else dnl we need this check for --enable-staticvm, otherwise ltdl can't find dlopen - AC_CHECK_LIB(dl, dlopen,, [AC_MSG_ERROR(cannot find libdl)]) + if test x"${ENABLE_STATICVM}" = "xyes"; then + AC_CHECK_LIB(dl, dlopen,, [AC_MSG_ERROR(cannot find libdl)]) + fi AC_CHECK_HEADERS([ltdl.h],, [AC_MSG_ERROR(cannot find ltdl.h)]) AC_CHECK_LIB(ltdl, lt_dlopen,, [AC_MSG_ERROR(cannot find libltdl)]) @@ -1029,6 +1031,8 @@ AC_CONFIG_FILES([Makefile] [src/vm/jit/alpha/Makefile] [src/vm/jit/alpha/freebsd/Makefile] [src/vm/jit/alpha/linux/Makefile] + [src/vm/jit/arm/Makefile] + [src/vm/jit/arm/linux/Makefile] [src/vm/jit/i386/Makefile] [src/vm/jit/i386/cygwin/Makefile] [src/vm/jit/i386/darwin/Makefile] diff --git a/src/vm/jit/Makefile.am b/src/vm/jit/Makefile.am index 616397159..0dfa665f9 100644 --- a/src/vm/jit/Makefile.am +++ b/src/vm/jit/Makefile.am @@ -28,7 +28,7 @@ ## ## Changes: Edwin Steiner ## -## $Id: Makefile.am 6265 2007-01-02 20:40:57Z edwin $ +## $Id: Makefile.am 6595 2007-01-11 14:19:48Z twisti $ ## Process this file with automake to produce Makefile.in @@ -47,6 +47,7 @@ DIST_SUBDIRS = \ \ intrp \ alpha \ + arm \ i386 \ mips \ parisc \ diff --git a/src/vm/jit/arm/.cvsignore b/src/vm/jit/arm/.cvsignore new file mode 100644 index 000000000..1f83cf9ad --- /dev/null +++ b/src/vm/jit/arm/.cvsignore @@ -0,0 +1,10 @@ +*.a +*.o +*.la +*.lo +.deps +.libs +Makefile +Makefile.in +TAGS +offsets.h diff --git a/src/vm/jit/arm/Makefile.am b/src/vm/jit/arm/Makefile.am new file mode 100644 index 000000000..9a1cfb496 --- /dev/null +++ b/src/vm/jit/arm/Makefile.am @@ -0,0 +1,85 @@ +## src/vm/jit/arm/Makefile.am +## +## Copyright (C) 1996-2005, 2006 R. Grafl, A. Krall, C. Kruegel, +## C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring, +## E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich, +## J. Wenninger, Institut f. Computersprachen - TU Wien +## +## This file is part of CACAO. +## +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License as +## published by the Free Software Foundation; either version 2, or (at +## your option) any later version. +## +## This program is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +## General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +## 02110-1301, USA. +## +## Contact: cacao@cacaojvm.org +## +## Authors: Christian Thalinger +## +## Changes: Michael Starzinger +## +## $Id: Makefile.am 6527 2006-05-09 17:55:18Z twisti $ + +## Process this file with automake to produce Makefile.in + +DIST_SUBDIRS = \ + linux + +SUBDIRS = $(OS_DIR) + +AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_builddir) -I$(top_builddir)/src +AM_CCASFLAGS = $(AM_CPPFLAGS) + +BUILT_SOURCES = offsets.h + +CLEANFILES = offsets.h + +noinst_HEADERS = \ + arch.h \ + machine-instr.h \ + md-asm.h + +noinst_LTLIBRARIES = libarch.la + +if ENABLE_DISASSEMBLER +DISASS_SOURCES = \ + disass.c +endif + +libarch_la_SOURCES = \ + asmpart.S \ + codegen.c \ + codegen.h \ + $(DISASS_SOURCES) \ + emit.c \ + md.c \ + md-abi.c \ + md-abi.h \ + patcher.c + +libarch_la_LIBADD = \ + $(OS_DIR)/libmd.la + +$(srcdir)/asmpart.S: $(top_builddir)/config.h offsets.h + +offsets.h: $(top_builddir)/src/vm/jit/tools/genoffsets $(top_builddir)/config.h + $(top_builddir)/src/vm/jit/tools/genoffsets > offsets.h + + +## Local variables: +## mode: Makefile +## indent-tabs-mode: t +## c-basic-offset: 4 +## tab-width: 8 +## compile-command: "automake --add-missing" +## End: diff --git a/src/vm/jit/arm/arch.h b/src/vm/jit/arm/arch.h new file mode 100644 index 000000000..992d90218 --- /dev/null +++ b/src/vm/jit/arm/arch.h @@ -0,0 +1,117 @@ +/* src/vm/jit/arm/arch.h - architecture defines for arm + + Copyright (C) 1996-2005, 2006 R. Grafl, A. Krall, C. Kruegel, + C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring, + E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich, + J. Wenninger, Institut f. Computersprachen - TU Wien + + This file is part of CACAO. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + + Contact: cacao@cacaojvm.org + + Authors: Michael Starzinger + Christian Thalinger + + $Id: arch.h 6591 2007-01-02 19:14:25Z twisti $ + +*/ + + +#ifndef _ARCH_H +#define _ARCH_H + +/* define architecture features ***********************************************/ + +#define U8_AVAILABLE 1 + +#define USEBUILTINTABLE + +#define SUPPORT_DIVISION 0 +#define SUPPORT_LONG 1 +#if defined(ENABLE_SOFTFLOAT) +# define SUPPORT_FLOAT 0 +# define SUPPORT_DOUBLE 0 +#else +# define SUPPORT_FLOAT 1 +# define SUPPORT_DOUBLE 1 +#endif /* defined(ENABLE_SOFTFLOAT) */ + +#define SUPPORT_I2F 1 +#define SUPPORT_I2D 1 +#define SUPPORT_L2F 0 +#define SUPPORT_L2D 0 + +#define SUPPORT_F2I 1 +#define SUPPORT_F2L 0 +#define SUPPORT_D2I 1 +#define SUPPORT_D2L 0 + +#define SUPPORT_LONG_ADD 1 +#define SUPPORT_LONG_CMP 1 +#define SUPPORT_LONG_CMP_CONST 1 +#define SUPPORT_LONG_LOGICAL 1 +#define SUPPORT_LONG_SHIFT 0 +#define SUPPORT_LONG_MUL 0 +#define SUPPORT_LONG_DIV 0 + +#define SUPPORT_LONG_DIV_POW2 0 +#define SUPPORT_LONG_REM_POW2 0 + +#define SUPPORT_CONST_LOGICAL 0 /* AND, OR, XOR with immediates */ +#define SUPPORT_CONST_MUL 0 /* mutiply with immediate */ +#define SUPPORT_CONST_STORE 0 /* do we support const stores */ +#define SUPPORT_CONST_STORE_ZERO_ONLY 0 /* on some risc machines we can */ + /* only store REG_ZERO */ + +#define HAS_4BYTE_STACKSLOT +#define SUPPORT_COMBINE_INTEGER_REGISTERS +#define SUPPORT_PASS_FLOATARGS_IN_INTREGS + +#define ALIGN_LONGS_IN_MEMORY /* Align Longs and/or Doubles at */ +#define ALIGN_DOUBLES_IN_MEMORY /* 2*Stackslotsize relativ to stackframe */ +/* Memory Positions for not Interface Stackslots (allocate_scratch_registers) */ +/* are not properly aligned in case HAS_4_BYTE_STACKSLOT is not defined! */ +/* For HAS_4_BYTE_STACKSLOT archs no distinction is made between long and */ +/* define SUPPORT_COMBINE_INTEGER_REGISTERS */ + + +/* exceptions *****************************************************************/ + +#define SUPPORT_HARDWARE_DIVIDE_BY_ZERO 0 + + +/* replacement ****************************************************************/ + +#define REPLACEMENT_PATCH_SIZE 4 /* bytes */ +#define REPLACEMENT_STUB_SIZE 5 /* words */ + +#endif /* _ARCH_H */ + + +/* + * These are local overrides for various environment variables in Emacs. + * Please do not remove this and leave it at the end of the file, where + * Emacs will automagically detect them. + * --------------------------------------------------------------------- + * Local variables: + * mode: c + * indent-tabs-mode: t + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff --git a/src/vm/jit/arm/asmpart.S b/src/vm/jit/arm/asmpart.S new file mode 100644 index 000000000..694a5e4ea --- /dev/null +++ b/src/vm/jit/arm/asmpart.S @@ -0,0 +1,471 @@ +/* src/vm/jit/arm/asmpart.S - Java-C interface functions for ARM + + Copyright (C) 1996-2005, 2006 R. Grafl, A. Krall, C. Kruegel, + C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring, + E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich, + J. Wenninger, Institut f. Computersprachen - TU Wien + + This file is part of CACAO. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + + Contact: cacao@cacaojvm.org + + Authors: Michael Starzinger + + Changes: Christian Thalinger + + $Id: asmpart.S 6541 2006-08-22 14:48:01Z twisti $ + +*/ + + +#include "config.h" + +#include "vm/jit/arm/offsets.h" +#include "vm/jit/arm/md-asm.h" + +#include "vm/jit/methodheader.h" + + + .file "asmpart.S" + .text + .align 2 + + +/* export functions ***********************************************************/ + + .globl asm_vm_call_method + .globl asm_vm_call_method_int + .globl asm_vm_call_method_long + .globl asm_vm_call_method_float + .globl asm_vm_call_method_double + .globl asm_vm_call_method_exception_handler + + .globl asm_call_jit_compiler + + .globl asm_handle_exception + .globl asm_handle_nat_exception + + .globl asm_abstractmethoderror + + .globl asm_patcher_wrapper + + .globl asm_cacheflush + + .globl asm_getclassvalues_atomic + .globl asm_criticalsections + + +#if !defined(ENABLE_THREADS) +asm_exceptionptr: + .word _no_threads_exceptionptr +#endif + +asm_jitcompilerptr: + .word asm_call_jit_compiler + +asm_criticalsections: +#if defined(ENABLE_THREADS) + .word _crit_begin + .word _crit_end + .word _crit_restart +#endif + .word 0 + + +/* asm_vm_call_method ********************************************************** + + This function calls a Java-method (which possibly needs compilation) + with up to 4 address parameters. + + This functions calls the JIT-compiler which eventually translates the + method into machine code. + +*******************************************************************************/ + + .align 2 + + .word 0 /* catch type all */ + .word 0 /* handler pc */ + .word 0 /* end pc */ + .word 0 /* start pc */ + .word 1 /* extable size */ + .word 0 /* line number table start */ + .word 0 /* line number table size */ + .word 0 /* FltSave */ + .word 0 /* IntSave */ + .word 0 /* IsLeaf */ + .word 0 /* IsSync */ + .word 0 /* FrameSize */ + .word 0 /* CodeinfoPointer */ + +asm_vm_call_method: +asm_vm_call_method_int: +asm_vm_call_method_long: +/* asm_vm_call_method_float: +asm_vm_call_method_double: */ + SAVE_SCRATCH_REGISTERS /* save our personal scratch regs */ + stmfd sp!, {v1} /* V1 is used to recompute SP ... */ + mov v1, #0 /* ... when using stack arguments */ + ldr ip, asm_jitcompilerptr + str ip, [sp, #-4]! /* store fake address */ + mov mptr, sp /* set method pointer */ + + mov itmp1, a1 /* pass methodinfo* via ITMP1 */ + + cmp a2, #0 /* do we have arguments? */ + ble asm_calljava_copyfinish /* no -> do not care :-) */ + + /* REMEMBER: stack space for arguments is reserved here! */ + /* TODO: we possibly reserve to much here */ + mov v1, a2, lsl #3 /* how much stack do we alloc? */ + sub sp, sp, v1 /* allocate stack for arguments! */ + + mov itmp3, #0 /* stack position */ +asm_calljava_copyloop: /* reorder stack arguments! */ +#if defined(__ARMEL__) + ldr ip, [a3,#offvmargdata] /* get LOW word of argument */ + str ip, [sp, itmp3] + add itmp3, itmp3, #4 + ldr ip, [a3,#offvmargtype] /* is it a 2_WORD_TYPE? */ + tst ip, #1 + ldrne ip, [a3,#offvmargdata + 4] /* yes -> get HIGH word of argument */ + strne ip, [sp, itmp3] + addne itmp3, itmp3, #4 +#else /* defined(__ARMEB__) */ + ldr ip, [a3,#offvmargtype + 4] /* get our item type (it is u8) */ + teq ip, #2 /* is it a TYPE_FLOAT? */ + ldreq ip, [a3,#offvmargdata] /* yes -> get LOW word of float */ + streq ip, [sp, itmp3] + addeq itmp3, itmp3, #4 + beq asm_calljava_copydone + tst ip, #1 /* is it a 2_WORD_TYPE? */ + ldrne ip, [a3,#offvmargdata] /* yes -> get HIGH word of argument */ + strne ip, [sp, itmp3] + addne itmp3, itmp3, #4 + ldr ip, [a3,#offvmargdata + 4] /* get LOW word of argument */ + str ip, [sp, itmp3] + add itmp3, itmp3, #4 +asm_calljava_copydone: +#endif + add a3, a3, #sizevmarg /* next argument block */ + subs a2, a2, #1 + bgt asm_calljava_copyloop + + /* REMEMBER: first four args are passed in regs, take them out again */ + ldmfd sp, {a1, a2, a3, a4} /* load first four args to register */ + cmp v1, #16 /* do we have four arguments? */ + addlt sp, sp, v1 + movlt v1, #0 + addge sp, sp, #16 + subge v1, v1, #16 + +asm_calljava_copyfinish: + /* REMEMBER: do the method call just like in java! */ + ldr ip, [mptr] /* fake virtual function call */ + mov lr, pc + mov pc, ip +fake2: + sub ip, pc, #(fake2 - asm_vm_call_method)+8 + + add sp, sp, v1 /* free stack arguments! */ + add sp, sp, #4 /* free fake address */ + ldmfd sp!, {v1} + RESTORE_SCRATCH_REGS_AND_RETURN /* return to caller, restore regs */ + +asm_vm_call_method_exception_handler: + mov a1, xptr /* exception pointer is arg1 */ + bl builtin_throw_exception /* throw the exception */ + mov res1, #0 /* return NULL */ + mov res2, #0 /* return NULL */ + add sp, sp, v1 /* free stack arguments! */ + add sp, sp, #4 /* free fake address */ + ldmfd sp!, {v1} + RESTORE_SCRATCH_REGS_AND_RETURN /* return to caller, restore regs */ + +asm_vm_call_method_float: + mov a1,#0x51 + b asm_debug +asm_vm_call_method_double: + mov a1,#0x52 + b asm_debug + + +/****************** function asm_call_jit_compiler ***************************** +* * +* Invokes the compiler for untranslated JavaVM methods. * +* What this method does: * +* - save args and LR * +* - fire up jit_compile (pass methodinfo pointer) * +* - try to find out where to write back the new method pointer * +* - restore args and LR * +* - check for exceptions * +* - eventually write back new method pointer * +* - call jit code (wich will then return to caller) * +* * +* These methods can call us: codegen_compilerstub & asm_calljavafunction * +* ATTENTION: use REG_ITMP1 to pass methodinfo pointer to me! * +* * +*******************************************************************************/ + +#define MYSTACKSIZE (5*4) + +asm_call_jit_compiler: + SAVE_ARGUMENT_REGISTERS /* save our argument registers & LR */ + + mov a1, itmp1 /* pass methodinfo pointer */ + mov a2, mptr /* pass method pointer */ + add a3, sp, #MYSTACKSIZE /* pass Java sp */ + mov a4, lr /* pass Java RA (correct for leafs) */ + bl jit_asm_compile + mov itmp1, res1 /* save pointer to new jit-code */ + + tst itmp1,itmp1 /* check for exeption */ + beq L_asm_call_jit_compiler_exception + + RESTORE_ARGUMENT_REGISTERS /* load our argument registers & LR */ + + mov ip, itmp1 + mov pc, ip /* call jit-code */ + +L_asm_call_jit_compiler_exception: + bl exceptions_get_and_clear_exception + mov xptr, res1 /* get exception */ + + RESTORE_ARGUMENT_REGISTERS /* load LR */ + + sub xpc, lr, #4 /* xpc = instruction that called us */ + b asm_handle_nat_exception + + +/********************* function asm_handle_exception *************************** +* * +* This function handles an exception. It does not use the usual calling * +* conventions. The exception pointer is passed in REG_ITMP1 and the * +* pc from the exception raising position is passed in REG_ITMP2. It searches * +* the local exception table for a handler. If no one is found, it unwinds * +* stacks and continues searching the callers. * +* * +* void asm_handle_exception (exceptionptr, exceptionpc); * +* * +*******************************************************************************/ + +asm_handle_nat_exception: + /*TODO:maybe make a macro out of it!!!*/ + SAVE_ARGUMENT_REGISTERS + mov a1, lr + bl md_codegen_get_pv_from_pc + mov ip, res1 + RESTORE_ARGUMENT_REGISTERS + /* fall through */ + +asm_handle_exception: + stmfd sp!, {r0 - r3} /* save possible used registers */ + mov itmp3, #1 /* set maybe-leaf flag */ + mov a4, #(4*4) /* prepare a4 for handle_exception */ + +asm_handle_exception_loop: + stmfd sp!, {ip,lr} /* call exception helper here! */ + mov a1, xptr /* pass exception pointer */ + mov a2, xpc /* pass exception pointer */ + mov a3, ip /* pass data segment pointer */ + add a4, sp, a4 /* calculate Java sp into a4... */ + add a4, a4, #(2*4) + bl exceptions_handle_exception + ldmfd sp!, {ip,lr} + + tst a1, a1 + beq asm_handle_exception_not_catched + + mov xpc, a1 /* move handlerpc into xpc */ + tst itmp3,itmp3 /* if this is a lead method ... */ + ldmnefd sp!, {r0 - r3} /* restore argument registers */ + + mov pc, xpc /* jump to handler */ + +asm_handle_exception_not_catched: + tst itmp3,itmp3 /* if this is a lead method ... */ + addne sp, sp, #(4*4) /* remove maybe-leaf stackframe */ + movne itmp3, #0 /* remove maybe-leaf flag */ + + ldr a3, [ip, #FrameSize] /* t2 = frame size */ + add a1, sp, a3 /* t0 = pointer to save area */ + ldr a2, [ip, #IsLeaf] /* t1 = is leaf procedure */ + tst a2, a2 /* if is leaf ... */ + ldreq lr, [a1, #-4]! /* ... restore RA */ + mov xpc, lr /* the new xpc is RA */ + + ldr a2, [ip, #IntSave] /* t1 = saved int register count */ + rsb a2, a2, #5 /* t1 = count of unsaved registers */ + sub a2, a2, #1 + add pc, pc, a2, lsl #2 /* do not load unsaved registers */ + ldr v1, [a1, #-20] /* ... but restore the other ones */ + ldr v2, [a1, #-16] + ldr v3, [a1, #-12] + ldr v4, [a1, #- 8] + ldr v5, [a1, #- 4] + + add sp, sp, a3 /* unwind stack (using t2) */ + mov a4, #0 /* prepare a4 for handle_exception */ + + /*TODO:maybe make a macro out of it!!!*/ + SAVE_ARGUMENT_REGISTERS + mov a1, lr + bl md_codegen_get_pv_from_pc + mov ip, res1 + RESTORE_ARGUMENT_REGISTERS + + b asm_handle_exception_loop + + +/* asm_patcher_wrapper ********************************************************* +* * +* TODO: document me * +* * +* Stack layout when calling patcher function: * +* 24 saved REG_ITMP3, should be restored ( -4) * +* 20 data segment displacement from load instructions ( -8) * +* 16 return address into JIT code (patch position) (-12) * +* 12 pointer to virtual java_objectheader * +* 8 machine code (which is patched back later) * +* [ 8 result of patcher function (indicates exception) ] * +* 4 unresolved class/method/field reference * +* [ 0 patcher function pointer to call ] * +* 0 saved IP of caller (caller needs it!) * +* * +*******************************************************************************/ + +#define PATCHSTACKSIZE 7*4 + +asm_patcher_wrapper: + mov itmp3, sp /* preserve original SP in ITMP3 */ + + SAVE_ARGUMENT_REGISTERS_IP /* save our argument registers & LR */ + SAVE_FLOAT_REGISTERS /* save our float registers here */ + + mov a1, itmp3 /* pass SP of patcher stub */ + mov a2, ip /* pass PV */ + mov a3, lr /* pass RA (correct for leafs) */ + bl patcher_wrapper + mov itmp3, res1 /* save return value */ + + RESTORE_FLOAT_REGISTERS /* restore our float registers here */ + RESTORE_ARGUMENT_REGISTERS_IP /* load our argument registers & LR */ + + tst itmp3, itmp3 /* check for an exception */ + bne L_asm_patcher_wrapper_exception + + add sp, sp, #PATCHSTACKSIZE /* remove patcher stack frame */ + + ldr itmp3, [sp, #-4] /* restore ITMP3 for calling method */ + ldr pc, [sp, #-12] /* jump to new patched code */ + +L_asm_patcher_wrapper_exception: + mov xptr, itmp3 /* get exception */ + ldr xpc, [sp, #16] /* RA is xpc */ + + add sp, sp, #PATCHSTACKSIZE /* remove patcher stack frame */ + + b asm_handle_exception + + +/* asm_abstractmethoderror ***************************************************** + + Creates and throws an AbstractMethodError. + +*******************************************************************************/ + +asm_abstractmethoderror: + stmfd sp!, {lr} /* save return address */ + add a1, sp, #(1*4) /* pass java sp */ + mov a2, lr /* pass exception address */ + bl exceptions_asm_new_abstractmethoderror + ldmfd sp!, {lr} /* restore return address */ + + mov xptr, res1 /* get exception pointer */ + sub xpc, lr, #4 /* exception address is ra - 4 */ + b asm_handle_nat_exception + + +/********************* function asm_cacheflush ********************************* +* * +* TODO: document me * +* * +* void asm_cacheflush(void *p, s4 size); * +* * +*******************************************************************************/ + +#if 1 +.equ sys_cacheflush, 0x9f0002 +asm_cacheflush: + add a2, a1, a2 + mov a3, #0 + #if 1 + /* TODO: repeair this! */ + /* cacheflush is messed up beyond all repair! */ + mov a1, #0x0 + mov a2, #0xff000000 + #endif + swi #sys_cacheflush + mov pc, lr +#else +.equ IMBa, 0xf00000 +.equ IMBb, 0xf00001 +asm_cacheflush: + /* clean and invalidate the entire cache!!! */ + swi #IMBa + mov pc, lr +#endif + + +/********************* function asm_getclassvalues_atomic *********************/ + +asm_getclassvalues_atomic: + stmfd sp!, {r4, r5, r6} +_crit_restart: +_crit_begin: + ldr r4,[a1,#offbaseval] + ldr r5,[a1,#offdiffval] + ldr r6,[a2,#offbaseval] +_crit_end: + str r4,[a3,#offcast_super_baseval] + str r5,[a3,#offcast_super_diffval] + str r6,[a3,#offcast_sub_baseval] + ldmfd sp!, {r4, r5, r6} + mov pc, lr + + +/* Disable exec-stacks, required for Gentoo ***********************************/ + +#if defined(__GCC__) && defined(__ELF__) + .section .note.GNU-stack,"",@progbits +#endif + + +/* + * These are local overrides for various environment variables in Emacs. + * Please do not remove this and leave it at the end of the file, where + * Emacs will automagically detect them. + * --------------------------------------------------------------------- + * Local variables: + * mode: asm + * indent-tabs-mode: t + * c-basic-offset: 4 + * tab-width: 4 + * End: + * vim:noexpandtab:sw=4:ts=4: + */ diff --git a/src/vm/jit/arm/codegen.c b/src/vm/jit/arm/codegen.c new file mode 100644 index 000000000..3db469a8f --- /dev/null +++ b/src/vm/jit/arm/codegen.c @@ -0,0 +1,3091 @@ +/* src/vm/jit/arm/codegen.c - machine code generator for Arm + + Copyright (C) 1996-2005, 2006 R. Grafl, A. Krall, C. Kruegel, + C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring, + E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich, + J. Wenninger, Institut f. Computersprachen - TU Wien + + This file is part of CACAO. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + + Contact: cacao@cacaojvm.org + + Authors: Michael Starzinger + + Changes: Christian Thalinger + Edwin Steiner + + $Id: codegen.c 6591 2007-01-02 19:14:25Z twisti $ + +*/ + + +#include "config.h" + +#include +#include + +#include "vm/types.h" + +#include "md-abi.h" + +#include "vm/jit/arm/arch.h" +#include "vm/jit/arm/codegen.h" + +#include "mm/memory.h" +#include "native/native.h" + +#if defined(ENABLE_THREADS) +# include "threads/native/lock.h" +#endif + +#include "vm/builtin.h" +#include "vm/exceptions.h" +#include "vm/global.h" +#include "vm/loader.h" +#include "vm/options.h" +#include "vm/stringlocal.h" +#include "vm/vm.h" +#include "vm/jit/asmpart.h" +#include "vm/jit/codegen-common.h" +#include "vm/jit/dseg.h" +#include "vm/jit/emit-common.h" +#include "vm/jit/jit.h" +#include "vm/jit/methodheader.h" +#include "vm/jit/parse.h" +#include "vm/jit/patcher.h" +#include "vm/jit/reg.h" + +#if defined(ENABLE_LSRA) +#include "vm/jit/allocator/lsra.h" +#endif + + +/* codegen ********************************************************************* + + Generates machine code. + +*******************************************************************************/ + +bool codegen(jitdata *jd) +{ + methodinfo *m; + codeinfo *code; + codegendata *cd; + registerdata *rd; + s4 i, t, len; + s4 s1, s2, s3, d; + s4 disp; + varinfo *var; + basicblock *bptr; + instruction *iptr; + exception_entry *ex; + s4 fieldtype; + s4 varindex; + + s4 spilledregs_num; + s4 savedregs_num; + u2 savedregs_bitmask; + u2 currentline; + + methodinfo *lm; /* local methodinfo for ICMD_INVOKE* */ + unresolved_method *um; + builtintable_entry *bte; + methoddesc *md; + + /* get required compiler data */ + + m = jd->m; + code = jd->code; + cd = jd->cd; + rd = jd->rd; + + /* prevent compiler warnings */ + + lm = NULL; + um = NULL; + bte = NULL; + + fieldtype = -1; + + /* space to save used callee saved registers */ + savedregs_num = (jd->isleafmethod) ? 0 : 1; /* space to save the LR */ + savedregs_num += (INT_SAV_CNT - rd->savintreguse); + savedregs_num += (FLT_SAV_CNT - rd->savfltreguse); + spilledregs_num = rd->memuse; + +#if defined(ENABLE_THREADS) /* space to save argument of monitor_enter */ + if (checksync && (m->flags & ACC_SYNCHRONIZED)) + spilledregs_num++; +#endif + + cd->stackframesize = spilledregs_num + savedregs_num; + + /* SECTION: Method Header */ + /* create method header */ + + (void) dseg_add_unique_address(cd, code); /* CodeinfoPointer */ + (void) dseg_add_unique_s4(cd, cd->stackframesize * 4); /* FrameSize */ + +#if defined(ENABLE_THREADS) + /* IsSync contains the offset relative to the stack pointer for the + argument of monitor_exit used in the exception handler. Since the + offset could be zero and give a wrong meaning of the flag it is + offset by one. + */ + + if (checksync && (m->flags & ACC_SYNCHRONIZED)) + (void) dseg_add_unique_s4(cd, (rd->memuse + 1) * 4);/* IsSync */ + else +#endif + (void) dseg_add_unique_s4(cd, 0); /* IsSync */ + + (void) dseg_add_unique_s4(cd, jd->isleafmethod); /* IsLeaf */ + (void) dseg_add_unique_s4(cd, INT_SAV_CNT - rd->savintreguse); /* IntSave */ + (void) dseg_add_unique_s4(cd, FLT_SAV_CNT - rd->savfltreguse); /* FltSave */ + (void) dseg_addlinenumbertablesize(cd); + (void) dseg_add_unique_s4(cd, jd->exceptiontablelength); /* ExTableSize */ + + /* create exception table */ + + for (ex = jd->exceptiontable; ex != NULL; ex = ex->down) { + dseg_add_target(cd, ex->start); + dseg_add_target(cd, ex->end); + dseg_add_target(cd, ex->handler); + (void) dseg_add_unique_address(cd, ex->catchtype.any); + } + + /* save return address and used callee saved registers */ + savedregs_bitmask = 0; + if (!jd->isleafmethod) + savedregs_bitmask = (1<= rd->savintreguse; i--) + savedregs_bitmask |= (1<<(rd->savintregs[i])); +#if !defined(NDEBUG) + for (i = FLT_SAV_CNT - 1; i >= rd->savfltreguse; i--) { + log_text("!!! CODEGEN: floating-point callee saved registers are not saved to stack (SEVERE! STACK IS MESSED UP!)"); + /* TODO: floating-point */ + } +#endif + if (savedregs_bitmask) { + M_STMFD(savedregs_bitmask, REG_SP); + } + + /* create additional stack frame for spilled variables (if necessary) */ + if (spilledregs_num) { + M_SUB_IMM_EXT_MUL4(REG_SP, REG_SP, spilledregs_num); + } + + /* take arguments out of register or stack frame */ + md = m->parseddesc; + for (i = 0, len = 0; i < md->paramcount; i++) { + s1 = md->params[i].regoff; + t = md->paramtypes[i].type; + + varindex = jd->local_map[len * 5 + t]; + + len += (IS_2_WORD_TYPE(t)) ? 2 : 1; /* 2 word type arguments */ + + if (varindex == UNUSED) + continue; + + var = VAR(varindex); + + /* ATTENTION: we use interger registers for all arguments (even float) */ +#if !defined(ENABLE_SOFTFLOAT) + if (IS_INT_LNG_TYPE(t)) { /* integer args */ +#endif + if (!md->params[i].inmemory) { /* register arguments */ + s2 = ARGUMENT_REGS(t, s1); /* get argument register */ + if (!(var->flags & INMEMORY)) { /* reg arg -> register */ + if (GET_LOW_REG(var->vv.regoff) == REG_SPLIT || GET_HIGH_REG(var->vv.regoff) == REG_SPLIT) { + /* TODO: remove this!!! */ + dolog("SPLIT in local var: %x>%x (%s.%s)", s2, var->vv.regoff, m->class->name->text, m->name->text); + assert(s2 == var->vv.regoff); + } + s3 = var->vv.regoff; + SPLIT_OPEN(t, s2, REG_ITMP1); + SPLIT_LOAD(t, s2, cd->stackframesize); + SPLIT_OPEN(t, s3, REG_ITMP1); + + if (IS_2_WORD_TYPE(t)) + M_LNGMOVE(s2, s3); + else + M_INTMOVE(s2, s3); + + SPLIT_STORE_AND_CLOSE(t, s3, cd->stackframesize); + } + else { /* reg arg -> spilled */ + SPLIT_OPEN(t, s2, REG_ITMP1); + SPLIT_LOAD(t, s2, cd->stackframesize); + + if (IS_2_WORD_TYPE(t)) + M_LST(s2, REG_SP, var->vv.regoff * 4); + else + M_IST(s2, REG_SP, var->vv.regoff * 4); + /* no SPLIT_CLOSE here because arg is fully spilled now */ + } + } + else { /* stack arguments */ + if (!(var->flags & INMEMORY)) { /* stack arg -> register */ + if (IS_2_WORD_TYPE(t)) + M_LLD(var->vv.regoff, REG_SP, (cd->stackframesize + s1) * 4); + else + M_ILD(var->vv.regoff, REG_SP, (cd->stackframesize + s1) * 4); + } + else { /* stack arg -> spilled */ + /* Reuse Memory Position on Caller Stack */ + var->vv.regoff = cd->stackframesize + s1; + } + } +#if !defined(ENABLE_SOFTFLOAT) + } else { /* floating args */ + if (!md->params[i].inmemory) { /* register arguments */ + s2 = ARGUMENT_REGS(t, s1); /* get argument register */ + if (!(var->flags & INMEMORY)) { /* reg arg -> register */ + SPLIT_OPEN(t, s2, REG_ITMP1); + SPLIT_LOAD(t, s2, cd->stackframesize); + M_CAST_INT_TO_FLT_TYPED(t, s2, var->vv.regoff); + } + else { /* reg arg -> spilled */ + SPLIT_OPEN(t, s2, REG_ITMP1); + SPLIT_LOAD(t, s2, cd->stackframesize); + + if (IS_2_WORD_TYPE(t)) + M_LST(s2, REG_SP, var->vv.regoff * 4); + else + M_IST(s2, REG_SP, var->vv.regoff * 4); + /* no SPLIT_CLOSE here because arg is fully spilled now */ + } + } + else { /* stack arguments */ + if (!(var->flags & INMEMORY)) { /* stack arg -> register */ + M_STACK_LOAD_FLT_TYPED(t, var->vv.regoff, cd->stackframesize + s1); + } else { /* stack arg -> spilled */ + /* Reuse Memory Position on Caller Stack */ + var->vv.regoff = cd->stackframesize + s1; + } + } + } +#endif /* !defined(ENABLE_SOFTFLOAT) */ + } + +#if defined(ENABLE_THREADS) + /* call monitorenter function */ + + if (checksync && (m->flags & ACC_SYNCHRONIZED)) { + /* stack offset for monitor argument */ + + s1 = rd->memuse; + +# if !defined(NDEBUG) + if (JITDATA_HAS_FLAG_VERBOSECALL(jd)) { + M_STMFD(BITMASK_ARGS, REG_SP); + s1 += 4; + } +# endif + + /* get the correct lock object */ + + if (m->flags & ACC_STATIC) { + disp = dseg_add_address(cd, &m->class->object.header); + M_DSEG_LOAD(REG_A0, disp); + } + else { + M_TST(REG_A0, REG_A0); + M_BEQ(0); + codegen_add_nullpointerexception_ref(cd); + } + + M_STR(REG_A0, REG_SP, s1 * 4); + disp = dseg_add_functionptr(cd, LOCK_monitor_enter); + M_DSEG_BRANCH(disp); + s1 = (s4) (cd->mcodeptr - cd->mcodebase); + M_RECOMPUTE_IP(s1); + +# if !defined(NDEBUG) + if (JITDATA_HAS_FLAG_VERBOSECALL(jd)) + M_LDMFD(BITMASK_ARGS, REG_SP); +# endif + } +#endif + +#if !defined(NDEBUG) + /* call trace function */ + + if (JITDATA_HAS_FLAG_VERBOSECALL(jd)) + emit_verbosecall_enter(jd); +#endif + + /* end of header generation */ + + /* SECTION: ICMD Code Generation */ + /* for all basic blocks */ + for (bptr = jd->basicblocks; bptr != NULL; bptr = bptr->next) + { + bptr->mpc = (s4) (cd->mcodeptr - cd->mcodebase); + + /* is this basic block reached? */ + if (bptr->flags < BBREACHED) + continue; + + /* branch resolving */ + { + branchref *brefs; + for (brefs = bptr->branchrefs; brefs != NULL; brefs = brefs->next) { + gen_resolvebranch(cd->mcodebase + brefs->branchpos, + brefs->branchpos, bptr->mpc); + } + } + + /* copy interface registers to their destination */ + len = bptr->indepth; + + MCODECHECK(64+len); + +#if defined(ENABLE_LSRA) + if (opt_lsra) { + while (len) { + len--; + var = VAR(bptr->invars[len]); + if ((len == bptr->indepth-1) && (bptr->type == BBTYPE_EXH)) { + if (!(var->flags & INMEMORY)) + d= var->vv.regoff; + else + d=REG_ITMP1; + M_INTMOVE(REG_ITMP1, d); + emit_store(jd, NULL, var, d); + } + } + } else { +#endif + while (len) { + len--; + var = VAR(bptr->invars[len]); + + if ((len == bptr->indepth-1) && (bptr->type == BBTYPE_EXH)) { + d = codegen_reg_of_var(0, var, REG_ITMP1); + M_INTMOVE(REG_ITMP1, d); + emit_store(jd, NULL, var, d); + } + else { + assert((var->flags & INOUT)); + } + } +#if defined(ENABLE_LSRA) + } +#endif + + /* for all instructions */ + len = bptr->icount; + currentline = 0; + for (iptr = bptr->iinstr; len > 0; len--, iptr++) { + + /* add line number */ + if (iptr->line != currentline) { + dseg_addlinenumber(cd, iptr->line); + currentline = iptr->line; + } + + MCODECHECK(64); /* an instruction usually needs < 64 words */ + + /* the big switch */ + switch (iptr->opc) { + case ICMD_NOP: /* ... ==> ... */ + break; + + /* constant operations ************************************************/ + + case ICMD_ICONST: /* ... ==> ..., constant */ + + d = codegen_reg_of_dst(jd, iptr, REG_ITMP1); + ICONST(d, iptr->sx.val.i); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_ACONST: /* ... ==> ..., constant */ + + d = codegen_reg_of_dst(jd, iptr, REG_ITMP1); + if (INSTRUCTION_IS_UNRESOLVED(iptr)) { + disp = dseg_add_unique_address(cd, NULL); + + codegen_addpatchref(cd, PATCHER_aconst, + iptr->sx.val.c.ref, + disp); + + if (opt_showdisassemble) + M_NOP; + + M_DSEG_LOAD(d, disp); + } + else { + ICONST(d, (u4) iptr->sx.val.anyptr); + } + emit_store_dst(jd, iptr, d); + break; + + case ICMD_LCONST: /* ... ==> ..., constant */ + + d = codegen_reg_of_dst(jd, iptr, REG_ITMP12_PACKED); + LCONST(d, iptr->sx.val.l); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_FCONST: /* ... ==> ..., constant */ + +#if defined(ENABLE_SOFTFLOAT) + d = codegen_reg_of_dst(jd, iptr, REG_ITMP1); + ICONST(d, iptr->sx.val.i); + emit_store_dst(jd, iptr, d); +#else + d = codegen_reg_of_dst(jd, iptr, REG_FTMP1); + FCONST(d, iptr->sx.val.f); + emit_store_dst(jd, iptr, d); +#endif + break; + + case ICMD_DCONST: /* ... ==> ..., constant */ + +#if defined(ENABLE_SOFTFLOAT) + d = codegen_reg_of_dst(jd, iptr, REG_ITMP12_PACKED); + LCONST(d, iptr->sx.val.l); + emit_store_dst(jd, iptr, d); +#else + d = codegen_reg_of_dst(jd, iptr, REG_FTMP1); + DCONST(d, iptr->sx.val.d); + emit_store_dst(jd, iptr, d); +#endif + break; + + + /* load/store/copy/move operations ************************************/ + + case ICMD_ILOAD: /* ... ==> ..., content of local variable */ + case ICMD_ALOAD: /* op1 = local variable */ + case ICMD_FLOAD: + case ICMD_LLOAD: + case ICMD_DLOAD: + case ICMD_ISTORE: /* ..., value ==> ... */ + case ICMD_FSTORE: + case ICMD_LSTORE: + case ICMD_DSTORE: + case ICMD_COPY: + case ICMD_MOVE: + + emit_copy(jd, iptr, VAROP(iptr->s1), VAROP(iptr->dst)); + break; + + case ICMD_ASTORE: + if (!(iptr->flags.bits & INS_FLAG_RETADDR)) + emit_copy(jd, iptr, VAROP(iptr->s1), VAROP(iptr->dst)); + break; + + /* pop operations *****************************************************/ + + /* attention: double and longs are only one entry in CACAO ICMDs */ + + case ICMD_POP: /* ..., value ==> ... */ + case ICMD_POP2: /* ..., value, value ==> ... */ + + break; + + + /* integer operations *************************************************/ + + case ICMD_INT2BYTE: /* ..., value ==> ..., value */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP2); + M_MOV(d, REG_LSL(s1, 24)); + M_MOV(d, REG_ASR(d, 24)); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_INT2CHAR: /* ..., value ==> ..., value */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP2); + M_MOV(d, REG_LSL(s1, 16)); + M_MOV(d, REG_LSR(d, 16)); /* ATTENTION: char is unsigned */ + emit_store_dst(jd, iptr, d); + break; + + case ICMD_INT2SHORT: /* ..., value ==> ..., value */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP2); + M_MOV(d, REG_LSL(s1, 16)); + M_MOV(d, REG_ASR(d, 16)); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_I2L: /* ..., value ==> ..., value */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP12_PACKED); + M_INTMOVE(s1, GET_LOW_REG(d)); + M_MOV(GET_HIGH_REG(d), REG_ASR(s1, 31)); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_L2I: /* ..., value ==> ..., value */ + + s1 = emit_load_s1_low(jd, iptr, REG_ITMP1); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP1); + M_INTMOVE(s1, d); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_INEG: /* ..., value ==> ..., - value */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP2); + M_RSB_IMM(d, s1, 0); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_LNEG: /* ..., value ==> ..., - value */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP12_PACKED); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP12_PACKED); + M_RSB_IMMS(GET_LOW_REG(d), GET_LOW_REG(s1), 0); + M_RSC_IMM(GET_HIGH_REG(d), GET_HIGH_REG(s1), 0); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_IADD: /* ..., val1, val2 ==> ..., val1 + val2 */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + s2 = emit_load_s2(jd, iptr, REG_ITMP2); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP1); + M_ADD(d, s1, s2); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_LADD: /* ..., val1, val2 ==> ..., val1 + val2 */ + + s1 = emit_load_s1_low(jd, iptr, REG_ITMP3); + s2 = emit_load_s2_low(jd, iptr, REG_ITMP1); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP12_PACKED); + M_ADD_S(GET_LOW_REG(d), s1, s2); + s1 = emit_load_s1_high(jd, iptr, REG_ITMP3); + s2 = emit_load_s2_high(jd, iptr, REG_ITMP2); + M_ADC(GET_HIGH_REG(d), s1, s2); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_IADDCONST: + case ICMD_IINC: + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP1); + + if (IS_IMM(iptr->sx.val.i)) { + M_ADD_IMM(d, s1, iptr->sx.val.i); + } else if (IS_IMM(-iptr->sx.val.i)) { + M_SUB_IMM(d, s1, (-iptr->sx.val.i)); + } else { + ICONST(REG_ITMP3, iptr->sx.val.i); + M_ADD(d, s1, REG_ITMP3); + } + + emit_store_dst(jd, iptr, d); + break; + + case ICMD_LADDCONST: /* ..., value ==> ..., value + constant */ + /* sx.val.l = constant */ + + s3 = iptr->sx.val.l & 0xffffffff; + s1 = emit_load_s1_low(jd, iptr, REG_ITMP1); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP12_PACKED); + if (IS_IMM(s3)) + M_ADD_IMMS(GET_LOW_REG(d), s1, s3); + else { + ICONST(REG_ITMP3, s3); + M_ADD_S(GET_LOW_REG(d), s1, REG_ITMP3); + } + s3 = iptr->sx.val.l >> 32; + s1 = emit_load_s1_high(jd, iptr, REG_ITMP2); + if (IS_IMM(s3)) + M_ADC_IMM(GET_HIGH_REG(d), s1, s3); + else { + ICONST(REG_ITMP3, s3); + M_ADC(GET_HIGH_REG(d), s1, REG_ITMP3); + } + emit_store_dst(jd, iptr, d); + break; + + case ICMD_ISUB: /* ..., val1, val2 ==> ..., val1 - val2 */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + s2 = emit_load_s2(jd, iptr, REG_ITMP2); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP1); + M_SUB(d, s1, s2); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_LSUB: /* ..., val1, val2 ==> ..., val1 - val2 */ + + s1 = emit_load_s1_low(jd, iptr, REG_ITMP3); + s2 = emit_load_s2_low(jd, iptr, REG_ITMP1); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP12_PACKED); + M_SUB_S(GET_LOW_REG(d), s1, s2); + s1 = emit_load_s1_high(jd, iptr, REG_ITMP3); + s2 = emit_load_s2_high(jd, iptr, REG_ITMP2); + M_SBC(GET_HIGH_REG(d), s1, s2); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_ISUBCONST: /* ..., value ==> ..., value + constant */ + /* sx.val.i = constant */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP2); + if (IS_IMM(iptr->sx.val.i)) + M_SUB_IMM(d, s1, iptr->sx.val.i); + else { + ICONST(REG_ITMP3, iptr->sx.val.i); + M_SUB(d, s1, REG_ITMP3); + } + emit_store_dst(jd, iptr, d); + break; + + case ICMD_LSUBCONST: /* ..., value ==> ..., value - constant */ + /* sx.val.l = constant */ + + s3 = iptr->sx.val.l & 0xffffffff; + s1 = emit_load_s1_low(jd, iptr, REG_ITMP1); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP12_PACKED); + if (IS_IMM(s3)) + M_SUB_IMMS(GET_LOW_REG(d), s1, s3); + else { + ICONST(REG_ITMP3, s3); + M_SUB_S(GET_LOW_REG(d), s1, REG_ITMP3); + } + s3 = iptr->sx.val.l >> 32; + s1 = emit_load_s1_high(jd, iptr, REG_ITMP2); + if (IS_IMM(s3)) + M_SBC_IMM(GET_HIGH_REG(d), s1, s3); + else { + ICONST(REG_ITMP3, s3); + M_SBC(GET_HIGH_REG(d), s1, REG_ITMP3); + } + emit_store_dst(jd, iptr, d); + break; + + case ICMD_IMUL: /* ..., val1, val2 ==> ..., val1 * val2 */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + s2 = emit_load_s2(jd, iptr, REG_ITMP2); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP1); + M_MUL(d, s1, s2); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_IDIV: /* ..., val1, val2 ==> ..., val1 / val2 */ + case ICMD_IREM: /* ..., val1, val2 ==> ..., val1 % val2 */ + + s1 = emit_load_s1(jd, iptr, REG_A0); + s2 = emit_load_s2(jd, iptr, REG_A1); + gen_div_check(VAROP(iptr->sx.s23.s2)->type, s2); + + /* move arguments into argument registers */ + M_INTMOVE(s1, REG_A0); + M_INTMOVE(s2, REG_A1); + + /* call builtin function */ + bte = iptr->sx.s23.s3.bte; + disp = dseg_add_functionptr(cd, bte->fp); + M_DSEG_BRANCH(disp); + + /* recompute ip */ + s1 = (s4) (cd->mcodeptr - cd->mcodebase); + M_RECOMPUTE_IP(s1); + + /* move result into destination register */ + d = codegen_reg_of_dst(jd, iptr, REG_RESULT); + M_INTMOVE(REG_RESULT, d); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_LDIV: /* ..., val1, val2 ==> ..., val1 / val2 */ + case ICMD_LREM: /* ..., val1, val2 ==> ..., val1 % val2 */ + + /* move arguments into argument registers */ + + s1 = emit_load_s1(jd, iptr, REG_A0_A1_PACKED); + M_LNGMOVE(s1, REG_A0_A1_PACKED); + + s2 = emit_load_s2(jd, iptr, REG_A2_A3_PACKED); + M_LNGMOVE(s2, REG_A2_A3_PACKED); + + gen_div_check(VAROP(iptr->sx.s23.s2)->type, s2); + + /* call builtin function */ + bte = iptr->sx.s23.s3.bte; + disp = dseg_add_functionptr(cd, bte->fp); + M_DSEG_BRANCH(disp); + + /* recompute ip */ + s1 = (s4) (cd->mcodeptr - cd->mcodebase); + M_RECOMPUTE_IP(s1); + + /* move result into destination register */ + d = codegen_reg_of_dst(jd, iptr, REG_RESULT_PACKED); + M_LNGMOVE(REG_RESULT_PACKED, d); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_IMULPOW2: /* ..., value ==> ..., value * (2 ^ constant) */ + /* sx.val.i = constant */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP2); + M_MOV(d, REG_LSL(s1, iptr->sx.val.i)); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_IDIVPOW2: /* ..., value ==> ..., value / (2 ^ constant) */ + /* sx.val.i = constant */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP2); + /* this rounds towards 0 as java likes it */ + M_MOV(REG_ITMP3, REG_ASR(s1, 31)); + M_ADD(REG_ITMP3, s1, REG_LSR(REG_ITMP3, 32 - iptr->sx.val.i)); + M_MOV(d, REG_ASR(REG_ITMP3, iptr->sx.val.i)); + /* this rounds towards nearest, not java style */ + /*M_MOV_S(d, REG_ASR(s1, iptr->sx.val.i)); + M_ADCMI_IMM(d, d, 0);*/ + emit_store_dst(jd, iptr, d); + break; + + case ICMD_IREMPOW2: /* ..., value ==> ..., value % constant */ + /* sx.val.i = constant [ (2 ^ x) - 1 ] */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP2); + M_MOV_S(REG_ITMP1, s1); + M_RSBMI_IMM(REG_ITMP1, REG_ITMP1, 0); + if (IS_IMM(iptr->sx.val.i)) + M_AND_IMM(d, REG_ITMP1, iptr->sx.val.i); + else { + ICONST(REG_ITMP3, iptr->sx.val.i); + M_AND(d, REG_ITMP1, REG_ITMP3); + } + M_RSBMI_IMM(d, d, 0); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_ISHL: /* ..., val1, val2 ==> ..., val1 << val2 */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + s2 = emit_load_s2(jd, iptr, REG_ITMP2); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP1); + M_AND_IMM(REG_ITMP2, s2, 0x1f); + M_MOV(d, REG_LSL_REG(s1, REG_ITMP2)); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_ISHR: /* ..., val1, val2 ==> ..., val1 >> val2 */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + s2 = emit_load_s2(jd, iptr, REG_ITMP2); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP1); + M_AND_IMM(REG_ITMP2, s2, 0x1f); + M_MOV(d, REG_ASR_REG(s1, REG_ITMP2)); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_IUSHR: /* ..., val1, val2 ==> ..., val1 >>> val2 */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + s2 = emit_load_s2(jd, iptr, REG_ITMP2); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP1); + M_AND_IMM(REG_ITMP2, s2, 0x1f); + M_MOV(d, REG_LSR_REG(s1, REG_ITMP2)); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_ISHLCONST: /* ..., value ==> ..., value << constant */ + /* sx.val.i = constant */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP2); + M_MOV(d, REG_LSL(s1, iptr->sx.val.i & 0x1f)); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_ISHRCONST: /* ..., value ==> ..., value >> constant */ + /* sx.val.i = constant */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP1); + /* we need to check for zero here because arm interprets it as SHR by 32 */ + if ((iptr->sx.val.i & 0x1f) == 0) { + M_INTMOVE(s1, d); + } else { + M_MOV(d, REG_ASR(s1, iptr->sx.val.i & 0x1f)); + } + emit_store_dst(jd, iptr, d); + break; + + case ICMD_IUSHRCONST: /* ..., value ==> ..., value >>> constant */ + /* sx.val.i = constant */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP1); + /* we need to check for zero here because arm interprets it as SHR by 32 */ + if ((iptr->sx.val.i & 0x1f) == 0) + M_INTMOVE(s1, d); + else + M_MOV(d, REG_LSR(s1, iptr->sx.val.i & 0x1f)); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_IAND: /* ..., val1, val2 ==> ..., val1 & val2 */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + s2 = emit_load_s2(jd, iptr, REG_ITMP2); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP1); + M_AND(d, s1, s2); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_LAND: /* ..., val1, val2 ==> ..., val1 & val2 */ + + s1 = emit_load_s1_low(jd, iptr, REG_ITMP3); + s2 = emit_load_s2_low(jd, iptr, REG_ITMP1); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP12_PACKED); + M_AND(GET_LOW_REG(d), s1, s2); + s1 = emit_load_s1_high(jd, iptr, REG_ITMP3); + s2 = emit_load_s2_high(jd, iptr, REG_ITMP2); + M_AND(GET_HIGH_REG(d), s1, s2); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_IOR: /* ..., val1, val2 ==> ..., val1 | val2 */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + s2 = emit_load_s2(jd, iptr, REG_ITMP2); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP1); + M_ORR(d, s1, s2); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_LOR: /* ..., val1, val2 ==> ..., val1 | val2 */ + + s1 = emit_load_s1_low(jd, iptr, REG_ITMP3); + s2 = emit_load_s2_low(jd, iptr, REG_ITMP1); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP12_PACKED); + M_ORR(GET_LOW_REG(d), s1, s2); + s1 = emit_load_s1_high(jd, iptr, REG_ITMP3); + s2 = emit_load_s2_high(jd, iptr, REG_ITMP2); + M_ORR(GET_HIGH_REG(d), s1, s2); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_IXOR: /* ..., val1, val2 ==> ..., val1 ^ val2 */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + s2 = emit_load_s2(jd, iptr, REG_ITMP2); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP1); + M_EOR(d, s1, s2); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_LXOR: /* ..., val1, val2 ==> ..., val1 ^ val2 */ + + s1 = emit_load_s1_low(jd, iptr, REG_ITMP3); + s2 = emit_load_s2_low(jd, iptr, REG_ITMP1); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP12_PACKED); + M_EOR(GET_LOW_REG(d), s1, s2); + s1 = emit_load_s1_high(jd, iptr, REG_ITMP3); + s2 = emit_load_s2_high(jd, iptr, REG_ITMP2); + M_EOR(GET_HIGH_REG(d), s1, s2); + emit_store_dst(jd, iptr, d); + break; + + + /* floating operations ************************************************/ + +#if !defined(ENABLE_SOFTFLOAT) + + case ICMD_FNEG: /* ..., value ==> ..., - value */ + + s1 = emit_load_s1(jd, iptr, REG_FTMP1); + d = codegen_reg_of_dst(jd, iptr, REG_FTMP1); + M_MNFS(d, s1); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_FADD: /* ..., val1, val2 ==> ..., val1 + val2 */ + + s1 = emit_load_s1(jd, iptr, REG_FTMP1); + s2 = emit_load_s2(jd, iptr, REG_FTMP2); + d = codegen_reg_of_dst(jd, iptr, REG_FTMP1); + M_ADFS(d, s1, s2); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_FSUB: /* ..., val1, val2 ==> ..., val1 - val2 */ + + s1 = emit_load_s1(jd, iptr, REG_FTMP1); + s2 = emit_load_s2(jd, iptr, REG_FTMP2); + d = codegen_reg_of_dst(jd, iptr, REG_FTMP1); + M_SUFS(d, s1, s2); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_FMUL: /* ..., val1, val2 ==> ..., val1 * val2 */ + + s1 = emit_load_s1(jd, iptr, REG_FTMP1); + s2 = emit_load_s2(jd, iptr, REG_FTMP2); + d = codegen_reg_of_dst(jd, iptr, REG_FTMP1); + M_MUFS(d, s1, s2); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_FDIV: /* ..., val1, val2 ==> ..., val1 / val2 */ + s1 = emit_load_s1(jd, iptr, REG_FTMP1); + s2 = emit_load_s2(jd, iptr, REG_FTMP2); + d = codegen_reg_of_dst(jd, iptr, REG_FTMP1); + M_DVFS(d, s1, s2); + emit_store_dst(jd, iptr, d); + break; + + /* ATTENTION: Jave does not want IEEE behaviour in FREM, do + not use this */ + + case ICMD_FREM: /* ..., val1, val2 ==> ..., val1 % val2 */ + + s1 = emit_load_s1(jd, iptr, REG_FTMP1); + s2 = emit_load_s2(jd, iptr, REG_FTMP2); + d = codegen_reg_of_dst(jd, iptr, REG_FTMP1); + M_RMFS(d, s1, s2); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_DNEG: /* ..., value ==> ..., - value */ + + s1 = emit_load_s1(jd, iptr, REG_FTMP1); + d = codegen_reg_of_dst(jd, iptr, REG_FTMP1); + M_MNFD(d, s1); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_DADD: /* ..., val1, val2 ==> ..., val1 + val2 */ + + s1 = emit_load_s1(jd, iptr, REG_FTMP1); + s2 = emit_load_s2(jd, iptr, REG_FTMP2); + d = codegen_reg_of_dst(jd, iptr, REG_FTMP1); + M_ADFD(d, s1, s2); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_DSUB: /* ..., val1, val2 ==> ..., val1 - val2 */ + + s1 = emit_load_s1(jd, iptr, REG_FTMP1); + s2 = emit_load_s2(jd, iptr, REG_FTMP2); + d = codegen_reg_of_dst(jd, iptr, REG_FTMP1); + M_SUFD(d, s1, s2); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_DMUL: /* ..., val1, val2 ==> ..., val1 * val2 */ + + s1 = emit_load_s1(jd, iptr, REG_FTMP1); + s2 = emit_load_s2(jd, iptr, REG_FTMP2); + d = codegen_reg_of_dst(jd, iptr, REG_FTMP1); + M_MUFD(d, s1, s2); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_DDIV: /* ..., val1, val2 ==> ..., val1 / val2 */ + + s1 = emit_load_s1(jd, iptr, REG_FTMP1); + s2 = emit_load_s2(jd, iptr, REG_FTMP2); + d = codegen_reg_of_dst(jd, iptr, REG_FTMP1); + M_DVFD(d, s1, s2); + emit_store_dst(jd, iptr, d); + break; + + /* ATTENTION: Jave does not want IEEE behaviour in DREM, do + not use this */ + + case ICMD_DREM: /* ..., val1, val2 ==> ..., val1 % val2 */ + + s1 = emit_load_s1(jd, iptr, REG_FTMP1); + s2 = emit_load_s2(jd, iptr, REG_FTMP2); + d = codegen_reg_of_dst(jd, iptr, REG_FTMP1); + M_RMFD(d, s1, s2); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_I2F: /* ..., value ==> ..., (float) value */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + d = codegen_reg_of_dst(jd, iptr, REG_FTMP1); + M_FLTS(d, s1); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_I2D: /* ..., value ==> ..., (double) value */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + d = codegen_reg_of_dst(jd, iptr, REG_FTMP1); + M_FLTD(d, s1); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_F2I: /* ..., value ==> ..., (int) value */ + + s1 = emit_load_s1(jd, iptr, REG_FTMP1); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP1); + /* this uses round towards zero, as Java likes it */ + M_FIX(d, s1); + /* this checks for NaN; to return zero as Java likes it */ + M_CMF(s1, 0x8); + M_MOVVS_IMM(d, 0); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_D2I: /* ..., value ==> ..., (int) value */ + + s1 = emit_load_s1(jd, iptr, REG_FTMP1); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP1); + /* this uses round towards zero, as Java likes it */ + M_FIX(d, s1); + /* this checks for NaN; to return zero as Java likes it */ + M_CMF(s1, 0x8); + M_MOVVS_IMM(d, 0); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_D2F: /* ..., value ==> ..., (float) value */ + + s1 = emit_load_s1(jd, iptr, REG_FTMP1); + d = codegen_reg_of_dst(jd, iptr, REG_FTMP2); + M_MVFS(d,s1); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_F2D: /* ..., value ==> ..., (double) value */ + + s1 = emit_load_s1(jd, iptr, REG_FTMP1); + d = codegen_reg_of_dst(jd, iptr, REG_FTMP2); + M_MVFD(d,s1); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_FCMPG: /* ..., val1, val2 ==> ..., val1 fcmpg val2 */ + + s1 = emit_load_s1(jd, iptr, REG_FTMP1); + s2 = emit_load_s2(jd, iptr, REG_FTMP2); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP1); + M_CMF(s2, s1); + M_MOV_IMM(d, 0); + M_SUBGT_IMM(d, d, 1); + M_ADDLT_IMM(d, d, 1); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_DCMPG: /* ..., val1, val2 ==> ..., val1 dcmpg val2 */ + + s1 = emit_load_s1(jd, iptr, REG_FTMP1); + s2 = emit_load_s2(jd, iptr, REG_FTMP2); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP1); + M_CMF(s2, s1); + M_MOV_IMM(d, 0); + M_SUBGT_IMM(d, d, 1); + M_ADDLT_IMM(d, d, 1); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_FCMPL: /* ..., val1, val2 ==> ..., val1 fcmpl val2 */ + + s1 = emit_load_s1(jd, iptr, REG_FTMP1); + s2 = emit_load_s2(jd, iptr, REG_FTMP2); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP1); + M_CMF(s1, s2); + M_MOV_IMM(d, 0); + M_SUBLT_IMM(d, d, 1); + M_ADDGT_IMM(d, d, 1); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_DCMPL: /* ..., val1, val2 ==> ..., val1 dcmpl val2 */ + + s1 = emit_load_s1(jd, iptr, REG_FTMP1); + s2 = emit_load_s2(jd, iptr, REG_FTMP2); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP1); + M_CMF(s1, s2); + M_MOV_IMM(d, 0); + M_SUBLT_IMM(d, d, 1); + M_ADDGT_IMM(d, d, 1); + emit_store_dst(jd, iptr, d); + break; + +#endif /* !defined(ENABLE_SOFTFLOAT) */ + + + /* memory operations **************************************************/ + + case ICMD_ARRAYLENGTH: /* ..., arrayref ==> ..., length */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP2); + gen_nullptr_check(s1); + M_ILD_INTERN(d, s1, OFFSET(java_arrayheader, size)); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_AALOAD: /* ..., arrayref, index ==> ..., value */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + s2 = emit_load_s2(jd, iptr, REG_ITMP2); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP1); + if (INSTRUCTION_MUST_CHECK(iptr)) { + gen_nullptr_check(s1); + gen_bound_check(s1, s2); + } + M_ADD(REG_ITMP1, s1, REG_LSL(s2, 2)); /* REG_ITMP1 = s1 + 4 * s2 */ + M_LDR_INTERN(d, REG_ITMP1, OFFSET(java_objectarray, data[0])); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_IALOAD: /* ..., arrayref, index ==> ..., value */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + s2 = emit_load_s2(jd, iptr, REG_ITMP2); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP1); + if (INSTRUCTION_MUST_CHECK(iptr)) { + gen_nullptr_check(s1); + gen_bound_check(s1, s2); + } + M_ADD(REG_ITMP1, s1, REG_LSL(s2, 2)); /* REG_ITMP1 = s1 + 4 * s2 */ + M_ILD_INTERN(d, REG_ITMP1, OFFSET(java_intarray, data[0])); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_BALOAD: /* ..., arrayref, index ==> ..., value */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + s2 = emit_load_s2(jd, iptr, REG_ITMP2); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP1); + if (INSTRUCTION_MUST_CHECK(iptr)) { + gen_nullptr_check(s1); + gen_bound_check(s1, s2); + } + M_ADD(REG_ITMP1, s1, s2); /* REG_ITMP1 = s1 + 1 * s2 */ + M_LDRSB(d, REG_ITMP1, OFFSET(java_bytearray, data[0])); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_CALOAD: /* ..., arrayref, index ==> ..., value */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + s2 = emit_load_s2(jd, iptr, REG_ITMP2); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP1); + if (INSTRUCTION_MUST_CHECK(iptr)) { + gen_nullptr_check(s1); + gen_bound_check(s1, s2); + } + M_ADD(REG_ITMP1, s1, REG_LSL(s2, 1)); /* REG_ITMP1 = s1 + 2 * s2 */ + M_LDRH(d, REG_ITMP1, OFFSET(java_chararray, data[0])); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_SALOAD: /* ..., arrayref, index ==> ..., value */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + s2 = emit_load_s2(jd, iptr, REG_ITMP2); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP1); + if (INSTRUCTION_MUST_CHECK(iptr)) { + gen_nullptr_check(s1); + gen_bound_check(s1, s2); + } + M_ADD(REG_ITMP1, s1, REG_LSL(s2, 1)); /* REG_ITMP1 = s1 + 2 * s2 */ + M_LDRSH(d, REG_ITMP1, OFFSET(java_shortarray, data[0])); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_LALOAD: /* ..., arrayref, index ==> ..., value */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + s2 = emit_load_s2(jd, iptr, REG_ITMP2); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP12_PACKED); + if (INSTRUCTION_MUST_CHECK(iptr)) { + gen_nullptr_check(s1); + gen_bound_check(s1, s2); + } + M_ADD(REG_ITMP3, s1, REG_LSL(s2, 3)); /* REG_ITMP3 = s1 + 8 * s2 */ + M_LLD_INTERN(d, REG_ITMP3, OFFSET(java_longarray, data[0])); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_FALOAD: /* ..., arrayref, index ==> ..., value */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + s2 = emit_load_s2(jd, iptr, REG_ITMP2); + if (INSTRUCTION_MUST_CHECK(iptr)) { + gen_nullptr_check(s1); + gen_bound_check(s1, s2); + } + M_ADD(REG_ITMP1, s1, REG_LSL(s2, 2)); /* REG_ITMP1 = s1 + 4 * s2 */ +#if !defined(ENABLE_SOFTFLOAT) + d = codegen_reg_of_dst(jd, iptr, REG_FTMP1); + M_FLD_INTERN(d, REG_ITMP1, OFFSET(java_floatarray, data[0])); +#else + d = codegen_reg_of_dst(jd, iptr, REG_ITMP1); + M_ILD_INTERN(d, REG_ITMP1, OFFSET(java_floatarray, data[0])); +#endif + emit_store_dst(jd, iptr, d); + break; + + case ICMD_DALOAD: /* ..., arrayref, index ==> ..., value */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + s2 = emit_load_s2(jd, iptr, REG_ITMP2); + if (INSTRUCTION_MUST_CHECK(iptr)) { + gen_nullptr_check(s1); + gen_bound_check(s1, s2); + } + M_ADD(REG_ITMP3, s1, REG_LSL(s2, 3)); /* REG_ITMP3 = s1 + 8 * s2 */ +#if !defined(ENABLE_SOFTFLOAT) + d = codegen_reg_of_dst(jd, iptr, REG_FTMP1); + M_DLD_INTERN(d, REG_ITMP3, OFFSET(java_doublearray, data[0])); +#else + d = codegen_reg_of_dst(jd, iptr, REG_ITMP12_PACKED); + M_LLD_INTERN(d, REG_ITMP3, OFFSET(java_doublearray, data[0])); +#endif + emit_store_dst(jd, iptr, d); + break; + + case ICMD_AASTORE: /* ..., arrayref, index, value ==> ... */ + + s1 = emit_load_s1(jd, iptr, REG_A0); + s2 = emit_load_s2(jd, iptr, REG_ITMP1); + if (INSTRUCTION_MUST_CHECK(iptr)) { + gen_nullptr_check(s1); + gen_bound_check(s1, s2); + } + s3 = emit_load_s3(jd, iptr, REG_A1); + + /* move arguments to argument registers */ + M_INTMOVE(s1, REG_A0); + M_INTMOVE(s3, REG_A1); + + /* call builtin function */ + disp = dseg_add_functionptr(cd, BUILTIN_canstore); + M_DSEG_BRANCH(disp); + + /* recompute ip */ + s1 = (s4) (cd->mcodeptr - cd->mcodebase); + M_RECOMPUTE_IP(s1); + + /* check resturn value of builtin */ + M_TST(REG_RESULT, REG_RESULT); + M_BEQ(0); + codegen_add_arraystoreexception_ref(cd); + + /* finally store address into array */ + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + s2 = emit_load_s2(jd, iptr, REG_ITMP2); + s3 = emit_load_s3(jd, iptr, REG_ITMP3); + M_ADD(REG_ITMP1, s1, REG_LSL(s2, 2)); /* REG_ITMP1 = s1 + 4 * s2 */ + M_STR_INTERN(s3, REG_ITMP1, OFFSET(java_objectarray, data[0])); + break; + + case ICMD_IASTORE: /* ..., arrayref, index, value ==> ... */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + s2 = emit_load_s2(jd, iptr, REG_ITMP2); + if (INSTRUCTION_MUST_CHECK(iptr)) { + gen_nullptr_check(s1); + gen_bound_check(s1, s2); + } + s3 = emit_load_s3(jd, iptr, REG_ITMP3); + M_ADD(REG_ITMP1, s1, REG_LSL(s2, 2)); /* REG_ITMP1 = s1 + 4 * s2 */ + M_IST_INTERN(s3, REG_ITMP1, OFFSET(java_intarray, data[0])); + break; + + case ICMD_BASTORE: /* ..., arrayref, index, value ==> ... */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + s2 = emit_load_s2(jd, iptr, REG_ITMP2); + if (INSTRUCTION_MUST_CHECK(iptr)) { + gen_nullptr_check(s1); + gen_bound_check(s1, s2); + } + s3 = emit_load_s3(jd, iptr, REG_ITMP3); + M_ADD(REG_ITMP1, s1, s2); /* REG_ITMP1 = s1 + 1 * s2 */ + M_STRB(s3, REG_ITMP1, OFFSET(java_bytearray, data[0])); + break; + + case ICMD_CASTORE: /* ..., arrayref, index, value ==> ... */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + s2 = emit_load_s2(jd, iptr, REG_ITMP2); + if (INSTRUCTION_MUST_CHECK(iptr)) { + gen_nullptr_check(s1); + gen_bound_check(s1, s2); + } + s3 = emit_load_s3(jd, iptr, REG_ITMP3); + M_ADD(REG_ITMP1, s1, REG_LSL(s2, 1)); /* REG_ITMP1 = s1 + 2 * s2 */ + M_STRH(s3, REG_ITMP1, OFFSET(java_chararray, data[0])); + break; + + case ICMD_SASTORE: /* ..., arrayref, index, value ==> ... */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + s2 = emit_load_s2(jd, iptr, REG_ITMP2); + if (INSTRUCTION_MUST_CHECK(iptr)) { + gen_nullptr_check(s1); + gen_bound_check(s1, s2); + } + s3 = emit_load_s3(jd, iptr, REG_ITMP3); + M_ADD(REG_ITMP1, s1, REG_LSL(s2, 1)); /* REG_ITMP1 = s1 + 2 * s2 */ + M_STRH(s3, REG_ITMP1, OFFSET(java_shortarray, data[0])); + break; + + case ICMD_LASTORE: /* ..., arrayref, index, value ==> ... */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + s2 = emit_load_s2(jd, iptr, REG_ITMP2); + if (INSTRUCTION_MUST_CHECK(iptr)) { + gen_nullptr_check(s1); + gen_bound_check(s1, s2); + } + M_ADD(REG_ITMP3, s1, REG_LSL(s2, 3)); /* REG_ITMP3 = s1 + 8 * s2 */ + s3 = emit_load_s3(jd, iptr, REG_ITMP12_PACKED); + M_LST_INTERN(s3, REG_ITMP3, OFFSET(java_longarray, data[0])); + break; + + case ICMD_FASTORE: /* ..., arrayref, index, value ==> ... */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + s2 = emit_load_s2(jd, iptr, REG_ITMP2); + if (INSTRUCTION_MUST_CHECK(iptr)) { + gen_nullptr_check(s1); + gen_bound_check(s1, s2); + } + M_ADD(REG_ITMP1, s1, REG_LSL(s2, 2)); /* REG_ITMP1 = s1 + 4 * s2 */ +#if !defined(ENABLE_SOFTFLOAT) + s3 = emit_load_s3(jd, iptr, REG_FTMP1); + M_FST_INTERN(s3, REG_ITMP1, OFFSET(java_floatarray, data[0])); +#else + s3 = emit_load_s3(jd, iptr, REG_ITMP3); + M_IST_INTERN(s3, REG_ITMP1, OFFSET(java_floatarray, data[0])); +#endif + break; + + case ICMD_DASTORE: /* ..., arrayref, index, value ==> ... */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + s2 = emit_load_s2(jd, iptr, REG_ITMP2); + if (INSTRUCTION_MUST_CHECK(iptr)) { + gen_nullptr_check(s1); + gen_bound_check(s1, s2); + } + M_ADD(REG_ITMP1, s1, REG_LSL(s2, 3)); /* REG_ITMP1 = s1 + 8 * s2 */ +#if !defined(ENABLE_SOFTFLOAT) + s3 = emit_load_s3(jd, iptr, REG_FTMP1); + M_DST_INTERN(s3, REG_ITMP1, OFFSET(java_doublearray, data[0])); +#else + s3 = emit_load_s3(jd, iptr, VAROP(iptr->sx.s23.s3), REG_ITMP23_PACKED); + M_LST_INTERN(s3, REG_ITMP1, OFFSET(java_doublearray, data[0])); +#endif + break; + + case ICMD_GETSTATIC: /* ... ==> ..., value */ + + if (INSTRUCTION_IS_UNRESOLVED(iptr)) { + unresolved_field *uf = iptr->sx.s23.s3.uf; + + fieldtype = uf->fieldref->parseddesc.fd->type; + + disp = dseg_add_unique_address(cd, NULL); + + codegen_addpatchref(cd, PATCHER_get_putstatic, uf, disp); + + if (opt_showdisassemble) + M_NOP; + } + else { + fieldinfo *fi = iptr->sx.s23.s3.fmiref->p.field; + + fieldtype = fi->type; + + if (!CLASS_IS_OR_ALMOST_INITIALIZED(fi->class)) { + codegen_addpatchref(cd, PATCHER_clinit, fi->class, 0); + + if (opt_showdisassemble) + M_NOP; + } + + disp = dseg_add_address(cd, &(fi->value)); + } + + M_DSEG_LOAD(REG_ITMP3, disp); + switch (fieldtype) { + case TYPE_INT: +#if defined(ENABLE_SOFTFLOAT) + case TYPE_FLT: +#endif + case TYPE_ADR: + d = codegen_reg_of_dst(jd, iptr, REG_ITMP1); + M_ILD_INTERN(d, REG_ITMP3, 0); + break; + case TYPE_LNG: +#if defined(ENABLE_SOFTFLOAT) + case TYPE_DBL: +#endif + d = codegen_reg_of_dst(jd, iptr, REG_ITMP12_PACKED); + M_LLD_INTERN(d, REG_ITMP3, 0); + break; +#if !defined(ENABLE_SOFTFLOAT) + case TYPE_FLT: + d = codegen_reg_of_dst(jd, iptr, REG_FTMP1); + M_FLD_INTERN(d, REG_ITMP3, 0); + break; + case TYPE_DBL: + d = codegen_reg_of_dst(jd, iptr, REG_FTMP1); + M_DLD_INTERN(d, REG_ITMP3, 0); + break; +#endif + default: + assert(0); + } + emit_store_dst(jd, iptr, d); + break; + + case ICMD_PUTSTATIC: /* ..., value ==> ... */ + + if (INSTRUCTION_IS_UNRESOLVED(iptr)) { + unresolved_field *uf = iptr->sx.s23.s3.uf; + + fieldtype = uf->fieldref->parseddesc.fd->type; + + disp = dseg_add_unique_address(cd, NULL); + + codegen_addpatchref(cd, PATCHER_get_putstatic, uf, disp); + + if (opt_showdisassemble) + M_NOP; + } + else { + fieldinfo *fi = iptr->sx.s23.s3.fmiref->p.field; + + fieldtype = fi->type; + + if (!CLASS_IS_OR_ALMOST_INITIALIZED(fi->class)) { + codegen_addpatchref(cd, PATCHER_clinit, fi->class, 0); + + if (opt_showdisassemble) + M_NOP; + } + + disp = dseg_add_address(cd, &(fi->value)); + } + + M_DSEG_LOAD(REG_ITMP3, disp); + switch (fieldtype) { + case TYPE_INT: +#if defined(ENABLE_SOFTFLOAT) + case TYPE_FLT: +#endif + case TYPE_ADR: + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + M_IST_INTERN(s1, REG_ITMP3, 0); + break; + case TYPE_LNG: +#if defined(ENABLE_SOFTFLOAT) + case TYPE_DBL: +#endif + s1 = emit_load_s1(jd, iptr, REG_ITMP12_PACKED); + M_LST_INTERN(s1, REG_ITMP3, 0); + break; +#if !defined(ENABLE_SOFTFLOAT) + case TYPE_FLT: + s1 = emit_load_s1(jd, iptr, REG_FTMP1); + M_FST_INTERN(s1, REG_ITMP3, 0); + break; + case TYPE_DBL: + s1 = emit_load_s1(jd, iptr, REG_FTMP1); + M_DST_INTERN(s1, REG_ITMP3, 0); + break; +#endif + default: + assert(0); + } + break; + + case ICMD_GETFIELD: /* ..., objectref, value ==> ... */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP3); + gen_nullptr_check(s1); +#if !defined(ENABLE_SOFTFLOAT) + /* HACK: softnull checks on floats */ + if (!checknull && IS_FLT_DBL_TYPE(fieldtype)) + gen_nullptr_check_intern(s1); +#endif + + if (INSTRUCTION_IS_UNRESOLVED(iptr)) { + unresolved_field *uf = iptr->sx.s23.s3.uf; + + fieldtype = uf->fieldref->parseddesc.fd->type; + + codegen_addpatchref(cd, PATCHER_get_putfield, + iptr->sx.s23.s3.uf, 0); + + if (opt_showdisassemble) + M_NOP; + + disp = 0; + } + else { + fieldinfo *fi = iptr->sx.s23.s3.fmiref->p.field; + + fieldtype = fi->type; + disp = fi->offset; + } + + switch (fieldtype) { + case TYPE_INT: +#if defined(ENABLE_SOFTFLOAT) + case TYPE_FLT: +#endif + case TYPE_ADR: + d = codegen_reg_of_dst(jd, iptr, REG_ITMP1); + M_ILD(d, s1, disp); + break; + case TYPE_LNG: +#if defined(ENABLE_SOFTFLOAT) + case TYPE_DBL: +#endif + d = codegen_reg_of_dst(jd, iptr, REG_ITMP12_PACKED); + M_LLD(d, s1, disp); + break; +#if !defined(ENABLE_SOFTFLOAT) + case TYPE_FLT: + d = codegen_reg_of_dst(jd, iptr, REG_FTMP1); + M_FLD(d, s1, disp); + break; + case TYPE_DBL: + d = codegen_reg_of_dst(jd, iptr, REG_FTMP1); + M_DLD(d, s1, disp); + break; +#endif + default: + assert(0); + } + emit_store_dst(jd, iptr, d); + break; + + case ICMD_PUTFIELD: /* ..., objectref, value ==> ... */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP3); + gen_nullptr_check(s1); + +#if !defined(ENABLE_SOFTFLOAT) + /* HACK: softnull checks on floats */ + if (!checknull && IS_FLT_DBL_TYPE(fieldtype)) + gen_nullptr_check_intern(s1); +#endif + + if (INSTRUCTION_IS_UNRESOLVED(iptr)) { + unresolved_field *uf = iptr->sx.s23.s3.uf; + + fieldtype = uf->fieldref->parseddesc.fd->type; + } + else { + fieldinfo *fi = iptr->sx.s23.s3.fmiref->p.field; + + fieldtype = fi->type; + disp = fi->offset; + } + + switch (fieldtype) { + case TYPE_INT: +#if defined(ENABLE_SOFTFLOAT) + case TYPE_FLT: +#endif + case TYPE_ADR: + s2 = emit_load_s2(jd, iptr, REG_ITMP1); + break; +#if defined(ENABLE_SOFTFLOAT) + case TYPE_DBL: /* fall through */ +#endif + case TYPE_LNG: + s2 = emit_load_s2(jd, iptr, REG_ITMP12_PACKED); + break; +#if !defined(ENABLE_SOFTFLOAT) + case TYPE_FLT: + case TYPE_DBL: + s2 = emit_load_s2(jd, iptr, REG_FTMP1); + break; +#endif + default: + assert(0); + } + + if (INSTRUCTION_IS_UNRESOLVED(iptr)) { + unresolved_field *uf = iptr->sx.s23.s3.uf; + + codegen_addpatchref(cd, PATCHER_get_putfield, uf, 0); + + if (opt_showdisassemble) + M_NOP; + + disp = 0; + } + + switch (fieldtype) { + case TYPE_INT: +#if defined(ENABLE_SOFTFLOAT) + case TYPE_FLT: +#endif + case TYPE_ADR: + M_IST(s2, s1, disp); + break; + case TYPE_LNG: +#if defined(ENABLE_SOFTFLOAT) + case TYPE_DBL: +#endif + M_LST(s2, s1, disp); + break; +#if !defined(ENABLE_SOFTFLOAT) + case TYPE_FLT: + M_FST(s2, s1, disp); + break; + case TYPE_DBL: + M_DST(s2, s1, disp); + break; +#endif + default: + assert(0); + } + break; + + + /* branch operations **************************************************/ + + case ICMD_ATHROW: /* ..., objectref ==> ... (, objectref) */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + M_INTMOVE(s1, REG_ITMP1_XPTR); + if (INSTRUCTION_IS_UNRESOLVED(iptr)) { + codegen_addpatchref(cd, PATCHER_athrow_areturn, + iptr->sx.s23.s2.uc, 0); + + if (opt_showdisassemble) + M_NOP; + } + disp = dseg_add_functionptr(cd, asm_handle_exception); + M_DSEG_LOAD(REG_ITMP3, disp); + M_MOV(REG_ITMP2_XPC, REG_PC); + M_MOV(REG_PC, REG_ITMP3); + M_NOP; /* nop ensures that XPC is less than the end */ + /* of basic block */ + break; + + case ICMD_GOTO: /* ... ==> ... */ + case ICMD_RET: + + M_B(0); + codegen_addreference(cd, iptr->dst.block); + break; + + case ICMD_JSR: /* ... ==> ... */ + + M_B(0); + codegen_addreference(cd, iptr->sx.s23.s3.jsrtarget.block); + break; + + case ICMD_IFNULL: /* ..., value ==> ... */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + M_TEQ_IMM(s1, 0); + M_BEQ(0); + codegen_addreference(cd, iptr->dst.block); + break; + + case ICMD_IFNONNULL: /* ..., value ==> ... */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + M_TEQ_IMM(s1, 0); + M_BNE(0); + codegen_addreference(cd, iptr->dst.block); + break; + + case ICMD_IFLT: /* ..., value ==> ... */ + case ICMD_IFLE: /* op1 = target JavaVM pc, val.i = constant */ + case ICMD_IFGT: + case ICMD_IFGE: + case ICMD_IFEQ: + case ICMD_IFNE: + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + M_COMPARE(s1, iptr->sx.val.i, UNCOND, 0); + + switch(iptr->opc) { + case ICMD_IFLT: + M_BLT(0); + break; + case ICMD_IFLE: + M_BLE(0); + break; + case ICMD_IFGT: + M_BGT(0); + break; + case ICMD_IFGE: + M_BGE(0); + break; + case ICMD_IFEQ: + M_BEQ(0); + break; + case ICMD_IFNE: + M_BNE(0); + break; + default: + assert(0); + } + codegen_addreference(cd, iptr->dst.block); + break; + + case ICMD_IF_LLT: /* ..., value ==> ... */ + case ICMD_IF_LLE: /* op1 = target JavaVM pc, val.l = constant */ + case ICMD_IF_LGT: + case ICMD_IF_LGE: + case ICMD_IF_LEQ: + case ICMD_IF_LNE: + + /* ATTENTION: compare high words signed and low words unsigned */ + + s1 = emit_load_s1_high(jd, iptr, REG_ITMP1); + M_COMPARE(s1, (iptr->sx.val.l >> 32), UNCOND, 0); + + switch(iptr->opc) { + case ICMD_IF_LLT: + case ICMD_IF_LLE: + M_BLT(0); + codegen_addreference(cd, iptr->dst.block); + break; + case ICMD_IF_LGT: + case ICMD_IF_LGE: + M_BGT(0); + codegen_addreference(cd, iptr->dst.block); + break; + case ICMD_IF_LEQ: /* EQ and NE are the same for unsigned */ + case ICMD_IF_LNE: + break; + default: + assert(0); + } + + s1 = emit_load_s1_low(jd, iptr, REG_ITMP1); + + switch(iptr->opc) { + case ICMD_IF_LLT: + M_COMPARE(s1, (iptr->sx.val.l & 0xffffffff), COND_EQ, 1); + M_BLO(0); + break; + case ICMD_IF_LLE: + M_COMPARE(s1, (iptr->sx.val.l & 0xffffffff), COND_EQ, 1); + M_BLS(0); + break; + case ICMD_IF_LGT: + M_COMPARE(s1, (iptr->sx.val.l & 0xffffffff), COND_EQ, 1); + M_BHI(0); + break; + case ICMD_IF_LGE: + M_COMPARE(s1, (iptr->sx.val.l & 0xffffffff), COND_EQ, 1); + M_BHS(0); + break; + case ICMD_IF_LEQ: + M_COMPARE(s1, (iptr->sx.val.l & 0xffffffff), COND_EQ, 0); + M_BEQ(0); + break; + case ICMD_IF_LNE: + M_COMPARE(s1, (iptr->sx.val.l & 0xffffffff), COND_EQ, 0); + M_BNE(0); + break; + default: + assert(0); + } + codegen_addreference(cd, iptr->dst.block); + break; + + case ICMD_IF_ICMPEQ: /* ..., value, value ==> ... */ + case ICMD_IF_ACMPEQ: /* op1 = target JavaVM pc */ + case ICMD_IF_ICMPNE: + case ICMD_IF_ACMPNE: + case ICMD_IF_ICMPLT: + case ICMD_IF_ICMPLE: + case ICMD_IF_ICMPGT: + case ICMD_IF_ICMPGE: + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + s2 = emit_load_s2(jd, iptr, REG_ITMP2); + M_CMP(s1, s2); + switch(iptr->opc) { + case ICMD_IF_ICMPLT: + M_BLT(0); + break; + case ICMD_IF_ICMPLE: + M_BLE(0); + break; + case ICMD_IF_ICMPGT: + M_BGT(0); + break; + case ICMD_IF_ICMPGE: + M_BGE(0); + break; + case ICMD_IF_ICMPEQ: + case ICMD_IF_ACMPEQ: + M_BEQ(0); + break; + case ICMD_IF_ICMPNE: + case ICMD_IF_ACMPNE: + M_BNE(0); + break; + default: + assert(0); + } + codegen_addreference(cd, iptr->dst.block); + break; + + case ICMD_IF_LCMPEQ: /* ..., value, value ==> ... */ + case ICMD_IF_LCMPNE: /* op1 = target JavaVM pc */ + case ICMD_IF_LCMPLT: + case ICMD_IF_LCMPLE: + case ICMD_IF_LCMPGT: + case ICMD_IF_LCMPGE: + + /* ATTENTION: compare high words signed and low words unsigned */ + s1 = emit_load_s1_high(jd, iptr, REG_ITMP1); + s2 = emit_load_s2_high(jd, iptr, REG_ITMP2); + M_CMP(s1, s2); + + switch(iptr->opc) { + case ICMD_IF_LCMPEQ: /* EQ and NE are the same for unsigned */ + case ICMD_IF_LCMPNE: + break; + case ICMD_IF_LCMPLT: + case ICMD_IF_LCMPLE: + M_BLT(0); + codegen_addreference(cd, iptr->dst.block); + break; + case ICMD_IF_LCMPGT: + case ICMD_IF_LCMPGE: + M_BGT(0); + codegen_addreference(cd, iptr->dst.block); + break; + default: + assert(0); + } + + s1 = emit_load_s1_low(jd, iptr, REG_ITMP1); + s2 = emit_load_s2_low(jd, iptr, REG_ITMP2); + + switch(iptr->opc) { + case ICMD_IF_LCMPEQ: + M_DAT(COND_EQ,0x0a,0,s1,1,0,s2); + M_BEQ(0); + break; + case ICMD_IF_LCMPNE: + M_DAT(COND_EQ,0x0a,0,s1,1,0,s2); + M_BNE(0); + break; + case ICMD_IF_LCMPLT: + M_BNE(1); + M_CMP(s1, s2); + M_BLO(0); + break; + case ICMD_IF_LCMPLE: + M_BNE(1); + M_CMP(s1, s2); + M_BLS(0); + break; + case ICMD_IF_LCMPGT: + M_BNE(1); + M_CMP(s1, s2); + M_BHI(0); + break; + case ICMD_IF_LCMPGE: + M_BNE(1); + M_CMP(s1, s2); + M_BHS(0); + break; + default: + assert(0); + } + codegen_addreference(cd, iptr->dst.block); + break; + + case ICMD_TABLESWITCH: /* ..., index ==> ... */ + { + s4 i, l; + branch_target_t *table; + + table = iptr->dst.table; + + l = iptr->sx.s23.s2.tablelow; + i = iptr->sx.s23.s3.tablehigh; + + /* calculate new index (index - low) */ + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + if (l == 0) { + M_INTMOVE(s1, REG_ITMP1); + } else if (IS_IMM(l)) { + M_SUB_IMM(REG_ITMP1, s1, l); + } else { + ICONST(REG_ITMP2, l); + M_SUB(REG_ITMP1, s1, REG_ITMP2); + } + + /* range check (index <= high-low) */ + i = i - l + 1; + M_COMPARE(REG_ITMP1, i-1, UNCOND, 0); + M_BHI(0); /* unsigned greater than */ + codegen_addreference(cd, table[0].block); + + /* build jump table top down and use address of lowest entry */ + + table += i; + + while (--i >= 0) { + dseg_add_target(cd, table->block); + --table; + } + } + + /* length of dataseg after last dseg_add_target is used by load */ + /* TODO: this loads from data-segment */ + M_ADD(REG_ITMP2, REG_IP, REG_LSL(REG_ITMP1, 2)); + M_LDR(REG_PC, REG_ITMP2, -(cd->dseglen)); + break; + + case ICMD_LOOKUPSWITCH: /* ..., key ==> ... */ + { + s4 i; + lookup_target_t *lookup; + + lookup = iptr->dst.lookup; + + i = iptr->sx.s23.s2.lookupcount; + + /* compare keys */ + MCODECHECK((i<<2)+8); + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + + while (--i >= 0) { + M_COMPARE(s1, lookup->value, UNCOND, 0); + M_BEQ(0); + codegen_addreference(cd, lookup->target.block); + lookup++; + } + + /* default branch */ + M_B(0); + codegen_addreference(cd, iptr->sx.s23.s3.lookupdefault.block); + } + break; + + case ICMD_FRETURN: /* ..., retvalue ==> ... */ + +#if !defined(ENABLE_SOFTFLOAT) + s1 = emit_load_s1(jd, iptr, REG_FTMP1); + M_CAST_FLT_TO_INT_TYPED(VAROP(iptr->s1)->type, s1, REG_RESULT); + goto ICMD_RETURN_do; +#endif + + case ICMD_IRETURN: /* ..., retvalue ==> ... */ + + s1 = emit_load_s1(jd, iptr, REG_RESULT); + M_INTMOVE(s1, REG_RESULT); + goto ICMD_RETURN_do; + + case ICMD_DRETURN: /* ..., retvalue ==> ... */ + +#if !defined(ENABLE_SOFTFLOAT) + s1 = emit_load_s1(jd, iptr, REG_FTMP1); + M_CAST_FLT_TO_INT_TYPED(VAROP(iptr->s1)->type, s1, REG_RESULT_PACKED); + goto ICMD_RETURN_do; +#endif + + case ICMD_LRETURN: /* ..., retvalue ==> ... */ + + s1 = emit_load_s1(jd, iptr, REG_RESULT_PACKED); + M_LNGMOVE(s1, REG_RESULT_PACKED); + goto ICMD_RETURN_do; + + case ICMD_ARETURN: /* ..., retvalue ==> ... */ + + s1 = emit_load_s1(jd, iptr, REG_RESULT); + M_INTMOVE(s1, REG_RESULT); + if (INSTRUCTION_IS_UNRESOLVED(iptr)) { + codegen_addpatchref(cd, PATCHER_athrow_areturn, + iptr->sx.s23.s2.uc, 0); + + if (opt_showdisassemble) + M_NOP; + } + goto ICMD_RETURN_do; + + case ICMD_RETURN: /* ... ==> ... */ + ICMD_RETURN_do: + +#if !defined(NDEBUG) + if (JITDATA_HAS_FLAG_VERBOSECALL(jd)) + emit_verbosecall_exit(jd); +#endif + +#if defined(ENABLE_THREADS) + /* call monitorexit function */ + + if (checksync && (m->flags & ACC_SYNCHRONIZED)) { + /* stack offset for monitor argument */ + + s1 = rd->memuse; + + /* we need to save the proper return value */ + + switch (iptr->opc) { + case ICMD_IRETURN: + case ICMD_ARETURN: + case ICMD_LRETURN: + case ICMD_FRETURN: /* XXX TWISTI: is that correct? */ + case ICMD_DRETURN: + M_STMFD(BITMASK_RESULT, REG_SP); + s1 += 2; + break; + } + + M_LDR(REG_A0, REG_SP, s1 * 4); + disp = dseg_add_functionptr(cd, LOCK_monitor_exit); + M_DSEG_BRANCH(disp); + + /* we no longer need IP here, no more loading */ + /*s1 = (s4) (cd->mcodeptr - cd->mcodebase); + M_RECOMPUTE_IP(s1);*/ + + switch (iptr->opc) { + case ICMD_IRETURN: + case ICMD_ARETURN: + case ICMD_LRETURN: + case ICMD_FRETURN: /* XXX TWISTI: is that correct? */ + case ICMD_DRETURN: + M_LDMFD(BITMASK_RESULT, REG_SP); + break; + } + } +#endif + + /* deallocate stackframe for spilled variables */ + if (spilledregs_num) { + M_ADD_IMM_EXT_MUL4(REG_SP, REG_SP, spilledregs_num); + } + + /* restore callee saved registers + do return */ + if (savedregs_bitmask) { + if (!jd->isleafmethod) { + savedregs_bitmask &= ~(1<isleafmethod) + M_MOV(REG_PC, REG_LR); + break; + + case ICMD_BUILTIN: /* ..., arg1, arg2, arg3 ==> ... */ + + bte = iptr->sx.s23.s3.bte; + md = bte->md; + goto ICMD_INVOKE_do; + + case ICMD_INVOKESTATIC: /* ..., [arg1, [arg2 ...]] ==> ... */ + case ICMD_INVOKESPECIAL:/* ..., objectref, [arg1, [arg2 ...]] ==> ... */ + case ICMD_INVOKEVIRTUAL:/* op1 = arg count, val.a = method pointer */ + case ICMD_INVOKEINTERFACE: + + if (INSTRUCTION_IS_UNRESOLVED(iptr)) { + lm = NULL; + um = iptr->sx.s23.s3.um; + md = um->methodref->parseddesc.md; + } + else { + lm = iptr->sx.s23.s3.fmiref->p.method; + um = NULL; + md = lm->parseddesc; + } + + ICMD_INVOKE_do: + /* copy arguments to registers or stack location */ + + s3 = md->paramcount; + + MCODECHECK((s3 << 1) + 64); + + for (s3 = s3 - 1; s3 >= 0; s3--) { + var = VAR(iptr->sx.s23.s2.args[s3]); + + if (var->flags & PREALLOC) /* argument was precolored? */ + continue; + + /* TODO: document me */ +#if !defined(ENABLE_SOFTFLOAT) + if (IS_INT_LNG_TYPE(var->type)) { +#endif /* !defined(ENABLE_SOFTFLOAT) */ + if (!md->params[s3].inmemory) { + s1 = ARGUMENT_REGS(var->type, md->params[s3].regoff); + SPLIT_OPEN(var->type, s1, REG_ITMP2); + d = emit_load(jd, iptr, var, s1); + + if (IS_2_WORD_TYPE(var->type)) + M_LNGMOVE(d, s1); + else + M_INTMOVE(d, s1); + + SPLIT_STORE_AND_CLOSE(var->type, s1, 0); + } + else { + if (IS_2_WORD_TYPE(var->type)) { + d = emit_load(jd, iptr, var, REG_ITMP12_PACKED); + M_LST(d, REG_SP, md->params[s3].regoff * 4); + } + else { + d = emit_load(jd, iptr, var, REG_ITMP1); + M_IST(d, REG_SP, md->params[s3].regoff * 4); + } + } +#if !defined(ENABLE_SOFTFLOAT) + } + else { + if (!md->params[s3].inmemory) { + s1 = ARGUMENT_REGS(var->type, md->params[s3].regoff); + d = emit_load(jd, iptr, var, REG_FTMP1); + SPLIT_OPEN(var->type, s1, REG_ITMP1); + M_CAST_FLT_TO_INT_TYPED(var->type, d, s1); + SPLIT_STORE_AND_CLOSE(var->type, s1, 0); + } + else { + d = emit_load(jd, iptr, var, REG_FTMP1); + M_STACK_STORE_FLT_TYPED(var->type, d, md->params[s3].regoff); + } + } +#endif /* !defined(ENABLE_SOFTFLOAT) */ + } + + switch (iptr->opc) { + case ICMD_BUILTIN: + disp = dseg_add_functionptr(cd, bte->fp); + + M_DSEG_LOAD(REG_IP, disp); /* Pointer to built-in-function */ + break; + + case ICMD_INVOKESPECIAL: + M_TST(REG_A0, REG_A0); + M_BEQ(0); + codegen_add_nullpointerexception_ref(cd); + /* fall through */ + + case ICMD_INVOKESTATIC: + if (lm == NULL) { + disp = dseg_add_unique_address(cd, NULL); + + codegen_addpatchref(cd, PATCHER_invokestatic_special, + um, disp); + + if (opt_showdisassemble) + M_NOP; + } + else + disp = dseg_add_address(cd, lm->stubroutine); + + M_DSEG_LOAD(REG_IP, disp); /* Pointer to method */ + break; + + case ICMD_INVOKEVIRTUAL: + gen_nullptr_check(REG_A0); + + if (lm == NULL) { + codegen_addpatchref(cd, PATCHER_invokevirtual, um, 0); + + if (opt_showdisassemble) + M_NOP; + + s1 = 0; + } + else + s1 = OFFSET(vftbl_t, table[0]) + + sizeof(methodptr) * lm->vftblindex; + + M_LDR_INTERN(REG_METHODPTR, REG_A0, + OFFSET(java_objectheader, vftbl)); + M_LDR_INTERN(REG_IP, REG_METHODPTR, s1); + break; + + case ICMD_INVOKEINTERFACE: + gen_nullptr_check(REG_A0); + + if (lm == NULL) { + codegen_addpatchref(cd, PATCHER_invokeinterface, um, 0); + + if (opt_showdisassemble) + M_NOP; + + s1 = 0; + s2 = 0; + } + else { + s1 = OFFSET(vftbl_t, interfacetable[0]) - + sizeof(methodptr*) * lm->class->index; + s2 = sizeof(methodptr) * (lm - lm->class->methods); + } + + M_LDR_INTERN(REG_METHODPTR, REG_A0, + OFFSET(java_objectheader, vftbl)); + M_LDR_INTERN(REG_METHODPTR, REG_METHODPTR, s1); + M_LDR_INTERN(REG_IP, REG_METHODPTR, s2); + break; + } + + /* generate the actual call */ + + M_MOV(REG_LR, REG_PC); /* save return address in LR */ + M_MOV(REG_PC, REG_IP); /* branch to method */ + s1 = (s4) (cd->mcodeptr - cd->mcodebase); + M_RECOMPUTE_IP(s1); + + /* actually only used for ICMD_BUILTIN */ + + if (INSTRUCTION_MUST_CHECK(iptr)) { + M_TST(REG_RESULT, REG_RESULT); + M_BEQ(0); + codegen_add_fillinstacktrace_ref(cd); + } + + /* store return value */ + + d = md->returntype.type; + +#if !defined(__SOFTFP__) + /* TODO: this is only a hack, since we use R0/R1 for float + return! this depends on gcc; it is independent from + our ENABLE_SOFTFLOAT define */ + if (iptr->opc == ICMD_BUILTIN && d != TYPE_VOID && IS_FLT_DBL_TYPE(d)) { +#if 0 && !defined(NDEBUG) + dolog("BUILTIN that returns float or double (%s.%s)", m->class->name->text, m->name->text); +#endif + /* we cannot use this macro, since it is not defined + in ENABLE_SOFTFLOAT M_CAST_FLT_TO_INT_TYPED(d, + REG_FRESULT, REG_RESULT_TYPED(d)); */ + if (IS_2_WORD_TYPE(d)) { + DCD(0xed2d8102); /* stfd f0, [sp, #-8]! */ + M_LDRD_UPDATE(REG_RESULT_PACKED, REG_SP, 8); + } else { + DCD(0xed2d0101); /* stfs f0, [sp, #-4]!*/ + M_LDR_UPDATE(REG_RESULT, REG_SP, 4); + } + } +#endif + + if (d != TYPE_VOID) { +#if !defined(ENABLE_SOFTFLOAT) + if (IS_INT_LNG_TYPE(d)) { +#endif /* !defined(ENABLE_SOFTFLOAT) */ + if (IS_2_WORD_TYPE(d)) { + s1 = codegen_reg_of_dst(jd, iptr, REG_RESULT_PACKED); + M_LNGMOVE(REG_RESULT_PACKED, s1); + } + else { + s1 = codegen_reg_of_dst(jd, iptr, REG_RESULT); + M_INTMOVE(REG_RESULT, s1); + } + +#if !defined(ENABLE_SOFTFLOAT) + } else { + s1 = codegen_reg_of_dst(jd, iptr, REG_FTMP1); + M_CAST_INT_TO_FLT_TYPED(VAROP(iptr->dst)->type, REG_RESULT_TYPED(VAROP(iptr->dst)->type), s1); + } +#endif /* !defined(ENABLE_SOFTFLOAT) */ + + emit_store_dst(jd, iptr, s1); + } + break; + + case ICMD_CHECKCAST: /* ..., objectref ==> ..., objectref */ + /* val.a: (classinfo*) superclass */ + if (!(iptr->flags.bits & INS_FLAG_ARRAY)) { + /* object type cast-check */ + + classinfo *super; + s4 superindex; + u1 *branch1 = NULL; + u1 *branch2 = NULL; + u1 *branch3 = NULL; + + if (INSTRUCTION_IS_UNRESOLVED(iptr)) { + super = NULL; + superindex = 0; + } + else { + super = iptr->sx.s23.s3.c.cls; + superindex = super->index; + } + +#if defined(ENABLE_THREADS) + codegen_threadcritrestart(cd, cd->mcodeptr - cd->mcodebase); +#endif + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + + /* if class is not resolved, check which code to call */ + + if (super == NULL) { + M_TST(s1, s1); + M_BEQ(0); + branch1 = cd->mcodeptr; + + disp = dseg_add_unique_s4(cd, 0); /* super->flags */ + codegen_addpatchref(cd, PATCHER_checkcast_instanceof_flags, + iptr->sx.s23.s3.c.ref, disp); + + if (opt_showdisassemble) + M_NOP; + + M_DSEG_LOAD(REG_ITMP2, disp); + disp = dseg_add_s4(cd, ACC_INTERFACE); + M_DSEG_LOAD(REG_ITMP3, disp); + M_TST(REG_ITMP2, REG_ITMP3); + M_BEQ(0); + branch2 = cd->mcodeptr; + } + + /* interface checkcast code */ + + if ((super == NULL) || (super->flags & ACC_INTERFACE)) { + if (super == NULL) { + codegen_addpatchref(cd, + PATCHER_checkcast_instanceof_interface, + iptr->sx.s23.s3.c.ref, 0); + + if (opt_showdisassemble) + M_NOP; + } + else { + M_TST(s1, s1); + M_BEQ(0); + branch1 = cd->mcodeptr; + } + + M_LDR_INTERN(REG_ITMP2, s1, OFFSET(java_objectheader, vftbl)); + M_LDR_INTERN(REG_ITMP3, REG_ITMP2, OFFSET(vftbl_t, interfacetablelength)); + assert(IS_IMM(superindex)); + M_CMP_IMM(REG_ITMP3, superindex); + M_BLE(0); + codegen_add_classcastexception_ref(cd, s1); + + s2 = OFFSET(vftbl_t, interfacetable[0]) - + superindex * sizeof(methodptr*); + + M_LDR_INTERN(REG_ITMP3, REG_ITMP2, s2); + M_TST(REG_ITMP3, REG_ITMP3); + M_BEQ(0); + codegen_add_classcastexception_ref(cd, s1); + + if (super == NULL) { + M_B(0); + branch3 = cd->mcodeptr; + } + } + + if (branch2) { + gen_resolvebranch(branch2, branch2 - cd->mcodebase, + cd->mcodeptr - cd->mcodebase); + } + + /* class checkcast code */ + + + if ((super == NULL) || !(super->flags & ACC_INTERFACE)) { + if (super == NULL) { + disp = dseg_add_unique_address(cd, NULL); + + codegen_addpatchref(cd, PATCHER_checkcast_instanceof_class, + iptr->sx.s23.s3.c.ref, + disp); + + if (opt_showdisassemble) + M_NOP; + } + else { + disp = dseg_add_address(cd, super->vftbl); + + M_TST(s1, s1); + M_BEQ(0); + branch1 = cd->mcodeptr; + } + + M_LDR_INTERN(REG_ITMP2, s1, OFFSET(java_objectheader, vftbl)); + M_DSEG_LOAD(REG_ITMP3, disp); +#if defined(ENABLE_THREADS) + codegen_threadcritstart(cd, cd->mcodeptr - cd->mcodebase); +#endif + M_LDR_INTERN(REG_ITMP2, REG_ITMP2, OFFSET(vftbl_t, baseval)); + M_LDR_INTERN(REG_ITMP3, REG_ITMP3, OFFSET(vftbl_t, baseval)); + M_SUB(REG_ITMP2, REG_ITMP2, REG_ITMP3); + M_DSEG_LOAD(REG_ITMP3, disp); + M_LDR_INTERN(REG_ITMP3, REG_ITMP3, OFFSET(vftbl_t, diffval)); +#if defined(ENABLE_THREADS) + codegen_threadcritstop(cd, cd->mcodeptr - cd->mcodebase); +#endif + M_CMP(REG_ITMP2, REG_ITMP3); + M_BHI(0); + codegen_add_classcastexception_ref(cd, s1); + } + + if (branch1) { + gen_resolvebranch(branch1, branch1 - cd->mcodebase, + cd->mcodeptr - cd->mcodebase); + } + + if (branch3) { + gen_resolvebranch(branch3, branch3 - cd->mcodebase, + cd->mcodeptr - cd->mcodebase); + } + + d = codegen_reg_of_dst(jd, iptr, REG_ITMP1); + } + else { + /* array type cast-check */ + + s1 = emit_load_s1(jd, iptr, REG_A0); + M_INTMOVE(s1, REG_A0); + + if (INSTRUCTION_IS_UNRESOLVED(iptr)) { + disp = dseg_add_unique_address(cd, NULL); + + codegen_addpatchref(cd, PATCHER_builtin_arraycheckcast, + iptr->sx.s23.s3.c.ref, + disp); + + if (opt_showdisassemble) + M_NOP; + } + else + disp = dseg_add_address(cd, iptr->sx.s23.s3.c.cls); + + M_DSEG_LOAD(REG_A1, disp); + disp = dseg_add_functionptr(cd, BUILTIN_arraycheckcast); + M_DSEG_BRANCH(disp); + + /* recompute ip */ + disp = (s4) (cd->mcodeptr - cd->mcodebase); + M_RECOMPUTE_IP(disp); + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + M_TST(REG_RESULT, REG_RESULT); + M_BEQ(0); + codegen_add_classcastexception_ref(cd, s1); + + d = codegen_reg_of_dst(jd, iptr, s1); + } + + M_INTMOVE(s1, d); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_INSTANCEOF: /* ..., objectref ==> ..., intresult */ + /* val.a: (classinfo*) superclass */ + { + classinfo *super; + s4 superindex; + u1 *branch1 = NULL; + u1 *branch2 = NULL; + u1 *branch3 = NULL; + + if (INSTRUCTION_IS_UNRESOLVED(iptr)) { + super = NULL; + superindex = 0; + } + else { + super = iptr->sx.s23.s3.c.cls; + superindex = super->index; + } + +#if defined(ENABLE_THREADS) + codegen_threadcritrestart(cd, cd->mcodeptr - cd->mcodebase); +#endif + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + d = codegen_reg_of_dst(jd, iptr, REG_ITMP2); + if (s1 == d) { + M_MOV(REG_ITMP1, s1); + s1 = REG_ITMP1; + } + + /* if class is not resolved, check which code to call */ + + if (super == NULL) { + M_EOR(d, d, d); + M_TST(s1, s1); + M_BEQ(0); + branch1 = cd->mcodeptr; + + disp = dseg_add_unique_s4(cd, 0); /* super->flags */ + codegen_addpatchref(cd, PATCHER_checkcast_instanceof_flags, + iptr->sx.s23.s3.c.ref, disp); + + if (opt_showdisassemble) + M_NOP; + + M_DSEG_LOAD(REG_ITMP2, disp); + disp = dseg_add_s4(cd, ACC_INTERFACE); + M_DSEG_LOAD(REG_ITMP3, disp); + M_TST(REG_ITMP2, REG_ITMP3); + M_BEQ(0); + branch2 = cd->mcodeptr; + } + + /* interface checkcast code */ + + if ((super == NULL) || (super->flags & ACC_INTERFACE)) { + if (super == NULL) { + /* If d == REG_ITMP2, then it's destroyed in check + code above. */ + if (d == REG_ITMP2) + M_EOR(d, d, d); + + codegen_addpatchref(cd, + PATCHER_checkcast_instanceof_interface, + iptr->sx.s23.s3.c.ref, 0); + + if (opt_showdisassemble) + M_NOP; + } + else { + M_EOR(d, d, d); + M_TST(s1, s1); + M_BEQ(0); + branch1 = cd->mcodeptr; + } + + M_LDR_INTERN(REG_ITMP1, s1, OFFSET(java_objectheader, vftbl)); + M_LDR_INTERN(REG_ITMP3, + REG_ITMP1, OFFSET(vftbl_t, interfacetablelength)); + assert(IS_IMM(superindex)); + M_CMP_IMM(REG_ITMP3, superindex); + M_BLE(2); + + s2 = OFFSET(vftbl_t, interfacetable[0]) - + superindex * sizeof(methodptr*); + + M_LDR_INTERN(REG_ITMP3, REG_ITMP1, s2); + M_TST(REG_ITMP3, REG_ITMP3); + M_MOVNE_IMM(d, 1); + + if (super == NULL) { + M_B(0); + branch3 = cd->mcodeptr; + } + } + + if (branch2) { + gen_resolvebranch(branch2, branch2 - cd->mcodebase, + cd->mcodeptr - cd->mcodebase); + } + + /* class checkcast code */ + + if ((super == NULL) || !(super->flags & ACC_INTERFACE)) { + if (super == NULL) { + disp = dseg_add_unique_address(cd, NULL); + + codegen_addpatchref(cd, PATCHER_checkcast_instanceof_class, + iptr->sx.s23.s3.c.ref, + disp); + + if (opt_showdisassemble) + M_NOP; + } + else { + disp = dseg_add_address(cd, super->vftbl); + + M_EOR(d, d, d); + M_TST(s1, s1); + M_BEQ(0); + branch1 = cd->mcodeptr; + } + + M_LDR_INTERN(REG_ITMP1, s1, OFFSET(java_objectheader, vftbl)); + M_DSEG_LOAD(REG_ITMP2, disp); +#if defined(ENABLE_THREADS) + codegen_threadcritstart(cd, cd->mcodeptr - cd->mcodebase); +#endif + M_LDR_INTERN(REG_ITMP1, REG_ITMP1, OFFSET(vftbl_t, baseval)); + M_LDR_INTERN(REG_ITMP3, REG_ITMP2, OFFSET(vftbl_t, baseval)); + M_LDR_INTERN(REG_ITMP2, REG_ITMP2, OFFSET(vftbl_t, diffval)); +#if defined(ENABLE_THREADS) + codegen_threadcritstop(cd, cd->mcodeptr - cd->mcodebase); +#endif + M_SUB(REG_ITMP1, REG_ITMP1, REG_ITMP3); + M_CMP(REG_ITMP1, REG_ITMP2); + /* If d == REG_ITMP2, then it's destroyed */ + if (d == REG_ITMP2) + M_EOR(d, d, d); + M_MOVLS_IMM(d, 1); + } + + if (branch1) { + gen_resolvebranch(branch1, branch1 - cd->mcodebase, + cd->mcodeptr - cd->mcodebase); + } + + if (branch3) { + gen_resolvebranch(branch3, branch3 - cd->mcodebase, + cd->mcodeptr - cd->mcodebase); + } + + } + + emit_store_dst(jd, iptr, d); + break; + + case ICMD_MULTIANEWARRAY:/* ..., cnt1, [cnt2, ...] ==> ..., arrayref */ + + /* copy sizes to stack if necessary */ + + MCODECHECK((iptr->s1.argcount << 1) + 64); + + for (s1 = iptr->s1.argcount; --s1 >= 0; ) { + + var = VAR(iptr->sx.s23.s2.args[s1]); + + /* copy SAVEDVAR sizes to stack */ + + if (!(var->flags & PREALLOC)) { + s2 = emit_load(jd, iptr, var, REG_ITMP1); + M_STR(s2, REG_SP, s1 * 4); + } + } + + /* a0 = dimension count */ + + assert(IS_IMM(iptr->s1.argcount)); + M_MOV_IMM(REG_A0, iptr->s1.argcount); + + /* is patcher function set? */ + + if (INSTRUCTION_IS_UNRESOLVED(iptr)) { + disp = dseg_add_unique_address(cd, NULL); + + codegen_addpatchref(cd, PATCHER_builtin_multianewarray, + iptr->sx.s23.s3.c.ref, disp); + + if (opt_showdisassemble) + M_NOP; + } + else + disp = dseg_add_address(cd, iptr->sx.s23.s3.c.cls); + + /* a1 = arraydescriptor */ + + M_DSEG_LOAD(REG_A1, disp); + + /* a2 = pointer to dimensions = stack pointer */ + + M_INTMOVE(REG_SP, REG_A2); + + /* call builtin_multianewarray here */ + + disp = dseg_add_functionptr(cd, BUILTIN_multianewarray); + M_DSEG_BRANCH(disp); + + /* recompute ip (pv) */ + + s1 = (s4) (cd->mcodeptr - cd->mcodebase); + M_RECOMPUTE_IP(s1); + + /* check for exception before result assignment */ + + M_TST(REG_RESULT, REG_RESULT); + M_BEQ(0); + codegen_add_fillinstacktrace_ref(cd); + + /* get arrayref */ + + d = codegen_reg_of_dst(jd, iptr, REG_RESULT); + M_INTMOVE(REG_RESULT, d); + emit_store_dst(jd, iptr, d); + break; + + case ICMD_CHECKNULL: /* ..., objectref ==> ..., objectref */ + + s1 = emit_load_s1(jd, iptr, REG_ITMP1); + M_TST(s1, s1); + M_BEQ(0); + codegen_add_nullpointerexception_ref(cd); + break; + + default: + *exceptionptr = new_internalerror("Unknown ICMD %d", iptr->opc); + return false; + } /* the big switch */ + + } /* for all instructions */ + + } /* for all basic blocks */ + + dseg_createlinenumbertable(cd); + + + /* generate exception and patcher stubs */ + + emit_exception_stubs(jd); + emit_patcher_stubs(jd); + + codegen_finish(jd); + + /* everything's ok */ + + return true; +} + + +/* createcompilerstub ********************************************************** + + creates a stub routine which calls the compiler + +*******************************************************************************/ + +#define COMPILERSTUB_DATASIZE 3 * SIZEOF_VOID_P +#define COMPILERSTUB_CODESIZE 2 * 4 + +#define COMPILERSTUB_SIZE COMPILERSTUB_DATASIZE + COMPILERSTUB_CODESIZE + + +u1 *createcompilerstub(methodinfo *m) +{ + u1 *s; /* memory to hold the stub */ + ptrint *d; + codeinfo *code; + codegendata *cd; + s4 dumpsize; /* code generation pointer */ + + s = CNEW(u1, COMPILERSTUB_SIZE); + + /* set data pointer and code pointer */ + + d = (ptrint *) s; + s = s + COMPILERSTUB_DATASIZE; + + /* mark start of dump memory area */ + + dumpsize = dump_size(); + + cd = DNEW(codegendata); + cd->mcodeptr = s; + + /* Store the codeinfo pointer in the same place as in the + methodheader for compiled methods. */ + + code = code_codeinfo_new(m); + + d[0] = (ptrint) asm_call_jit_compiler; + d[1] = (ptrint) m; + d[2] = (ptrint) code; + + /* code for the stub */ + + M_LDR_INTERN(REG_ITMP1, REG_PC, -(2 * 4 + 2 * SIZEOF_VOID_P)); + M_LDR_INTERN(REG_PC, REG_PC, -(3 * 4 + 3 * SIZEOF_VOID_P)); + +#if defined(ENABLE_STATISTICS) + if (opt_stat) + count_cstub_len += COMPILERSTUB_SIZE * 4; +#endif + + /* release dump area */ + + dump_release(dumpsize); + + /* synchronize instruction and data cache */ + + md_cacheflush(s, cd->mcodeptr - (u1 *) d); + + return s; +} + + +/* createnativestub ************************************************************ + + Creates a stub routine which calls a native method. + +*******************************************************************************/ + +u1 *createnativestub(functionptr f, jitdata *jd, methoddesc *nmd) +{ + methodinfo *m; + codeinfo *code; + codegendata *cd; + registerdata *rd; + s4 nativeparams; + methoddesc *md; + s4 i, j; + s4 t; + s4 disp, funcdisp, s1, s2; + + /* get required compiler data */ + + m = jd->m; + code = jd->code; + cd = jd->cd; + rd = jd->rd; + + /* initialize variables */ + + md = m->parseddesc; + nativeparams = (m->flags & ACC_STATIC) ? 2 : 1; + + /* calculate stackframe size */ + + cd->stackframesize = + 1 + /* return address */ + sizeof(stackframeinfo) / SIZEOF_VOID_P + /* stackframeinfo */ + sizeof(localref_table) / SIZEOF_VOID_P + /* localref_table */ + nmd->memuse; /* stack arguments */ + + /* create method header */ + + (void) dseg_add_unique_address(cd, code); /* CodeinfoPointer */ + (void) dseg_add_unique_s4(cd, cd->stackframesize * 4); /* FrameSize */ + (void) dseg_add_unique_s4(cd, 0); /* IsSync */ + (void) dseg_add_unique_s4(cd, 0); /* IsLeaf */ + (void) dseg_add_unique_s4(cd, 0); /* IntSave */ + (void) dseg_add_unique_s4(cd, 0); /* FltSave */ + (void) dseg_addlinenumbertablesize(cd); + (void) dseg_add_unique_s4(cd, 0); /* ExTableSize */ + + /* generate stub code */ + /* TODO: don't forget ... there is a M_ADD_IMM at the end of this stub!!! */ + M_STMFD(1<stackframesize - 1) { + M_SUB_IMM_EXT_MUL4(REG_SP, REG_SP, cd->stackframesize - 1); + } + +#if !defined(NDEBUG) + if (JITDATA_HAS_FLAG_VERBOSECALL(jd)) + emit_verbosecall_enter(jd); +#endif + + /* get function address (this must happen before the stackframeinfo) */ + + funcdisp = dseg_add_functionptr(cd, f); + +#if !defined(WITH_STATIC_CLASSPATH) + if (f == NULL) { + codegen_addpatchref(cd, PATCHER_resolve_native, m, funcdisp); + + if (opt_showdisassemble) + M_NOP; + } +#endif + + /* save integer and float argument registers */ + M_STMFD(BITMASK_ARGS | (1<stackframesize * 4)); + M_ADD_IMM(REG_A0, REG_SP, 20 + cd->stackframesize * 4 - SIZEOF_VOID_P); + M_MOV(REG_A1, REG_IP); + M_ADD_IMM(REG_A2, REG_SP, 20 + cd->stackframesize * 4); + M_LDR_INTERN(REG_A3, REG_SP, 20 + cd->stackframesize * 4 - SIZEOF_VOID_P); + disp = dseg_add_functionptr(cd, codegen_start_native_call); + M_DSEG_BRANCH(disp); + + /* recompute ip */ + /*s1 = (s4) (cd->mcodeptr - cd->mcodebase); + M_RECOMPUTE_IP(s1);*/ + + /* restore integer and float argument registers */ + M_LDMFD(BITMASK_ARGS | (1<paramcount - 1, j = i + nativeparams; i >= 0; i--, j--) { + t = md->paramtypes[i].type; + + if (!md->params[i].inmemory) { + s1 = ARGUMENT_REGS(t, md->params[i].regoff); + + if (!nmd->params[j].inmemory) { + s2 = ARGUMENT_REGS(t, nmd->params[j].regoff); + SPLIT_OPEN(t, s1, REG_ITMP1); + SPLIT_LOAD(t, s1, cd->stackframesize); + SPLIT_OPEN(t, s2, REG_ITMP1); + + if (IS_2_WORD_TYPE(t)) + M_LNGMOVE(s1, s2); + else + M_INTMOVE(s1, s2); + + SPLIT_STORE_AND_CLOSE(t, s2, 0); + } + else { + s2 = nmd->params[j].regoff; + SPLIT_OPEN(t, s1, REG_ITMP1); + SPLIT_LOAD(t, s1, cd->stackframesize); + + if (IS_2_WORD_TYPE(t)) + M_LST(s1, REG_SP, s2 * 4); + else + M_IST(s1, REG_SP, s2 * 4); + /* no SPLIT_CLOSE here because argument is fully on stack now */ + } + } + else { + s1 = md->params[i].regoff + cd->stackframesize; + s2 = nmd->params[j].regoff; + + if (IS_2_WORD_TYPE(t)) { + M_LLD(REG_ITMP12_PACKED, REG_SP, s1 * 4); + M_LST(REG_ITMP12_PACKED, REG_SP, s2 * 4); + } + else { + M_ILD(REG_ITMP1, REG_SP, s1 * 4); + M_IST(REG_ITMP1, REG_SP, s2 * 4); + } + } + } + + /* put class into second argument register */ + if (m->flags & ACC_STATIC) { + disp = dseg_add_address(cd, m->class); + M_DSEG_LOAD(REG_A1, disp); + } + + /* put env into first argument register */ + disp = dseg_add_address(cd, _Jv_env); + M_DSEG_LOAD(REG_A0, disp); + + /* do the native function call */ + M_DSEG_BRANCH(funcdisp); /* call native method */ + + /* recompute ip from pc */ + /* TODO: this is only needed because of the tracer ... do we + really need it? */ + s1 = (s4) (cd->mcodeptr - cd->mcodebase); + M_RECOMPUTE_IP(s1); + +#if !defined(__SOFTFP__) + /* TODO: this is only a hack, since we use R0/R1 for float return! */ + /* this depends on gcc; it is independent from our ENABLE_SOFTFLOAT define */ + if (md->returntype.type != TYPE_VOID && IS_FLT_DBL_TYPE(md->returntype.type)) { +#if 0 && !defined(NDEBUG) + dolog("NATIVESTUB that returns float or double (%s.%s)", m->class->name->text, m->name->text); +#endif + /* we cannot use this macro, since it is not defined in ENABLE_SOFTFLOAT */ + /* M_CAST_FLT_TO_INT_TYPED(md->returntype.type, REG_FRESULT, REG_RESULT_TYPED(md->returntype.type)); */ + if (IS_2_WORD_TYPE(md->returntype.type)) { + DCD(0xed2d8102); /* stfd f0, [sp, #-8]! */ + M_LDRD_UPDATE(REG_RESULT_PACKED, REG_SP, 8); + } else { + DCD(0xed2d0101); /* stfs f0, [sp, #-4]!*/ + M_LDR_UPDATE(REG_RESULT, REG_SP, 4); + } + } +#endif + +#if !defined(NDEBUG) + if (JITDATA_HAS_FLAG_VERBOSECALL(jd)) + emit_verbosecall_exit(jd); +#endif + + /* remove native stackframe info */ + /* TODO: improve this store/load */ + + M_STMFD(BITMASK_RESULT | (1<stackframesize * 4 - SIZEOF_VOID_P); + disp = dseg_add_functionptr(cd, codegen_finish_native_call); + M_DSEG_BRANCH(disp); + M_MOV(REG_ITMP1_XPTR, REG_RESULT); + M_LDMFD(BITMASK_RESULT | (1<stackframesize - 1) + M_ADD_IMM_EXT_MUL4(REG_SP, REG_SP, cd->stackframesize - 1); + + M_LDMFD(1<entrypoint; +} + + +/* asm_debug ******************************************************************* + + Lazy debugger! + +*******************************************************************************/ + +void asm_debug(int a1, int a2, int a3, int a4) +{ + printf("===> i am going to exit after this debugging message!\n"); + printf("got asm_debug(%p, %p, %p, %p)\n",(void*)a1,(void*)a2,(void*)a3,(void*)a4); + throw_cacao_exception_exit(string_java_lang_InternalError, "leave you now"); +} + + +/* + * These are local overrides for various environment variables in Emacs. + * Please do not remove this and leave it at the end of the file, where + * Emacs will automagically detect them. + * --------------------------------------------------------------------- + * Local variables: + * mode: c + * indent-tabs-mode: t + * c-basic-offset: 4 + * tab-width: 4 + * End: + * vim:noexpandtab:sw=4:ts=4: + */ diff --git a/src/vm/jit/arm/codegen.h b/src/vm/jit/arm/codegen.h new file mode 100644 index 000000000..5cca08322 --- /dev/null +++ b/src/vm/jit/arm/codegen.h @@ -0,0 +1,1244 @@ +/* src/vm/jit/arm/codegen.h - code generation macros and definitions for ARM + + Copyright (C) 1996-2005, 2006 R. Grafl, A. Krall, C. Kruegel, + C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring, + E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich, + J. Wenninger, Institut f. Computersprachen - TU Wien + + This file is part of CACAO. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + + Contact: cacao@cacaojvm.org + + Authors: Michael Starzinger + Christian Thalinger + + $Id: codegen.h 6591 2007-01-02 19:14:25Z twisti $ + +*/ + + +#ifndef _CODEGEN_H +#define _CODEGEN_H + +#include "config.h" + + +/* helper macros for generating code ******************************************/ + +/* load_var_to_reg_xxx: + this function generates code to fetch data from a pseudo-register + into a real register. + If the pseudo-register has actually been assigned to a real + register, no code will be emitted, since following operations + can use this register directly. + v: pseudoregister to be fetched from + tempnr: temporary register to be used if v is actually spilled to ram + regnr: the register number, where the operand can be found after + fetching (this wil be either tempregnum or the register + number allready given to v) +*/ + +#if defined(__ARMEL__) +#define load_var_to_reg_lng(regnr,v,tempnr) { \ + if ((v)->flags & INMEMORY) { \ + COUNT_SPILLS; \ + M_STACK_LOAD_LNG(tempnr, (v)->regoff); \ + regnr = tempnr; \ + } else if (GET_HIGH_REG((v)->regoff)==REG_SPLIT) { \ + M_LDR_INTERN(GET_HIGH_REG(tempnr), REG_SP, 0); /* TODO: where to load? */ \ + regnr = PACK_REGS(GET_LOW_REG((v)->regoff), GET_HIGH_REG(tempnr)); \ + } else regnr = (v)->regoff; \ +} +#else /* defined(__ARMEB__) */ +#define load_var_to_reg_lng(regnr,v,tempnr) { \ + if ((v)->flags & INMEMORY) { \ + COUNT_SPILLS; \ + M_STACK_LOAD_LNG(tempnr, (v)->regoff); \ + regnr = tempnr; \ + } else if (GET_LOW_REG((v)->regoff)==REG_SPLIT) { \ + M_LDR_INTERN(GET_LOW_REG(tempnr), REG_SP, 0); /* TODO: where to load? */ \ + regnr = PACK_REGS(GET_LOW_REG(tempnr), GET_HIGH_REG((v)->regoff)); \ + } else regnr = (v)->regoff; \ +} +#endif + + +/* store_reg_to_var_xxx: + This function generates the code to store the result of an operation + back into a spilled pseudo-variable. + If the pseudo-variable has not been spilled in the first place, this + function will generate nothing. + v: Pseudovariable + tempnr: Number of the temporary registers as returned by + reg_of_var. +*/ + +#if defined(__ARMEL__) +#define store_reg_to_var_lng(v,tempnr) { \ + if ((v)->flags & INMEMORY) { \ + COUNT_SPILLS; \ + M_STACK_STORE_LNG(tempnr, (v)->regoff); \ + } else if (GET_HIGH_REG((v)->regoff)==REG_SPLIT) { \ + M_STR_INTERN(GET_HIGH_REG(tempnr), REG_SP, 0); /* TODO: where to store? */ \ + } \ +} +#else /* defined(__ARMEB__) */ +#define store_reg_to_var_lng(v,tempnr) { \ + if ((v)->flags & INMEMORY) { \ + COUNT_SPILLS; \ + M_STACK_STORE_LNG(tempnr, (v)->regoff); \ + } else if (GET_LOW_REG((v)->regoff)==REG_SPLIT) { \ + M_STR_INTERN(GET_LOW_REG(tempnr), REG_SP, 0); /* TODO: where to store? */ \ + } \ +} +#endif + +#if defined(__ARMEL__) +#define SPLIT_OPEN(type, reg, tmpreg) \ + if (IS_2_WORD_TYPE(type) && GET_HIGH_REG(reg)==REG_SPLIT) { \ + /*dolog("SPLIT_OPEN({R%d;SPL} > {R%d;R%d})", GET_LOW_REG(reg), GET_LOW_REG(reg), tmpreg);*/ \ + /*assert(GET_LOW_REG(reg) == 3);*/ \ + (reg) = PACK_REGS(GET_LOW_REG(reg), tmpreg); \ + } +#define SPLIT_LOAD(type, reg, offset) \ + if (IS_2_WORD_TYPE(type) && GET_LOW_REG(reg)==3) { \ + /*dolog("SPLIT_LOAD({R%d;R%d} from [%x])", GET_LOW_REG(reg), GET_HIGH_REG(reg), offset);*/ \ + M_LDR(GET_HIGH_REG(reg), REG_SP, 4 * (offset)); \ + } +#define SPLIT_STORE_AND_CLOSE(type, reg, offset) \ + if (IS_2_WORD_TYPE(type) && GET_LOW_REG(reg)==3) { \ + /*dolog("SPLIT_STORE({R%d;R%d} to [%x])", GET_LOW_REG(reg), GET_HIGH_REG(reg), offset);*/ \ + M_STR(GET_HIGH_REG(reg), REG_SP, 4 * (offset)); \ + (reg) = PACK_REGS(GET_LOW_REG(reg), REG_SPLIT); \ + } +#else /* defined(__ARMEB__) */ +#define SPLIT_OPEN(type, reg, tmpreg) \ + if (IS_2_WORD_TYPE(type) && GET_LOW_REG(reg)==REG_SPLIT) { \ + /*dolog("SPLIT_OPEN({SPL;R%d} > {R%d;R%d})", GET_HIGH_REG(reg), tmpreg, GET_HIGH_REG(reg));*/ \ + /*assert(GET_HIGH_REG(reg) == 3);*/ \ + (reg) = PACK_REGS(tmpreg, GET_HIGH_REG(reg)); \ + } +#define SPLIT_LOAD(type, reg, offset) \ + if (IS_2_WORD_TYPE(type) && GET_HIGH_REG(reg)==3) { \ + /*dolog("SPLIT_LOAD({R%d;R%d} from [%x])", GET_LOW_REG(reg), GET_HIGH_REG(reg), offset);*/ \ + M_LDR(GET_LOW_REG(reg), REG_SP, 4 * (offset)); \ + } +#define SPLIT_STORE_AND_CLOSE(type, reg, offset) \ + if (IS_2_WORD_TYPE(type) && GET_HIGH_REG(reg)==3) { \ + /*dolog("SPLIT_STORE({R%d;R%d} to [%x])", GET_LOW_REG(reg), GET_HIGH_REG(reg), offset);*/ \ + M_STR(GET_LOW_REG(reg), REG_SP, 4 * (offset)); \ + (reg) = PACK_REGS(REG_SPLIT, GET_HIGH_REG(reg)); \ + } +#endif + + +#define MCODECHECK(icnt) \ + do { \ + if ((cd->mcodeptr + (icnt) * 4) > cd->mcodeend) \ + codegen_increase(cd); \ + } while (0) + + +/* TODO: correct this! */ +#define IS_IMM(val) ( ((val) >= 0) && ((val) <= 255) ) +#define IS_OFFSET(off,max) ((s4)(off) <= (max) && (s4)(off) >= -(max)) + +#if !defined(NDEBUG) +# define CHECK_INT_REG(r) if ((r)<0 || (r)>15) printf("CHECK_INT_REG: this is not an integer register: %d\n", r); assert((r)>=0 && (r)<=15) +# define CHECK_FLT_REG(r) if ((r)<0 || (r)>7) printf("CHECK_FLT_REG: this is not an float register: %d\n", r); assert((r)>=0 && (r)<=7) +# define CHECK_OFFSET(off,max) \ + if (!IS_OFFSET(off,max)) printf("CHECK_OFFSET: offset out of range: %x (>%x) SEVERE ERROR!!!\n", ((off)<0)?-(off):off, max); \ + assert(IS_OFFSET(off,max)) +#else +# define CHECK_INT_REG(r) +# define CHECK_FLT_REG(r) +# define CHECK_OFFSET(off,max) +#endif + + +/* branch defines *************************************************************/ + +#define BRANCH_NOPS \ + do { \ + M_NOP; \ + } while (0) + + +/* patcher defines ************************************************************/ + +#define PATCHER_CALL_SIZE 1 * 4 /* an instruction is 4-bytes long */ + +#define PATCHER_NOPS \ + do { \ + M_NOP; \ + } while (0) + + +/* lazy debugger **************************************************************/ + +#if !defined(NDEBUG) +void asm_debug(int a1, int a2, int a3, int a4); +void asm_debug_intern(int a1, int a2, int a3, int a4); + +/* if called with this macros, it can be placed nearly anywhere */ +/* almost all registers are saved and restored afterwards */ +/* it uses a long branch to call the asm_debug_intern (no exit) */ +#define ASM_DEBUG_PREPARE \ + M_STMFD(0x7fff, REG_SP) +#define ASM_DEBUG_EXECUTE \ + M_LONGBRANCH(asm_debug_intern); \ + M_LDMFD(0x7fff, REG_SP) +#endif + + +/* macros to create code ******************************************************/ + +/* the condition field */ +#define COND_EQ 0x0 /* Equal Z set */ +#define COND_NE 0x1 /* Not equal Z clear */ +#define COND_CS 0x2 /* Carry set C set */ +#define COND_CC 0x3 /* Carry clear C clear */ +#define COND_MI 0x4 /* Negative N set */ +#define COND_PL 0x5 /* Positive N clear */ +#define COND_VS 0x6 /* Overflow V set */ +#define COND_VC 0x7 /* No overflow V clear */ +#define COND_HI 0x8 /* Unsigned higher */ +#define COND_LS 0x9 /* Unsigned lower, same */ +#define COND_GE 0xA /* Sig. greater, equal */ +#define COND_LT 0xB /* Sig. less than */ +#define COND_GT 0xC /* Sig. greater than */ +#define COND_LE 0xD /* Sig. less, equal */ +#define COND_AL 0xE /* Always */ +#define CONDNV 0xF /* Special (see A3-5) */ +#define UNCOND COND_AL + +/* data processing operation: M_DAT + cond ... conditional execution + op ..... opcode + d ...... destination reg + n ...... source reg + S ...... update condition codes + I ...... switch to immediate mode + shift .. shifter operand +*/ + +#define M_DAT(cond,op,d,n,S,I,shift) \ + do { \ + *((u4 *) cd->mcodeptr) = (((cond) << 28) | ((op) << 21) | ((d) << 12) | ((n) << 16) | ((I) << 25) | ((S) << 20) | ((shift) & 0x00000fff)); \ + cd->mcodeptr += 4; \ + } while (0) + + +/* load and store instruction: M_MEM + cond ... conditional execution + L ...... load (L=1) or store (L=0) + B ...... unsigned byte (B=1) or word (B=0) + d ...... destination reg + n ...... base reg for addressing + adr .... addressing mode specific +*/ + +#define M_MEM(cond,L,B,d,n,adr,I,P,U,W) \ + do { \ + *((u4 *) cd->mcodeptr) = (((cond) << 28) | (1 << 26) | ((L) << 20) | ((B) << 22) | ((d) << 12) | ((n) << 16) | ((adr) & 0x0fff) | ((I) << 25) | ((P) << 24) | ((U) << 23) | ((W) << 21)); \ + cd->mcodeptr += 4; \ + } while (0) + + +/* load and store instruction: M_MEM2 + cond ... conditional execution + L ...... load (L=1) or store (L=0) + H ...... halfword (H=1) or signed byte (H=0) + S ...... signed (S=1) or unsigned (S=0) halfword + d ...... destination reg + n ...... base reg for addressing + adr .... addressing mode specific +*/ + +#define M_MEM2(cond,L,H,S,d,n,adr,I,P,U,W) \ + do { \ + *((u4 *) cd->mcodeptr) = (((cond) << 28) | (1 << 22) | (0x9 << 4) | ((L) << 20) | ((H) << 5) | ((S) << 6) | ((d) << 12) | ((n) << 16) | ((adr) & 0x0f) | (((adr) & 0xf0) << (8-4)) | ((I) << 22) | ((P) << 24) | ((U) << 23) | ((W) << 21)); \ + cd->mcodeptr += 4; \ + } while (0) + + +/* load and store multiple instruction: M_MEM_MULTI + cond ... conditional execution + L ...... load (L=1) or store (L=0) + S ...... special (see "The ARM ARM A3-21") + regs ... register list + n ...... base reg for addressing +*/ + +#define M_MEM_MULTI(cond,L,S,regs,n,P,U,W) \ + do { \ + *((u4 *) cd->mcodeptr) = (((cond) << 28) | (1 << 27) | ((L) << 20) | ((S) << 22) | ((n) << 16) | ((regs) & 0xffff) | ((P) << 24) | ((U) << 23) | ((W) << 21)); \ + cd->mcodeptr += 4; \ + } while (0) + + +/* branch and branch with link: M_BRA + cond ... conditional execution + L ...... branch with link (L=1) + offset . 24bit offset +*/ + +#define M_BRA(cond,L,offset) \ + do { \ + *((u4 *) cd->mcodeptr) = (((cond) << 28) | (0x5 << 25) | ((L) << 24) | ((offset) & 0x00ffffff)); \ + cd->mcodeptr += 4; \ + } while (0) + + +/* multiplies: M_MULT + cond ... conditional execution + d ...... destination register + n, m ... source registers + S ...... update conditional codes + A ...... accumulate flag (enables third source) + s ...... third source register +*/ + +#define M_MULT(cond,d,n,m,S,A,s) \ + do { \ + *((u4 *) cd->mcodeptr) = (((cond) << 28) | ((d) << 16) | ((n) << 8) | (m) | (0x09 << 4) | ((S) << 20) | ((A) << 21) | ((s) << 12)); \ + cd->mcodeptr += 4; \ + } while (0) + + +/* no operation (mov r0,r0): M_NOP */ + +#define M_NOP \ + do { \ + *((u4 *) cd->mcodeptr) = (0xe1a00000); \ + cd->mcodeptr += 4; \ + } while (0) + + +/* software breakpoint (only v5 and above): M_BREAKPOINT */ + +#define M_BREAKPOINT(imm) \ + do { \ + *((u4 *) cd->mcodeptr) = (0x0e12 << 20) | (0x07 << 4) | (((imm) & 0xfff0) << (8-4)) | ((imm) & 0x0f); \ + cd->mcodeptr += 4; \ + } while (0) + + +#if !defined(ENABLE_SOFTFLOAT) + +/* M_CPDO ********************************************************************** + + Floating-Point Coprocessor Data Operations + + cond ... conditional execution + op ..... opcode + D ...... dyadic (D=0) or monadic (D=1) instruction + Fd ..... destination float-register + Fn ..... source float-register + Fm ..... source float-register or immediate + +*******************************************************************************/ + +#define M_CPDOS(cond,op,D,Fd,Fn,Fm) \ + do { \ + *((u4 *) cd->mcodeptr) = (((cond) << 28) | (0x0e << 24) | (1 << 8) | ((op) << 20) | ((D) << 15) | ((Fd) << 12) | ((Fn) << 16) | ((Fm) & 0x0f)); \ + cd->mcodeptr += 4; \ + } while (0) + + +#define M_CPDOD(cond,op,D,Fd,Fn,Fm) \ + do { \ + *((u4 *) cd->mcodeptr) = (((cond) << 28) | (0x0e << 24) | (1 << 8) | ((op) << 20) | ((D) << 15) | ((Fd) << 12) | ((Fn) << 16) | ((Fm) & 0x0f) | (1 << 7)); \ + cd->mcodeptr += 4; \ + } while (0) + + +/* M_CPDT ********************************************************************** + + Floating-Point Coprocessor Data Transfer + + cond ... conditional execution + L ...... load (L=1) or store (L=0) + Fd ..... destination float-register + n ...... base reg for addressing + +*******************************************************************************/ + +#define M_CPDT(cond,L,T1,T0,Fd,n,off,P,U,W) \ + do { \ + *((u4 *) cd->mcodeptr) = (((cond) << 28) | (0x0c << 24) | (1 << 8) | ((L) << 20) | ((T1) << 22) | ((T0) << 15) | ((Fd) << 12) | ((n) << 16) | ((off) & 0xff) | ((P) << 24) | ((U) << 23) | ((W) << 21)); \ + cd->mcodeptr += 4; \ + } while (0) + + +/* M_CPRT ********************************************************************** + + Floating-Point Coprocessor Register Transfer + + XXX + +*******************************************************************************/ + +#define M_CPRTS(cond,L,d,Fn,Fm) \ + do { \ + *((u4 *) cd->mcodeptr) = (((cond) << 28) | (0x0e << 24) | (1 << 8) | (1 << 4) | ((L) << 20) | ((d) << 12) | ((Fn) << 16) | (Fm)); \ + cd->mcodeptr += 4; \ + } while (0) + + +#define M_CPRTD(cond,L,d,Fn,Fm) \ + do { \ + *((u4 *) cd->mcodeptr) = (((cond) << 28) | (0x0e << 24) | (1 << 8) | (1 << 4) | ((L) << 20) | ((d) << 12) | ((Fn) << 16) | (Fm) | (1 << 7)); \ + cd->mcodeptr += 4; \ + } while (0) + + +#define M_CPRTI(cond,L,d,Fn,Fm) \ + do { \ + *((u4 *) cd->mcodeptr) = (((cond) << 28) | (0x0e << 24) | (1 << 8) | (1 << 4) | ((L) << 20) | ((d) << 12) | ((Fn) << 16) | (Fm) | (3 << 5)); \ + cd->mcodeptr += 4; \ + } while (0) + + +/* XXX TWISTI: replace X by something useful */ + +#define M_CPRTX(cond,L,d,Fn,Fm) \ + do { \ + *((u4 *) cd->mcodeptr) = (((cond) << 28) | (0x0e << 24) | (1 << 8) | (1 << 4) | ((L) << 20) | ((d) << 12) | ((Fn) << 16) | (Fm) | (1 << 23)); \ + cd->mcodeptr += 4; \ + } while (0) + +#endif /* !defined(ENABLE_SOFTFLOAT) */ + + +/* used to store values! */ +#define DCD(val) \ + do { \ + *((u4 *) cd->mcodeptr) = val; \ + cd->mcodeptr += 4; \ + } while (0) + + +/* used to directly access shifter; insert this as shifter operand! */ +#define REG_LSL(reg, shift) ( (((shift) & 0x1f) << 7) | ((reg) & 0x0f) ) +#define REG_LSR(reg, shift) ( (((shift) & 0x1f) << 7) | ((reg) & 0x0f) | (1 << 5) ) +#define REG_ASR(reg, shift) ( (((shift) & 0x1f) << 7) | ((reg) & 0x0f) | (1 << 6) ) +#define REG_LSL_REG(reg, s) ( (((s) & 0x0f) << 8) | ((reg) & 0x0f) | (1 << 4) ) +#define REG_LSR_REG(reg, s) ( (((s) & 0x0f) << 8) | ((reg) & 0x0f) | (1 << 4) | (1 << 5) ) +#define REG_ASR_REG(reg, s) ( (((s) & 0x0f) << 8) | ((reg) & 0x0f) | (1 << 4) | (1 << 6) ) + +/* used to directly rotate immediate values; insert this as immediate! */ +/* ATTENTION: this rotates the immediate right by (2 * rot) bits */ +#define IMM_ROTR(imm, rot) ( ((imm) & 0xff) | (((rot) & 0x0f) << 8) ) +#define IMM_ROTL(imm, rot) IMM_ROTR(imm, 16-(rot)) + +/* macros for all arm instructions ********************************************/ + +#define M_ADD(d,a,b) M_DAT(UNCOND,0x04,d,a,0,0,b) /* d = a + b */ +#define M_ADC(d,a,b) M_DAT(UNCOND,0x05,d,a,0,0,b) /* d = a + b (with Carry) */ +#define M_SUB(d,a,b) M_DAT(UNCOND,0x02,d,a,0,0,b) /* d = a - b */ +#define M_SBC(d,a,b) M_DAT(UNCOND,0x06,d,a,0,0,b) /* d = a - b (with Carry) */ +#define M_AND(d,a,b) M_DAT(UNCOND,0x00,d,a,0,0,b) /* d = a & b */ +#define M_ORR(d,a,b) M_DAT(UNCOND,0x0c,d,a,0,0,b) /* d = a | b */ +#define M_EOR(d,a,b) M_DAT(UNCOND,0x01,d,a,0,0,b) /* d = a ^ b */ +#define M_TST(a,b) M_DAT(UNCOND,0x08,0,a,1,0,b) /* TST a & b */ +#define M_TEQ(a,b) M_DAT(UNCOND,0x09,0,a,1,0,b) /* TST a ^ b */ +#define M_CMP(a,b) M_DAT(UNCOND,0x0a,0,a,1,0,b) /* TST a - b */ +#define M_MOV(d,b) M_DAT(UNCOND,0x0d,d,0,0,0,b) /* d = b */ +#define M_ADD_S(d,a,b) M_DAT(UNCOND,0x04,d,a,1,0,b) /* d = a + b (update Flags) */ +#define M_SUB_S(d,a,b) M_DAT(UNCOND,0x02,d,a,1,0,b) /* d = a - b (update Flags) */ +#define M_MOV_S(d,b) M_DAT(UNCOND,0x0d,d,0,1,0,b) /* d = b (update Flags) */ + +#define M_ADD_IMM(d,a,i) M_DAT(UNCOND,0x04,d,a,0,1,i) /* d = a + i */ +#define M_ADC_IMM(d,a,i) M_DAT(UNCOND,0x05,d,a,0,1,i) /* d = a + i (with Carry) */ +#define M_SUB_IMM(d,a,i) M_DAT(UNCOND,0x02,d,a,0,1,i) /* d = a - i */ +#define M_SBC_IMM(d,a,i) M_DAT(UNCOND,0x06,d,a,0,1,i) /* d = a - i (with Carry) */ +#define M_RSB_IMM(d,a,i) M_DAT(UNCOND,0x03,d,a,0,1,i) /* d = -a + i */ +#define M_RSC_IMM(d,a,i) M_DAT(UNCOND,0x07,d,a,0,1,i) /* d = -a + i (with Carry) */ +#define M_AND_IMM(d,a,i) M_DAT(UNCOND,0x00,d,a,0,1,i) /* d = a & i */ +#define M_TST_IMM(a,i) M_DAT(UNCOND,0x08,0,a,1,1,i) /* TST a & i */ +#define M_TEQ_IMM(a,i) M_DAT(UNCOND,0x09,0,a,1,1,i) /* TST a ^ i */ +#define M_CMP_IMM(a,i) M_DAT(UNCOND,0x0a,0,a,1,1,i) /* TST a - i */ +#define M_CMN_IMM(a,i) M_DAT(UNCOND,0x0b,0,a,1,1,i) /* TST a + i */ +#define M_MOV_IMM(d,i) M_DAT(UNCOND,0x0d,d,0,0,1,i) /* d = i */ +#define M_ADD_IMMS(d,a,i) M_DAT(UNCOND,0x04,d,a,1,1,i) /* d = a + i (update Flags) */ +#define M_SUB_IMMS(d,a,i) M_DAT(UNCOND,0x02,d,a,1,1,i) /* d = a - i (update Flags) */ +#define M_RSB_IMMS(d,a,i) M_DAT(UNCOND,0x03,d,a,1,1,i) /* d = -a + i (update Flags) */ + +#define M_ADDSUB_IMM(d,a,i) if((i)>=0) M_ADD_IMM(d,a,i); else M_SUB_IMM(d,a,-(i)) +#define M_MOVEQ(d,b) M_DAT(COND_EQ,0x0d,d,0,0,0,b) +#define M_MOVVS_IMM(d,i) M_DAT(COND_VS,0x0d,d,0,0,1,i) +#define M_MOVNE_IMM(d,i) M_DAT(COND_NE,0x0d,d,0,0,1,i) +#define M_MOVLS_IMM(d,i) M_DAT(COND_LS,0x0d,d,0,0,1,i) +#define M_ADDLT_IMM(d,a,i) M_DAT(COND_LT,0x04,d,a,0,1,i) +#define M_ADDGT_IMM(d,a,i) M_DAT(COND_GT,0x04,d,a,0,1,i) +#define M_SUBLT_IMM(d,a,i) M_DAT(COND_LT,0x02,d,a,0,1,i) +#define M_SUBGT_IMM(d,a,i) M_DAT(COND_GT,0x02,d,a,0,1,i) +#define M_RSBMI_IMM(d,a,i) M_DAT(COND_MI,0x03,d,a,0,1,i) +#define M_ADCMI_IMM(d,a,i) M_DAT(COND_MI,0x05,d,a,0,1,i) + +#define M_MUL(d,a,b) M_MULT(UNCOND,d,a,b,0,0,0x0) /* d = a * b */ + + +#define M_LDMFD(regs,base) M_MEM_MULTI(UNCOND,1,0,regs,base,0,1,1) +#define M_STMFD(regs,base) M_MEM_MULTI(UNCOND,0,0,regs,base,1,0,1) + +#define M_LDR_INTERN(d,base,off) \ + do { \ + CHECK_OFFSET(off, 0x0fff); \ + M_MEM(UNCOND,1,0,d,base,(((off) < 0) ? -(off) : off),0,1,(((off) < 0) ? 0 : 1),0); \ + } while (0) + +#define M_STR_INTERN(d,base,off) \ + do { \ + CHECK_OFFSET(off, 0x0fff); \ + M_MEM(UNCOND,0,0,d,base,(((off) < 0) ? -(off) : off),0,1,(((off) < 0) ? 0 : 1),0); \ + } while (0) + +#define M_LDR_UPDATE(d,base,off) \ + do { \ + CHECK_OFFSET(off, 0x0fff); \ + M_MEM(UNCOND,1,0,d,base,(((off) < 0) ? -(off) : off),0,0,(((off) < 0) ? 0 : 1),0); \ + } while (0) + +#define M_STR_UPDATE(d,base,off) \ + do { \ + CHECK_OFFSET(off,0x0fff); \ + M_MEM(UNCOND,0,0,d,base,(((off) < 0) ? -(off) : off),0,1,(((off) < 0) ? 0 : 1),1); \ + } while (0) + + +#define M_LDRH(d,base,off) \ + do { \ + CHECK_OFFSET(off, 0x00ff); \ + assert(off >= 0); \ + M_MEM2(UNCOND,1,1,0,d,base,off,1,1,1,0); \ + } while (0) + +#define M_LDRSH(d,base,off) \ + do { \ + CHECK_OFFSET(off, 0x00ff); \ + assert(off >= 0); \ + M_MEM2(UNCOND,1,1,1,d,base,off,1,1,1,0); \ + } while (0) + +#define M_LDRSB(d,base,off) \ + do { \ + CHECK_OFFSET(off, 0x00ff); \ + assert(off >= 0); \ + M_MEM2(UNCOND,1,0,1,d,base,off,1,1,1,0); \ + } while (0) + +#define M_STRH(d,base,off) \ + do { \ + CHECK_OFFSET(off, 0x00ff); \ + assert(off >= 0); \ + M_MEM2(UNCOND,0,1,0,d,base,off,1,1,1,0); \ + } while (0) + +#define M_STRB(d,base,off) \ + do { \ + CHECK_OFFSET(off, 0x0fff); \ + assert(off >= 0); \ + M_MEM(UNCOND,0,1,d,base,off,0,1,1,0); \ + } while (0) + + +#if !defined(ENABLE_SOFTFLOAT) + +#define M_LDFS_INTERN(d,base,off) \ + do { \ + CHECK_OFFSET(off, 0x03ff); \ + M_CPDT(UNCOND,1,0,0,d,base,(((off) < 0) ? -(off) >> 2 : (off) >> 2),1,(((off) < 0) ? 0 : 1),0); \ + } while (0) + +#define M_LDFD_INTERN(d,base,off) \ + do { \ + CHECK_OFFSET(off, 0x03ff); \ + M_CPDT(UNCOND,1,0,1,d,base,(((off) < 0) ? -(off) >> 2 : (off) >> 2),1,(((off) < 0) ? 0 : 1),0); \ + } while (0) + +#define M_STFS_INTERN(d,base,off) \ + do { \ + CHECK_OFFSET(off, 0x03ff); \ + M_CPDT(UNCOND,0,0,0,d,base,(((off) < 0) ? -(off) >> 2 : (off) >> 2),1,(((off) < 0) ? 0 : 1),0); \ + } while (0) + +#define M_STFD_INTERN(d,base,off) \ + do { \ + CHECK_OFFSET(off, 0x03ff); \ + M_CPDT(UNCOND,0,0,1,d,base,(((off) < 0) ? -(off) >> 2 : (off) >> 2),1,(((off) < 0) ? 0 : 1),0); \ + } while (0) + +#define M_LDFS_UPDATE(d,base,off) \ + do { \ + CHECK_OFFSET(off, 0x03ff); \ + M_CPDT(UNCOND,1,0,0,d,base,(((off) < 0) ? -(off) >> 2 : (off) >> 2),0,(((off) < 0) ? 0 : 1),1); \ + } while (0) + +#define M_LDFD_UPDATE(d,base,off) \ + do { \ + CHECK_OFFSET(off, 0x03ff); \ + M_CPDT(UNCOND,1,0,1,d,base,(((off) < 0) ? -(off) >> 2 : (off) >> 2),0,(((off) < 0) ? 0 : 1),1); \ + } while (0) + +#define M_STFS_UPDATE(d,base,off) \ + do { \ + CHECK_OFFSET(off, 0x03ff); \ + M_CPDT(UNCOND,0,0,0,d,base,(((off) < 0) ? -(off) >> 2 : (off) >> 2),1,(((off) < 0) ? 0 : 1),1); \ + } while (0) + +#define M_STFD_UPDATE(d,base,off) \ + do { \ + CHECK_OFFSET(off, 0x03ff); \ + M_CPDT(UNCOND,0,0,1,d,base,(((off) < 0) ? -(off) >> 2 : (off) >> 2),1,(((off) < 0) ? 0 : 1),1); \ + } while (0) + +#define M_ADFS(d,a,b) M_CPDOS(UNCOND,0x00,0,d,a,b) /* d = a + b */ +#define M_SUFS(d,a,b) M_CPDOS(UNCOND,0x02,0,d,a,b) /* d = a - b */ +#define M_RSFS(d,a,b) M_CPDOS(UNCOND,0x03,0,d,a,b) /* d = b - a */ +#define M_MUFS(d,a,b) M_CPDOS(UNCOND,0x01,0,d,a,b) /* d = a * b */ +#define M_DVFS(d,a,b) M_CPDOS(UNCOND,0x04,0,d,a,b) /* d = a / b */ +#define M_RMFS(d,a,b) M_CPDOS(UNCOND,0x08,0,d,a,b) /* d = a % b */ +#define M_ADFD(d,a,b) M_CPDOD(UNCOND,0x00,0,d,a,b) /* d = a + b */ +#define M_SUFD(d,a,b) M_CPDOD(UNCOND,0x02,0,d,a,b) /* d = a - b */ +#define M_RSFD(d,a,b) M_CPDOD(UNCOND,0x03,0,d,a,b) /* d = b - a */ +#define M_MUFD(d,a,b) M_CPDOD(UNCOND,0x01,0,d,a,b) /* d = a * b */ +#define M_DVFD(d,a,b) M_CPDOD(UNCOND,0x04,0,d,a,b) /* d = a / b */ +#define M_RMFD(d,a,b) M_CPDOD(UNCOND,0x08,0,d,a,b) /* d = a % b */ +#define M_MVFS(d,a) M_CPDOS(UNCOND,0x00,1,d,0,a) /* d = a */ +#define M_MVFD(d,a) M_CPDOD(UNCOND,0x00,1,d,0,a) /* d = a */ +#define M_MNFS(d,a) M_CPDOS(UNCOND,0x01,1,d,0,a) /* d = - a */ +#define M_MNFD(d,a) M_CPDOD(UNCOND,0x01,1,d,0,a) /* d = - a */ +#define M_CMF(a,b) M_CPRTX(UNCOND,1,0x0f,a,b) /* COMPARE a; b */ +#define M_FLTS(d,a) M_CPRTS(UNCOND,0,a,d,0) /* d = (float) a */ +#define M_FLTD(d,a) M_CPRTD(UNCOND,0,a,d,0) /* d = (float) a */ +#define M_FIX(d,a) M_CPRTI(UNCOND,1,d,0,a) /* d = (int) a */ + +#endif /* !defined(ENABLE_SOFTFLOAT) */ + + +#define M_B(off) M_BRA(UNCOND,0,off) /* unconditional branch */ +#define M_BL(off) M_BRA(UNCOND,1,off) /* branch and link */ +#define M_BEQ(off) M_BRA(COND_EQ,0,off) /* conditional branches */ +#define M_BNE(off) M_BRA(COND_NE,0,off) +#define M_BGE(off) M_BRA(COND_GE,0,off) +#define M_BGT(off) M_BRA(COND_GT,0,off) +#define M_BLT(off) M_BRA(COND_LT,0,off) +#define M_BLE(off) M_BRA(COND_LE,0,off) +#define M_BHI(off) M_BRA(COND_HI,0,off) /* unsigned conditional */ +#define M_BHS(off) M_BRA(COND_CS,0,off) +#define M_BLO(off) M_BRA(COND_CC,0,off) +#define M_BLS(off) M_BRA(COND_LS,0,off) + + +#define M_FMOV(a,b) M_MVFS(b,a) +#define M_DMOV(a,b) M_MVFD(b,a) + + +/* if we do not have double-word load/store command, we can fake them */ +/* ATTENTION: the original LDRD/STRD of ARMv5e would always use (Rd/Rd+1), + so these faked versions are more "powerful" */ + +#if defined(__ARMEL__) + +#define M_LDRD_INTERN(d,base,off) \ + do { \ + M_LDR_INTERN(GET_LOW_REG(d), base, off); \ + M_LDR_INTERN(GET_HIGH_REG(d), base, (off) + 4); \ + } while (0) + +#define M_STRD_INTERN(d,base,off) \ + do { \ + M_STR_INTERN(GET_LOW_REG(d), base, off); \ + M_STR_INTERN(GET_HIGH_REG(d), base, (off) + 4); \ + } while (0) + +#define M_LDRD_ALTERN(d,base,off) \ + do { \ + M_LDR_INTERN(GET_HIGH_REG(d), base, (off) + 4); \ + M_LDR_INTERN(GET_LOW_REG(d), base, off); \ + } while (0) + +#define M_LDRD_UPDATE(d,base,off) \ + do { \ + assert((off) == +8); \ + M_LDR_UPDATE(GET_LOW_REG(d), base, 4); \ + M_LDR_UPDATE(GET_HIGH_REG(d), base, 4); \ + } while (0) + +#define M_STRD_UPDATE(d,base,off) \ + do { \ + assert((off) == -8); \ + M_STR_UPDATE(GET_HIGH_REG(d), base, -4); \ + M_STR_UPDATE(GET_LOW_REG(d), base, -4); \ + } while (0) + +#define GET_FIRST_REG(d) GET_LOW_REG(d) +#define GET_SECOND_REG(d) GET_HIGH_REG(d) + +#else /* defined(__ARMEB__) */ + +#define M_LDRD_INTERN(d,base,off) \ + do { \ + M_LDR_INTERN(GET_HIGH_REG(d), base, off); \ + M_LDR_INTERN(GET_LOW_REG(d), base, (off) + 4); \ + } while (0) + +#define M_STRD_INTERN(d,base,off) \ + do { \ + M_STR_INTERN(GET_HIGH_REG(d), base, off); \ + M_STR_INTERN(GET_LOW_REG(d), base, (off) + 4); \ + } while (0) + +#define M_LDRD_ALTERN(d,base,off) \ + do { \ + M_LDR_INTERN(GET_LOW_REG(d), base, (off) + 4); \ + M_LDR_INTERN(GET_HIGH_REG(d), base, off); \ + } while (0) + +#define M_LDRD_UPDATE(d,base,off) \ + do { \ + assert((off) == +8); \ + M_LDR_UPDATE(GET_HIGH_REG(d), base, 4); \ + M_LDR_UPDATE(GET_LOW_REG(d), base, 4); \ + } while (0) + +#define M_STRD_UPDATE(d,base,off) \ + do { \ + assert((off) == -8); \ + M_STR_UPDATE(GET_LOW_REG(d), base, -4); \ + M_STR_UPDATE(GET_HIGH_REG(d) ,base, -4); \ + } while (0) + +#define GET_FIRST_REG(d) GET_HIGH_REG(d) +#define GET_SECOND_REG(d) GET_LOW_REG(d) + +#endif /* defined(__ARMEB__) */ + + +/* M_LDR/M_STR: + these are replacements for the original LDR/STR instructions, which can + handle longer offsets (up to 20bits). the original functions are now + called M_xxx_INTERN. +*/ +/* ATTENTION: We use ITMP3 here, take into account that it gets destroyed. + This means that only ITMP1 and ITMP2 can be used in reg_of_var()!!! +*/ +/* ATTENTION2: It is possible to use ITMP3 as base reg. Remember that when + changing these macros!!! +*/ + +#define M_LDR(d, base, offset) \ +do { \ + CHECK_OFFSET(offset, 0x0fffff); \ + if (IS_OFFSET(offset, 0x000fff)) { \ + M_LDR_INTERN(d, base, offset); \ + } else { \ + /* we cannot handle REG_PC here */ \ + assert((d) != REG_PC); \ + if ((offset) > 0) { \ + M_ADD_IMM(d, base, IMM_ROTL((offset) >> 12, 6)); \ + M_LDR_INTERN(d, d, (offset) & 0x000fff); \ + } else { \ + M_SUB_IMM(d, base, IMM_ROTL((-(offset)) >> 12, 6)); \ + M_LDR_INTERN(d, d, -(-(offset) & 0x000fff)); \ + } \ + } \ +} while (0) + +#define M_LDR_NEGATIVE(d, base, offset) { \ + /*assert((offset) <= 0);*/ \ + if (IS_OFFSET(offset, 0x000fff)) { \ + M_LDR_INTERN(d, base, offset); \ + } else { \ + /* we cannot handle REG_PC here */ \ + assert((d) != REG_PC); \ + M_SUB_IMM(d, base, IMM_ROTL((-(offset)) >> 12, 6)); \ + M_LDR_INTERN(d, d, -(-(offset) & 0x000fff)); \ + } \ +} + +#define M_LDRD(d, base, offset) \ +do { \ + CHECK_OFFSET(offset, 0x0fffff - 4); \ + if (IS_OFFSET(offset, 0x000fff - 4)) { \ + if (GET_FIRST_REG(d) != (base)) { \ + M_LDRD_INTERN(d, base, offset); \ + } else { \ + M_LDRD_ALTERN(d, base, offset); \ + } \ + } else if (IS_OFFSET(offset, 0x000fff)) { \ + dolog("M_LDRD: this offset seems to be complicated (%d)", offset); \ + assert(0); \ + } else { \ + if ((offset) > 0) { \ + M_ADD_IMM(GET_SECOND_REG(d), base, IMM_ROTL((offset) >> 12, 6)); \ + M_LDRD_INTERN(d, GET_SECOND_REG(d), (offset) & 0x000fff); \ + } else { \ + M_SUB_IMM(GET_SECOND_REG(d), base, IMM_ROTL((-(offset)) >> 12, 6)); \ + M_LDRD_INTERN(d, GET_SECOND_REG(d), -(-(offset) & 0x000fff)); \ + } \ + } \ +} while (0) + +#if !defined(ENABLE_SOFTFLOAT) +#define M_LDFS(d, base, offset) \ +do { \ + CHECK_OFFSET(offset, 0x03ffff); \ + if (IS_OFFSET(offset, 0x03ff)) { \ + M_LDFS_INTERN(d, base, offset); \ + } else { \ + if ((offset) > 0) { \ + M_ADD_IMM(REG_ITMP3, base, IMM_ROTL((offset) >> 10, 5)); \ + M_LDFS_INTERN(d, REG_ITMP3, (offset) & 0x03ff); \ + } else { \ + M_SUB_IMM(REG_ITMP3, base, IMM_ROTL((-(offset)) >> 10, 5)); \ + M_LDFS_INTERN(d, REG_ITMP3, -(-(offset) & 0x03ff)); \ + } \ + } \ +} while (0) + +#define M_LDFD(d, base, offset) \ +do { \ + CHECK_OFFSET(offset, 0x03ffff); \ + if (IS_OFFSET(offset, 0x03ff)) { \ + M_LDFD_INTERN(d, base, offset); \ + } else { \ + if ((offset) > 0) { \ + M_ADD_IMM(REG_ITMP3, base, IMM_ROTL((offset) >> 10, 5)); \ + M_LDFD_INTERN(d, REG_ITMP3, (offset) & 0x03ff); \ + } else { \ + M_SUB_IMM(REG_ITMP3, base, IMM_ROTL((-(offset)) >> 10, 5)); \ + M_LDFD_INTERN(d, REG_ITMP3, -(-(offset) & 0x03ff)); \ + } \ + } \ +} while (0) + +#endif /* !defined(ENABLE_SOFTFLOAT) */ + +#define M_STR(d, base, offset) \ +do { \ + assert((d) != REG_ITMP3); \ + CHECK_OFFSET(offset, 0x0fffff); \ + if (IS_OFFSET(offset, 0x000fff)) { \ + M_STR_INTERN(d, base, offset); \ + } else { \ + if ((offset) > 0) { \ + M_ADD_IMM(REG_ITMP3, base, IMM_ROTL((offset) >> 12, 6)); \ + M_STR_INTERN(d, REG_ITMP3, (offset) & 0x000fff); \ + } else { \ + M_SUB_IMM(REG_ITMP3, base, IMM_ROTL((-(offset)) >> 12, 6)); \ + M_STR_INTERN(d, REG_ITMP3, -(-(offset) & 0x000fff)); \ + } \ + } \ +} while (0) + +#define M_STRD(d, base, offset) \ +do { \ + assert(GET_LOW_REG(d) != REG_ITMP3); \ + assert(GET_HIGH_REG(d) != REG_ITMP3); \ + CHECK_OFFSET(offset, 0x0fffff - 4); \ + if (IS_OFFSET(offset, 0x000fff - 4)) { \ + M_STRD_INTERN(d,base,offset); \ + } else if (IS_OFFSET(offset, 0x000fff)) { \ + dolog("M_STRD: this offset seems to be complicated (%d)", offset); \ + assert(0); \ + } else { \ + if ((offset) > 0) { \ + M_ADD_IMM(REG_ITMP3, base, IMM_ROTL((offset) >> 12, 6)); \ + M_STRD_INTERN(d, REG_ITMP3, (offset) & 0x000fff); \ + } else { \ + M_SUB_IMM(REG_ITMP3, base, IMM_ROTL((-(offset)) >> 12, 6)); \ + M_STRD_INTERN(d, REG_ITMP3, -(-(offset) & 0x000fff)); \ + } \ + } \ +} while (0) + +#if !defined(ENABLE_SOFTFLOAT) + +#define M_STFS(d, base, offset) \ +do { \ + CHECK_OFFSET(offset, 0x03ffff); \ + if (IS_OFFSET(offset, 0x03ff)) { \ + M_STFS_INTERN(d, base, offset); \ + } else { \ + if ((offset) > 0) { \ + M_ADD_IMM(REG_ITMP3, base, IMM_ROTL((offset) >> 10, 5)); \ + M_STFS_INTERN(d, REG_ITMP3, (offset) & 0x03ff); \ + } else { \ + M_SUB_IMM(REG_ITMP3, base, IMM_ROTL((-(offset)) >> 10, 5)); \ + M_STFS_INTERN(d, REG_ITMP3, -(-(offset) & 0x03ff)); \ + } \ + } \ +} while (0) + +#define M_STFD(d, base, offset) \ +do { \ + CHECK_OFFSET(offset, 0x03ffff); \ + if (IS_OFFSET(offset, 0x03ff)) { \ + M_STFD_INTERN(d, base, offset); \ + } else { \ + if ((offset) > 0) { \ + M_ADD_IMM(REG_ITMP3, base, IMM_ROTL((offset) >> 10, 5)); \ + M_STFD_INTERN(d, REG_ITMP3, (offset) & 0x03ff); \ + } else { \ + M_SUB_IMM(REG_ITMP3, base, IMM_ROTL((-(offset)) >> 10, 5)); \ + M_STFD_INTERN(d, REG_ITMP3, -(-(offset) & 0x03ff)); \ + } \ + } \ +} while (0) + +#endif /* !defined(ENABLE_SOFTFLOAT) */ + +/* M_???_IMM_EXT_MUL4: + extended immediate operations, to handle immediates lager than 8bit. + ATTENTION: the immediate is rotatet left by 2 (multiplied by 4)!!! +*/ +#define M_ADD_IMM_EXT_MUL4(d,n,imm) \ + assert(d!=REG_PC); \ + assert((imm) >= 0 && (imm) <= 0x00ffffff); \ + M_ADD_IMM(d, n, IMM_ROTL(imm, 1)); \ + if ((imm) > 0x000000ff) M_ADD_IMM(d, d, IMM_ROTL((imm) >> 8, 5)); \ + if ((imm) > 0x0000ffff) M_ADD_IMM(d, d, IMM_ROTL((imm) >> 16, 9)); +#define M_SUB_IMM_EXT_MUL4(d,n,imm) \ + assert(d!=REG_PC); \ + assert((imm) >= 0 && (imm) <= 0x00ffffff); \ + M_SUB_IMM(d, n, IMM_ROTL(imm, 1)); \ + if ((imm) > 0x000000ff) M_SUB_IMM(d, d, IMM_ROTL((imm) >> 8, 5)); \ + if ((imm) > 0x0000ffff) M_SUB_IMM(d, d, IMM_ROTL((imm) >> 16, 9)); + + +/* ICONST/LCONST: + loads the integer/long value const into register d. +*/ + +#define ICONST(d,c) emit_iconst(cd, (d), (c)) + +#define ICONST_CONDITIONAL(cond,d,const) \ + if (IS_IMM(const)) { \ + /* M_MOV_IMM */ M_DAT(cond,0x0d,d,0,0,1,const); \ + } else { \ + disp = dseg_adds4(cd, const); \ + /* TODO: implement this using M_DSEG_LOAD!!! */ \ + /* M_LDR_INTERN */ CHECK_OFFSET(disp,0x0fff); M_MEM(cond,1,0,d,REG_IP,(disp<0)?-disp:disp,0,1,(disp<0)?0:1,0); \ + } + +#define LCONST(d,c) \ + if (IS_IMM((c) >> 32)) { \ + M_MOV_IMM(GET_HIGH_REG(d), (s4) ((s8) (c) >> 32)); \ + ICONST(GET_LOW_REG(d), (s4) ((s8) (c) & 0xffffffff)); \ + } else if (IS_IMM((c) & 0xffffffff)) { \ + M_MOV_IMM(GET_LOW_REG(d), (s4) ((s8) (c) & 0xffffffff)); \ + ICONST(GET_HIGH_REG(d), (s4) ((s8) (c) >> 32)); \ + } else { \ + disp = dseg_add_s8(cd, (c)); \ + M_LDRD(d, REG_IP, disp); \ + } + + +#if !defined(ENABLE_SOFTFLOAT) + +#define FCONST(d,c) \ + do { \ + disp = dseg_add_float(cd, (c)); \ + M_LDFS(d, REG_IP, disp); \ + } while (0) + +#define DCONST(d,c) \ + do { \ + disp = dseg_add_double(cd, (c)); \ + M_LDFD(d, REG_IP, disp); \ + } while (0) + +#endif /* !defined(ENABLE_SOFTFLOAT) */ + + +/* M_RECOMPUTE_IP: + used to recompute our IP (something like PV) out of the current PC + ATTENTION: if you change this, you have to look at other functions as well! + Following things depend on it: asm_call_jit_compiler(); codegen_findmethod(); +*/ +#define M_RECOMPUTE_IP(disp) \ + disp += 8; /* we use PC relative addr. */ \ + assert((disp & 0x03) == 0); \ + assert(disp >= 0 && disp <= 0x03ffffff); \ + M_SUB_IMM(REG_IP, REG_PC, IMM_ROTL(disp >> 2, 1)); \ + if (disp > 0x000003ff) M_SUB_IMM(REG_IP, REG_IP, IMM_ROTL(disp >> 10, 5)); \ + if (disp > 0x0003ffff) M_SUB_IMM(REG_IP, REG_IP, IMM_ROTL(disp >> 18, 9)); \ + +/* M_INTMOVE: + generates an integer-move from register a to b. + if a and b are the same int-register, no code will be generated. +*/ + +#define M_INTMOVE(a,b) \ + do { \ + if ((a) != (b)) \ + M_MOV(b, a); \ + } while (0) + +#define M_LNGMOVE(a,b) \ + do { \ + if (GET_HIGH_REG(a) == GET_LOW_REG(b)) { \ + assert((GET_LOW_REG(a) != GET_HIGH_REG(b))); \ + M_INTMOVE(GET_HIGH_REG(a), GET_HIGH_REG(b)); \ + M_INTMOVE(GET_LOW_REG(a), GET_LOW_REG(b)); \ + } else { \ + M_INTMOVE(GET_LOW_REG(a), GET_LOW_REG(b)); \ + M_INTMOVE(GET_HIGH_REG(a), GET_HIGH_REG(b)); \ + } \ + } while (0) + + +#if !defined(ENABLE_SOFTFLOAT) + +/* M_FLTMOVE: + generates a floating-point-move from register a to b. + if a and b are the same float-register, no code will be generated. +*/ + +#define M_FLTMOVE(a,b) \ + do { \ + if ((a) != (b)) \ + M_FMOV(a, b); \ + } while (0) + +#define M_DBLMOVE(a,b) \ + do { \ + if ((a) != (b)) \ + M_DMOV(a, b); \ + } while (0) + +#endif + +#if !defined(ENABLE_SOFTFLOAT) +/* M_CAST_INT_TO_FLT_TYPED: + loads the value of the integer-register a (argument or result) into + float-register Fb. (and vice versa) +*/ +#define M_CAST_INT_TO_FLT_TYPED(t,a,Fb) \ + CHECK_FLT_REG(Fb); \ + if ((t) == TYPE_FLT) { \ + CHECK_INT_REG(a); \ + M_STR_UPDATE(a, REG_SP, -4); \ + M_LDFS_UPDATE(Fb, REG_SP, 4); \ + } else { \ + CHECK_INT_REG(GET_LOW_REG(a)); \ + CHECK_INT_REG(GET_HIGH_REG(a)); \ + M_STRD_UPDATE(a, REG_SP, -8); \ + M_LDFD_UPDATE(Fb, REG_SP, 8); \ + } +#define M_CAST_FLT_TO_INT_TYPED(t,Fa,b) \ + CHECK_FLT_REG(Fa); \ + if ((t) == TYPE_FLT) { \ + CHECK_INT_REG(b); \ + M_STFS_UPDATE(Fa, REG_SP, -4); \ + M_LDR_UPDATE(b, REG_SP, 4); \ + } else { \ + CHECK_INT_REG(GET_LOW_REG(b)); \ + CHECK_INT_REG(GET_HIGH_REG(b)); \ + M_STFD_UPDATE(Fa, REG_SP, -8); \ + M_LDRD_UPDATE(b, REG_SP, 8); \ + } +#endif /* !defined(ENABLE_SOFTFLOAT) */ + + +/* M_COMPARE: + generates the compare part of an if-sequece + uses M_CMP or M_CMP_IMM to do the compare + ATTENTION: uses REG_ITMP3 as intermediate register +*/ +/* TODO: improve this and add some comments! */ +#define M_COMPARE(reg, val, cond, overjump) \ + if (IS_IMM(val)) { \ + if (overjump) M_BNE(1); \ + /* M_CMP_IMM */ M_DAT(cond,0x0a,0,(reg),1,1,(val)); \ + } else if(IS_IMM(-(val))) { \ + if (overjump) M_BNE(1); \ + /* M_CMN_IMM */ M_DAT(cond,0x0b,0,(reg),1,1,-(val)); \ + } else { \ + ICONST(REG_ITMP3, (val)); \ + if (overjump) M_BNE(1); \ + /* M_CMP */ M_DAT(cond,0x0a,0,(reg),1,0,REG_ITMP3); \ + } + +/* M_LONGBRANCH: + performs a long branch to an absolute address with return address in LR + takes up 3 bytes of code space; address is hard-coded into code +*/ +#define M_LONGBRANCH(adr) \ + M_ADD_IMM(REG_LR, REG_PC, 4); \ + M_LDR_INTERN(REG_PC, REG_PC, -4); \ + DCD((s4) adr); + +/* M_DSEG_LOAD/BRANCH: + TODO: document me + ATTENTION: if you change this, you have to look at the asm_call_jit_compiler! + ATTENTION: we use M_LDR, so the same restrictions apply to us! +*/ +#define M_DSEG_LOAD(reg, offset) \ + M_LDR_NEGATIVE(reg, REG_IP, offset) + +#define M_DSEG_BRANCH(offset) \ + if (IS_OFFSET(offset, 0x0fff)) { \ + M_MOV(REG_LR, REG_PC); \ + M_LDR_INTERN(REG_PC, REG_IP, offset); \ + } else { \ + /*assert((offset) <= 0);*/ \ + CHECK_OFFSET(offset,0x0fffff); \ + M_SUB_IMM(REG_ITMP3, REG_IP, ((-(offset) >> 12) & 0xff) | (((10) & 0x0f) << 8)); /*TODO: more to go*/ \ + M_MOV(REG_LR, REG_PC); \ + M_LDR_INTERN(REG_PC, REG_ITMP3, -(-(offset) & 0x0fff)); /*TODO: this looks ugly*/ \ + } + +/* M_STACK_LOAD/STORE: + loads or stores integer values from register to stack or from stack to register + ATTENTION: we use M_LDR and M_STR, so the same restrictions apply to us! +*/ + +#define M_STACK_LOAD_LNG(reg, offset) { \ + CHECK_INT_REG(GET_LOW_REG(reg)); \ + CHECK_INT_REG(GET_HIGH_REG(reg)); \ + M_LDRD(reg, REG_SP, (offset) * 4); \ +} + + +#define M_ILD(a,b,c) M_LDR(a,b,c) +#define M_LLD(a,b,c) M_LDRD(a,b,c) + +#define M_ILD_INTERN(a,b,c) M_LDR_INTERN(a,b,c) +#define M_LLD_INTERN(a,b,c) M_LDRD_INTERN(a,b,c) + +#define M_ALD(a,b,c) M_ILD(a,b,c) +#define M_ALD_INTERN(a,b,c) M_ILD_INTERN(a,b,c) + + +#define M_IST(a,b,c) M_STR(a,b,c) +#define M_LST(a,b,c) M_STRD(a,b,c) + +#define M_IST_INTERN(a,b,c) M_STR_INTERN(a,b,c) +#define M_LST_INTERN(a,b,c) M_STRD_INTERN(a,b,c) + +#define M_AST(a,b,c) M_IST(a,b,c) +#define M_AST_INTERN(a,b,c) M_IST_INTERN(a,b,c) + + +#if !defined(ENABLE_SOFTFLOAT) + +#define M_STACK_LOAD_FLT_TYPED(t, reg, offset) { \ + CHECK_FLT_REG(reg); \ + if (IS_2_WORD_TYPE(t)) { \ + M_LDFD(reg, REG_SP, (offset) * 4); \ + } else { \ + M_LDFS(reg, REG_SP, (offset) * 4); \ + } \ +} + +#define M_FLD(a,b,c) M_LDFS(a,b,c) +#define M_DLD(a,b,c) M_LDFD(a,b,c) + +#define M_FLD_INTERN(a,b,c) M_LDFS_INTERN(a,b,c) +#define M_DLD_INTERN(a,b,c) M_LDFD_INTERN(a,b,c) + + +#define M_STACK_STORE_FLT_TYPED(t, reg, offset) { \ + CHECK_FLT_REG(reg); \ + if (IS_2_WORD_TYPE(t)) { \ + M_STFD(reg, REG_SP, (offset) * 4); \ + } else { \ + M_STFS(reg, REG_SP, (offset) * 4); \ + } \ +} + +#define M_FST(a,b,c) M_STFS(a,b,c) +#define M_DST(a,b,c) M_STFD(a,b,c) + +#define M_FST_INTERN(a,b,c) M_STFS_INTERN(a,b,c) +#define M_DST_INTERN(a,b,c) M_STFD_INTERN(a,b,c) + +#endif /* !defined(ENABLE_SOFTFLOAT) */ + + +/* function gen_resolvebranch ************************************************** + ip ... pointer to instruction after branch (void*) + so ... offset of instruction after branch (s4) + to ... offset of branch target (s4) */ + +#define gen_resolvebranch(ip,so,to) \ + assert((((s4*) (ip))[-1] & 0x0e000000) == 0x0a000000); \ + ((s4*) (ip))[-1] |= ((s4) (to) - (so) - 1) >> 2 & 0x0ffffff + + +/* function gen_nullptr_check *************************************************/ + +#define gen_nullptr_check_intern(objreg) { \ + M_TST((objreg), (objreg)); \ + M_BEQ(0); \ + codegen_add_nullpointerexception_ref(cd); \ +} + +#define gen_nullptr_check(objreg) \ + if (checknull) \ + gen_nullptr_check_intern(objreg) + + +/* function gen_div_check *****************************************************/ + +#define gen_div_check(type,reg) \ + do { \ + if (IS_2_WORD_TYPE(type)) { \ + M_TEQ_IMM(GET_LOW_REG(reg), 0); \ + /* M_TEQ_EQ_IMM(GET_HIGH_REG(reg), 0) */ M_DAT(COND_EQ,0x09,0,GET_HIGH_REG(reg),1,1,0); \ + M_BEQ(0); \ + } else { \ + M_TEQ_IMM((reg), 0); \ + M_BEQ(0); \ + } \ + codegen_add_arithmeticexception_ref(cd); \ + } while (0) + +/* function gen_bound_check *************************************************** + ATTENTION: uses REG_ITMP3 as intermediate register */ +/* TODO: maybe use another reg instead of REG_ITMP3! */ + +#define gen_bound_check(arrayref,index) \ + if (checkbounds) { \ + M_LDR_INTERN(REG_ITMP3, (arrayref), OFFSET(java_arrayheader, size)); \ + M_CMP((index), REG_ITMP3); \ + M_BHS(0); \ + codegen_add_arrayindexoutofboundsexception_ref(cd, index); \ + } + + +#endif /* _CODEGEN_H */ + + +/* + * These are local overrides for various environment variables in Emacs. + * Please do not remove this and leave it at the end of the file, where + * Emacs will automagically detect them. + * --------------------------------------------------------------------- + * Local variables: + * mode: c + * indent-tabs-mode: t + * c-basic-offset: 4 + * tab-width: 4 + * End: + * vim:noexpandtab:sw=4:ts=4: + */ diff --git a/src/vm/jit/arm/disass.c b/src/vm/jit/arm/disass.c new file mode 100644 index 000000000..d0f11db93 --- /dev/null +++ b/src/vm/jit/arm/disass.c @@ -0,0 +1,107 @@ +/* src/vm/jit/arm/disass.c - wrapper functions for GNU binutils disassembler + + Copyright (C) 1996-2005, 2006 R. Grafl, A. Krall, C. Kruegel, + C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring, + E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich, + J. Wenninger, Institut f. Computersprachen - TU Wien + + This file is part of CACAO. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + + Contact: cacao@cacaojvm.org + + Authors: Michael Starzinger + + $Id: disass.c 6454 2006-01-23 12:19:37Z michi $ + +*/ + + +#include "config.h" + +#include + +#include "vm/types.h" +#include "vm/global.h" +#include "vm/jit/disass.h" + + +char *regs[] = { + "a1", + "a2", + "a3", + "a4", + "v1", + "v2", + "v3", + "v4", + "v5", + "t3", + "t1", + "t2", + "IP", + "SP", + "LR", + "PC", + "***" +}; + + +/* disassinstr ***************************************************************** + + Outputs a disassembler listing of one machine code instruction on + 'stdout'. + + code: pointer to instructions machine code + +*******************************************************************************/ + +u1 *disassinstr(u1 *code) +{ + if (!disass_initialized) { + INIT_DISASSEMBLE_INFO(info, stdout, disass_printf); + info.read_memory_func = &disass_buffer_read_memory; + disass_initialized = true; + } + + printf("0x%08x: %08x ", (u4) code, *((s4 *) code)); + +#if defined(__ARMEL__) + print_insn_little_arm((bfd_vma) code, &info); +#else + print_insn_big_arm((bfd_vma) code, &info); +#endif + + printf("\n"); + + /* 1 instruction is 4-bytes long */ + return code + 4; +} + + +/* + * These are local overrides for various environment variables in Emacs. + * Please do not remove this and leave it at the end of the file, where + * Emacs will automagically detect them. + * --------------------------------------------------------------------- + * Local variables: + * mode: c + * indent-tabs-mode: t + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff --git a/src/vm/jit/arm/emit.c b/src/vm/jit/arm/emit.c new file mode 100644 index 000000000..ea2a185f9 --- /dev/null +++ b/src/vm/jit/arm/emit.c @@ -0,0 +1,814 @@ +/* src/vm/jit/arm/emit.c - Arm code emitter functions + + Copyright (C) 1996-2005, 2006, 2007 R. Grafl, A. Krall, C. Kruegel, + C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring, + E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich, + J. Wenninger, Institut f. Computersprachen - TU Wien + + This file is part of CACAO. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + + Contact: cacao@cacaojvm.org + + Authors: Christian Thalinger + + $Id: emit.c 4398 2006-01-31 23:43:08Z twisti $ + +*/ + + +#include "config.h" + +#include + +#include "vm/types.h" + +#include "md-abi.h" + +#include "vm/jit/arm/codegen.h" + +#if defined(ENABLE_THREADS) +# include "threads/native/lock.h" +#endif + +#include "vm/builtin.h" +#include "vm/jit/asmpart.h" +#include "vm/jit/emit-common.h" +#include "vm/jit/jit.h" +#include "vm/jit/replace.h" + +#include "toolbox/logging.h" /* XXX for debugging only */ + + +/* emit_load ******************************************************************* + + Emits a possible load of an operand. + +*******************************************************************************/ + +s4 emit_load(jitdata *jd, instruction *iptr, varinfo *src, s4 tempreg) +{ + codegendata *cd; + s4 disp; + s4 reg; + + /* get required compiler data */ + + cd = jd->cd; + + if (src->flags & INMEMORY) { + COUNT_SPILLS; + + disp = src->vv.regoff * 4; + + if (IS_FLT_DBL_TYPE(src->type)) { +#if !defined(ENABLE_SOFTFLOAT) + if (IS_2_WORD_TYPE(src->type)) + M_DLD(tempreg, REG_SP, disp); + else + M_FLD(tempreg, REG_SP, disp); +#else + assert(0); +#endif + } + else { + if (IS_2_WORD_TYPE(src->type)) + M_LLD(tempreg, REG_SP, disp); + else + M_ILD(tempreg, REG_SP, disp); + } + + reg = tempreg; + } + else + reg = src->vv.regoff; + + return reg; +} + + +/* emit_load_low *************************************************************** + + Emits a possible load of the low 32-bits of a long source operand. + +*******************************************************************************/ + +s4 emit_load_low(jitdata *jd, instruction *iptr, varinfo *src, s4 tempreg) +{ + codegendata *cd; + s4 disp; + s4 reg; + + assert(src->type == TYPE_LNG); + + /* get required compiler data */ + + cd = jd->cd; + + if (src->flags & INMEMORY) { + COUNT_SPILLS; + + disp = src->vv.regoff * 4; + +#if defined(__ARMEL__) + M_ILD(tempreg, REG_SP, disp); +#else + M_ILD(tempreg, REG_SP, disp + 4); +#endif + + reg = tempreg; + } + else + reg = GET_LOW_REG(src->vv.regoff); + + return reg; +} + + +/* emit_load_high ************************************************************** + + Emits a possible load of the high 32-bits of a long source operand. + +*******************************************************************************/ + +s4 emit_load_high(jitdata *jd, instruction *iptr, varinfo *src, s4 tempreg) +{ + codegendata *cd; + s4 disp; + s4 reg; + + assert(src->type == TYPE_LNG); + + /* get required compiler data */ + + cd = jd->cd; + + if (src->flags & INMEMORY) { + COUNT_SPILLS; + + disp = src->vv.regoff * 4; + +#if defined(__ARMEL__) + M_ILD(tempreg, REG_SP, disp + 4); +#else + M_ILD(tempreg, REG_SP, disp); +#endif + + reg = tempreg; + } + else + reg = GET_HIGH_REG(src->vv.regoff); + + return reg; +} + + +/* emit_store ****************************************************************** + + Emits a possible store to a variable. + +*******************************************************************************/ + +void emit_store(jitdata *jd, instruction *iptr, varinfo *dst, s4 d) +{ + codegendata *cd; + s4 disp; + + /* get required compiler data */ + + cd = jd->cd; + + if (dst->flags & INMEMORY) { + COUNT_SPILLS; + + disp = dst->vv.regoff * 4; + +#if !defined(ENABLE_SOFTFLOAT) + if (IS_FLT_DBL_TYPE(dst->type)) { + if (IS_2_WORD_TYPE(dst->type)) + M_DST(d, REG_SP, disp); + else + M_FST(d, REG_SP, disp); + } + else { + if (IS_2_WORD_TYPE(dst->type)) + M_LST(d, REG_SP, disp); + else + M_IST(d, REG_SP, disp); + } +#else + if (IS_2_WORD_TYPE(dst->type)) + M_LST(d, REG_SP, disp); + else + M_IST(d, REG_SP, disp); +#endif + } + else if (IS_LNG_TYPE(dst->type)) { +#if defined(__ARMEL__) + if (GET_HIGH_REG(dst->vv.regoff) == REG_SPLIT) + M_IST_INTERN(GET_HIGH_REG(d), REG_SP, 0 * 4); +#else + if (GET_LOW_REG(dst->vv.regoff) == REG_SPLIT) + M_IST_INTERN(GET_LOW_REG(d), REG_SP, 0 * 4); +#endif + } +} + + +/* emit_copy ******************************************************************* + + XXX + +*******************************************************************************/ + +void emit_copy(jitdata *jd, instruction *iptr, varinfo *src, varinfo *dst) +{ + codegendata *cd; + registerdata *rd; + s4 s1, d; + + /* get required compiler data */ + + cd = jd->cd; + rd = jd->rd; + + /* XXX dummy call, removed me!!! */ + d = codegen_reg_of_var(iptr->opc, dst, REG_ITMP1); + + if ((src->vv.regoff != dst->vv.regoff) || + ((src->flags ^ dst->flags) & INMEMORY)) { + + /* If one of the variables resides in memory, we can eliminate + the register move from/to the temporary register with the + order of getting the destination register and the load. */ + + if (IS_INMEMORY(src->flags)) { +#if !defined(ENABLE_SOFTFLOAT) + if (IS_FLT_DBL_TYPE(src->type)) + d = codegen_reg_of_var(iptr->opc, dst, REG_FTMP1); + else +#endif + { + if (IS_2_WORD_TYPE(src->type)) + d = codegen_reg_of_var(iptr->opc, dst, REG_ITMP12_PACKED); + else + d = codegen_reg_of_var(iptr->opc, dst, REG_ITMP1); + } + + s1 = emit_load(jd, iptr, src, d); + } + else { +#if !defined(ENABLE_SOFTFLOAT) + if (IS_FLT_DBL_TYPE(src->type)) + s1 = emit_load(jd, iptr, src, REG_FTMP1); + else +#endif + { + if (IS_2_WORD_TYPE(src->type)) + s1 = emit_load(jd, iptr, src, REG_ITMP12_PACKED); + else + s1 = emit_load(jd, iptr, src, REG_ITMP1); + } + + d = codegen_reg_of_var(iptr->opc, dst, s1); + } + + if (s1 != d) { +#if !defined(ENABLE_SOFTFLOAT) + if (IS_FLT_DBL_TYPE(src->type)) { + if (IS_2_WORD_TYPE(src->type)) + M_DMOV(s1, d); + else + M_FMOV(s1, d); + } + else { + if (IS_2_WORD_TYPE(src->type)) + M_LNGMOVE(s1, d); + else + /* XXX grrrr, wrong direction! */ + M_MOV(d, s1); + } +#else + if (IS_2_WORD_TYPE(src->type)) + M_LNGMOVE(s1, d); + else + /* XXX grrrr, wrong direction! */ + M_MOV(d, s1); +#endif + } + + emit_store(jd, iptr, dst, d); + } +} + + +/* emit_iconst ***************************************************************** + + XXX + +*******************************************************************************/ + +void emit_iconst(codegendata *cd, s4 d, s4 value) +{ + s4 disp; + + if (IS_IMM(value)) + M_MOV_IMM(d, value); + else { + disp = dseg_add_s4(cd, value); + M_DSEG_LOAD(d, disp); + } +} + + +/* emit_nullpointer_check ****************************************************** + + Emit a NullPointerException check. + +*******************************************************************************/ + +void emit_nullpointer_check(codegendata *cd, instruction *iptr, s4 reg) +{ + if (INSTRUCTION_MUST_CHECK(iptr)) { + M_TST(reg, reg); + M_BEQ(0); + codegen_add_nullpointerexception_ref(cd); + } +} + + +/* emit_arrayindexoutofbounds_check ******************************************** + + Emit a ArrayIndexOutOfBoundsException check. + +*******************************************************************************/ + +void emit_arrayindexoutofbounds_check(codegendata *cd, instruction *iptr, s4 s1, s4 s2) +{ + if (INSTRUCTION_MUST_CHECK(iptr)) { + M_ILD_INTERN(REG_ITMP3, s1, OFFSET(java_arrayheader, size)); + M_CMP(s2, REG_ITMP3); + M_BHS(0); + codegen_add_arrayindexoutofboundsexception_ref(cd, s2); + } +} + + +/* emit_exception_stubs ******************************************************** + + Generates the code for the exception stubs. + +*******************************************************************************/ + +void emit_exception_stubs(jitdata *jd) +{ + codegendata *cd; + registerdata *rd; + exceptionref *eref; + s4 targetdisp; + s4 disp; + + /* get required compiler data */ + + cd = jd->cd; + rd = jd->rd; + + /* generate exception stubs */ + + targetdisp = 0; + + for (eref = cd->exceptionrefs; eref != NULL; eref = eref->next) { + gen_resolvebranch(cd->mcodebase + eref->branchpos, + eref->branchpos, cd->mcodeptr - cd->mcodebase); + + MCODECHECK(100); + + /* Check if the exception is an + ArrayIndexOutOfBoundsException. If so, move index register + into REG_ITMP1. */ + + if (eref->reg != -1) + M_MOV(REG_ITMP1, eref->reg); + + /* calcuate exception address */ + + assert((eref->branchpos - 4) % 4 == 0); + M_ADD_IMM_EXT_MUL4(REG_ITMP2_XPC, REG_IP, (eref->branchpos - 4) / 4); + + /* move function to call into REG_ITMP3 */ + + disp = dseg_add_functionptr(cd, eref->function); + M_DSEG_LOAD(REG_ITMP3, disp); + + if (targetdisp == 0) { + targetdisp = ((u4 *) cd->mcodeptr) - ((u4 *) cd->mcodebase); + + M_MOV(rd->argintregs[0], REG_IP); + M_MOV(rd->argintregs[1], REG_SP); + + if (jd->isleafmethod) + M_MOV(rd->argintregs[2], REG_LR); + else + M_LDR(rd->argintregs[2], REG_SP, + cd->stackframesize * 4 - SIZEOF_VOID_P); + + M_MOV(rd->argintregs[3], REG_ITMP2_XPC); + + /* save registers */ + /* TODO: we only need to save LR in leaf methods */ + + M_STMFD(BITMASK_ARGS | 1<mcodebase) + targetdisp) - + (((u4 *) cd->mcodeptr) + 2); + + M_B(disp); + } + } +} + + +/* emit_patcher_stubs ********************************************************** + + Generates the code for the patcher stubs. + +*******************************************************************************/ + +void emit_patcher_stubs(jitdata *jd) +{ + codegendata *cd; + patchref *pref; + u4 mcode; + u1 *savedmcodeptr; + u1 *tmpmcodeptr; + s4 targetdisp; + s4 disp; + + /* get required compiler data */ + + cd = jd->cd; + + /* generate patcher stub call code */ + + targetdisp = 0; + + for (pref = cd->patchrefs; pref != NULL; pref = pref->next) { + /* check code segment size */ + + MCODECHECK(100); + + /* Get machine code which is patched back in later. The + call is 1 instruction word long. */ + + tmpmcodeptr = (u1 *) (cd->mcodebase + pref->branchpos); + + mcode = *((u4 *) tmpmcodeptr); + + /* Patch in the call to call the following code (done at + compile time). */ + + savedmcodeptr = cd->mcodeptr; /* save current mcodeptr */ + cd->mcodeptr = tmpmcodeptr; /* set mcodeptr to patch position */ + + disp = ((u4 *) savedmcodeptr) - (((u4 *) tmpmcodeptr) + 2); + M_B(disp); + + cd->mcodeptr = savedmcodeptr; /* restore the current mcodeptr */ + + /* create stack frame */ + + M_SUB_IMM(REG_SP, REG_SP, 7 * 4); + + /* save itmp3 onto stack */ + + M_STR_INTERN(REG_ITMP3, REG_SP, 6 * 4); + + /* calculate return address and move it onto stack */ + /* ATTENTION: we can not use BL to branch to patcher stub, */ + /* ATTENTION: because we need to preserve LR for leaf methods */ + + disp = (s4) (((u4 *) cd->mcodeptr) - (((u4 *) tmpmcodeptr) + 1) + 2); + + M_SUB_IMM_EXT_MUL4(REG_ITMP3, REG_PC, disp); + M_STR_INTERN(REG_ITMP3, REG_SP, 4 * 4); + + /* move pointer to java_objectheader onto stack */ + +#if defined(ENABLE_THREADS) + /* order reversed because of data segment layout */ + + (void) dseg_add_unique_address(cd, NULL); /* flcword */ + (void) dseg_add_unique_address(cd, lock_get_initial_lock_word()); + disp = dseg_add_unique_address(cd, NULL); /* vftbl */ + + M_SUB_IMM_EXT_MUL4(REG_ITMP3, REG_IP, -disp / 4); + M_STR_INTERN(REG_ITMP3, REG_SP, 3 * 4); +#else + M_EOR(REG_ITMP3, REG_ITMP3, REG_ITMP3); + M_STR_INTERN(REG_ITMP3, REG_SP, 3 * 4); +#endif + + /* move machine code onto stack */ + + disp = dseg_add_unique_s4(cd, mcode); + M_DSEG_LOAD(REG_ITMP3, disp); + M_STR_INTERN(REG_ITMP3, REG_SP, 2 * 4); + + /* move class/method/field reference onto stack */ + + disp = dseg_add_unique_address(cd, pref->ref); + M_DSEG_LOAD(REG_ITMP3, disp); + M_STR_INTERN(REG_ITMP3, REG_SP, 1 * 4); + + /* move data segment displacement onto stack */ + + disp = dseg_add_unique_s4(cd, pref->disp); + M_DSEG_LOAD(REG_ITMP3, disp); + M_STR_INTERN(REG_ITMP3, REG_SP, 5 * 4); + + /* move patcher function pointer onto stack */ + + disp = dseg_add_functionptr(cd, pref->patcher); + M_DSEG_LOAD(REG_ITMP3, disp); + M_STR_INTERN(REG_ITMP3, REG_SP, 0 * 4); + + /* finally call the patcher via asm_patcher_wrapper */ + /* ATTENTION: don't use REG_IP here, because some patchers need it */ + + if (targetdisp == 0) { + targetdisp = ((u4 *) cd->mcodeptr) - ((u4 *) cd->mcodebase); + + disp = dseg_add_functionptr(cd, asm_patcher_wrapper); + /*M_DSEG_BRANCH_NOLINK(REG_PC, REG_IP, a);*/ + /* TODO: this is only a hack */ + M_DSEG_LOAD(REG_ITMP3, disp); + M_MOV(REG_PC, REG_ITMP3); + } + else { + disp = (((u4 *) cd->mcodebase) + targetdisp) - + (((u4 *) cd->mcodeptr) + 2); + + M_B(disp); + } + } +} + + +/* emit_replacement_stubs ****************************************************** + + Generates the code for the replacement stubs. + +*******************************************************************************/ + +#if defined(ENABLE_REPLACEMENT) +void emit_replacement_stubs(jitdata *jd) +{ + codegendata *cd; + codeinfo *code; + rplpoint *rplp; + u1 *savedmcodeptr; + s4 disp; + s4 i; + + /* get required compiler data */ + + cd = jd->cd; + code = jd->code; +} +#endif /* defined(ENABLE_REPLACEMENT) */ + + +/* emit_verbosecall_enter ****************************************************** + + Generates the code for the call trace. + +*******************************************************************************/ + +#if !defined(NDEBUG) +void emit_verbosecall_enter(jitdata *jd) +{ + methodinfo *m; + codegendata *cd; + registerdata *rd; + methoddesc *md; + s4 stackframesize; + s4 disp; + s4 i, t, s1, s2; + + /* get required compiler data */ + + m = jd->m; + cd = jd->cd; + rd = jd->rd; + + md = m->parseddesc; + + /* stackframesize is changed below */ + + stackframesize = cd->stackframesize; + + /* mark trace code */ + + M_NOP; + + /* save argument registers to stack (including LR and IP) */ + M_STMFD(BITMASK_ARGS | (1<paramcount - 1; + + if (i > 3) + i = 3; + + for (; i >= 0; i--) { + t = md->paramtypes[i].type; + + /* load argument into register (s1) and make it of TYPE_LNG */ + + if (!md->params[i].inmemory) { + s1 = md->params[i].regoff; + + if (!IS_2_WORD_TYPE(t)) { + M_MOV_IMM(REG_ITMP1, 0); + s1 = PACK_REGS(s1, REG_ITMP1); + } + else { + SPLIT_OPEN(t, s1, REG_ITMP1); + SPLIT_LOAD(t, s1, stackframesize); + } + } + else { + s1 = md->params[i].regoff + stackframesize; + + if (IS_2_WORD_TYPE(t)) + M_LLD(REG_ITMP12_PACKED, REG_SP, s1 * 4); + else + M_ILD(REG_ITMP1, REG_SP, s1 * 4); + } + + /* place argument for tracer */ + + if (i < 2) { +#if defined(__ARMEL__) + s2 = PACK_REGS(rd->argintregs[i * 2], rd->argintregs[i * 2 + 1]); +#else /* defined(__ARMEB__) */ + s2 = PACK_REGS(rd->argintregs[i * 2 + 1], rd->argintregs[i * 2]); +#endif + M_LNGMOVE(s1, s2); + } + else { + s2 = (i - 2) * 2; + M_LST(s1, REG_SP, s2 * 4); + } + } + + /* prepare methodinfo pointer for tracer */ + + disp = dseg_add_address(cd, m); + M_DSEG_LOAD(REG_ITMP1, disp); + M_STR_INTERN(REG_ITMP1, REG_SP, 16); + + /* call tracer here (we use a long branch) */ + + M_LONGBRANCH(builtin_trace_args); + + /* restore argument registers from stack */ + + M_ADD_IMM(REG_SP, REG_SP, (2 + 2 + 1) * 4); /* free argument stack */ + M_LDMFD(BITMASK_ARGS | (1<m; + cd = jd->cd; + rd = jd->rd; + + md = m->parseddesc; + + /* mark trace code */ + + M_NOP; + + M_STMFD(BITMASK_RESULT | (1<argintregs[1], rd->argintregs[2]); +#else /* defined(__ARMEB__) */ + s1 = PACK_REGS(rd->argintregs[2], rd->argintregs[1]); +#endif + + switch (md->returntype.type) { + case TYPE_ADR: + case TYPE_INT: + M_INTMOVE(REG_RESULT, GET_LOW_REG(s1)); + M_MOV_IMM(GET_HIGH_REG(s1), 0); + break; + + case TYPE_LNG: + M_LNGMOVE(REG_RESULT_PACKED, s1); + break; + + case TYPE_FLT: + M_IST(REG_RESULT, REG_SP, 1 * 4); + break; + + case TYPE_DBL: + s1 = rd->argintregs[3]; + M_INTMOVE(REG_RESULT, s1); + M_IST(REG_RESULT2, REG_SP, 0 * 4); + break; + } + + disp = dseg_add_address(cd, m); + M_DSEG_LOAD(rd->argintregs[0], disp); + M_LONGBRANCH(builtin_displaymethodstop); + + M_ADD_IMM(REG_SP, REG_SP, (1 + 1) * 4); /* free argument stack */ + M_LDMFD(BITMASK_RESULT | (1< +#undef ucontext +#undef ucontext_t + +typedef struct ucontext { + unsigned long uc_flags; + struct ucontext *uc_link; + stack_t uc_stack; + struct sigcontext uc_mcontext; + sigset_t uc_sigmask; +} ucontext_t; + +#define scontext_t struct sigcontext + +#include "mm/memory.h" +#include "vm/exceptions.h" +#include "vm/signallocal.h" +#include "vm/stringlocal.h" +#include "vm/jit/asmpart.h" +#include "vm/jit/stacktrace.h" + + +/* md_signal_handler_sigsegv *************************************************** + + NullPointerException signal handler for hardware null pointer + check. + +*******************************************************************************/ + +void md_signal_handler_sigsegv(int sig, siginfo_t *siginfo, void *_p) +{ + ucontext_t *_uc; + /*mcontext_t *_mc;*/ + scontext_t *_sc; + u4 instr; + ptrint addr; + ptrint base; + u1 *pv; + u1 *sp; + u1 *ra; + u1 *xpc; + + _uc = (ucontext_t*) _p; + _sc = &_uc->uc_mcontext; + + /* ATTENTION: glibc included messed up kernel headers */ + /* we needed a workaround for the ucontext structure */ + + addr = (ptrint) siginfo->si_addr; + /*xpc = (u1*) _mc->gregs[REG_PC];*/ + xpc = (u1*) _sc->arm_pc; + + instr = *((s4*) xpc); + base = *((s4*) _sc + OFFSET(scontext_t, arm_r0)/4 + ((instr >> 16) & 0x0f)); + + if (base == 0) { + pv = (u1*) _sc->arm_ip; + sp = (u1*) _sc->arm_sp; + ra = (u1*) _sc->arm_lr; /* this is correct for leafs */ + + _sc->arm_r10 = (ptrint) stacktrace_hardware_nullpointerexception(pv, sp, ra, xpc); + _sc->arm_fp = (ptrint) xpc; + _sc->arm_pc = (ptrint) asm_handle_exception; + } else { + throw_cacao_exception_exit(string_java_lang_InternalError, + "Segmentation fault: %p (pc=%p, instr=%x, base=%p)\n", + addr, xpc, instr, base); + } +} + + +/* thread_restartcriticalsection *********************************************** + + TODO: document me + +*******************************************************************************/ + +#if defined(ENABLE_THREADS) +void thread_restartcriticalsection(ucontext_t *_uc) +{ + scontext_t *_sc; + void *critical; + + _sc = &_uc->uc_mcontext; + + critical = critical_find_restart_point((void *) _sc->arm_pc); + + if (critical) + _sc->arm_pc = (ptrint) critical; +} +#endif + + +/* + * These are local overrides for various environment variables in Emacs. + * Please do not remove this and leave it at the end of the file, where + * Emacs will automagically detect them. + * --------------------------------------------------------------------- + * Local variables: + * mode: c + * indent-tabs-mode: t + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ + diff --git a/src/vm/jit/arm/machine-instr.h b/src/vm/jit/arm/machine-instr.h new file mode 100644 index 000000000..05cae0e46 --- /dev/null +++ b/src/vm/jit/arm/machine-instr.h @@ -0,0 +1,56 @@ +#ifndef _MACHINE_INSTR_H +#define _MACHINE_INSTR_H + +static inline void atomic_add(int *mem, int val) +{ + int temp, temp2, temp3; + /*dolog("atomic_add(%p [%d], %d)", mem, *mem, val);*/ + + /* TODO: improve this one! */ + __asm__ __volatile__ ( + "1:\t" + "ldr %0,[%3]\n\t" + "add %1,%0,%4\n\t" + "swp %2,%1,[%3]\n\t" + "cmp %0,%2\n\t" + "swpne %1,%2,[%3]\n\t" + "bne 1b" + : "=&r" (temp), "=&r" (temp2), "=&r" (temp3) + : "r" (mem), "r"(val) + : "cc", "memory" + ); + + /*dolog("atomic_add() mem=%d", *mem);*/ +} + +static inline long compare_and_swap(long *p, long oldval, long newval) +{ + long ret, temp; + /*dolog("compare_and_swap(%p [%d], %d, %d)", p, *p, oldval, newval);*/ + + /* TODO: improve this one! */ + __asm__ __volatile__ ( + "1:\t" + "ldr %0,[%2]\n\t" + "cmp %0,%4\n\t" + "bne 2f\n\t" + "swp %1,%3,[%2]\n\t" + "cmp %1,%0\n\t" + "swpne %0,%1,[%2]\n\t" + "bne 1b\n\t" + "2:" + : "=&r" (ret), "=&r" (temp) + : "r" (p), "r" (newval), "r" (oldval) + : "cc", "memory" + ); + + /*dolog("compare_and_swap() return=%d mem=%d", ret, *p);*/ + return ret; +} + +#define STORE_ORDER_BARRIER() __asm__ __volatile__ ("" : : : "memory"); +#define MEMORY_BARRIER_BEFORE_ATOMIC() __asm__ __volatile__ ("" : : : "memory"); +#define MEMORY_BARRIER_AFTER_ATOMIC() __asm__ __volatile__ ("" : : : "memory"); +#define MEMORY_BARRIER() __asm__ __volatile__ ("" : : : "memory" ); + +#endif diff --git a/src/vm/jit/arm/md-abi.c b/src/vm/jit/arm/md-abi.c new file mode 100644 index 000000000..eb547663f --- /dev/null +++ b/src/vm/jit/arm/md-abi.c @@ -0,0 +1,253 @@ +/* src/vm/jit/arm/md-abi.c - functions for arm ABI + + Copyright (C) 1996-2005, 2006 R. Grafl, A. Krall, C. Kruegel, + C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring, + E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich, + J. Wenninger, Institut f. Computersprachen - TU Wien + + This file is part of CACAO. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + + Contact: cacao@cacaojvm.org + + Authors: Michael Starzinger + + Changes: Christian Thalinger + + $Id: md-abi.c 6548 2006-10-01 22:18:38Z edwin $ + +*/ + + +#include "config.h" +#include "vm/types.h" + +#include "vm/jit/arm/md-abi.h" + +#include "vm/descriptor.h" +#include "vm/global.h" +#include "vm/jit/abi.h" + + +/* register descripton array **************************************************/ + +s4 nregdescint[] = { + REG_ARG, REG_ARG, REG_ARG, REG_ARG, REG_SAV, REG_SAV, REG_SAV, REG_SAV, + REG_SAV, REG_RES, REG_RES, REG_RES, REG_RES, REG_RES, REG_RES, REG_RES, + REG_END +}; + +#if defined(ENABLE_SOFTFLOAT) +s4 nregdescfloat[] = { + REG_RES, REG_RES, REG_RES, REG_RES, + REG_RES, REG_RES, REG_RES, REG_RES, + REG_END +}; +#else +/* TODO: FPA register usage conventions */ +s4 nregdescfloat[] = { + REG_TMP, REG_TMP, REG_TMP, REG_TMP, + REG_TMP, REG_TMP, REG_RES, REG_RES, + REG_END +}; +#endif /* defined(ENABLE_SOFTFLOAT) */ + + +/* md_param_alloc ************************************************************** + + Allocate Arguments to Stackslots according the Calling Conventions + + --- in: + md->paramcount: Number of arguments for this method + md->paramtypes[].type: Argument types + + --- out: + md->params[].inmemory: Argument spilled on stack + md->params[].regoff: Stack offset or rd->arg[int|flt]regs index + md->memuse: Stackslots needed for argument spilling + md->argintreguse: max number of integer arguments used + md->argfltreguse: max number of float arguments used + +*******************************************************************************/ + +void md_param_alloc(methoddesc *md) +{ + paramdesc *pd; + s4 i; + s4 reguse; + s4 stacksize; + + /* set default values */ + reguse = 0; + stacksize = 0; + + /* get params field of methoddesc */ + pd = md->params; + + for (i = 0; i < md->paramcount; i++, pd++) { + switch (md->paramtypes[i].type) { + case TYPE_INT: + case TYPE_ADR: + case TYPE_FLT: + if (reguse < INT_ARG_CNT) { + pd->inmemory = false; + pd->regoff = reguse; + reguse++; + } + else { + pd->inmemory = true; + pd->regoff = stacksize; + stacksize++; + } + break; + + case TYPE_LNG: + case TYPE_DBL: + if (reguse+1 < INT_ARG_CNT) { + pd->inmemory = false; +#if defined(__ARMEL__) + pd->regoff = PACK_REGS(reguse, reguse+1); +#else + pd->regoff = PACK_REGS(reguse+1, reguse); +#endif + reguse += 2; + } + else if (reguse < INT_ARG_CNT) { + pd->inmemory = false; +#if defined(__ARMEL__) + pd->regoff = PACK_REGS(reguse, INT_ARG_CNT); +#else + pd->regoff = PACK_REGS(INT_ARG_CNT, reguse); +#endif + reguse++; + stacksize++; + } + else { + pd->inmemory = true; + pd->regoff = stacksize; + stacksize += 2; + } + break; + } + } + + /* Since R0/R1 (==A0/A1) are used for passing return values, this + argument register usage has to be regarded, too. */ + + if (md->returntype.type != TYPE_VOID) { + if (!IS_2_WORD_TYPE(md->returntype.type)) { + if (reguse < 1) + reguse = 1; + } + else { + if (reguse < 2) + reguse = 2; + } + } + + /* fill register and stack usage */ + + md->argintreguse = reguse; + md->argfltreguse = 0; + md->memuse = stacksize; +} + + +/* md_return_alloc ************************************************************* + + Precolor the Java Stackelement containing the Return Value, if possible. + + --- in + m: Methodinfo of current method + return_type: Return Type of the Method (TYPE_INT.. TYPE_ADR) + TYPE_VOID is not allowed! + stackslot: Java Stackslot to contain the Return Value + + --- out + if precoloring was possible: + VAR(stackslot->varnum)->flags = PREALLOC + VAR(stackslot->varnum)->vv.regoff = [REG_RESULT, (REG_RESULT2/REG_RESULT), REG_FRESULT] + rd->arg[flt|int]reguse set to a value according the register usage + +*******************************************************************************/ + +void md_return_alloc(jitdata *jd, stackptr stackslot) +{ + methodinfo *m; + registerdata *rd; + methoddesc *md; + + /* get required compiler data */ + + m = jd->m; + rd = jd->rd; + + md = m->parseddesc; + + /* In Leafmethods Local Vars holding parameters are precolored to + their argument register -> so leafmethods with paramcount > 0 + could already use R0 == a00! */ + + if (!jd->isleafmethod || (md->paramcount == 0)) { + /* Only precolor the stackslot, if it is not a SAVEDVAR <-> + has not to survive method invokations. */ + + if (!(stackslot->flags & SAVEDVAR)) { +#if !defined(ENABLE_SOFTFLOAT) + /* Stackelements containing float or double values + (TYPE_FLT | TYPE_DBL) cannot be precolored, because we + use integer register to pass return values. (floats: + R0, doubles: R0/R1) */ + + if (!IS_FLT_DBL_TYPE(md->returntype.type)) { +#endif + + VAR(stackslot->varnum)->flags = PREALLOC; + + if (!IS_2_WORD_TYPE(md->returntype.type)) { + if (rd->argintreguse < 1) + rd->argintreguse = 1; + + VAR(stackslot->varnum)->vv.regoff = REG_RESULT; + } + else { + if (rd->argintreguse < 2) + rd->argintreguse = 2; + + VAR(stackslot->varnum)->vv.regoff = REG_RESULT_PACKED; + } + +#if !defined(ENABLE_SOFTFLOAT) + } +#endif + } + } +} + + +/* + * These are local overrides for various environment variables in Emacs. + * Please do not remove this and leave it at the end of the file, where + * Emacs will automagically detect them. + * --------------------------------------------------------------------- + * Local variables: + * mode: c + * indent-tabs-mode: t + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff --git a/src/vm/jit/arm/md-abi.h b/src/vm/jit/arm/md-abi.h new file mode 100644 index 000000000..960965c3a --- /dev/null +++ b/src/vm/jit/arm/md-abi.h @@ -0,0 +1,148 @@ +/* src/vm/jit/arm/md-abi.h - defines for arm ABI + + Copyright (C) 1996-2005, 2006 R. Grafl, A. Krall, C. Kruegel, + C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring, + E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich, + J. Wenninger, Institut f. Computersprachen - TU Wien + + This file is part of CACAO. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + + Contact: cacao@cacaojvm.org + + Authors: Michael Starzinger + + $Id: md-abi.h 6545 2006-09-06 21:42:42Z twisti $ + +*/ + + +#ifndef _MD_ABI_H +#define _MD_ABI_H + + +/* preallocated registers *****************************************************/ + +/* integer registers */ + +#define REG_RESULT 0 /* to deliver method results */ +#define REG_RESULT2 1 /* to deliver long method results */ + +#define REG_IP 12 /* intra-procedure-call scratch register */ +#define REG_SP 13 /* stack pointer */ +#define REG_LR 14 /* link register */ +#define REG_PC 15 /* program counter */ + +#define REG_METHODPTR 11 /* special register to fetch procedure vector */ + +#define REG_ITMP1 10 /* temporary register */ +#define REG_ITMP2 11 /* temporary register and method pointer */ +#define REG_ITMP3 9 /* temporary register */ + +#define REG_ITMP1_XPTR 10 /* exception pointer = temporary register 1 */ +#define REG_ITMP2_XPC 11 /* exception pc = temporary register 2 */ + +#define REG_A0 0 /* define some argument registers */ +#define REG_A1 1 +#define REG_A2 2 +#define REG_A3 3 + +#define REG_SPLIT 16 /* dummy register to mark a split of longs and */ + /* doubles across register/stack barrier */ + /* set LOW_REG = ARG4 and HIGH_REG = REG_SPLIT */ + +#define BITMASK_ARGS 0x0f /* bitmask for LDM/STM to save method arguments */ +#define BITMASK_RESULT 0x03 /* bitmask for LDM/STM to save method results */ + +#if !defined(ENABLE_SOFTFLOAT) +/* floating point registers */ + +#define REG_FRESULT 0 /* to deliver floating point method results */ +#define REG_FTMP1 6 /* temporary floating point register */ +#define REG_FTMP2 7 /* temporary floating point register */ + +#endif /* !defined(ENABLE_SOFTFLOAT) */ + + +/* register count *************************************************************/ +#define INT_REG_CNT 16 /* number of integer registers */ +#define INT_TMP_CNT 0 /* number of integer registers */ +#define INT_SAV_CNT 5 /* number of int callee saved registers */ +#define INT_ARG_CNT 4 /* number of int argument registers */ +#define INT_RES_CNT 7 /* number of reserved integer registers */ + +#define FLT_REG_CNT 8 /* number of float registers */ +#if defined(ENABLE_SOFTFLOAT) +# define FLT_TMP_CNT 0 /* number of flt temp registers */ +# define FLT_SAV_CNT 0 /* number of flt callee saved registers */ +# define FLT_ARG_CNT 0 /* number of flt argument registers */ +# define FLT_RES_CNT 8 /* number of reserved float registers */ +#else +# define FLT_TMP_CNT 6 /* number of flt temp registers */ +# define FLT_SAV_CNT 0 /* number of flt callee saved registers */ +# define FLT_ARG_CNT 0 /* number of flt argument registers */ +# define FLT_RES_CNT 2 /* number of reserved float registers */ +#endif /* defined(ENABLE_SOFTFLOAT) */ + +#define TRACE_ARGS_NUM 4 + + +/* Register Pack/Unpack Macros ************************************************/ + +#if defined(__ARMEL__) + +# define REG_ITMP12_PACKED PACK_REGS(REG_ITMP1, REG_ITMP2) +# define REG_ITMP23_PACKED PACK_REGS(REG_ITMP2, REG_ITMP3) +# define REG_RESULT_PACKED PACK_REGS(REG_RESULT, REG_RESULT2) + +# define REG_A0_A1_PACKED PACK_REGS(REG_A0, REG_A1) +# define REG_A2_A3_PACKED PACK_REGS(REG_A2, REG_A3) + +#else /* defined(__ARMEB__) */ + +# define REG_ITMP12_PACKED PACK_REGS(REG_ITMP2, REG_ITMP1) +# define REG_ITMP23_PACKED PACK_REGS(REG_ITMP3, REG_ITMP2) +# define REG_RESULT_PACKED PACK_REGS(REG_RESULT2, REG_RESULT) + +# define REG_A0_A1_PACKED PACK_REGS(REG_A1, REG_A0) +# define REG_A2_A3_PACKED PACK_REGS(REG_A3, REG_A2) + +#endif + +#define REG_ITMP12_TYPED(t) ((IS_2_WORD_TYPE(t)) ? REG_ITMP12_PACKED : REG_ITMP1) +#define REG_RESULT_TYPED(t) ((IS_2_WORD_TYPE(t)) ? REG_RESULT_PACKED : REG_RESULT) +#define ARGUMENT_REGS(t,a) ((IS_2_WORD_TYPE(t)) ? \ + (PACK_REGS(rd->argintregs[GET_LOW_REG(a)],rd->argintregs[GET_HIGH_REG(a)])) : \ + (rd->argintregs[(a)]) \ + ) + + +#endif /* _MD_ABI_H */ + + +/* + * These are local overrides for various environment variables in Emacs. + * Please do not remove this and leave it at the end of the file, where + * Emacs will automagically detect them. + * --------------------------------------------------------------------- + * Local variables: + * mode: c + * indent-tabs-mode: t + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff --git a/src/vm/jit/arm/md-asm.h b/src/vm/jit/arm/md-asm.h new file mode 100644 index 000000000..0956af7e0 --- /dev/null +++ b/src/vm/jit/arm/md-asm.h @@ -0,0 +1,129 @@ +/* src/vm/jit/arm/md-asm.h - assembler defines for arm ABI + + Copyright (C) 1996-2005, 2006 R. Grafl, A. Krall, C. Kruegel, + C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring, + E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich, + J. Wenninger, Institut f. Computersprachen - TU Wien + + This file is part of CACAO. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + + Contact: cacao@cacaojvm.org + + Authors: Michael Starzinger + + Changes: Christian Thalinger + + $Id: md-asm.h 6514 2006-02-17 20:05:00Z michi $ + +*/ + + +#ifndef _MD_ASM_H +#define _MD_ASM_H + +#include "config.h" + + +/* register defines ***********************************************************/ + +#define res1 r0 /* result registers */ +#define res2 r1 + +#define a1 r0 /* argument registers */ +#define a2 r1 +#define a3 r2 +#define a4 r3 + +#define v1 r4 /* variable registers */ +#define v2 r5 +#define v3 r6 +#define v4 r7 +#define v5 r8 +#define v6 r9 +#define v7 r10 +#define v8 r11 + +#define pc r15 /* program counter */ +#define lr r14 /* return address */ +#define sp r13 /* stack pointer */ +#define ip r12 /* something like pv */ +#define fp r11 /* frame pointer (not used) */ + +#define itmp1 v7 /* temporary scratch regs */ +#define itmp2 v8 +#define itmp3 v6 + +#define mptr v8 + +#define xptr itmp1 /* exception registers */ +#define xpc itmp2 + + +/* save and restore macros ****************************************************/ + +#define SAVE_ARGUMENT_REGISTERS \ + stmfd sp!, {a1,a2,a3,a4,lr} + +#define SAVE_ARGUMENT_REGISTERS_IP \ + stmfd sp!, {a1,a2,a3,a4,ip,lr} + +#if defined(ENABLE_SOFTFLOAT) +# define SAVE_FLOAT_REGISTERS +#else +# define SAVE_FLOAT_REGISTERS \ + sfmfd f0, 4, [sp]!; \ + sfmfd f4, 4, [sp]! +#endif + +#define SAVE_SCRATCH_REGISTERS \ + stmfd sp!, {itmp3,itmp1,itmp2,lr} + + +#define RESTORE_ARGUMENT_REGISTERS \ + ldmfd sp!, {a1,a2,a3,a4,lr} + +#define RESTORE_ARGUMENT_REGISTERS_IP \ + ldmfd sp!, {a1,a2,a3,a4,ip,lr} + +#if defined(ENABLE_SOFTFLOAT) +# define RESTORE_FLOAT_REGISTERS +#else +# define RESTORE_FLOAT_REGISTERS \ + lfmfd f4, 4, [sp]!; \ + lfmfd f0, 4, [sp]! +#endif + +#define RESTORE_SCRATCH_REGS_AND_RETURN \ + ldmfd sp!, {itmp3,itmp1,itmp2,pc} + +#endif /* _MD_ASM_H */ + + +/* + * These are local overrides for various environment variables in Emacs. + * Please do not remove this and leave it at the end of the file, where + * Emacs will automagically detect them. + * --------------------------------------------------------------------- + * Local variables: + * mode: c + * indent-tabs-mode: t + * c-basic-offset: 4 + * tab-width: 4 + * End: + * vim:noexpandtab:sw=4:ts=4: + */ diff --git a/src/vm/jit/arm/md.c b/src/vm/jit/arm/md.c new file mode 100644 index 000000000..80c6b2fd2 --- /dev/null +++ b/src/vm/jit/arm/md.c @@ -0,0 +1,285 @@ +/* src/vm/jit/arm/md.c - machine dependent Arm functions + + Copyright (C) 1996-2005, 2006, 2007 R. Grafl, A. Krall, C. Kruegel, + C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring, + E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich, + J. Wenninger, Institut f. Computersprachen - TU Wien + + This file is part of CACAO. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + + Contact: cacao@cacaojvm.org + + Authors: Michael Starzinger + Christian Thalinger + + $Id: md.c 6591 2007-01-02 19:14:25Z twisti $ + +*/ + + +#include "config.h" + +#include + +#include "vm/global.h" +#include "vm/types.h" + +#include "vm/jit/arm/md-abi.h" + +#include "vm/exceptions.h" +#include "vm/stringlocal.h" +#include "vm/jit/asmpart.h" + + +/* md_init ********************************************************************* + + Do some machine dependent initialization. + +*******************************************************************************/ + +void md_init(void) +{ + /* do nothing here */ +} + + +/* md_codegen_patch_branch ***************************************************** + + Back-patches a branch instruction. + +*******************************************************************************/ + +void md_codegen_patch_branch(codegendata *cd, s4 branchmpc, s4 targetmpc) +{ + s4 *mcodeptr; + s4 disp; /* branch displacement */ + + /* calculate the patch position */ + + mcodeptr = (s4 *) (cd->mcodebase + branchmpc); + + /* Calculate the branch displacement. */ + + disp = (targetmpc - branchmpc - 4) >> 2; + + if ((disp < (s4) 0xff000000) || (disp > (s4) 0x00ffffff)) + vm_abort("md_codegen_patch_branch: branch displacement out of range: %d > +/-%d", disp, 0x00ffffff); + + /* patch the branch instruction before the mcodeptr */ + + mcodeptr[-1] |= (disp & 0x00ffffff); +} + + +/* md_stacktrace_get_returnaddress ********************************************* + + Returns the return address of the current stackframe, specified by + the passed stack pointer and the stack frame size. + +*******************************************************************************/ + +u1 *md_stacktrace_get_returnaddress(u1 *sp, u4 framesize) +{ + u1 *ra; + + /*printf("md_stacktrace_get_returnaddress(): called (sp=%x, framesize=%d)\n", sp, framesize);*/ + + /* on ARM the return address is located on the top of the stackframe */ + /* ATTENTION: this is only true for non-leaf methods !!! */ + ra = *((u1 **) (sp + framesize - SIZEOF_VOID_P)); + + /*printf("md_stacktrace_get_returnaddress(): result (ra=%x)\n", ra);*/ + + return ra; +} + + +/* md_assembler_get_patch_address ********************************************** + + Gets the patch address of the currently compiled method. The offset + is extracted from the load instruction(s) before the jump and added + to the right base address (PV or REG_METHODPTR). + + Machine code: + + e51cc040 ldr ip, [ip, #-64] + e1a0e00f mov lr, pc + e1a0f00c mov pc, ip + + or + + e590b000 ldr fp, [r0] + e59bc000 ldr ip, [fp] + e1a0e00f mov lr, pc + e1a0f00c mov pc, ip + + How we find out the patching address to store new method pointer: + - loaded IP with LDR IP,[METHODPTR]? + yes=INVOKEVIRTUAL or INVOKEINTERFACE (things are easy!) + - loaded IP from data segment + yes=INVOKESTATIC or INVOKESPECIAL (things are complicated) + recompute pointer to data segment, maybe larger offset + +*******************************************************************************/ + +u1 *md_get_method_patch_address(u1 *ra, stackframeinfo *sfi, u1 *mptr) +{ + u4 mcode; + s4 offset; + u1 *pa; /* patch address */ + + /* sanity check: are we inside jit code? */ + + assert(*((u4 *) (ra - 2*4)) == 0xe1a0e00f /*MOV LR,PC*/); + assert(*((u4 *) (ra - 1*4)) == 0xe1a0f00c /*MOV PC,IP*/); + + /* get the load instruction and offset */ + + mcode = *((u4 *) (ra - 12)); + offset = (s4) (mcode & 0x0fff); + + assert ((mcode & 0xff70f000) == 0xe510c000); + + if ((mcode & 0x000f0000) == 0x000b0000) { + /* sanity check: offset was positive */ + + assert((mcode & 0x00800000) == 0x00800000); + + /* we loaded from REG_METHODPTR */ + + pa = mptr + offset; + } + else { + /* sanity check: we loaded from REG_IP; offset was negative or zero */ + + assert((mcode & 0x008f0000) == 0x000c0000 || + (mcode & 0x008f0fff) == 0x008c0000); + + /* we loaded from data segment; offset can be larger */ + + mcode = *((u4 *) (ra - 4*4)); + + /* check for "SUB IP, IP, #??, ROTL 12" */ + + if ((mcode & 0xffffff00) == 0xe24cca00) + offset += (s4) ((mcode & 0x00ff) << 12); + + /* and get the final data segment address */ + + pa = sfi->pv - offset; + } + + return pa; +} + + +/* md_codegen_get_pv_from_pc *************************************************** + + TODO: document me + +*******************************************************************************/ + +u1 *md_codegen_get_pv_from_pc(u1 *ra) +{ + u1 *pv; + u4 mcode1, mcode2, mcode3; + + pv = ra; + + /* this can either be a RECOMPUTE_IP in JIT code or a fake in asm_calljavafunction */ + mcode1 = *((u4*) ra); + if ((mcode1 & 0xffffff00) == 0xe24fcf00 /*sub ip,pc,#__*/) + pv -= (s4) ((mcode1 & 0x000000ff) << 2); + else if ((mcode1 & 0xffffff00) == 0xe24fc000 /*sub ip,pc,#__*/) + pv -= (s4) (mcode1 & 0x000000ff); + else { + /* if this happens, we got an unexpected instruction at (*ra) */ + throw_cacao_exception_exit(string_java_lang_InternalError, + "Unable to find method: %p (instr=%x)\n", + ra, mcode1); + } + + /* if we have a RECOMPUTE_IP there can be more than one instruction */ + mcode2 = *((u4*) (ra + 4)); + mcode3 = *((u4*) (ra + 8)); + if ((mcode2 & 0xffffff00) == 0xe24ccb00 /*sub ip,ip,#__*/) + pv -= (s4) ((mcode2 & 0x000000ff) << 10); + if ((mcode3 & 0xffffff00) == 0xe24cc700 /*sub ip,ip,#__*/) + pv -= (s4) ((mcode3 & 0x000000ff) << 18); + + /* we used PC-relative adressing; but now it is LR-relative */ + pv += 8; + + /* if we found our method the data segment has to be valid */ + /* we check this by looking up the IsLeaf field, which has to be boolean */ + assert( *((s4*)pv-4) == (s4)true || *((s4*)pv-4) == (s4)false ); + + return pv; +} + + +/* md_cacheflush *************************************************************** + + Calls the system's function to flush the instruction and data + cache. + +*******************************************************************************/ + +void md_cacheflush(u1 *addr, s4 nbytes) +{ + asm_cacheflush(addr, nbytes); +} + + +/* md_icacheflush ************************************************************** + + Calls the system's function to flush the instruction cache. + +*******************************************************************************/ + +void md_icacheflush(u1 *addr, s4 nbytes) +{ + asm_cacheflush(addr, nbytes); +} + + +/* md_dcacheflush ************************************************************** + + Calls the system's function to flush the data cache. + +*******************************************************************************/ + +void md_dcacheflush(u1 *addr, s4 nbytes) +{ + asm_cacheflush(addr, nbytes); +} + + +/* + * These are local overrides for various environment variables in Emacs. + * Please do not remove this and leave it at the end of the file, where + * Emacs will automagically detect them. + * --------------------------------------------------------------------- + * Local variables: + * mode: c + * indent-tabs-mode: t + * c-basic-offset: 4 + * tab-width: 4 + * End: + * vim:noexpandtab:sw=4:ts=4: + */ diff --git a/src/vm/jit/arm/patcher.c b/src/vm/jit/arm/patcher.c new file mode 100644 index 000000000..f7135b238 --- /dev/null +++ b/src/vm/jit/arm/patcher.c @@ -0,0 +1,766 @@ +/* src/vm/jit/arm/patcher.c - ARM code patching functions + + Copyright (C) 1996-2005, 2006 R. Grafl, A. Krall, C. Kruegel, + C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring, + E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich, + J. Wenninger, Institut f. Computersprachen - TU Wien + + This file is part of CACAO. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + + Contact: cacao@cacaojvm.org + + Authors: Michael Starzinger + + Changes: Christian Thalinger + + $Id: patcher.c 6541 2006-08-22 14:48:01Z twisti $ + +*/ + + +#include "config.h" + +#include + +#include "vm/types.h" + +#include "mm/memory.h" +#include "native/native.h" +#include "vm/builtin.h" +#include "vm/exceptions.h" +#include "vm/field.h" +#include "vm/initialize.h" +#include "vm/options.h" +#include "vm/references.h" +#include "vm/resolve.h" +#include "vm/jit/asmpart.h" +#include "vm/jit/patcher.h" + + +#define gen_resolveload(inst,offset) \ + assert((offset) >= -0x0fff && (offset) <= 0x0fff); \ + assert(!((inst) & 0x0fff)); \ + if ((offset) < 0) { \ + (inst) = ((inst) & 0xff7ff000) | ((-(offset)) & 0x0fff); \ + /*(inst) &= ~(1 << 23);*/ \ + } else { \ + (inst) = ((inst) & 0xfffff000) | ((offset) & 0x0fff); \ + /*(inst) |= (1 << 23);*/ \ + } + + +/* patcher_wrapper ************************************************************* + + Wrapper for all patchers. It also creates the stackframe info + structure. + + If the return value of the patcher function is false, it gets the + exception object, clears the exception pointer and returns the + exception. + +*******************************************************************************/ + +java_objectheader *patcher_wrapper(u1 *sp, u1 *pv, u1 *ra) +{ + stackframeinfo sfi; + u1 *xpc; + java_objectheader *o; + u4 mcode; + functionptr f; + bool result; + java_objectheader *e; + + /* define the patcher function */ + + bool (*patcher_function)(u1 *); + + assert(pv != NULL); + + /* get stuff from the stack */ + + xpc = (u1 *) *((ptrint *) (sp + 4 * 4)); + o = (java_objectheader *) *((ptrint *) (sp + 3 * 4)); + f = (functionptr) *((ptrint *) (sp + 0 * 4)); + + /* calculate and set the new return address */ + + xpc = xpc - 1 * 4; + + *((ptrint *) (sp + 4 * 4)) = (ptrint) xpc; + + /* store PV into the patcher function position */ + + *((ptrint *) (sp + 0 * 4)) = (ptrint) pv; + + /* cast the passed function to a patcher function */ + + patcher_function = (bool (*)(u1 *)) (ptrint) f; + + /* enter a monitor on the patching position */ + + PATCHER_MONITORENTER; + + /* create the stackframeinfo */ + + stacktrace_create_extern_stackframeinfo(&sfi, pv, sp + 7 * 4, ra, xpc); + + /* call the proper patcher function */ + + result = (patcher_function)(sp); + + /* remove the stackframeinfo */ + + stacktrace_remove_stackframeinfo(&sfi); + + /* check for an error, get the exception and return it */ + + if (result == false) { + e = exceptions_get_and_clear_exception(); + + PATCHER_MONITOREXIT; + + return e; + } + + /* patch back original code */ + + mcode = *((u4 *) (sp + 2 * 4)); + + *((u4 *) xpc) = mcode; + + /* synchronize instruction cache */ + + md_icacheflush(xpc, 1 * 4); + + PATCHER_MARK_PATCHED_MONITOREXIT; + + return NULL; +} + + +/* patcher_get_putstatic ******************************************************* + + Machine code: + + + e51c103c ldr r1, [ip, #-60] + +*******************************************************************************/ + +bool patcher_get_putstatic(u1 *sp) +{ + s4 disp; + unresolved_field *uf; + u1 *pv; + fieldinfo *fi; + + /* get stuff from the stack */ + + disp = *((s4 *) (sp + 5 * 4)); + uf = (unresolved_field *) *((ptrint *) (sp + 1 * 4)); + pv = (u1 *) *((ptrint *) (sp + 0 * 4)); + + /* get the fieldinfo */ + + if (!(fi = resolve_field_eager(uf))) + return false; + + /* check if the field's class is initialized */ + + if (!(fi->class->state & CLASS_INITIALIZED)) + if (!initialize_class(fi->class)) + return false; + + /* patch the field value's address */ + + *((ptrint *) (pv + disp)) = (ptrint) &(fi->value); + + return true; +} + + +/* patcher_get_putfield ******************************************************** + + Machine code: + + + e58a8000 str r8, [sl, #__] + +*******************************************************************************/ + +bool patcher_get_putfield(u1 *sp) +{ + u1 *ra; + u4 mcode; + unresolved_field *uf; + u1 *pv; + fieldinfo *fi; + + /* get stuff from the stack */ + ra = (u1*) *((ptrint *) (sp + 4 * 4)); + mcode = *((u4 *) (sp + 2 * 4)); + uf = (unresolved_field*) *((ptrint *) (sp + 1 * 4)); + pv = (u1*) *((ptrint *) (sp + 0 * 4)); + + /* get the fieldinfo */ + + if (!(fi = resolve_field_eager(uf))) + return false; + + /* if we show disassembly, we have to skip the nop */ + + if (opt_showdisassemble) { + ra = ra + 1 * 4; + + /* patch the field's offset into the instruction */ + + switch(fi->type) { + case TYPE_ADR: + case TYPE_INT: +#if defined(ENABLE_SOFTFLOAT) + case TYPE_FLT: +#endif + assert(fi->offset <= 0x0fff); + *((u4 *) (ra + 0 * 4)) |= (fi->offset & 0x0fff); + break; + + case TYPE_LNG: +#if defined(ENABLE_SOFTFLOAT) + case TYPE_DBL: +#endif + assert((fi->offset + 4) <= 0x0fff); + *((u4 *) (ra + 0 * 4)) |= ((fi->offset + 0) & 0x0fff); + *((u4 *) (ra + 1 * 4)) |= ((fi->offset + 4) & 0x0fff); + break; + +#if !defined(ENABLE_SOFTFLOAT) + case TYPE_FLT: + case TYPE_DBL: + assert(fi->offset <= 0x03ff); + *((u4 *) (ra + 0 * 4)) |= ((fi->offset >> 2) & 0x00ff); + break; +#endif + } + } + else { + /* patch the field's offset into the instruction stored on the + stack and the next instruction in the code */ + + switch(fi->type) { + case TYPE_ADR: + case TYPE_INT: +#if defined(ENABLE_SOFTFLOAT) + case TYPE_FLT: +#endif + assert(fi->offset <= 0x0fff); + *((u4 *) (sp + 2 * 4)) |= (fi->offset & 0x0fff); + break; + + case TYPE_LNG: +#if defined(ENABLE_SOFTFLOAT) + case TYPE_DBL: +#endif + assert((fi->offset + 4) <= 0x0fff); + *((u4 *) (sp + 2 * 4)) |= ((fi->offset + 0) & 0x0fff); + *((u4 *) (ra + 1 * 4)) |= ((fi->offset + 4) & 0x0fff); + break; + +#if !defined(ENABLE_SOFTFLOAT) + case TYPE_FLT: + case TYPE_DBL: + assert(fi->offset <= 0x03ff); + *((u4 *) (sp + 2 * 4)) |= ((fi->offset >> 2) & 0x00ff); + break; +#endif + } + } + + /* synchronize instruction cache */ + + md_icacheflush(ra, 2 * 4); + + return true; +} + + +/* patcher_aconst ************************************************************** + + Machine code: + + + e51cc030 ldr r0, [ip, #-48] + +*******************************************************************************/ + +bool patcher_aconst(u1 *sp) +{ + s4 disp; + constant_classref *cr; + u1 *pv; + classinfo *c; + + /* get stuff from the stack */ + + disp = *((s4 *) (sp + 5 * 4)); + cr = (constant_classref *) *((ptrint *) (sp + 1 * 4)); + pv = (u1 *) *((ptrint *) (sp + 0 * 4)); + + /* get the classinfo */ + + if (!(c = resolve_classref_eager(cr))) + return false; + + /* patch the classinfo pointer */ + + *((ptrint *) (pv + disp)) = (ptrint) c; + + return true; +} + + +/* patcher_builtin_multianewarray ********************************************** + + Machine code: + + + e3a00002 mov r0, #2 ; 0x2 + e51c1064 ldr r1, [ip, #-100] + e1a0200d mov r2, sp + e1a0e00f mov lr, pc + e51cf068 ldr pc, [ip, #-104] + +*******************************************************************************/ + +bool patcher_builtin_multianewarray(u1 *sp) +{ + s4 disp; + constant_classref *cr; + u1 *pv; + classinfo *c; + + /* get stuff from the stack */ + + disp = *((s4 *) (sp + 5 * 4)); + cr = (constant_classref *) *((ptrint *) (sp + 1 * 4)); + pv = (u1 *) *((ptrint *) (sp + 0 * 4)); + + /* get the classinfo */ + + if (!(c = resolve_classref_eager(cr))) + return false; + + /* patch the classinfo pointer */ + + *((ptrint *) (pv + disp)) = (ptrint) c; + + return true; +} + + +/* patcher_builtin_arraycheckcast ********************************************** + + Machine code: + + + e51c1120 ldr r1, [ip, #-288] + e1a0e00f mov lr, pc + e51cf124 ldr pc, [ip, #-292] + +*******************************************************************************/ + +bool patcher_builtin_arraycheckcast(u1 *sp) +{ + s4 disp; + constant_classref *cr; + u1 *pv; + classinfo *c; + + /* get stuff from the stack */ + + disp = *((s4 *) (sp + 5 * 4)); + cr = (constant_classref *) *((ptrint *) (sp + 1 * 4)); + pv = (u1 *) *((ptrint *) (sp + 0 * 4)); + + /* get the classinfo */ + + if (!(c = resolve_classref_eager(cr))) + return false; + + /* patch the classinfo pointer */ + + *((ptrint *) (pv + disp)) = (ptrint) c; + + return true; +} + + +/* patcher_invokestatic_special ************************************************ + + Machine code: + + + e51cc02c ldr ip, [ip, #-44] + e1a0e00f mov lr, pc + e1a0f00c mov pc, ip + +******************************************************************************/ + +bool patcher_invokestatic_special(u1 *sp) +{ + s4 disp; + unresolved_method *um; + u1 *pv; + methodinfo *m; + + /* get stuff from the stack */ + + disp = *((s4 *) (sp + 5 * 4)); + um = (unresolved_method*) *((ptrint *) (sp + 1 * 4)); + pv = (u1*) *((ptrint *) (sp + 0 * 4)); + + /* get the methodinfo */ + + if (!(m = resolve_method_eager(um))) + return false; + + /* patch stubroutine */ + + *((ptrint *) (pv + disp)) = (ptrint) m->stubroutine; + + return true; +} + + +/* patcher_invokevirtual ******************************************************* + + Machine code: + + + e590b000 ldr fp, [r0] + e59bc000 ldr ip, [fp, #__] + e1a0e00f mov lr, pc + e1a0f00c mov pc, ip + +*******************************************************************************/ + +bool patcher_invokevirtual(u1 *sp) +{ + u1 *ra; + unresolved_method *um; + methodinfo *m; + + /* get stuff from the stack */ + + ra = (u1 *) *((ptrint *) (sp + 4 * 4)); + um = (unresolved_method *) *((ptrint *) (sp + 1 * 4)); + + /* get the methodinfo */ + + if (!(m = resolve_method_eager(um))) + return false; + + /* if we show disassembly, we have to skip the nop */ + + if (opt_showdisassemble) + ra = ra + 1 * 4; + + /* patch vftbl index */ + + gen_resolveload(*((s4 *) (ra + 1 * 4)), (s4) (OFFSET(vftbl_t, table[0]) + sizeof(methodptr) * m->vftblindex)); + + /* synchronize instruction cache */ + + md_icacheflush(ra + 1 * 4, 1 * 4); + + return true; +} + + +/* patcher_invokeinterface ***************************************************** + + Machine code: + + + e590b000 ldr fp, [r0] + e59bb000 ldr fp, [fp, #__] + e59bc000 ldr ip, [fp, #__] + e1a0e00f mov lr, pc + e1a0f00c mov pc, ip + + +*******************************************************************************/ + +bool patcher_invokeinterface(u1 *sp) +{ + u1 *ra; + unresolved_method *um; + methodinfo *m; + + /* get stuff from the stack */ + + ra = (u1 *) *((ptrint *) (sp + 4 * 4)); + um = (unresolved_method *) *((ptrint *) (sp + 1 * 4)); + + /* get the methodinfo */ + + if (!(m = resolve_method_eager(um))) + return false; + + /* if we show disassembly, we have to skip the nop */ + + if (opt_showdisassemble) + ra = ra + 1 * 4; + + /* patch interfacetable index */ + + gen_resolveload(*((s4 *) (ra + 1 * 4)), (s4) (OFFSET(vftbl_t, interfacetable[0]) - sizeof(methodptr*) * m->class->index)); + + /* patch method offset */ + + gen_resolveload(*((s4 *) (ra + 2 * 4)), (s4) (sizeof(methodptr) * (m - m->class->methods))); + + /* synchronize instruction cache */ + + md_icacheflush(ra + 1 * 4, 2 * 4); + + return true; +} + + +/* patcher_checkcast_instanceof_flags ****************************************** + + Machine code: + + + +*******************************************************************************/ + +bool patcher_checkcast_instanceof_flags(u1 *sp) +{ + s4 disp; + constant_classref *cr; + u1 *pv; + classinfo *c; + + /* get stuff from the stack */ + + disp = *((s4 *) (sp + 5 * 4)); + cr = (constant_classref *) *((ptrint *) (sp + 1 * 4)); + pv = (u1 *) *((ptrint *) (sp + 0 * 4)); + + /* get the classinfo */ + + if (!(c = resolve_classref_eager(cr))) + return false; + + /* patch class flags */ + + *((s4 *) (pv + disp)) = (s4) c->flags; + + return true; +} + + +/* patcher_checkcast_instanceof_interface ************************************** + + Machine code: + + + e59ab000 ldr fp, [sl] + e59b9010 ldr r9, [fp, #16] + e3590000 cmp r9, #0 ; 0x0 + da000000 ble 0x000000 + e59b9000 ldr r9, [fp, #__] + e1190009 tst r9, r9 + 0a000000 beq 0x000000 + +*******************************************************************************/ + +bool patcher_checkcast_instanceof_interface(u1 *sp) +{ + u1 *ra; + constant_classref *cr; + classinfo *c; + + /* get stuff from the stack */ + + ra = (u1 *) *((ptrint *) (sp + 4 * 4)); + cr = (constant_classref *) *((ptrint *) (sp + 1 * 4)); + + /* get the classinfo */ + + if (!(c = resolve_classref_eager(cr))) + return false; + + /* if we show disassembly, we have to skip the nop */ + + if (opt_showdisassemble) + ra = ra + 4; + + /* patch super class index */ + + assert(*((s4 *) (ra + 2 * 4)) == 0xe3590000); + assert(c->index <= 0xff); + + *((s4 *) (ra + 2 * 4)) |= (s4) (c->index & 0x000000ff); + + /* patch super class vftbl index */ + + gen_resolveload(*((s4 *) (ra + 4 * 4)), (s4) (OFFSET(vftbl_t, interfacetable[0]) - sizeof(methodptr*) * c->index)); + + /* synchronize instruction cache */ + + md_icacheflush(ra + 2 * 4, 3 * 4); + + return true; +} + + +/* patcher_checkcast_instanceof_class ****************************************** + + Machine code: + + + +*******************************************************************************/ + +bool patcher_checkcast_instanceof_class(u1 *sp) +{ + s4 disp; + constant_classref *cr; + u1 *pv; + classinfo *c; + + /* get stuff from the stack */ + + disp = *((s4 *) (sp + 5 * 4)); + cr = (constant_classref *) *((ptrint *) (sp + 1 * 4)); + pv = (u1 *) *((ptrint *) (sp + 0 * 4)); + + /* get the classinfo */ + + if (!(c = resolve_classref_eager(cr))) + return false; + + /* patch super class' vftbl */ + + *((ptrint *) (pv + disp)) = (ptrint) c->vftbl; + + return true; +} + + +/* patcher_clinit ************************************************************** + + XXX + +*******************************************************************************/ + +bool patcher_clinit(u1 *sp) +{ + classinfo *c; + + /* get stuff from the stack */ + + c = (classinfo *) *((ptrint *) (sp + 1 * 4)); + + /* check if the class is initialized */ + + if (!(c->state & CLASS_INITIALIZED)) + if (!initialize_class(c)) + return false; + + return true; +} + + +/* patcher_athrow_areturn ****************************************************** + + Machine code: + + + +*******************************************************************************/ + +#ifdef ENABLE_VERIFIER +bool patcher_athrow_areturn(u1 *sp) +{ + unresolved_class *uc; + classinfo *c; + + /* get stuff from the stack */ + + uc = (unresolved_class *) *((ptrint *) (sp + 1 * 4)); + + /* check if the class is initialized */ + + if (!resolve_class(uc, resolveEager, false, &c)) + return false; + + return true; +} +#endif /* ENABLE_VERIFIER */ + + +/* patcher_resolve_native ****************************************************** + + XXX + +*******************************************************************************/ + +#if !defined(WITH_STATIC_CLASSPATH) +bool patcher_resolve_native(u1 *sp) +{ + s4 disp; + methodinfo *m; + u1 *pv; + functionptr f; + + /* get stuff from the stack */ + + disp = *((s4 *) (sp + 5 * 4)); + m = (methodinfo *) *((ptrint *) (sp + 1 * 4)); + pv = (u1 *) *((ptrint *) (sp + 0 * 4)); + + /* resolve native function */ + + if (!(f = native_resolve_function(m))) + return false; + + /* patch native function pointer */ + + *((ptrint *) (pv + disp)) = (ptrint) f; + + return true; +} +#endif /* !defined(WITH_STATIC_CLASSPATH) */ + + +/* + * These are local overrides for various environment variables in Emacs. + * Please do not remove this and leave it at the end of the file, where + * Emacs will automagically detect them. + * --------------------------------------------------------------------- + * Local variables: + * mode: c + * indent-tabs-mode: t + * c-basic-offset: 4 + * tab-width: 4 + * End: + * vim:noexpandtab:sw=4:ts=4: + */