mirror of
https://forge.sourceware.org/marek/gcc.git
synced 2026-02-22 03:47:02 -05:00
1006 lines
24 KiB
C++
1006 lines
24 KiB
C++
/* Operations within the code being analyzed.
|
|
Copyright (C) 2019-2026 Free Software Foundation, Inc.
|
|
Contributed by David Malcolm <dmalcolm@redhat.com>.
|
|
|
|
This file is part of GCC.
|
|
|
|
GCC is free software; you can redistribute it and/or modify it
|
|
under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3, or (at your option)
|
|
any later version.
|
|
|
|
GCC is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GCC; see the file COPYING3. If not see
|
|
<http://www.gnu.org/licenses/>. */
|
|
|
|
#ifndef GCC_ANALYZER_OPS_H
|
|
#define GCC_ANALYZER_OPS_H
|
|
|
|
#include "except.h"
|
|
#include "gimple-walk.h"
|
|
|
|
namespace ana {
|
|
|
|
class operation;
|
|
class control_flow_op;
|
|
class call_and_return_op;
|
|
class phis_for_edge_op;
|
|
|
|
class callsite_expr;
|
|
|
|
struct operation_context
|
|
{
|
|
operation_context (exploded_graph &eg,
|
|
exploded_node &src_enode,
|
|
const superedge &sedge)
|
|
: m_eg (eg),
|
|
m_src_enode (src_enode),
|
|
m_sedge (sedge)
|
|
{
|
|
}
|
|
|
|
void DEBUG_FUNCTION dump () const;
|
|
|
|
logger *get_logger () const;
|
|
|
|
const extrinsic_state &get_ext_state () const;
|
|
|
|
const program_point &
|
|
get_initial_point () const;
|
|
|
|
const program_state &
|
|
get_initial_state () const;
|
|
|
|
const supergraph &
|
|
get_supergraph () const;
|
|
|
|
program_point
|
|
get_next_intraprocedural_point () const;
|
|
|
|
void
|
|
add_outcome (const program_point &dst_point,
|
|
program_state dst_state,
|
|
bool could_do_work,
|
|
uncertainty_t *uncertainty,
|
|
std::unique_ptr<custom_edge_info> info = nullptr);
|
|
|
|
exploded_graph &m_eg;
|
|
exploded_node &m_src_enode;
|
|
const superedge &m_sedge;
|
|
};
|
|
|
|
/* Abstract base class for an operation along a superedge. */
|
|
|
|
class operation
|
|
{
|
|
public:
|
|
// Discriminator for concrete subclasses
|
|
enum kind
|
|
{
|
|
asm_stmt,
|
|
assignment,
|
|
predict_stmt,
|
|
return_stmt,
|
|
resx,
|
|
cond_edge,
|
|
goto_edge,
|
|
switch_edge,
|
|
eh_dispatch_try_edge,
|
|
eh_dispatch_allowed_edge,
|
|
phis,
|
|
call_and_return
|
|
};
|
|
|
|
virtual ~operation () {}
|
|
|
|
void
|
|
dump () const;
|
|
|
|
virtual std::unique_ptr<operation>
|
|
clone () const = 0;
|
|
|
|
virtual void
|
|
print_as_edge_label (pretty_printer *pp, bool user_facing) const = 0;
|
|
|
|
virtual bool
|
|
defines_ssa_name_p (const_tree ssa_name) const = 0;
|
|
|
|
virtual void
|
|
walk_load_store_addr_ops (void *,
|
|
walk_stmt_load_store_addr_fn,
|
|
walk_stmt_load_store_addr_fn,
|
|
walk_stmt_load_store_addr_fn) const = 0;
|
|
|
|
virtual const gimple *
|
|
maybe_get_stmt () const
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
virtual void
|
|
execute (operation_context &op_ctxt) const = 0;
|
|
|
|
virtual bool
|
|
execute_for_feasibility (const exploded_edge &,
|
|
feasibility_state &,
|
|
region_model_context *,
|
|
std::unique_ptr<rejected_constraint> */*out_rc*/) const
|
|
{
|
|
// no-op
|
|
return true;
|
|
}
|
|
|
|
/* Is this op suitable for bulk-merging?
|
|
It must have a single outcome, at the intraprocedural
|
|
next point, with some state. */
|
|
virtual bool
|
|
supports_bulk_merge_p () const = 0;
|
|
virtual void
|
|
update_state_for_bulk_merger (const program_state &,
|
|
program_state &) const
|
|
{
|
|
/* Must be implemented for any subclasses that return true
|
|
for supports_bulk_merge_p. */
|
|
gcc_unreachable ();
|
|
}
|
|
virtual void
|
|
add_any_events_for_eedge (const exploded_edge &eedge,
|
|
checker_path &out_path) const = 0;
|
|
|
|
virtual const control_flow_op *
|
|
dyn_cast_control_flow_op () const { return nullptr; }
|
|
|
|
virtual const call_and_return_op *
|
|
dyn_cast_call_and_return_op () const { return nullptr; }
|
|
|
|
virtual const phis_for_edge_op *
|
|
dyn_cast_phis_for_edge_op () const { return nullptr; }
|
|
|
|
enum kind get_kind () const { return m_kind; }
|
|
|
|
protected:
|
|
operation (enum kind kind_)
|
|
: m_kind (kind_)
|
|
{
|
|
}
|
|
|
|
static void
|
|
handle_on_stmt_for_state_machines (operation_context &op_ctxt,
|
|
program_state &dst_state,
|
|
path_context *path_ctxt,
|
|
bool &unknown_side_effects,
|
|
const gimple &stmt);
|
|
|
|
private:
|
|
enum kind m_kind;
|
|
};
|
|
|
|
/* Subclass for an operation representing a specific gimple stmt
|
|
that isn't control flow. */
|
|
|
|
class gimple_stmt_op : public operation
|
|
{
|
|
public:
|
|
const gimple &get_stmt () const { return m_stmt; }
|
|
|
|
void
|
|
print_as_edge_label (pretty_printer *pp, bool user_facing) const override;
|
|
|
|
bool
|
|
defines_ssa_name_p (const_tree ssa_name) const final override;
|
|
|
|
void
|
|
walk_load_store_addr_ops (void *,
|
|
walk_stmt_load_store_addr_fn,
|
|
walk_stmt_load_store_addr_fn,
|
|
walk_stmt_load_store_addr_fn) const final override;
|
|
|
|
const gimple *
|
|
maybe_get_stmt () const final override
|
|
{
|
|
return &m_stmt;
|
|
}
|
|
|
|
void
|
|
execute (operation_context &op_ctxt) const override;
|
|
|
|
void
|
|
execute_on_state (operation_context &op_ctxt,
|
|
program_state dst_state) const;
|
|
|
|
bool
|
|
execute_for_feasibility (const exploded_edge &,
|
|
feasibility_state &,
|
|
region_model_context *,
|
|
std::unique_ptr<rejected_constraint> *out_rc) const override;
|
|
|
|
virtual bool
|
|
supports_bulk_merge_p () const override;
|
|
|
|
void
|
|
add_any_events_for_eedge (const exploded_edge &eedge,
|
|
checker_path &out_path) const override;
|
|
|
|
protected:
|
|
gimple_stmt_op (enum kind kind_, const gimple &stmt)
|
|
: operation (kind_), m_stmt (stmt)
|
|
{}
|
|
|
|
private:
|
|
const gimple &m_stmt;
|
|
};
|
|
|
|
/* Various subclasses of gimple_stmt_op. */
|
|
|
|
/* An operation subclass representing the effect of a GIMPLE_ASM stmt. */
|
|
|
|
class gasm_op : public gimple_stmt_op
|
|
{
|
|
public:
|
|
gasm_op (const gasm &asm_stmt)
|
|
: gimple_stmt_op (kind::asm_stmt, asm_stmt)
|
|
{
|
|
}
|
|
|
|
std::unique_ptr<operation>
|
|
clone () const final override
|
|
{
|
|
return std::make_unique<gasm_op> (get_gasm ());
|
|
}
|
|
|
|
const gasm &get_gasm () const
|
|
{
|
|
return *as_a <const gasm *> (&get_stmt ());
|
|
}
|
|
};
|
|
|
|
/* An operation subclass representing the effect of a GIMPLE_ASSIGN stmt. */
|
|
|
|
class gassign_op : public gimple_stmt_op
|
|
{
|
|
public:
|
|
gassign_op (const gassign &assign_stmt)
|
|
: gimple_stmt_op (kind::assignment, assign_stmt)
|
|
{
|
|
}
|
|
|
|
std::unique_ptr<operation>
|
|
clone () const final override
|
|
{
|
|
return std::make_unique<gassign_op> (get_gassign ());
|
|
}
|
|
|
|
const gassign &get_gassign () const
|
|
{
|
|
return *as_a <const gassign *> (&get_stmt ());
|
|
}
|
|
};
|
|
|
|
/* An operation subclass for a GIMPLE_PREDICT stmt.
|
|
They have no effect on state, but can be useful for reconstructing
|
|
where "return" statements were in the code the user originally wrote,
|
|
to improve the reported locations in diagnostics. */
|
|
|
|
class predict_op : public gimple_stmt_op
|
|
{
|
|
public:
|
|
predict_op (const gimple &predict_stmt)
|
|
: gimple_stmt_op (kind::predict_stmt, predict_stmt)
|
|
{
|
|
gcc_assert (predict_stmt.code == GIMPLE_PREDICT);
|
|
}
|
|
|
|
std::unique_ptr<operation>
|
|
clone () const final override
|
|
{
|
|
return std::make_unique<predict_op> (get_stmt ());
|
|
}
|
|
};
|
|
|
|
/* An operation subclass representing both:
|
|
(a) the effect of a GIMPLE_RETURN stmt: copying a value into the
|
|
RESULT_DECL of the current frame, and
|
|
(b) a hint when reporting diagnostics that this is the return
|
|
path from the function (rather than say, throwing an exception). */
|
|
|
|
class greturn_op : public gimple_stmt_op
|
|
{
|
|
public:
|
|
greturn_op (const greturn &return_stmt)
|
|
: gimple_stmt_op (kind::return_stmt, return_stmt)
|
|
{
|
|
}
|
|
|
|
std::unique_ptr<operation>
|
|
clone () const final override
|
|
{
|
|
return std::make_unique<greturn_op> (get_greturn ());
|
|
}
|
|
|
|
void
|
|
execute (operation_context &op_ctxt) const final override;
|
|
|
|
bool
|
|
execute_for_feasibility (const exploded_edge &,
|
|
feasibility_state &,
|
|
region_model_context *ctxt,
|
|
std::unique_ptr<rejected_constraint> *out_rc) const override;
|
|
|
|
bool
|
|
supports_bulk_merge_p () const final override
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void
|
|
add_any_events_for_eedge (const exploded_edge &eedge,
|
|
checker_path &out_path) const final override;
|
|
|
|
const greturn &get_greturn () const
|
|
{
|
|
return *as_a <const greturn *> (&get_stmt ());
|
|
}
|
|
|
|
tree get_retval () const
|
|
{
|
|
return gimple_return_retval (&get_greturn ());
|
|
}
|
|
};
|
|
|
|
/* A concrete operation subclass representing the effect of a GIMPLE_CALL stmt.
|
|
|
|
If the function is identified and has a known body, either simulate
|
|
it interprocedurally by pushing a stack frame and transitioning to the
|
|
callee, or simulate it intraprocedurally by replaying a summary of the
|
|
effects of the call.
|
|
|
|
If the function is identified but has an unknown body,
|
|
simulate it intraprocedurally, either using a known_function
|
|
subclass for precision, or following conservative rules that
|
|
assume various side-effects.
|
|
|
|
If the function is unidentified (for some kinds of dynamic calls),
|
|
simulate it intraprocedurally, following conservative rules that
|
|
assume various side-effects.
|
|
|
|
In the various intraprocedural simulation cases, the exploded edge will
|
|
correspond to the underlying superedge.
|
|
|
|
In the interprocedural simulation case, the exploded edge will
|
|
link two supernodes in different functions, and thus will require
|
|
custom edge info.
|
|
|
|
Various subclasses exist for handling awkward special cases,
|
|
such as longjmp. */
|
|
|
|
class call_and_return_op : public gimple_stmt_op
|
|
{
|
|
public:
|
|
static std::unique_ptr<operation>
|
|
make (const gcall &call_stmt);
|
|
|
|
std::unique_ptr<operation>
|
|
clone () const override
|
|
{
|
|
return std::make_unique<call_and_return_op> (get_gcall ());
|
|
}
|
|
|
|
const gcall &get_gcall () const
|
|
{
|
|
return *as_a <const gcall *> (&get_stmt ());
|
|
}
|
|
|
|
void
|
|
execute (operation_context &op_ctxt) const override;
|
|
|
|
bool
|
|
supports_bulk_merge_p () const final override
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void
|
|
add_any_events_for_eedge (const exploded_edge &eedge,
|
|
checker_path &out_path) const override;
|
|
|
|
const call_and_return_op *
|
|
dyn_cast_call_and_return_op () const final override { return this; }
|
|
|
|
tree
|
|
map_expr_from_caller_to_callee (tree callee_fndecl,
|
|
tree caller_expr,
|
|
callsite_expr *out) const;
|
|
tree
|
|
map_expr_from_callee_to_caller (tree callee_fndecl,
|
|
tree callee_expr,
|
|
callsite_expr *out) const;
|
|
|
|
call_and_return_op (const gcall &call_stmt)
|
|
: gimple_stmt_op (kind::call_and_return, call_stmt)
|
|
{
|
|
}
|
|
|
|
const known_function *
|
|
maybe_get_known_function (const call_details &cd) const;
|
|
|
|
private:
|
|
cgraph_edge *
|
|
get_any_cgraph_edge (operation_context &op_ctxt) const;
|
|
|
|
void
|
|
replay_call_summaries (operation_context &op_ctxt,
|
|
function &called_fn,
|
|
per_function_data &called_fn_data,
|
|
region_model_context *ctxt) const;
|
|
|
|
void
|
|
replay_call_summary (operation_context &op_ctxt,
|
|
function &called_fn,
|
|
call_summary &summary,
|
|
region_model_context *ctxt) const;
|
|
|
|
tree
|
|
get_arg_for_parm (tree callee_fndecl,
|
|
tree parm,
|
|
callsite_expr *out) const;
|
|
tree
|
|
get_parm_for_arg (tree callee_fndecl,
|
|
tree arg,
|
|
callsite_expr *out) const;
|
|
};
|
|
|
|
/* A call to one of the various __analyzer_dump* functions.
|
|
These have no effect on state. */
|
|
|
|
class dump_op : public call_and_return_op
|
|
{
|
|
public:
|
|
enum dump_kind
|
|
{
|
|
state,
|
|
sarif,
|
|
dot,
|
|
state_2
|
|
};
|
|
|
|
dump_op (const gcall &call_stmt, enum dump_kind dump_kind_)
|
|
: call_and_return_op (call_stmt),
|
|
m_dump_kind (dump_kind_)
|
|
{
|
|
}
|
|
|
|
std::unique_ptr<operation>
|
|
clone () const final override
|
|
{
|
|
return std::make_unique<dump_op> (get_gcall (), m_dump_kind);
|
|
}
|
|
|
|
void
|
|
execute (operation_context &op_ctxt) const final override;
|
|
|
|
private:
|
|
enum dump_kind m_dump_kind;
|
|
};
|
|
|
|
class setjmp_op : public call_and_return_op
|
|
{
|
|
public:
|
|
setjmp_op (const gcall &call_stmt)
|
|
: call_and_return_op (call_stmt)
|
|
{
|
|
}
|
|
|
|
std::unique_ptr<operation>
|
|
clone () const final override
|
|
{
|
|
return std::make_unique<setjmp_op> (get_gcall ());
|
|
}
|
|
|
|
void
|
|
execute (operation_context &op_ctxt) const final override;
|
|
|
|
void
|
|
add_any_events_for_eedge (const exploded_edge &eedge,
|
|
checker_path &out_path) const final override;
|
|
};
|
|
|
|
class longjmp_op : public call_and_return_op
|
|
{
|
|
public:
|
|
longjmp_op (const gcall &call_stmt)
|
|
: call_and_return_op (call_stmt)
|
|
{
|
|
}
|
|
|
|
std::unique_ptr<operation>
|
|
clone () const final override
|
|
{
|
|
return std::make_unique<longjmp_op> (get_gcall ());
|
|
}
|
|
|
|
void
|
|
execute (operation_context &op_ctxt) const final override;
|
|
};
|
|
|
|
class cxa_throw_op : public call_and_return_op
|
|
{
|
|
public:
|
|
cxa_throw_op (const gcall &call_stmt, bool is_rethrow)
|
|
: call_and_return_op (call_stmt),
|
|
m_is_rethrow (is_rethrow)
|
|
{
|
|
}
|
|
|
|
std::unique_ptr<operation>
|
|
clone () const final override
|
|
{
|
|
return std::make_unique<cxa_throw_op> (get_gcall (), m_is_rethrow);
|
|
}
|
|
|
|
void
|
|
execute (operation_context &op_ctxt) const final override;
|
|
|
|
private:
|
|
bool m_is_rethrow;
|
|
};
|
|
|
|
class resx_op : public gimple_stmt_op
|
|
{
|
|
public:
|
|
resx_op (const gresx &resx_stmt)
|
|
: gimple_stmt_op (kind::resx, resx_stmt)
|
|
{
|
|
}
|
|
|
|
std::unique_ptr<operation>
|
|
clone () const final override
|
|
{
|
|
return std::make_unique<resx_op> (get_gresx ());
|
|
}
|
|
|
|
const gresx &get_gresx () const
|
|
{
|
|
return *as_a <const gresx *> (&get_stmt ());
|
|
}
|
|
|
|
void
|
|
execute (operation_context &op_ctxt) const final override;
|
|
|
|
bool
|
|
supports_bulk_merge_p () const final override
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void
|
|
add_any_events_for_eedge (const exploded_edge &eedge,
|
|
checker_path &out_path) const final override;
|
|
};
|
|
|
|
/* An abstract subclass of operation representing the filtering effect on
|
|
state of a gimple control-flow statement at the end of a BB, for
|
|
a specific CFG out-edge from that BB. */
|
|
|
|
class control_flow_op : public operation
|
|
{
|
|
public:
|
|
void
|
|
add_any_events_for_eedge (const exploded_edge &eedge,
|
|
checker_path &out_path) const override;
|
|
|
|
bool
|
|
defines_ssa_name_p (const_tree) const final override
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void
|
|
walk_load_store_addr_ops (void *,
|
|
walk_stmt_load_store_addr_fn,
|
|
walk_stmt_load_store_addr_fn,
|
|
walk_stmt_load_store_addr_fn) const final override;
|
|
|
|
const gimple *
|
|
maybe_get_stmt () const final override
|
|
{
|
|
return &m_ctrlflow_stmt;
|
|
}
|
|
|
|
virtual label_text
|
|
maybe_describe_condition (bool can_colorize) const;
|
|
|
|
void
|
|
execute (operation_context &op_ctxt) const final override;
|
|
|
|
bool
|
|
supports_bulk_merge_p () const final override
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
execute_for_feasibility (const exploded_edge &,
|
|
feasibility_state &,
|
|
region_model_context *,
|
|
std::unique_ptr<rejected_constraint> *out_rc) const override;
|
|
|
|
const control_flow_op *
|
|
dyn_cast_control_flow_op () const final override { return this; }
|
|
|
|
::edge get_cfg_edge () const { return m_cfg_edge; }
|
|
int get_flags () const { return m_cfg_edge->flags; }
|
|
int back_edge_p () const { return get_flags () & EDGE_DFS_BACK; }
|
|
|
|
const gimple &get_ctrlflow_stmt () const { return m_ctrlflow_stmt; }
|
|
|
|
protected:
|
|
control_flow_op (enum kind kind_,
|
|
::edge cfg_edge,
|
|
const gimple &ctrlflow_stmt)
|
|
: operation (kind_),
|
|
m_cfg_edge (cfg_edge),
|
|
m_ctrlflow_stmt (ctrlflow_stmt)
|
|
{}
|
|
|
|
private:
|
|
virtual bool
|
|
apply_constraints (const superedge *sedge,
|
|
region_model &model,
|
|
region_model_context *ctxt,
|
|
std::unique_ptr<rejected_constraint> *out) const = 0;
|
|
|
|
::edge m_cfg_edge;
|
|
const gimple &m_ctrlflow_stmt;
|
|
};
|
|
|
|
/* Concrete operation subclass representing filtering/applying state
|
|
transitions on a specific CFG edge after a GIMPLE_COND stmt, either the
|
|
"if (cond) is true" or the "if (cond) is false" branch. */
|
|
|
|
class gcond_edge_op : public control_flow_op
|
|
{
|
|
public:
|
|
gcond_edge_op (::edge cfg_edge,
|
|
const gcond &cond_stmt);
|
|
|
|
std::unique_ptr<operation>
|
|
clone () const final override
|
|
{
|
|
return std::make_unique<gcond_edge_op> (get_cfg_edge (),
|
|
get_gcond ());
|
|
}
|
|
|
|
void
|
|
print_as_edge_label (pretty_printer *pp,
|
|
bool user_facing) const final override;
|
|
|
|
label_text
|
|
maybe_describe_condition (bool can_colorize) const final override;
|
|
|
|
const gcond &get_gcond () const
|
|
{
|
|
return *as_a <const gcond *> (&get_ctrlflow_stmt ());
|
|
}
|
|
|
|
private:
|
|
static label_text
|
|
maybe_describe_condition (bool can_colorize,
|
|
tree lhs,
|
|
enum tree_code op,
|
|
tree rhs);
|
|
static bool should_print_expr_p (tree expr);
|
|
|
|
bool
|
|
apply_constraints (const superedge *sedge,
|
|
region_model &model,
|
|
region_model_context *ctxt,
|
|
std::unique_ptr<rejected_constraint> *out)
|
|
const final override;
|
|
|
|
bool m_true_value;
|
|
};
|
|
|
|
/* Concrete operation subclass representing filtering/applying state
|
|
transitions on a specific CFG edge after a GIMPLE_GOTO stmt, thus
|
|
handling computed gotos. */
|
|
|
|
class ggoto_edge_op : public control_flow_op
|
|
{
|
|
public:
|
|
ggoto_edge_op (::edge cfg_edge,
|
|
const ggoto &goto_stmt,
|
|
tree dst_label);
|
|
|
|
std::unique_ptr<operation>
|
|
clone () const final override
|
|
{
|
|
return std::make_unique<ggoto_edge_op> (get_cfg_edge (),
|
|
get_ggoto (),
|
|
m_dst_label);
|
|
}
|
|
|
|
void
|
|
print_as_edge_label (pretty_printer *pp,
|
|
bool user_facing) const final override;
|
|
|
|
label_text
|
|
maybe_describe_condition (bool can_colorize) const final override;
|
|
|
|
const ggoto &get_ggoto () const
|
|
{
|
|
return *as_a <const ggoto *> (&get_ctrlflow_stmt ());
|
|
}
|
|
|
|
private:
|
|
bool
|
|
apply_constraints (const superedge *sedge,
|
|
region_model &model,
|
|
region_model_context *ctxt,
|
|
std::unique_ptr<rejected_constraint> *out)
|
|
const final override;
|
|
|
|
tree m_dst_label;
|
|
};
|
|
|
|
/* Concrete operation subclass representing filtering/applying state
|
|
transitions on a specific CFG edge after a GIMPLE_SWITCH stmt, thus
|
|
handling a cluster of cases/default value. */
|
|
|
|
class switch_case_op : public control_flow_op
|
|
{
|
|
public:
|
|
switch_case_op (function &fun,
|
|
::edge cfg_edge,
|
|
const gswitch &switch_stmt,
|
|
bounded_ranges_manager &mgr);
|
|
|
|
std::unique_ptr<operation>
|
|
clone () const final override
|
|
{
|
|
return std::make_unique<switch_case_op> (*this);
|
|
}
|
|
|
|
void
|
|
print_as_edge_label (pretty_printer *pp,
|
|
bool user_facing) const final override;
|
|
|
|
bool implicitly_created_default_p () const;
|
|
|
|
const gswitch &get_gswitch () const
|
|
{
|
|
return *as_a <const gswitch *> (&get_ctrlflow_stmt ());
|
|
}
|
|
|
|
private:
|
|
bool
|
|
apply_constraints (const superedge *sedge,
|
|
region_model &model,
|
|
region_model_context *ctxt,
|
|
std::unique_ptr<rejected_constraint> *out)
|
|
const final override;
|
|
|
|
std::vector<tree> m_case_labels;
|
|
const bounded_ranges *m_all_cases_ranges;
|
|
};
|
|
|
|
/* Abstract subclass for edges from eh_dispatch statements. */
|
|
|
|
class eh_dispatch_edge_op : public control_flow_op
|
|
{
|
|
public:
|
|
static std::unique_ptr<eh_dispatch_edge_op>
|
|
make (supernode *src,
|
|
supernode *dest,
|
|
::edge cfg_edge,
|
|
const geh_dispatch &geh_dispatch_stmt);
|
|
|
|
const geh_dispatch &
|
|
get_geh_dispatch () const
|
|
{
|
|
return *as_a <const geh_dispatch *> (&get_ctrlflow_stmt ());
|
|
}
|
|
|
|
eh_region
|
|
get_eh_region () const { return m_eh_region; }
|
|
|
|
protected:
|
|
eh_dispatch_edge_op (supernode *src_snode,
|
|
supernode *dst_snode,
|
|
enum kind kind_,
|
|
::edge cfg_edge,
|
|
const geh_dispatch &geh_dispatch_stmt,
|
|
eh_region eh_reg);
|
|
|
|
supernode *get_src_snode () const { return m_src_snode; }
|
|
|
|
private:
|
|
bool
|
|
apply_constraints (const superedge *sedge,
|
|
region_model &model,
|
|
region_model_context *ctxt,
|
|
std::unique_ptr<rejected_constraint> *out)
|
|
const final override;
|
|
|
|
virtual bool
|
|
apply_eh_constraints (const superedge *sedge,
|
|
region_model &model,
|
|
region_model_context *ctxt,
|
|
tree exception_type,
|
|
std::unique_ptr<rejected_constraint> *out) const = 0;
|
|
|
|
supernode *m_src_snode;
|
|
supernode *m_dst_snode;
|
|
eh_region m_eh_region;
|
|
};
|
|
|
|
/* Concrete operation for edges from an eh_dispatch statement
|
|
for ERT_TRY regions. */
|
|
|
|
class eh_dispatch_try_edge_op : public eh_dispatch_edge_op
|
|
{
|
|
public:
|
|
eh_dispatch_try_edge_op (supernode *src_snode,
|
|
supernode *dst_snode,
|
|
::edge cfg_edge,
|
|
const geh_dispatch &geh_dispatch_stmt,
|
|
eh_region eh_reg,
|
|
eh_catch ehc);
|
|
|
|
std::unique_ptr<operation>
|
|
clone () const final override
|
|
{
|
|
return std::make_unique<eh_dispatch_try_edge_op> (*this);
|
|
}
|
|
|
|
void
|
|
print_as_edge_label (pretty_printer *pp,
|
|
bool user_facing) const final override;
|
|
|
|
void
|
|
add_any_events_for_eedge (const exploded_edge &eedge,
|
|
checker_path &out_path) const final override;
|
|
|
|
private:
|
|
bool
|
|
apply_eh_constraints (const superedge *sedge,
|
|
region_model &model,
|
|
region_model_context *ctxt,
|
|
tree exception_type,
|
|
std::unique_ptr<rejected_constraint> *out)
|
|
const final override;
|
|
|
|
eh_catch m_eh_catch;
|
|
};
|
|
|
|
/* Concrete operation for edges from an eh_dispatch statement
|
|
for ERT_ALLOWED_EXCEPTIONS regions. */
|
|
|
|
class eh_dispatch_allowed_edge_op : public eh_dispatch_edge_op
|
|
{
|
|
public:
|
|
enum eh_kind
|
|
{
|
|
expected,
|
|
unexpected
|
|
};
|
|
|
|
eh_dispatch_allowed_edge_op (supernode *src_snode,
|
|
supernode *dst_snode,
|
|
::edge cfg_edge,
|
|
const geh_dispatch &geh_dispatch_stmt,
|
|
eh_region eh_reg);
|
|
|
|
std::unique_ptr<operation>
|
|
clone () const final override
|
|
{
|
|
return std::make_unique<eh_dispatch_allowed_edge_op> (*this);
|
|
}
|
|
|
|
void
|
|
print_as_edge_label (pretty_printer *pp,
|
|
bool user_facing) const final override;
|
|
|
|
enum eh_kind get_eh_kind () const { return m_kind; }
|
|
|
|
private:
|
|
bool
|
|
apply_eh_constraints (const superedge *sedge,
|
|
region_model &model,
|
|
region_model_context *ctxt,
|
|
tree exception_type,
|
|
std::unique_ptr<rejected_constraint> *out)
|
|
const final override;
|
|
|
|
enum eh_kind m_kind;
|
|
};
|
|
|
|
/* Concrete operation subclass representing the state transition
|
|
for simultaneously handling all of the phi nodes at the entry to a BB,
|
|
after following a specific CFG in-edge.
|
|
Note that this covers multiple gimple stmts: all of the gphi stmts
|
|
at a basic block entry (albeit for just one in-edge).
|
|
This can be thought of as handling one column of the entries in the
|
|
phi nodes of a BB (for a specific in-edge).
|
|
We ignore MEM entries, and discard phi nodes purely affecting them. */
|
|
|
|
class phis_for_edge_op : public operation
|
|
{
|
|
public:
|
|
/* A "dst=src;" pair within a phi node. */
|
|
struct pair
|
|
{
|
|
tree m_dst;
|
|
tree m_src;
|
|
};
|
|
|
|
static std::unique_ptr<operation>
|
|
maybe_make (::edge cfg_in_edge);
|
|
|
|
std::unique_ptr<operation>
|
|
clone () const final override
|
|
{
|
|
return std::make_unique<phis_for_edge_op> (*this);
|
|
}
|
|
|
|
phis_for_edge_op (std::vector<pair> &&pairs);
|
|
|
|
const phis_for_edge_op *
|
|
dyn_cast_phis_for_edge_op () const final override { return this; }
|
|
|
|
void
|
|
print_as_edge_label (pretty_printer *pp,
|
|
bool user_facing) const final override;
|
|
|
|
bool
|
|
defines_ssa_name_p (const_tree ssa_name) const final override;
|
|
|
|
void
|
|
walk_load_store_addr_ops (void *,
|
|
walk_stmt_load_store_addr_fn,
|
|
walk_stmt_load_store_addr_fn,
|
|
walk_stmt_load_store_addr_fn) const final override;
|
|
void
|
|
execute (operation_context &op_ctxt) const final override;
|
|
|
|
bool
|
|
execute_for_feasibility (const exploded_edge &,
|
|
feasibility_state &,
|
|
region_model_context *,
|
|
std::unique_ptr<rejected_constraint> *out_rc) const override;
|
|
|
|
bool
|
|
supports_bulk_merge_p () const final override
|
|
{
|
|
return true;
|
|
}
|
|
void
|
|
update_state_for_bulk_merger (const program_state &src_state,
|
|
program_state &dst_state) const final override;
|
|
|
|
void
|
|
add_any_events_for_eedge (const exploded_edge &eedge,
|
|
checker_path &out_path) const final override;
|
|
|
|
const std::vector<pair> &get_pairs () const { return m_pairs; }
|
|
|
|
private:
|
|
static std::vector<pair>
|
|
get_pairs_for_phi_along_in_edge (::edge cfg_in_edge);
|
|
|
|
void
|
|
update_state (const program_state &src_state,
|
|
program_state &dst_state,
|
|
region_model_context *ctxt) const;
|
|
|
|
std::vector<pair> m_pairs;
|
|
};
|
|
|
|
} // namespace ana
|
|
|
|
#endif /* GCC_ANALYZER_OPS_H */
|