/* 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 #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=%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 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: */