// 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
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];
}
// ------------------------------------------------------------------------
// 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)
{
}
}
match_key &
-match_key::with_number()
+match_key::with_number()
{
have_parameter = true;
parameter_type = pe_long;
}
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)
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));
}
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);
}
{}
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");
return n;
}
-void
+void
match_node::bind(derived_probe_builder * e)
{
if (end)
end = e;
}
-match_node *
+match_node *
match_node::bind(string const & k)
{
return bind(match_key(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());
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
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;
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);
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;
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)
// 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();
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
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);
}
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)
{
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);
}
static unsigned max_recursion = 100;
-struct
+struct
recursion_guard
{
unsigned & i;
throw semantic_error("recursion limit reached");
++i;
}
- ~recursion_guard()
+ ~recursion_guard()
{
--i;
}
{
symbol *&sym;
- symbol_fetcher (symbol *&sym): sym(sym)
+ symbol_fetcher (symbol *&sym): sym(sym)
{}
void visit_symbol (symbol* e)
{
set<vardecl *> * mutated_vars;
- mutated_var_collector (set<vardecl *> * mm)
+ mutated_var_collector (set<vardecl *> * mm)
: mutated_vars (mm)
{}
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)
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())
if (vd)
vars_being_iterated.push_back (vd);
-
+
traversing_visitor::visit_foreach_loop (s);
if (vd)
: public traversing_visitor
{
systemtap_session & session;
-
+
stat_decl_collector(systemtap_session & sess)
: session(sess)
{}
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);
}
}
- }
+ }
}
};
{
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)
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 + "'");
}
}
}
-
+
return sess.num_errors();
}
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 *>();
}
}
- 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();
}
//
// 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 %}
// }
//
ifs->condition = notex;
p->body = new block (ifs, p->body);
}
- }
+ }
return sess.num_errors();
}
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]);
if (pending_interrupts) break;
functiondecl* fd = dome->functions[i];
- try
+ try
{
sym.current_function = fd;
sym.current_probe = 0;
}
// Pass 3: derive probes and resolve any further symbols in the
- // derived results.
+ // derived results.
for (unsigned i=0; i<dome->probes.size(); i++)
{
s.probes.push_back (dp);
dp->join_group (s);
- try
+ try
{
sym.current_function = 0;
sym.current_probe = dp;
// 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");
}
s.print_error (e);
rc ++;
}
-
+
return rc;
}
// 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)
}
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); }
}
}
{
for (unsigned i=0; i<e->statements.size(); i++)
{
- try
+ try
{
e->statements[i]->visit (this);
}
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;
}
}
}
- else
+ else
{
assert (hist);
hist->visit (this);
}
-struct
+struct
delete_statement_symresolution_info:
public traversing_visitor
{
{
if (e->referent)
return;
-
+
vardecl* d = parent->find_var (e->name, -1);
if (d)
e->referent = d;
}
};
-void
+void
symresolution_info::visit_delete_statement (delete_statement* s)
{
delete_statement_symresolution_info di (this);
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);
// must not happen
throw semantic_error ("no current probe/function", e->tok);
array->referent = v;
- }
+ }
}
else
{
}
-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);
// 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++)
{
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;
}
}
}
-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;
}
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?
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]);
}
}
// 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);
//
// 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 */)
{
{
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;
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];
{
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++;
}
}
// ------------------------------------------------------------------------
-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.
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
// 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
// ------------------------------------------------------------------------
-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);
// easy!
if (session.verbose>2)
clog << "Eliding side-effect-free null statement " << *s->tok << endl;
- *current_stmt = 0;
+ s = 0;
+ provide (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)
{
{
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);
}
{
// 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
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 "
// 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);
}
// 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 ();
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;
// 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());
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;
}
}
+
+// ------------------------------------------------------------------------
+
+// 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;
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.
int rc = 0;
bool relaxed_p = false;
+ unsigned iterations = 0;
while (! relaxed_p)
{
if (pending_interrupts) break;
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;
if (pending_interrupts) break;
relaxed_p = true; // until proven otherwise
- semantic_pass_opt5 (s, relaxed_p);
+ semantic_pass_opt6 (s, relaxed_p);
}
return rc;
// 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)
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++)
{
if (gd->type == pe_unknown)
ti.unresolved (gd->tok);
}
-
+
if (ti.num_newly_resolved == 0) // converged
{
if (ti.num_still_unresolved == 0)
}
}
}
-
+
return rc + s.num_errors();
}
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;
(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;
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;
current_probe->body->print (clog);
clog << endl;
}
- else
+ else
clog << "other" << endl;
}
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).
{
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);
t = ft;
ee->visit (this);
exp_type at = ee->type;
-
+
if (((at == pe_string) || (at == pe_long)) && ft == pe_unknown)
{
// propagate to formal arg
{
for (unsigned i=0; i<e->statements.size(); i++)
{
- try
+ try
{
t = pe_unknown;
e->statements[i]->visit (this);
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);
}
classify_indexable(e->base, array, hist);
if (hist)
- {
+ {
if (e->indexes.size() != 1)
unresolved (e->tok);
t = pe_long;
}
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++)
t = ft;
ee->visit (this);
exp_type at = ee->type;
-
+
if ((at == pe_string || at == pe_long) && ft == pe_unknown)
{
// propagate to formal type
}
t = pe_unknown;
- e->block->visit (this);
+ e->block->visit (this);
}
}
-struct delete_statement_typeresolution_info:
+struct delete_statement_typeresolution_info:
public throwing_visitor
{
typeresolution_info *parent;
{
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);
}
};
// 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;
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
invalid (e->value->tok, e->value->type);
}
-void
+void
typeresolution_info::visit_print_format (print_format* e)
{
size_t unresolved_args = 0;
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;
{
case print_format::conv_unspecified:
case print_format::conv_literal:
- case print_format::conv_size:
assert (false);
break;
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;
}
}
}
-
+
if (unresolved_args == 0)
{
if (e->type == pe_unknown)
if (e->print_to_stream)
e->type = pe_long;
else
- e->type = pe_string;
+ e->type = pe_string;
resolved (e->tok, e->type);
}
}
}
-void
+void
typeresolution_info::visit_stat_op (stat_op* e)
{
t = pe_stats;
mismatch (e->tok, e->type, pe_long);
}
-void
+void
typeresolution_info::visit_hist_op (hist_op* e)
{
t = pe_stats;
num_newly_resolved ++;
}
+/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */