diff options
Diffstat (limited to 'elaborate.cxx')
-rw-r--r-- | elaborate.cxx | 568 |
1 files changed, 522 insertions, 46 deletions
diff --git a/elaborate.cxx b/elaborate.cxx index 2f246e2c..73358a1d 100644 --- a/elaborate.cxx +++ b/elaborate.cxx @@ -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 @@ -322,7 +323,7 @@ match_node::find_and_build (systemtap_session& s, throw semantic_error (string("probe point truncated at position ") + lex_cast<string> (pos) + - " (follow:" + alternatives + ")"); + " (follow:" + alternatives + ")", loc->tok); } map<string, literal *> param_map; @@ -394,7 +395,8 @@ match_node::find_and_build (systemtap_session& s, throw semantic_error(string("probe point mismatch at position ") + lex_cast<string> (pos) + - " (alternatives:" + alternatives + ")"); + " (alternatives:" + alternatives + ")", + loc->tok); } } else @@ -409,7 +411,8 @@ match_node::find_and_build (systemtap_session& s, throw semantic_error (string("probe point mismatch at position ") + lex_cast<string> (pos) + - " (alternatives:" + alternatives + ")"); + " (alternatives:" + alternatives + ")", + loc->tok); } match_node* subnode = i->second; @@ -1194,11 +1197,40 @@ systemtap_session::systemtap_session (): 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) { @@ -1211,9 +1243,9 @@ systemtap_session::print_error (const semantic_error& e) message << "semantic error: " << e.what (); if (e.tok1 || e.tok2) message << ": "; - if (e.tok1) message << *e.tok1; + if (e.tok1) print_token (message, e.tok1); message << e.msg2; - if (e.tok2) message << *e.tok2; + if (e.tok2) print_token (message, e.tok2); message << endl; message_str = message.str(); @@ -1228,6 +1260,19 @@ systemtap_session::print_error (const semantic_error& e) print_error (* e.chain); } +void +systemtap_session::print_warning (const string& message_str, const token* tok) +{ + // Duplicate elimination + if (seen_warnings.find (message_str) == seen_warnings.end()) + { + seen_warnings.insert (message_str); + clog << "WARNING: " << message_str; + if (tok) { clog << ": "; print_token (clog, tok); } + clog << endl; + } +} + // ------------------------------------------------------------------------ // semantic processing: symbol resolution @@ -1558,7 +1603,7 @@ void semantic_pass_opt1 (systemtap_session& s, bool& relaxed_p) { if (s.functions[i]->tok->location.file == s.user_file->name && // !tapset ! s.suppress_warnings) - clog << "WARNING: eliding unused function " << *s.functions[i]->tok << endl; + s.print_warning ("eliding unused function '" + s.functions[i]->name + "'", s.functions[i]->tok); else if (s.verbose>2) clog << "Eliding unused function " << s.functions[i]->name << endl; @@ -1579,10 +1624,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); @@ -1601,7 +1646,7 @@ void semantic_pass_opt2 (systemtap_session& s, bool& relaxed_p) // Now in vut.read/written, we have a mixture of all locals, globals - for (unsigned i=0; i<s.probes.size(); i++) + for (unsigned i=0; i<s.probes.size(); i++) for (unsigned j=0; j<s.probes[i]->locals.size(); /* see below */) { vardecl* l = s.probes[i]->locals[j]; @@ -1611,7 +1656,7 @@ void semantic_pass_opt2 (systemtap_session& s, bool& relaxed_p) { if (l->tok->location.file == s.user_file->name && // !tapset ! s.suppress_warnings) - clog << "WARNING: eliding unused variable " << *l->tok << endl; + 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; @@ -1626,12 +1671,24 @@ void semantic_pass_opt2 (systemtap_session& s, bool& relaxed_p) else { if (vut.written.find (l) == vut.written.end()) - if (! s.suppress_warnings) - clog << "WARNING: read-only local variable " << *l->tok << endl; - + 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 (unsigned i=0; i<s.functions.size(); i++) for (unsigned j=0; j<s.functions[i]->locals.size(); /* see below */) { @@ -1641,7 +1698,7 @@ void semantic_pass_opt2 (systemtap_session& s, bool& relaxed_p) { if (l->tok->location.file == s.user_file->name && // !tapset ! s.suppress_warnings) - clog << "WARNING: eliding unused variable " << *l->tok << endl; + s.print_warning ("eliding unused variable '" + l->name + "'", l->tok); else if (s.verbose>2) clog << "Eliding unused local variable " << l->name << " in function " << s.functions[i]->name @@ -1657,8 +1714,25 @@ void semantic_pass_opt2 (systemtap_session& s, bool& relaxed_p) else { if (vut.written.find (l) == vut.written.end()) - if (! s.suppress_warnings) - clog << "WARNING: read-only local variable " << *l->tok << endl; + if (iterations == 0 && ! s.suppress_warnings) + { + stringstream o; + vector<vardecl*>::iterator it; + for ( it = s.functions[i]->formal_args.begin() ; + it != s.functions[i]->formal_args.end(); it++) + if (l->name != (*it)->name) + o << " " << (*it)->name; + for (it = s.functions[i]->locals.begin(); it != s.functions[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++; } } @@ -1670,7 +1744,7 @@ void semantic_pass_opt2 (systemtap_session& s, bool& relaxed_p) { if (l->tok->location.file == s.user_file->name && // !tapset ! s.suppress_warnings) - clog << "WARNING: eliding unused variable " << *l->tok << endl; + s.print_warning ("eliding unused variable '" + l->name + "'", l->tok); else if (s.verbose>2) clog << "Eliding unused global variable " << l->name << endl; @@ -1683,10 +1757,19 @@ void semantic_pass_opt2 (systemtap_session& s, bool& relaxed_p) } else { - if (vut.written.find (l) == vut.written.end() && - ! l->init) // no initializer - if (! s.suppress_warnings) - clog << "WARNING: read-only global variable " << *l->tok << endl; + 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++; } } @@ -1912,7 +1995,20 @@ dead_stmtexpr_remover::visit_block (block *s) current_stmt = & s->statements[i]; s->statements[i]->visit (this); if (*current_stmt != 0) - new_stmts.push_back (*current_stmt); + { + // flatten nested blocks into this one + block *b = dynamic_cast<block *>(*current_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 (*current_stmt); + } current_stmt = last_stmt; } if (new_stmts.size() == 0) @@ -1948,27 +2044,49 @@ dead_stmtexpr_remover::visit_if_statement (if_statement *s) } 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; + *current_stmt = 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; + *current_stmt = es; + } + } + 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; - } } void @@ -2084,8 +2202,7 @@ void semantic_pass_opt4 (systemtap_session& s, bool& relaxed_p) if (p->body == 0) { if (! s.suppress_warnings) - clog << "WARNING: side-effect-free probe '" << p->name << "' " - << *p->tok << endl; + s.print_warning ("side-effect-free probe '" + p->name + "'", p->tok); p->body = new null_statement(); p->body->tok = p->tok; @@ -2109,8 +2226,7 @@ void semantic_pass_opt4 (systemtap_session& s, bool& relaxed_p) if (fn->body == 0) { if (! s.suppress_warnings) - clog << "WARNING: side-effect-free function '" << fn->name << "' " - << *fn->tok << endl; + s.print_warning ("side-effect-free function '" + fn->name + "'", fn->tok); fn->body = new null_statement(); fn->body->tok = fn->tok; @@ -2124,6 +2240,362 @@ 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. +struct void_statement_reducer: public traversing_visitor +{ + systemtap_session& session; + bool& relaxed_p; + statement** current_stmt; // pointer to current stmt* being iterated + expr_statement* current_expr; // pointer to current expr being iterated + set<vardecl*> focal_vars; // vars considered subject to side-effects + + void_statement_reducer(systemtap_session& s, bool& r): + session(s), relaxed_p(r), current_stmt(0), current_expr(0) {} + + // these just maintain current_stmt while recursing, but don't visit + // expressions in the conditional / loop controls. + void visit_expr_statement (expr_statement* s); + void visit_block (block *s); + 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) {} + void visit_arrayindex (arrayindex* e) {} + void visit_stat_op (stat_op* e) {} + void visit_hist_op (hist_op* e) {} + + // these can't be reduced because they always have an effect + void visit_return_statement (return_statement* s) {} + void visit_delete_statement (delete_statement* s) {} + void visit_pre_crement (pre_crement* e) {} + void visit_post_crement (post_crement* e) {} + void visit_assignment (assignment* e) {} +}; + + +void +void_statement_reducer::visit_expr_statement (expr_statement* s) +{ + assert(!current_expr); // it shouldn't be possible to have nested expr's + current_expr = s; + s->value->visit (this); + current_expr = NULL; +} + +void +void_statement_reducer::visit_block (block *s) +{ + statement** last_stmt = current_stmt; + for (unsigned i=0; i<s->statements.size(); i++ ) + { + current_stmt = & s->statements[i]; + s->statements[i]->visit (this); + } + current_stmt = last_stmt; +} + +void +void_statement_reducer::visit_if_statement (if_statement* s) +{ + statement** last_stmt = current_stmt; + current_stmt = & s->thenblock; + s->thenblock->visit (this); + + if (s->elseblock) + { + current_stmt = & s->elseblock; + s->elseblock->visit (this); + } + current_stmt = last_stmt; +} + +void +void_statement_reducer::visit_for_loop (for_loop* s) +{ + statement** last_stmt = current_stmt; + current_stmt = & s->block; + s->block->visit (this); + current_stmt = last_stmt; +} + +void +void_statement_reducer::visit_foreach_loop (foreach_loop* s) +{ + statement** last_stmt = current_stmt; + current_stmt = & s->block; + s->block->visit (this); + current_stmt = last_stmt; +} + +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. + + assert(current_expr && current_expr->value == e); + + 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; + *current_stmt = is; + current_expr = NULL; + + 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; +} + +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. + + assert(current_expr && current_expr->value == e); + + 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; + *current_stmt = is; + current_expr = NULL; + + expr_statement *es = new expr_statement; + es->value = e->right; + es->tok = es->value->tok; + is->thenblock = es; + + is->visit(this); + relaxed_p = false; +} + +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. + + assert(current_expr && current_expr->value == e); + + 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; + *current_stmt = is; + current_expr = NULL; + + 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; +} + +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. + + assert(current_expr && current_expr->value == e); + + if (session.verbose>2) + clog << "Eliding unused binary " << *e->tok << endl; + + block *b = new block; + b->tok = current_expr->tok; + *current_stmt = b; + current_expr = NULL; + + 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; +} + +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 + + assert(current_expr && current_expr->value == e); + + if (session.verbose>2) + clog << "Eliding unused unary " << *e->tok << endl; + + current_expr->value = e->operand; + current_expr->tok = current_expr->value->tok; + current_expr->value->visit(this); + + relaxed_p = false; +} + +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()) + 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)) + return; + + assert(current_expr && current_expr->value == e); + + if (session.verbose>2) + clog << "Eliding side-effect-free function call " << *e->tok << endl; + + block *b = new block; + b->tok = e->tok; + *current_stmt = b; + current_expr = NULL; + + 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; +} + +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()) + return; + + assert(current_expr && current_expr->value == e); + + if (session.verbose>2) + clog << "Eliding unused print " << *e->tok << endl; + + block *b = new block; + b->tok = e->tok; + *current_stmt = b; + current_expr = NULL; + + 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; +} + + +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++) + { + derived_probe* p = s.probes[i]; + vuv.current_stmt = & p->body; + p->body->visit (& vuv); + } + for (unsigned i=0; i<s.functions.size(); i++) + { + functiondecl* fn = s.functions[i]; + vuv.current_stmt = & fn->body; + fn->body->visit (& vuv); + } +} + + struct duplicate_function_remover: public functioncall_traversing_visitor { systemtap_session& s; @@ -2176,7 +2648,7 @@ 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; @@ -2232,6 +2704,7 @@ semantic_pass_optimize1 (systemtap_session& s) int rc = 0; bool relaxed_p = false; + unsigned iterations = 0; while (! relaxed_p) { if (pending_interrupts) break; @@ -2239,9 +2712,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; @@ -2263,7 +2739,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; |