From patchwork Fri Dec 13 18:11:26 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Malcolm X-Patchwork-Id: 1209361 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=gcc-patches-return-515947-incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="Jxt+WUPb"; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="fF5FpV8Y"; dkim-atps=neutral Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 47ZJq50kjRz9sPJ for ; Sat, 14 Dec 2019 05:19:44 +1100 (AEDT) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-type:content-transfer-encoding; q=dns; s= default; b=SkAWcFZRElP4dYRODIQM6eyOW5OBM0DbQ/hthXLvzwtXvAlj1B6FP sO/iDMYMntZV185x31ufJkzRjnbn46UFTpyzxzM3vE4GP4Iw/girR5dXoqrSbUq0 9bBxB4YuZNf4ekUxquxvE4cPKTKf5qWiH4Y3YpRsTe/R38yyfKEYNk= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-type:content-transfer-encoding; s=default; bh=+U5vStcUzE7zCJvxN4RyyObSm5M=; b=Jxt+WUPbFnVeDWqSuIgANuZAaHWe dv7er1da94TMlfyPISWOLVEmvfRSxdy48ILstYNrCaPLE9tGt1x62mB4dtoQmRFo yWzWNDxrMyPAXvH5WeG8aGXK4jIlZeENjB4SNBjAvRBBbeuSBIaVLXQQ/2zB0B9R vCVu7ZB6gJIMhOY= Received: (qmail 104284 invoked by alias); 13 Dec 2019 18:12:31 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 102692 invoked by uid 89); 13 Dec 2019 18:12:15 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-22.9 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_SHORT autolearn=ham version=3.3.1 spammy=Port X-HELO: us-smtp-1.mimecast.com Received: from us-smtp-delivery-1.mimecast.com (HELO us-smtp-1.mimecast.com) (205.139.110.120) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Fri, 13 Dec 2019 18:12:05 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1576260723; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=0OM+RhvibGziBQ0brBljr6wgUNhl2sBFalU+v2fbmVo=; b=fF5FpV8YCtQpsTTBm3d/ODZ847UBFFzGQ0nN0jiuWzNj83ts6j2NmneYMFhv52cnohUjXU 993pqvpwnP+JTtq7IMshB0lZhftYTtIKFqFe13cSu2UWKcjNxgPK0cpStC/AcfrfnsIXfa BIG8Kb95J95THJfiajRR5+s6RWIOe6Q= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-238-DvKetmK1OVyeXAkABEUWMA-1; Fri, 13 Dec 2019 13:12:02 -0500 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 3164F18A6EC1 for ; Fri, 13 Dec 2019 18:12:01 +0000 (UTC) Received: from t470.redhat.com (ovpn-117-164.phx2.redhat.com [10.3.117.164]) by smtp.corp.redhat.com (Postfix) with ESMTP id C33865C241; Fri, 13 Dec 2019 18:12:00 +0000 (UTC) From: David Malcolm To: gcc-patches@gcc.gnu.org Cc: David Malcolm Subject: [PATCH 37/45] analyzer: new files: program-point.{cc|h} Date: Fri, 13 Dec 2019 13:11:26 -0500 Message-Id: <20191213181134.1830-38-dmalcolm@redhat.com> In-Reply-To: <20191213181134.1830-1-dmalcolm@redhat.com> References: <20191213181134.1830-1-dmalcolm@redhat.com> MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-IsSubscribed: yes Changed in v4: - Remove include of gcc-plugin.h, reworking includes accordingly. - Wrap everything in #if ENABLE_ANALYZER - Remove /// comment lines - Add support for more validation, part of: https://gcc.gnu.org/ml/gcc-patches/2019-11/msg02517.html - Rework logging to avoid exploded_graph multiple-inheritance (moving log_user base to a member) - Port to new param API This patch introduces function_point and program_point, classes for tracking locations within the program (the latter adding a call_string for tracking interprocedural location). gcc/ChangeLog: * analyzer/program-point.cc: New file. * analyzer/program-point.h: New file. --- gcc/analyzer/program-point.cc | 529 ++++++++++++++++++++++++++++++++++ gcc/analyzer/program-point.h | 313 ++++++++++++++++++++ 2 files changed, 842 insertions(+) create mode 100644 gcc/analyzer/program-point.cc create mode 100644 gcc/analyzer/program-point.h diff --git a/gcc/analyzer/program-point.cc b/gcc/analyzer/program-point.cc new file mode 100644 index 000000000000..8e2ead047427 --- /dev/null +++ b/gcc/analyzer/program-point.cc @@ -0,0 +1,529 @@ +/* Classes for representing locations within the program. + Copyright (C) 2019 Free Software Foundation, Inc. + Contributed by David Malcolm . + +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 +. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tree.h" +#include "gimple-pretty-print.h" +#include "gcc-rich-location.h" +#include "analyzer/program-point.h" +#include "analyzer/exploded-graph.h" +#include "analyzer/analysis-plan.h" + +#if ENABLE_ANALYZER + +/* Get a string for PK. */ + +const char * +point_kind_to_string (enum point_kind pk) +{ + switch (pk) + { + default: + gcc_unreachable (); + case PK_ORIGIN: + return "PK_ORIGIN"; + case PK_BEFORE_SUPERNODE: + return "PK_BEFORE_SUPERNODE"; + case PK_BEFORE_STMT: + return "PK_BEFORE_STMT"; + case PK_AFTER_SUPERNODE: + return "PK_AFTER_SUPERNODE"; + case PK_EMPTY: + return "PK_EMPTY"; + case PK_DELETED: + return "PK_DELETED"; + } +} + +/* class function_point. */ + +/* Print this function_point to PP. */ + +void +function_point::print (pretty_printer *pp, const format &f) const +{ + switch (get_kind ()) + { + default: + gcc_unreachable (); + + case PK_ORIGIN: + pp_printf (pp, "origin"); + break; + + case PK_BEFORE_SUPERNODE: + { + if (m_from_edge) + pp_printf (pp, "before SN: %i (from SN: %i)", + m_supernode->m_index, m_from_edge->m_src->m_index); + else + pp_printf (pp, "before SN: %i (NULL from-edge)", + m_supernode->m_index); + f.spacer (pp); + for (gphi_iterator gpi + = const_cast(get_supernode ())->start_phis (); + !gsi_end_p (gpi); gsi_next (&gpi)) + { + const gphi *phi = gpi.phi (); + pp_gimple_stmt_1 (pp, phi, 0, (dump_flags_t)0); + } + } + break; + + case PK_BEFORE_STMT: + pp_printf (pp, "before (SN: %i stmt: %i): ", m_supernode->m_index, + m_stmt_idx); + f.spacer (pp); + pp_gimple_stmt_1 (pp, get_stmt (), 0, (dump_flags_t)0); + if (f.m_newlines) + { + pp_newline (pp); + print_source_line (pp); + } + break; + + case PK_AFTER_SUPERNODE: + pp_printf (pp, "after SN: %i", m_supernode->m_index); + break; + } +} + +/* Generate a hash value for this function_point. */ + +hashval_t +function_point::hash () const +{ + inchash::hash hstate; + if (m_supernode) + hstate.add_int (m_supernode->m_index); + hstate.add_ptr (m_from_edge); + hstate.add_int (m_stmt_idx); + hstate.add_int (m_kind); + return hstate.end (); +} + +/* Get the gimple stmt for this function_point, if any. */ + +const gimple * +function_point::get_stmt () const +{ + if (m_kind == PK_BEFORE_STMT) + return m_supernode->m_stmts[m_stmt_idx]; + else if (m_kind == PK_AFTER_SUPERNODE) + return m_supernode->get_last_stmt (); + else + return NULL; +} + +/* Get a location for this function_point, if any. */ + +location_t +function_point::get_location () const +{ + const gimple *stmt = get_stmt (); + if (stmt) + return stmt->location; + + return UNKNOWN_LOCATION; +} + +/* A subclass of diagnostic_context for use by + program_point::print_source_line. */ + +class debug_diagnostic_context : public diagnostic_context +{ +public: + debug_diagnostic_context () + { + diagnostic_initialize (this, 0); + show_line_numbers_p = true; + show_caret = true; + } + ~debug_diagnostic_context () + { + diagnostic_finish (this); + } +}; + +/* Print the source line (if any) for this function_point to PP. */ + +void +function_point::print_source_line (pretty_printer *pp) const +{ + const gimple *stmt = get_stmt (); + if (!stmt) + return; + // TODO: monospace font + debug_diagnostic_context tmp_dc; + gcc_rich_location richloc (stmt->location); + diagnostic_show_locus (&tmp_dc, &richloc, DK_ERROR); + pp_string (pp, pp_formatted_text (tmp_dc.printer)); +} + +/* class program_point. */ + +/* Print this program_point to PP. */ + +void +program_point::print (pretty_printer *pp, const format &f) const +{ + pp_string (pp, "callstring: "); + m_call_string.print (pp); + f.spacer (pp); + + m_function_point.print (pp, f); +} + +/* Dump this point to stderr. */ + +DEBUG_FUNCTION void +program_point::dump () const +{ + pretty_printer pp; + pp_show_color (&pp) = pp_show_color (global_dc->printer); + pp.buffer->stream = stderr; + print (&pp, format (true)); + pp_flush (&pp); +} + +/* Generate a hash value for this program_point. */ + +hashval_t +program_point::hash () const +{ + inchash::hash hstate; + hstate.merge_hash (m_function_point.hash ()); + hstate.merge_hash (m_call_string.hash ()); + return hstate.end (); +} + +/* Get the function * at DEPTH within the call stack. */ + +function * +program_point::get_function_at_depth (unsigned depth) const +{ + gcc_assert (depth <= m_call_string.length ()); + if (depth == m_call_string.length ()) + return m_function_point.get_function (); + else + return m_call_string[depth]->get_caller_function (); +} + +/* Assert that this object is sane. */ + +void +program_point::validate () const +{ + /* Skip this in a release build. */ +#if !CHECKING_P + return; +#endif + + m_call_string.validate (); + /* The "callee" of the final entry in the callstring should be the + function of the m_function_point. */ + if (m_call_string.length () > 0) + gcc_assert (m_call_string[m_call_string.length () - 1]->get_callee_function () + == get_function ()); +} + +/* Check to see if SUCC is a valid edge to take (ensuring that we have + interprocedurally valid paths in the exploded graph, and enforcing + recursion limits). + + Update the call string if SUCC is a call or a return. + + Return true if SUCC can be taken, or false otherwise. + + This is the "point" half of exploded_node::on_edge. */ + +bool +program_point::on_edge (exploded_graph &eg, + const superedge *succ) +{ + logger * const logger = eg.get_logger (); + LOG_FUNC (logger); + switch (succ->m_kind) + { + case SUPEREDGE_CFG_EDGE: + { + const cfg_superedge *cfg_sedge = as_a (succ); + + /* Reject abnormal edges; we special-case setjmp/longjmp. */ + if (cfg_sedge->get_flags () & EDGE_ABNORMAL) + return false; + } + break; + + case SUPEREDGE_CALL: + { + const call_superedge *call_sedge = as_a (succ); + + if (eg.get_analysis_plan ().use_summary_p (call_sedge->m_cedge)) + { + if (logger) + logger->log ("rejecting call edge: using summary instead"); + return false; + } + + /* Add the callsite to the call string. */ + m_call_string.push_call (eg.get_supergraph (), call_sedge); + + /* 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 (m_call_string.calc_recursion_depth () + > param_analyzer_max_recursion_depth) + { + if (logger) + logger->log ("rejecting call edge: recursion limit exceeded"); + // TODO: issue a sorry for this? + return false; + } + } + break; + + case SUPEREDGE_RETURN: + { + /* Require that we return to the call site in the call string. */ + if (m_call_string.empty_p ()) + { + if (logger) + logger->log ("rejecting return edge: empty call string"); + return false; + } + const return_superedge *top_of_stack = m_call_string.pop (); + if (top_of_stack != succ) + { + if (logger) + logger->log ("rejecting return edge: return to wrong callsite"); + return false; + } + } + break; + + case SUPEREDGE_INTRAPROCEDURAL_CALL: + { + const callgraph_superedge *cg_sedge + = as_a (succ); + /* Consider turning this edge into a use of an + interprocedural summary. */ + if (eg.get_analysis_plan ().use_summary_p (cg_sedge->m_cedge)) + { + if (logger) + logger->log ("using function summary for %qE in %qE", + cg_sedge->get_callee_decl (), + cg_sedge->get_caller_decl ()); + return true; + } + else + { + /* Otherwise, we ignore these edges */ + if (logger) + logger->log ("rejecting interprocedural edge"); + return false; + } + } + } + + return true; +} + +/* Comparator for program points within the same supernode, + for implementing worklist::key_t comparison operators. + Return negative if POINT_A is before POINT_B + Return positive if POINT_A is after POINT_B + Return 0 if they are equal. */ + +int +function_point::cmp_within_supernode_1 (const function_point &point_a, + const function_point &point_b) +{ + gcc_assert (point_a.get_supernode () == point_b.get_supernode ()); + + switch (point_a.m_kind) + { + default: + gcc_unreachable (); + case PK_BEFORE_SUPERNODE: + switch (point_b.m_kind) + { + default: + gcc_unreachable (); + case PK_BEFORE_SUPERNODE: + { + int a_src_idx = -1; + int b_src_idx = -1; + if (point_a.m_from_edge) + a_src_idx = point_a.m_from_edge->m_src->m_index; + if (point_b.m_from_edge) + b_src_idx = point_b.m_from_edge->m_src->m_index; + return a_src_idx - b_src_idx; + } + break; + + case PK_BEFORE_STMT: + case PK_AFTER_SUPERNODE: + return -1; + } + break; + case PK_BEFORE_STMT: + switch (point_b.m_kind) + { + default: + gcc_unreachable (); + case PK_BEFORE_SUPERNODE: + return 1; + + case PK_BEFORE_STMT: + return point_a.m_stmt_idx - point_b.m_stmt_idx; + + case PK_AFTER_SUPERNODE: + return -1; + } + break; + case PK_AFTER_SUPERNODE: + switch (point_b.m_kind) + { + default: + gcc_unreachable (); + case PK_BEFORE_SUPERNODE: + case PK_BEFORE_STMT: + return 1; + + case PK_AFTER_SUPERNODE: + return 0; + } + break; + } +} + +/* Comparator for program points within the same supernode, + for implementing worklist::key_t comparison operators. + Return negative if POINT_A is before POINT_B + Return positive if POINT_A is after POINT_B + Return 0 if they are equal. */ + +int +function_point::cmp_within_supernode (const function_point &point_a, + const function_point &point_b) +{ + int result = cmp_within_supernode_1 (point_a, point_b); + + /* Check that the ordering is symmetric */ +#if CHECKING_P + int reversed = cmp_within_supernode_1 (point_b, point_a); + gcc_assert (reversed == -result); +#endif + + return result; +} + +#if CHECKING_P + +namespace selftest { + +/* Verify that function_point::operator== works as expected. */ + +static void +test_function_point_equality () +{ + const supernode *snode = NULL; + + function_point a = function_point (snode, NULL, 0, + PK_BEFORE_SUPERNODE); + function_point b = function_point::before_supernode (snode, NULL); + ASSERT_EQ (a, b); +} + +/* Verify that function_point::cmp_within_supernode works as expected. */ + +static void +test_function_point_ordering () +{ + const supernode *snode = NULL; + const call_string call_string; + + /* Populate an array with various points within the same + snode, in order. */ + auto_vec points; + points.safe_push (function_point::before_supernode (snode, NULL)); + points.safe_push (function_point::before_stmt (snode, 0)); + points.safe_push (function_point::before_stmt (snode, 1)); + points.safe_push (function_point::after_supernode (snode)); + + /* Check all pairs. */ + unsigned i; + function_point *point_a; + FOR_EACH_VEC_ELT (points, i, point_a) + { + unsigned j; + function_point *point_b; + FOR_EACH_VEC_ELT (points, j, point_b) + { + int cmp = function_point::cmp_within_supernode (*point_a, *point_b); + if (i == j) + ASSERT_EQ (cmp, 0); + if (i < j) + ASSERT_TRUE (cmp < 0); + if (i > j) + ASSERT_TRUE (cmp > 0); + } + } +} + +/* Verify that program_point::operator== works as expected. */ + +static void +test_program_point_equality () +{ + const supernode *snode = NULL; + + const call_string cs; + + program_point a = program_point::before_supernode (snode, NULL, + cs); + + program_point b = program_point::before_supernode (snode, NULL, + cs); + + ASSERT_EQ (a, b); + // TODO: verify with non-empty callstrings, with different edges +} + +/* Run all of the selftests within this file. */ + +void +analyzer_program_point_cc_tests () +{ + test_function_point_equality (); + test_function_point_ordering (); + test_program_point_equality (); +} + +} // namespace selftest + +#endif /* CHECKING_P */ + +#endif /* #if ENABLE_ANALYZER */ diff --git a/gcc/analyzer/program-point.h b/gcc/analyzer/program-point.h new file mode 100644 index 000000000000..3471f11d0afe --- /dev/null +++ b/gcc/analyzer/program-point.h @@ -0,0 +1,313 @@ +/* Classes for representing locations within the program. + Copyright (C) 2019 Free Software Foundation, Inc. + Contributed by David Malcolm . + +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 +. */ + +#ifndef GCC_ANALYZER_PROGRAM_POINT_H +#define GCC_ANALYZER_PROGRAM_POINT_H + +#include "analyzer/call-string.h" +#include "analyzer/supergraph.h" + +class exploded_graph; + +/* An enum for distinguishing the various kinds of program_point. */ + +enum point_kind { + /* A "fake" node which has edges to all entrypoints. */ + PK_ORIGIN, + + PK_BEFORE_SUPERNODE, + PK_BEFORE_STMT, + PK_AFTER_SUPERNODE, + + /* Special values used for hash_map: */ + PK_EMPTY, + PK_DELETED, + + NUM_POINT_KINDS +}; + +extern const char *point_kind_to_string (enum point_kind pk); + +class format +{ +public: + format (bool newlines) : m_newlines (newlines) {} + + void spacer (pretty_printer *pp) const + { + if (m_newlines) + pp_newline (pp); + else + pp_space (pp); + } + + bool m_newlines; +}; + +/* A class for representing a location within the program, without + interprocedural information. + + This represents a fine-grained location within the supergraph (or + within one of its nodes). */ + +class function_point +{ +public: + function_point (const supernode *supernode, + const superedge *from_edge, + unsigned stmt_idx, + enum point_kind kind) + : m_supernode (supernode), m_from_edge (from_edge), + m_stmt_idx (stmt_idx), m_kind (kind) + { + if (from_edge) + { + gcc_checking_assert (m_kind == PK_BEFORE_SUPERNODE); + gcc_checking_assert (from_edge->get_kind () == SUPEREDGE_CFG_EDGE); + } + if (stmt_idx) + gcc_checking_assert (m_kind == PK_BEFORE_STMT); + } + + void print (pretty_printer *pp, const format &f) const; + void print_source_line (pretty_printer *pp) const; + void dump () const; + + hashval_t hash () const; + bool operator== (const function_point &other) const + { + return (m_supernode == other.m_supernode + && m_from_edge == other.m_from_edge + && m_stmt_idx == other.m_stmt_idx + && m_kind == other.m_kind); + } + + /* Accessors. */ + + const supernode *get_supernode () const { return m_supernode; } + function *get_function () const + { + if (m_supernode) + return m_supernode->m_fun; + else + return NULL; + } + const gimple *get_stmt () const; + location_t get_location () const; + enum point_kind get_kind () const { return m_kind; } + const superedge *get_from_edge () const + { + return m_from_edge; + } + unsigned get_stmt_idx () const + { + gcc_assert (m_kind == PK_BEFORE_STMT); + return m_stmt_idx; + } + + /* Factory functions for making various kinds of program_point. */ + + static function_point from_function_entry (const supergraph &sg, + function *fun) + { + return before_supernode (sg.get_node_for_function_entry (fun), + NULL); + } + + static function_point before_supernode (const supernode *supernode, + const superedge *from_edge) + { + if (from_edge && from_edge->get_kind () != SUPEREDGE_CFG_EDGE) + from_edge = NULL; + return function_point (supernode, from_edge, 0, PK_BEFORE_SUPERNODE); + } + + static function_point before_stmt (const supernode *supernode, + unsigned stmt_idx) + { + return function_point (supernode, NULL, stmt_idx, PK_BEFORE_STMT); + } + + static function_point after_supernode (const supernode *supernode) + { + return function_point (supernode, NULL, 0, PK_AFTER_SUPERNODE); + } + + /* Support for hash_map. */ + + static function_point empty () + { + return function_point (NULL, NULL, 0, PK_EMPTY); + } + static function_point deleted () + { + return function_point (NULL, NULL, 0, PK_DELETED); + } + + static int cmp_within_supernode_1 (const function_point &point_a, + const function_point &point_b); + static int cmp_within_supernode (const function_point &point_a, + const function_point &point_b); + + private: + const supernode *m_supernode; + + /* For PK_BEFORE_SUPERNODE, and only for CFG edges. */ + const superedge *m_from_edge; + + /* Only for PK_BEFORE_STMT. */ + unsigned m_stmt_idx; + + enum point_kind m_kind; +}; + +/* A class for representing a location within the program, including + interprocedural information. + + This represents a fine-grained location within the supergraph (or + within one of its nodes), along with a call string giving the + interprocedural context. */ + +class program_point +{ +public: + program_point (const function_point &fn_point, + const call_string &call_string) + : m_function_point (fn_point), + m_call_string (call_string) + { + } + + void print (pretty_printer *pp, const format &f) const; + void print_source_line (pretty_printer *pp) const; + void dump () const; + + hashval_t hash () const; + bool operator== (const program_point &other) const + { + return (m_function_point == other.m_function_point + && m_call_string == other.m_call_string); + } + + /* Accessors. */ + + const function_point &get_function_point () const { return m_function_point; } + const call_string &get_call_string () const { return m_call_string; } + + const supernode *get_supernode () const + { + return m_function_point.get_supernode (); + } + function *get_function () const + { + return m_function_point.get_function (); + } + function *get_function_at_depth (unsigned depth) const; + tree get_fndecl () const + { + gcc_assert (get_kind () != PK_ORIGIN); + return get_function ()->decl; + } + const gimple *get_stmt () const + { + return m_function_point.get_stmt (); + } + location_t get_location () const + { + return m_function_point.get_location (); + } + enum point_kind get_kind () const + { + return m_function_point.get_kind (); + } + const superedge *get_from_edge () const + { + return m_function_point.get_from_edge (); + } + unsigned get_stmt_idx () const + { + return m_function_point.get_stmt_idx (); + } + + /* Get the number of frames we expect at this program point. + This will be one more than the length of the call_string + (which stores the parent callsites), apart from the origin + node, which doesn't have any frames. */ + int get_stack_depth () const + { + if (get_kind () == PK_ORIGIN) + return 0; + return m_call_string.length () + 1; + } + + /* Factory functions for making various kinds of program_point. */ + + static program_point from_function_entry (const supergraph &sg, + function *fun) + { + return program_point (function_point::from_function_entry (sg, fun), + call_string ()); + } + + static program_point before_supernode (const supernode *supernode, + const superedge *from_edge, + const call_string &call_string) + { + return program_point (function_point::before_supernode (supernode, + from_edge), + call_string); + } + + static program_point before_stmt (const supernode *supernode, + unsigned stmt_idx, + const call_string &call_string) + { + return program_point (function_point::before_stmt (supernode, stmt_idx), + call_string); + } + + static program_point after_supernode (const supernode *supernode, + const call_string &call_string) + { + return program_point (function_point::after_supernode (supernode), + call_string); + } + + /* Support for hash_map. */ + + static program_point empty () + { + return program_point (function_point::empty (), call_string ()); + } + static program_point deleted () + { + return program_point (function_point::deleted (), call_string ()); + } + + bool on_edge (exploded_graph &eg, const superedge *succ); + + void validate () const; + + private: + const function_point m_function_point; + call_string m_call_string; +}; + +#endif /* GCC_ANALYZER_PROGRAM_POINT_H */