* Merged in twisti-branch.
[cacao.git] / src / vm / jit / powerpc / emit.c
index 0d28b795ae37de4518d01625430e8181501cd9e8..1b77a9f0c67591444869a5e9546ba3529b8e3652 100644 (file)
@@ -1,6 +1,6 @@
 /* src/vm/jit/powerpc/emit.c - PowerPC code emitter functions
 
-   Copyright (C) 1996-2005, 2006 R. Grafl, A. Krall, C. Kruegel,
+   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
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
 
-   Contact: cacao@cacaojvm.org
-
-   Authors: Christian Thalinger
-
-   Changes:
-
-   $Id: emitfuncs.c 4398 2006-01-31 23:43:08Z twisti $
+   $Id: emit.c 4398 2006-01-31 23:43:08Z twisti $
 
 */
 
 
 #include "vm/jit/powerpc/codegen.h"
 
+#include "mm/memory.h"
+
+#if defined(ENABLE_THREADS)
+# include "threads/native/lock.h"
+#endif
+
 #include "vm/builtin.h"
-#include "vm/options.h"
+#include "vm/exceptions.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/replace.h"
 
+#include "vmcore/options.h"
+
 
 /* emit_load *******************************************************************
 
@@ -73,17 +77,22 @@ s4 emit_load(jitdata *jd, instruction *iptr, varinfo *src, s4 tempreg)
 
                disp = src->vv.regoff * 4;
 
-               if (IS_FLT_DBL_TYPE(src->type)) {
-                       if (IS_2_WORD_TYPE(src->type))
-                               M_DLD(tempreg, REG_SP, disp);
-                       else
-                               M_FLD(tempreg, REG_SP, disp);
-               }
-               else {
-                       if (IS_2_WORD_TYPE(src->type))
-                               M_LLD(tempreg, REG_SP, disp);
-                       else
-                               M_ILD(tempreg, REG_SP, disp);
+               switch (src->type) {
+               case TYPE_INT:
+               case TYPE_ADR:
+                       M_ILD(tempreg, REG_SP, disp);
+                       break;
+               case TYPE_LNG:
+                       M_LLD(tempreg, REG_SP, disp);
+                       break;
+               case TYPE_FLT:
+                       M_FLD(tempreg, REG_SP, disp);
+                       break;
+               case TYPE_DBL:
+                       M_DLD(tempreg, REG_SP, disp);
+                       break;
+               default:
+                       vm_abort("emit_load: unknown type %d", src->type);
                }
 
                reg = tempreg;
@@ -165,13 +174,14 @@ s4 emit_load_high(jitdata *jd, instruction *iptr, varinfo *src, s4 tempreg)
 
 /* emit_store ******************************************************************
 
-   XXX
+   Emit a possible store for the given variable.
 
 *******************************************************************************/
 
 void emit_store(jitdata *jd, instruction *iptr, varinfo *dst, s4 d)
 {
        codegendata *cd;
+       s4           disp;
 
        /* get required compiler data */
 
@@ -180,17 +190,24 @@ void emit_store(jitdata *jd, instruction *iptr, varinfo *dst, s4 d)
        if (IS_INMEMORY(dst->flags)) {
                COUNT_SPILLS;
 
-               if (IS_FLT_DBL_TYPE(dst->type)) {
-                       if (IS_2_WORD_TYPE(dst->type))
-                               M_DST(d, REG_SP, dst->vv.regoff * 4);
-                       else
-                               M_FST(d, REG_SP, dst->vv.regoff * 4);
-               }
-               else {
-                       if (IS_2_WORD_TYPE(dst->type))
-                               M_LST(d, REG_SP, dst->vv.regoff * 4);
-                       else
-                               M_IST(d, REG_SP, dst->vv.regoff * 4);
+               disp = dst->vv.regoff * 4;
+
+               switch (dst->type) {
+               case TYPE_INT:
+               case TYPE_ADR:
+                       M_IST(d, REG_SP, disp);
+                       break;
+               case TYPE_LNG:
+                       M_LST(d, REG_SP, disp);
+                       break;
+               case TYPE_FLT:
+                       M_FST(d, REG_SP, disp);
+                       break;
+               case TYPE_DBL:
+                       M_DST(d, REG_SP, disp);
+                       break;
+               default:
+                       vm_abort("emit_store: unknown type %d", dst->type);
                }
        }
 }
@@ -236,15 +253,21 @@ void emit_copy(jitdata *jd, instruction *iptr, varinfo *src, varinfo *dst)
                }
 
                if (s1 != d) {
-                       if (IS_FLT_DBL_TYPE(src->type))
+                       switch (src->type) {
+                       case TYPE_INT:
+                       case TYPE_ADR:
+                               M_MOV(s1, d);
+                               break;
+                       case TYPE_LNG:
+                               M_MOV(GET_LOW_REG(s1), GET_LOW_REG(d));
+                               M_MOV(GET_HIGH_REG(s1), GET_HIGH_REG(d));
+                               break;
+                       case TYPE_FLT:
+                       case TYPE_DBL:
                                M_FMOV(s1, d);
-                       else {
-                               if (IS_2_WORD_TYPE(src->type)) {
-                                       M_MOV(GET_LOW_REG(s1), GET_LOW_REG(d));
-                                       M_MOV(GET_HIGH_REG(s1), GET_HIGH_REG(d));
-                }
-                               else
-                    M_MOV(s1, d);
+                               break;
+                       default:
+                               vm_abort("emit_copy: unknown type %d", dst->type);
                        }
                }
 
@@ -272,137 +295,201 @@ void emit_iconst(codegendata *cd, s4 d, s4 value)
 }
 
 
-/* emit_nullpointer_check ******************************************************
+/* emit_branch *****************************************************************
 
-   Emit a NullPointerException check.
+   Emits the code for conditional and unconditional branchs.
 
 *******************************************************************************/
 
-void emit_nullpointer_check(codegendata *cd, s4 reg)
+void emit_branch(codegendata *cd, s4 disp, s4 condition, s4 reg, u4 opt)
 {
-       if (checknull) {
-               M_TST(reg);
-               M_BEQ(0);
-               codegen_add_nullpointerexception_ref(cd);
-       }
-}
+       s4 checkdisp;
+       s4 branchdisp;
 
+       /* calculate the different displacements */
 
-/* emit_arrayindexoutofbounds_check ********************************************
+       checkdisp  =  disp + 4;
+       branchdisp = (disp - 4) >> 2;
 
-   Emit a ArrayIndexOutOfBoundsException check.
+       /* check which branch to generate */
 
-*******************************************************************************/
+       if (condition == BRANCH_UNCONDITIONAL) {
+               /* check displacement for overflow */
 
-void emit_arrayindexoutofbounds_check(codegendata *cd, s4 s1, s4 s2)
-{
-       if (checkbounds) {
-               M_ILD(REG_ITMP3, s1, OFFSET(java_arrayheader, size));
-               M_CMPU(s2, REG_ITMP3);
-               M_BGE(0);
-               codegen_add_arrayindexoutofboundsexception_ref(cd, s2);
-       }
-}
+               if ((checkdisp < (s4) 0xfe000000) || (checkdisp > (s4) 0x01fffffc)) {
+                       /* if the long-branches flag isn't set yet, do it */
 
+                       if (!CODEGENDATA_HAS_FLAG_LONGBRANCHES(cd)) {
+                               cd->flags |= (CODEGENDATA_FLAG_ERROR |
+                                                         CODEGENDATA_FLAG_LONGBRANCHES);
+                       }
 
-/* emit_exception_stubs ********************************************************
+                       vm_abort("emit_branch: emit unconditional long-branch code");
+               }
+               else {
+                       M_BR(branchdisp);
+               }
+       }
+       else {
+               /* and displacement for overflow */
 
-   Generates the code for the exception stubs.
+               if ((checkdisp < (s4) 0xffff8000) || (checkdisp > (s4) 0x00007fff)) {
+                       /* if the long-branches flag isn't set yet, do it */
 
-*******************************************************************************/
+                       if (!CODEGENDATA_HAS_FLAG_LONGBRANCHES(cd)) {
+                               cd->flags |= (CODEGENDATA_FLAG_ERROR |
+                                                         CODEGENDATA_FLAG_LONGBRANCHES);
+                       }
 
-void emit_exception_stubs(jitdata *jd)
-{
-       codegendata  *cd;
-       registerdata *rd;
-       exceptionref *er;
-       s4            branchmpc;
-       s4            targetmpc;
-       s4            targetdisp;
-       s4            disp;
+                       switch (condition) {
+                       case BRANCH_EQ:
+                               M_BNE(1);
+                               M_BR(branchdisp);
+                               break;
+                       case BRANCH_NE:
+                               M_BEQ(1);
+                               M_BR(branchdisp);
+                               break;
+                       case BRANCH_LT:
+                               M_BGE(1);
+                               M_BR(branchdisp);
+                               break;
+                       case BRANCH_GE:
+                               M_BLT(1);
+                               M_BR(branchdisp);
+                               break;
+                       case BRANCH_GT:
+                               M_BLE(1);
+                               M_BR(branchdisp);
+                               break;
+                       case BRANCH_LE:
+                               M_BGT(1);
+                               M_BR(branchdisp);
+                               break;
+                       case BRANCH_NAN:
+                               vm_abort("emit_branch: long BRANCH_NAN");
+                               break;
+                       default:
+                               vm_abort("emit_branch: unknown condition %d", condition);
+                       }
+               }
+               else {
+                       switch (condition) {
+                       case BRANCH_EQ:
+                               M_BEQ(branchdisp);
+                               break;
+                       case BRANCH_NE:
+                               M_BNE(branchdisp);
+                               break;
+                       case BRANCH_LT:
+                               M_BLT(branchdisp);
+                               break;
+                       case BRANCH_GE:
+                               M_BGE(branchdisp);
+                               break;
+                       case BRANCH_GT:
+                               M_BGT(branchdisp);
+                               break;
+                       case BRANCH_LE:
+                               M_BLE(branchdisp);
+                               break;
+                       case BRANCH_NAN:
+                               M_BNAN(branchdisp);
+                               break;
+                       default:
+                               vm_abort("emit_branch: unknown condition %d", condition);
+                       }
+               }
+       }
+}
 
-       /* get required compiler data */
 
-       cd = jd->cd;
-       rd = jd->rd;
+/* emit_arithmetic_check *******************************************************
 
-       /* generate exception stubs */
+   Emit an ArithmeticException check.
 
-       targetdisp = 0;
+*******************************************************************************/
 
-       for (er = cd->exceptionrefs; er != NULL; er = er->next) {
-               /* back-patch the branch to this exception code */
+void emit_arithmetic_check(codegendata *cd, instruction *iptr, s4 reg)
+{
+       if (INSTRUCTION_MUST_CHECK(iptr)) {
+               M_TST(reg);
+               M_BNE(1);
+               M_ALD_INTERN(REG_ZERO, REG_ZERO, EXCEPTION_HARDWARE_ARITHMETIC);
+       }
+}
 
-               branchmpc = er->branchpos;
-               targetmpc = cd->mcodeptr - cd->mcodebase;
 
-               md_codegen_patch_branch(cd, branchmpc, targetmpc);
+/* emit_arrayindexoutofbounds_check ********************************************
 
-               MCODECHECK(100);
+   Emit a ArrayIndexOutOfBoundsException check.
 
-               /* Move the value register to a temporary register, if
-                  there is the need for it. */
+*******************************************************************************/
 
-               if (er->reg != -1)
-                       M_MOV(er->reg, REG_ITMP1);
+void emit_arrayindexoutofbounds_check(codegendata *cd, instruction *iptr, s4 s1, s4 s2)
+{
+       if (INSTRUCTION_MUST_CHECK(iptr)) {
+               M_ILD(REG_ITMP3, s1, OFFSET(java_arrayheader, size));
+               M_TRAPGEU(s2, REG_ITMP3);
+       }
+}
 
-               /* calcuate exception address */
 
-               M_LDA(REG_ITMP2_XPC, REG_PV, er->branchpos - 4);
+/* emit_classcast_check ********************************************************
 
-               /* move function to call into REG_ITMP3 */
+   Emit a ClassCastException check.
 
-               disp = dseg_add_functionptr(cd, er->function);
-               M_ALD(REG_ITMP3, REG_PV, disp);
+*******************************************************************************/
 
-               if (targetdisp == 0) {
-                   targetdisp = ((u4 *) cd->mcodeptr) - ((u4 *) cd->mcodebase);
+void emit_classcast_check(codegendata *cd, instruction *iptr, s4 condition, s4 reg, s4 s1)
+{
+       if (INSTRUCTION_MUST_CHECK(iptr)) {
+               switch (condition) {
+               case BRANCH_LE:
+                       M_BGT(1);
+                       break;
+               case BRANCH_EQ:
+                       M_BNE(1);
+                       break;
+               case BRANCH_GT:
+                       M_BLE(1);
+                       break;
+               default:
+                       vm_abort("emit_classcast_check: unknown condition %d", condition);
+               }
+               M_ALD_INTERN(s1, REG_ZERO, EXCEPTION_HARDWARE_CLASSCAST);
+       }
+}
 
-                       if (jd->isleafmethod) {
-                               M_MFLR(REG_ZERO);
-                               M_AST(REG_ZERO, REG_SP, cd->stackframesize * 4 + LA_LR_OFFSET);
-                       }
 
-                       M_MOV(REG_PV, rd->argintregs[0]);
-                       M_MOV(REG_SP, rd->argintregs[1]);
+/* emit_nullpointer_check ******************************************************
 
-                       if (jd->isleafmethod)
-                               M_MOV(REG_ZERO, rd->argintregs[2]);
-                       else
-                               M_ALD(rd->argintregs[2],
-                                         REG_SP, cd->stackframesize * 4 + LA_LR_OFFSET);
+   Emit a NullPointerException check.
 
-                       M_MOV(REG_ITMP2_XPC, rd->argintregs[3]);
-                       M_MOV(REG_ITMP1, rd->argintregs[4]);
+*******************************************************************************/
 
-                       M_STWU(REG_SP, REG_SP, -(LA_SIZE + 6 * 4));
-                       M_AST(REG_ITMP2_XPC, REG_SP, LA_SIZE + 5 * 4);
+void emit_nullpointer_check(codegendata *cd, instruction *iptr, s4 reg)
+{
+       if (INSTRUCTION_MUST_CHECK(iptr)) {
+               M_TST(reg);
+               M_BNE(1);
+               M_ALD_INTERN(REG_ZERO, REG_ZERO, EXCEPTION_HARDWARE_NULLPOINTER);
+       }
+}
 
-                       M_MTCTR(REG_ITMP3);
-                       M_JSR;
-                       M_MOV(REG_RESULT, REG_ITMP1_XPTR);
 
-                       M_ALD(REG_ITMP2_XPC, REG_SP, LA_SIZE + 5 * 4);
-                       M_IADD_IMM(REG_SP, LA_SIZE + 6 * 4, REG_SP);
+/* emit_exception_check ********************************************************
 
-                       if (jd->isleafmethod) {
-                               /* XXX FIXME: REG_ZERO can cause problems here! */
-                               assert(cd->stackframesize * 4 <= 32767);
+   Emit an Exception check.
 
-                               M_ALD(REG_ZERO, REG_SP, cd->stackframesize * 4 + LA_LR_OFFSET);
-                               M_MTLR(REG_ZERO);
-                       }
+*******************************************************************************/
 
-                       disp = dseg_add_functionptr(cd, asm_handle_exception);
-                       M_ALD(REG_ITMP3, REG_PV, disp);
-                       M_MTCTR(REG_ITMP3);
-                       M_RTS;
-               }
-               else {
-                       disp = (((u4 *) cd->mcodebase) + targetdisp) -
-                               (((u4 *) cd->mcodeptr) + 1);
-                       M_BR(disp);
-               }
+void emit_exception_check(codegendata *cd, instruction *iptr)
+{
+       if (INSTRUCTION_MUST_CHECK(iptr)) {
+               M_TST(REG_RESULT);
+               M_BNE(1);
+               M_ALD_INTERN(REG_ZERO, REG_ZERO, EXCEPTION_HARDWARE_EXCEPTION);
        }
 }
 
@@ -525,14 +612,17 @@ void emit_patcher_stubs(jitdata *jd)
 
 *******************************************************************************/
 
+#if defined(ENABLE_REPLACEMENT)
 void emit_replacement_stubs(jitdata *jd)
 {
        codegendata *cd;
        codeinfo    *code;
        rplpoint    *rplp;
-       u1          *savedmcodeptr;
        s4           disp;
        s4           i;
+#if !defined(NDEBUG)
+       u1          *savedmcodeptr;
+#endif
 
        /* get required compiler data */
 
@@ -541,24 +631,23 @@ void emit_replacement_stubs(jitdata *jd)
 
        rplp = code->rplpoints;
 
-       for (i = 0; i < code->rplpointcount; ++i, ++rplp) {
-               /* check code segment size */
-
-               MCODECHECK(100);
+       /* store beginning of replacement stubs */
 
-               /* note start of stub code */
+       code->replacementstubs = (u1*) (cd->mcodeptr - cd->mcodebase);
 
-               rplp->outcode = (u1 *) (cd->mcodeptr - cd->mcodebase);
+       for (i = 0; i < code->rplpointcount; ++i, ++rplp) {
+               /* do not generate stubs for non-trappable points */
 
-               /* make machine code for patching */
+               if (rplp->flags & RPLPOINT_FLAG_NOTRAP)
+                       continue;
 
-               savedmcodeptr = cd->mcodeptr;
-               cd->mcodeptr  = (u1 *) &(rplp->mcode) + 1;              /* big-endian */
+               /* check code segment size */
 
-               disp = (ptrint) ((s4 *) rplp->outcode - (s4 *) rplp->pc) - 1;
-               M_BR(disp);
+               MCODECHECK(100);
 
-               cd->mcodeptr = savedmcodeptr;
+#if !defined(NDEBUG)
+               savedmcodeptr = cd->mcodeptr;
+#endif
 
                /* create stack frame - keep 16-byte aligned */
 
@@ -576,8 +665,11 @@ void emit_replacement_stubs(jitdata *jd)
                M_ALD(REG_ITMP3, REG_PV, disp);
                M_MTCTR(REG_ITMP3);
                M_RTS;
+
+               assert((cd->mcodeptr - savedmcodeptr) == 4*REPLACEMENT_STUB_SIZE);
        }
 }
+#endif /* defined(ENABLE_REPLACEMENT) */
 
 
 /* emit_verbosecall_enter ******************************************************
@@ -586,9 +678,9 @@ void emit_replacement_stubs(jitdata *jd)
 
 *******************************************************************************/
 
-#if !defined(NDEBUG)
 void emit_verbosecall_enter(jitdata *jd)
 {
+#if !defined(NDEBUG)
        methodinfo   *m;
        codegendata  *cd;
        registerdata *rd;
@@ -597,6 +689,9 @@ void emit_verbosecall_enter(jitdata *jd)
        int stack_size;
        methoddesc *md;
 
+       if (!JITDATA_HAS_FLAG_VERBOSECALL(jd))
+               return;
+
        /* get required compiler data */
 
        m  = jd->m;
@@ -748,7 +843,7 @@ void emit_verbosecall_enter(jitdata *jd)
 #else
        M_AST(REG_ITMP1, REG_SP, LA_SIZE + 4 * 8);
 #endif
-       p = dseg_add_functionptr(cd, builtin_trace_args);
+       p = dseg_add_functionptr(cd, builtin_verbosecall_enter);
        M_ALD(REG_ITMP2, REG_PV, p);
        M_MTCTR(REG_ITMP2);
        M_JSR;
@@ -791,25 +886,30 @@ void emit_verbosecall_enter(jitdata *jd)
        /* mark trace code */
 
        M_NOP;
-}
 #endif /* !defined(NDEBUG) */
+}
 
 
 /* emit_verbosecall_exit *******************************************************
 
    Generates the code for the call trace.
 
+   void builtin_verbosecall_exit(s8 l, double d, float f, methodinfo *m);
+
 *******************************************************************************/
 
-#if !defined(NDEBUG)
 void emit_verbosecall_exit(jitdata *jd)
 {
+#if !defined(NDEBUG)
        methodinfo   *m;
        codegendata  *cd;
        registerdata *rd;
        methoddesc   *md;
        s4            disp;
 
+       if (!JITDATA_HAS_FLAG_VERBOSECALL(jd))
+               return;
+
        /* get required compiler data */
 
        m  = jd->m;
@@ -835,33 +935,22 @@ void emit_verbosecall_exit(jitdata *jd)
        switch (md->returntype.type) {
        case TYPE_INT:
        case TYPE_ADR:
-#if defined(__DARWIN__)
-               M_MOV(REG_RESULT, rd->argintregs[2]);
-               M_CLR(rd->argintregs[1]);
-#else
-               M_MOV(REG_RESULT, rd->argintregs[3]);
-               M_CLR(rd->argintregs[2]);
-#endif
+               M_INTMOVE(REG_RESULT, REG_A1);
+               M_CLR(REG_A0);
                break;
 
        case TYPE_LNG:
-#if defined(__DARWIN__)
-               M_MOV(REG_RESULT2, rd->argintregs[2]);
-               M_MOV(REG_RESULT, rd->argintregs[1]);
-#else
-               M_MOV(REG_RESULT2, rd->argintregs[3]);
-               M_MOV(REG_RESULT, rd->argintregs[2]);
-#endif
+               M_LNGMOVE(REG_RESULT_PACKED, REG_A0_A1_PACKED);
                break;
        }
 
-       M_FLTMOVE(REG_FRESULT, rd->argfltregs[0]);
-       M_FLTMOVE(REG_FRESULT, rd->argfltregs[1]);
+       M_FLTMOVE(REG_FRESULT, REG_FA0);
+       M_FLTMOVE(REG_FRESULT, REG_FA1);
 
        disp = dseg_add_address(cd, m);
-       M_ALD(rd->argintregs[0], REG_PV, disp);
+       M_ALD(REG_A2, REG_PV, disp);
 
-       disp = dseg_add_functionptr(cd, builtin_displaymethodstop);
+       disp = dseg_add_functionptr(cd, builtin_verbosecall_exit);
        M_ALD(REG_ITMP2, REG_PV, disp);
        M_MTCTR(REG_ITMP2);
        M_JSR;
@@ -878,8 +967,8 @@ void emit_verbosecall_exit(jitdata *jd)
        /* mark trace code */
 
        M_NOP;
-}
 #endif /* !defined(NDEBUG) */
+}
 
 
 /*