Removed return value from descriptor_params_from_paramtypes.
[cacao.git] / src / vm / jit / optimizing / bytecode_escape.c
index 4deae2c46a10664f969547c3bfbf182f67979ed2..0ee6d19b5b8a6eff77961ec879e48b94aeb65c33 100644 (file)
@@ -1,16 +1,45 @@
-#include "mm/dumpmemory.h"
-#include "mm/memory.h"
+/* src/vm/optimizing/bytecode_escape.c
+
+   Copyright (C) 1996-2011
+   CACAOVM - Verein zu Foerderung der freien virtuellen Machine CACAO
+
+   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.
+
+*/
+
+
+#include "config.h"
+
+#include <stdint.h>
+
+#include "mm/dumpmemory.hpp"
+#include "mm/memory.hpp"
 
 #include "toolbox/bitvector.h"
 
+#include "vm/class.hpp"
+#include "vm/descriptor.hpp"
 #include "vm/global.h"
+#include "vm/references.h"
+#include "vm/resolve.hpp"
+
 #include "vm/jit/ir/bytecode.h"
 #include "vm/jit/optimizing/escape.h"
-#include "vm/resolve.h"
-
-#include "vmcore/class.h"
-#include "vmcore/descriptor.h"
-#include "vmcore/references.h"
 
 #include <assert.h>
 #include <stdarg.h>
@@ -95,15 +124,10 @@ typedef struct {
        op_stack_slot_t *ptr;
        op_stack_slot_t *bottom;
        unsigned max;
+       bool *perror_flag;
 } op_stack_t;
 
-#define stack_assert_position(stack, pos) \
-       do { \
-               assert((stack)->elements <= (pos)); \
-               assert((pos) < (stack)->end); \
-       } while (0)
-
-static void op_stack_init(op_stack_t *stack, unsigned max) {
+static void op_stack_init(op_stack_t *stack, unsigned max, bool *perror_flag) {
        op_stack_slot_t *it;
 
        stack->elements = DMNEW(op_stack_slot_t, max * 2);
@@ -117,6 +141,27 @@ static void op_stack_init(op_stack_t *stack, unsigned max) {
 
        stack->ptr = stack->start;
        stack->bottom = stack->start;
+
+       stack->perror_flag = perror_flag;
+}
+
+static void op_stack_set_error(op_stack_t *stack) {
+       *(stack->perror_flag) = true;
+#if BC_ESCAPE_VERBOSE
+       printf("%s: error.\n", __FUNCTION__);
+#endif
+}
+
+static bool op_stack_test_position(op_stack_t *stack, op_stack_slot_t *pos) {
+       if (!(stack->elements <= pos)) {
+               op_stack_set_error(stack);
+               return false;
+       } else if (!(pos < stack->end)) {
+               op_stack_set_error(stack);
+               return false;
+       } else {
+               return true;
+       }
 }
 
 static void op_stack_reset(op_stack_t *stack) {
@@ -137,7 +182,9 @@ static void op_stack_reset(op_stack_t *stack) {
 static op_stack_slot_t op_stack_pop(op_stack_t *stack) {
        op_stack_slot_t ret;
        stack->ptr -= 1;
-       stack_assert_position(stack, stack->ptr);
+       if (! op_stack_test_position(stack, stack->ptr)) {
+               return OP_STACK_SLOT_UNKNOWN;
+       }
        ret = *(stack->ptr);
        if (stack->ptr < stack->bottom) {
                stack->bottom = stack->ptr;
@@ -146,19 +193,24 @@ static op_stack_slot_t op_stack_pop(op_stack_t *stack) {
 }
 
 static void op_stack_push(op_stack_t *stack, op_stack_slot_t element) {
-       stack_assert_position(stack, stack->ptr);
-       *(stack->ptr) = element;
-       stack->ptr += 1;
+       if (op_stack_test_position(stack, stack->ptr)) {
+               *(stack->ptr) = element;
+               stack->ptr += 1;
+       }
 }
 
 static op_stack_slot_t op_stack_get(const op_stack_t *stack, int offset) {
-       stack_assert_position(stack, stack->ptr - offset);
-       return *(stack->ptr - offset);
+       if (op_stack_test_position(stack, stack->ptr - offset)) {
+               return *(stack->ptr - offset);
+       } else {
+               return OP_STACK_SLOT_UNKNOWN;
+       }
 }
 
 static void op_stack_set(op_stack_t *stack, int offset, op_stack_slot_t value) {
-       stack_assert_position(stack, stack->ptr - offset);
-       *(stack->ptr - offset) = value;
+       if (op_stack_test_position(stack, stack->ptr - offset)) {
+               *(stack->ptr - offset) = value;
+       }
 }
 
 static inline void op_stack_push_unknown(op_stack_t *stack) {
@@ -278,13 +330,22 @@ typedef struct {
        u1 *pos;
        u1 *instruction_start;
        s4 offset;
+       bool *perror_flag;
 } jcode_t;
 
-static void jcode_init(jcode_t *jc, u1 *start, s4 length, s4 offset) {
+static void jcode_init(jcode_t *jc, u1 *start, s4 length, s4 offset, bool *perror_flag) {
        jc->start = start;
        jc->end = jc->start + length;
        jc->pos = jc->start;
        jc->offset = offset;
+       jc->perror_flag = perror_flag;
+}
+
+static void jcode_set_error(jcode_t *jc) {
+       *(jc->perror_flag) = true;
+#if BC_ESCAPE_VERBOSE
+       printf("%s: error.\n", __FUNCTION__);
+#endif
 }
 
 static void jcode_move_to_index(jcode_t *jc, s4 index) {
@@ -324,38 +385,56 @@ static s4 jcode_get_index(const jcode_t *jc) {
        return jc->offset + (jc->pos - jc->start);
 }
 
-#define jcode_assert_has_bytes(jc, n) \
-       assert((jc->pos + n) <= jc->end)
+bool jcode_test_has_bytes(jcode_t *jc, s4 n) {
+       if ((jc->pos + n) <= jc->end) {
+               return true;
+       } else {
+               jcode_set_error(jc);
+               return false;
+       }
+}
 
 static u1 jcode_get_u1(jcode_t *jc) {
        u1 ret;
-       jcode_assert_has_bytes(jc, 1);
-       ret = jc->pos[0];
-       jc->pos += 1;
+       if (jcode_test_has_bytes(jc, 1)) {
+               ret = jc->pos[0];
+               jc->pos += 1;
+       } else {
+               ret = 0;
+       }
        return ret;
 }
 
 static s2 jcode_get_s2(jcode_t *jc) {
        s2 ret;
-       jcode_assert_has_bytes(jc, 2);
-       ret = (jc->pos[0] << 8) | (jc->pos[1]);
-       jc->pos += 2;
+       if (jcode_test_has_bytes(jc, 2)) {
+               ret = (jc->pos[0] << 8) | (jc->pos[1]);
+               jc->pos += 2;
+       } else {
+               ret = 0;
+       }
        return ret;
 }
 
 static u2 jcode_get_u2(jcode_t *jc) {
        u2 ret;
-       jcode_assert_has_bytes(jc, 2);
-       ret = (jc->pos[0] << 8) | (jc->pos[1]);
-       jc->pos += 2;
+       if (jcode_test_has_bytes(jc, 2)) {
+               ret = (jc->pos[0] << 8) | (jc->pos[1]);
+               jc->pos += 2;
+       } else {
+               ret = 0;
+       }
        return ret;
 }
 
 static s4 jcode_get_s4(jcode_t *jc) {
        s4 ret;
-       jcode_assert_has_bytes(jc, 4);
-       ret = (jc->pos[0] << 24) | (jc->pos[1] << 16) | (jc->pos[2] << 8) | (jc->pos[3]);
-       jc->pos += 4;
+       if (jcode_test_has_bytes(jc, 4)) {
+               ret = (jc->pos[0] << 24) | (jc->pos[1] << 16) | (jc->pos[2] << 8) | (jc->pos[3]);
+               jc->pos += 4;
+       } else {
+               ret = 0;
+       }
        return ret;
 }
 
@@ -371,7 +450,9 @@ static s4 jcode_get_branch_target_wide(jcode_t *jc) {
 
 static s4 jcode_get_fall_through_target(jcode_t *jc) {
        int length = bytecode[*jc->instruction_start].length;
-       assert(length > 0);
+       if (length <= 0) {
+               jcode_set_error(jc);
+       }
        return jc->offset + (jc->instruction_start - jc->start) + length;
 }
 
@@ -397,6 +478,8 @@ typedef struct {
        bool verbose;
 #endif
        int depth;
+
+       bool fatal_error;
 } bc_escape_analysis_t;
 
 static void bc_escape_analysis_perform_intern(methodinfo *m, int depth);
@@ -407,13 +490,13 @@ static void bc_escape_analysis_init(bc_escape_analysis_t *be, methodinfo *m, boo
        int a;
        u1 *ite;
        u1 t;
-       int ret_adr;
        unsigned n;
+       int ret_val_is_adr;
 
        be->method = m;
 
        be->stack = DNEW(op_stack_t);
-       op_stack_init(be->stack, m->maxstack);
+       op_stack_init(be->stack, m->maxstack, &(be->fatal_error));
 
        be->basicblocks = DNEW(basicblock_work_list_t);
        basicblock_work_list_init(be->basicblocks);
@@ -441,27 +524,28 @@ static void bc_escape_analysis_init(bc_escape_analysis_t *be, methodinfo *m, boo
 
        assert(l == be->local_to_adr_param_size);
 
-       /* Determine whether return type is address */
-
-       ret_adr = m->parseddesc->returntype.type == TYPE_ADR ? 1 : 0;
+       ret_val_is_adr = m->parseddesc->returntype.type == TYPE_ADR ? 1 : 0;
 
        /* Allocate param_escape on heap. */
 
        be->param_escape_size = a;
-       n = a + ret_adr;
+       n = a + ret_val_is_adr;
 
        if (n == 0) {
                /* Use some non-NULL value. */
                be->param_escape = (u1 *)1;
        } else {
                be->param_escape = MNEW(u1, n);
+               be->param_escape += ret_val_is_adr;
        }
 
        for (ite = be->param_escape; ite != be->param_escape + n; ++ite) {
-               *ite = (u1)ESCAPE_NONE;
+               *ite = escape_state_to_u1(ESCAPE_NONE);
        }
 
-       be->param_escape += ret_adr;
+       if (ret_val_is_adr) {
+               be->param_escape[-1] = escape_state_to_u1(ESCAPE_NONE);
+       }
 
        be->adr_param_dirty = DNEW(bit_vector_t);
        bit_vector_init(be->adr_param_dirty, a);
@@ -476,6 +560,8 @@ static void bc_escape_analysis_init(bc_escape_analysis_t *be, methodinfo *m, boo
 #endif
 
        be->depth = depth;
+
+       be->fatal_error = false;
 }
 
 static void bc_escape_analysis_branch_target(bc_escape_analysis_t *be, s4 branch_target) {
@@ -501,8 +587,8 @@ static void bc_escape_analysis_adjust_state(
                                   parameters. */
 
                                if (
-                                       old < ESCAPE_GLOBAL_THROUGH_METHOD && 
-                                       escape_state >= ESCAPE_GLOBAL_THROUGH_METHOD
+                                       old < ESCAPE_GLOBAL && 
+                                       escape_state >= ESCAPE_GLOBAL
                                ) {
                                        be->non_escaping_adr_params -= 1;
                                }
@@ -527,16 +613,17 @@ static void bc_escape_analysis_dirty_2(bc_escape_analysis_t *be, s4 local) {
        bc_escape_analysis_dirty(be, local + 1);
 }
 
-static void bc_escape_analyisis_returned(bc_escape_analysis_t *be, op_stack_slot_t value) {
+static void bc_escape_analysis_returned(bc_escape_analysis_t *be, op_stack_slot_t value) {
        if (op_stack_slot_is_param(value)) {
                /* A parameter is returned, mark it as being returned. */
                bit_vector_set(be->adr_param_returned, value.index);
-       } else if (op_stack_slot_is_unknown(value)) {
-               /* An untracked value is returned.
-                  Conservatively asume a globally escaping value is returned. */
+               /* The escape state of the return value will be adjusted later. */
+       } else {
+               /* Adjust escape state of return value. */
                if (be->method->parseddesc->returntype.type == TYPE_ADR) {
-                       be->param_escape[-1] = (u1)ESCAPE_GLOBAL;
+                       be->param_escape[-1] = escape_state_to_u1(ESCAPE_GLOBAL);
                }
+               bc_escape_analysis_adjust_state(be, value, ESCAPE_GLOBAL);
        }
 }
 
@@ -566,6 +653,25 @@ value_category_t bc_escape_analysis_value_category(bc_escape_analysis_t *be, s4
        }
 }
 
+static void bc_escape_analysis_push_return_value(
+       bc_escape_analysis_t *be,
+       methoddesc *md
+) {
+       switch (md->returntype.type) {
+               case TYPE_LNG:
+               case TYPE_DBL:
+                       op_stack_push_unknown(be->stack);
+                       op_stack_push_unknown(be->stack);
+                       break;
+               case TYPE_VOID:
+                       /* Do nothing */
+                       break;
+               default:
+                       op_stack_push_unknown(be->stack);
+                       break;
+       }
+}
+
 static void bc_escape_analysis_adjust_invoke_parameters(
        bc_escape_analysis_t *be,
        methodinfo *mi
@@ -574,6 +680,8 @@ static void bc_escape_analysis_adjust_invoke_parameters(
        methoddesc *md = mi->parseddesc;
        u1 *paramescape = mi->paramescape;
        s4 stack_depth = md->paramslots;
+       unsigned num_params_returned = 0;
+       op_stack_slot_t param_returned;
 
        /* Process parameters. 
         * The first parameter is at the highest depth on the stack.
@@ -582,10 +690,14 @@ static void bc_escape_analysis_adjust_invoke_parameters(
        for (i = 0; i < md->paramcount; ++i) {
                switch (md->paramtypes[i].type) {
                        case TYPE_ADR:
+                               if (*paramescape & 0x80) {
+                                       num_params_returned += 1;
+                                       param_returned = op_stack_get(be->stack, stack_depth);
+                               }
                                bc_escape_analysis_adjust_state(
                                        be,
                                        op_stack_get(be->stack, stack_depth),
-                                       (escape_state_t)*(paramescape++)
+                                       escape_state_from_u1(*paramescape++)
                                );
                                stack_depth -= 1;
                                break;
@@ -604,7 +716,20 @@ static void bc_escape_analysis_adjust_invoke_parameters(
        for (i = 0; i < md->paramslots; ++i) {
                op_stack_pop(be->stack);
        }
+       
+       /* Push return value. */
 
+       if (md->returntype.type == TYPE_ADR) {
+               if ((num_params_returned == 1) && (mi->paramescape[-1] < ESCAPE_GLOBAL)) {
+                       /* Only a single argument can be returned by the method,
+                          and the retun value does not escape otherwise. */
+                       op_stack_push(be->stack, param_returned);
+               } else {
+                       op_stack_push_unknown(be->stack);
+               }
+       } else {
+               bc_escape_analysis_push_return_value(be, md);
+       }
 }
 
 static void bc_escape_analysis_escape_invoke_parameters(
@@ -615,6 +740,8 @@ static void bc_escape_analysis_escape_invoke_parameters(
        for (i = 0; i < md->paramslots; ++i) {
                bc_escape_analysis_adjust_state(be, op_stack_pop(be->stack), ESCAPE_GLOBAL);
        }
+
+       bc_escape_analysis_push_return_value(be, md);
 }
 
 static void bc_escape_analysis_parse_invoke(bc_escape_analysis_t *be, jcode_t *jc) {
@@ -648,12 +775,8 @@ static void bc_escape_analysis_parse_invoke(bc_escape_analysis_t *be, jcode_t *j
 
        /* Parse parameters if not done yet. */
 
-       if (md->params == NULL) {
-               if (! descriptor_params_from_paramtypes(md, opc == BC_invokestatic ? ACC_STATIC : 0)) {
-                       /* TODO */
-                       assert(0);
-               }
-       }
+       if (md->params == NULL)
+               descriptor_params_from_paramtypes(md, opc == BC_invokestatic ? ACC_STATIC : 0);
 
        /* Try to lazyly resolve method. */
 
@@ -690,7 +813,7 @@ static void bc_escape_analysis_parse_invoke(bc_escape_analysis_t *be, jcode_t *j
           or recurse into callee. 
           Otherwise we must assume, that all parameters escape. */
 
-       if (mi != NULL) {
+       if (mi != NULL && escape_is_monomorphic(be->method, mi)) {
 
                if (mi->paramescape == NULL) {
                        bc_escape_analysis_perform_intern(mi, be->depth + 1);
@@ -707,20 +830,6 @@ static void bc_escape_analysis_parse_invoke(bc_escape_analysis_t *be, jcode_t *j
        } else {
                bc_escape_analysis_escape_invoke_parameters(be, md);
        }
-
-       switch (md->returntype.type) {
-               case TYPE_LNG:
-               case TYPE_DBL:
-                       op_stack_push_unknown(be->stack);
-                       op_stack_push_unknown(be->stack);
-                       break;
-               case TYPE_VOID:
-                       /* Do nothing */
-                       break;
-               default:
-                       op_stack_push_unknown(be->stack);
-                       break;
-       }
 }
 
 static void bc_escape_analysis_parse_tableswitch(
@@ -802,7 +911,7 @@ static void bc_escape_analysis_process_basicblock(bc_escape_analysis_t *be, jcod
        /* TODO end if all parameters escape */
        /* TODO move code into process_instruction or the like */
 
-       while ((! jcode_end(jc)) && (! bb_end)) {
+       while ((! jcode_end(jc)) && (! bb_end) && (! be->fatal_error)) {
 
                jcode_record_instruction_start(jc);
 
@@ -1367,7 +1476,7 @@ static void bc_escape_analysis_process_basicblock(bc_escape_analysis_t *be, jcod
 
                        case BC_areturn:
                                /* FIXME */
-                               bc_escape_analyisis_returned(be, op_stack_pop(be->stack));
+                               bc_escape_analysis_returned(be, op_stack_pop(be->stack));
                                bb_end = true;
                                break;
 
@@ -1570,7 +1679,7 @@ static void bc_escape_analysis_process_basicblock(bc_escape_analysis_t *be, jcod
        }
 #endif
 
-       while (! op_stack_is_empty(be->stack)) {
+       while ((! op_stack_is_empty(be->stack)) && (! be->fatal_error)) {
 #if BC_ESCAPE_VERBOSE
                if (be->verbose) {
                        dprintf(be->depth, "Stack element: ");
@@ -1586,10 +1695,19 @@ static void bc_escape_analysis_process_basicblock(bc_escape_analysis_t *be, jcod
                }
 #endif
        }
+
+       if (be->fatal_error) {
+#if BC_ESCAPE_VERBOSE
+               if (be->verbose) {
+                       printf("Fatal error while processing basic block. Aborting.\n");
+               }
+#endif
+               assert(0);
+       }
 }
 
 static void    bc_escape_analysis_adjust_return_value(bc_escape_analysis_t *be) {
-       escape_state_t e, pe;
+       escape_state_t re, pe;
        int i;
 
        /* Only calculate, if return value is of type address. */
@@ -1598,22 +1716,20 @@ static void     bc_escape_analysis_adjust_return_value(bc_escape_analysis_t *be) {
                return ;
        }
 
-       /* Get current escape state of return value. */
-
-       e = (escape_state_t)be->param_escape[-1];
-
        /* If a parameter can be returned, adjust to its escape state. */
 
        for (i = 0; i < be->param_escape_size; ++i) {
                if (bit_vector_get(be->adr_param_returned, i)) {
-                       pe = (escape_state_t)be->param_escape[i];
-                       if (pe > e) {
-                               e = pe;
+                       be->param_escape[i] |= 0x80;
+
+                       pe = escape_state_from_u1(be->param_escape[i]);
+                       re = escape_state_from_u1(be->param_escape[-1]);
+                       
+                       if (pe > re) {
+                               be->param_escape[-1] = escape_state_to_u1(pe);
                        }
                }
        }
-
-       be->param_escape[-1] = (u1)e;
 }
 
 static void bc_escape_analysis_analyze(bc_escape_analysis_t *be) {
@@ -1641,7 +1757,8 @@ static void bc_escape_analysis_analyze(bc_escape_analysis_t *be) {
                &jc,
                be->method->jcode,
                be->method->jcodelength,
-               0
+               0,
+               &(be->fatal_error)
        );
 
        /* Process basicblock by basicblock. */
@@ -1679,6 +1796,10 @@ static void bc_escape_analysis_perform_intern(methodinfo *m, int depth) {
        }
 #endif
 
+       if (depth >= 3) {
+               return;
+       }
+
        if (m->paramescape != NULL) {
 #if BC_ESCAPE_VERBOSE
                if (verbose) {  
@@ -1706,7 +1827,14 @@ static void bc_escape_analysis_perform_intern(methodinfo *m, int depth) {
                return;
        }
 
-       /* TODO threshold */
+       if (m->jcodelength > 250) {
+#if BC_ESCAPE_VERBOSE
+               if (verbose) {
+                       dprintf(depth, "Bytecode too long: %d.\n", m->jcodelength);
+               }
+#endif
+               return;
+       }
 
        be = DNEW(bc_escape_analysis_t);
        bc_escape_analysis_init(be, m, verbose, depth);
@@ -1735,3 +1863,18 @@ void bc_escape_analysis_perform(methodinfo *m) {
 }
 
 
+
+/*
+ * 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:
+ */
+