* src/vm/jit/x86_64/codegen.h: Use multi-byte NOP for code alignment.
[cacao.git] / src / vm / jit / x86_64 / emit.c
index 46c21d26d0c85a18016cd862d98c433a811b127a..7a4c65a39d20537e2ec1d99604208695a9634156 100644 (file)
@@ -1,9 +1,7 @@
 /* src/vm/jit/x86_64/emit.c - x86_64 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
+   Copyright (C) 1996-2005, 2006, 2007, 2008, 2009
+   CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
 
    This file is part of CACAO.
 
 #include <assert.h>
 
 #include "vm/types.h"
+#include "vm/os.hpp"
 
 #include "md-abi.h"
 
 #include "vm/jit/x86_64/codegen.h"
 #include "vm/jit/x86_64/emit.h"
 
-#include "mm/memory.h"
+#include "mm/memory.hpp"
 
-#include "threads/lock-common.h"
+#include "threads/lock.hpp"
 
-#include "vm/exceptions.h"
+#include "vm/options.h"
 
 #include "vm/jit/abi.h"
 #include "vm/jit/abi-asm.h"
 #include "vm/jit/asmpart.h"
-#include "vm/jit/codegen-common.h"
-#include "vm/jit/emit-common.h"
-#include "vm/jit/jit.h"
-#include "vm/jit/patcher-common.h"
-#include "vm/jit/replace.h"
-#include "vm/jit/trace.h"
-
-#include "vmcore/options.h"
+#include "vm/jit/codegen-common.hpp"
+#include "vm/jit/emit-common.hpp"
+#include "vm/jit/jit.hpp"
+#include "vm/jit/patcher-common.hpp"
+#include "vm/jit/replace.hpp"
+#include "vm/jit/trace.hpp"
+#include "vm/jit/trap.hpp"
 
 
 /* emit_load *******************************************************************
@@ -111,7 +109,7 @@ s4 emit_load(jitdata *jd, instruction *iptr, varinfo *src, s4 tempreg)
     
 *******************************************************************************/
 
-inline void emit_store(jitdata *jd, instruction *iptr, varinfo *dst, s4 d)
+void emit_store(jitdata *jd, instruction *iptr, varinfo *dst, s4 d)
 {
        codegendata  *cd;
        s4            disp;
@@ -235,6 +233,16 @@ void emit_cmovxx(codegendata *cd, instruction *iptr, s4 s, s4 d)
 }
 
 
+/**
+ * Emits code updating the condition register by comparing one integer
+ * register to an immediate integer value.
+ */
+void emit_icmp_imm(codegendata* cd, int reg, int32_t value)
+{
+       M_ICMP_IMM(value, reg);
+}
+
+
 /* emit_branch *****************************************************************
 
    Emits the code for conditional and unconditional branchs.
@@ -311,7 +319,7 @@ void emit_arithmetic_check(codegendata *cd, instruction *iptr, s4 reg)
        if (INSTRUCTION_MUST_CHECK(iptr)) {
                M_TEST(reg);
                M_BNE(8);
-               M_ALD_MEM(reg, EXCEPTION_HARDWARE_ARITHMETIC);
+               M_ALD_MEM(reg, TRAP_ArithmeticException);
        }
 }
 
@@ -328,7 +336,7 @@ void emit_arrayindexoutofbounds_check(codegendata *cd, instruction *iptr, s4 s1,
         M_ILD(REG_ITMP3, s1, OFFSET(java_array_t, size));
         M_ICMP(REG_ITMP3, s2);
                M_BULT(8);
-               M_ALD_MEM(s2, EXCEPTION_HARDWARE_ARRAYINDEXOUTOFBOUNDS);
+               M_ALD_MEM(s2, TRAP_ArrayIndexOutOfBoundsException);
        }
 }
 
@@ -344,7 +352,7 @@ void emit_arraystore_check(codegendata *cd, instruction *iptr)
        if (INSTRUCTION_MUST_CHECK(iptr)) {
                M_TEST(REG_RESULT);
                M_BNE(8);
-               M_ALD_MEM(REG_RESULT, EXCEPTION_HARDWARE_ARRAYSTORE);
+               M_ALD_MEM(REG_RESULT, TRAP_ArrayStoreException);
        }
 }
 
@@ -362,16 +370,22 @@ void emit_classcast_check(codegendata *cd, instruction *iptr, s4 condition, s4 r
                case BRANCH_LE:
                        M_BGT(8);
                        break;
+               case BRANCH_GE:
+                       M_BLT(8);
+                       break;
                case BRANCH_EQ:
                        M_BNE(8);
                        break;
+               case BRANCH_NE:
+                       M_BEQ(8);
+                       break;
                case BRANCH_UGT:
                        M_BULE(8);
                        break;
                default:
                        vm_abort("emit_classcast_check: unknown condition %d", condition);
                }
-               M_ALD_MEM(s1, EXCEPTION_HARDWARE_CLASSCAST);
+               M_ALD_MEM(s1, TRAP_ClassCastException);
        }
 }
 
@@ -387,7 +401,7 @@ void emit_nullpointer_check(codegendata *cd, instruction *iptr, s4 reg)
        if (INSTRUCTION_MUST_CHECK(iptr)) {
                M_TEST(reg);
                M_BNE(8);
-               M_ALD_MEM(reg, EXCEPTION_HARDWARE_NULLPOINTER);
+               M_ALD_MEM(reg, TRAP_NullPointerException);
        }
 }
 
@@ -403,7 +417,7 @@ void emit_exception_check(codegendata *cd, instruction *iptr)
        if (INSTRUCTION_MUST_CHECK(iptr)) {
                M_TEST(REG_RESULT);
                M_BNE(8);
-               M_ALD_MEM(REG_RESULT, EXCEPTION_HARDWARE_EXCEPTION);
+               M_ALD_MEM(REG_RESULT, TRAP_CHECK_EXCEPTION);
        }
 }
 
@@ -416,7 +430,20 @@ void emit_exception_check(codegendata *cd, instruction *iptr)
 
 void emit_trap_compiler(codegendata *cd)
 {
-       M_ALD_MEM(REG_METHODPTR, EXCEPTION_HARDWARE_COMPILER);
+       M_ALD_MEM(REG_METHODPTR, TRAP_COMPILER);
+}
+
+
+/* emit_patcher_alignment ******************************************************
+
+   Emit NOP to ensure placement at an even address.
+
+*******************************************************************************/
+
+void emit_patcher_alignment(codegendata *cd)
+{
+       if ((uintptr_t) cd->mcodeptr & 1)
+               M_NOP;
 }
 
 
@@ -444,6 +471,207 @@ uint32_t emit_trap(codegendata *cd)
 }
 
 
+/**
+ * Generates fast-path code for the below builtin.
+ *   Function:  LOCK_monitor_enter
+ *   Signature: (Ljava/lang/Object;)V
+ *   Slow-path: bool lock_monitor_enter(java_handle_t*);
+ */
+void emit_fastpath_monitor_enter(jitdata* jd, instruction* iptr, int d)
+{
+       // Get required compiler data.
+       codegendata* cd = jd->cd;
+
+       // XXX Currently the fast-path always fails. Implement me!
+       M_CLR(d);
+}
+
+
+/**
+ * Generates fast-path code for the below builtin.
+ *   Function:  LOCK_monitor_exit
+ *   Signature: (Ljava/lang/Object;)V
+ *   Slow-path: bool lock_monitor_exit(java_handle_t*);
+ */
+void emit_fastpath_monitor_exit(jitdata* jd, instruction* iptr, int d)
+{
+       // Get required compiler data.
+       codegendata* cd = jd->cd;
+
+       // XXX Currently the fast-path always fails. Implement me!
+       M_CLR(d);
+}
+
+
+/**
+ * Generates synchronization code to enter a monitor.
+ */
+#if defined(ENABLE_THREADS)
+void emit_monitor_enter(jitdata* jd, int32_t syncslot_offset)
+{
+       int32_t p;
+
+       // Get required compiler data.
+       methodinfo*  m  = jd->m;
+       codegendata* cd = jd->cd;
+
+# if !defined(NDEBUG)
+       if (JITDATA_HAS_FLAG_VERBOSECALL(jd)) {
+               M_LSUB_IMM((INT_ARG_CNT + FLT_ARG_CNT) * 8, REG_SP);
+
+               for (p = 0; p < INT_ARG_CNT; p++)
+                       M_LST(abi_registers_integer_argument[p], REG_SP, p * 8);
+
+               for (p = 0; p < FLT_ARG_CNT; p++)
+                       M_DST(abi_registers_float_argument[p], REG_SP, (INT_ARG_CNT + p) * 8);
+
+               syncslot_offset += (INT_ARG_CNT + FLT_ARG_CNT) * 8;
+       }
+# endif
+
+       /* decide which monitor enter function to call */
+
+       if (m->flags & ACC_STATIC) {
+               M_MOV_IMM(&m->clazz->object.header, REG_A0);
+       }
+       else {
+               M_TEST(REG_A0);
+               M_BNE(8);
+               M_ALD_MEM(REG_A0, TRAP_NullPointerException);
+       }
+
+       M_AST(REG_A0, REG_SP, syncslot_offset);
+       M_MOV_IMM(LOCK_monitor_enter, REG_ITMP1);
+       M_CALL(REG_ITMP1);
+
+# if !defined(NDEBUG)
+       if (JITDATA_HAS_FLAG_VERBOSECALL(jd)) {
+
+               for (p = 0; p < INT_ARG_CNT; p++)
+                       M_LLD(abi_registers_integer_argument[p], REG_SP, p * 8);
+
+               for (p = 0; p < FLT_ARG_CNT; p++)
+                       M_DLD(abi_registers_float_argument[p], REG_SP, (INT_ARG_CNT + p) * 8);
+
+               M_LADD_IMM((INT_ARG_CNT + FLT_ARG_CNT) * 8, REG_SP);
+       }
+# endif
+}
+#endif
+
+
+/**
+ * Generates synchronization code to leave a monitor.
+ */
+#if defined(ENABLE_THREADS)
+void emit_monitor_exit(jitdata* jd, int32_t syncslot_offset)
+{
+       // Get required compiler data.
+       methodinfo*  m  = jd->m;
+       codegendata* cd = jd->cd;
+
+       M_ALD(REG_A0, REG_SP, syncslot_offset);
+
+       /* we need to save the proper return value */
+
+       methoddesc* md = m->parseddesc;
+
+       switch (md->returntype.type) {
+       case TYPE_INT:
+       case TYPE_ADR:
+       case TYPE_LNG:
+               M_LST(REG_RESULT, REG_SP, syncslot_offset);
+               break;
+       case TYPE_FLT:
+       case TYPE_DBL:
+               M_DST(REG_FRESULT, REG_SP, syncslot_offset);
+               break;
+       }
+
+       M_MOV_IMM(LOCK_monitor_exit, REG_ITMP1);
+       M_CALL(REG_ITMP1);
+
+       /* and now restore the proper return value */
+
+       switch (md->returntype.type) {
+       case TYPE_INT:
+       case TYPE_ADR:
+       case TYPE_LNG:
+               M_LLD(REG_RESULT, REG_SP, syncslot_offset);
+               break;
+       case TYPE_FLT:
+       case TYPE_DBL:
+               M_DLD(REG_FRESULT, REG_SP, syncslot_offset);
+               break;
+       }
+}
+#endif
+
+
+/**
+ * Emit profiling code for method frequency counting.
+ */
+#if defined(ENABLE_PROFILING)
+void emit_profile_method(codegendata* cd, codeinfo* code)
+{
+       M_MOV_IMM(code, REG_ITMP3);
+       M_IINC_MEMBASE(REG_ITMP3, OFFSET(codeinfo, frequency));
+}
+#endif
+
+
+/**
+ * Emit profiling code for basicblock frequency counting.
+ */
+#if defined(ENABLE_PROFILING)
+void emit_profile_basicblock(codegendata* cd, codeinfo* code, basicblock* bptr)
+{
+       M_MOV_IMM(code->bbfrequency, REG_ITMP3);
+       M_IINC_MEMBASE(REG_ITMP3, bptr->nr * 4);
+}
+#endif
+
+
+/**
+ * Emit profiling code to start CPU cycle counting.
+ */
+#if defined(ENABLE_PROFILING)
+void emit_profile_cycle_start(codegendata* cd, codeinfo* code)
+{
+       M_PUSH(RAX);
+       M_PUSH(RDX);
+
+       M_MOV_IMM(code, REG_ITMP3);
+       M_RDTSC;
+       M_ISUB_MEMBASE(RAX, REG_ITMP3, OFFSET(codeinfo, cycles));
+       M_ISBB_MEMBASE(RDX, REG_ITMP3, OFFSET(codeinfo, cycles) + 4);
+
+       M_POP(RDX);
+       M_POP(RAX);
+}
+#endif
+
+
+/**
+ * Emit profiling code to stop CPU cycle counting.
+ */
+#if defined(ENABLE_PROFILING)
+void emit_profile_cycle_stop(codegendata* cd, codeinfo* code)
+{
+       M_PUSH(RAX);
+       M_PUSH(RDX);
+
+       M_MOV_IMM(code, REG_ITMP3);
+       M_RDTSC;
+       M_IADD_MEMBASE(RAX, REG_ITMP3, OFFSET(codeinfo, cycles));
+       M_IADC_MEMBASE(RDX, REG_ITMP3, OFFSET(codeinfo, cycles) + 4);
+
+       M_POP(RDX);
+       M_POP(RAX);
+}
+#endif
+
+
 /* emit_verbosecall_enter ******************************************************
 
    Generates the code for the call trace.
@@ -977,6 +1205,76 @@ void emit_lshift(jitdata *jd, s4 shift_op, instruction *iptr)
 
 /* low-level code emitter functions *******************************************/
 
+void emit_nop(codegendata *cd, int length)
+{
+    assert(length >= 1 && length <= 9);
+    switch (length) {
+    case 1:
+        *(cd->mcodeptr++) = 0x90;
+        break;
+    case 2:
+        *(cd->mcodeptr++) = 0x66;
+        *(cd->mcodeptr++) = 0x90;
+        break;
+    case 3:
+        *(cd->mcodeptr++) = 0x0f;
+        *(cd->mcodeptr++) = 0x1f;
+        *(cd->mcodeptr++) = 0x00;
+        break;
+    case 4:
+        *(cd->mcodeptr++) = 0x0f;
+        *(cd->mcodeptr++) = 0x1f;
+        *(cd->mcodeptr++) = 0x40;
+        *(cd->mcodeptr++) = 0x00;
+        break;
+    case 5:
+        *(cd->mcodeptr++) = 0x0f;
+        *(cd->mcodeptr++) = 0x1f;
+        *(cd->mcodeptr++) = 0x44;
+        *(cd->mcodeptr++) = 0x00;
+        *(cd->mcodeptr++) = 0x00;
+        break;
+    case 6:
+        *(cd->mcodeptr++) = 0x66;
+        *(cd->mcodeptr++) = 0x0f;
+        *(cd->mcodeptr++) = 0x1f;
+        *(cd->mcodeptr++) = 0x44;
+        *(cd->mcodeptr++) = 0x00;
+        *(cd->mcodeptr++) = 0x00;
+        break;
+    case 7:
+        *(cd->mcodeptr++) = 0x0f;
+        *(cd->mcodeptr++) = 0x1f;
+        *(cd->mcodeptr++) = 0x80;
+        *(cd->mcodeptr++) = 0x00;
+        *(cd->mcodeptr++) = 0x00;
+        *(cd->mcodeptr++) = 0x00;
+        *(cd->mcodeptr++) = 0x00;
+        break;
+    case 8:
+        *(cd->mcodeptr++) = 0x0f;
+        *(cd->mcodeptr++) = 0x1f;
+        *(cd->mcodeptr++) = 0x84;
+        *(cd->mcodeptr++) = 0x00;
+        *(cd->mcodeptr++) = 0x00;
+        *(cd->mcodeptr++) = 0x00;
+        *(cd->mcodeptr++) = 0x00;
+        *(cd->mcodeptr++) = 0x00;
+        break;
+    case 9:
+        *(cd->mcodeptr++) = 0x66;
+        *(cd->mcodeptr++) = 0x0f;
+        *(cd->mcodeptr++) = 0x1f;
+        *(cd->mcodeptr++) = 0x84;
+        *(cd->mcodeptr++) = 0x00;
+        *(cd->mcodeptr++) = 0x00;
+        *(cd->mcodeptr++) = 0x00;
+        *(cd->mcodeptr++) = 0x00;
+        *(cd->mcodeptr++) = 0x00;
+        break;
+    }
+}
+
 void emit_mov_reg_reg(codegendata *cd, s8 reg, s8 dreg)
 {
        emit_rex(1,(reg),0,(dreg));
@@ -1180,6 +1478,16 @@ void emit_movslq_reg_reg(codegendata *cd, s8 reg, s8 dreg)
 }
 
 
+void emit_movzbq_reg_reg(codegendata *cd, s8 reg, s8 dreg)
+{
+       emit_rex(1,(dreg),0,(reg));
+       *(cd->mcodeptr++) = 0x0f;
+       *(cd->mcodeptr++) = 0xb6;
+       /* XXX: why do reg and dreg have to be exchanged */
+       emit_reg((dreg),(reg));
+}
+
+
 void emit_movzwq_reg_reg(codegendata *cd, s8 reg, s8 dreg)
 {
        emit_rex(1,(dreg),0,(reg));
@@ -1388,6 +1696,19 @@ void emit_alul_imm_membase(codegendata *cd, s8 opc, s8 imm, s8 basereg, s8 disp)
        }
 }
 
+void emit_alu_memindex_reg(codegendata *cd, s8 opc, s8 disp, s8 basereg, s8 indexreg, s8 scale, s8 reg)
+{
+       emit_rex(1,(reg),(indexreg),(basereg));
+       *(cd->mcodeptr++) = (((opc)) << 3) + 3;
+       emit_memindex(cd, (reg),(disp),(basereg),(indexreg),(scale));
+}
+
+void emit_alul_memindex_reg(codegendata *cd, s8 opc, s8 disp, s8 basereg, s8 indexreg, s8 scale, s8 reg)
+{
+       emit_rex(0,(reg),(indexreg),(basereg));
+       *(cd->mcodeptr++) = (((opc)) << 3) + 3;
+       emit_memindex(cd, (reg),(disp),(basereg),(indexreg),(scale));
+}
 
 void emit_test_reg_reg(codegendata *cd, s8 reg, s8 dreg) {
        emit_rex(1,(reg),0,(dreg));
@@ -1439,6 +1760,18 @@ void emit_leal_membase_reg(codegendata *cd, s8 basereg, s8 disp, s8 reg) {
 }
 
 
+void emit_incl_reg(codegendata *cd, s8 reg)
+{
+       *(cd->mcodeptr++) = 0xff;
+       emit_reg(0,(reg));
+}
+
+void emit_incq_reg(codegendata *cd, s8 reg)
+{
+       emit_rex(1,0,0,(reg));
+       *(cd->mcodeptr++) = 0xff;
+       emit_reg(0,(reg));
+}
 
 void emit_incl_membase(codegendata *cd, s8 basereg, s8 disp)
 {
@@ -1447,6 +1780,13 @@ void emit_incl_membase(codegendata *cd, s8 basereg, s8 disp)
        emit_membase(cd, (basereg),(disp),0);
 }
 
+void emit_incq_membase(codegendata *cd, s8 basereg, s8 disp)
+{
+       emit_rex(1,0,0,(basereg));
+       *(cd->mcodeptr++) = 0xff;
+       emit_membase(cd, (basereg),(disp),0);
+}
+
 
 
 void emit_cltd(codegendata *cd) {
@@ -1679,6 +2019,18 @@ void emit_jmp_imm(codegendata *cd, s8 imm) {
        emit_imm32((imm));
 }
 
+/* like emit_jmp_imm but allows 8 bit optimization */
+void emit_jmp_imm2(codegendata *cd, s8 imm) {
+       if (IS_IMM8(imm)) {
+               *(cd->mcodeptr++) = 0xeb;
+               emit_imm8((imm));
+       }
+       else {
+               *(cd->mcodeptr++) = 0xe9;
+               emit_imm32((imm));
+       }
+}
+
 
 void emit_jmp_reg(codegendata *cd, s8 reg) {
        emit_rex(0,0,0,(reg));
@@ -2291,6 +2643,13 @@ void emit_rdtsc(codegendata *cd)
        *(cd->mcodeptr++) = 0x31;
 }
 
+void emit_mfence(codegendata *cd)
+{
+       *(cd->mcodeptr++) = 0x0f;
+       *(cd->mcodeptr++) = 0xae;
+       *(cd->mcodeptr++) = 0xf0;
+}
+
 
 /*
  * These are local overrides for various environment variables in Emacs.
@@ -2303,4 +2662,5 @@ void emit_rdtsc(codegendata *cd)
  * c-basic-offset: 4
  * tab-width: 4
  * End:
+ * vim:noexpandtab:sw=4:ts=4:
  */