[gcjx] Patch: FYI: implement LocalVariableTable

Tom Tromey tromey@redhat.com
Sat Jan 15 21:27:00 GMT 2005


I'm checking this in on the gcjx branch.

This implements LocalVariableTable support when generating bytecode.
This table is used to tell debuggers about the names and locations of
local variables in a method.

Tom

Index: ChangeLog
from  Tom Tromey  <tromey@redhat.com>

	* bytecode/generate.cc (generate): Write local variable table.
	(bytecode_generator): Updated.
	* bytecode/locals.cc (request): Updated.
	(get_index): Likewise.
	(remove): Likewise.
	(update): New method.
	(emit): Likewise.
	* bytecode/generate.hh (bytecode_generator::get_current): New
	method.
	(bytecode_generator::local_variable_table_attribute): New class.
	* bytecode/locals.hh (class locals): More documentation.
	(class temporary_local): Likewise.
	(locals::gen): New field.
	(locals::debug_info): New structure.
	(locals::keep_debug_info): New field.
	(locals::var_descriptions): Likewise.
	(locals): Initialize new fields.
	(locals::indexes): Removed.
	(locals::scope_map): Likewise.
	(locals::update): Declare.
	(locals::emit): Likewise.

Index: TODO
===================================================================
RCS file: /cvs/gcc/gcc/gcjx/Attic/TODO,v
retrieving revision 1.1.2.3
diff -u -r1.1.2.3 TODO
--- TODO 15 Jan 2005 07:34:02 -0000 1.1.2.3
+++ TODO 15 Jan 2005 21:24:48 -0000
@@ -169,10 +169,6 @@
 outpool::add(string) and outpool::add_utf() have the wrong names.
 add_utf() is used more and should be plain add()
 
-we also don't emit variable names
-  these could be handled like exceptions
-  except that is already ugly
-
 in gcc a declaration can also be used as a variable reference.
 the idea is, having all those extra reference objects is
 wasteful.  we could try this... but is it true?  we would
Index: bytecode/generate.cc
===================================================================
RCS file: /cvs/gcc/gcc/gcjx/bytecode/Attic/generate.cc,v
retrieving revision 1.1.2.3
diff -u -r1.1.2.3 generate.cc
--- bytecode/generate.cc 15 Jan 2005 07:34:06 -0000 1.1.2.3
+++ bytecode/generate.cc 15 Jan 2005 21:24:50 -0000
@@ -38,6 +38,7 @@
 					output_constant_pool *cp)
   : method (m),
     cpool (cp),
+    vars (this),
     this_index (-1),
     first_block (NULL),
     current_block (NULL),
@@ -171,10 +172,15 @@
       cpool->add (hand.type);
     }
 
-  if (global->get_compiler ()->target_debug () && line_count > 0)
-    attributes.push_back (new line_table_attribute (cpool, this));
+  if (global->get_compiler ()->target_debug ())
+    {
+      if (line_count > 0)
+	attributes.push_back (new line_table_attribute (cpool, this));
 
-  // FIXME: LocalVariableTable
+      if (vars.update ())
+	attributes.push_back (new local_variable_table_attribute (cpool,
+								  &vars));
+    }
 }
 
 void
Index: bytecode/generate.hh
===================================================================
RCS file: /cvs/gcc/gcc/gcjx/bytecode/Attic/generate.hh,v
retrieving revision 1.1.2.2
diff -u -r1.1.2.2 generate.hh
--- bytecode/generate.hh 15 Jan 2005 07:34:06 -0000 1.1.2.2
+++ bytecode/generate.hh 15 Jan 2005 21:24:50 -0000
@@ -181,6 +181,28 @@
     }
   };
 
+  /// This class is used to handle the LocalVariableTable attribute.
+  class local_variable_table_attribute : public bytecode_attribute
+  {
+    locals *vars;
+
+  public:
+
+    local_variable_table_attribute (output_constant_pool *p, locals *v)
+      : bytecode_attribute (p, "LocalVariableTable"),
+	vars (v)
+    {
+      // This is inefficient, we could cache information.
+      vars->emit (pool, NULL);
+    }
+
+    void emit (bytecode_stream &writer)
+    {
+      bytecode_attribute::emit (writer);
+      vars->emit (pool, &writer);
+    }
+  };
+
   void write_line_table (bytecode_stream *);
 
   int adjust_for_type (model_type *) const;
@@ -329,6 +351,12 @@
   /// Write the bytecode for this method.
   void write (bytecode_stream *);
 
+  /// Used to keep track of lifetimes of local variables.
+  bytecode_block *get_current () const
+  {
+    return current_block;
+  }
+
 
   void visit_method (model_method *,
 		     const std::list<ref_variable_decl> &,
Index: bytecode/locals.cc
===================================================================
RCS file: /cvs/gcc/gcc/gcjx/bytecode/Attic/locals.cc,v
retrieving revision 1.1.2.1
diff -u -r1.1.2.1 locals.cc
--- bytecode/locals.cc 13 Jan 2005 03:18:34 -0000 1.1.2.1
+++ bytecode/locals.cc 15 Jan 2005 21:24:50 -0000
@@ -1,6 +1,6 @@
 // Local variable handling.
 
-// Copyright (C) 2004 Free Software Foundation, Inc.
+// Copyright (C) 2004, 2005 Free Software Foundation, Inc.
 //
 // This file is part of GCC.
 //
@@ -20,8 +20,12 @@
 // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
 #include "typedefs.hh"
+#include "bytecode/outpool.hh"
 #include "bytecode/locals.hh"
 #include "bytecode/insns.hh"
+#include "bytecode/block.hh"
+#include "bytecode/attribute.hh"
+#include "bytecode/generate.hh"
 #include "bytecode/byteutil.hh"
 
 void
@@ -33,7 +37,7 @@
 int
 locals::request (model_variable_decl *decl)
 {
-  assert (decl == NULL || indexes.find (decl) == indexes.end ());
+  assert (decl == NULL || vars.find (decl) == vars.end ());
   int n;
   int delta = 0;
   if (wide_p (decl ? decl->type () : NULL))
@@ -58,8 +62,12 @@
 
   if (decl)
     {
-      indexes[decl] = n;
-      scope_map[decl] = scope.back ();
+      debug_info &info = vars[decl];
+      info.variable = decl;
+      info.start = gen->get_current ();
+      info.end = NULL;
+      info.scope = scope.back ();
+      info.index = n;
     }
   return n;
 }
@@ -68,24 +76,29 @@
 locals::get_index (model_variable_decl *decl)
 {
   // It would be way easier to have a field on the variable.
-  std::map<model_variable_decl *, int>::const_iterator i
-    = indexes.find (decl);
-  assert (i != indexes.end ());
-  return (*i).second;
+  std::map<model_variable_decl *, debug_info>::const_iterator i
+    = vars.find (decl);
+  assert (i != vars.end ());
+  return (*i).second.index;
 }
 
 void
 locals::remove (model_variable_decl *decl)
 {
-  std::map<model_variable_decl *, int>::iterator i
-    = indexes.find (decl);
-  assert (i != indexes.end ());
-  used[(*i).second] = false;
+  std::map<model_variable_decl *, debug_info>::iterator i
+    = vars.find (decl);
+  assert (i != vars.end ());
 
+  debug_info info = (*i).second;
+
+  used[info.index] = false;
   if (wide_p (decl ? decl->type () : NULL))
-    used[(*i).second + 1] = false;
+    used[info.index + 1] = false;
+
+  vars.erase (i);
 
-  indexes.erase (i);
+  info.end = gen->get_current ();
+  var_descriptions.push_back (info);
 }
 
 void
@@ -100,22 +113,90 @@
 locals::remove (const model_stmt *stmt)
 {
   std::list<model_variable_decl *> dels;
-  for (std::map<model_variable_decl *, int>::iterator i
-	 = indexes.begin ();
-       i != indexes.end ();
+  for (std::map<model_variable_decl *, debug_info>::iterator i
+	 = vars.begin ();
+       i != vars.end ();
        ++i)
     {
-      if (scope_map[(*i).first] == stmt)
+      debug_info info = (*i).second;
+      if (info.scope == stmt)
 	{
-	  used[(*i).second] = false;
-	  if (wide_p ((*i).first->type ()))
-	    used[(*i).second + 1] = false;
-	  dels.push_back ((*i).first);
+	  used[info.index] = false;
+	  if (wide_p (info.variable->type ()))
+	    used[info.index + 1] = false;
+	  info.end = gen->get_current ();
+	  var_descriptions.push_back (info);
+	  dels.push_back (info.variable);
 	}
     }
 
   for (std::list<model_variable_decl *>::const_iterator i = dels.begin ();
        i != dels.end ();
        ++i)
-    indexes.erase (*i);
+    vars.erase (*i);
+}
+
+bool
+locals::update ()
+{
+  bool any = false;
+  for (std::list<debug_info>::iterator i = var_descriptions.begin ();
+       i != var_descriptions.end ();
+       ++i)
+    {
+      debug_info &info (*i);
+
+      info.start = info.start->update ();
+      if (info.start->live_p ())
+	{
+	  info.end = info.end->update ();
+	  assert (info.end->live_p ());
+	  // Don't emit debug info for a zero-length range.
+	  if (info.start != info.end)
+	    any = true;
+	}
+    }
+  return any;
+}
+
+void
+locals::emit (output_constant_pool *pool, bytecode_stream *out)
+{
+  int count = 0;
+  for (std::list<debug_info>::iterator i = var_descriptions.begin ();
+       i != var_descriptions.end ();
+       ++i)
+    {
+      debug_info &info (*i);
+      if (! info.start->live_p () || info.start == info.end)
+	continue;
+
+      ++count;
+      if (! out)
+	{
+	  // Make sure these are in the pool.
+	  pool->add_utf (info.variable->get_name ());
+	  pool->add_utf (info.variable->type ()->get_descriptor ());
+	}
+    }
+
+  if (! out)
+    return;
+
+  out->put4 (2 + 10 * count);
+  out->put2 (count);
+  for (std::list<debug_info>::const_iterator i = var_descriptions.begin ();
+       i != var_descriptions.end ();
+       ++i)
+    {
+      const debug_info &info (*i);
+      if (! info.start->live_p () || info.start == info.end)
+	continue;
+
+      out->put2 (info.start->get_pc ());
+      out->put2 (info.end->get_pc () - info.start->get_pc ());
+      out->put2 (pool->add_utf (info.variable->get_name ()));
+      out->put2 (pool->add_utf (info.variable->type ()->get_descriptor ()));
+      out->put2 (info.index);
+    }
 }
Index: bytecode/locals.hh
===================================================================
RCS file: /cvs/gcc/gcc/gcjx/bytecode/Attic/locals.hh,v
retrieving revision 1.1.2.1
diff -u -r1.1.2.1 locals.hh
--- bytecode/locals.hh 13 Jan 2005 03:18:34 -0000 1.1.2.1
+++ bytecode/locals.hh 15 Jan 2005 21:24:50 -0000
@@ -1,6 +1,6 @@
 // Local variable handling for bytecode generation.
 
-// Copyright (C) 2004 Free Software Foundation, Inc.
+// Copyright (C) 2004, 2005 Free Software Foundation, Inc.
 //
 // This file is part of GCC.
 //
@@ -22,13 +22,33 @@
 #ifndef GCJX_BYTECODE_LOCALS_HH
 #define GCJX_BYTECODE_LOCALS_HH
 
+class bytecode_block;
+class bytecode_generator;
+class bytecode_stream;
+class output_constant_pool;
+
+/// This class is used to keep track of the local variable slots while
+/// generating code for a given method.
 class locals
 {
-  // This maps a local variable to its index.
-  std::map<model_variable_decl *, int> indexes;
+  // This represents a single local variable and is used when
+  // generating debug information.
+  struct debug_info
+  {
+    // The variable.
+    model_variable_decl *variable;
+    // The starting block.
+    bytecode_block *start;
+    // The ending block.
+    bytecode_block *end;
+    // The defining scope.
+    const model_stmt *scope;
+    // The index.
+    int index;
+  };
 
-  // This maps a local variable to its defining scope.
-  std::map<model_variable_decl *, const model_stmt *> scope_map;
+  // The corresponding bytecode generator.
+  bytecode_generator *gen;
 
   // This is used as a quick way to deterine whether a slot is in use.
   std::deque<bool> used;
@@ -39,10 +59,18 @@
   // Number of entries ever allocated in 'used'.
   int max;
 
+  // Keep track of a variable while it is live.
+  std::map<model_variable_decl *, debug_info> vars;
+
+  // Keep track of a variable after it is live.  A given variable may
+  // appear several times here, due to how we emit 'finally' clauses.
+  std::list<debug_info> var_descriptions;
+
 public:
 
-  locals ()
-    : max (0)
+  locals (bytecode_generator *g)
+    : gen (g),
+      max (0)
   {
     scope.push_back (NULL);
   }
@@ -78,9 +106,20 @@
   /// This also pops the scope associated with this statement.
   /// @param stmt the enclosing statement
   void remove (const model_stmt *);
-};
 
+  /// Used when generating debug information.  Updates the ranges of
+  /// variables and returns 'true' if any exist to be written out.
+  bool update ();
+
+  /// Used when generating debug information.  Write the actual
+  /// information.  If the writer is NULL, just enter information into
+  /// the constant pool.
+  void emit (output_constant_pool *, bytecode_stream *);
+};
 
+/// This is an class for allocating a temporary local variable.  Usage
+/// is RAII; when the temporary_local is destroyed, the corresponding
+/// local variable slot is reclaimed.
 class temporary_local
 {
   // The variable, or NULL for synthetic.



More information about the Java-patches mailing list