]> sourceware.org Git - systemtap.git/blobdiff - elaborate.cxx
PR 9716, replaced pkgconfig checks with AC macros in configure.ac for server deps.
[systemtap.git] / elaborate.cxx
index e9746cddf92aabb4c412f661a0f06834f0805228..ba50defb427eb9b4565ce4ae76a5e87acc90dbca 100644 (file)
@@ -1,5 +1,6 @@
 // elaboration functions
 // Copyright (C) 2005-2008 Red Hat Inc.
+// Copyright (C) 2008 Intel Corporation
 //
 // This file is part of systemtap, and is free software.  You can
 // redistribute it and/or modify it under the terms of the GNU General
@@ -126,7 +127,7 @@ derived_probe::sole_location () const
     throw semantic_error ("derived_probe with no locations", this->tok);
   else if (locations.size() > 1)
     throw semantic_error ("derived_probe with too many locations", this->tok);
-  else 
+  else
     return locations[0];
 }
 
@@ -182,9 +183,9 @@ derived_probe_builder::has_null_param (std::map<std::string, literal*> const & p
 // ------------------------------------------------------------------------
 // Members of match_key.
 
-match_key::match_key(string const & n) 
-  : name(n), 
-    have_parameter(false), 
+match_key::match_key(string const & n)
+  : name(n),
+    have_parameter(false),
     parameter_type(pe_unknown)
 {
 }
@@ -197,7 +198,7 @@ match_key::match_key(probe_point::component const & c)
 }
 
 match_key &
-match_key::with_number() 
+match_key::with_number()
 {
   have_parameter = true;
   parameter_type = pe_long;
@@ -205,14 +206,14 @@ match_key::with_number()
 }
 
 match_key &
-match_key::with_string() 
+match_key::with_string()
 {
   have_parameter = true;
   parameter_type = pe_string;
   return *this;
 }
 
-string 
+string
 match_key::str() const
 {
   if (have_parameter)
@@ -225,16 +226,16 @@ match_key::str() const
   return name;
 }
 
-bool 
+bool
 match_key::operator<(match_key const & other) const
 {
   return ((name < other.name)
-         
-         || (name == other.name 
+
+         || (name == other.name
              && have_parameter < other.have_parameter)
-         
-         || (name == other.name 
-             && have_parameter == other.have_parameter 
+
+         || (name == other.name
+             && have_parameter == other.have_parameter
              && parameter_type < other.parameter_type));
 }
 
@@ -251,7 +252,7 @@ match_key::globmatch(match_key const & other) const
   const char *name_str = name.c_str();
 
   return ((fnmatch(name_str, other_str, FNM_NOESCAPE) == 0)
-         && have_parameter == other.have_parameter 
+         && have_parameter == other.have_parameter
          && parameter_type == other.parameter_type);
 }
 
@@ -264,7 +265,7 @@ match_node::match_node()
 {}
 
 match_node *
-match_node::bind(match_key const & k) 
+match_node::bind(match_key const & k)
 {
   if (k.name == "*")
     throw semantic_error("invalid use of wildcard probe point component");
@@ -277,7 +278,7 @@ match_node::bind(match_key const & k)
   return n;
 }
 
-void 
+void
 match_node::bind(derived_probe_builder * e)
 {
   if (end)
@@ -285,7 +286,7 @@ match_node::bind(derived_probe_builder * e)
   end = e;
 }
 
-match_node * 
+match_node *
 match_node::bind(string const & k)
 {
   return bind(match_key(k));
@@ -297,7 +298,7 @@ match_node::bind_str(string const & k)
   return bind(match_key(k).with_string());
 }
 
-match_node * 
+match_node *
 match_node::bind_num(string const & k)
 {
   return bind(match_key(k).with_number());
@@ -310,7 +311,7 @@ match_node::find_and_build (systemtap_session& s,
                             vector<derived_probe *>& results)
 {
   assert (pos <= loc->components.size());
-  if (pos == loc->components.size()) // matched all probe point components so far 
+  if (pos == loc->components.size()) // matched all probe point components so far
     {
       derived_probe_builder *b = end; // may be 0 if only nested names are bound
 
@@ -320,9 +321,9 @@ match_node::find_and_build (systemtap_session& s,
           for (sub_map_iterator_t i = sub.begin(); i != sub.end(); i++)
             alternatives += string(" ") + i->first.str();
 
-          throw semantic_error (string("probe point truncated at position ") + 
+          throw semantic_error (string("probe point truncated at position ") +
                                 lex_cast<string> (pos) +
-                                " (follow:" + alternatives + ")");
+                                " (follow:" + alternatives + ")", loc->tok);
         }
 
       map<string, literal *> param_map;
@@ -391,13 +392,15 @@ match_node::find_and_build (systemtap_session& s,
           string alternatives;
           for (sub_map_iterator_t i = sub.begin(); i != sub.end(); i++)
             alternatives += string(" ") + i->first.str();
-          
+
          throw semantic_error(string("probe point mismatch at position ") +
                               lex_cast<string> (pos) +
-                              " (alternatives:" + alternatives + ")");
+                              " (alternatives:" + alternatives + ")" +
+                              " didn't find any wildcard matches",
+                               loc->tok);
        }
     }
-  else 
+  else
     {
       match_key match (* loc->components[pos]);
       sub_map_iterator_t i = sub.find (match);
@@ -406,10 +409,11 @@ match_node::find_and_build (systemtap_session& s,
           string alternatives;
           for (sub_map_iterator_t i = sub.begin(); i != sub.end(); i++)
             alternatives += string(" ") + i->first.str();
-          
-          throw semantic_error (string("probe point mismatch at position ") + 
+
+          throw semantic_error (string("probe point mismatch at position ") +
                                 lex_cast<string> (pos) +
-                                " (alternatives:" + alternatives + ")");
+                                " (alternatives:" + alternatives + ")",
+                                loc->tok);
         }
 
       match_node* subnode = i->second;
@@ -453,17 +457,17 @@ private:
 
 
 struct
-alias_expansion_builder 
+alias_expansion_builder
   : public derived_probe_builder
 {
   probe_alias * alias;
 
-  alias_expansion_builder(probe_alias * a) 
+  alias_expansion_builder(probe_alias * a)
     : alias(a)
   {}
 
   virtual void build(systemtap_session & sess,
-                    probe * use, 
+                    probe * use,
                     probe_point * location,
                     std::map<std::string, literal *> const &,
                     vector<derived_probe *> & finished_results)
@@ -480,7 +484,7 @@ alias_expansion_builder
     // We're going to build a new probe and wrap it up in an
     // alias_expansion_probe so that the expansion loop recognizes it as
     // such and re-expands its expansion.
-    
+
     alias_derived_probe * n = new alias_derived_probe (use, location /* soon overwritten */, this->alias);
     n->body = new block();
 
@@ -494,7 +498,7 @@ alias_expansion_builder
     n->tok = location->tok;
 
     // and statements representing the concatenation of the alias'
-    // body with the use's. 
+    // body with the use's.
     //
     // NB: locals are *not* copied forward, from either alias or
     // use. The expansion should have its locals re-inferred since
@@ -505,7 +509,7 @@ alias_expansion_builder
       n->body = new block (use->body, alias->body);
     else
       n->body = new block (alias->body, use->body);
-    
+
     derive_probes (sess, n, finished_results, location->optional);
   }
 
@@ -551,7 +555,7 @@ systemtap_session::register_library_aliases()
       for (unsigned a = 0; a < file->aliases.size(); ++a)
        {
          probe_alias * alias = file->aliases[a];
-          try 
+          try
             {
               for (unsigned n = 0; n < alias->alias_names.size(); ++n)
                 {
@@ -562,8 +566,8 @@ systemtap_session::register_library_aliases()
                       probe_point::component * comp = name->components[c];
                       // XXX: alias parameters
                       if (comp->arg)
-                        throw semantic_error("alias component " 
-                                             + comp->functor 
+                        throw semantic_error("alias component "
+                                             + comp->functor
                                              + " contains illegal parameter");
                       n = n->bind(comp->functor);
                     }
@@ -588,7 +592,7 @@ systemtap_session::register_library_aliases()
 
 static unsigned max_recursion = 100;
 
-struct 
+struct
 recursion_guard
 {
   unsigned & i;
@@ -598,7 +602,7 @@ recursion_guard
        throw semantic_error("recursion limit reached");
       ++i;
     }
-  ~recursion_guard() 
+  ~recursion_guard()
     {
       --i;
     }
@@ -679,7 +683,7 @@ struct symbol_fetcher
 {
   symbol *&sym;
 
-  symbol_fetcher (symbol *&sym): sym(sym) 
+  symbol_fetcher (symbol *&sym): sym(sym)
   {}
 
   void visit_symbol (symbol* e)
@@ -729,7 +733,7 @@ struct mutated_var_collector
 {
   set<vardecl *> * mutated_vars;
 
-  mutated_var_collector (set<vardecl *> * mm) 
+  mutated_var_collector (set<vardecl *> * mm)
     : mutated_vars (mm)
   {}
 
@@ -765,8 +769,8 @@ struct no_var_mutation_during_iteration_check
   systemtap_session & session;
   map<functiondecl *,set<vardecl *> *> & function_mutates_vars;
   vector<vardecl *> vars_being_iterated;
-  
-  no_var_mutation_during_iteration_check 
+
+  no_var_mutation_during_iteration_check
   (systemtap_session & sess,
    map<functiondecl *,set<vardecl *> *> & fmv)
     : session(sess), function_mutates_vars (fmv)
@@ -796,7 +800,7 @@ struct no_var_mutation_during_iteration_check
 
   void visit_functioncall (functioncall* e)
   {
-    map<functiondecl *,set<vardecl *> *>::const_iterator i 
+    map<functiondecl *,set<vardecl *> *>::const_iterator i
       = function_mutates_vars.find (e->referent);
 
     if (i != function_mutates_vars.end())
@@ -822,7 +826,7 @@ struct no_var_mutation_during_iteration_check
 
     if (vd)
       vars_being_iterated.push_back (vd);
-    
+
     traversing_visitor::visit_foreach_loop (s);
 
     if (vd)
@@ -837,7 +841,7 @@ struct stat_decl_collector
   : public traversing_visitor
 {
   systemtap_session & session;
-  
+
   stat_decl_collector(systemtap_session & sess)
     : session(sess)
   {}
@@ -894,12 +898,12 @@ struct stat_decl_collector
            else
              {
                // FIXME: Support multiple co-declared histogram types
-               semantic_error se("multiple histogram types declared on '" + sym->name + "'", 
+               semantic_error se("multiple histogram types declared on '" + sym->name + "'",
                                  e->tok);
                session.print_error (se);
              }
          }
-      }    
+      }
   }
 
 };
@@ -909,10 +913,10 @@ semantic_pass_stats (systemtap_session & sess)
 {
   stat_decl_collector sdc(sess);
 
-  for (unsigned i = 0; i < sess.functions.size(); ++i)    
-    sess.functions[i]->body->visit (&sdc);
+  for (map<string,functiondecl*>::iterator it = sess.functions.begin(); it != sess.functions.end(); it++)
+    it->second->body->visit (&sdc);
 
-  for (unsigned i = 0; i < sess.probes.size(); ++i)    
+  for (unsigned i = 0; i < sess.probes.size(); ++i)
     sess.probes[i]->body->visit (&sdc);
 
   for (unsigned i = 0; i < sess.globals.size(); ++i)
@@ -920,7 +924,7 @@ semantic_pass_stats (systemtap_session & sess)
       vardecl *v = sess.globals[i];
       if (v->type == pe_stats)
        {
-         
+
          if (sess.stat_decls.find(v->name) == sess.stat_decls.end())
            {
              semantic_error se("unable to infer statistic parameters for global '" + v->name + "'");
@@ -928,7 +932,7 @@ semantic_pass_stats (systemtap_session & sess)
            }
        }
     }
-  
+
   return sess.num_errors();
 }
 
@@ -939,13 +943,13 @@ semantic_pass_stats (systemtap_session & sess)
 static int
 semantic_pass_vars (systemtap_session & sess)
 {
-  
+
   map<functiondecl *, set<vardecl *> *> fmv;
   no_var_mutation_during_iteration_check chk(sess, fmv);
-  
-  for (unsigned i = 0; i < sess.functions.size(); ++i)
+
+  for (map<string,functiondecl*>::iterator it = sess.functions.begin(); it != sess.functions.end(); it++)
     {
-      functiondecl * fn = sess.functions[i];
+      functiondecl * fn = it->second;
       if (fn->body)
        {
          set<vardecl *> * m = new set<vardecl *>();
@@ -955,17 +959,17 @@ semantic_pass_vars (systemtap_session & sess)
        }
     }
 
-  for (unsigned i = 0; i < sess.functions.size(); ++i)
+  for (map<string,functiondecl*>::iterator it = sess.functions.begin(); it != sess.functions.end(); it++)
     {
-      if (sess.functions[i]->body)
-       sess.functions[i]->body->visit (&chk);
+      functiondecl * fn = it->second;
+      if (fn->body) fn->body->visit (&chk);
     }
 
   for (unsigned i = 0; i < sess.probes.size(); ++i)
     {
       if (sess.probes[i]->body)
        sess.probes[i]->body->visit (&chk);
-    }  
+    }
 
   return sess.num_errors();
 }
@@ -983,7 +987,7 @@ semantic_pass_vars (systemtap_session & sess)
 //
 // probe begin(MAX) { if (! (g1 || g2)) %{ disable_probe_foo %} }
 // probe foo { if (! (g1 || g2)) next; ... }
-// probe bar { ... g1 ++ ...; 
+// probe bar { ... g1 ++ ...;
 //             if (g1 || g2) %{ enable_probe_foo %} else %{ disable_probe_foo %}
 //           }
 //
@@ -1026,7 +1030,7 @@ semantic_pass_conditions (systemtap_session & sess)
           ifs->condition = notex;
           p->body = new block (ifs, p->body);
         }
-    }  
+    }
 
   return sess.num_errors();
 }
@@ -1069,7 +1073,7 @@ semantic_pass_symbols (systemtap_session& s)
         s.globals.push_back (dome->globals[i]);
 
       for (unsigned i=0; i<dome->functions.size(); i++)
-        s.functions.push_back (dome->functions[i]);
+        s.functions[dome->functions[i]->name] = dome->functions[i];
 
       for (unsigned i=0; i<dome->embeds.size(); i++)
         s.embeds.push_back (dome->embeds[i]);
@@ -1081,7 +1085,7 @@ semantic_pass_symbols (systemtap_session& s)
           if (pending_interrupts) break;
           functiondecl* fd = dome->functions[i];
 
-          try 
+          try
             {
               sym.current_function = fd;
               sym.current_probe = 0;
@@ -1094,7 +1098,7 @@ semantic_pass_symbols (systemtap_session& s)
         }
 
       // Pass 3: derive probes and resolve any further symbols in the
-      // derived results. 
+      // derived results.
 
       for (unsigned i=0; i<dome->probes.size(); i++)
         {
@@ -1113,7 +1117,7 @@ semantic_pass_symbols (systemtap_session& s)
               s.probes.push_back (dp);
               dp->join_group (s);
 
-              try 
+              try
                 {
                   sym.current_function = 0;
                   sym.current_probe = dp;
@@ -1136,30 +1140,274 @@ semantic_pass_symbols (systemtap_session& s)
   // Inform all derived_probe builders that we're done with
   // all resolution, so it's time to release caches.
   s.pattern_root->build_no_more (s);
-  
+
   return s.num_errors(); // all those print_error calls
 }
 
 
+// Keep unread global variables for probe end value display.
+void add_global_var_display (systemtap_session& s)
+{
+  // Don't generate synthetic end probes when in listings mode;
+  // it would clutter up the list of probe points with "end ...".
+  if (s.listing_mode) return;
+
+  varuse_collecting_visitor vut;
+  for (unsigned i=0; i<s.probes.size(); i++)
+    {
+      s.probes[i]->body->visit (& vut);
+
+      if (s.probes[i]->sole_location()->condition)
+       s.probes[i]->sole_location()->condition->visit (& vut);
+    }
+
+  for (unsigned g=0; g < s.globals.size(); g++)
+    {
+      vardecl* l = s.globals[g];
+      if (vut.read.find (l) != vut.read.end()
+          || vut.written.find (l) == vut.written.end())
+       continue;
+
+      // Don't generate synthetic end probes for unread globals
+      // declared only within tapsets. (RHBZ 468139), but rather
+      // only within the end-user script.
+
+      bool tapset_global = false;
+      for (size_t m=0; m < s.library_files.size(); m++)
+       {
+         for (size_t n=0; n < s.library_files[m]->globals.size(); n++)
+           {
+             if (l->name == s.library_files[m]->globals[n]->name)
+               {tapset_global = true; break;}
+           }
+       }
+      if (tapset_global)
+       continue;
+
+      print_format* pf = new print_format;
+      probe* p = new probe;
+      probe_point* pl = new probe_point;
+      probe_point::component* c = new probe_point::component("end");
+      token* print_tok = new token;
+      vector<derived_probe*> dps;
+      block *b = new block;
+
+      pl->components.push_back (c);
+      p->tok = l->tok;
+      p->locations.push_back (pl);
+      print_tok->type = tok_identifier;
+      print_tok->content = "printf";
+
+      // Create a symbol
+      symbol* g_sym = new symbol;
+      g_sym->name = l->name;
+      g_sym->tok = l->tok;
+      g_sym->type = l->type;
+      g_sym->referent = l;
+
+      pf->print_to_stream = true;
+      pf->print_with_format = true;
+      pf->print_with_delim = false;
+      pf->print_with_newline = false;
+      pf->print_char = false;
+      pf->raw_components += l->name;
+      pf->tok = print_tok;
+
+      if (l->index_types.size() == 0) // Scalar
+       {
+         if (l->type == pe_stats)
+           pf->raw_components += " @count=%#x @min=%#x @max=%#x @sum=%#x @avg=%#x\\n";
+         else if (l->type == pe_string)
+           pf->raw_components += "=\"%#s\"\\n";
+         else
+           pf->raw_components += "=%#x\\n";
+         pf->components = print_format::string_to_components(pf->raw_components);
+         expr_statement* feb = new expr_statement;
+         feb->value = pf;
+         feb->tok = print_tok;
+         if (l->type == pe_stats)
+           {
+             struct stat_op* so [5];
+             const stat_component_type stypes[] = {sc_count, sc_min, sc_max, sc_sum, sc_average};
+
+             for (unsigned si = 0;
+                  si < (sizeof(so)/sizeof(struct stat_op*));
+                  si++)
+               {
+                 so[si]= new stat_op;
+                 so[si]->ctype = stypes[si];
+                 so[si]->type = pe_long;
+                 so[si]->stat = g_sym;
+                 so[si]->tok = l->tok;
+                 pf->args.push_back(so[si]);
+               }
+           }
+         else
+           pf->args.push_back(g_sym);
+
+         /* PR7053: Checking empty aggregate for global variable */
+         if (l->type == pe_stats) {
+              stat_op *so= new stat_op;
+              so->ctype = sc_count;
+              so->type = pe_long;
+              so->stat = g_sym;
+              so->tok = l->tok;
+              comparison *be = new comparison;
+              be->op = ">";
+              be->tok = l->tok;
+              be->left = so;
+              be->right = new literal_number(0);
+
+              /* Create printf @count=0x0 in else block */
+              print_format* pf_0 = new print_format;
+              pf_0->print_to_stream = true;
+              pf_0->print_with_format = true;
+              pf_0->print_with_delim = false;
+              pf_0->print_with_newline = false;
+              pf_0->print_char = false;
+              pf_0->raw_components += l->name;
+              pf_0->raw_components += " @count=0x0\\n";
+              pf_0->tok = print_tok;
+              pf_0->components = print_format::string_to_components(pf_0->raw_components);
+              expr_statement* feb_else = new expr_statement;
+              feb_else->value = pf_0;
+              feb_else->tok = print_tok;
+              if_statement *ifs = new if_statement;
+              ifs->tok = l->tok;
+              ifs->condition = be;
+              ifs->thenblock = feb ;
+              ifs->elseblock = feb_else;
+              b->statements.push_back(ifs);
+           }
+         else /* other non-stat cases */
+           b->statements.push_back(feb);
+       }
+      else                     // Array
+       {
+         int idx_count = l->index_types.size();
+         symbol* idx_sym[idx_count];
+         vardecl* idx_v[idx_count];
+         // Create a foreach loop
+         foreach_loop* fe = new foreach_loop;
+         fe->sort_direction = -1; // imply decreasing sort on value
+         fe->sort_column = 0;     // as in   foreach ([a,b,c] in array-) { }
+         fe->limit = NULL;
+
+         // Create indices for the foreach loop
+         for (int i=0; i < idx_count; i++)
+           {
+             char *idx_name;
+             if (asprintf (&idx_name, "idx%d", i) < 0)
+               return;
+             idx_sym[i] = new symbol;
+             idx_sym[i]->name = idx_name;
+             idx_sym[i]->tok = l->tok;
+             idx_v[i] = new vardecl;
+             idx_v[i]->name = idx_name;
+             idx_v[i]->type = l->index_types[i];
+             idx_v[i]->tok = l->tok;
+             idx_sym[i]->referent = idx_v[i];
+             fe->indexes.push_back (idx_sym[i]);
+           }
+
+         // Create a printf for the foreach loop
+          pf->raw_components += "[";
+         for (int i=0; i < idx_count; i++)
+            {
+              if (i > 0)
+                pf->raw_components += ",";
+              if (l->index_types[i] == pe_string)
+                pf->raw_components += "\"%#s\"";
+              else
+                pf->raw_components += "%#d";
+            }
+          pf->raw_components += "]";
+         if (l->type == pe_stats)
+           pf->raw_components += " @count=%#x @min=%#x @max=%#x @sum=%#x @avg=%#x\\n";
+         else if (l->type == pe_string)
+           pf->raw_components += "=\"%#s\"\\n";
+         else
+           pf->raw_components += "=%#x\\n";
+
+         // Create an index for the array
+         struct arrayindex* ai = new arrayindex;
+         ai->tok = l->tok;
+         ai->base = g_sym;
+
+         for (int i=0; i < idx_count; i++)
+           {
+             ai->indexes.push_back (idx_sym[i]);
+             pf->args.push_back(idx_sym[i]);
+           }
+         if (l->type == pe_stats)
+           {
+             struct stat_op* so [5];
+             const stat_component_type stypes[] = {sc_count, sc_min, sc_max, sc_sum, sc_average};
+
+             ai->type = pe_stats;
+             for (unsigned si = 0;
+                  si < (sizeof(so)/sizeof(struct stat_op*));
+                  si++)
+               {
+                 so[si]= new stat_op;
+                 so[si]->ctype = stypes[si];
+                 so[si]->type = pe_long;
+                 so[si]->stat = ai;
+                 so[si]->tok = l->tok;
+                 pf->args.push_back(so[si]);
+               }
+           }
+         else
+           pf->args.push_back(ai);
+
+         pf->components = print_format::string_to_components(pf->raw_components);
+         expr_statement* feb = new expr_statement;
+         feb->value = pf;
+         fe->base = g_sym;
+         fe->block = (statement*)feb;
+         b->statements.push_back(fe);
+       }
+
+      // Add created probe
+      p->body = b;
+      derive_probes (s, p, dps);
+      for (unsigned i = 0; i < dps.size(); i++)
+       {
+         derived_probe* dp = dps[i];
+         s.probes.push_back (dp);
+         dp->join_group (s);
+       }
+      // Repopulate symbol and type info
+      symresolution_info sym (s);
+      sym.current_function = 0;
+      sym.current_probe = dps[0];
+      dps[0]->body->visit (& sym);
+
+      semantic_pass_types(s);
+      // Mark that variable is read
+      vut.read.insert (l);
+    }
+}
 
 int
 semantic_pass (systemtap_session& s)
 {
   int rc = 0;
 
-  try 
+  try
     {
       s.register_library_aliases();
       register_standard_tapsets(s);
-      
+
       if (rc == 0) rc = semantic_pass_symbols (s);
       if (rc == 0) rc = semantic_pass_conditions (s);
       if (rc == 0 && ! s.unoptimized) rc = semantic_pass_optimize1 (s);
       if (rc == 0) rc = semantic_pass_types (s);
+      if (rc == 0) add_global_var_display (s);
       if (rc == 0 && ! s.unoptimized) rc = semantic_pass_optimize2 (s);
       if (rc == 0) rc = semantic_pass_vars (s);
       if (rc == 0) rc = semantic_pass_stats (s);
-      
+
       if (s.probes.size() == 0 && !s.listing_mode)
         throw semantic_error ("no probes found");
     }
@@ -1168,7 +1416,7 @@ semantic_pass (systemtap_session& s)
       s.print_error (e);
       rc ++;
     }
-  
+
   return rc;
 }
 
@@ -1180,48 +1428,99 @@ systemtap_session::systemtap_session ():
   // NB: pointer members must be manually initialized!
   pattern_root(new match_node),
   user_file (0),
-  be_derived_probes(0), 
-  dwarf_derived_probes(0), 
-  uprobe_derived_probes(0), 
-  utrace_derived_probes(0), 
-  task_finder_derived_probes(0), 
-  timer_derived_probes(0), 
-  profile_derived_probes(0), 
-  mark_derived_probes(0), 
-  hrtimer_derived_probes(0), 
-  perfmon_derived_probes(0), 
-  procfs_derived_probes(0), 
+  be_derived_probes(0),
+  dwarf_derived_probes(0),
+  uprobe_derived_probes(0),
+  utrace_derived_probes(0),
+  itrace_derived_probes(0),
+  task_finder_derived_probes(0),
+  timer_derived_probes(0),
+  profile_derived_probes(0),
+  mark_derived_probes(0),
+  hrtimer_derived_probes(0),
+  perfmon_derived_probes(0),
+  procfs_derived_probes(0),
   op (0), up (0),
   sym_kprobes_text_start (0),
   sym_kprobes_text_end (0),
-  sym_stext (0)
+  sym_stext (0),
+  module_cache (0),
+  last_token (0)
 {
 }
 
 
+// Print this given token, but abbreviate it if the last one had the
+// same file name.
+void
+systemtap_session::print_token (ostream& o, const token* tok)
+{
+  assert (tok);
+
+  if (last_token && last_token->location.file == tok->location.file)
+    {
+      stringstream tmpo;
+      tmpo << *tok;
+      string ts = tmpo.str();
+      // search & replace the file name with nothing
+      size_t idx = ts.find (tok->location.file);
+      if (idx != string::npos)
+          ts.replace (idx, tok->location.file.size(), "");
+
+      o << ts;
+    }
+  else
+    o << *tok;
+
+  last_token = tok;
+}
+
+
+
 void
 systemtap_session::print_error (const semantic_error& e)
 {
-  string message_str;
-  stringstream message;
+  string message_str[2];
+  string align_semantic_error ("        ");
 
-  // NB: we don't print error messages during listing mode.
-  if (listing_mode) return;
+  // We generate two messages.  The second one ([1]) is printed
+  // without token compression, for purposes of duplicate elimination.
+  // This way, the same message that may be generated once with a
+  // compressed and once with an uncompressed token still only gets
+  // printed once.
+  for (int i=0; i<2; i++)
+    {
+      stringstream message;
 
-  message << "semantic error: " << e.what ();
-  if (e.tok1 || e.tok2)
-    message << ": ";
-  if (e.tok1) message << *e.tok1;
-  message << e.msg2;
-  if (e.tok2) message << *e.tok2;
-  message << endl;
-  message_str = message.str();
+      message << "semantic error: " << e.what ();
+      if (e.tok1 || e.tok2)
+        message << ": ";
+      if (e.tok1)
+        {
+          if (i == 0) print_token (message, e.tok1);
+          else message << *e.tok1;
+        }
+      message << e.msg2;
+      if (e.tok2)
+        {
+          if (i == 0) print_token (message, e.tok2);
+          else message << *e.tok2;
+        }
+      message << endl;
+      message_str[i] = message.str();
+    }
 
   // Duplicate elimination
-  if (seen_errors.find (message_str) == seen_errors.end())
+  if (seen_errors.find (message_str[1]) == seen_errors.end())
     {
-      seen_errors.insert (message_str);
-      cerr << message_str;
+      seen_errors.insert (message_str[1]);
+      cerr << message_str[0];
+
+      if (e.tok1)
+        print_error_source (cerr, align_semantic_error, e.tok1);
+
+      if (e.tok2)
+        print_error_source (cerr, align_semantic_error, e.tok2);
     }
 
   if (e.chain)
@@ -1229,13 +1528,53 @@ systemtap_session::print_error (const semantic_error& e)
 }
 
 void
-systemtap_session::print_warning (const string& message_str)
+systemtap_session::print_error_source (std::ostream& message,
+                                       std::string& align, const token* tok)
+{
+  unsigned i = 0;
+  unsigned line = tok->location.line;
+  unsigned col = tok->location.column;
+  string file_contents;
+
+  assert (tok);
+  if (tok->location.stap_file)
+    file_contents = tok->location.stap_file->file_contents;
+  else
+    //No source to print, silently exit
+    return;
+  size_t start_pos = 0, end_pos = 0;
+  //Navigate to the appropriate line
+  while (i != line && end_pos != std::string::npos)
+    {
+      start_pos = end_pos;
+      end_pos = file_contents.find ('\n', start_pos) + 1;
+      i++;
+    }
+  message << align << "source: " << file_contents.substr (start_pos, end_pos-start_pos-1) << endl;
+  message << align << "        ";
+  //Navigate to the appropriate column
+  for (i=start_pos; i<start_pos+col-1; i++)
+    {
+      if(isspace(file_contents[i]))
+       message << file_contents[i];
+      else
+       message << ' ';
+    }
+  message << "^" << endl;
+}
+
+void
+systemtap_session::print_warning (const string& message_str, const token* tok)
 {
   // Duplicate elimination
+  string align_warning (" ");
   if (seen_warnings.find (message_str) == seen_warnings.end())
     {
       seen_warnings.insert (message_str);
-      clog << "WARNING: " << message_str << endl;
+      clog << "WARNING: " << message_str;
+      if (tok) { clog << ": "; print_token (clog, tok); }
+      clog << endl;
+      if (tok) { print_error_source (clog, align_warning, tok); }
     }
 }
 
@@ -1255,7 +1594,7 @@ symresolution_info::visit_block (block* e)
 {
   for (unsigned i=0; i<e->statements.size(); i++)
     {
-      try 
+      try
        {
          e->statements[i]->visit (this);
        }
@@ -1273,14 +1612,14 @@ symresolution_info::visit_foreach_loop (foreach_loop* e)
   for (unsigned i=0; i<e->indexes.size(); i++)
     e->indexes[i]->visit (this);
 
-  symbol *array = NULL;  
+  symbol *array = NULL;
   hist_op *hist = NULL;
   classify_indexable (e->base, array, hist);
 
   if (array)
     {
       if (!array->referent)
-       {         
+       {
          vardecl* d = find_var (array->name, e->indexes.size ());
          if (d)
            array->referent = d;
@@ -1293,7 +1632,7 @@ symresolution_info::visit_foreach_loop (foreach_loop* e)
             }
        }
     }
-  else 
+  else
     {
       assert (hist);
       hist->visit (this);
@@ -1306,7 +1645,7 @@ symresolution_info::visit_foreach_loop (foreach_loop* e)
 }
 
 
-struct 
+struct
 delete_statement_symresolution_info:
   public traversing_visitor
 {
@@ -1329,7 +1668,7 @@ delete_statement_symresolution_info:
   {
     if (e->referent)
       return;
-    
+
     vardecl* d = parent->find_var (e->name, -1);
     if (d)
       e->referent = d;
@@ -1338,7 +1677,7 @@ delete_statement_symresolution_info:
   }
 };
 
-void 
+void
 symresolution_info::visit_delete_statement (delete_statement* s)
 {
   delete_statement_symresolution_info di (this);
@@ -1379,7 +1718,7 @@ symresolution_info::visit_arrayindex (arrayindex* e)
   for (unsigned i=0; i<e->indexes.size(); i++)
     e->indexes[i]->visit (this);
 
-  symbol *array = NULL;  
+  symbol *array = NULL;
   hist_op *hist = NULL;
   classify_indexable(e->base, array, hist);
 
@@ -1406,7 +1745,7 @@ symresolution_info::visit_arrayindex (arrayindex* e)
            // must not happen
            throw semantic_error ("no current probe/function", e->tok);
          array->referent = v;
-       }      
+       }
     }
   else
     {
@@ -1446,19 +1785,19 @@ symresolution_info::visit_functioncall (functioncall* e)
 }
 
 
-vardecl* 
+vardecl*
 symresolution_info::find_var (const string& name, int arity)
 {
   if (current_function || current_probe)
     {
       // search locals
-      vector<vardecl*>& locals = (current_function ? 
+      vector<vardecl*>& locals = (current_function ?
                                   current_function->locals :
                                   current_probe->locals);
-      
-      
+
+
       for (unsigned i=0; i<locals.size(); i++)
-        if (locals[i]->name == name 
+        if (locals[i]->name == name
             && locals[i]->compatible_arity(arity))
           {
             locals[i]->set_arity (arity);
@@ -1479,12 +1818,12 @@ symresolution_info::find_var (const string& name, int arity)
   // search processed globals
   for (unsigned i=0; i<session.globals.size(); i++)
     if (session.globals[i]->name == name
-       && session.globals[i]->compatible_arity(arity))  
+       && session.globals[i]->compatible_arity(arity))
       {
        session.globals[i]->set_arity (arity);
        return session.globals[i];
       }
-  
+
   // search library globals
   for (unsigned i=0; i<session.library_files.size(); i++)
     {
@@ -1495,12 +1834,12 @@ symresolution_info::find_var (const string& name, int arity)
           if (g->name == name && g->compatible_arity (arity))
             {
              g->set_arity (arity);
-              
-              // put library into the queue if not already there           
-              if (find (session.files.begin(), session.files.end(), f) 
+
+              // put library into the queue if not already there
+              if (find (session.files.begin(), session.files.end(), f)
                   == session.files.end())
                 session.files.push_back (f);
-              
+
               return g;
             }
         }
@@ -1510,14 +1849,15 @@ symresolution_info::find_var (const string& name, int arity)
 }
 
 
-functiondecl* 
+functiondecl*
 symresolution_info::find_function (const string& name, unsigned arity)
 {
-  for (unsigned j = 0; j < session.functions.size(); j++)
+  // the common path
+  if (session.functions.find(name) != session.functions.end())
     {
-      functiondecl* fd = session.functions[j];
-      if (fd->name == name &&
-          fd->formal_args.size() == arity)
+      functiondecl* fd = session.functions[name];
+      assert (fd->name == name);
+      if (fd->formal_args.size() == arity)
         return fd;
     }
 
@@ -1534,7 +1874,7 @@ symresolution_info::find_function (const string& name, unsigned arity)
               cerr << "      function " << name << " "
                    << "is defined from " << f->name << endl;
 
-            if (find (session.files.begin(), session.files.end(), f) 
+            if (find (session.files.begin(), session.files.end(), f)
                 == session.files.end())
               session.files.push_back (f);
             // else .. print different message?
@@ -1563,25 +1903,30 @@ void semantic_pass_opt1 (systemtap_session& s, bool& relaxed_p)
       if (s.probes[i]->sole_location()->condition)
         s.probes[i]->sole_location()->condition->visit (& ftv);
     }
-  for (unsigned i=0; i<s.functions.size(); /* see below */)
+  vector<functiondecl*> new_unused_functions;
+  for (map<string,functiondecl*>::iterator it = s.functions.begin(); it != s.functions.end(); it++)
     {
-      if (ftv.traversed.find(s.functions[i]) == ftv.traversed.end())
+      functiondecl* fd = it->second;
+      if (ftv.traversed.find(fd) == ftv.traversed.end())
         {
-          if (s.functions[i]->tok->location.file == s.user_file->name && // !tapset
+          if (fd->tok->location.file == s.user_file->name && // !tapset
               ! s.suppress_warnings)
-           s.print_warning ("eliding unused function " + stringify(*s.functions[i]->tok));
+           s.print_warning ("eliding unused function '" + fd->name + "'", fd->tok);
           else if (s.verbose>2)
-            clog << "Eliding unused function " << s.functions[i]->name
+            clog << "Eliding unused function " << fd->name
                  << endl;
-         if (s.tapset_compile_coverage) {
-           s.unused_functions.push_back (s.functions[i]);
-         }
-          s.functions.erase (s.functions.begin() + i);
+          // s.functions.erase (it); // NB: can't, since we're already iterating upon it
+          new_unused_functions.push_back (fd);
           relaxed_p = false;
-          // NB: don't increment i
         }
-      else
-        i++;
+    }
+  for (unsigned i=0; i<new_unused_functions.size(); i++)
+    {
+      map<string,functiondecl*>::iterator where = s.functions.find (new_unused_functions[i]->name);
+      assert (where != s.functions.end());
+      s.functions.erase (where);
+      if (s.tapset_compile_coverage)
+        s.unused_functions.push_back (new_unused_functions[i]);
     }
 }
 
@@ -1590,10 +1935,10 @@ void semantic_pass_opt1 (systemtap_session& s, bool& relaxed_p)
 
 // Do away with local & global variables that are never
 // written nor read.
-void semantic_pass_opt2 (systemtap_session& s, bool& relaxed_p)
+void semantic_pass_opt2 (systemtap_session& s, bool& relaxed_p, unsigned iterations)
 {
   varuse_collecting_visitor vut;
-  
+
   for (unsigned i=0; i<s.probes.size(); i++)
     {
       s.probes[i]->body->visit (& vut);
@@ -1609,9 +1954,9 @@ void semantic_pass_opt2 (systemtap_session& s, bool& relaxed_p)
   //
   // for (unsigned i=0; i<s.functions.size(); i++)
   //   s.functions[i]->body->visit (& vut);
-  
+
   // Now in vut.read/written, we have a mixture of all locals, globals
-  
+
   for (unsigned i=0; i<s.probes.size(); i++)
     for (unsigned j=0; j<s.probes[i]->locals.size(); /* see below */)
       {
@@ -1622,7 +1967,7 @@ void semantic_pass_opt2 (systemtap_session& s, bool& relaxed_p)
           {
             if (l->tok->location.file == s.user_file->name && // !tapset
                 ! s.suppress_warnings)
-             s.print_warning ("eliding unused variable " + stringify(*l->tok));
+             s.print_warning ("eliding unused variable '" + l->name + "'", l->tok);
             else if (s.verbose>2)
               clog << "Eliding unused local variable "
                    << l->name << " in " << s.probes[i]->name << endl;
@@ -1637,42 +1982,73 @@ void semantic_pass_opt2 (systemtap_session& s, bool& relaxed_p)
         else
           {
             if (vut.written.find (l) == vut.written.end())
-              if (! s.suppress_warnings)
-               s.print_warning ("read-only local variable " + stringify(*l->tok));
-
-            j++;
-          }
-      }
-  for (unsigned i=0; i<s.functions.size(); i++)
-    for (unsigned j=0; j<s.functions[i]->locals.size(); /* see below */)
-      {
-        vardecl* l = s.functions[i]->locals[j];
-        if (vut.read.find (l) == vut.read.end() &&
-            vut.written.find (l) == vut.written.end())
-          {
-            if (l->tok->location.file == s.user_file->name && // !tapset
-                ! s.suppress_warnings)
-              s.print_warning ("eliding unused variable " + stringify(*l->tok));
-            else if (s.verbose>2)
-              clog << "Eliding unused local variable "
-                   << l->name << " in function " << s.functions[i]->name
-                   << endl;
-           if (s.tapset_compile_coverage) {
-              s.functions[i]->unused_locals.push_back
-                   (s.functions[i]->locals[j]);
-           }
-            s.functions[i]->locals.erase(s.functions[i]->locals.begin() + j);
-            relaxed_p = false;
-            // don't increment j
-          }
-        else
-          {
-            if (vut.written.find (l) == vut.written.end())
-              if (! s.suppress_warnings)
-                s.print_warning ("read-only local variable " + stringify(*l->tok));
+              if (iterations == 0 && ! s.suppress_warnings)
+                 {
+                   stringstream o;
+                   vector<vardecl*>::iterator it;
+                   for (it = s.probes[i]->locals.begin(); it != s.probes[i]->locals.end(); it++)
+                     if (l->name != (*it)->name)
+                       o << " " <<  (*it)->name;
+                   for (it = s.globals.begin(); it != s.globals.end(); it++)
+                     if (l->name != (*it)->name)
+                       o << " " <<  (*it)->name;
+
+                   s.print_warning ("read-only local variable '" + l->name + "' " +
+                                     (o.str() == "" ? "" : ("(alternatives:" + o.str() + ")")), l->tok);
+                 }
             j++;
           }
       }
+
+  for (map<string,functiondecl*>::iterator it = s.functions.begin(); it != s.functions.end(); it++)
+    {
+      functiondecl *fd = it->second;
+      for (unsigned j=0; j<fd->locals.size(); /* see below */)
+        {
+          vardecl* l = fd->locals[j];
+          if (vut.read.find (l) == vut.read.end() &&
+              vut.written.find (l) == vut.written.end())
+            {
+              if (l->tok->location.file == s.user_file->name && // !tapset
+                  ! s.suppress_warnings)
+                s.print_warning ("eliding unused variable '" + l->name + "'", l->tok);
+              else if (s.verbose>2)
+                clog << "Eliding unused local variable "
+                     << l->name << " in function " << fd->name
+                     << endl;
+              if (s.tapset_compile_coverage) {
+                fd->unused_locals.push_back (fd->locals[j]);
+              }
+              fd->locals.erase(fd->locals.begin() + j);
+              relaxed_p = false;
+              // don't increment j
+            }
+          else
+            {
+              if (vut.written.find (l) == vut.written.end())
+                if (iterations == 0 && ! s.suppress_warnings)
+                  {
+                    stringstream o;
+                    vector<vardecl*>::iterator it;
+                    for (it = fd->formal_args.begin() ;
+                         it != fd->formal_args.end(); it++)
+                      if (l->name != (*it)->name)
+                        o << " " << (*it)->name;
+                    for (it = fd->locals.begin(); it != fd->locals.end(); it++)
+                      if (l->name != (*it)->name)
+                        o << " " << (*it)->name;
+                    for (it = s.globals.begin(); it != s.globals.end(); it++)
+                      if (l->name != (*it)->name)
+                        o << " " << (*it)->name;
+
+                    s.print_warning ("read-only local variable '" + l->name + "' " +
+                                     (o.str() == "" ? "" : ("(alternatives:" + o.str() + ")")), l->tok);
+                  }
+
+              j++;
+            }
+        }
+    }
   for (unsigned i=0; i<s.globals.size(); /* see below */)
     {
       vardecl* l = s.globals[i];
@@ -1681,23 +2057,32 @@ void semantic_pass_opt2 (systemtap_session& s, bool& relaxed_p)
         {
           if (l->tok->location.file == s.user_file->name && // !tapset
               ! s.suppress_warnings)
-            s.print_warning ("eliding unused variable " + stringify(*l->tok));
+            s.print_warning ("eliding unused variable '" + l->name + "'", l->tok);
           else if (s.verbose>2)
             clog << "Eliding unused global variable "
                  << l->name << endl;
          if (s.tapset_compile_coverage) {
-            s.unused_globals.push_back(s.globals[i]);
+           s.unused_globals.push_back(s.globals[i]);
          }
-          s.globals.erase(s.globals.begin() + i);
-          relaxed_p = false;
-          // don't increment i
+         s.globals.erase(s.globals.begin() + i);
+         relaxed_p = false;
+         // don't increment i
         }
       else
         {
-          if (vut.written.find (l) == vut.written.end() &&
-              ! l->init) // no initializer
-            if (! s.suppress_warnings)
-              s.print_warning ("read-only global variable " + stringify(*l->tok));
+          if (vut.written.find (l) == vut.written.end() && ! l->init) // no initializer
+            if (iterations == 0 && ! s.suppress_warnings)
+              {
+                stringstream o;
+                vector<vardecl*>::iterator it;
+                for (it = s.globals.begin(); it != s.globals.end(); it++)
+                  if (l->name != (*it)->name)
+                    o << " " << (*it)->name;
+
+                s.print_warning ("read-only global variable '" + l->name + "' " +
+                                 (o.str() == "" ? "" : ("(alternatives:" + o.str() + ")")), l->tok);
+              }
+
           i++;
         }
     }
@@ -1706,69 +2091,52 @@ void semantic_pass_opt2 (systemtap_session& s, bool& relaxed_p)
 
 // ------------------------------------------------------------------------
 
-struct dead_assignment_remover: public traversing_visitor
+struct dead_assignment_remover: public update_visitor
 {
   systemtap_session& session;
   bool& relaxed_p;
   const varuse_collecting_visitor& vut;
-  expression** current_expr;
 
   dead_assignment_remover(systemtap_session& s, bool& r,
-                          const varuse_collecting_visitor& v): 
-    session(s), relaxed_p(r), vut(v), current_expr(0) {}
-
-  void visit_expr_statement (expr_statement* s);
-  // XXX: other places where an assignment may be nested should be
-  // handled too (e.g., loop/if conditionals, array indexes, function
-  // parameters).  Until then, they result in visit_assignment() being
-  // called with null current_expr.
+                          const varuse_collecting_visitor& v):
+    session(s), relaxed_p(r), vut(v) {}
 
   void visit_assignment (assignment* e);
-  void visit_binary_expression (binary_expression* e);
-  void visit_arrayindex (arrayindex* e);
-  void visit_functioncall (functioncall* e);
-  void visit_if_statement (if_statement* e);
-  void visit_for_loop (for_loop* e);
 };
 
 
-void
-dead_assignment_remover::visit_expr_statement (expr_statement* s)
-{
-  expression** last_expr = current_expr;
-  current_expr = & s->value;
-  s->value->visit (this);
-  s->tok = s->value->tok; // in case it was replaced
-  current_expr = last_expr;
-}
-
-
 void
 dead_assignment_remover::visit_assignment (assignment* e)
 {
+  e->left = require (e->left);
+  e->right = require (e->right);
+
   symbol* left = get_symbol_within_expression (e->left);
   vardecl* leftvar = left->referent; // NB: may be 0 for unresolved $target
-  if (current_expr && // see XXX above: this case represents a missed
-                      // optimization opportunity
-      *current_expr == e && // we're not nested any deeper than expected 
-      leftvar) // not unresolved $target; intended sideeffect cannot be elided
+  if (leftvar) // not unresolved $target, so intended sideeffect may be elided
     {
-      expression** last_expr = current_expr;
-      e->left->visit (this);
-      current_expr = &e->right;
-      e->right->visit (this);
-      current_expr = last_expr;
       if (vut.read.find(leftvar) == vut.read.end()) // var never read?
         {
           // NB: Not so fast!  The left side could be an array whose
           // index expressions may have side-effects.  This would be
-          // OK if we could replace the array assignment with a 
+          // OK if we could replace the array assignment with a
           // statement-expression containing all the index expressions
           // and the rvalue... but we can't.
+         // Another possibility is that we have an unread global variable
+         // which are kept for probe end value display.
 
-          varuse_collecting_visitor vut;
-          e->left->visit (& vut);
-          if (vut.side_effect_free ()) // XXX: use _wrt() once we track focal_vars
+         bool is_global = false;
+         vector<vardecl*>::iterator it;
+         for (it = session.globals.begin(); it != session.globals.end(); it++)
+           if (leftvar->name == (*it)->name)
+             {
+               is_global = true;
+               break;
+             }
+
+          varuse_collecting_visitor lvut;
+          e->left->visit (& lvut);
+          if (lvut.side_effect_free () && !is_global) // XXX: use _wrt() once we track focal_vars
             {
               /* PR 1119: NB: This is not necessary here.  A write-only
                  variable will also be elided soon at the next _opt2 iteration.
@@ -1778,80 +2146,16 @@ dead_assignment_remover::visit_assignment (assignment* e)
               else
               */
               if (session.verbose>2)
-                clog << "Eliding assignment to " << leftvar->name 
+                clog << "Eliding assignment to " << leftvar->name
                      << " at " << *e->tok << endl;
-              
-              *current_expr = e->right; // goodbye assignment*
+
+              provide (e->right); // goodbye assignment*
               relaxed_p = false;
+              return;
             }
         }
     }
-}
-
-void
-dead_assignment_remover::visit_binary_expression (binary_expression* e)
-{
-  expression** last_expr = current_expr;
-  current_expr = &e->left;
-  e->left->visit (this);
-  current_expr = &e->right;
-  e->right->visit (this);
-  current_expr = last_expr;
-}
-
-void
-dead_assignment_remover::visit_arrayindex (arrayindex *e)
-{
-  symbol *array = NULL;
-  hist_op *hist = NULL;
-  classify_indexable(e->base, array, hist);
-
-  if (array)
-    {
-      expression** last_expr = current_expr;
-      for (unsigned i=0; i < e->indexes.size(); i++)
-       {
-         current_expr = & e->indexes[i];
-         e->indexes[i]->visit (this);
-       }
-      current_expr = last_expr;
-    }
-}
-
-void
-dead_assignment_remover::visit_functioncall (functioncall* e)
-{
-  expression** last_expr = current_expr;
-  for (unsigned i=0; i<e->args.size(); i++)
-    {
-      current_expr = & e->args[i];
-      e->args[i]->visit (this);
-    }
-  current_expr = last_expr;
-}
-
-void
-dead_assignment_remover::visit_if_statement (if_statement* s)
-{
-  expression** last_expr = current_expr;
-  current_expr = & s->condition;
-  s->condition->visit (this);
-  s->thenblock->visit (this);
-  if (s->elseblock)
-    s->elseblock->visit (this);
-  current_expr = last_expr;
-}
-
-void
-dead_assignment_remover::visit_for_loop (for_loop* s)
-{
-  expression** last_expr = current_expr;
-  if (s->init) s->init->visit (this);
-  current_expr = & s->cond;
-  s->cond->visit (this);
-  if (s->incr) s->incr->visit (this);
-  s->block->visit (this);
-  current_expr = last_expr;
+  provide (e);
 }
 
 // Let's remove assignments to variables that are never read.  We
@@ -1871,9 +2175,10 @@ void semantic_pass_opt3 (systemtap_session& s, bool& relaxed_p)
   // This instance may be reused for multiple probe/function body trims.
 
   for (unsigned i=0; i<s.probes.size(); i++)
-    s.probes[i]->body->visit (& dar);
-  for (unsigned i=0; i<s.functions.size(); i++)
-    s.functions[i]->body->visit (& dar);
+    s.probes[i]->body = dar.require (s.probes[i]->body);
+  for (map<string,functiondecl*>::iterator it = s.functions.begin();
+       it != s.functions.end(); it++)
+    it->second->body = dar.require (it->second->body);
   // The rewrite operation is performed within the visitor.
 
   // XXX: we could also zap write-only globals here
@@ -1882,15 +2187,14 @@ void semantic_pass_opt3 (systemtap_session& s, bool& relaxed_p)
 
 // ------------------------------------------------------------------------
 
-struct dead_stmtexpr_remover: public traversing_visitor
+struct dead_stmtexpr_remover: public update_visitor
 {
   systemtap_session& session;
   bool& relaxed_p;
-  statement** current_stmt; // pointer to current stmt* being iterated
   set<vardecl*> focal_vars; // vars considered subject to side-effects
 
-  dead_stmtexpr_remover(systemtap_session& s, bool& r): 
-    session(s), relaxed_p(r), current_stmt(0) {}
+  dead_stmtexpr_remover(systemtap_session& s, bool& r):
+    session(s), relaxed_p(r) {}
 
   void visit_block (block *s);
   void visit_null_statement (null_statement *s);
@@ -1909,7 +2213,8 @@ dead_stmtexpr_remover::visit_null_statement (null_statement *s)
   // easy!
   if (session.verbose>2)
     clog << "Eliding side-effect-free null statement " << *s->tok << endl;
-  *current_stmt = 0;
+  s = 0;
+  provide (s);
 }
 
 
@@ -1919,92 +2224,112 @@ dead_stmtexpr_remover::visit_block (block *s)
   vector<statement*> new_stmts;
   for (unsigned i=0; i<s->statements.size(); i++ )
     {
-      statement** last_stmt = current_stmt;
-      current_stmt = & s->statements[i];
-      s->statements[i]->visit (this);
-      if (*current_stmt != 0) 
-        new_stmts.push_back (*current_stmt);
-      current_stmt = last_stmt;
+      statement* new_stmt = require (s->statements[i], true);
+      if (new_stmt != 0)
+        {
+          // flatten nested blocks into this one
+          block *b = dynamic_cast<block *>(new_stmt);
+          if (b)
+            {
+              if (session.verbose>2)
+                clog << "Flattening nested block " << *b->tok << endl;
+              new_stmts.insert(new_stmts.end(),
+                  b->statements.begin(), b->statements.end());
+              relaxed_p = false;
+            }
+          else
+            new_stmts.push_back (new_stmt);
+        }
     }
   if (new_stmts.size() == 0)
     {
       if (session.verbose>2)
         clog << "Eliding side-effect-free empty block " << *s->tok << endl;
-      *current_stmt = 0;
+      s = 0;
     }
   else if (new_stmts.size() == 1)
     {
       if (session.verbose>2)
         clog << "Eliding side-effect-free singleton block " << *s->tok << endl;
-      *current_stmt = new_stmts[0];
+      provide (new_stmts[0]);
+      return;
     }
   else
-    {
-      s->statements = new_stmts;
-    }
+    s->statements = new_stmts;
+  provide (s);
 }
 
 void
 dead_stmtexpr_remover::visit_if_statement (if_statement *s)
 {
-  statement** last_stmt = current_stmt;
-  current_stmt = & s->thenblock;
-  s->thenblock->visit (this);
+  s->thenblock = require (s->thenblock, true);
+  s->elseblock = require (s->elseblock, true);
 
-  if (s->elseblock)
-    {
-      current_stmt = & s->elseblock;
-      s->elseblock->visit (this);
-      // null *current_stmt is OK here.
-    }
-  current_stmt = last_stmt;
-
-  if (s->elseblock == 0 && s->thenblock == 0)
+  if (s->thenblock == 0)
     {
-      // We may be able to elide this statement, if the condition
-      // expression is side-effect-free.
-      varuse_collecting_visitor vct;
-      s->condition->visit(& vct);
-      if (vct.side_effect_free ())
+      if (s->elseblock == 0)
+        {
+          // We may be able to elide this statement, if the condition
+          // expression is side-effect-free.
+          varuse_collecting_visitor vct;
+          s->condition->visit(& vct);
+          if (vct.side_effect_free ())
+            {
+              if (session.verbose>2)
+                clog << "Eliding side-effect-free if statement "
+                     << *s->tok << endl;
+              s = 0; // yeah, baby
+            }
+          else
+            {
+              // We can still turn it into a simple expr_statement though...
+              if (session.verbose>2)
+                clog << "Creating simple evaluation from if statement "
+                     << *s->tok << endl;
+              expr_statement *es = new expr_statement;
+              es->value = s->condition;
+              es->tok = es->value->tok;
+              provide (es);
+              return;
+            }
+        }
+      else
         {
+          // For an else without a then, we can invert the condition logic to
+          // avoid having a null statement in the thenblock
           if (session.verbose>2)
-            clog << "Eliding side-effect-free if statement " << *s->tok << endl;
-          *current_stmt = 0; // yeah, baby
-          return;
+            clog << "Inverting the condition of if statement "
+                 << *s->tok << endl;
+          unary_expression *ue = new unary_expression;
+          ue->operand = s->condition;
+          ue->tok = ue->operand->tok;
+          ue->op = "!";
+          s->condition = ue;
+          s->thenblock = s->elseblock;
+          s->elseblock = 0;
         }
     }
-
-  if (s->thenblock == 0)
-    {
-      // Can't elide this whole if/else statement; put a null in there.
-      s->thenblock = new null_statement();
-      s->thenblock->tok = s->tok;
-    }
+  provide (s);
 }
 
 void
 dead_stmtexpr_remover::visit_foreach_loop (foreach_loop *s)
 {
-  statement** last_stmt = current_stmt;
-  current_stmt = & s->block;
-  s->block->visit (this);
-  current_stmt = last_stmt;
+  s->block = require(s->block, true);
 
   if (s->block == 0)
     {
       if (session.verbose>2)
         clog << "Eliding side-effect-free foreach statement " << *s->tok << endl;
-      *current_stmt = 0; // yeah, baby
+      s = 0; // yeah, baby
     }
+  provide (s);
 }
 
 void
 dead_stmtexpr_remover::visit_for_loop (for_loop *s)
 {
-  statement** last_stmt = current_stmt;
-  current_stmt = & s->block;
-  s->block->visit (this);
-  current_stmt = last_stmt;
+  s->block = require(s->block, true);
 
   if (s->block == 0)
     {
@@ -2018,14 +2343,16 @@ dead_stmtexpr_remover::visit_for_loop (for_loop *s)
         {
           if (session.verbose>2)
             clog << "Eliding side-effect-free for statement " << *s->tok << endl;
-          *current_stmt = 0; // yeah, baby
-          return;
+          s = 0; // yeah, baby
+        }
+      else
+        {
+          // Can't elide this whole statement; put a null in there.
+          s->block = new null_statement();
+          s->block->tok = s->tok;
         }
-
-      // Can't elide this whole statement; put a null in there.
-      s->block = new null_statement();
-      s->block->tok = s->tok;
     }
+  provide (s);
 }
 
 
@@ -2035,8 +2362,7 @@ dead_stmtexpr_remover::visit_expr_statement (expr_statement *s)
 {
   // Run a varuse query against the operand expression.  If it has no
   // side-effects, replace the entire statement expression by a null
-  // statement.  This replacement is done by overwriting the
-  // current_stmt pointer.
+  // statement with the provide() call.
   //
   // Unlike many other visitors, we do *not* traverse this outermost
   // one into the expression subtrees.  There is no need - no
@@ -2048,15 +2374,15 @@ dead_stmtexpr_remover::visit_expr_statement (expr_statement *s)
 
   varuse_collecting_visitor vut;
   s->value->visit (& vut);
-  if (vut.side_effect_free_wrt (focal_vars) &&
-      *current_stmt == s) // we're not nested any deeper than expected 
+
+  if (vut.side_effect_free_wrt (focal_vars))
     {
       /* PR 1119: NB: this message is not a good idea here.  It can
          name some arbitrary RHS expression of an assignment.
       if (s->value->tok->location.file == session.user_file->name && // not tapset
           ! session.suppress_warnings)
         clog << "WARNING: eliding read-only " << *s->value->tok << endl;
-      else 
+      else
       */
       if (session.verbose>2)
         clog << "Eliding side-effect-free expression "
@@ -2064,10 +2390,10 @@ dead_stmtexpr_remover::visit_expr_statement (expr_statement *s)
 
       // NB: this 0 pointer is invalid to leave around for any length of
       // time, but the parent parse tree objects above handle it.
-      * current_stmt = 0;
-
+      s = 0;
       relaxed_p = false;
     }
+  provide (s);
 }
 
 
@@ -2076,12 +2402,14 @@ void semantic_pass_opt4 (systemtap_session& s, bool& relaxed_p)
   // Finally, let's remove some statement-expressions that have no
   // side-effect.  These should be exactly those whose private varuse
   // visitors come back with an empty "written" and "embedded" lists.
-  
+
   dead_stmtexpr_remover duv (s, relaxed_p);
   // This instance may be reused for multiple probe/function body trims.
 
   for (unsigned i=0; i<s.probes.size(); i++)
     {
+      if (pending_interrupts) break;
+
       derived_probe* p = s.probes[i];
 
       duv.focal_vars.clear ();
@@ -2090,13 +2418,11 @@ void semantic_pass_opt4 (systemtap_session& s, bool& relaxed_p)
       duv.focal_vars.insert (p->locals.begin(),
                              p->locals.end());
 
-      duv.current_stmt = & p->body;
-      p->body->visit (& duv);
+      p->body = duv.require(p->body, true);
       if (p->body == 0)
         {
           if (! s.suppress_warnings)
-            s.print_warning ("side-effect-free probe '" + p->name + "' " 
-                            + stringify(*p->tok));
+            s.print_warning ("side-effect-free probe '" + p->name + "'", p->tok);
 
           p->body = new null_statement();
           p->body->tok = p->tok;
@@ -2104,9 +2430,11 @@ void semantic_pass_opt4 (systemtap_session& s, bool& relaxed_p)
           // XXX: possible duplicate warnings; see below
         }
     }
-  for (unsigned i=0; i<s.functions.size(); i++)
+  for (map<string,functiondecl*>::iterator it = s.functions.begin(); it != s.functions.end(); it++)
     {
-      functiondecl* fn = s.functions[i];
+      if (pending_interrupts) break;
+
+      functiondecl* fn = it->second;
       duv.focal_vars.clear ();
       duv.focal_vars.insert (fn->locals.begin(),
                              fn->locals.end());
@@ -2115,13 +2443,11 @@ void semantic_pass_opt4 (systemtap_session& s, bool& relaxed_p)
       duv.focal_vars.insert (s.globals.begin(),
                              s.globals.end());
 
-      duv.current_stmt = & fn->body;
-      fn->body->visit (& duv);
+      fn->body = duv.require(fn->body, true);
       if (fn->body == 0)
         {
           if (! s.suppress_warnings)
-            s.print_warning ("side-effect-free function '" + fn->name + "' "
-                            + stringify(*fn->tok));
+            s.print_warning ("side-effect-free function '" + fn->name + "'", fn->tok);
 
           fn->body = new null_statement();
           fn->body->tok = fn->tok;
@@ -2135,6 +2461,335 @@ void semantic_pass_opt4 (systemtap_session& s, bool& relaxed_p)
     }
 }
 
+
+// ------------------------------------------------------------------------
+
+// The goal of this visitor is to reduce top-level expressions in void context
+// into separate statements that evaluate each subcomponent of the expression.
+// The dead-statement-remover can later remove some parts if they have no side
+// effects.
+//
+// All expressions must be overridden here so we never visit their subexpressions
+// accidentally.  Thus, the only visited expressions should be value of an
+// expr_statement.
+//
+// For an expression to replace its expr_statement with something else, it will
+// let the new statement provide(), and then provide(0) for itself.  The
+// expr_statement will take this as a sign that it's been replaced.
+struct void_statement_reducer: public update_visitor
+{
+  systemtap_session& session;
+  bool& relaxed_p;
+  set<vardecl*> focal_vars; // vars considered subject to side-effects
+
+  void_statement_reducer(systemtap_session& s, bool& r):
+    session(s), relaxed_p(r) {}
+
+  void visit_expr_statement (expr_statement* s);
+
+  // expressions in conditional / loop controls are definitely a side effect,
+  // but still recurse into the child statements
+  void visit_if_statement (if_statement* s);
+  void visit_for_loop (for_loop* s);
+  void visit_foreach_loop (foreach_loop* s);
+
+  // these expressions get rewritten into their statement equivalents
+  void visit_logical_or_expr (logical_or_expr* e);
+  void visit_logical_and_expr (logical_and_expr* e);
+  void visit_ternary_expression (ternary_expression* e);
+
+  // all of these can be reduced into simpler statements
+  void visit_binary_expression (binary_expression* e);
+  void visit_unary_expression (unary_expression* e);
+  void visit_comparison (comparison* e);
+  void visit_concatenation (concatenation* e);
+  void visit_functioncall (functioncall* e);
+  void visit_print_format (print_format* e);
+
+  // these are a bit hairy to grok due to the intricacies of indexables and
+  // stats, so I'm chickening out and skipping them...
+  void visit_array_in (array_in* e) { provide (e); }
+  void visit_arrayindex (arrayindex* e) { provide (e); }
+  void visit_stat_op (stat_op* e) { provide (e); }
+  void visit_hist_op (hist_op* e) { provide (e); }
+
+  // these can't be reduced because they always have an effect
+  void visit_return_statement (return_statement* s) { provide (s); }
+  void visit_delete_statement (delete_statement* s) { provide (s); }
+  void visit_pre_crement (pre_crement* e) { provide (e); }
+  void visit_post_crement (post_crement* e) { provide (e); }
+  void visit_assignment (assignment* e) { provide (e); }
+};
+
+
+void
+void_statement_reducer::visit_expr_statement (expr_statement* s)
+{
+  s->value = require (s->value, true);
+
+  // if the expression provides 0, that's our signal that a new
+  // statement has been provided, so we shouldn't provide this one.
+  if (s->value != 0)
+    provide(s);
+}
+
+void
+void_statement_reducer::visit_if_statement (if_statement* s)
+{
+  // s->condition is never void
+  s->thenblock = require (s->thenblock);
+  s->elseblock = require (s->elseblock);
+  provide (s);
+}
+
+void
+void_statement_reducer::visit_for_loop (for_loop* s)
+{
+  // s->init/cond/incr are never void
+  s->block = require (s->block);
+  provide (s);
+}
+
+void
+void_statement_reducer::visit_foreach_loop (foreach_loop* s)
+{
+  // s->indexes/base/limit are never void
+  s->block = require (s->block);
+  provide (s);
+}
+
+void
+void_statement_reducer::visit_logical_or_expr (logical_or_expr* e)
+{
+  // In void context, the evaluation of "a || b" is exactly like
+  // "if (!a) b", so let's do that instead.
+
+  if (session.verbose>2)
+    clog << "Creating if statement from unused logical-or "
+         << *e->tok << endl;
+
+  if_statement *is = new if_statement;
+  is->tok = e->tok;
+  is->elseblock = 0;
+
+  unary_expression *ue = new unary_expression;
+  ue->operand = e->left;
+  ue->tok = e->tok;
+  ue->op = "!";
+  is->condition = ue;
+
+  expr_statement *es = new expr_statement;
+  es->value = e->right;
+  es->tok = es->value->tok;
+  is->thenblock = es;
+
+  is->visit(this);
+  relaxed_p = false;
+  e = 0;
+  provide (e);
+}
+
+void
+void_statement_reducer::visit_logical_and_expr (logical_and_expr* e)
+{
+  // In void context, the evaluation of "a && b" is exactly like
+  // "if (a) b", so let's do that instead.
+
+  if (session.verbose>2)
+    clog << "Creating if statement from unused logical-and "
+         << *e->tok << endl;
+
+  if_statement *is = new if_statement;
+  is->tok = e->tok;
+  is->elseblock = 0;
+  is->condition = e->left;
+
+  expr_statement *es = new expr_statement;
+  es->value = e->right;
+  es->tok = es->value->tok;
+  is->thenblock = es;
+
+  is->visit(this);
+  relaxed_p = false;
+  e = 0;
+  provide (e);
+}
+
+void
+void_statement_reducer::visit_ternary_expression (ternary_expression* e)
+{
+  // In void context, the evaluation of "a ? b : c" is exactly like
+  // "if (a) b else c", so let's do that instead.
+
+  if (session.verbose>2)
+    clog << "Creating if statement from unused ternary expression "
+         << *e->tok << endl;
+
+  if_statement *is = new if_statement;
+  is->tok = e->tok;
+  is->condition = e->cond;
+
+  expr_statement *es = new expr_statement;
+  es->value = e->truevalue;
+  es->tok = es->value->tok;
+  is->thenblock = es;
+
+  es = new expr_statement;
+  es->value = e->falsevalue;
+  es->tok = es->value->tok;
+  is->elseblock = es;
+
+  is->visit(this);
+  relaxed_p = false;
+  e = 0;
+  provide (e);
+}
+
+void
+void_statement_reducer::visit_binary_expression (binary_expression* e)
+{
+  // When the result of a binary operation isn't needed, it's just as good to
+  // evaluate the operands as sequential statements in a block.
+
+  if (session.verbose>2)
+    clog << "Eliding unused binary " << *e->tok << endl;
+
+  block *b = new block;
+  b->tok = e->tok;
+
+  expr_statement *es = new expr_statement;
+  es->value = e->left;
+  es->tok = es->value->tok;
+  b->statements.push_back(es);
+
+  es = new expr_statement;
+  es->value = e->right;
+  es->tok = es->value->tok;
+  b->statements.push_back(es);
+
+  b->visit(this);
+  relaxed_p = false;
+  e = 0;
+  provide (e);
+}
+
+void
+void_statement_reducer::visit_unary_expression (unary_expression* e)
+{
+  // When the result of a unary operation isn't needed, it's just as good to
+  // evaluate the operand directly
+
+  if (session.verbose>2)
+    clog << "Eliding unused unary " << *e->tok << endl;
+
+  relaxed_p = false;
+  e->operand->visit(this);
+}
+
+void
+void_statement_reducer::visit_comparison (comparison* e)
+{
+  visit_binary_expression(e);
+}
+
+void
+void_statement_reducer::visit_concatenation (concatenation* e)
+{
+  visit_binary_expression(e);
+}
+
+void
+void_statement_reducer::visit_functioncall (functioncall* e)
+{
+  // If a function call is pure and its result ignored, we can elide the call
+  // and just evaluate the arguments in sequence
+
+  if (!e->args.size())
+    {
+      provide (e);
+      return;
+    }
+
+  varuse_collecting_visitor vut;
+  vut.traversed.insert (e->referent);
+  vut.current_function = e->referent;
+  e->referent->body->visit (& vut);
+  if (!vut.side_effect_free_wrt (focal_vars))
+    {
+      provide (e);
+      return;
+    }
+
+  if (session.verbose>2)
+    clog << "Eliding side-effect-free function call " << *e->tok << endl;
+
+  block *b = new block;
+  b->tok = e->tok;
+
+  for (unsigned i=0; i<e->args.size(); i++ )
+    {
+      expr_statement *es = new expr_statement;
+      es->value = e->args[i];
+      es->tok = es->value->tok;
+      b->statements.push_back(es);
+    }
+
+  b->visit(this);
+  relaxed_p = false;
+  e = 0;
+  provide (e);
+}
+
+void
+void_statement_reducer::visit_print_format (print_format* e)
+{
+  // When an sprint's return value is ignored, we can simply evaluate the
+  // arguments in sequence
+
+  if (e->print_to_stream || !e->args.size())
+    {
+      provide (e);
+      return;
+    }
+
+  if (session.verbose>2)
+    clog << "Eliding unused print " << *e->tok << endl;
+
+  block *b = new block;
+  b->tok = e->tok;
+
+  for (unsigned i=0; i<e->args.size(); i++ )
+    {
+      expr_statement *es = new expr_statement;
+      es->value = e->args[i];
+      es->tok = es->value->tok;
+      b->statements.push_back(es);
+    }
+
+  b->visit(this);
+  relaxed_p = false;
+  e = 0;
+  provide (e);
+}
+
+
+void semantic_pass_opt5 (systemtap_session& s, bool& relaxed_p)
+{
+  // Let's simplify statements with unused computed values.
+
+  void_statement_reducer vuv (s, relaxed_p);
+  // This instance may be reused for multiple probe/function body trims.
+
+  vuv.focal_vars.insert (s.globals.begin(), s.globals.end());
+
+  for (unsigned i=0; i<s.probes.size(); i++)
+    s.probes[i]->body = vuv.require (s.probes[i]->body);
+  for (map<string,functiondecl*>::iterator it = s.functions.begin();
+       it != s.functions.end(); it++)
+    it->second->body = vuv.require (it->second->body);
+}
+
+
 struct duplicate_function_remover: public functioncall_traversing_visitor
 {
   systemtap_session& s;
@@ -2187,35 +2842,39 @@ get_functionsig (functiondecl* f)
   return str;
 }
 
-void semantic_pass_opt5 (systemtap_session& s, bool& relaxed_p)
+void semantic_pass_opt6 (systemtap_session& s, bool& relaxed_p)
 {
   // Walk through all the functions, looking for duplicates.
   map<string, functiondecl*> functionsig_map;
   map<functiondecl*, functiondecl*> duplicate_function_map;
-  for (unsigned i=0; i < s.functions.size(); /* see below */)
+
+
+  vector<functiondecl*> newly_zapped_functions;
+  for (map<string,functiondecl*>::iterator it = s.functions.begin(); it != s.functions.end(); it++)
     {
-      string functionsig = get_functionsig(s.functions[i]);
+      functiondecl *fd = it->second;
+      string functionsig = get_functionsig(fd);
 
       if (functionsig_map.count(functionsig) == 0)
        {
          // This function is unique.  Remember it.
-         functionsig_map[functionsig] = s.functions[i];
-         i++;
+         functionsig_map[functionsig] = fd;
        }
       else
         {
          // This function is a duplicate.
-         duplicate_function_map[s.functions[i]]
-           = functionsig_map[functionsig];
-
-         // Remove the duplicate function (since we don't need it
-         // anymore).
-         s.functions.erase (s.functions.begin() + i);
-
+         duplicate_function_map[fd] = functionsig_map[functionsig];
+          newly_zapped_functions.push_back (fd);
          relaxed_p = false;
-          // NB: don't increment i
        }
     }
+  for (unsigned i=0; i<newly_zapped_functions.size(); i++)
+    {
+      map<string,functiondecl*>::iterator where = s.functions.find (newly_zapped_functions[i]->name);
+      assert (where != s.functions.end());
+      s.functions.erase (where);
+    }
+
 
   // If we have duplicate functions, traverse down the tree, replacing
   // the appropriate function calls.
@@ -2243,6 +2902,7 @@ semantic_pass_optimize1 (systemtap_session& s)
   int rc = 0;
 
   bool relaxed_p = false;
+  unsigned iterations = 0;
   while (! relaxed_p)
     {
       if (pending_interrupts) break;
@@ -2250,9 +2910,12 @@ semantic_pass_optimize1 (systemtap_session& s)
       relaxed_p = true; // until proven otherwise
 
       semantic_pass_opt1 (s, relaxed_p);
-      semantic_pass_opt2 (s, relaxed_p);
+      semantic_pass_opt2 (s, relaxed_p, iterations); // produce some warnings only on iteration=0
       semantic_pass_opt3 (s, relaxed_p);
       semantic_pass_opt4 (s, relaxed_p);
+      semantic_pass_opt5 (s, relaxed_p);
+
+      iterations ++;
     }
 
   return rc;
@@ -2274,7 +2937,7 @@ semantic_pass_optimize2 (systemtap_session& s)
       if (pending_interrupts) break;
       relaxed_p = true; // until proven otherwise
 
-      semantic_pass_opt5 (s, relaxed_p);
+      semantic_pass_opt6 (s, relaxed_p);
     }
 
   return rc;
@@ -2294,7 +2957,7 @@ semantic_pass_types (systemtap_session& s)
   // next pass: type inference
   unsigned iterations = 0;
   typeresolution_info ti (s);
-  
+
   ti.assert_resolvability = false;
   // XXX: maybe convert to exception-based error signalling
   while (1)
@@ -2305,22 +2968,22 @@ semantic_pass_types (systemtap_session& s)
       ti.num_newly_resolved = 0;
       ti.num_still_unresolved = 0;
 
-      for (unsigned j=0; j<s.functions.size(); j++)
+  for (map<string,functiondecl*>::iterator it = s.functions.begin(); it != s.functions.end(); it++)
         {
           if (pending_interrupts) break;
 
-          functiondecl* fn = s.functions[j];
+          functiondecl* fd = it->second;
           ti.current_probe = 0;
-          ti.current_function = fn;
+          ti.current_function = fd;
           ti.t = pe_unknown;
-          fn->body->visit (& ti);
+          fd->body->visit (& ti);
          // NB: we don't have to assert a known type for
          // functions here, to permit a "void" function.
          // The translator phase will omit the "retvalue".
          //
-          // if (fn->type == pe_unknown)
-          //   ti.unresolved (fn->tok);
-        }          
+          // if (fd->type == pe_unknown)
+          //   ti.unresolved (fd->tok);
+        }
 
       for (unsigned j=0; j<s.probes.size(); j++)
         {
@@ -2348,7 +3011,7 @@ semantic_pass_types (systemtap_session& s)
           if (gd->type == pe_unknown)
             ti.unresolved (gd->tok);
         }
-      
+
       if (ti.num_newly_resolved == 0) // converged
         {
           if (ti.num_still_unresolved == 0)
@@ -2362,7 +3025,7 @@ semantic_pass_types (systemtap_session& s)
             }
         }
     }
-  
+
   return rc + s.num_errors();
 }
 
@@ -2421,12 +3084,12 @@ typeresolution_info::visit_comparison (comparison *e)
   e->left->visit (this);
   t = (e->left->type != pe_unknown) ? e->left->type : pe_unknown;
   e->right->visit (this);
-  
+
   if (e->left->type != pe_unknown &&
       e->right->type != pe_unknown &&
       e->left->type != e->right->type)
     mismatch (e->tok, e->left->type, e->right->type);
-  
+
   if (e->type == pe_unknown)
     {
       e->type = pe_long;
@@ -2530,7 +3193,7 @@ typeresolution_info::visit_assignment (assignment *e)
         (e->left->type != pe_unknown) ? e->left->type :
         pe_unknown;
       e->right->visit (this);
-      
+
       if ((sub_type != pe_unknown) && (e->type == pe_unknown))
         {
           e->type = sub_type;
@@ -2568,7 +3231,7 @@ typeresolution_info::visit_binary_expression (binary_expression* e)
       e->right->type != pe_unknown &&
       e->left->type != e->right->type)
     mismatch (e->tok, e->left->type, e->right->type);
-  
+
   if (e->type == pe_unknown)
     {
       e->type = pe_long;
@@ -2724,7 +3387,7 @@ typeresolution_info::visit_target_symbol (target_symbol* e)
           current_probe->body->print (clog);
           clog << endl;
         }
-      else 
+      else
         clog << "other" << endl;
     }
 
@@ -2742,7 +3405,7 @@ typeresolution_info::visit_arrayindex (arrayindex* e)
   symbol *array = NULL;
   hist_op *hist = NULL;
   classify_indexable(e->base, array, hist);
-  
+
   // Every hist_op has type [int]:int, that is to say, every hist_op
   // is a pseudo-one-dimensional integer array type indexed by
   // integers (bucket numbers).
@@ -2810,7 +3473,7 @@ typeresolution_info::visit_functioncall (functioncall* e)
 {
   assert (e->referent != 0);
 
-  resolve_2types (e, e->referent, this, t, true); // accept unknown type 
+  resolve_2types (e, e->referent, this, t, true); // accept unknown type
 
   if (e->type == pe_stats)
     invalid (e->tok, e->type);
@@ -2826,7 +3489,7 @@ typeresolution_info::visit_functioncall (functioncall* e)
       t = ft;
       ee->visit (this);
       exp_type at = ee->type;
-      
+
       if (((at == pe_string) || (at == pe_long)) && ft == pe_unknown)
         {
           // propagate to formal arg
@@ -2850,7 +3513,7 @@ typeresolution_info::visit_block (block* e)
 {
   for (unsigned i=0; i<e->statements.size(); i++)
     {
-      try 
+      try
        {
          t = pe_unknown;
          e->statements[i]->visit (this);
@@ -2894,9 +3557,9 @@ typeresolution_info::visit_for_loop (for_loop* e)
   t = pe_long;
   e->cond->visit (this);
   t = pe_unknown;
-  if (e->incr) e->incr->visit (this);  
+  if (e->incr) e->incr->visit (this);
   t = pe_unknown;
-  e->block->visit (this);  
+  e->block->visit (this);
 }
 
 
@@ -2916,7 +3579,7 @@ typeresolution_info::visit_foreach_loop (foreach_loop* e)
   classify_indexable(e->base, array, hist);
 
   if (hist)
-    {      
+    {
       if (e->indexes.size() != 1)
        unresolved (e->tok);
       t = pe_long;
@@ -2927,7 +3590,7 @@ typeresolution_info::visit_foreach_loop (foreach_loop* e)
     }
   else
     {
-      assert (array);  
+      assert (array);
       if (e->indexes.size() != array->referent->index_types.size())
        unresolved (e->tok); // symbol resolution should prevent this
       else for (unsigned i=0; i<e->indexes.size(); i++)
@@ -2937,7 +3600,7 @@ typeresolution_info::visit_foreach_loop (foreach_loop* e)
          t = ft;
          ee->visit (this);
          exp_type at = ee->type;
-         
+
          if ((at == pe_string || at == pe_long) && ft == pe_unknown)
            {
              // propagate to formal type
@@ -2963,7 +3626,7 @@ typeresolution_info::visit_foreach_loop (foreach_loop* e)
     }
 
   t = pe_unknown;
-  e->block->visit (this);  
+  e->block->visit (this);
 }
 
 
@@ -2981,7 +3644,7 @@ typeresolution_info::visit_expr_statement (expr_statement* e)
 }
 
 
-struct delete_statement_typeresolution_info: 
+struct delete_statement_typeresolution_info:
   public throwing_visitor
 {
   typeresolution_info *parent;
@@ -2994,11 +3657,11 @@ struct delete_statement_typeresolution_info:
   {
     parent->visit_arrayindex (e);
   }
-  
+
   void visit_symbol (symbol* e)
   {
     exp_type ignored = pe_unknown;
-    assert (e->referent != 0);    
+    assert (e->referent != 0);
     resolve_2types (e, e->referent, parent, ignored);
   }
 };
@@ -3057,7 +3720,7 @@ typeresolution_info::visit_return_statement (return_statement* e)
   // This is like symbol, where the referent is
   // the return value of the function.
 
-  // translation pass will print error 
+  // translation pass will print error
   if (current_function == 0)
     return;
 
@@ -3068,7 +3731,7 @@ typeresolution_info::visit_return_statement (return_statement* e)
   if (e_type != pe_unknown && e->value->type != pe_unknown
       && e_type != e->value->type)
     mismatch (current_function->tok, e_type, e->value->type);
-  if (e_type == pe_unknown && 
+  if (e_type == pe_unknown &&
       (e->value->type == pe_long || e->value->type == pe_string))
     {
       // propagate non-statistics from value
@@ -3079,7 +3742,7 @@ typeresolution_info::visit_return_statement (return_statement* e)
     invalid (e->value->tok, e->value->type);
 }
 
-void 
+void
 typeresolution_info::visit_print_format (print_format* e)
 {
   size_t unresolved_args = 0;
@@ -3104,8 +3767,7 @@ typeresolution_info::visit_print_format (print_format* e)
          if (e->components[i].type == print_format::conv_unspecified)
            throw semantic_error ("Unspecified conversion in print operator format string",
                                  e->tok);
-         else if (e->components[i].type == print_format::conv_literal 
-                  || e->components[i].type == print_format::conv_size)
+         else if (e->components[i].type == print_format::conv_literal)
            continue;
          components.push_back(e->components[i]);
          ++expected_num_args;
@@ -3147,7 +3809,6 @@ typeresolution_info::visit_print_format (print_format* e)
            {
            case print_format::conv_unspecified:
            case print_format::conv_literal:
-           case print_format::conv_size:
              assert (false);
              break;
 
@@ -3158,7 +3819,9 @@ typeresolution_info::visit_print_format (print_format* e)
            case print_format::conv_unsigned_uppercase_hex:
            case print_format::conv_unsigned_lowercase_hex:
            case print_format::conv_binary:
+           case print_format::conv_char:
            case print_format::conv_memory:
+           case print_format::conv_memory_hex:
              wanted = pe_long;
              break;
 
@@ -3187,7 +3850,7 @@ typeresolution_info::visit_print_format (print_format* e)
            }
        }
     }
-  
+
   if (unresolved_args == 0)
     {
       if (e->type == pe_unknown)
@@ -3195,7 +3858,7 @@ typeresolution_info::visit_print_format (print_format* e)
          if (e->print_to_stream)
            e->type = pe_long;
          else
-           e->type = pe_string;      
+           e->type = pe_string;
          resolved (e->tok, e->type);
        }
     }
@@ -3207,7 +3870,7 @@ typeresolution_info::visit_print_format (print_format* e)
 }
 
 
-void 
+void
 typeresolution_info::visit_stat_op (stat_op* e)
 {
   t = pe_stats;
@@ -3221,7 +3884,7 @@ typeresolution_info::visit_stat_op (stat_op* e)
     mismatch (e->tok, e->type, pe_long);
 }
 
-void 
+void
 typeresolution_info::visit_hist_op (hist_op* e)
 {
   t = pe_stats;
@@ -3307,3 +3970,4 @@ typeresolution_info::resolved (const token*, exp_type)
   num_newly_resolved ++;
 }
 
+/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
This page took 0.096724 seconds and 5 git commands to generate.