diff options
Diffstat (limited to 'gcc/analyzer/engine.cc')
-rw-r--r-- | gcc/analyzer/engine.cc | 970 |
1 files changed, 751 insertions, 219 deletions
diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc index 5792c14..24f0931 100644 --- a/gcc/analyzer/engine.cc +++ b/gcc/analyzer/engine.cc @@ -62,8 +62,11 @@ along with GCC; see the file COPYING3. If not see #include "analyzer/checker-path.h" #include "analyzer/state-purge.h" #include "analyzer/bar-chart.h" +#include "analyzer/call-info.h" #include <zlib.h> #include "plugin.h" +#include "target.h" +#include <memory> /* For an overview, see gcc/doc/analyzer.texi. */ @@ -75,9 +78,11 @@ namespace ana { impl_region_model_context:: impl_region_model_context (exploded_graph &eg, - const exploded_node *enode_for_diag, + exploded_node *enode_for_diag, const program_state *old_state, program_state *new_state, + uncertainty_t *uncertainty, + path_context *path_ctxt, const gimple *stmt, stmt_finder *stmt_finder) : m_eg (&eg), m_logger (eg.get_logger ()), @@ -86,40 +91,58 @@ impl_region_model_context (exploded_graph &eg, m_new_state (new_state), m_stmt (stmt), m_stmt_finder (stmt_finder), - m_ext_state (eg.get_ext_state ()) + m_ext_state (eg.get_ext_state ()), + m_uncertainty (uncertainty), + m_path_ctxt (path_ctxt) { } impl_region_model_context:: impl_region_model_context (program_state *state, const extrinsic_state &ext_state, + uncertainty_t *uncertainty, logger *logger) : m_eg (NULL), m_logger (logger), m_enode_for_diag (NULL), m_old_state (NULL), m_new_state (state), m_stmt (NULL), m_stmt_finder (NULL), - m_ext_state (ext_state) + m_ext_state (ext_state), + m_uncertainty (uncertainty), + m_path_ctxt (NULL) { } -void +bool impl_region_model_context::warn (pending_diagnostic *d) { LOG_FUNC (get_logger ()); + if (m_stmt == NULL && m_stmt_finder == NULL) + { + if (get_logger ()) + get_logger ()->log ("rejecting diagnostic: no stmt"); + delete d; + return false; + } if (m_eg) - m_eg->get_diagnostic_manager ().add_diagnostic - (m_enode_for_diag, m_enode_for_diag->get_supernode (), - m_stmt, m_stmt_finder, d); + { + m_eg->get_diagnostic_manager ().add_diagnostic + (m_enode_for_diag, m_enode_for_diag->get_supernode (), + m_stmt, m_stmt_finder, d); + return true; + } + else + { + delete d; + return false; + } } void impl_region_model_context::on_svalue_leak (const svalue *sval) { - int sm_idx; - sm_state_map *smap; - FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap) + for (sm_state_map *smap : m_new_state->m_checker_states) smap->on_svalue_leak (sval, this); } @@ -128,9 +151,7 @@ impl_region_model_context:: on_liveness_change (const svalue_set &live_svalues, const region_model *model) { - int sm_idx; - sm_state_map *smap; - FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap) + for (sm_state_map *smap : m_new_state->m_checker_states) smap->on_liveness_change (live_svalues, model, this); } @@ -138,9 +159,7 @@ void impl_region_model_context::on_unknown_change (const svalue *sval, bool is_mutable) { - int sm_idx; - sm_state_map *smap; - FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap) + for (sm_state_map *smap : m_new_state->m_checker_states) smap->on_unknown_change (sval, is_mutable, m_ext_state); } @@ -150,6 +169,56 @@ impl_region_model_context::on_escaped_function (tree fndecl) m_eg->on_escaped_function (fndecl); } +uncertainty_t * +impl_region_model_context::get_uncertainty () +{ + return m_uncertainty; +} + +/* Purge state involving SVAL. The region_model has already been purged, + so we only need to purge other state in the program_state: + the sm-state. */ + +void +impl_region_model_context::purge_state_involving (const svalue *sval) +{ + int i; + sm_state_map *smap; + FOR_EACH_VEC_ELT (m_new_state->m_checker_states, i, smap) + smap->purge_state_involving (sval, m_ext_state); +} + +void +impl_region_model_context::bifurcate (custom_edge_info *info) +{ + if (m_path_ctxt) + m_path_ctxt->bifurcate (info); + else + delete info; +} + +void +impl_region_model_context::terminate_path () +{ + if (m_path_ctxt) + return m_path_ctxt->terminate_path (); +} + +bool +impl_region_model_context::get_malloc_map (sm_state_map **out_smap, + const state_machine **out_sm, + unsigned *out_sm_idx) +{ + unsigned malloc_sm_idx; + if (!m_ext_state.get_sm_idx_by_name ("malloc", &malloc_sm_idx)) + return false; + + *out_smap = m_new_state->m_checker_states[malloc_sm_idx]; + *out_sm = &m_ext_state.get_sm (malloc_sm_idx); + *out_sm_idx = malloc_sm_idx; + return true; +} + /* struct setjmp_record. */ int @@ -199,17 +268,19 @@ public: impl_sm_context (exploded_graph &eg, int sm_idx, const state_machine &sm, - const exploded_node *enode_for_diag, + exploded_node *enode_for_diag, const program_state *old_state, program_state *new_state, const sm_state_map *old_smap, sm_state_map *new_smap, + path_context *path_ctxt, stmt_finder *stmt_finder = NULL) : sm_context (sm_idx, sm), m_logger (eg.get_logger ()), m_eg (eg), m_enode_for_diag (enode_for_diag), m_old_state (old_state), m_new_state (new_state), m_old_smap (old_smap), m_new_smap (new_smap), + m_path_ctxt (path_ctxt), m_stmt_finder (stmt_finder) { } @@ -219,27 +290,36 @@ public: tree get_fndecl_for_call (const gcall *call) FINAL OVERRIDE { impl_region_model_context old_ctxt - (m_eg, m_enode_for_diag, NULL, NULL/*m_enode->get_state ()*/, - call); + (m_eg, m_enode_for_diag, NULL, NULL, NULL/*m_enode->get_state ()*/, + NULL, call); region_model *model = m_new_state->m_region_model; return model->get_fndecl_for_call (call, &old_ctxt); } - state_machine::state_t get_state (const gimple *stmt, + state_machine::state_t get_state (const gimple *stmt ATTRIBUTE_UNUSED, tree var) { logger * const logger = get_logger (); LOG_FUNC (logger); - impl_region_model_context old_ctxt - (m_eg, m_enode_for_diag, NULL, NULL/*m_enode->get_state ()*/, - stmt); + /* Use NULL ctxt on this get_rvalue call to avoid triggering + uninitialized value warnings. */ const svalue *var_old_sval - = m_old_state->m_region_model->get_rvalue (var, &old_ctxt); + = m_old_state->m_region_model->get_rvalue (var, NULL); state_machine::state_t current = m_old_smap->get_state (var_old_sval, m_eg.get_ext_state ()); return current; } + state_machine::state_t get_state (const gimple *stmt ATTRIBUTE_UNUSED, + const svalue *sval) + { + logger * const logger = get_logger (); + LOG_FUNC (logger); + state_machine::state_t current + = m_old_smap->get_state (sval, m_eg.get_ext_state ()); + return current; + } + void set_next_state (const gimple *stmt, tree var, @@ -248,22 +328,18 @@ public: { logger * const logger = get_logger (); LOG_FUNC (logger); - impl_region_model_context old_ctxt - (m_eg, m_enode_for_diag, NULL, NULL/*m_enode->get_state ()*/, - stmt); - const svalue *var_old_sval - = m_old_state->m_region_model->get_rvalue (var, &old_ctxt); - impl_region_model_context new_ctxt (m_eg, m_enode_for_diag, m_old_state, m_new_state, + NULL, NULL, stmt); const svalue *var_new_sval = m_new_state->m_region_model->get_rvalue (var, &new_ctxt); const svalue *origin_new_sval = m_new_state->m_region_model->get_rvalue (origin, &new_ctxt); + /* We use the new sval here to avoid issues with uninitialized values. */ state_machine::state_t current - = m_old_smap->get_state (var_old_sval, m_eg.get_ext_state ()); + = m_old_smap->get_state (var_new_sval, m_eg.get_ext_state ()); if (logger) logger->log ("%s: state transition of %qE: %s -> %s", m_sm.get_name (), @@ -274,13 +350,48 @@ public: to, origin_new_sval, m_eg.get_ext_state ()); } + void set_next_state (const gimple *stmt, + const svalue *sval, + state_machine::state_t to, + tree origin) + { + logger * const logger = get_logger (); + LOG_FUNC (logger); + impl_region_model_context old_ctxt + (m_eg, m_enode_for_diag, NULL, NULL, NULL/*m_enode->get_state ()*/, + NULL, stmt); + + impl_region_model_context new_ctxt (m_eg, m_enode_for_diag, + m_old_state, m_new_state, + NULL, NULL, + stmt); + const svalue *origin_new_sval + = m_new_state->m_region_model->get_rvalue (origin, &new_ctxt); + + state_machine::state_t current + = m_old_smap->get_state (sval, m_eg.get_ext_state ()); + if (logger) + { + logger->start_log_line (); + logger->log_partial ("%s: state transition of ", + m_sm.get_name ()); + sval->dump_to_pp (logger->get_printer (), true); + logger->log_partial (": %s -> %s", + current->get_name (), + to->get_name ()); + logger->end_log_line (); + } + m_new_smap->set_state (m_new_state->m_region_model, sval, + to, origin_new_sval, m_eg.get_ext_state ()); + } + void warn (const supernode *snode, const gimple *stmt, tree var, pending_diagnostic *d) FINAL OVERRIDE { LOG_FUNC (get_logger ()); gcc_assert (d); // take ownership impl_region_model_context old_ctxt - (m_eg, m_enode_for_diag, m_old_state, m_new_state, NULL); + (m_eg, m_enode_for_diag, m_old_state, m_new_state, NULL, NULL, NULL); const svalue *var_old_sval = m_old_state->m_region_model->get_rvalue (var, &old_ctxt); @@ -317,6 +428,11 @@ public: return expr; } + tree get_diagnostic_tree (const svalue *sval) FINAL OVERRIDE + { + return m_new_state->m_region_model->get_representative_tree (sval); + } + state_machine::state_t get_global_state () const FINAL OVERRIDE { return m_old_state->m_checker_states[m_sm_idx]->get_global_state (); @@ -340,7 +456,7 @@ public: if (!assign_stmt) return NULL_TREE; impl_region_model_context old_ctxt - (m_eg, m_enode_for_diag, m_old_state, m_new_state, stmt); + (m_eg, m_enode_for_diag, m_old_state, m_new_state, NULL, NULL, stmt); if (const svalue *sval = m_new_state->m_region_model->get_gassign_result (assign_stmt, &old_ctxt)) @@ -350,13 +466,19 @@ public: return NULL_TREE; } + path_context *get_path_context () const FINAL OVERRIDE + { + return m_path_ctxt; + } + log_user m_logger; exploded_graph &m_eg; - const exploded_node *m_enode_for_diag; + exploded_node *m_enode_for_diag; const program_state *m_old_state; program_state *m_new_state; const sm_state_map *m_old_smap; sm_state_map *m_new_smap; + path_context *m_path_ctxt; stmt_finder *m_stmt_finder; }; @@ -471,9 +593,22 @@ readability (const_tree expr) case SSA_NAME: { if (tree var = SSA_NAME_VAR (expr)) - /* Slightly favor the underlying var over the SSA name to - avoid having them compare equal. */ - return readability (var) - 1; + { + if (DECL_ARTIFICIAL (var)) + { + /* If we have an SSA name for an artificial var, + only use it if it has a debug expr associated with + it that fixup_tree_for_diagnostic can use. */ + if (VAR_P (var) && DECL_HAS_DEBUG_EXPR_P (var)) + return readability (DECL_DEBUG_EXPR (var)) - 1; + } + else + { + /* Slightly favor the underlying var over the SSA name to + avoid having them compare equal. */ + return readability (var) - 1; + } + } /* Avoid printing '<unknown>' for SSA names for temporaries. */ return -1; } @@ -634,12 +769,13 @@ impl_region_model_context::on_state_leak (const state_machine &sm, } } - pending_diagnostic *pd = sm.on_leak (leaked_tree); + tree leaked_tree_for_diag = fixup_tree_for_diagnostic (leaked_tree); + pending_diagnostic *pd = sm.on_leak (leaked_tree_for_diag); if (pd) m_eg->get_diagnostic_manager ().add_diagnostic (&sm, m_enode_for_diag, m_enode_for_diag->get_supernode (), m_stmt, &stmt_finder, - leaked_tree, sval, state, pd); + leaked_tree_for_diag, sval, state, pd); } /* Implementation of region_model_context::on_condition vfunc. @@ -647,7 +783,9 @@ impl_region_model_context::on_state_leak (const state_machine &sm, state transitions. */ void -impl_region_model_context::on_condition (tree lhs, enum tree_code op, tree rhs) +impl_region_model_context::on_condition (const svalue *lhs, + enum tree_code op, + const svalue *rhs) { int sm_idx; sm_state_map *smap; @@ -657,9 +795,13 @@ impl_region_model_context::on_condition (tree lhs, enum tree_code op, tree rhs) impl_sm_context sm_ctxt (*m_eg, sm_idx, sm, m_enode_for_diag, m_old_state, m_new_state, m_old_state->m_checker_states[sm_idx], - m_new_state->m_checker_states[sm_idx]); + m_new_state->m_checker_states[sm_idx], + m_path_ctxt); sm.on_condition (&sm_ctxt, - m_enode_for_diag->get_supernode (), m_stmt, + (m_enode_for_diag + ? m_enode_for_diag->get_supernode () + : NULL), + m_stmt, lhs, op, rhs); } } @@ -679,7 +821,8 @@ impl_region_model_context::on_phi (const gphi *phi, tree rhs) impl_sm_context sm_ctxt (*m_eg, sm_idx, sm, m_enode_for_diag, m_old_state, m_new_state, m_old_state->m_checker_states[sm_idx], - m_new_state->m_checker_states[sm_idx]); + m_new_state->m_checker_states[sm_idx], + m_path_ctxt); sm.on_phi (&sm_ctxt, m_enode_for_diag->get_supernode (), phi, rhs); } } @@ -949,7 +1092,7 @@ exploded_node::dump_dot (graphviz_out *gv, const dump_args_t &args) const dump_processed_stmts (pp); } - dump_saved_diagnostics (pp, args.m_eg.get_diagnostic_manager ()); + dump_saved_diagnostics (pp); args.dump_extra_info (this, pp); @@ -987,18 +1130,15 @@ exploded_node::dump_processed_stmts (pretty_printer *pp) const /* Dump any saved_diagnostics at this enode to PP. */ void -exploded_node::dump_saved_diagnostics (pretty_printer *pp, - const diagnostic_manager &dm) const +exploded_node::dump_saved_diagnostics (pretty_printer *pp) const { - for (unsigned i = 0; i < dm.get_num_diagnostics (); i++) + unsigned i; + const saved_diagnostic *sd; + FOR_EACH_VEC_ELT (m_saved_diagnostics, i, sd) { - const saved_diagnostic *sd = dm.get_saved_diagnostic (i); - if (sd->m_enode == this) - { - pp_printf (pp, "DIAGNOSTIC: %s (sd: %i)", - sd->m_d->get_kind (), sd->get_index ()); - pp_newline (pp); - } + pp_printf (pp, "DIAGNOSTIC: %s (sd: %i)", + sd->m_d->get_kind (), sd->get_index ()); + pp_newline (pp); } } @@ -1091,26 +1231,6 @@ fndecl_has_gimple_body_p (tree fndecl) namespace ana { -/* A pending_diagnostic subclass for implementing "__analyzer_dump_path". */ - -class dump_path_diagnostic - : public pending_diagnostic_subclass<dump_path_diagnostic> -{ -public: - bool emit (rich_location *richloc) FINAL OVERRIDE - { - inform (richloc, "path"); - return true; - } - - const char *get_kind () const FINAL OVERRIDE { return "dump_path_diagnostic"; } - - bool operator== (const dump_path_diagnostic &) const - { - return true; - } -}; - /* Modify STATE in place, applying the effects of the stmt at this node's point. */ @@ -1118,7 +1238,9 @@ exploded_node::on_stmt_flags exploded_node::on_stmt (exploded_graph &eg, const supernode *snode, const gimple *stmt, - program_state *state) const + program_state *state, + uncertainty_t *uncertainty, + path_context *path_ctxt) { logger *logger = eg.get_logger (); LOG_SCOPE (logger); @@ -1142,98 +1264,18 @@ exploded_node::on_stmt (exploded_graph &eg, const program_state old_state (*state); impl_region_model_context ctxt (eg, this, - &old_state, state, - stmt); + &old_state, state, uncertainty, + path_ctxt, stmt); bool unknown_side_effects = false; bool terminate_path = false; - switch (gimple_code (stmt)) - { - default: - /* No-op for now. */ - break; - - case GIMPLE_ASSIGN: - { - const gassign *assign = as_a <const gassign *> (stmt); - state->m_region_model->on_assignment (assign, &ctxt); - } - break; - - case GIMPLE_ASM: - /* No-op for now. */ - break; - - case GIMPLE_CALL: - { - /* Track whether we have a gcall to a function that's not recognized by - anything, for which we don't have a function body, or for which we - don't know the fndecl. */ - const gcall *call = as_a <const gcall *> (stmt); - - /* Debugging/test support. */ - if (is_special_named_call_p (call, "__analyzer_describe", 2)) - state->m_region_model->impl_call_analyzer_describe (call, &ctxt); - else if (is_special_named_call_p (call, "__analyzer_dump", 0)) - { - /* Handle the builtin "__analyzer_dump" by dumping state - to stderr. */ - state->dump (eg.get_ext_state (), true); - } - else if (is_special_named_call_p (call, "__analyzer_dump_path", 0)) - { - /* Handle the builtin "__analyzer_dump_path" by queuing a - diagnostic at this exploded_node. */ - ctxt.warn (new dump_path_diagnostic ()); - } - else if (is_special_named_call_p (call, "__analyzer_dump_region_model", - 0)) - { - /* Handle the builtin "__analyzer_dump_region_model" by dumping - the region model's state to stderr. */ - state->m_region_model->dump (false); - } - else if (is_special_named_call_p (call, "__analyzer_eval", 1)) - state->m_region_model->impl_call_analyzer_eval (call, &ctxt); - else if (is_special_named_call_p (call, "__analyzer_break", 0)) - { - /* Handle the builtin "__analyzer_break" by triggering a - breakpoint. */ - /* TODO: is there a good cross-platform way to do this? */ - raise (SIGINT); - } - else if (is_special_named_call_p (call, - "__analyzer_dump_exploded_nodes", - 1)) - { - /* This is handled elsewhere. */ - } - else if (is_setjmp_call_p (call)) - state->m_region_model->on_setjmp (call, this, &ctxt); - else if (is_longjmp_call_p (call)) - { - on_longjmp (eg, call, state, &ctxt); - return on_stmt_flags::terminate_path (); - } - else - unknown_side_effects - = state->m_region_model->on_call_pre (call, &ctxt, &terminate_path); - } - break; - - case GIMPLE_RETURN: - { - const greturn *return_ = as_a <const greturn *> (stmt); - state->m_region_model->on_return (return_, &ctxt); - } - break; - } + on_stmt_pre (eg, stmt, state, &terminate_path, + &unknown_side_effects, &ctxt); if (terminate_path) return on_stmt_flags::terminate_path (); - bool any_sm_changes = false; int sm_idx; sm_state_map *smap; FOR_EACH_VEC_ELT (old_state.m_checker_states, sm_idx, smap) @@ -1243,18 +1285,77 @@ exploded_node::on_stmt (exploded_graph &eg, = old_state.m_checker_states[sm_idx]; sm_state_map *new_smap = state->m_checker_states[sm_idx]; impl_sm_context sm_ctxt (eg, sm_idx, sm, this, &old_state, state, - old_smap, new_smap); + old_smap, new_smap, path_ctxt); + /* Allow the state_machine to handle the stmt. */ if (sm.on_stmt (&sm_ctxt, snode, stmt)) unknown_side_effects = false; - if (*old_smap != *new_smap) - any_sm_changes = true; } + if (path_ctxt->terminate_path_p ()) + return on_stmt_flags::terminate_path (); + + on_stmt_post (stmt, state, unknown_side_effects, &ctxt); + + return on_stmt_flags (); +} + +/* Handle the pre-sm-state part of STMT, modifying STATE in-place. + Write true to *OUT_TERMINATE_PATH if the path should be terminated. + Write true to *OUT_UNKNOWN_SIDE_EFFECTS if the stmt has unknown + side effects. */ + +void +exploded_node::on_stmt_pre (exploded_graph &eg, + const gimple *stmt, + program_state *state, + bool *out_terminate_path, + bool *out_unknown_side_effects, + region_model_context *ctxt) +{ + /* Handle special-case calls that require the full program_state. */ if (const gcall *call = dyn_cast <const gcall *> (stmt)) - state->m_region_model->on_call_post (call, unknown_side_effects, &ctxt); + { + if (is_special_named_call_p (call, "__analyzer_dump", 0)) + { + /* Handle the builtin "__analyzer_dump" by dumping state + to stderr. */ + state->dump (eg.get_ext_state (), true); + return; + } + else if (is_special_named_call_p (call, "__analyzer_dump_state", 2)) + state->impl_call_analyzer_dump_state (call, eg.get_ext_state (), + ctxt); + else if (is_setjmp_call_p (call)) + { + state->m_region_model->on_setjmp (call, this, ctxt); + return; + } + else if (is_longjmp_call_p (call)) + { + on_longjmp (eg, call, state, ctxt); + *out_terminate_path = true; + return; + } + } + + /* Otherwise, defer to m_region_model. */ + state->m_region_model->on_stmt_pre (stmt, + out_terminate_path, + out_unknown_side_effects, + ctxt); +} - return on_stmt_flags (any_sm_changes); +/* Handle the post-sm-state part of STMT, modifying STATE in-place. */ + +void +exploded_node::on_stmt_post (const gimple *stmt, + program_state *state, + bool unknown_side_effects, + region_model_context *ctxt) +{ + if (const gcall *call = dyn_cast <const gcall *> (stmt)) + state->m_region_model->on_call_post (call, unknown_side_effects, ctxt); } /* Consider the effect of following superedge SUCC from this node. @@ -1277,14 +1378,15 @@ bool exploded_node::on_edge (exploded_graph &eg, const superedge *succ, program_point *next_point, - program_state *next_state) const + program_state *next_state, + uncertainty_t *uncertainty) { LOG_FUNC (eg.get_logger ()); if (!next_point->on_edge (eg, succ)) return false; - if (!next_state->on_edge (eg, *this, succ)) + if (!next_state->on_edge (eg, this, succ, uncertainty)) return false; return true; @@ -1320,7 +1422,7 @@ valid_longjmp_stack_p (const program_point &longjmp_point, where the enclosing function of the "setjmp" has returned (and thus the stack frame no longer exists). */ -class stale_jmp_buf : public pending_diagnostic_subclass<dump_path_diagnostic> +class stale_jmp_buf : public pending_diagnostic_subclass<stale_jmp_buf> { public: stale_jmp_buf (const gcall *setjmp_call, const gcall *longjmp_call, @@ -1365,7 +1467,7 @@ public: { /* Compare with diagnostic_manager::add_events_for_superedge. */ const int src_stack_depth = src_point.get_stack_depth (); - m_stack_pop_event = new custom_event + m_stack_pop_event = new precanned_custom_event (src_point.get_location (), src_point.get_fndecl (), src_stack_depth, @@ -1409,7 +1511,7 @@ void exploded_node::on_longjmp (exploded_graph &eg, const gcall *longjmp_call, program_state *new_state, - region_model_context *ctxt) const + region_model_context *ctxt) { tree buf_ptr = gimple_call_arg (longjmp_call, 0); gcc_assert (POINTER_TYPE_P (TREE_TYPE (buf_ptr))); @@ -1419,7 +1521,8 @@ exploded_node::on_longjmp (exploded_graph &eg, const region *buf = new_region_model->deref_rvalue (buf_ptr_sval, buf_ptr, ctxt); - const svalue *buf_content_sval = new_region_model->get_store_value (buf); + const svalue *buf_content_sval + = new_region_model->get_store_value (buf, ctxt); const setjmp_svalue *setjmp_sval = buf_content_sval->dyn_cast_setjmp_svalue (); if (!setjmp_sval) @@ -1518,7 +1621,7 @@ exploded_node::on_longjmp (exploded_graph &eg, leaks. */ void -exploded_node::detect_leaks (exploded_graph &eg) const +exploded_node::detect_leaks (exploded_graph &eg) { LOG_FUNC_1 (eg.get_logger (), "EN: %i", m_index); @@ -1540,8 +1643,9 @@ exploded_node::detect_leaks (exploded_graph &eg) const gcc_assert (new_state.m_region_model); + uncertainty_t uncertainty; impl_region_model_context ctxt (eg, this, - &old_state, &new_state, + &old_state, &new_state, &uncertainty, NULL, get_stmt ()); const svalue *result = NULL; new_state.m_region_model->pop_frame (NULL, &result, &ctxt); @@ -1576,21 +1680,70 @@ exploded_node::dump_succs_and_preds (FILE *outf) const } } -/* class rewind_info_t : public exploded_edge::custom_info_t. */ +/* class dynamic_call_info_t : public custom_edge_info. */ + +/* Implementation of custom_edge_info::update_model vfunc + for dynamic_call_info_t. + + Update state for the dynamically discorverd calls */ + +bool +dynamic_call_info_t::update_model (region_model *model, + const exploded_edge *eedge, + region_model_context *) const +{ + gcc_assert (eedge); + const program_state &dest_state = eedge->m_dest->get_state (); + *model = *dest_state.m_region_model; + return true; +} + +/* Implementation of custom_edge_info::add_events_to_path vfunc + for dynamic_call_info_t. */ -/* Implementation of exploded_edge::custom_info_t::update_model vfunc +void +dynamic_call_info_t::add_events_to_path (checker_path *emission_path, + const exploded_edge &eedge) const +{ + const exploded_node *src_node = eedge.m_src; + const program_point &src_point = src_node->get_point (); + const int src_stack_depth = src_point.get_stack_depth (); + const exploded_node *dest_node = eedge.m_dest; + const program_point &dest_point = dest_node->get_point (); + const int dest_stack_depth = dest_point.get_stack_depth (); + + if (m_is_returning_call) + emission_path->add_event (new return_event (eedge, (m_dynamic_call + ? m_dynamic_call->location + : UNKNOWN_LOCATION), + dest_point.get_fndecl (), + dest_stack_depth)); + else + emission_path->add_event (new call_event (eedge, (m_dynamic_call + ? m_dynamic_call->location + : UNKNOWN_LOCATION), + src_point.get_fndecl (), + src_stack_depth)); + +} + +/* class rewind_info_t : public custom_edge_info. */ + +/* Implementation of custom_edge_info::update_model vfunc for rewind_info_t. Update state for the special-case of a rewind of a longjmp to a setjmp (which doesn't have a superedge, but does affect state). */ -void +bool rewind_info_t::update_model (region_model *model, - const exploded_edge &eedge) + const exploded_edge *eedge, + region_model_context *) const { - const program_point &longjmp_point = eedge.m_src->get_point (); - const program_point &setjmp_point = eedge.m_dest->get_point (); + gcc_assert (eedge); + const program_point &longjmp_point = eedge->m_src->get_point (); + const program_point &setjmp_point = eedge->m_dest->get_point (); gcc_assert (longjmp_point.get_stack_depth () >= setjmp_point.get_stack_depth ()); @@ -1598,14 +1751,15 @@ rewind_info_t::update_model (region_model *model, model->on_longjmp (get_longjmp_call (), get_setjmp_call (), setjmp_point.get_stack_depth (), NULL); + return true; } -/* Implementation of exploded_edge::custom_info_t::add_events_to_path vfunc +/* Implementation of custom_edge_info::add_events_to_path vfunc for rewind_info_t. */ void rewind_info_t::add_events_to_path (checker_path *emission_path, - const exploded_edge &eedge) + const exploded_edge &eedge) const { const exploded_node *src_node = eedge.m_src; const program_point &src_point = src_node->get_point (); @@ -1632,7 +1786,7 @@ rewind_info_t::add_events_to_path (checker_path *emission_path, exploded_edge::exploded_edge (exploded_node *src, exploded_node *dest, const superedge *sedge, - custom_info_t *custom_info) + custom_edge_info *custom_info) : dedge<eg_traits> (src, dest), m_sedge (sedge), m_custom_info (custom_info) { @@ -1967,7 +2121,25 @@ worklist::key_t::cmp (const worklist::key_t &ka, const worklist::key_t &kb) return cmp; } - /* First, order by SCC. */ + /* Sort by callstring, so that nodes with deeper call strings are processed + before those with shallower call strings. + If we have + splitting BB + / \ + / \ + fn call no fn call + \ / + \ / + join BB + then we want the path inside the function call to be fully explored up + to the return to the join BB before we explore on the "no fn call" path, + so that both enodes at the join BB reach the front of the worklist at + the same time and thus have a chance of being merged. */ + int cs_cmp = call_string::cmp (call_string_a, call_string_b); + if (cs_cmp) + return cs_cmp; + + /* Order by SCC. */ int scc_id_a = ka.get_scc_id (ka.m_enode); int scc_id_b = kb.get_scc_id (kb.m_enode); if (scc_id_a != scc_id_b) @@ -1996,11 +2168,6 @@ worklist::key_t::cmp (const worklist::key_t &ka, const worklist::key_t &kb) gcc_assert (snode_a == snode_b); - /* The points might vary by callstring; try sorting by callstring. */ - int cs_cmp = call_string::cmp (call_string_a, call_string_b); - if (cs_cmp) - return cs_cmp; - /* Order within supernode via program point. */ int within_snode_cmp = function_point::cmp_within_supernode (point_a.get_function_point (), @@ -2137,7 +2304,7 @@ exploded_graph::add_function_entry (function *fun) exploded_node * exploded_graph::get_or_create_node (const program_point &point, const program_state &state, - const exploded_node *enode_for_diag) + exploded_node *enode_for_diag) { logger * const logger = get_logger (); LOG_FUNC (logger); @@ -2172,8 +2339,9 @@ exploded_graph::get_or_create_node (const program_point &point, /* Prune state to try to improve the chances of a cache hit, avoiding generating redundant nodes. */ + uncertainty_t uncertainty; program_state pruned_state - = state.prune_for_point (*this, point, enode_for_diag); + = state.prune_for_point (*this, point, enode_for_diag, &uncertainty); pruned_state.validate (get_ext_state ()); @@ -2230,6 +2398,7 @@ exploded_graph::get_or_create_node (const program_point &point, if (pruned_state.can_merge_with_p (existing_state, point, &merged_state)) { + merged_state.validate (m_ext_state); if (logger) logger->log ("merging new state with that of EN: %i", existing_enode->m_index); @@ -2322,7 +2491,7 @@ exploded_graph::get_or_create_node (const program_point &point, exploded_edge * exploded_graph::add_edge (exploded_node *src, exploded_node *dest, const superedge *sedge, - exploded_edge::custom_info_t *custom_info) + custom_edge_info *custom_info) { if (get_logger ()) get_logger ()->log ("creating edge EN: %i -> EN: %i", @@ -2749,17 +2918,21 @@ maybe_process_run_of_before_supernode_enodes (exploded_node *enode) items.quick_push (it); const program_state &state = iter_enode->get_state (); program_state *next_state = &it->m_processed_state; + next_state->validate (m_ext_state); const program_point &iter_point = iter_enode->get_point (); if (const superedge *iter_sedge = iter_point.get_from_edge ()) { + uncertainty_t uncertainty; impl_region_model_context ctxt (*this, iter_enode, - &state, next_state, NULL); + &state, next_state, + &uncertainty, NULL, NULL); const cfg_superedge *last_cfg_superedge = iter_sedge->dyn_cast_cfg_superedge (); if (last_cfg_superedge) next_state->m_region_model->update_for_phis (snode, last_cfg_superedge, &ctxt); } + next_state->validate (m_ext_state); } /* Attempt to partition the items into a set of merged states. @@ -2776,10 +2949,12 @@ maybe_process_run_of_before_supernode_enodes (exploded_node *enode) unsigned iter_merger_idx; FOR_EACH_VEC_ELT (merged_states, iter_merger_idx, merged_state) { + merged_state->validate (m_ext_state); program_state merge (m_ext_state); if (it_state.can_merge_with_p (*merged_state, next_point, &merge)) { *merged_state = merge; + merged_state->validate (m_ext_state); it->m_merger_idx = iter_merger_idx; if (logger) logger->log ("reusing merger state %i for item %i (EN: %i)", @@ -2878,6 +3053,173 @@ stmt_requires_new_enode_p (const gimple *stmt, return false; } +/* Return true if OLD_STATE and NEW_STATE are sufficiently different that + we should split enodes and create an exploded_edge separating them + (which makes it easier to identify state changes of intereset when + constructing checker_paths). */ + +static bool +state_change_requires_new_enode_p (const program_state &old_state, + const program_state &new_state) +{ + /* Changes in dynamic extents signify creations of heap/alloca regions + and resizings of heap regions; likely to be of interest in + diagnostic paths. */ + if (old_state.m_region_model->get_dynamic_extents () + != new_state.m_region_model->get_dynamic_extents ()) + return true; + + /* Changes in sm-state are of interest. */ + int sm_idx; + sm_state_map *smap; + FOR_EACH_VEC_ELT (old_state.m_checker_states, sm_idx, smap) + { + const sm_state_map *old_smap = old_state.m_checker_states[sm_idx]; + const sm_state_map *new_smap = new_state.m_checker_states[sm_idx]; + if (*old_smap != *new_smap) + return true; + } + + return false; +} + +/* Create enodes and eedges for the function calls that doesn't have an + underlying call superedge. + + Such case occurs when GCC's middle end didn't know which function to + call but the analyzer does (with the help of current state). + + Some example such calls are dynamically dispatched calls to virtual + functions or calls that happen via function pointer. */ + +bool +exploded_graph::maybe_create_dynamic_call (const gcall *call, + tree fn_decl, + exploded_node *node, + program_state next_state, + program_point &next_point, + uncertainty_t *uncertainty, + logger *logger) +{ + LOG_FUNC (logger); + + const program_point *this_point = &node->get_point (); + function *fun = DECL_STRUCT_FUNCTION (fn_decl); + if (fun) + { + const supergraph &sg = this->get_supergraph (); + supernode *sn_entry = sg.get_node_for_function_entry (fun); + supernode *sn_exit = sg.get_node_for_function_exit (fun); + + program_point new_point + = program_point::before_supernode (sn_entry, + NULL, + this_point->get_call_string ()); + + new_point.push_to_call_stack (sn_exit, + next_point.get_supernode()); + + /* Impose a maximum recursion depth and don't analyze paths + that exceed it further. + This is something of a blunt workaround, but it only + applies to recursion (and mutual recursion), not to + general call stacks. */ + if (new_point.get_call_string ().calc_recursion_depth () + > param_analyzer_max_recursion_depth) + { + if (logger) + logger->log ("rejecting call edge: recursion limit exceeded"); + return false; + } + + next_state.push_call (*this, node, call, uncertainty); + + if (next_state.m_valid) + { + if (logger) + logger->log ("Discovered call to %s [SN: %i -> SN: %i]", + function_name(fun), + this_point->get_supernode ()->m_index, + sn_entry->m_index); + + exploded_node *enode = get_or_create_node (new_point, + next_state, + node); + if (enode) + add_edge (node,enode, NULL, + new dynamic_call_info_t (call)); + return true; + } + } + return false; +} + +/* Subclass of path_context for use within exploded_graph::process_node, + so that we can split states e.g. at "realloc" calls. */ + +class impl_path_context : public path_context +{ +public: + impl_path_context (const program_state *cur_state) + : m_cur_state (cur_state), + m_terminate_path (false) + { + } + + bool bifurcation_p () const + { + return m_custom_eedge_infos.length () > 0; + } + + const program_state &get_state_at_bifurcation () const + { + gcc_assert (m_state_at_bifurcation); + return *m_state_at_bifurcation; + } + + void + bifurcate (custom_edge_info *info) FINAL OVERRIDE + { + if (m_state_at_bifurcation) + /* Verify that the state at bifurcation is consistent when we + split into multiple out-edges. */ + gcc_assert (*m_state_at_bifurcation == *m_cur_state); + else + /* Take a copy of the cur_state at the moment when bifurcation + happens. */ + m_state_at_bifurcation + = std::unique_ptr<program_state> (new program_state (*m_cur_state)); + + /* Take ownership of INFO. */ + m_custom_eedge_infos.safe_push (info); + } + + void terminate_path () FINAL OVERRIDE + { + m_terminate_path = true; + } + + bool terminate_path_p () const FINAL OVERRIDE + { + return m_terminate_path; + } + + const vec<custom_edge_info *> & get_custom_eedge_infos () + { + return m_custom_eedge_infos; + } + +private: + const program_state *m_cur_state; + + /* Lazily-created copy of the state before the split. */ + std::unique_ptr<program_state> m_state_at_bifurcation; + + auto_vec <custom_edge_info *> m_custom_eedge_infos; + + bool m_terminate_path; +}; + /* The core of exploded_graph::process_worklist (the main analysis loop), handling one node in the worklist. @@ -2927,11 +3269,13 @@ exploded_graph::process_node (exploded_node *node) case PK_BEFORE_SUPERNODE: { program_state next_state (state); + uncertainty_t uncertainty; if (point.get_from_edge ()) { impl_region_model_context ctxt (*this, node, - &state, &next_state, NULL); + &state, &next_state, + &uncertainty, NULL, NULL); const cfg_superedge *last_cfg_superedge = point.get_from_edge ()->dyn_cast_cfg_superedge (); if (last_cfg_superedge) @@ -2969,6 +3313,10 @@ exploded_graph::process_node (exploded_node *node) the sm-state-change occurs on an edge where the src enode has exactly one stmt, the one that caused the change. */ program_state next_state (state); + + impl_path_context path_ctxt (&next_state); + + uncertainty_t uncertainty; const supernode *snode = point.get_supernode (); unsigned stmt_idx; const gimple *prev_stmt = NULL; @@ -2990,7 +3338,8 @@ exploded_graph::process_node (exploded_node *node) /* Process the stmt. */ exploded_node::on_stmt_flags flags - = node->on_stmt (*this, snode, stmt, &next_state); + = node->on_stmt (*this, snode, stmt, &next_state, &uncertainty, + &path_ctxt); node->m_num_processed_stmts++; /* If flags.m_terminate_path, stop analyzing; any nodes/edges @@ -3001,7 +3350,8 @@ exploded_graph::process_node (exploded_node *node) if (next_state.m_region_model) { impl_region_model_context ctxt (*this, node, - &old_state, &next_state, stmt); + &old_state, &next_state, + &uncertainty, NULL, stmt); program_state::detect_leaks (old_state, next_state, NULL, get_ext_state (), &ctxt); } @@ -3013,9 +3363,13 @@ exploded_graph::process_node (exploded_node *node) point.get_call_string ()) : program_point::after_supernode (point.get_supernode (), point.get_call_string ())); - next_state = next_state.prune_for_point (*this, next_point, node); + next_state = next_state.prune_for_point (*this, next_point, node, + &uncertainty); - if (flags.m_sm_changes || flag_analyzer_fine_grained) + if (flag_analyzer_fine_grained + || state_change_requires_new_enode_p (old_state, next_state) + || path_ctxt.bifurcation_p () + || path_ctxt.terminate_path_p ()) { program_point split_point = program_point::before_stmt (point.get_supernode (), @@ -3059,17 +3413,77 @@ exploded_graph::process_node (exploded_node *node) point.get_call_string ()) : program_point::after_supernode (point.get_supernode (), point.get_call_string ())); - exploded_node *next = get_or_create_node (next_point, next_state, node); - if (next) - add_edge (node, next, NULL); + if (path_ctxt.terminate_path_p ()) + { + if (logger) + logger->log ("not adding node: terminating path"); + } + else + { + exploded_node *next + = get_or_create_node (next_point, next_state, node); + if (next) + add_edge (node, next, NULL); + } + + /* If we have custom edge infos, "bifurcate" the state + accordingly, potentially creating a new state/enode/eedge + instances. For example, to handle a "realloc" call, we + might split into 3 states, for the "failure", + "resizing in place", and "moving to a new buffer" cases. */ + for (auto edge_info : path_ctxt.get_custom_eedge_infos ()) + { + if (logger) + { + logger->start_log_line (); + logger->log_partial ("bifurcating for edge: "); + edge_info->print (logger->get_printer ()); + logger->end_log_line (); + } + program_state bifurcated_new_state + (path_ctxt.get_state_at_bifurcation ()); + + /* Apply edge_info to state. */ + impl_region_model_context + bifurcation_ctxt (*this, + NULL, // enode_for_diag + &path_ctxt.get_state_at_bifurcation (), + &bifurcated_new_state, + NULL, // uncertainty_t *uncertainty + NULL, // path_context *path_ctxt + stmt); + if (edge_info->update_model (bifurcated_new_state.m_region_model, + NULL, /* no exploded_edge yet. */ + &bifurcation_ctxt)) + { + exploded_node *next2 + = get_or_create_node (next_point, bifurcated_new_state, node); + if (next2) + { + /* Take ownership of edge_info. */ + add_edge (node, next2, NULL, edge_info); + } + else + delete edge_info; + } + else + { + if (logger) + logger->log ("infeasible state, not adding node"); + delete edge_info; + } + } } break; case PK_AFTER_SUPERNODE: { + bool found_a_superedge = false; + bool is_an_exit_block = false; /* If this is an EXIT BB, detect leaks, and potentially create a function summary. */ if (point.get_supernode ()->return_p ()) { + is_an_exit_block = true; node->detect_leaks (*this); if (flag_analyzer_call_summaries && point.get_call_string ().empty_p ()) @@ -3097,6 +3511,7 @@ exploded_graph::process_node (exploded_node *node) superedge *succ; FOR_EACH_VEC_ELT (point.get_supernode ()->m_succs, i, succ) { + found_a_superedge = true; if (logger) logger->log ("considering SN: %i -> SN: %i", succ->m_src->m_index, succ->m_dest->m_index); @@ -3105,20 +3520,102 @@ exploded_graph::process_node (exploded_node *node) = program_point::before_supernode (succ->m_dest, succ, point.get_call_string ()); program_state next_state (state); - - if (!node->on_edge (*this, succ, &next_point, &next_state)) + uncertainty_t uncertainty; + + /* Make use the current state and try to discover and analyse + indirect function calls (a call that doesn't have an underlying + cgraph edge representing call). + + Some examples of such calls are virtual function calls + and calls that happen via a function pointer. */ + if (succ->m_kind == SUPEREDGE_INTRAPROCEDURAL_CALL + && !(succ->get_any_callgraph_edge ())) + { + const gcall *call + = point.get_supernode ()->get_final_call (); + + impl_region_model_context ctxt (*this, + node, + &state, + &next_state, + &uncertainty, + NULL, + point.get_stmt()); + + region_model *model = state.m_region_model; + bool call_discovered = false; + + if (tree fn_decl = model->get_fndecl_for_call(call,&ctxt)) + call_discovered = maybe_create_dynamic_call (call, + fn_decl, + node, + next_state, + next_point, + &uncertainty, + logger); + if (!call_discovered) + { + /* An unknown function or a special function was called + at this point, in such case, don't terminate the + analysis of the current function. + + The analyzer handles calls to such functions while + analysing the stmt itself, so the the function call + must have been handled by the anlyzer till now. */ + exploded_node *next + = get_or_create_node (next_point, + next_state, + node); + if (next) + add_edge (node, next, succ); + } + } + + if (!node->on_edge (*this, succ, &next_point, &next_state, + &uncertainty)) { if (logger) logger->log ("skipping impossible edge to SN: %i", succ->m_dest->m_index); continue; } - exploded_node *next = get_or_create_node (next_point, next_state, node); if (next) add_edge (node, next, succ); } + + /* Return from the calls which doesn't have a return superedge. + Such case occurs when GCC's middle end didn't knew which function to + call but analyzer did. */ + if((is_an_exit_block && !found_a_superedge) + && (!point.get_call_string ().empty_p ())) + { + const call_string cs = point.get_call_string (); + program_point next_point + = program_point::before_supernode (cs.get_caller_node (), + NULL, + cs); + program_state next_state (state); + uncertainty_t uncertainty; + + const gcall *call + = next_point.get_supernode ()->get_returning_call (); + + if(call) + next_state.returning_call (*this, node, call, &uncertainty); + + if (next_state.m_valid) + { + next_point.pop_from_call_stack (); + exploded_node *enode = get_or_create_node (next_point, + next_state, + node); + if (enode) + add_edge (node, enode, NULL, + new dynamic_call_info_t (call, true)); + } + } } break; } @@ -3477,10 +3974,12 @@ exploded_path::feasible_p (logger *logger, feasibility_problem **out, return true; } -/* Dump this path in multiline form to PP. */ +/* Dump this path in multiline form to PP. + If EXT_STATE is non-NULL, then show the nodes. */ void -exploded_path::dump_to_pp (pretty_printer *pp) const +exploded_path::dump_to_pp (pretty_printer *pp, + const extrinsic_state *ext_state) const { for (unsigned i = 0; i < m_edges.length (); i++) { @@ -3490,28 +3989,48 @@ exploded_path::dump_to_pp (pretty_printer *pp) const eedge->m_src->m_index, eedge->m_dest->m_index); pp_newline (pp); + + if (ext_state) + eedge->m_dest->dump_to_pp (pp, *ext_state); } } /* Dump this path in multiline form to FP. */ void -exploded_path::dump (FILE *fp) const +exploded_path::dump (FILE *fp, const extrinsic_state *ext_state) const { pretty_printer pp; pp_format_decoder (&pp) = default_tree_printer; pp_show_color (&pp) = pp_show_color (global_dc->printer); pp.buffer->stream = fp; - dump_to_pp (&pp); + dump_to_pp (&pp, ext_state); pp_flush (&pp); } /* Dump this path in multiline form to stderr. */ DEBUG_FUNCTION void -exploded_path::dump () const +exploded_path::dump (const extrinsic_state *ext_state) const { - dump (stderr); + dump (stderr, ext_state); +} + +/* Dump this path verbosely to FILENAME. */ + +void +exploded_path::dump_to_file (const char *filename, + const extrinsic_state &ext_state) const +{ + FILE *fp = fopen (filename, "w"); + if (!fp) + return; + pretty_printer pp; + pp_format_decoder (&pp) = default_tree_printer; + pp.buffer->stream = fp; + dump_to_pp (&pp, &ext_state); + pp_flush (&pp); + fclose (fp); } /* class feasibility_problem. */ @@ -3526,7 +4045,7 @@ feasibility_problem::dump_to_pp (pretty_printer *pp) const pp_string (pp, "; rejected constraint: "); m_rc->dump_to_pp (pp); pp_string (pp, "; rmodel: "); - m_rc->m_model.dump_to_pp (pp, true, false); + m_rc->get_model ().dump_to_pp (pp, true, false); } } @@ -3588,6 +4107,15 @@ feasibility_state::maybe_update_for_edge (logger *logger, if (const gassign *assign = dyn_cast <const gassign *> (stmt)) m_model.on_assignment (assign, NULL); + else if (const gasm *asm_stmt = dyn_cast <const gasm *> (stmt)) + m_model.on_asm_stmt (asm_stmt, NULL); + else if (const gcall *call = dyn_cast <const gcall *> (stmt)) + { + bool terminate_path; + bool unknown_side_effects + = m_model.on_call_pre (call, NULL, &terminate_path); + m_model.on_call_post (call, unknown_side_effects, NULL); + } else if (const greturn *return_ = dyn_cast <const greturn *> (stmt)) m_model.on_return (return_, NULL); } @@ -3629,7 +4157,7 @@ feasibility_state::maybe_update_for_edge (logger *logger, } else if (eedge->m_custom_info) { - eedge->m_custom_info->update_model (&m_model, *eedge); + eedge->m_custom_info->update_model (&m_model, eedge, NULL); } } @@ -4638,16 +5166,13 @@ private: break; } gv->end_tdtr (); + /* Dump any saved_diagnostics at this enode. */ - { - const diagnostic_manager &dm = m_eg.get_diagnostic_manager (); - for (unsigned i = 0; i < dm.get_num_diagnostics (); i++) - { - const saved_diagnostic *sd = dm.get_saved_diagnostic (i); - if (sd->m_enode == enode) - print_saved_diagnostic (gv, sd); - } - } + for (unsigned i = 0; i < enode->get_num_diagnostics (); i++) + { + const saved_diagnostic *sd = enode->get_saved_diagnostic (i); + print_saved_diagnostic (gv, sd); + } pp_printf (pp, "</TABLE>"); pp_printf (pp, "</TD>"); } @@ -4766,6 +5291,13 @@ impl_run_checkers (logger *logger) { LOG_SCOPE (logger); + if (logger) + { + logger->log ("BITS_BIG_ENDIAN: %i", BITS_BIG_ENDIAN ? 1 : 0); + logger->log ("BYTES_BIG_ENDIAN: %i", BYTES_BIG_ENDIAN ? 1 : 0); + logger->log ("WORDS_BIG_ENDIAN: %i", WORDS_BIG_ENDIAN ? 1 : 0); + } + /* If using LTO, ensure that the cgraph nodes have function bodies. */ cgraph_node *node; FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node) |