blob: 17edaf26276fed755ecfe62fe12ef8006011193a [file] [log] [blame]
/* Helper class for handling a call with specific arguments.
Copyright (C) 2020-2023 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/>. */
#include "config.h"
#define INCLUDE_MEMORY
#include "system.h"
#include "coretypes.h"
#include "tree.h"
#include "function.h"
#include "basic-block.h"
#include "gimple.h"
#include "diagnostic-core.h"
#include "analyzer/analyzer.h"
#include "analyzer/analyzer-logging.h"
#include "diagnostic.h"
#include "tree-diagnostic.h" /* for default_tree_printer. */
#include "gimple-pretty-print.h"
#include "analyzer/region-model.h"
#include "analyzer/call-details.h"
#if ENABLE_ANALYZER
namespace ana {
/* class call_details. */
/* call_details's ctor. */
call_details::call_details (const gcall *call, region_model *model,
region_model_context *ctxt)
: m_call (call), m_model (model), m_ctxt (ctxt),
m_lhs_type (NULL_TREE), m_lhs_region (NULL)
{
m_lhs_type = NULL_TREE;
if (tree lhs = gimple_call_lhs (call))
{
m_lhs_region = model->get_lvalue (lhs, ctxt);
m_lhs_type = TREE_TYPE (lhs);
}
}
/* Get the manager from m_model. */
region_model_manager *
call_details::get_manager () const
{
return m_model->get_manager ();
}
/* Get any logger associated with this object. */
logger *
call_details::get_logger () const
{
if (m_ctxt)
return m_ctxt->get_logger ();
else
return NULL;
}
/* Get any uncertainty_t associated with the region_model_context. */
uncertainty_t *
call_details::get_uncertainty () const
{
if (m_ctxt)
return m_ctxt->get_uncertainty ();
else
return NULL;
}
/* If the callsite has a left-hand-side region, set it to RESULT
and return true.
Otherwise do nothing and return false. */
bool
call_details::maybe_set_lhs (const svalue *result) const
{
gcc_assert (result);
if (m_lhs_region)
{
m_model->set_value (m_lhs_region, result, m_ctxt);
return true;
}
else
return false;
}
/* Return the number of arguments used by the call statement. */
unsigned
call_details::num_args () const
{
return gimple_call_num_args (m_call);
}
/* Return true if argument IDX is a size_t (or compatible with it). */
bool
call_details::arg_is_size_p (unsigned idx) const
{
return types_compatible_p (get_arg_type (idx), size_type_node);
}
/* Get the location of the call statement. */
location_t
call_details::get_location () const
{
return m_call->location;
}
/* Get argument IDX at the callsite as a tree. */
tree
call_details::get_arg_tree (unsigned idx) const
{
return gimple_call_arg (m_call, idx);
}
/* Get the type of argument IDX. */
tree
call_details::get_arg_type (unsigned idx) const
{
return TREE_TYPE (gimple_call_arg (m_call, idx));
}
/* Get argument IDX at the callsite as an svalue. */
const svalue *
call_details::get_arg_svalue (unsigned idx) const
{
tree arg = get_arg_tree (idx);
return m_model->get_rvalue (arg, m_ctxt);
}
/* Attempt to get the string literal for argument IDX, or return NULL
otherwise.
For use when implementing "__analyzer_*" functions that take
string literals. */
const char *
call_details::get_arg_string_literal (unsigned idx) const
{
const svalue *str_arg = get_arg_svalue (idx);
if (const region *pointee = str_arg->maybe_get_region ())
if (const string_region *string_reg = pointee->dyn_cast_string_region ())
{
tree string_cst = string_reg->get_string_cst ();
return TREE_STRING_POINTER (string_cst);
}
return NULL;
}
/* Attempt to get the fndecl used at this call, if known, or NULL_TREE
otherwise. */
tree
call_details::get_fndecl_for_call () const
{
return m_model->get_fndecl_for_call (m_call, m_ctxt);
}
/* Dump a multiline representation of this call to PP. */
void
call_details::dump_to_pp (pretty_printer *pp, bool simple) const
{
pp_string (pp, "gcall: ");
pp_gimple_stmt_1 (pp, m_call, 0 /* spc */, TDF_NONE /* flags */);
pp_newline (pp);
pp_string (pp, "return region: ");
if (m_lhs_region)
m_lhs_region->dump_to_pp (pp, simple);
else
pp_string (pp, "NULL");
pp_newline (pp);
for (unsigned i = 0; i < gimple_call_num_args (m_call); i++)
{
const svalue *arg_sval = get_arg_svalue (i);
pp_printf (pp, "arg %i: ", i);
arg_sval->dump_to_pp (pp, simple);
pp_newline (pp);
}
}
/* Dump a multiline representation of this call to stderr. */
DEBUG_FUNCTION void
call_details::dump (bool simple) const
{
pretty_printer pp;
pp_format_decoder (&pp) = default_tree_printer;
pp_show_color (&pp) = pp_show_color (global_dc->printer);
pp.buffer->stream = stderr;
dump_to_pp (&pp, simple);
pp_flush (&pp);
}
/* Get a conjured_svalue for this call for REG,
and purge any state already relating to that conjured_svalue. */
const svalue *
call_details::get_or_create_conjured_svalue (const region *reg) const
{
region_model_manager *mgr = m_model->get_manager ();
return mgr->get_or_create_conjured_svalue (reg->get_type (), m_call, reg,
conjured_purge (m_model, m_ctxt));
}
} // namespace ana
#endif /* #if ENABLE_ANALYZER */