* src/vm/jit/verify/icmds.cpp,
[cacao.git] / src / vm / jit / verify / typecheck-common.cpp
diff --git a/src/vm/jit/verify/typecheck-common.cpp b/src/vm/jit/verify/typecheck-common.cpp
new file mode 100644 (file)
index 0000000..62818a1
--- /dev/null
@@ -0,0 +1,558 @@
+/* src/vm/jit/verify/typecheck-common.c - shared verifier code
+
+   Copyright (C) 1996-2005, 2006, 2008
+   CACAOVM - Verein zur Foerderung der freien virtuellen Maschine 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 "vm/types.h"
+#include "vm/global.h"
+
+#include <assert.h>
+
+#include "vm/exceptions.hpp"
+#include "vm/globals.hpp"
+
+#include "vm/jit/show.hpp"
+
+#include "typecheck-common.hpp"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/****************************************************************************/
+/* DEBUG HELPERS                                                            */
+/****************************************************************************/
+
+#ifdef TYPECHECK_VERBOSE_OPT
+bool opt_typecheckverbose = false;
+#endif
+
+#if defined(TYPECHECK_VERBOSE) || defined(TYPECHECK_VERBOSE_IMPORTANT)
+
+void typecheck_print_var(FILE *file, jitdata *jd, s4 index)
+{
+       varinfo *var;
+
+       assert(index >= 0 && index < jd->varcount);
+       var = VAR(index);
+       typeinfo_print_type(file, var->type, &(var->typeinfo));
+}
+
+void typecheck_print_vararray(FILE *file, jitdata *jd, s4 *vars, int len)
+{
+       s4 i;
+
+       for (i=0; i<len; ++i) {
+               if (i)
+                       fputc(' ', file);
+               typecheck_print_var(file, jd, *vars++);
+       }
+}
+
+#endif /* defined(TYPECHECK_VERBOSE) || defined(TYPECHECK_VERBOSE_IMPORTANT) */
+
+
+/****************************************************************************/
+/* STATISTICS                                                               */
+/****************************************************************************/
+
+#if defined(TYPECHECK_STATISTICS)
+int stat_typechecked = 0;
+int stat_methods_with_handlers = 0;
+int stat_methods_maythrow = 0;
+int stat_iterations[STAT_ITERATIONS+1] = { 0 };
+int stat_reached = 0;
+int stat_copied = 0;
+int stat_merged = 0;
+int stat_merging_changed = 0;
+int stat_blocks[STAT_BLOCKS+1] = { 0 };
+int stat_locals[STAT_LOCALS+1] = { 0 };
+int stat_ins = 0;
+int stat_ins_maythrow = 0;
+int stat_ins_stack = 0;
+int stat_ins_field = 0;
+int stat_ins_field_unresolved = 0;
+int stat_ins_field_uninitialized = 0;
+int stat_ins_invoke = 0;
+int stat_ins_invoke_unresolved = 0;
+int stat_ins_primload = 0;
+int stat_ins_aload = 0;
+int stat_ins_builtin = 0;
+int stat_ins_builtin_gen = 0;
+int stat_ins_branch = 0;
+int stat_ins_switch = 0;
+int stat_ins_primitive_return = 0;
+int stat_ins_areturn = 0;
+int stat_ins_areturn_unresolved = 0;
+int stat_ins_athrow = 0;
+int stat_ins_athrow_unresolved = 0;
+int stat_ins_unchecked = 0;
+int stat_handlers_reached = 0;
+int stat_savedstack = 0;
+
+static void print_freq(FILE *file,int *array,int limit)
+{
+       int i;
+       for (i=0; i<limit; ++i)
+               fprintf(file,"      %3d: %8d\n",i,array[i]);
+       fprintf(file,"    >=%3d: %8d\n",limit,array[limit]);
+}
+
+void typecheck_print_statistics(FILE *file) {
+       fprintf(file,"typechecked methods: %8d\n",stat_typechecked);
+       fprintf(file,"    with handler(s): %8d\n",stat_methods_with_handlers);
+       fprintf(file,"    with throw(s)  : %8d\n",stat_methods_maythrow);
+       fprintf(file,"reached blocks     : %8d\n",stat_reached);
+       fprintf(file,"copied states      : %8d\n",stat_copied);
+       fprintf(file,"merged states      : %8d\n",stat_merged);
+       fprintf(file,"merging changed    : %8d\n",stat_merging_changed);
+       fprintf(file,"handlers reached   : %8d\n",stat_handlers_reached);
+       fprintf(file,"saved stack (times): %8d\n",stat_savedstack);
+       fprintf(file,"instructions       : %8d\n",stat_ins);
+       fprintf(file,"    stack          : %8d\n",stat_ins_stack);
+       fprintf(file,"    field access   : %8d\n",stat_ins_field);
+       fprintf(file,"      (unresolved) : %8d\n",stat_ins_field_unresolved);
+       fprintf(file,"      (uninit.)    : %8d\n",stat_ins_field_uninitialized);
+       fprintf(file,"    invocations    : %8d\n",stat_ins_invoke);
+       fprintf(file,"      (unresolved) : %8d\n",stat_ins_invoke_unresolved);
+       fprintf(file,"    load primitive : (currently not counted) %8d\n",stat_ins_primload);
+       fprintf(file,"    load address   : %8d\n",stat_ins_aload);
+       fprintf(file,"    builtins       : %8d\n",stat_ins_builtin);
+       fprintf(file,"        generic    : %8d\n",stat_ins_builtin_gen);
+       fprintf(file,"    branches       : %8d\n",stat_ins_branch);
+       fprintf(file,"    switches       : %8d\n",stat_ins_switch);
+       fprintf(file,"    prim. return   : %8d\n",stat_ins_primitive_return);
+       fprintf(file,"    areturn        : %8d\n",stat_ins_areturn);
+       fprintf(file,"      (unresolved) : %8d\n",stat_ins_areturn_unresolved);
+       fprintf(file,"    athrow         : %8d\n",stat_ins_athrow);
+       fprintf(file,"      (unresolved) : %8d\n",stat_ins_athrow_unresolved);
+       fprintf(file,"    unchecked      : %8d\n",stat_ins_unchecked);
+       fprintf(file,"    maythrow       : %8d\n",stat_ins_maythrow);
+       fprintf(file,"iterations used:\n");
+       print_freq(file,stat_iterations,STAT_ITERATIONS);
+       fprintf(file,"basic blocks per method / 10:\n");
+       print_freq(file,stat_blocks,STAT_BLOCKS);
+       fprintf(file,"locals:\n");
+       print_freq(file,stat_locals,STAT_LOCALS);
+}
+#endif /* defined(TYPECHECK_STATISTICS) */
+
+
+/* typecheck_init_flags ********************************************************
+   Initialize the basic block flags for the following CFG traversal.
+  
+   IN:
+       state............the current state of the verifier
+       minflags.........minimum flags value of blocks that should be
+                        considered
+
+*******************************************************************************/
+
+void typecheck_init_flags(verifier_state *state, s4 minflags)
+{
+       basicblock *block;
+
+    /* set all BBFINISHED blocks to BBTYPECHECK_UNDEF. */
+       
+    for (block = state->basicblocks; block; block = block->next) {
+               
+#ifdef TYPECHECK_DEBUG
+               /* check for invalid flags */
+        if (block->flags != BBFINISHED && block->flags != BBDELETED && block->flags != BBUNDEF)
+        {
+            LOGSTR1("block flags: %d\n",block->flags); LOGFLUSH;
+                       TYPECHECK_ASSERT(false);
+        }
+#endif
+
+        if (block->flags >= minflags) {
+            block->flags = BBTYPECHECK_UNDEF;
+        }
+    }
+
+    /* the first block is always reached */
+       
+    if (state->basicblockcount && state->basicblocks[0].flags == BBTYPECHECK_UNDEF)
+        state->basicblocks[0].flags = BBTYPECHECK_REACHED;
+}
+
+
+/* typecheck_reset_flags *******************************************************
+   Reset the flags of basic blocks we have not reached.
+  
+   IN:
+       state............the current state of the verifier
+
+*******************************************************************************/
+
+void typecheck_reset_flags(verifier_state *state)
+{
+       basicblock *block;
+
+       /* check for invalid flags at exit */
+       
+#ifdef TYPECHECK_DEBUG
+       for (block = state->basicblocks; block; block = block->next) {
+               if (block->flags != BBDELETED
+                       && block->flags != BBUNDEF
+                       && block->flags != BBFINISHED
+                       && block->flags != BBTYPECHECK_UNDEF) /* typecheck may never reach
+                                                                                                        * some exception handlers,
+                                                                                                        * that's ok. */
+               {
+                       LOG2("block L%03d has invalid flags after typecheck: %d",
+                                block->nr,block->flags);
+                       TYPECHECK_ASSERT(false);
+               }
+       }
+#endif
+       
+       /* Delete blocks we never reached */
+       
+       for (block = state->basicblocks; block; block = block->next) {
+               if (block->flags == BBTYPECHECK_UNDEF)
+                       block->flags = BBDELETED;
+       }
+}
+
+
+/****************************************************************************/
+/* TYPESTACK MACROS AND FUNCTIONS                                           */
+/*                                                                          */
+/* These macros and functions act on the 'type stack', which is a shorthand */
+/* for the types of the stackslots of the current stack. The type of a      */
+/* stack slot is usually described by a TYPE_* constant and -- for TYPE_ADR */
+/* -- by the typeinfo of the slot. The only thing that makes the type stack */
+/* more complicated are returnAddresses of local subroutines, because a     */
+/* single stack slot may contain a set of more than one possible return     */
+/* address. This is handled by 'return address sets'. A return address set  */
+/* is kept as a linked list dangling off the typeinfo of the stack slot.    */
+/****************************************************************************/
+
+/* typecheck_copy_types ********************************************************
+
+   Copy the types of the source variables to the destination variables.
+
+   IN:
+          state............current verifier state
+          srcvars..........array of variable indices to copy
+          dstvars..........array of the destination variables
+          n................number of variables to copy
+
+   RETURN VALUE:
+       true.............success
+          false............an exception has been thrown
+
+*******************************************************************************/
+
+bool typecheck_copy_types(verifier_state *state, s4 *srcvars, s4 *dstvars, s4 n)
+{
+       s4 i;
+       varinfo *sv;
+       varinfo *dv;
+       jitdata *jd = state->jd;
+
+       for (i=0; i < n; ++i, ++srcvars, ++dstvars) {
+               sv = VAR(*srcvars);
+               dv = VAR(*dstvars);
+
+               dv->type = sv->type;
+               if (dv->type == TYPE_ADR) {
+                       TYPEINFO_CLONE(sv->typeinfo,dv->typeinfo);
+               }
+       }
+       return true;
+}
+
+
+/* typecheck_merge_types *******************************************************
+
+   Merge the types of the source variables into the destination variables.
+
+   IN:
+       state............current state of the verifier
+          srcvars..........source variable indices
+          dstvars..........destination variable indices
+          n................number of variables
+
+   RETURN VALUE:
+       typecheck_TRUE...the destination variables have been modified
+          typecheck_FALSE..the destination variables are unchanged
+          typecheck_FAIL...an exception has been thrown
+
+*******************************************************************************/
+
+typecheck_result typecheck_merge_types(verifier_state *state,
+                                                                          s4 *srcvars,
+                                                                          s4 *dstvars,
+                                                                          s4 n)
+{
+       s4 i;
+       varinfo *sv;
+       varinfo *dv;
+       jitdata *jd = state->jd;
+       typecheck_result r;
+       bool changed = false;
+
+       for (i=0; i < n; ++i, ++srcvars, ++dstvars) {
+               sv = VAR(*srcvars);
+               dv = VAR(*dstvars);
+
+               if (dv->type != sv->type) {
+                       exceptions_throw_verifyerror(state->m,"Stack type mismatch");
+                       return typecheck_FAIL;
+               }
+               if (dv->type == TYPE_ADR) {
+                       if (TYPEINFO_IS_PRIMITIVE(dv->typeinfo)) {
+                               /* dv has returnAddress type */
+                               if (!TYPEINFO_IS_PRIMITIVE(sv->typeinfo)) {
+                                       exceptions_throw_verifyerror(state->m,"Merging returnAddress with reference");
+                                       return typecheck_FAIL;
+                               }
+                       }
+                       else {
+                               /* dv has reference type */
+                               if (TYPEINFO_IS_PRIMITIVE(sv->typeinfo)) {
+                                       exceptions_throw_verifyerror(state->m,"Merging reference with returnAddress");
+                                       return typecheck_FAIL;
+                               }
+                               r = typeinfo_merge(state->m,&(dv->typeinfo),&(sv->typeinfo));
+                               if (r == typecheck_FAIL)
+                                       return r;
+                               changed |= r;
+                       }
+               }
+       }
+       return (typecheck_result) changed;
+}
+
+
+/* typestate_merge *************************************************************
+
+   Merge the types of one state into the destination state.
+
+   IN:
+       state............current state of the verifier
+          dstvars..........indices of the destinations invars
+          dstlocals........the destinations inlocals
+          srcvars..........indices of the source's outvars
+          srclocals........the source locals
+          n................number of invars (== number of outvars)
+
+   RETURN VALUE:
+       typecheck_TRUE...destination state has been modified
+          typecheck_FALSE..destination state has not been modified
+          typecheck_FAIL...an exception has been thrown
+
+*******************************************************************************/
+
+typecheck_result typestate_merge(verifier_state *state,
+                                                s4 *srcvars, varinfo *srclocals,
+                                                s4 *dstvars, varinfo *dstlocals,
+                                                s4 n)
+{
+       bool changed = false;
+       typecheck_result r;
+
+       /* The stack is always merged. If there are returnAddresses on
+        * the stack they are ignored in this step. */
+
+       r = typecheck_merge_types(state, srcvars, dstvars, n);
+       if (r == typecheck_FAIL)
+               return r;
+       changed |= r;
+
+       /* merge the locals */
+
+       r = typevector_merge(state->m, dstlocals, srclocals, state->numlocals);
+       if (r == typecheck_FAIL)
+               return r;
+       return (typecheck_result) (changed | r);
+}
+
+
+/* typestate_reach *************************************************************
+
+   Reach a destination block and propagate stack and local variable types
+
+   IN:
+       state............current state of the verifier
+          destblock........destination basic block
+          srcvars..........variable indices of the outvars to propagate
+          srclocals........local variables to propagate
+          n................number of srcvars
+
+   OUT:
+       state->repeat....set to true if the verifier must iterate again
+                           over the basic blocks
+
+   RETURN VALUE:
+       true.............success
+          false............an exception has been thrown
+
+*******************************************************************************/
+
+bool typestate_reach(verifier_state *state,
+                                        basicblock *destblock,
+                                        s4 *srcvars, varinfo *srclocals, s4 n)
+{
+       varinfo *destloc;
+       bool changed = false;
+       typecheck_result r;
+
+       LOG1("reaching block L%03d",destblock->nr);
+       TYPECHECK_COUNT(stat_reached);
+
+       destloc = destblock->inlocals;
+
+       if (destblock->flags == BBTYPECHECK_UNDEF) {
+               /* The destblock has never been reached before */
+
+               TYPECHECK_COUNT(stat_copied);
+               LOG1("block L%03d reached first time",destblock->nr);
+
+               if (!typecheck_copy_types(state, srcvars, destblock->invars, n))
+                       return false;
+               typevector_copy_inplace(srclocals, destloc, state->numlocals);
+               changed = true;
+       }
+       else {
+               /* The destblock has already been reached before */
+
+               TYPECHECK_COUNT(stat_merged);
+               LOG1("block L%03d reached before", destblock->nr);
+
+               r = typestate_merge(state, srcvars, srclocals,
+                               destblock->invars, destblock->inlocals, n);
+               if (r == typecheck_FAIL)
+                       return false;
+               changed = r;
+               TYPECHECK_COUNTIF(changed,stat_merging_changed);
+       }
+
+       if (changed) {
+               LOG("changed!");
+               destblock->flags = BBTYPECHECK_REACHED;
+               if (destblock->nr <= state->bptr->nr) {
+                       LOG("REPEAT!");
+                       state->repeat = true;
+               }
+       }
+       return true;
+}
+
+
+/* typecheck_init_locals *******************************************************
+
+   Initialize the local variables in the verifier state.
+
+   IN:
+       state............the current state of the verifier
+          newthis..........if true, mark the instance in <init> methods as
+                           uninitialized object.
+
+   RETURN VALUE:
+       true.............success,
+          false............an exception has been thrown.
+
+*******************************************************************************/
+
+bool typecheck_init_locals(verifier_state *state, bool newthis)
+{
+       int i;
+       int varindex;
+       varinfo *locals;
+       varinfo *v;
+       jitdata *jd = state->jd;
+       int skip = 0;
+
+       locals = state->basicblocks[0].inlocals;
+
+       /* allocate parameter descriptors if necessary */
+
+       if (!state->m->parseddesc->params)
+               if (!descriptor_params_from_paramtypes(state->m->parseddesc,state->m->flags))
+                       return false;
+
+       /* pre-initialize variables as TYPE_VOID */
+
+       i = state->numlocals;
+       v = locals;
+       while (i--) {
+               v->type = TYPE_VOID;
+               v++;
+       }
+
+    /* if this is an instance method initialize the "this" ref type */
+
+    if (!(state->m->flags & ACC_STATIC)) {
+               varindex = jd->local_map[5*0 + TYPE_ADR];
+               if (varindex != UNUSED) {
+                       if (state->validlocals < 1)
+                               TYPECHECK_VERIFYERROR_bool("Not enough local variables for method arguments");
+                       v = locals + varindex;
+                       v->type = TYPE_ADR;
+                       if (state->initmethod && newthis)
+                               TYPEINFO_INIT_NEWOBJECT(v->typeinfo, NULL);
+                       else
+                               typeinfo_init_classinfo(&(v->typeinfo), state->m->clazz);
+               }
+
+               skip = 1;
+    }
+
+    LOG("'this' argument set.\n");
+
+    /* the rest of the arguments and the return type */
+
+    if (!typeinfo_init_varinfos_from_methoddesc(locals, state->m->parseddesc,
+                                                                                         state->validlocals,
+                                                                                         skip, /* skip 'this' pointer */
+                                                                                         jd->local_map,
+                                                                                         &state->returntype))
+               return false;
+
+    LOG("Arguments set.\n");
+       return true;
+}
+
+#if defined(__cplusplus)
+}
+#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:
+ * vim:noexpandtab:sw=4:ts=4:
+ */