PR libgcj/13439:
authortromey <tromey@138bc75d-0d04-0410-961f-82ee72b054a4>
Thu, 8 Jan 2004 05:27:39 +0000 (05:27 +0000)
committertromey <tromey@138bc75d-0d04-0410-961f-82ee72b054a4>
Thu, 8 Jan 2004 05:27:39 +0000 (05:27 +0000)
* 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.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@75533 138bc75d-0d04-0410-961f-82ee72b054a4

libjava/ChangeLog
libjava/verify.cc

index 11b5324..39a5669 100644 (file)
@@ -1,5 +1,19 @@
 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
        backslash-newline.
 
index e085938..f91df81 100644 (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 ();
   }