re PR libgcj/13439 (gij VerifyError; works with JDK 1.4)

PR libgcj/13439:
	* verify.cc (state::merge): Copy changed locals out of subroutine
	in NO_STACK case.
	(state::FLAG_CHANGED): New const.
	(state::FLAG_UNUSED): Likewise.
	(state::local_changed): Removed.  Updated all users.
	(state::flags): New field.
	(state::merge): Added jsr_semantics argument, more logic.
	(push_jump_merge): Added jsr_semantics argument.
	(handle_jsr_insn): Set jsr_semantics on push_jump_merge when
	merging through the jsr instruction.

From-SVN: r75533
This commit is contained in:
Tom Tromey 2004-01-08 05:27:39 +00:00 committed by Tom Tromey
parent ce972ee8f6
commit 11e584edfe
2 changed files with 121 additions and 26 deletions

View File

@ -1,3 +1,17 @@
2004-01-07 Tom Tromey <tromey@redhat.com>
PR libgcj/13439:
* verify.cc (state::merge): Copy changed locals out of subroutine
in NO_STACK case.
(state::FLAG_CHANGED): New const.
(state::FLAG_UNUSED): Likewise.
(state::local_changed): Removed. Updated all users.
(state::flags): New field.
(state::merge): Added jsr_semantics argument, more logic.
(push_jump_merge): Added jsr_semantics argument.
(handle_jsr_insn): Set jsr_semantics on push_jump_merge when
merging through the jsr instruction.
2004-01-07 Tom Tromey <tromey@redhat.com>
* scripts/MakeDefaultMimeTypes.java: Use \n, not

View File

@ -895,9 +895,9 @@ private:
type *stack;
// The local variables.
type *locals;
// This is used in subroutines to keep track of which local
// variables have been accessed.
bool *local_changed;
// Flags are used in subroutines to keep track of which local
// variables have been accessed. They are also used after
char *flags;
// If not 0, then we are in a subroutine. The value is the PC of
// the subroutine's entry point. We can use 0 as an exceptional
// value because PC=0 can never be a subroutine.
@ -930,12 +930,21 @@ private:
// `ret'. See handle_jsr_insn for more information.
static const int NO_STACK = -1;
// This flag indicates that the local was changed in this
// subroutine.
static const int FLAG_CHANGED = 1;
// This is set only on the flags of the state of an instruction
// directly following a "jsr". It indicates that the local
// variable was changed by the subroutine corresponding to the
// "jsr".
static const int FLAG_USED = 2;
state ()
: this_type ()
{
stack = NULL;
locals = NULL;
local_changed = NULL;
flags = NULL;
seen_subrs = NULL;
}
@ -948,12 +957,12 @@ private:
for (int i = 0; i < max_stack; ++i)
stack[i] = unsuitable_type;
locals = new type[max_locals];
local_changed = (bool *) _Jv_Malloc (sizeof (bool) * max_locals);
flags = (char *) _Jv_Malloc (sizeof (char) * max_locals);
seen_subrs = NULL;
for (int i = 0; i < max_locals; ++i)
{
locals[i] = unsuitable_type;
local_changed[i] = false;
flags[i] = 0;
}
next = INVALID;
subroutine = 0;
@ -964,7 +973,7 @@ private:
{
stack = new type[max_stack];
locals = new type[max_locals];
local_changed = (bool *) _Jv_Malloc (sizeof (bool) * max_locals);
flags = (char *) _Jv_Malloc (sizeof (char) * max_locals);
seen_subrs = NULL;
copy (orig, max_stack, max_locals, ret_semantics);
next = INVALID;
@ -976,8 +985,8 @@ private:
delete[] stack;
if (locals)
delete[] locals;
if (local_changed)
_Jv_Free (local_changed);
if (flags)
_Jv_Free (flags);
clean_subrs ();
}
@ -1025,12 +1034,29 @@ private:
{
// See push_jump_merge to understand this case.
if (ret_semantics)
locals[i] = type (copy->local_changed[i]
? copy->locals[i]
: unused_by_subroutine_type);
{
if ((copy->flags[i] & FLAG_CHANGED))
{
// Changed in the subroutine, so we copy it here.
locals[i] = copy->locals[i];
flags[i] |= FLAG_USED;
}
else
{
// Not changed in the subroutine. Use a special
// type so the coming merge will overwrite.
locals[i] = type (unused_by_subroutine_type);
}
}
else
locals[i] = copy->locals[i];
local_changed[i] = subroutine ? copy->local_changed[i] : false;
// Clear the flag unconditionally just so printouts look ok,
// then only set it if we're still in a subroutine and it
// did in fact change.
flags[i] &= ~FLAG_CHANGED;
if (subroutine && (copy->flags[i] & FLAG_CHANGED) != 0)
flags[i] |= FLAG_CHANGED;
}
clean_subrs ();
@ -1064,7 +1090,7 @@ private:
// nested subroutines, this information will be merged back into
// parent by the `ret'.
for (int i = 0; i < max_locals; ++i)
local_changed[i] = false;
flags[i] &= ~FLAG_CHANGED;
}
// Indicate that we've been in this this subroutine.
@ -1080,7 +1106,8 @@ private:
// state. Returns true if the new state was in fact changed.
// Will throw an exception if the states are not mergeable.
bool merge (state *state_old, bool ret_semantics,
int max_locals, _Jv_BytecodeVerifier *verifier)
int max_locals, _Jv_BytecodeVerifier *verifier,
bool jsr_semantics = false)
{
bool changed = false;
@ -1122,11 +1149,21 @@ private:
}
}
// Merge stacks. Special handling for NO_STACK case.
// Merge stacks, including special handling for NO_STACK case.
// If the destination is NO_STACK, this means it is the
// instruction following a "jsr" and has not yet been processed
// in any way. In this situation, if we are currently
// processing a "ret", then we must *copy* any locals changed in
// the subroutine into the current state. Merging in this
// situation is incorrect because the locals we've noted didn't
// come real program flow, they are just an artifact of how
// we've chosen to handle the post-jsr state.
bool copy_in_locals = ret_semantics && stacktop == NO_STACK;
if (state_old->stacktop == NO_STACK)
{
// Nothing to do in this case; we don't care about modifying
// the old state.
// This can happen if we're doing a pass-through jsr merge.
// Here we can just ignore the stack.
}
else if (stacktop == NO_STACK)
{
@ -1155,8 +1192,36 @@ private:
// only merge locals which changed in the subroutine. When
// processing a `ret', STATE_OLD is the state at the point
// of the `ret', and THIS is the state just after the `jsr'.
if (! ret_semantics || state_old->local_changed[i])
// See comment above for explanation of COPY_IN_LOCALS.
if (copy_in_locals)
{
if ((state_old->flags[i] & FLAG_CHANGED) != 0)
{
locals[i] = state_old->locals[i];
changed = true;
// There's no point in calling note_variable here,
// since we call it under the same condition before
// the loop ends.
}
}
else if (jsr_semantics && (flags[i] & FLAG_USED) != 0)
{
// We are processing the "pass-through" part of a jsr
// statement. In this particular case, the local was
// changed by the subroutine. So, we have no work to
// do, as the pre-jsr value does not survive the
// subroutine call.
}
else if (! ret_semantics
|| (state_old->flags[i] & FLAG_CHANGED) != 0)
{
// If we have ordinary (not ret) semantics, then we have
// merging flow control, so we merge types. Or, we have
// jsr pass-through semantics and the type survives the
// subroutine (see above), so again we merge. Or,
// finally, we have ret semantics and this value did
// change, in which case we merge the change from the
// subroutine into the post-jsr instruction.
if (locals[i].merge (state_old->locals[i], true, verifier))
{
// Note that we don't call `note_variable' here.
@ -1172,8 +1237,14 @@ private:
// If we're in a subroutine, we must compute the union of
// all the changed local variables.
if (state_old->local_changed[i])
if ((state_old->flags[i] & FLAG_CHANGED) != 0)
note_variable (i);
// If we're returning from a subroutine, we must mark the
// post-jsr instruction with information about what changed,
// so that future "pass-through" jsr merges work correctly.
if (ret_semantics && (state_old->flags[i] & FLAG_CHANGED) != 0)
flags[i] |= FLAG_USED;
}
return changed;
@ -1218,7 +1289,7 @@ private:
void note_variable (int index)
{
if (subroutine > 0)
local_changed[index] = true;
flags[index] |= FLAG_CHANGED;
}
// Mark each `new'd object we know of that was allocated at PC as
@ -1259,7 +1330,10 @@ private:
for (i = 0; i < max_locals; ++i)
{
locals[i].print ();
debug_print (local_changed[i] ? "+" : " ");
if ((flags[i] & FLAG_USED) != 0)
debug_print ((flags[i] & FLAG_CHANGED) ? ">" : "<");
else
debug_print ((flags[i] & FLAG_CHANGED) ? "+" : " ");
}
if (subroutine == 0)
debug_print (" | None");
@ -1450,8 +1524,14 @@ private:
// schedule a new PC if there is a change. If RET_SEMANTICS is
// true, then we are merging from a `ret' instruction into the
// instruction after a `jsr'. This is a special case with its own
// modified semantics.
void push_jump_merge (int npc, state *nstate, bool ret_semantics = false)
// modified semantics. If JSR_SEMANTICS is true, then we're merging
// some type information from a "jsr" instruction to the immediately
// following instruction. In this situation we have to be careful
// not to merge local variables whose values are modified by the
// subroutine we're about to call.
void push_jump_merge (int npc, state *nstate,
bool ret_semantics = false,
bool jsr_semantics = false)
{
bool changed = true;
if (states[npc] == NULL)
@ -1478,7 +1558,8 @@ private:
states[npc]->print (" To", npc, current_method->max_stack,
current_method->max_locals);
changed = states[npc]->merge (nstate, ret_semantics,
current_method->max_locals, this);
current_method->max_locals, this,
jsr_semantics);
states[npc]->print ("New", npc, current_method->max_stack,
current_method->max_locals);
}
@ -1672,7 +1753,7 @@ private:
if (PC < current_method->code_length)
{
current_state->stacktop = state::NO_STACK;
push_jump_merge (PC, current_state);
push_jump_merge (PC, current_state, false, true);
}
invalidate_pc ();
}