LCOV - code coverage report
Current view: top level - gcc/analyzer - access-diagram.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 86.5 % 1247 1079
Test Date: 2026-05-11 19:44:49 Functions: 88.2 % 93 82
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* Text art visualizations within -fanalyzer.
       2              :    Copyright (C) 2023-2026 Free Software Foundation, Inc.
       3              : 
       4              : This file is part of GCC.
       5              : 
       6              : GCC is free software; you can redistribute it and/or modify it
       7              : under the terms of the GNU General Public License as published by
       8              : the Free Software Foundation; either version 3, or (at your option)
       9              : any later version.
      10              : 
      11              : GCC is distributed in the hope that it will be useful, but
      12              : WITHOUT ANY WARRANTY; without even the implied warranty of
      13              : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      14              : General Public License for more details.
      15              : 
      16              : You should have received a copy of the GNU General Public License
      17              : along with GCC; see the file COPYING3.  If not see
      18              : <http://www.gnu.org/licenses/>.  */
      19              : 
      20              : #define INCLUDE_ALGORITHM
      21              : #define INCLUDE_MAP
      22              : #define INCLUDE_SET
      23              : #include "analyzer/common.h"
      24              : 
      25              : #include "fold-const.h"
      26              : #include "intl.h"
      27              : 
      28              : #include "text-art/ruler.h"
      29              : 
      30              : #include "analyzer/region-model.h"
      31              : #include "analyzer/access-diagram.h"
      32              : #include "analyzer/analyzer-selftests.h"
      33              : 
      34              : #if ENABLE_ANALYZER
      35              : 
      36              : /* Consider this code:
      37              :      int32_t arr[10];
      38              :      arr[10] = x;
      39              :    where we've emitted a buffer overflow diagnostic like this:
      40              :      out-of-bounds write from byte 40 till byte 43 but 'arr' ends at byte 40
      41              : 
      42              :    We want to emit a diagram that visualizes:
      43              :    - the spatial relationship between the valid region to access, versus
      44              :    the region that was actually accessed: does it overlap, was it touching,
      45              :    close, or far away?  Was it before or after in memory?  What are the
      46              :    relative sizes involved?
      47              :    - the direction of the access (read vs write)
      48              : 
      49              :    The following code supports emitting diagrams similar to the following:
      50              : 
      51              :    #                                        +--------------------------------+
      52              :    #                                        |write from ‘x’ (type: ‘int32_t’)|
      53              :    #                                        +--------------------------------+
      54              :    #                                                        |
      55              :    #                                                        |
      56              :    #                                                        v
      57              :    #  +---------+-----------+-----------+   +--------------------------------+
      58              :    #  |   [0]   |    ...    |    [9]    |   |       after valid range        |
      59              :    #  +---------+-----------+-----------+   |                                |
      60              :    #  |   ‘arr’ (type: ‘int32_t[10]’)   |   |                                |
      61              :    #  +---------------------------------+   +--------------------------------+
      62              :    #  |~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~|   |~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~|
      63              :    #                   |                                    |
      64              :    #         +---------+--------+                 +---------+---------+
      65              :    #         |capacity: 40 bytes|                 |overflow of 4 bytes|
      66              :    #         +------------------+                 +-------------------+
      67              : 
      68              :   where the diagram is laid out via table columns where each table column
      69              :   represents either a range of bits/bytes, or is a spacing column (to highlight
      70              :   the boundary between valid vs invalid accesses).  The table columns can be
      71              :   seen via -fanalyzer-debug-text-art.  For example, here there are 5 table
      72              :   columns ("tc0" through "tc4"):
      73              : 
      74              :    #  +---------+-----------+-----------+---+--------------------------------+
      75              :    #  |   tc0   |    tc1    |    tc2    |tc3|              tc4               |
      76              :    #  +---------+-----------+-----------+---+--------------------------------+
      77              :    #  |bytes 0-3|bytes 4-35 |bytes 36-39|   |          bytes 40-43           |
      78              :    #  +---------+-----------+-----------+   +--------------------------------+
      79              :    #
      80              :    #                                        +--------------------------------+
      81              :    #                                        |write from ‘x’ (type: ‘int32_t’)|
      82              :    #                                        +--------------------------------+
      83              :    #                                                        |
      84              :    #                                                        |
      85              :    #                                                        v
      86              :    #  +---------+-----------+-----------+   +--------------------------------+
      87              :    #  |   [0]   |    ...    |    [9]    |   |       after valid range        |
      88              :    #  +---------+-----------+-----------+   |                                |
      89              :    #  |   ‘arr’ (type: ‘int32_t[10]’)   |   |                                |
      90              :    #  +---------------------------------+   +--------------------------------+
      91              :    #  |~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~|   |~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~|
      92              :    #                   |                                    |
      93              :    #         +---------+--------+                 +---------+---------+
      94              :    #         |capacity: 40 bytes|                 |overflow of 4 bytes|
      95              :    #         +------------------+                 +-------------------+
      96              : 
      97              :   The diagram is built up from the following:
      98              : 
      99              :    #                                        +--------------------------------+
     100              :    #                                        | ITEM FOR SVALUE/ACCESSED REGION|
     101              :    #                                        +--------------------------------+
     102              :    #                                                        |
     103              :    #                                                        | DIRECTION WIDGET
     104              :    #                                                        v
     105              :    #  +---------------------------------+   +--------------------------------+
     106              :    #  |   VALID REGION                  |   | INVALID ACCESS                 |
     107              :    #  +---------------------------------+   +--------------------------------+
     108              :    #
     109              :    #  |                       VALID-VS-INVALID RULER                         |
     110              : 
     111              :   i.e. a vbox_widget containing 4 child widgets laid out vertically:
     112              :   - ALIGNED CHILD WIDGET: ITEM FOR SVALUE/ACCESSED REGION
     113              :   - DIRECTION WIDGET
     114              :   - ALIGNED CHILD WIDGET: VALID AND INVALID ACCESSES
     115              :   - VALID-VS-INVALID RULER.
     116              : 
     117              :   A more complicated example, given this overflow:
     118              :      char buf[100];
     119              :      strcpy (buf, LOREM_IPSUM);
     120              : 
     121              :    01| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
     122              :    02| |[0]|[1]|[2]|[3]|[4]|[5]|   ...    |[440]|[441]|[442]|[443]|[444]|[445]|
     123              :    03| +---+---+---+---+---+---+          +-----+-----+-----+-----+-----+-----+
     124              :    04| |'L'|'o'|'r'|'e'|'m'|' '|          | 'o' | 'r' | 'u' | 'm' | '.' | NUL |
     125              :    05| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
     126              :    06| |                  string literal (type: 'char[446]')                  |
     127              :    07| +----------------------------------------------------------------------+
     128              :    08|   |   |   |   |   |   |  |  |    |    |     |     |     |     |     |
     129              :    09|   |   |   |   |   |   |  |  |    |    |     |     |     |     |     |
     130              :    10|   v   v   v   v   v   v  v  v    v    v     v     v     v     v     v
     131              :    11| +---+---------------------+----++--------------------------------------+
     132              :    12| |[0]|         ...         |[99]||          after valid range           |
     133              :    13| +---+---------------------+----+|                                      |
     134              :    14| |  'buf' (type: 'char[100]')   ||                                      |
     135              :    15| +------------------------------++--------------------------------------+
     136              :    16| |~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~||~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
     137              :    17|                |                                   |
     138              :    18|      +---------+---------+              +----------+----------+
     139              :    19|      |capacity: 100 bytes|              |overflow of 346 bytes|
     140              :    20|      +-------------------+              +---------------------+
     141              : 
     142              :  which is:
     143              : 
     144              :    01| ALIGNED CHILD WIDGET (lines 01-07): (string_region_spatial_item)-+-----+
     145              :    02| |[0]|[1]|[2]|[3]|[4]|[5]|   ...    |[440]|[441]|[442]|[443]|[444]|[445]|
     146              :    03| +---+---+---+---+---+---+          +-----+-----+-----+-----+-----+-----+
     147              :    04| |'L'|'o'|'r'|'e'|'m'|' '|          | 'o' | 'r' | 'u' | 'm' | '.' | NUL |
     148              :    05| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
     149              :    06| |                  string literal (type: 'char[446]')                  |
     150              :    07| +----------------------------------------------------------------------+
     151              :    08| DIRECTION WIDGET (lines 08-10)   |    |     |     |     |     |     |
     152              :    09|   |   |   |   |   |   |  |  |    |    |     |     |     |     |     |
     153              :    10|   v   v   v   v   v   v  v  v    v    v     v     v     v     v     v
     154              :    11| ALIGNED CHILD WIDGET (lines 11-15)-------------------------------------+
     155              :    12| VALID REGION  ...         |[99]|| INVALID ACCESS                       |
     156              :    13| +---+---------------------+----+|                                      |
     157              :    14| |  'buf' (type: 'char[100]')   ||                                      |
     158              :    15| +------------------------------++--------------------------------------+
     159              :    16| VALID-VS-INVALID RULER (lines 16-20): ~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
     160              :    17|                |                                   |
     161              :    18|      +---------+---------+              +----------+----------+
     162              :    19|      |capacity: 100 bytes|              |overflow of 346 bytes|
     163              :    20|      +-------------------+              +---------------------+
     164              : 
     165              :    We build the diagram in several phases:
     166              :    - (1) we construct an access_diagram_impl widget.  Within the ctor, we have
     167              :    these subphases:
     168              :    -   (1.1) find all of the boundaries of interest
     169              :    -   (1.2) use the boundaries to build a bit_table_map, associating bit ranges
     170              :    with table columns (e.g. "byte 0 is column 0, bytes 1-98 are column 2" etc)
     171              :    -   (1.3) create child widgets that share this table-based geometry
     172              :    - (2) ask the widget for its size request
     173              :    -   (2.1) column widths and row heights for the table are computed by
     174              :    access_diagram_impl::calc_req_size
     175              :    -   (2.2) child widgets request sizes based on these widths/heights
     176              :    - (3) create a canvas of the appropriate size
     177              :    - (4) paint the widget hierarchy to the canvas.  */
     178              : 
     179              : 
     180              : using namespace text_art;
     181              : 
     182              : namespace ana {
     183              : 
     184              : static styled_string
     185              : fmt_styled_string (style_manager &sm,
     186              :                    const char *fmt, ...)
     187              :     ATTRIBUTE_GCC_DIAG(2, 3);
     188              : 
     189              : static styled_string
     190          760 : fmt_styled_string (style_manager &sm,
     191              :                    const char *fmt, ...)
     192              : {
     193          760 :   va_list ap;
     194          760 :   va_start (ap, fmt);
     195          760 :   styled_string result
     196          760 :     = styled_string::from_fmt_va (sm, default_tree_printer, fmt, &ap);
     197          760 :   va_end (ap);
     198          760 :   return result;
     199              : }
     200              : 
     201              : class access_diagram_impl;
     202              : class bit_to_table_map;
     203              : 
     204              : static void
     205           10 : pp_bit_size_t (pretty_printer *pp, bit_size_t num_bits)
     206              : {
     207           10 :   if (num_bits % BITS_PER_UNIT == 0)
     208              :     {
     209           10 :       byte_size_t num_bytes = num_bits / BITS_PER_UNIT;
     210           10 :       if (num_bytes == 1)
     211            0 :         pp_printf (pp, _("%wi byte"), num_bytes.to_uhwi ());
     212              :       else
     213           10 :         pp_printf (pp, _("%wi bytes"), num_bytes.to_uhwi ());
     214              :     }
     215              :   else
     216              :     {
     217            0 :       if (num_bits == 1)
     218            0 :         pp_printf (pp, _("%wi bit"), num_bits.to_uhwi ());
     219              :       else
     220            0 :         pp_printf (pp, _("%wi bits"), num_bits.to_uhwi ());
     221              :     }
     222           10 : }
     223              : 
     224              : static styled_string
     225           25 : get_access_size_str (style_manager &sm,
     226              :                      const access_operation &op,
     227              :                      access_range accessed_range,
     228              :                      tree type)
     229              : {
     230           25 :   bit_size_expr num_bits (accessed_range.get_size (op.m_model.get_manager ()));
     231           25 :   if (type)
     232              :     {
     233           10 :       styled_string s;
     234           10 :       pretty_printer pp;
     235           10 :       pp_format_decoder (&pp) = default_tree_printer;
     236           10 :       if (num_bits.maybe_print_for_user (&pp, op.m_model))
     237              :         {
     238           10 :           if (op.m_dir == access_direction::read)
     239            6 :             return fmt_styled_string (sm,
     240            6 :                                       _("read of %qT (%s)"),
     241              :                                       type,
     242            6 :                                       pp_formatted_text (&pp));
     243              :           else
     244            4 :             return fmt_styled_string (sm,
     245            4 :                                       _("write of %qT (%s)"),
     246              :                                       type,
     247            4 :                                       pp_formatted_text (&pp));
     248              :         }
     249           10 :     }
     250           15 :   if (op.m_dir == access_direction::read)
     251              :     {
     252           11 :       if (auto p
     253              :           = num_bits.maybe_get_formatted_str (sm, op.m_model,
     254           11 :                                               _("read of %wi bit"),
     255           11 :                                               _("read of %wi bits"),
     256           11 :                                               _("read of %wi byte"),
     257           11 :                                               _("read of %wi bytes"),
     258           11 :                                               _("read of %qs bits"),
     259           11 :                                               _("read of %qs bytes")))
     260           11 :         return std::move (*p.get ());
     261              :     }
     262              :   else
     263              :     {
     264            4 :       if (auto p
     265              :           = num_bits.maybe_get_formatted_str (sm, op.m_model,
     266            4 :                                               _("write of %wi bit"),
     267            4 :                                               _("write of %wi bits"),
     268            4 :                                               _("write of %wi byte"),
     269            4 :                                               _("write of %wi bytes"),
     270            4 :                                               _("write of %qs bits"),
     271            4 :                                               _("write of %qs bytes")))
     272            4 :         return std::move (*p.get ());
     273              :     }
     274              : 
     275            0 :   if (type)
     276              :     {
     277            0 :       if (op.m_dir == access_direction::read)
     278            0 :         return fmt_styled_string (sm, _("read of %qT"), type);
     279              :       else
     280            0 :         return fmt_styled_string (sm, _("write of %qT"), type);
     281              :     }
     282              : 
     283            0 :   if (op.m_dir == access_direction::read)
     284            0 :     return styled_string (sm, _("read"));
     285              :   else
     286            0 :     return styled_string (sm, _("write"));
     287              : }
     288              : 
     289              : /* Subroutine of clean_up_for_diagram.  */
     290              : 
     291              : static tree
     292            0 : strip_any_cast (tree expr)
     293              : {
     294            0 :   if (TREE_CODE (expr) == NOP_EXPR
     295            0 :       || TREE_CODE (expr) == NON_LVALUE_EXPR)
     296            0 :     expr = TREE_OPERAND (expr, 0);
     297            0 :   return expr;
     298              : }
     299              : 
     300              : /* Duplicate EXPR, replacing any SSA names with the underlying variable.  */
     301              : 
     302              : tree
     303            4 : remove_ssa_names (tree expr)
     304              : {
     305            4 :   if (TREE_CODE (expr) == SSA_NAME
     306            4 :       && SSA_NAME_VAR (expr))
     307              :     return SSA_NAME_VAR (expr);
     308            0 :   tree t = copy_node (expr);
     309            0 :   for (int i = 0; i < TREE_OPERAND_LENGTH (expr); i++)
     310            0 :     if (TREE_OPERAND (expr, i))
     311            0 :       TREE_OPERAND (t, i) = remove_ssa_names (TREE_OPERAND (expr, i));
     312              :   return t;
     313              : }
     314              : 
     315              : /* We want to be able to print tree expressions from the analyzer,
     316              :    which is in the middle end.
     317              : 
     318              :    We could use the front-end pretty_printer's formatting routine,
     319              :    but:
     320              :    (a) some have additional state in a pretty_printer subclass, so we'd
     321              :    need to clone global_dc->printer
     322              :    (b) the "aka" type information added by the C and C++ frontends are
     323              :    too verbose when building a diagram, and there isn't a good way to ask
     324              :    for a less verbose version of them.
     325              : 
     326              :    Hence we use default_tree_printer.
     327              :    However, we want to avoid printing SSA names, and instead print the
     328              :    underlying var name.
     329              :    Ideally there would be a better tree printer for use by middle end
     330              :    warnings, but as workaround, this function clones a tree, replacing
     331              :    SSA names with the var names.  */
     332              : 
     333              : tree
     334            0 : clean_up_for_diagram (tree expr)
     335              : {
     336            0 :   tree without_ssa_names = remove_ssa_names (expr);
     337            0 :   return strip_any_cast (without_ssa_names);
     338              : }
     339              : 
     340              : /* struct bit_size_expr.  */
     341              : 
     342              : /* Attempt to generate a user-facing styled string that mentions this
     343              :    bit_size_expr.
     344              :    Use MODEL for extracting representative tree values where necessary.
     345              :    The CONCRETE_* format strings should contain a single %wi.
     346              :    The SYMBOLIC_* format strings should contain a single %qs.
     347              :    Return nullptr if unable to represent the expression.  */
     348              : 
     349              : std::unique_ptr<text_art::styled_string>
     350          169 : bit_size_expr::maybe_get_formatted_str (text_art::style_manager &sm,
     351              :                                         const region_model &model,
     352              :                                         const char *concrete_single_bit_fmt,
     353              :                                         const char *concrete_plural_bits_fmt,
     354              :                                         const char *concrete_single_byte_fmt,
     355              :                                         const char *concrete_plural_bytes_fmt,
     356              :                                         const char *symbolic_bits_fmt,
     357              :                                         const char *symbolic_bytes_fmt) const
     358              : {
     359          169 :   region_model_manager &mgr = *model.get_manager ();
     360          169 :   if (const svalue *num_bytes = maybe_get_as_bytes (mgr))
     361              :     {
     362          168 :       if (tree cst = num_bytes->maybe_get_constant ())
     363              :         {
     364          136 :           byte_size_t concrete_num_bytes = wi::to_offset (cst);
     365          136 :           if (!wi::fits_uhwi_p (concrete_num_bytes))
     366            0 :             return nullptr;
     367          136 :           if (concrete_num_bytes == 1)
     368           11 :             return std::make_unique <text_art::styled_string>
     369           22 :               (fmt_styled_string (sm, concrete_single_byte_fmt,
     370           11 :                                   concrete_num_bytes.to_uhwi ()));
     371              :           else
     372          125 :             return std::make_unique <text_art::styled_string>
     373          250 :               (fmt_styled_string (sm, concrete_plural_bytes_fmt,
     374          125 :                                   concrete_num_bytes.to_uhwi ()));
     375              :         }
     376              :       else
     377              :         {
     378           32 :           pretty_printer pp;
     379           32 :           pp_format_decoder (&pp) = default_tree_printer;
     380           32 :           if (!num_bytes->maybe_print_for_user (&pp, model))
     381            0 :             return nullptr;
     382           32 :           return std::make_unique <text_art::styled_string>
     383           64 :             (fmt_styled_string (sm, symbolic_bytes_fmt,
     384           32 :                                 pp_formatted_text (&pp)));
     385           32 :         }
     386              :     }
     387            1 :   else if (tree cst = m_num_bits.maybe_get_constant ())
     388              :     {
     389            0 :       bit_size_t concrete_num_bits = wi::to_offset (cst);
     390            0 :       if (!wi::fits_uhwi_p (concrete_num_bits))
     391            0 :         return nullptr;
     392            0 :       if (concrete_num_bits == 1)
     393            0 :         return std::make_unique <text_art::styled_string>
     394            0 :           (fmt_styled_string (sm, concrete_single_bit_fmt,
     395            0 :                               concrete_num_bits.to_uhwi ()));
     396              :       else
     397            0 :         return std::make_unique <text_art::styled_string>
     398            0 :           (fmt_styled_string (sm, concrete_plural_bits_fmt,
     399            0 :                               concrete_num_bits.to_uhwi ()));
     400              :     }
     401              :   else
     402              :     {
     403            1 :       pretty_printer pp;
     404            1 :       pp_format_decoder (&pp) = default_tree_printer;
     405            1 :       if (!m_num_bits.maybe_print_for_user (&pp, model))
     406            1 :         return nullptr;
     407            0 :       return std::make_unique <text_art::styled_string>
     408            0 :         (fmt_styled_string (sm, symbolic_bits_fmt,
     409            0 :                             pp_formatted_text (&pp)));
     410            1 :     }
     411              : }
     412              : 
     413              : bool
     414           10 : bit_size_expr::maybe_print_for_user (pretty_printer *pp,
     415              :                                      const region_model &model) const
     416              : {
     417           10 :   if (tree cst = m_num_bits.maybe_get_constant ())
     418              :     {
     419           10 :       bit_size_t concrete_num_bits = wi::to_offset (cst);
     420           10 :       pp_bit_size_t (pp, concrete_num_bits);
     421           10 :       return true;
     422              :     }
     423              :   else
     424              :     {
     425            0 :       if (const svalue *num_bytes = maybe_get_as_bytes (*model.get_manager ()))
     426              :         {
     427            0 :           pretty_printer tmp_pp;
     428            0 :           pp_format_decoder (&tmp_pp) = default_tree_printer;
     429            0 :           if (!num_bytes->maybe_print_for_user (&tmp_pp, model))
     430              :             return false;
     431            0 :           pp_printf (pp, _("%qs bytes"), pp_formatted_text (&tmp_pp));
     432            0 :           return true;
     433            0 :         }
     434              :       else
     435              :         {
     436            0 :           pretty_printer tmp_pp;
     437            0 :           pp_format_decoder (&tmp_pp) = default_tree_printer;
     438            0 :           if (!m_num_bits.maybe_print_for_user (&tmp_pp, model))
     439              :             return false;
     440            0 :           pp_printf (pp, _("%qs bits"), pp_formatted_text (&tmp_pp));
     441            0 :           return true;
     442            0 :         }
     443              :     }
     444              : }
     445              : 
     446              : /* Attempt to get a symbolic value for this symbolic bit size,
     447              :    expressed in bytes.
     448              :    Return null if it's not known to divide exactly.  */
     449              : 
     450              : const svalue *
     451          185 : bit_size_expr::maybe_get_as_bytes (region_model_manager &mgr) const
     452              : {
     453          185 :   if (tree cst = m_num_bits.maybe_get_constant ())
     454              :     {
     455          144 :       bit_offset_t concrete_bits = wi::to_offset (cst);
     456          144 :       if (concrete_bits % BITS_PER_UNIT != 0)
     457              :         /* Not an exact multiple, so fail.  */
     458            4 :         return nullptr;
     459              :     }
     460          181 :   const svalue *bits_per_byte
     461          181 :     = mgr.get_or_create_int_cst (NULL_TREE, BITS_PER_UNIT);
     462          181 :   return mgr.maybe_fold_binop (NULL_TREE, EXACT_DIV_EXPR,
     463          181 :                                &m_num_bits, bits_per_byte);
     464              : }
     465              : 
     466              : /* struct access_range.  */
     467              : 
     468         1130 : access_range::access_range (const region *base_region, const bit_range &bits)
     469         1130 : : m_start (region_offset::make_concrete (base_region,
     470              :                                          bits.get_start_bit_offset ())),
     471         1130 :   m_next (region_offset::make_concrete (base_region,
     472              :                                         bits.get_next_bit_offset ()))
     473              : {
     474         1130 : }
     475              : 
     476            0 : access_range::access_range (const region *base_region, const byte_range &bytes)
     477            0 : : m_start (region_offset::make_concrete (base_region,
     478              :                                          bytes.get_start_bit_offset ())),
     479            0 :   m_next (region_offset::make_concrete (base_region,
     480              :                                         bytes.get_next_bit_offset ()))
     481              : {
     482            0 : }
     483              : 
     484          657 : access_range::access_range (const region &reg, region_model_manager *mgr)
     485          657 : : m_start (strip_types (reg.get_offset (mgr), *mgr)),
     486          657 :   m_next (strip_types (reg.get_next_offset (mgr), *mgr))
     487              : {
     488          657 : }
     489              : 
     490              : bit_size_expr
     491          165 : access_range::get_size (region_model_manager *mgr) const
     492              : {
     493          165 :   const svalue &start_bit_offset = m_start.calc_symbolic_bit_offset (mgr);
     494          165 :   const svalue &next_bit_offset = m_next.calc_symbolic_bit_offset (mgr);
     495          165 :   return bit_size_expr
     496          165 :     (*mgr->get_or_create_binop (NULL_TREE, MINUS_EXPR,
     497          165 :                                 &next_bit_offset, &start_bit_offset));
     498              : }
     499              : 
     500              : bool
     501          735 : access_range::contains_p (const access_range &other) const
     502              : {
     503          735 :   return (m_start <= other.m_start
     504          735 :           && other.m_next <= m_next);
     505              : }
     506              : 
     507              : bool
     508          559 : access_range::empty_p () const
     509              : {
     510          559 :   bit_range concrete_bits (0, 0);
     511          559 :   if (!as_concrete_bit_range (&concrete_bits))
     512              :     return false;
     513          465 :   return concrete_bits.empty_p ();
     514              : }
     515              : 
     516              : void
     517            4 : access_range::dump_to_pp (pretty_printer *pp, bool simple) const
     518              : {
     519            4 :   if (m_start.concrete_p () && m_next.concrete_p ())
     520              :     {
     521            4 :       bit_range bits (m_start.get_bit_offset (),
     522            4 :                       m_next.get_bit_offset () - m_start.get_bit_offset ());
     523            4 :       bits.dump_to_pp (pp);
     524            4 :       return;
     525              :     }
     526            0 :   pp_character (pp, '[');
     527            0 :   m_start.dump_to_pp (pp, simple);
     528            0 :   pp_string (pp, " to ");
     529            0 :   m_next.dump_to_pp (pp, simple);
     530            0 :   pp_character (pp, ')');
     531              : }
     532              : 
     533              : DEBUG_FUNCTION void
     534            0 : access_range::dump (bool simple) const
     535              : {
     536            0 :   tree_dump_pretty_printer pp (stderr);
     537            0 :   dump_to_pp (&pp, simple);
     538            0 :   pp_newline (&pp);
     539            0 : }
     540              : 
     541              : void
     542            0 : access_range::log (const char *title, logger &logger) const
     543              : {
     544            0 :   logger.start_log_line ();
     545            0 :   logger.log_partial ("%s: ", title);
     546            0 :   dump_to_pp (logger.get_printer (), true);
     547            0 :   logger.end_log_line ();
     548            0 : }
     549              : 
     550              : /* struct access_operation.  */
     551              : 
     552              : access_range
     553         1314 : access_operation::get_valid_bits () const
     554              : {
     555         1314 :   const svalue *capacity_in_bytes_sval = m_model.get_capacity (m_base_region);
     556         1314 :   return access_range
     557         1314 :     (region_offset::make_concrete (m_base_region, 0),
     558         1314 :      region_offset::make_byte_offset (m_base_region, capacity_in_bytes_sval),
     559         2628 :      *get_manager ());
     560              : }
     561              : 
     562              : access_range
     563          581 : access_operation::get_actual_bits () const
     564              : {
     565          581 :   return access_range (m_reg, get_manager ());
     566              : }
     567              : 
     568              : /* If there are any bits accessed invalidly before the valid range,
     569              :    return true and write their range to *OUT.
     570              :    Return false if there aren't, or if there's a problem
     571              :    (e.g. symbolic ranges.  */
     572              : 
     573              : bool
     574          136 : access_operation::maybe_get_invalid_before_bits (access_range *out) const
     575              : {
     576          136 :   access_range valid_bits (get_valid_bits ());
     577          136 :   access_range actual_bits (get_actual_bits ());
     578              : 
     579          136 :   if (actual_bits.m_start >= valid_bits.m_start)
     580              :     {
     581              :       /* No part of accessed range is before the valid range.  */
     582              :       return false;
     583              :     }
     584           30 :   else if (actual_bits.m_next > valid_bits.m_start)
     585              :     {
     586              :       /* Get part of accessed range that's before the valid range.  */
     587            8 :       *out = access_range (actual_bits.m_start, valid_bits.m_start,
     588            8 :                            *get_manager ());
     589            8 :       return true;
     590              :     }
     591              :   else
     592              :     {
     593              :       /* Accessed range is fully before valid range.  */
     594           22 :       *out = actual_bits;
     595           22 :       return true;
     596              :     }
     597              : }
     598              : 
     599              : /* If there are any bits accessed invalidly after the valid range,
     600              :    return true and write their range to *OUT.
     601              :    Return false if there aren't, or if there's a problem.  */
     602              : 
     603              : bool
     604          136 : access_operation::maybe_get_invalid_after_bits (access_range *out) const
     605              : {
     606          136 :   access_range valid_bits (get_valid_bits ());
     607          136 :   access_range actual_bits (get_actual_bits ());
     608              : 
     609          136 :   if (actual_bits.m_next <= valid_bits.m_next)
     610              :     {
     611              :       /* No part of accessed range is after the valid range.  */
     612              :       return false;
     613              :     }
     614          114 :   else if (actual_bits.m_start < valid_bits.m_next)
     615              :     {
     616              :       /* Get part of accessed range that's after the valid range.  */
     617           56 :       *out = access_range (valid_bits.m_next, actual_bits.m_next,
     618           56 :                            *get_manager ());
     619           56 :       return true;
     620              :     }
     621              :   else
     622              :     {
     623              :       /* Accessed range is fully after valid range.  */
     624           58 :       *out = actual_bits;
     625           58 :       return true;
     626              :     }
     627              : }
     628              : 
     629              : /* A class for capturing all of the region offsets of interest (both concrete
     630              :    and symbolic), to help align everything in the diagram.
     631              :    Boundaries can be soft or hard; hard boundaries are emphasized visually
     632              :    (e.g. the boundary between valid vs invalid accesses).
     633              : 
     634              :    Offsets in the boundaries are all expressed relative to the base
     635              :    region of the access_operation.  */
     636              : 
     637              : class boundaries
     638              : {
     639              : public:
     640              :   enum class kind { HARD, SOFT};
     641              : 
     642           72 :   boundaries (const region &base_reg, logger *logger)
     643           72 :   : m_base_reg (base_reg), m_logger (logger)
     644              :   {
     645              :   }
     646              : 
     647          899 :   void add (region_offset offset, enum kind k)
     648              :   {
     649          323 :     m_all_offsets.insert (offset);
     650          576 :     if (k == kind::HARD)
     651          398 :       m_hard_offsets.insert (offset);
     652              :   }
     653              : 
     654          288 :   void add (const access_range &range, enum kind kind)
     655              :   {
     656          288 :     add (range.m_start, kind);
     657          288 :     add (range.m_next, kind);
     658          288 :     if (m_logger)
     659              :       {
     660            0 :         m_logger->start_log_line ();
     661            0 :         m_logger->log_partial ("added access_range: ");
     662            0 :         range.dump_to_pp (m_logger->get_printer (), true);
     663            0 :         m_logger->log_partial (" (%s)",
     664              :                                (kind == boundaries::kind::HARD)
     665              :                                ? "HARD" : "soft");
     666            0 :         m_logger->end_log_line ();
     667              :       }
     668          288 :   }
     669              : 
     670           76 :   void add (const region &reg, region_model_manager *mgr, enum kind kind)
     671              :   {
     672           76 :     add (access_range (reg.get_offset (mgr),
     673              :                        reg.get_next_offset (mgr),
     674           76 :                        *mgr),
     675              :          kind);
     676           76 :   }
     677              : 
     678              :   void add (const byte_range bytes, enum kind kind)
     679              :   {
     680              :     add (access_range (&m_base_reg, bytes), kind);
     681              :   }
     682              : 
     683           42 :   void add_all_bytes_in_range (const byte_range &bytes)
     684              :   {
     685           42 :     for (byte_offset_t byte_idx = bytes.get_start_byte_offset ();
     686          365 :          byte_idx <= bytes.get_next_byte_offset ();
     687          323 :          byte_idx = byte_idx + 1)
     688          323 :       add (region_offset::make_concrete (&m_base_reg, byte_idx * 8),
     689              :            kind::SOFT);
     690           42 :   }
     691              : 
     692           18 :   void add_all_bytes_in_range (const access_range &range)
     693              :   {
     694           18 :     byte_range bytes (0, 0);
     695           18 :     bool valid = range.as_concrete_byte_range (&bytes);
     696           18 :     gcc_assert (valid);
     697           18 :     add_all_bytes_in_range (bytes);
     698           18 :   }
     699              : 
     700            0 :   void log (logger &logger) const
     701              :   {
     702            0 :     logger.log ("boundaries:");
     703            0 :     logger.inc_indent ();
     704            0 :     for (auto offset : m_all_offsets)
     705              :       {
     706            0 :         enum kind k = get_kind (offset);
     707            0 :         logger.start_log_line ();
     708            0 :         logger.log_partial ("%s: ", (k == kind::HARD) ? "HARD" : "soft");
     709            0 :         offset.dump_to_pp (logger.get_printer (), true);
     710            0 :         logger.end_log_line ();
     711              :       }
     712            0 :     logger.dec_indent ();
     713            0 :   }
     714              : 
     715          391 :   enum kind get_kind (region_offset offset) const
     716              :   {
     717          391 :     gcc_assert (m_all_offsets.find (offset) != m_all_offsets.end ());
     718          391 :     if (m_hard_offsets.find (offset) != m_hard_offsets.end ())
     719              :       return kind::HARD;
     720              :     else
     721          286 :       return kind::SOFT;
     722              :   }
     723              : 
     724           72 :   std::set<region_offset>::const_iterator begin () const
     725              :   {
     726           72 :     return m_all_offsets.begin ();
     727              :   }
     728           72 :   std::set<region_offset>::const_iterator end () const
     729              :   {
     730           72 :     return m_all_offsets.end ();
     731              :   }
     732              :   std::set<region_offset>::size_type size () const
     733              :   {
     734              :     return m_all_offsets.size ();
     735              :   }
     736              : 
     737              :   std::vector<region_offset>
     738           21 :   get_hard_boundaries_in_range (byte_offset_t min_offset,
     739              :                                 byte_offset_t max_offset) const
     740              :   {
     741           21 :     std::vector<region_offset> result;
     742           99 :     for (auto &offset : m_hard_offsets)
     743              :       {
     744           78 :         if (!offset.concrete_p ())
     745           46 :           continue;
     746           78 :         byte_offset_t byte;
     747           78 :         if (!offset.get_concrete_byte_offset (&byte))
     748            0 :           continue;
     749           78 :         if (byte < min_offset)
     750            4 :           continue;
     751           74 :         if (byte > max_offset)
     752           42 :           continue;
     753           32 :         result.push_back (offset);
     754              :       }
     755           21 :     return result;
     756              :   }
     757              : 
     758              : private:
     759              :   const region &m_base_reg;
     760              :   logger *m_logger;
     761              :   std::set<region_offset> m_all_offsets;
     762              :   std::set<region_offset> m_hard_offsets;
     763              : };
     764              : 
     765              : /* A widget that wraps a table but offloads column-width calculation
     766              :    to a shared object, so that we can vertically line up multiple tables
     767              :    and have them all align their columns.
     768              : 
     769              :    For example, in:
     770              : 
     771              :    01| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
     772              :    02| |[0]|[1]|[2]|[3]|[4]|[5]|   ...    |[440]|[441]|[442]|[443]|[444]|[445]|
     773              :    03| +---+---+---+---+---+---+          +-----+-----+-----+-----+-----+-----+
     774              :    04| |'L'|'o'|'r'|'e'|'m'|' '|          | 'o' | 'r' | 'u' | 'm' | '.' | NUL |
     775              :    05| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
     776              :    06| |                  string literal (type: 'char[446]')                  |
     777              :    07| +----------------------------------------------------------------------+
     778              :    08|   |   |   |   |   |   |  |  |    |    |     |     |     |     |     |
     779              :    09|   |   |   |   |   |   |  |  |    |    |     |     |     |     |     |
     780              :    10|   v   v   v   v   v   v  v  v    v    v     v     v     v     v     v
     781              :    11|+---+---------------------+----++--------------------------------------+
     782              :    12||[0]|         ...         |[99]||          after valid range           |
     783              :    13|+---+---------------------+----+|                                      |
     784              :    14||  'buf' (type: 'char[100]')   ||                                      |
     785              :    15|+------------------------------++--------------------------------------+
     786              :    16||~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~||~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
     787              :    17|               |                                   |
     788              :    18|     +---------+---------+              +----------+----------+
     789              :    19|     |capacity: 100 bytes|              |overflow of 346 bytes|
     790              :    20|     +-------------------+              +---------------------+
     791              : 
     792              :    rows 01-07 and rows 11-15 are x_aligned_table_widget instances.  */
     793              : 
     794              : class x_aligned_table_widget : public leaf_widget
     795              : {
     796              : public:
     797          137 :   x_aligned_table_widget (table t,
     798              :                           const theme &theme,
     799              :                           table_dimension_sizes &col_widths)
     800          137 :   : m_table (std::move (t)),
     801          137 :     m_theme (theme),
     802          137 :     m_col_widths (col_widths),
     803          137 :     m_row_heights (t.get_size ().h),
     804          137 :     m_cell_sizes (m_col_widths, m_row_heights),
     805          274 :     m_tg (m_table, m_cell_sizes)
     806              :   {
     807          137 :   }
     808              : 
     809            0 :   const char *get_desc () const override
     810              :   {
     811            0 :     return "x_aligned_table_widget";
     812              :   }
     813              : 
     814          137 :   canvas::size_t calc_req_size () final override
     815              :   {
     816              :     /* We don't compute the size requirements;
     817              :        the parent should have done this.  */
     818          137 :     return m_tg.get_canvas_size ();
     819              :   }
     820              : 
     821          137 :   void paint_to_canvas (canvas &canvas) final override
     822              :   {
     823          137 :     m_table.paint_to_canvas (canvas,
     824          137 :                              get_top_left (),
     825          137 :                              m_tg,
     826              :                              m_theme);
     827          137 :   }
     828              : 
     829          274 :   const table &get_table () const { return m_table; }
     830          274 :   table_cell_sizes &get_cell_sizes () { return m_cell_sizes; }
     831          137 :   void recalc_coords ()
     832              :   {
     833          137 :     m_tg.recalc_coords ();
     834              :   }
     835              : 
     836              : private:
     837              :   table m_table;
     838              :   const theme &m_theme;
     839              :   table_dimension_sizes &m_col_widths; // Reference to shared column widths
     840              :   table_dimension_sizes m_row_heights; // Unique row heights
     841              :   table_cell_sizes m_cell_sizes;
     842              :   table_geometry m_tg;
     843              : };
     844              : 
     845              : /* A widget for printing arrows between the accessed region
     846              :    and the svalue, showing the direction of the access.
     847              : 
     848              :    For example, in:
     849              : 
     850              :    01| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
     851              :    02| |[0]|[1]|[2]|[3]|[4]|[5]|   ...    |[440]|[441]|[442]|[443]|[444]|[445]|
     852              :    03| +---+---+---+---+---+---+          +-----+-----+-----+-----+-----+-----+
     853              :    04| |'L'|'o'|'r'|'e'|'m'|' '|          | 'o' | 'r' | 'u' | 'm' | '.' | NUL |
     854              :    05| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
     855              :    06| |                  string literal (type: 'char[446]')                  |
     856              :    07| +----------------------------------------------------------------------+
     857              :    08|   |   |   |   |   |   |  |  |    |    |     |     |     |     |     |
     858              :    09|   |   |   |   |   |   |  |  |    |    |     |     |     |     |     |
     859              :    10|   v   v   v   v   v   v  v  v    v    v     v     v     v     v     v
     860              :    11|+---+---------------------+----++--------------------------------------+
     861              :    12||[0]|         ...         |[99]||          after valid range           |
     862              :    13|+---+---------------------+----+|                                      |
     863              :    14||  'buf' (type: 'char[100]')   ||                                      |
     864              :    15|+------------------------------++--------------------------------------+
     865              :    16||~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~||~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
     866              :    17|               |                                   |
     867              :    18|     +---------+---------+              +----------+----------+
     868              :    19|     |capacity: 100 bytes|              |overflow of 346 bytes|
     869              :    20|     +-------------------+              +---------------------+
     870              : 
     871              :    rows 8-10 are the direction widget.  */
     872              : 
     873              : class direction_widget : public leaf_widget
     874              : {
     875              : public:
     876           68 :   direction_widget (const access_diagram_impl &dia_impl,
     877              :                     const bit_to_table_map &btm)
     878           68 :   : leaf_widget (),
     879           68 :     m_dia_impl (dia_impl),
     880           68 :     m_btm (btm)
     881              :   {
     882              :   }
     883            0 :   const char *get_desc () const override
     884              :   {
     885            0 :     return "direction_widget";
     886              :   }
     887           68 :   canvas::size_t calc_req_size () final override
     888              :   {
     889              :     /* Get our width from our siblings.  */
     890           68 :     return canvas::size_t (0, 3);
     891              :   }
     892              :   void paint_to_canvas (canvas &canvas) final override;
     893              : 
     894              : private:
     895              :   const access_diagram_impl &m_dia_impl;
     896              :   const bit_to_table_map &m_btm;
     897              : };
     898              : 
     899              : /* A widget for adding an x_ruler to a diagram based on table columns,
     900              :    offloading column-width calculation to shared objects, so that the ruler
     901              :    lines up with other tables in the diagram.
     902              : 
     903              :    For example, in:
     904              : 
     905              :    01| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
     906              :    02| |[0]|[1]|[2]|[3]|[4]|[5]|   ...    |[440]|[441]|[442]|[443]|[444]|[445]|
     907              :    03| +---+---+---+---+---+---+          +-----+-----+-----+-----+-----+-----+
     908              :    04| |'L'|'o'|'r'|'e'|'m'|' '|          | 'o' | 'r' | 'u' | 'm' | '.' | NUL |
     909              :    05| +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
     910              :    06| |                  string literal (type: 'char[446]')                  |
     911              :    07| +----------------------------------------------------------------------+
     912              :    08|   |   |   |   |   |   |  |  |    |    |     |     |     |     |     |
     913              :    09|   |   |   |   |   |   |  |  |    |    |     |     |     |     |     |
     914              :    10|   v   v   v   v   v   v  v  v    v    v     v     v     v     v     v
     915              :    11|+---+---------------------+----++--------------------------------------+
     916              :    12||[0]|         ...         |[99]||          after valid range           |
     917              :    13|+---+---------------------+----+|                                      |
     918              :    14||  'buf' (type: 'char[100]')   ||                                      |
     919              :    15|+------------------------------++--------------------------------------+
     920              :    16||~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~||~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~|
     921              :    17|               |                                   |
     922              :    18|     +---------+---------+              +----------+----------+
     923              :    19|     |capacity: 100 bytes|              |overflow of 346 bytes|
     924              :    20|     +-------------------+              +---------------------+
     925              : 
     926              :    rows 16-20 are the x_aligned_x_ruler_widget.  */
     927              : 
     928              : class x_aligned_x_ruler_widget : public leaf_widget
     929              : {
     930              : public:
     931           68 :   x_aligned_x_ruler_widget (const access_diagram_impl &dia_impl,
     932              :                             const theme &theme)
     933           68 :   : m_dia_impl (dia_impl),
     934           68 :     m_theme (theme)
     935              :   {
     936              :   }
     937              : 
     938            0 :   const char *get_desc () const override
     939              :   {
     940            0 :     return "x_aligned_ruler_widget";
     941              :   }
     942              : 
     943          153 :   void add_range (const table::range_t &x_range,
     944              :                   styled_string text,
     945              :                   style::id_t style_id)
     946              :   {
     947          153 :     m_labels.push_back (label (x_range, std::move (text), style_id));
     948          153 :   }
     949              : 
     950           68 :   canvas::size_t calc_req_size () final override
     951              :   {
     952           68 :     x_ruler r (make_x_ruler ());
     953           68 :     return r.get_size ();
     954           68 :   }
     955              : 
     956           68 :   void paint_to_canvas (canvas &canvas) final override
     957              :   {
     958           68 :     x_ruler r (make_x_ruler ());
     959           68 :     r.paint_to_canvas (canvas,
     960           68 :                        get_top_left (),
     961              :                        m_theme);
     962           68 :   }
     963              : 
     964              : private:
     965          562 :   struct label
     966              :   {
     967          153 :     label (const table::range_t &table_x_range,
     968              :            styled_string text,
     969              :            style::id_t style_id)
     970          153 :     : m_table_x_range (table_x_range),
     971          153 :       m_text (std::move (text)),
     972          153 :       m_style_id (style_id)
     973              :     {
     974              :     }
     975              :     table::range_t m_table_x_range;
     976              :     styled_string m_text;
     977              :     style::id_t m_style_id;
     978              :   };
     979              : 
     980              :   x_ruler make_x_ruler () const;
     981              : 
     982              :   const access_diagram_impl &m_dia_impl;
     983              :   const theme &m_theme;
     984              :   std::vector<label> m_labels;
     985              : };
     986              : 
     987              : /* A two-way mapping between access_ranges and table columns, for use by
     988              :    spatial_item subclasses for creating tables.
     989              :    For example when visualizing a bogus access of 'int arr[10];'
     990              :    at 'arr[10]', we might have:
     991              :    - table column 0 is "bytes 0-3" (for arr[0])
     992              :    - table column 1 is "bytes 4-35" (for arr[1] through arr[8])
     993              :    - table column 2 is "bytes 36-39 (for arr[9])
     994              :    - table column 3 is blank to emphasize a hard boundary between
     995              :      valid/invalid accesses.
     996              :    - table column 4 is "bytes 40-44" (for arr[10])
     997              : 
     998              :    We store this as a pair of maps from region_offset to table x; in
     999              :    the abvove example:
    1000              : 
    1001              :      region offset          table_x  prev_table_x
    1002              :      bit 0 (aka byte 0)     0        (none)
    1003              :      bit 32 (aka byte 4)    1        0
    1004              :      bit 288 (aka byte 36)  2        1
    1005              :      bit 320 (aka byte 40)  4        2
    1006              :      bit 352 (aka byte 44)  (none)   (none)
    1007              : 
    1008              :      so that e.g given the half-open byte range [0, 40)
    1009              :      we can determine the closed range of table x [0, 2].  */
    1010              : 
    1011           72 : class bit_to_table_map
    1012              : {
    1013              : public:
    1014              :   /* Populate m_table_x_for_bit and m_bit_for_table_x.  */
    1015           72 :   void populate (const boundaries &boundaries,
    1016              :                  region_model_manager &mgr,
    1017              :                  logger *logger)
    1018              :   {
    1019           72 :     LOG_SCOPE (logger);
    1020              : 
    1021           72 :     int table_x = 0;
    1022           72 :     std::vector <region_offset> vec_boundaries (boundaries.begin (),
    1023           72 :                                                 boundaries.end ());
    1024              : 
    1025              :     /* Sort into an order that makes sense.  */
    1026           72 :     std::sort (vec_boundaries.begin (),
    1027              :                vec_boundaries.end ());
    1028              : 
    1029           72 :     if (logger)
    1030              :       {
    1031            0 :         logger->log ("vec_boundaries");
    1032            0 :         logger->inc_indent ();
    1033            0 :         for (unsigned idx = 0; idx < vec_boundaries.size (); idx++)
    1034              :           {
    1035            0 :             logger->start_log_line ();
    1036            0 :             logger->log_partial ("idx: %i: ", idx);
    1037            0 :             vec_boundaries[idx].dump_to_pp (logger->get_printer (), true);
    1038            0 :             logger->end_log_line ();
    1039              :           }
    1040            0 :         logger->dec_indent ();
    1041              :       }
    1042              : 
    1043          607 :     for (size_t idx = 0; idx < vec_boundaries.size (); idx++)
    1044              :       {
    1045          535 :         const region_offset &offset = vec_boundaries[idx];
    1046          535 :         if (idx > 0 && (idx + 1) < vec_boundaries.size ())
    1047              :           {
    1048          391 :             if (boundaries.get_kind (offset) == boundaries::kind::HARD)
    1049          105 :               table_x += 1;
    1050              :           }
    1051          535 :         m_table_x_for_offset[offset] = table_x;
    1052          535 :         if ((idx + 1) < vec_boundaries.size ())
    1053              :           {
    1054          463 :             const region_offset &next_offset = vec_boundaries[idx + 1];
    1055          463 :             m_table_x_for_prev_offset[next_offset] = table_x;
    1056          463 :             m_range_for_table_x[table_x]
    1057          926 :               = access_range (offset, next_offset, mgr);
    1058              :           }
    1059          535 :         table_x += 1;
    1060              :       }
    1061           72 :     m_num_columns = table_x - 1;
    1062              : 
    1063           72 :     if (logger)
    1064            0 :       log (*logger);
    1065           72 :   }
    1066              : 
    1067         1714 :   unsigned get_num_columns () const
    1068              :   {
    1069         1714 :     return m_num_columns;
    1070              :   }
    1071              : 
    1072         1103 :   table::range_t get_table_x_for_range (const access_range &range) const
    1073              :   {
    1074         1103 :     return table::range_t (get_table_x_for_offset (range.m_start),
    1075         1103 :                            get_table_x_for_prev_offset (range.m_next) + 1);
    1076              :   }
    1077              : 
    1078          734 :   table::rect_t get_table_rect (const access_range &range,
    1079              :                                 const int table_y, const int table_h) const
    1080              :   {
    1081          734 :     const table::range_t x_range (get_table_x_for_range (range));
    1082          734 :     return table::rect_t (table::coord_t (x_range.start, table_y),
    1083          734 :                           table::size_t (x_range.get_size (), table_h));
    1084              :   }
    1085              : 
    1086          543 :   table::rect_t get_table_rect (const region *base_reg,
    1087              :                                 const bit_range &bits,
    1088              :                                 const int table_y, const int table_h) const
    1089              :   {
    1090          543 :     const access_range range (base_reg, bits);
    1091          543 :     return get_table_rect (range, table_y, table_h);
    1092              :   }
    1093              : 
    1094          543 :   table::rect_t get_table_rect (const region *base_reg,
    1095              :                                 const byte_range &bytes,
    1096              :                                 const int table_y, const int table_h) const
    1097              :   {
    1098          543 :     return get_table_rect (base_reg, bytes.as_bit_range (), table_y, table_h);
    1099              :   }
    1100              : 
    1101         1117 :   bool maybe_get_access_range_for_table_x (int table_x,
    1102              :                                            access_range *out) const
    1103              :   {
    1104         1117 :     auto slot = m_range_for_table_x.find (table_x);
    1105         1117 :     if (slot == m_range_for_table_x.end ())
    1106              :       return false;
    1107          914 :     *out = slot->second;
    1108          914 :     return true;
    1109              :   }
    1110              : 
    1111            0 :   void log (logger &logger) const
    1112              :   {
    1113            0 :     logger.log ("table columns");
    1114            0 :     logger.inc_indent ();
    1115            0 :     for (unsigned table_x = 0; table_x < get_num_columns (); table_x++)
    1116              :       {
    1117            0 :         logger.start_log_line ();
    1118            0 :         logger.log_partial ("table_x: %i", table_x);
    1119            0 :         access_range range_for_column (nullptr, bit_range (0, 0));
    1120            0 :         if (maybe_get_access_range_for_table_x (table_x, &range_for_column))
    1121              :           {
    1122            0 :             logger.log_partial (": range: ");
    1123            0 :             range_for_column.dump_to_pp (logger.get_printer (), true);
    1124              :           }
    1125            0 :         logger.end_log_line ();
    1126              :       }
    1127            0 :     logger.dec_indent ();
    1128            0 :   }
    1129              : 
    1130         1135 :   int get_table_x_for_offset (region_offset offset) const
    1131              :   {
    1132         1135 :     auto slot = m_table_x_for_offset.find (offset);
    1133              : 
    1134              :     /* If this fails, then we probably failed to fully populate m_boundaries
    1135              :        in find_boundaries.  */
    1136         1135 :     gcc_assert (slot != m_table_x_for_offset.end ());
    1137              : 
    1138         1135 :     return slot->second;
    1139              :   }
    1140              : 
    1141              : private:
    1142         1103 :   int get_table_x_for_prev_offset (region_offset offset) const
    1143              :   {
    1144         1103 :     auto slot = m_table_x_for_prev_offset.find (offset);
    1145              : 
    1146              :     /* If this fails, then we probably failed to fully populate m_boundaries
    1147              :        in find_boundaries.  */
    1148         1103 :     gcc_assert (slot != m_table_x_for_prev_offset.end ());
    1149              : 
    1150         1103 :     return slot->second;
    1151              :   }
    1152              : 
    1153              :   std::map<region_offset, int> m_table_x_for_offset;
    1154              :   std::map<region_offset, int> m_table_x_for_prev_offset;
    1155              :   std::map<int, access_range> m_range_for_table_x;
    1156              :   unsigned m_num_columns;
    1157              : };
    1158              : 
    1159              : /* Base class for something in the diagram that participates
    1160              :    in two steps of diagram creation:
    1161              :    (a) populating a boundaries instance with the boundaries of interest
    1162              :    (b) creating a table instance for itself.
    1163              : 
    1164              :    Offsets in the boundaries are all expressed relative to the base
    1165              :    region of the access_operation.  */
    1166              : 
    1167          223 : class spatial_item
    1168              : {
    1169              : public:
    1170              :   virtual ~spatial_item () {}
    1171              :   virtual void add_boundaries (boundaries &out, logger *) const = 0;
    1172              : 
    1173              :   virtual table make_table (const bit_to_table_map &btm,
    1174              :                             style_manager &sm) const = 0;
    1175              : };
    1176              : 
    1177              : /* A spatial_item that involves showing an svalue at a particular offset.  */
    1178              : 
    1179              : class svalue_spatial_item : public spatial_item
    1180              : {
    1181              : public:
    1182              :   enum class kind
    1183              :   {
    1184              :      WRITTEN,
    1185              :      EXISTING
    1186              :   };
    1187              : protected:
    1188           41 :   svalue_spatial_item (const svalue &sval,
    1189              :                        access_range bits,
    1190              :                        enum kind kind)
    1191           41 :   : m_sval (sval), m_bits (bits), m_kind (kind)
    1192              :   {
    1193              :   }
    1194              : 
    1195              :   const svalue &m_sval;
    1196              :   access_range m_bits;
    1197              :   enum kind m_kind;
    1198              : };
    1199              : 
    1200              : static std::unique_ptr<spatial_item>
    1201              : make_existing_svalue_spatial_item (const svalue *sval,
    1202              :                                    const access_range &bits,
    1203              :                                    const theme &theme);
    1204              : 
    1205              : class compound_svalue_spatial_item : public svalue_spatial_item
    1206              : {
    1207              : public:
    1208           11 :   compound_svalue_spatial_item (const compound_svalue &sval,
    1209              :                                 const access_range &bits,
    1210              :                                 enum kind kind,
    1211              :                                 const theme &theme)
    1212           11 :   : svalue_spatial_item (sval, bits, kind),
    1213           11 :     m_compound_sval (sval)
    1214              :   {
    1215           11 :     const concrete_binding_map &map = m_compound_sval.get_concrete_bindings ();
    1216           11 :     auto_vec <const binding_key *> binding_keys;
    1217           37 :     for (auto iter : map)
    1218              :       {
    1219           26 :         const bit_range &key = iter.first;
    1220           26 :         const svalue *bound_sval = iter.second;
    1221           26 :         access_range range (nullptr, key);
    1222           26 :         if (std::unique_ptr<spatial_item> child
    1223              :             = make_existing_svalue_spatial_item (bound_sval,
    1224              :                                                  range,
    1225           26 :                                                  theme))
    1226           26 :           m_children.push_back (std::move (child));
    1227              :       }
    1228           11 :   }
    1229              : 
    1230           11 :   void add_boundaries (boundaries &out, logger *logger) const final override
    1231              :   {
    1232           11 :     LOG_SCOPE (logger);
    1233           22 :     for (auto &iter : m_children)
    1234           11 :       iter->add_boundaries (out, logger);
    1235           11 :   }
    1236              : 
    1237           11 :   table make_table (const bit_to_table_map &btm,
    1238              :                     style_manager &sm) const final override
    1239              :   {
    1240           11 :     std::vector<table> child_tables;
    1241           11 :     int max_rows = 0;
    1242           22 :     for (auto &iter : m_children)
    1243              :       {
    1244           11 :         table child_table (iter->make_table (btm, sm));
    1245           11 :         max_rows = MAX (max_rows, child_table.get_size ().h);
    1246           11 :         child_tables.push_back (std::move (child_table));
    1247           11 :       }
    1248           11 :     table t (table::size_t (btm.get_num_columns (), max_rows));
    1249           22 :     for (auto &&child_table : child_tables)
    1250           11 :       t.add_other_table (std::move (child_table),
    1251           11 :                          table::coord_t (0, 0));
    1252           22 :     return t;
    1253           11 :   }
    1254              : 
    1255              : private:
    1256              :   const compound_svalue &m_compound_sval;
    1257              :   std::vector<std::unique_ptr<spatial_item>> m_children;
    1258              : };
    1259              : 
    1260              : /* Loop through the TABLE_X_RANGE columns of T, adding
    1261              :    cells containing "..." in any unoccupied ranges of table cell.  */
    1262              : 
    1263              : static void
    1264           38 : add_ellipsis_to_gaps (table &t,
    1265              :                       style_manager &sm,
    1266              :                       const table::range_t &table_x_range,
    1267              :                       const table::range_t &table_y_range)
    1268              : {
    1269           38 :   int table_x = table_x_range.get_min ();
    1270           80 :   while (table_x < table_x_range.get_next ())
    1271              :     {
    1272              :       /* Find a run of unoccupied table cells.  */
    1273              :       const int start_table_x = table_x;
    1274          236 :       while (table_x < table_x_range.get_next ()
    1275          236 :              && !t.get_placement_at (table::coord_t (table_x,
    1276          205 :                                                      table_y_range.get_min ())))
    1277          194 :         table_x++;
    1278           42 :       const table::range_t unoccupied_x_range (start_table_x, table_x);
    1279           42 :       if (unoccupied_x_range.get_size () > 0)
    1280           42 :         t.set_cell_span (table::rect_t (unoccupied_x_range, table_y_range),
    1281           84 :                          styled_string (sm, "..."));
    1282              :       /* Skip occupied table cells.  */
    1283           53 :       while (table_x < table_x_range.get_next ()
    1284           53 :              && t.get_placement_at (table::coord_t (table_x,
    1285           21 :                                                     table_y_range.get_min ())))
    1286           11 :         table_x++;
    1287              :     }
    1288           38 : }
    1289              : 
    1290              : /* Subclass of spatial_item for visualizing the region of memory
    1291              :    that's valid to access relative to the base region of region accessed in
    1292              :    the operation.  */
    1293              : 
    1294              : class valid_region_spatial_item : public spatial_item
    1295              : {
    1296              : public:
    1297           72 :   valid_region_spatial_item (const access_operation &op,
    1298              :                              diagnostics::paths::event_id_t region_creation_event_id,
    1299              :                              const theme &theme)
    1300           72 :   : m_op (op),
    1301           72 :     m_region_creation_event_id (region_creation_event_id),
    1302           72 :     m_boundaries (nullptr),
    1303          144 :     m_existing_sval (op.m_model.get_store_value (op.m_base_region, nullptr)),
    1304           72 :     m_existing_sval_spatial_item
    1305              :       (make_existing_svalue_spatial_item (m_existing_sval,
    1306           72 :                                           op.get_valid_bits (),
    1307           72 :                                           theme))
    1308              :   {
    1309           72 :   }
    1310              : 
    1311           72 :   void add_boundaries (boundaries &out, logger *logger) const final override
    1312              :   {
    1313           72 :     LOG_SCOPE (logger);
    1314           72 :     m_boundaries = &out;
    1315           72 :     access_range valid_bits = m_op.get_valid_bits ();
    1316           72 :     if (logger)
    1317              :       {
    1318            0 :         logger->start_log_line ();
    1319            0 :         logger->log_partial ("valid bits: ");
    1320            0 :         valid_bits.dump_to_pp (logger->get_printer (), true);
    1321            0 :         logger->end_log_line ();
    1322              :       }
    1323           72 :     out.add (valid_bits, boundaries::kind::HARD);
    1324              : 
    1325           72 :     if (m_existing_sval_spatial_item)
    1326              :       {
    1327           13 :         if (logger)
    1328              :           {
    1329            0 :             logger->start_log_line ();
    1330            0 :             logger->log_partial ("existing svalue: ");
    1331            0 :             m_existing_sval->dump_to_pp (logger->get_printer (), true);
    1332            0 :             logger->end_log_line ();
    1333              :           }
    1334           13 :         m_existing_sval_spatial_item->add_boundaries (out, logger);
    1335              :       }
    1336              : 
    1337              :     /* Support for showing first and final element in array types.  */
    1338           72 :     if (tree base_type = m_op.m_base_region->get_type ())
    1339           39 :       if (TREE_CODE (base_type) == ARRAY_TYPE)
    1340              :         {
    1341           39 :           if (logger)
    1342            0 :             logger->log ("showing first and final element in array type");
    1343           39 :           region_model_manager *mgr = m_op.m_model.get_manager ();
    1344           39 :           tree domain = TYPE_DOMAIN (base_type);
    1345           39 :           if (domain && TYPE_MIN_VALUE (domain) && TYPE_MAX_VALUE (domain))
    1346              :             {
    1347           38 :               const svalue *min_idx_sval
    1348           38 :                 = mgr->get_or_create_constant_svalue (TYPE_MIN_VALUE (domain));
    1349           38 :               const svalue *max_idx_sval
    1350           38 :                 = mgr->get_or_create_constant_svalue (TYPE_MAX_VALUE (domain));
    1351           38 :               const region *min_element =
    1352           38 :                 mgr->get_element_region (m_op.m_base_region,
    1353           38 :                                          TREE_TYPE (base_type),
    1354              :                                          min_idx_sval);
    1355           38 :               out.add (*min_element, mgr, boundaries::kind::SOFT);
    1356           38 :               const region *max_element =
    1357           38 :                 mgr->get_element_region (m_op.m_base_region,
    1358           38 :                                          TREE_TYPE (base_type),
    1359              :                                          max_idx_sval);
    1360           38 :               out.add (*max_element, mgr, boundaries::kind::SOFT);
    1361              :             }
    1362              :         }
    1363           72 :   }
    1364              : 
    1365              :   /* Subroutine of make_table when base region has ARRAY_TYPE.  */
    1366           39 :   void add_array_elements_to_table (table &t,
    1367              :                                     const bit_to_table_map &btm,
    1368              :                                     style_manager &sm) const
    1369              :   {
    1370           39 :     tree base_type = m_op.m_base_region->get_type ();
    1371           39 :     gcc_assert (TREE_CODE (base_type) == ARRAY_TYPE);
    1372           39 :     gcc_assert (m_boundaries != nullptr);
    1373              : 
    1374           39 :     tree domain = TYPE_DOMAIN (base_type);
    1375           39 :     if (!(domain && TYPE_MIN_VALUE (domain) && TYPE_MAX_VALUE (domain)))
    1376            1 :       return;
    1377              : 
    1378           38 :     const int table_y = 0;
    1379           38 :     const int table_h = 1;
    1380           38 :     const table::range_t table_y_range (table_y, table_y + table_h);
    1381              : 
    1382           38 :     t.add_row ();
    1383              : 
    1384           38 :     const table::range_t min_x_range
    1385           38 :       = maybe_add_array_index_to_table (t, btm, sm, table_y_range,
    1386           38 :                                         TYPE_MIN_VALUE (domain));
    1387           38 :     const table::range_t max_x_range
    1388           38 :       = maybe_add_array_index_to_table (t, btm, sm, table_y_range,
    1389           38 :                                         TYPE_MAX_VALUE (domain));
    1390              : 
    1391           38 :     if (TREE_TYPE (base_type) == char_type_node)
    1392              :       {
    1393              :         /* For a char array,: if there are any hard boundaries in
    1394              :            m_boundaries that are *within* the valid region,
    1395              :            then show those index values.  */
    1396           21 :         std::vector<region_offset> hard_boundaries
    1397           21 :           = m_boundaries->get_hard_boundaries_in_range
    1398           21 :               (tree_to_shwi (TYPE_MIN_VALUE (domain)),
    1399           42 :                tree_to_shwi (TYPE_MAX_VALUE (domain)));
    1400           53 :         for (auto &offset : hard_boundaries)
    1401              :           {
    1402           32 :             const int table_x = btm.get_table_x_for_offset (offset);
    1403           32 :             if (!offset.concrete_p ())
    1404            0 :               continue;
    1405           32 :             byte_offset_t byte;
    1406           32 :             if (!offset.get_concrete_byte_offset (&byte))
    1407            0 :               continue;
    1408           32 :             table::range_t table_x_range (table_x, table_x + 1);
    1409           64 :             t.maybe_set_cell_span (table::rect_t (table_x_range,
    1410           32 :                                                   table_y_range),
    1411           64 :                                    fmt_styled_string (sm, "[%wi]",
    1412              :                                                       byte.to_shwi ()));
    1413              :           }
    1414           21 :       }
    1415              : 
    1416           38 :     add_ellipsis_to_gaps (t, sm,
    1417           38 :                           table::range_t (min_x_range.get_next (),
    1418           38 :                                           max_x_range.get_min ()),
    1419              :                           table_y_range);
    1420              :   }
    1421              : 
    1422              :   table::range_t
    1423           76 :   maybe_add_array_index_to_table (table &t,
    1424              :                                   const bit_to_table_map &btm,
    1425              :                                   style_manager &sm,
    1426              :                                   const table::range_t table_y_range,
    1427              :                                   tree idx_cst) const
    1428              :   {
    1429           76 :     region_model_manager * const mgr = m_op.get_manager ();
    1430           76 :     tree base_type = m_op.m_base_region->get_type ();
    1431           76 :     const svalue *idx_sval
    1432           76 :       = mgr->get_or_create_constant_svalue (idx_cst);
    1433           76 :     const region *element_reg = mgr->get_element_region (m_op.m_base_region,
    1434           76 :                                                          TREE_TYPE (base_type),
    1435              :                                                          idx_sval);
    1436           76 :     const access_range element_range (*element_reg, mgr);
    1437           76 :     const table::range_t element_x_range
    1438           76 :       = btm.get_table_x_for_range (element_range);
    1439              : 
    1440           76 :     t.maybe_set_cell_span (table::rect_t (element_x_range,
    1441           76 :                                           table_y_range),
    1442          152 :                            fmt_styled_string (sm, "[%E]", idx_cst));
    1443              : 
    1444           76 :     return element_x_range;
    1445              :   }
    1446              : 
    1447           68 :   table make_table (const bit_to_table_map &btm,
    1448              :                     style_manager &sm) const final override
    1449              :   {
    1450           68 :     table t (table::size_t (btm.get_num_columns (), 0));
    1451              : 
    1452           68 :     if (tree base_type = m_op.m_base_region->get_type ())
    1453           39 :       if (TREE_CODE (base_type) == ARRAY_TYPE)
    1454           39 :         add_array_elements_to_table (t, btm, sm);
    1455              : 
    1456              :     /* Make use of m_existing_sval_spatial_item, if any.  */
    1457           68 :     if (m_existing_sval_spatial_item)
    1458              :       {
    1459           13 :         table table_for_existing
    1460           13 :           = m_existing_sval_spatial_item->make_table (btm, sm);
    1461           13 :         const int table_y = t.add_rows (table_for_existing.get_size ().h);
    1462           13 :         t.add_other_table (std::move (table_for_existing),
    1463           13 :                            table::coord_t (0, table_y));
    1464           13 :       }
    1465              : 
    1466           68 :     access_range valid_bits = m_op.get_valid_bits ();
    1467           68 :     const int table_y = t.add_row ();
    1468           68 :     const int table_h = 1;
    1469           68 :     table::rect_t rect = btm.get_table_rect (valid_bits, table_y, table_h);
    1470           68 :     styled_string s;
    1471           68 :     switch (m_op.m_base_region->get_kind ())
    1472              :       {
    1473            0 :       default:
    1474            0 :         s = styled_string (sm, _("region"));
    1475            0 :         break;
    1476           37 :       case RK_DECL:
    1477           37 :         {
    1478           37 :           const decl_region *decl_reg
    1479           37 :             = as_a <const decl_region *> (m_op.m_base_region);
    1480           37 :           tree decl = decl_reg->get_decl ();
    1481           37 :           s = fmt_styled_string (sm, "%qE (type: %qT)",
    1482              :                                  decl,
    1483           74 :                                  TREE_TYPE (decl));
    1484              :         }
    1485           37 :         break;
    1486           17 :       case RK_HEAP_ALLOCATED:
    1487           17 :         {
    1488           17 :           if (m_region_creation_event_id.known_p ())
    1489           34 :             s = fmt_styled_string (sm, _("buffer allocated on heap at %@"),
    1490           17 :                                    &m_region_creation_event_id);
    1491              :           else
    1492            0 :             s = styled_string (sm, _("heap-allocated buffer"));
    1493              :         }
    1494              :         break;
    1495           12 :       case RK_ALLOCA:
    1496           12 :         {
    1497           12 :           if (m_region_creation_event_id.known_p ())
    1498           24 :             s = fmt_styled_string (sm, _("buffer allocated on stack at %@"),
    1499           12 :                                    &m_region_creation_event_id);
    1500              :           else
    1501            0 :             s = styled_string (sm, _("stack-allocated buffer"));
    1502              :         }
    1503              :         break;
    1504            2 :       case RK_STRING:
    1505            2 :         {
    1506            2 :           const string_region *string_reg
    1507            2 :             = as_a <const string_region *> (m_op.m_base_region);
    1508            2 :           tree string_cst = string_reg->get_string_cst ();
    1509            2 :           s = fmt_styled_string (sm, _("string literal (type: %qT)"),
    1510            4 :                                  TREE_TYPE (string_cst));
    1511              :         }
    1512            2 :         break;
    1513              :       }
    1514           68 :     t.set_cell_span (rect, std::move (s));
    1515              : 
    1516           68 :     return t;
    1517           68 :   }
    1518              : 
    1519              : private:
    1520              :   const access_operation &m_op;
    1521              :   diagnostics::paths::event_id_t m_region_creation_event_id;
    1522              :   mutable const boundaries *m_boundaries;
    1523              :   const svalue *m_existing_sval;
    1524              :   std::unique_ptr<spatial_item> m_existing_sval_spatial_item;
    1525              : };
    1526              : 
    1527              : /* Subclass of spatial_item for visualizing the region of memory
    1528              :    that's actually accessed by the read or write, for reads and
    1529              :    for write cases where we don't know the svalue written.  */
    1530              : 
    1531              : class accessed_region_spatial_item : public spatial_item
    1532              : {
    1533              : public:
    1534           72 :   accessed_region_spatial_item (const access_operation &op) : m_op (op) {}
    1535              : 
    1536           72 :   void add_boundaries (boundaries &out, logger *logger) const final override
    1537              :   {
    1538           72 :     LOG_SCOPE (logger);
    1539           72 :     access_range actual_bits = m_op.get_actual_bits ();
    1540           72 :     if (logger)
    1541              :       {
    1542            0 :         logger->start_log_line ();
    1543            0 :         logger->log_partial ("actual bits: ");
    1544            0 :         actual_bits.dump_to_pp (logger->get_printer (), true);
    1545            0 :         logger->end_log_line ();
    1546              :       }
    1547           72 :     out.add (actual_bits, boundaries::kind::HARD);
    1548           72 :   }
    1549              : 
    1550           17 :   table make_table (const bit_to_table_map &btm,
    1551              :                     style_manager &sm) const final override
    1552              :   {
    1553           17 :     table t (table::size_t (btm.get_num_columns (), 1));
    1554              : 
    1555           17 :     access_range actual_bits = m_op.get_actual_bits ();
    1556           17 :     const int table_y = 0;
    1557           17 :     const int table_h = 1;
    1558           17 :     table::rect_t rect = btm.get_table_rect (actual_bits, table_y, table_h);
    1559           17 :     t.set_cell_span (rect, styled_string (get_label_string (sm)));
    1560              : 
    1561           17 :     return t;
    1562              :   }
    1563              : 
    1564              : private:
    1565           17 :   styled_string get_label_string (style_manager &sm) const
    1566              :   {
    1567           17 :     const access_range accessed_bits (m_op.get_actual_bits ());
    1568           17 :     return get_access_size_str (sm,
    1569              :                                 m_op,
    1570              :                                 accessed_bits,
    1571           17 :                                 m_op.m_reg.get_type ());
    1572              :   }
    1573              : 
    1574              :   const access_operation &m_op;
    1575              : };
    1576              : 
    1577              : /* Subclass of spatial_item for when we know the svalue being written
    1578              :    to the accessed region.
    1579              :    Can be subclassed to give visualizations of specific kinds of svalue.  */
    1580              : 
    1581              : class written_svalue_spatial_item : public spatial_item
    1582              : {
    1583              : public:
    1584           38 :   written_svalue_spatial_item (const access_operation &op,
    1585              :                        const svalue &sval,
    1586              :                        access_range actual_bits)
    1587           38 :   : m_op (op), m_sval (sval), m_actual_bits (actual_bits)
    1588              :   {}
    1589              : 
    1590           38 :   void add_boundaries (boundaries &out, logger *logger) const override
    1591              :   {
    1592           38 :     LOG_SCOPE (logger);
    1593           38 :     out.add (m_actual_bits, boundaries::kind::HARD);
    1594           38 :   }
    1595              : 
    1596           34 :   table make_table (const bit_to_table_map &btm,
    1597              :                     style_manager &sm) const override
    1598              :   {
    1599           34 :     table t (table::size_t (btm.get_num_columns (), 0));
    1600              : 
    1601           34 :     const int table_y = t.add_row ();
    1602           34 :     const int table_h = 1;
    1603           34 :     table::rect_t rect = btm.get_table_rect (m_actual_bits, table_y, table_h);
    1604           34 :     t.set_cell_span (rect, styled_string (get_label_string (sm)));
    1605           34 :     return t;
    1606              :   }
    1607              : 
    1608              : protected:
    1609           34 :   styled_string get_label_string (style_manager &sm) const
    1610              :   {
    1611           34 :     tree rep_tree = m_op.m_model.get_representative_tree (&m_sval);
    1612           34 :     if (rep_tree)
    1613              :       {
    1614           30 :         if (TREE_CODE (rep_tree) == SSA_NAME)
    1615           17 :           if (tree var = SSA_NAME_VAR (rep_tree))
    1616           30 :             rep_tree = var;
    1617           30 :         switch (TREE_CODE (rep_tree))
    1618              :           {
    1619              :           default:
    1620              :             break;
    1621           13 :           case INTEGER_CST:
    1622           26 :             return fmt_styled_string (sm, _("write of %<(%T) %E%>"),
    1623           13 :                                       TREE_TYPE (rep_tree),
    1624           13 :                                       rep_tree);
    1625              : 
    1626           13 :           case PARM_DECL:
    1627           13 :           case VAR_DECL:
    1628           26 :             return fmt_styled_string (sm, _("write from %qE (type: %qT)"),
    1629              :                                       rep_tree,
    1630           13 :                                       TREE_TYPE (rep_tree));
    1631              :             break;
    1632              :           }
    1633              :         }
    1634              : 
    1635            8 :     const access_range accessed_bits (m_op.get_actual_bits ());
    1636            8 :     return get_access_size_str (sm,
    1637              :                                 m_op,
    1638              :                                 accessed_bits,
    1639            8 :                                 m_sval.get_type ());
    1640              :   }
    1641              : 
    1642              :   const access_operation &m_op;
    1643              :   const svalue &m_sval;
    1644              :   access_range m_actual_bits;
    1645              : };
    1646              : 
    1647              : /* Subclass of svalue_spatial_item for initial_svalue of a string_region
    1648              :    i.e. for string literals.
    1649              : 
    1650              :    There are three cases:
    1651              :    (a) for long strings, show just the head and tail of the string,
    1652              :    with an ellipsis:
    1653              :      +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
    1654              :      |[0]|[1]|[2]|[3]|[4]|[5]|          |[440]|[441]|[442]|[443]|[444]|[445]|
    1655              :      +---+---+---+---+---+---+   ...    +-----+-----+-----+-----+-----+-----+
    1656              :      |‘L’|‘o’|‘r’|‘e’|‘m’|‘ ’|          | ‘o’ | ‘r’ | ‘u’ | ‘m’ | ‘.’ | NUL |
    1657              :      +---+---+---+---+---+---+----------+-----+-----+-----+-----+-----+-----+
    1658              :      |                  string literal (type: ‘char[446]’)                  |
    1659              :      +----------------------------------------------------------------------+
    1660              :    (b) For sufficiently short strings, show the full string:
    1661              :      +----------+---------+---------+---------+---------+ +-----------------+
    1662              :      |   [0]    |   [1]   |   [2]   |   [3]   |   [4]   | |       [5]       |
    1663              :      +----------+---------+---------+---------+---------+ +-----------------+
    1664              :      |   ‘h’    |   ‘e’   |   ‘l’   |   ‘l’   |   ‘o’   | |       NUL       |
    1665              :      +----------+---------+---------+---------+---------+-+-----------------+
    1666              :      |                   string literal (type: ‘char[6]’)                   |
    1667              :      +----------------------------------------------------------------------+
    1668              :    (c) for non-ASCII strings that are short enough to show the full string,
    1669              :    show how unicode code points of the bytes decoded as UTF-8:
    1670              :      +-----+-----+-----+----+----++----+----+----+----+----+----+----+------+
    1671              :      | [0] | [1] | [2] |[3] |[4] ||[5] |[6] |[7] |[8] |[9] |[10]|[11]| [12] |
    1672              :      +-----+-----+-----+----+----++----+----+----+----+----+----+----+------+
    1673              :      |0xe6 |0x96 |0x87 |0xe5|0xad||0x97|0xe5|0x8c|0x96|0xe3|0x81|0x91| 0x00 |
    1674              :      +-----+-----+-----+----+----++----+----+----+----+----+----+----+------+
    1675              :      |     U+6587      |    U+5b57     |    U+5316    |    U+3051    |U+0000|
    1676              :      +-----------------+---------------+--------------+--------------+------+
    1677              :      |                  string literal (type: ‘char[13]’)                   |
    1678              :      +----------------------------------------------------------------------+
    1679              :    and show the characters themselves if unicode is supported and they are not
    1680              :    control characters:
    1681              :      ┌─────┬─────┬─────┬────┬────┐┌────┬────┬────┬────┬────┬────┬────┬──────┐
    1682              :      │ [0] │ [1] │ [2] │[3] │[4] ││[5] │[6] │[7] │[8] │[9] │[10]│[11]│ [12] │
    1683              :      ├─────┼─────┼─────┼────┼────┤├────┼────┼────┼────┼────┼────┼────┼──────┤
    1684              :      │0xe6 │0x96 │0x87 │0xe5│0xad││0x97│0xe5│0x8c│0x96│0xe3│0x81│0x91│ 0x00 │
    1685              :      ├─────┴─────┴─────┼────┴────┴┴────┼────┴────┴────┼────┴────┴────┼──────┤
    1686              :      │     U+6587      │    U+5b57     │    U+5316    │    U+3051    │U+0000│
    1687              :      ├─────────────────┼───────────────┼──────────────┼──────────────┼──────┤
    1688              :      │       文        │      字       │      化      │      け      │ NUL  │
    1689              :      ├─────────────────┴───────────────┴──────────────┴──────────────┴──────┤
    1690              :      │                  string literal (type: ‘char[13]’)                   │
    1691              :      └──────────────────────────────────────────────────────────────────────┘
    1692              : */
    1693              : 
    1694              : class string_literal_spatial_item : public svalue_spatial_item
    1695              : {
    1696              : public:
    1697           30 :   string_literal_spatial_item (const svalue &sval,
    1698              :                                access_range actual_bits,
    1699              :                                const string_region &string_reg,
    1700              :                                const theme &theme,
    1701              :                                enum kind kind)
    1702           30 :   : svalue_spatial_item (sval, actual_bits, kind),
    1703           30 :     m_string_reg (string_reg),
    1704           30 :     m_theme (theme),
    1705           30 :     m_ellipsis_threshold (param_analyzer_text_art_string_ellipsis_threshold),
    1706           30 :     m_ellipsis_head_len (param_analyzer_text_art_string_ellipsis_head_len),
    1707           30 :     m_ellipsis_tail_len (param_analyzer_text_art_string_ellipsis_tail_len),
    1708           60 :     m_show_full_string (calc_show_full_string ()),
    1709           30 :     m_show_utf8 (m_show_full_string && !pure_ascii_p ())
    1710              :   {
    1711           30 :   }
    1712              : 
    1713           30 :   void add_boundaries (boundaries &out, logger *logger) const override
    1714              :   {
    1715           30 :     LOG_SCOPE (logger);
    1716           30 :     out.add (m_bits, m_kind == svalue_spatial_item::kind::WRITTEN
    1717           30 :              ? boundaries::kind::HARD
    1718              :              : boundaries::kind::SOFT);
    1719              : 
    1720           30 :     tree string_cst = get_string_cst ();
    1721              :     /* TREE_STRING_LENGTH is sizeof, not strlen.  */
    1722           30 :     if (m_show_full_string)
    1723           18 :       out.add_all_bytes_in_range (m_bits);
    1724              :     else
    1725              :       {
    1726           12 :         byte_range bytes (0, 0);
    1727           12 :         bool valid = m_bits.as_concrete_byte_range (&bytes);
    1728           12 :         gcc_assert (valid);
    1729           12 :         byte_range head_of_string (bytes.get_start_byte_offset (),
    1730           12 :                                    m_ellipsis_head_len);
    1731           12 :         out.add_all_bytes_in_range (head_of_string);
    1732           12 :         byte_range tail_of_string
    1733           12 :           ((bytes.get_start_byte_offset ()
    1734           12 :             + TREE_STRING_LENGTH (string_cst)
    1735           12 :             - m_ellipsis_tail_len),
    1736           24 :            m_ellipsis_tail_len);
    1737           12 :         out.add_all_bytes_in_range (tail_of_string);
    1738              :         /* Adding the above pair of ranges will also effectively add
    1739              :            the boundaries of the range of ellipsized chars, as they're
    1740              :            exactly in between head_of_string and tail_of_string.  */
    1741              :       }
    1742           30 :   }
    1743              : 
    1744           30 :   table make_table (const bit_to_table_map &btm,
    1745              :                     style_manager &sm) const override
    1746              :   {
    1747           30 :     table t (table::size_t (btm.get_num_columns (), 0));
    1748              : 
    1749           30 :     const int byte_idx_table_y = (m_kind == svalue_spatial_item::kind::WRITTEN
    1750           30 :                                   ? t.add_row ()
    1751              :                                   : -1);
    1752           30 :     const int byte_val_table_y = t.add_row ();
    1753              : 
    1754           30 :     byte_range bytes (0, 0);
    1755           30 :     bool valid = m_bits.as_concrete_byte_range (&bytes);
    1756           30 :     gcc_assert (valid);
    1757           30 :     tree string_cst = get_string_cst ();
    1758           30 :     if (m_show_full_string)
    1759              :       {
    1760           18 :        for (byte_offset_t byte_idx_within_cluster
    1761           18 :               = bytes.get_start_byte_offset ();
    1762          155 :             byte_idx_within_cluster < bytes.get_next_byte_offset ();
    1763          137 :             byte_idx_within_cluster = byte_idx_within_cluster + 1)
    1764          137 :          add_column_for_byte
    1765          137 :            (t, btm, sm, byte_idx_within_cluster,
    1766          274 :             byte_idx_within_cluster - bytes.get_start_byte_offset (),
    1767              :             byte_idx_table_y, byte_val_table_y);
    1768              : 
    1769           18 :        if (m_show_utf8)
    1770              :          {
    1771            6 :            const bool show_unichars = m_theme.unicode_p ();
    1772            6 :            const int utf8_code_point_table_y = t.add_row ();
    1773            6 :            int utf8_character_table_y;
    1774            6 :            if (show_unichars)
    1775            5 :              utf8_character_table_y = t.add_row ();
    1776              : 
    1777              :            /* We don't actually want the display widths here, but
    1778              :               it's an easy way to decode UTF-8.  */
    1779            6 :            cpp_char_column_policy policy (8, cpp_wcwidth);
    1780            6 :            cpp_display_width_computation dw (TREE_STRING_POINTER (string_cst),
    1781            6 :                                              TREE_STRING_LENGTH (string_cst),
    1782            6 :                                              policy);
    1783           47 :            while (!dw.done ())
    1784              :              {
    1785           35 :                cpp_decoded_char decoded_char;
    1786           35 :                dw.process_next_codepoint (&decoded_char);
    1787              : 
    1788           35 :                if (!decoded_char.m_valid_ch)
    1789            0 :                  continue;
    1790           35 :                size_t start_byte_idx
    1791           35 :                  = decoded_char.m_start_byte - TREE_STRING_POINTER (string_cst);
    1792           35 :                byte_size_t size_in_bytes
    1793           35 :                  = decoded_char.m_next_byte - decoded_char.m_start_byte;
    1794           35 :                byte_range cluster_bytes_for_codepoint
    1795           35 :                  (start_byte_idx + bytes.get_start_byte_offset (),
    1796           35 :                   size_in_bytes);
    1797              : 
    1798           35 :                const table::rect_t code_point_table_rect
    1799           35 :                  = btm.get_table_rect (&m_string_reg,
    1800              :                                        cluster_bytes_for_codepoint,
    1801              :                                        utf8_code_point_table_y, 1);
    1802           35 :                char buf[100];
    1803           35 :                sprintf (buf, "U+%04x", decoded_char.m_ch);
    1804           35 :                t.set_cell_span (code_point_table_rect,
    1805           70 :                                 styled_string (sm, buf));
    1806              : 
    1807           35 :                if (show_unichars)
    1808              :                  {
    1809           30 :                    const table::rect_t character_table_rect
    1810           30 :                      = btm.get_table_rect (&m_string_reg,
    1811              :                                            cluster_bytes_for_codepoint,
    1812              :                                            utf8_character_table_y, 1);
    1813           30 :                    if (cpp_is_printable_char (decoded_char.m_ch))
    1814           23 :                      t.set_cell_span (character_table_rect,
    1815           46 :                                       styled_string (decoded_char.m_ch));
    1816            7 :                    else if (decoded_char.m_ch == 0)
    1817            5 :                      t.set_cell_span (character_table_rect,
    1818           10 :                                       styled_string (sm, "NUL"));
    1819              :                    else
    1820            2 :                      t.set_cell_span (character_table_rect,
    1821            4 :                                       styled_string (sm, ""));
    1822              :                  }
    1823              :              }
    1824              :          }
    1825              :       }
    1826              :     else
    1827              :       {
    1828              :         /* Head of string.  */
    1829           84 :         for (int byte_idx = 0; byte_idx < m_ellipsis_head_len; byte_idx++)
    1830          144 :           add_column_for_byte (t, btm, sm,
    1831          144 :                                byte_idx + bytes.get_start_byte_offset (),
    1832           72 :                                byte_idx,
    1833              :                                byte_idx_table_y, byte_val_table_y);
    1834              : 
    1835              :         /* Ellipsis.  */
    1836           12 :         const byte_range ellipsis_bytes
    1837           12 :           (m_ellipsis_head_len + bytes.get_start_byte_offset (),
    1838           12 :            TREE_STRING_LENGTH (string_cst)
    1839           12 :            - (m_ellipsis_head_len + m_ellipsis_tail_len));
    1840           12 :         const table::rect_t table_rect
    1841              :           = ((byte_idx_table_y != -1)
    1842           12 :              ? btm.get_table_rect (&m_string_reg, ellipsis_bytes,
    1843              :                                    byte_idx_table_y, 2)
    1844            6 :              : btm.get_table_rect (&m_string_reg, ellipsis_bytes,
    1845              :                                    byte_val_table_y, 1));
    1846           12 :         t.set_cell_span(table_rect, styled_string (sm, "..."));
    1847              : 
    1848              :         /* Tail of string.  */
    1849           12 :         for (int byte_idx
    1850           12 :                = (TREE_STRING_LENGTH (string_cst) - m_ellipsis_tail_len);
    1851           84 :              byte_idx < TREE_STRING_LENGTH (string_cst);
    1852              :              byte_idx++)
    1853          144 :           add_column_for_byte (t, btm, sm,
    1854          144 :                                byte_idx + bytes.get_start_byte_offset (),
    1855           72 :                                byte_idx,
    1856              :                                byte_idx_table_y, byte_val_table_y);
    1857              :       }
    1858              : 
    1859           30 :     if (m_kind == svalue_spatial_item::kind::WRITTEN)
    1860              :       {
    1861           17 :         const int summary_table_y = t.add_row ();
    1862           17 :         t.set_cell_span (btm.get_table_rect (&m_string_reg, bytes,
    1863              :                                              summary_table_y, 1),
    1864           34 :                          fmt_styled_string (sm,
    1865           17 :                                             _("string literal (type: %qT)"),
    1866           17 :                                             TREE_TYPE (string_cst)));
    1867              :       }
    1868              : 
    1869           30 :     return t;
    1870              :   }
    1871              : 
    1872           60 :   tree get_string_cst () const { return m_string_reg.get_string_cst (); }
    1873              : 
    1874              : private:
    1875           30 :   bool calc_show_full_string () const
    1876              :   {
    1877           30 :     tree string_cst = get_string_cst ();
    1878           30 :     if (TREE_STRING_LENGTH (string_cst) < m_ellipsis_threshold)
    1879              :       return true;
    1880           13 :     if (TREE_STRING_LENGTH (string_cst) <
    1881           13 :         (m_ellipsis_head_len + m_ellipsis_tail_len))
    1882            1 :       return true;
    1883              :     return false;
    1884              :   }
    1885              : 
    1886           18 :   bool pure_ascii_p () const
    1887              :   {
    1888           18 :     tree string_cst = get_string_cst ();
    1889          104 :     for (unsigned byte_idx = 0;
    1890          104 :          byte_idx < (unsigned) TREE_STRING_LENGTH (string_cst);
    1891              :          byte_idx++)
    1892              :       {
    1893           92 :         unsigned char ch = TREE_STRING_POINTER (string_cst)[byte_idx];
    1894           92 :         if (ch >= 0x80)
    1895              :           return false;
    1896              :       }
    1897              :     return true;
    1898              :   }
    1899              : 
    1900          281 :   void add_column_for_byte (table &t, const bit_to_table_map &btm,
    1901              :                             style_manager &sm,
    1902              :                             const byte_offset_t byte_idx_within_cluster,
    1903              :                             const byte_offset_t byte_idx_within_string,
    1904              :                             const int byte_idx_table_y,
    1905              :                             const int byte_val_table_y) const
    1906              :   {
    1907          281 :     tree string_cst = get_string_cst ();
    1908          281 :     gcc_assert (byte_idx_within_string >= 0);
    1909          281 :     gcc_assert (byte_idx_within_string < TREE_STRING_LENGTH (string_cst));
    1910              : 
    1911          281 :     const byte_range bytes (byte_idx_within_cluster, 1);
    1912          281 :     if (byte_idx_table_y != -1)
    1913              :       {
    1914          168 :         const table::rect_t idx_table_rect
    1915          168 :           = btm.get_table_rect (&m_string_reg, bytes, byte_idx_table_y, 1);
    1916          336 :         t.set_cell_span (idx_table_rect,
    1917          336 :                          fmt_styled_string (sm, "[%wu]",
    1918              :                                             byte_idx_within_string.ulow ()));
    1919              :       }
    1920              : 
    1921          281 :     char byte_val
    1922          281 :       = TREE_STRING_POINTER (string_cst)[byte_idx_within_string.ulow ()];
    1923          281 :     const table::rect_t val_table_rect
    1924          281 :       = btm.get_table_rect (&m_string_reg, bytes, byte_val_table_y, 1);
    1925          281 :     table_cell_content content (make_cell_content_for_byte (sm, byte_val));
    1926          281 :     t.set_cell_span (val_table_rect, std::move (content));
    1927          281 :   }
    1928              : 
    1929          281 :   table_cell_content make_cell_content_for_byte (style_manager &sm,
    1930              :                                                  unsigned char byte_val) const
    1931              :   {
    1932          281 :     if (!m_show_utf8)
    1933              :        {
    1934          218 :         if (byte_val == '\0')
    1935           24 :           return styled_string (sm, "NUL");
    1936          194 :         else if (byte_val < 0x80)
    1937          194 :           if (ISPRINT (byte_val))
    1938          190 :             return fmt_styled_string (sm, "%qc", byte_val);
    1939              :        }
    1940           67 :     char buf[100];
    1941           67 :     sprintf (buf, "0x%02x", byte_val);
    1942           67 :     return styled_string (sm, buf);
    1943              :   }
    1944              : 
    1945              :   const string_region &m_string_reg;
    1946              :   const theme &m_theme;
    1947              :   const int m_ellipsis_threshold;
    1948              :   const int m_ellipsis_head_len;
    1949              :   const int m_ellipsis_tail_len;
    1950              :   const bool m_show_full_string;
    1951              :   const bool m_show_utf8;
    1952              : };
    1953              : 
    1954              : static std::unique_ptr<spatial_item>
    1955           55 : make_written_svalue_spatial_item (const access_operation &op,
    1956              :                                   const svalue &sval,
    1957              :                                   access_range actual_bits,
    1958              :                                   const theme &theme)
    1959              : {
    1960           55 :   if (const initial_svalue *initial_sval = sval.dyn_cast_initial_svalue ())
    1961           33 :     if (const string_region *string_reg
    1962           33 :         = initial_sval->get_region ()->dyn_cast_string_region ())
    1963           17 :       return std::make_unique <string_literal_spatial_item>
    1964           17 :         (sval, actual_bits,
    1965              :          *string_reg, theme,
    1966           17 :          svalue_spatial_item::kind::WRITTEN);
    1967           38 :   return std::make_unique <written_svalue_spatial_item> (op, sval, actual_bits);
    1968              : }
    1969              : 
    1970              : static std::unique_ptr<spatial_item>
    1971           98 : make_existing_svalue_spatial_item (const svalue *sval,
    1972              :                                    const access_range &bits,
    1973              :                                    const theme &theme)
    1974              : {
    1975           98 :   if (!sval)
    1976            0 :     return nullptr;
    1977              : 
    1978           98 :   switch (sval->get_kind ())
    1979              :     {
    1980           60 :     default:
    1981           60 :       return nullptr;
    1982              : 
    1983           27 :     case SK_INITIAL:
    1984           27 :       {
    1985           27 :         const initial_svalue *initial_sval = (const initial_svalue *)sval;
    1986           27 :         if (const string_region *string_reg
    1987           27 :             = initial_sval->get_region ()->dyn_cast_string_region ())
    1988           13 :           return std::make_unique <string_literal_spatial_item>
    1989           13 :             (*sval, bits,
    1990              :              *string_reg, theme,
    1991           13 :              svalue_spatial_item::kind::EXISTING);
    1992           14 :         return nullptr;
    1993              :       }
    1994              : 
    1995           11 :     case SK_COMPOUND:
    1996           11 :       return std::make_unique<compound_svalue_spatial_item>
    1997           11 :         (*((const compound_svalue *)sval),
    1998              :          bits,
    1999           22 :          svalue_spatial_item::kind::EXISTING,
    2000           11 :          theme);
    2001              :     }
    2002              : }
    2003              : 
    2004              : /* Widget subclass implementing access diagrams.  */
    2005              : 
    2006              : class access_diagram_impl : public vbox_widget
    2007              : {
    2008              : public:
    2009           72 :   access_diagram_impl (const access_operation &op,
    2010              :                        diagnostics::paths::event_id_t region_creation_event_id,
    2011              :                        style_manager &sm,
    2012              :                        const theme &theme,
    2013              :                        logger *logger)
    2014           72 :   : m_op (op),
    2015           72 :     m_region_creation_event_id (region_creation_event_id),
    2016           72 :     m_sm (sm),
    2017           72 :     m_theme (theme),
    2018           72 :     m_logger (logger),
    2019           72 :     m_invalid (false),
    2020           72 :     m_valid_region_spatial_item (op, region_creation_event_id, theme),
    2021           72 :     m_accessed_region_spatial_item (op),
    2022           72 :     m_btm (),
    2023           72 :     m_calc_req_size_called (false)
    2024              :   {
    2025           72 :     LOG_SCOPE (logger);
    2026              : 
    2027           72 :     if (logger)
    2028              :       {
    2029            0 :         access_range invalid_before_bits;
    2030            0 :         if (op.maybe_get_invalid_before_bits (&invalid_before_bits))
    2031            0 :           invalid_before_bits.log ("invalid before range", *logger);
    2032            0 :         access_range invalid_after_bits;
    2033            0 :         if (op.maybe_get_invalid_after_bits (&invalid_after_bits))
    2034            0 :           invalid_after_bits.log ("invalid after range", *logger);
    2035              : 
    2036            0 :         if (op.m_sval_hint)
    2037              :           {
    2038            0 :             logger->start_log_line ();
    2039            0 :             logger->log_partial ("sval_hint: ");
    2040            0 :             op.m_sval_hint->dump_to_pp (logger->get_printer (), true);
    2041            0 :             logger->end_log_line ();
    2042              :           }
    2043              :       }
    2044              : 
    2045              :     /* Register painting styles.  */
    2046           72 :     {
    2047           72 :       style valid_style (get_style_from_color_cap_name ("valid"));
    2048           72 :       m_valid_style_id = m_sm.get_or_create_id (valid_style);
    2049              : 
    2050           72 :       style invalid_style (get_style_from_color_cap_name ("invalid"));
    2051           72 :       m_invalid_style_id = m_sm.get_or_create_id (invalid_style);
    2052           72 :     }
    2053              : 
    2054           72 :     if (op.m_sval_hint)
    2055              :       {
    2056           55 :         access_range actual_bits = m_op.get_actual_bits ();
    2057           55 :         m_written_svalue_spatial_item
    2058           55 :           = make_written_svalue_spatial_item (m_op,
    2059           55 :                                               *op.m_sval_hint,
    2060              :                                               actual_bits,
    2061           55 :                                               m_theme);
    2062              :       }
    2063              : 
    2064              :     /* Two passes:
    2065              :        First, figure out all of the boundaries of interest.
    2066              :        Then use that to build child widgets showing the regions of interest,
    2067              :        with a common tabular layout.  */
    2068              : 
    2069           72 :     m_boundaries = find_boundaries ();
    2070           72 :     if (logger)
    2071            0 :       m_boundaries->log (*logger);
    2072              : 
    2073              :     /* Populate m_table_x_for_bit and m_bit_for_table_x.
    2074              :        Each table column represents the range [offset, next_offset).
    2075              :        We don't create a column in the table for the final offset, but we
    2076              :        do populate it, so that looking at the table_x of one beyond the
    2077              :        final table column gives us the upper bound offset.  */
    2078           72 :     m_btm.populate (*m_boundaries, *m_op.get_manager (), logger);
    2079              : 
    2080              :     /* Gracefully reject cases where the boundary sorting has gone wrong
    2081              :        (due to awkward combinations of symbolic values).  */
    2082           72 :     {
    2083           72 :       table::range_t actual_bits_x_range
    2084           72 :         = m_btm.get_table_x_for_range (m_op.get_actual_bits ());
    2085           72 :       if (actual_bits_x_range.get_size () <= 0)
    2086              :         {
    2087            4 :           if (logger)
    2088            0 :             logger->log ("giving up: bad table columns for actual_bits");
    2089            4 :           m_invalid = true;
    2090            4 :           return;
    2091              :         }
    2092           68 :       table::range_t valid_bits_x_range
    2093           68 :         = m_btm.get_table_x_for_range (m_op.get_valid_bits ());
    2094           68 :       if (valid_bits_x_range.get_size () <= 0)
    2095              :         {
    2096            0 :           if (logger)
    2097            0 :             logger->log ("giving up: bad table columns for valid_bits");
    2098            0 :           m_invalid = true;
    2099            0 :           return;
    2100              :         }
    2101              :     }
    2102              : 
    2103           68 :     m_col_widths
    2104           68 :       = std::make_unique <table_dimension_sizes> (m_btm.get_num_columns ());
    2105              : 
    2106              :     /* Now create child widgets.  */
    2107              : 
    2108           68 :     if (flag_analyzer_debug_text_art)
    2109              :       {
    2110            1 :         table t_headings (make_headings_table ());
    2111            2 :         add_aligned_child_table (std::move (t_headings));
    2112            1 :       }
    2113              : 
    2114           68 :     if (m_written_svalue_spatial_item)
    2115              :       {
    2116           51 :         table t_sval (m_written_svalue_spatial_item->make_table (m_btm, m_sm));
    2117          102 :         add_aligned_child_table (std::move (t_sval));
    2118           51 :       }
    2119              :     else
    2120              :       {
    2121           17 :         table t_accessed
    2122           17 :           (m_accessed_region_spatial_item.make_table (m_btm, m_sm));
    2123           34 :         add_aligned_child_table (std::move (t_accessed));
    2124           17 :       }
    2125              : 
    2126           68 :     add_direction_widget ();
    2127              : 
    2128           68 :     table t_valid (m_valid_region_spatial_item.make_table (m_btm, m_sm));
    2129           68 :     add_invalid_accesses_to_region_table (t_valid);
    2130           68 :     add_aligned_child_table (std::move (t_valid));
    2131              : 
    2132           68 :     add_valid_vs_invalid_ruler ();
    2133           72 :   }
    2134              : 
    2135            0 :   const char *get_desc () const override
    2136              :   {
    2137            0 :     return "access_diagram_impl";
    2138              :   }
    2139              : 
    2140           72 :   canvas::size_t calc_req_size () final override
    2141              :   {
    2142           72 :     if (m_invalid)
    2143            4 :       return canvas::size_t (0, 0);
    2144              : 
    2145              :     /* Now compute the size requirements for the tables.  */
    2146          205 :     for (auto iter : m_aligned_table_widgets)
    2147          137 :       iter->get_cell_sizes ().pass_1 (iter->get_table ());
    2148          205 :     for (auto iter : m_aligned_table_widgets)
    2149          137 :       iter->get_cell_sizes ().pass_2 (iter->get_table ());
    2150              : 
    2151           68 :     adjust_to_scale();
    2152              : 
    2153              :     /* ...and relayout the tables.  */
    2154          205 :     for (auto iter : m_aligned_table_widgets)
    2155          137 :       iter->recalc_coords ();
    2156              : 
    2157              :     /* Populate the canvas_x per table_x.  */
    2158           68 :     m_col_start_x.clear ();
    2159           68 :     int iter_canvas_x = 0;
    2160          624 :     for (auto w : m_col_widths->m_requirements)
    2161              :       {
    2162          556 :         m_col_start_x.push_back (iter_canvas_x);
    2163          556 :         iter_canvas_x += w + 1;
    2164              :       }
    2165           68 :     m_col_start_x.push_back (iter_canvas_x);
    2166              : 
    2167           68 :     m_calc_req_size_called = true;
    2168              : 
    2169           68 :     return vbox_widget::calc_req_size ();
    2170              :   }
    2171              : 
    2172         1172 :   int get_canvas_x_for_table_x (int table_x) const
    2173              :   {
    2174         1172 :     gcc_assert (m_calc_req_size_called);
    2175         1172 :     return m_col_start_x[table_x];
    2176              :   }
    2177              : 
    2178          586 :   canvas::range_t get_canvas_x_range (const table::range_t &table_x_range) const
    2179              :   {
    2180          586 :     gcc_assert (m_calc_req_size_called);
    2181         1172 :     return canvas::range_t (get_canvas_x_for_table_x (table_x_range.start),
    2182          586 :                             get_canvas_x_for_table_x (table_x_range.next));
    2183              :   }
    2184              : 
    2185          416 :   const access_operation &get_op () const { return m_op; }
    2186              : 
    2187          280 :   style::id_t get_style_id_for_validity (bool is_valid) const
    2188              :   {
    2189          280 :     return is_valid ? m_valid_style_id : m_invalid_style_id;
    2190              :   }
    2191              : 
    2192          280 :   const theme &get_theme () const { return m_theme; }
    2193              : 
    2194              : private:
    2195              :   /* Figure out all of the boundaries of interest when visualizing ths op.  */
    2196              :   std::unique_ptr<boundaries>
    2197           72 :   find_boundaries () const
    2198              :   {
    2199           72 :     auto result
    2200           72 :       = std::make_unique<boundaries> (*m_op.m_base_region, m_logger);
    2201              : 
    2202           72 :     m_valid_region_spatial_item.add_boundaries (*result, m_logger);
    2203           72 :     m_accessed_region_spatial_item.add_boundaries (*result, m_logger);
    2204           72 :     if (m_written_svalue_spatial_item)
    2205           55 :       m_written_svalue_spatial_item->add_boundaries (*result, m_logger);
    2206              : 
    2207           72 :     return result;
    2208              :   }
    2209              : 
    2210          137 :   void add_aligned_child_table (table t)
    2211              :   {
    2212          137 :     auto w = std::make_unique<x_aligned_table_widget> (std::move (t),
    2213          137 :                                                        m_theme, *m_col_widths);
    2214          137 :     m_aligned_table_widgets.push_back (w.get ());
    2215          137 :     add_child (std::move (w));
    2216          137 :   }
    2217              : 
    2218              :   /* Create a table showing headings for use by -fanalyzer-debug-text-art, for
    2219              :      example:
    2220              :      +---------+-----------+-----------+---+--------------------------------+
    2221              :      |   tc0   |    tc1    |    tc2    |tc3|              tc4               |
    2222              :      +---------+-----------+-----------+---+--------------------------------+
    2223              :      |bytes 0-3|bytes 4-35 |bytes 36-39|   |          bytes 40-43           |
    2224              :      +---------+-----------+-----------+   +--------------------------------+
    2225              :      which has:
    2226              :      - a row showing the table column numbers, labelled "tc0", "tc1", etc
    2227              :      - a row showing the memory range of each table column that has one.  */
    2228              : 
    2229            1 :   table make_headings_table () const
    2230              :   {
    2231            1 :     table t (table::size_t (m_btm.get_num_columns (), 2));
    2232              : 
    2233            6 :     for (int table_x = 0; table_x < t.get_size ().w; table_x++)
    2234              :       {
    2235            5 :         const int table_y = 0;
    2236            5 :         t.set_cell (table::coord_t (table_x, table_y),
    2237           10 :                     fmt_styled_string (m_sm, "tc%i", table_x));
    2238              :       }
    2239            6 :     for (int table_x = 0; table_x < t.get_size ().w; table_x++)
    2240              :       {
    2241            5 :         const int table_y = 1;
    2242            5 :         access_range range_for_column (nullptr, bit_range (0, 0));
    2243            5 :         if (m_btm.maybe_get_access_range_for_table_x (table_x,
    2244              :                                                       &range_for_column))
    2245              :           {
    2246            4 :             pretty_printer pp;
    2247            4 :             pp_format_decoder (&pp) = default_tree_printer;
    2248            4 :             range_for_column.dump_to_pp (&pp, true);
    2249            4 :             t.set_cell (table::coord_t (table_x, table_y),
    2250            8 :                         styled_string (m_sm, pp_formatted_text (&pp)));
    2251            4 :           }
    2252              :       }
    2253              : 
    2254            1 :     return t;
    2255              :   }
    2256              : 
    2257           68 :   void add_direction_widget ()
    2258              :   {
    2259           68 :     add_child (std::make_unique<direction_widget> (*this, m_btm));
    2260           68 :   }
    2261              : 
    2262           68 :   void add_invalid_accesses_to_region_table (table &t_region)
    2263              :   {
    2264           68 :     gcc_assert (t_region.get_size ().w == (int)m_btm.get_num_columns ());
    2265              : 
    2266           68 :     const int table_y = 0;
    2267           68 :     const int table_h = t_region.get_size ().h;
    2268              : 
    2269           68 :     access_range invalid_before_bits;
    2270           68 :     if (m_op.maybe_get_invalid_before_bits (&invalid_before_bits))
    2271              :       {
    2272           15 :         t_region.set_cell_span (m_btm.get_table_rect (invalid_before_bits,
    2273              :                                                       table_y, table_h),
    2274           30 :                                 styled_string (m_sm,
    2275           15 :                                                _("before valid range")));
    2276              :       }
    2277           68 :     access_range invalid_after_bits;
    2278           68 :     if (m_op.maybe_get_invalid_after_bits (&invalid_after_bits))
    2279              :       {
    2280           57 :         t_region.set_cell_span (m_btm.get_table_rect (invalid_after_bits,
    2281              :                                                       table_y, table_h),
    2282          114 :                                 styled_string (m_sm,
    2283           57 :                                                _("after valid range")));
    2284              :       }
    2285           68 :   }
    2286              : 
    2287          125 :   void maybe_add_gap (x_aligned_x_ruler_widget *w,
    2288              :                       const access_range &lower,
    2289              :                       const access_range &upper) const
    2290              :   {
    2291          125 :     LOG_SCOPE (m_logger);
    2292          125 :     if (m_logger)
    2293              :       {
    2294            0 :         lower.log ("lower", *m_logger);
    2295            0 :         upper.log ("upper", *m_logger);
    2296              :       }
    2297          125 :     region_model_manager *mgr = m_op.get_manager ();
    2298          125 :     const svalue &lower_next = lower.m_next.calc_symbolic_bit_offset (mgr);
    2299          125 :     const svalue &upper_start = upper.m_start.calc_symbolic_bit_offset (mgr);
    2300          125 :     const svalue *num_bits_gap
    2301          125 :       = mgr->get_or_create_binop (NULL_TREE, MINUS_EXPR,
    2302              :                                   &upper_start, &lower_next);
    2303          125 :     if (m_logger)
    2304            0 :       m_logger->log ("num_bits_gap: %qs", num_bits_gap->get_desc ().get ());
    2305              : 
    2306          125 :     const svalue *zero = mgr->get_or_create_int_cst (NULL_TREE, 0);
    2307          125 :     tristate ts_gt_zero = m_op.m_model.eval_condition (num_bits_gap,
    2308              :                                                        GT_EXPR,
    2309              :                                                        zero);
    2310          125 :     if (ts_gt_zero.is_false ())
    2311              :       {
    2312          111 :         if (m_logger)
    2313            0 :           m_logger->log ("rejecting as not > 0");
    2314          111 :         return;
    2315              :       }
    2316              : 
    2317           14 :     bit_size_expr num_bits (*num_bits_gap);
    2318           14 :     if (auto p = num_bits.maybe_get_formatted_str (m_sm, m_op.m_model,
    2319           14 :                                                    _("%wi bit"),
    2320           14 :                                                    _("%wi bits"),
    2321           14 :                                                    _("%wi byte"),
    2322           14 :                                                    _("%wi bytes"),
    2323           14 :                                                    _("%qs bits"),
    2324           14 :                                                    _("%qs bytes")))
    2325              :       {
    2326           14 :         styled_string label = std::move (*p.get ());
    2327           28 :         w->add_range (m_btm.get_table_x_for_range
    2328           28 :                       (access_range (lower.m_next,
    2329              :                                      upper.m_start,
    2330           14 :                                      *mgr)),
    2331              :                       std::move (label),
    2332              :                       style::id_plain);
    2333           14 :       }
    2334          125 :   }
    2335              : 
    2336              :   styled_string
    2337           72 :   make_warning_string (styled_string &&text)
    2338              :   {
    2339           72 :     styled_string result;
    2340           72 :     if (!m_theme.emojis_p ())
    2341           71 :       return std::move (text);
    2342              : 
    2343            1 :     result.append (styled_string (0x26A0, /* U+26A0 WARNING SIGN.  */
    2344            1 :                                   true));
    2345              :     /* U+26A0 WARNING SIGN has East_Asian_Width == Neutral, but in its
    2346              :        emoji variant is printed (by vte at least) with a 2nd half
    2347              :        overlapping the next char.  Hence we add two spaces here: a space
    2348              :        to be covered by this overlap, plus another space of padding.  */
    2349            1 :     result.append (styled_string (m_sm, "  "));
    2350            1 :     result.append (std::move (text));
    2351            1 :     return result;
    2352           72 :   }
    2353              : 
    2354              :   /* Add a ruler child widet showing valid, invalid, and gaps.  */
    2355           68 :   void add_valid_vs_invalid_ruler ()
    2356              :   {
    2357           68 :     LOG_SCOPE (m_logger);
    2358              : 
    2359           68 :     auto w = std::make_unique<x_aligned_x_ruler_widget> (*this, m_theme);
    2360              : 
    2361           68 :     access_range invalid_before_bits;
    2362           68 :     if (m_op.maybe_get_invalid_before_bits (&invalid_before_bits))
    2363              :       {
    2364           15 :         if (m_logger)
    2365            0 :           invalid_before_bits.log ("invalid_before_bits", *m_logger);
    2366           15 :         bit_size_expr num_before_bits
    2367           15 :           (invalid_before_bits.get_size (m_op.get_manager ()));
    2368           15 :         std::unique_ptr<styled_string> label;
    2369           15 :         if (m_op.m_dir == access_direction::read)
    2370            8 :           label = num_before_bits.maybe_get_formatted_str
    2371           16 :             (m_sm, m_op.m_model,
    2372            8 :              _("under-read of %wi bit"),
    2373            8 :              _("under-read of %wi bits"),
    2374            8 :              _("under-read of %wi byte"),
    2375            8 :              _("under-read of %wi bytes"),
    2376            8 :              _("under-read of %qs bits"),
    2377           16 :              _("under-read of %qs bytes"));
    2378              :         else
    2379            7 :           label = num_before_bits.maybe_get_formatted_str
    2380           14 :             (m_sm, m_op.m_model,
    2381            7 :              _("underwrite of %wi bit"),
    2382            7 :              _("underwrite of %wi bits"),
    2383            7 :              _("underwrite of %wi byte"),
    2384            7 :              _("underwrite of %wi bytes"),
    2385            7 :              _("underwrite of %qs bits"),
    2386           14 :              _("underwrite of %qs bytes"));
    2387           15 :         if (label)
    2388           15 :           w->add_range (m_btm.get_table_x_for_range (invalid_before_bits),
    2389           30 :                         make_warning_string (std::move (*label)),
    2390           15 :                         m_invalid_style_id);
    2391           15 :       }
    2392              :     else
    2393              :       {
    2394           53 :         if (m_logger)
    2395            0 :           m_logger->log ("no invalid_before_bits");
    2396              :       }
    2397              : 
    2398              :     /* It would be nice to be able to use std::optional<access_range> here,
    2399              :        but std::optional is C++17.  */
    2400           68 :     bool got_valid_bits = false;
    2401           68 :     access_range valid_bits (m_op.get_valid_bits ());
    2402           68 :     bit_size_expr num_valid_bits (valid_bits.get_size (m_op.get_manager ()));
    2403           68 :     if (m_logger)
    2404            0 :       valid_bits.log ("valid_bits", *m_logger);
    2405              : 
    2406           68 :     got_valid_bits = true;
    2407           68 :     maybe_add_gap (w.get (), invalid_before_bits, valid_bits);
    2408              : 
    2409           68 :     std::unique_ptr<styled_string> label;
    2410           68 :     if (m_op.m_dir == access_direction::read)
    2411           34 :       label = num_valid_bits.maybe_get_formatted_str (m_sm,
    2412           17 :                                                       m_op.m_model,
    2413           17 :                                                       _("size: %wi bit"),
    2414           17 :                                                       _("size: %wi bits"),
    2415           17 :                                                       _("size: %wi byte"),
    2416           17 :                                                       _("size: %wi bytes"),
    2417           17 :                                                       _("size: %qs bits"),
    2418           34 :                                                       _("size: %qs bytes"));
    2419              :     else
    2420           51 :       label
    2421          102 :         = num_valid_bits.maybe_get_formatted_str (m_sm,
    2422           51 :                                                   m_op.m_model,
    2423           51 :                                                   _("capacity: %wi bit"),
    2424           51 :                                                   _("capacity: %wi bits"),
    2425           51 :                                                   _("capacity: %wi byte"),
    2426           51 :                                                   _("capacity: %wi bytes"),
    2427           51 :                                                   _("capacity: %qs bits"),
    2428          102 :                                                   _("capacity: %qs bytes"));
    2429           68 :     if (label)
    2430           67 :       w->add_range (m_btm.get_table_x_for_range (m_op.get_valid_bits ()),
    2431           67 :                     std::move (*label),
    2432           67 :                     m_valid_style_id);
    2433              : 
    2434           68 :     access_range invalid_after_bits;
    2435           68 :     if (m_op.maybe_get_invalid_after_bits (&invalid_after_bits))
    2436              :       {
    2437           57 :         if (got_valid_bits)
    2438           57 :           maybe_add_gap (w.get (), valid_bits, invalid_after_bits);
    2439              : 
    2440           57 :         if (m_logger)
    2441            0 :           invalid_before_bits.log ("invalid_after_bits", *m_logger);
    2442              : 
    2443           57 :         bit_size_expr num_after_bits
    2444           57 :           (invalid_after_bits.get_size (m_op.get_manager ()));
    2445           57 :         std::unique_ptr<styled_string> label;
    2446           57 :         if (m_op.m_dir == access_direction::read)
    2447           11 :           label = num_after_bits.maybe_get_formatted_str
    2448           22 :             (m_sm, m_op.m_model,
    2449           11 :              _("over-read of %wi bit"),
    2450           11 :              _("over-read of %wi bits"),
    2451           11 :              _("over-read of %wi byte"),
    2452           11 :              _("over-read of %wi bytes"),
    2453           11 :              _("over-read of %qs bits"),
    2454           22 :              _("over-read of %qs bytes"));
    2455              :         else
    2456           46 :           label = num_after_bits.maybe_get_formatted_str
    2457           92 :             (m_sm, m_op.m_model,
    2458           46 :              _("overflow of %wi bit"),
    2459           46 :              _("overflow of %wi bits"),
    2460           46 :              _("overflow of %wi byte"),
    2461           46 :              _("overflow of %wi bytes"),
    2462           46 :              _("overflow of %qs bits"),
    2463           92 :              _("overflow of %qs bytes"));
    2464           57 :         if (label)
    2465           57 :           w->add_range (m_btm.get_table_x_for_range (invalid_after_bits),
    2466          114 :                         make_warning_string (std::move (*label)),
    2467           57 :                         m_invalid_style_id);
    2468           57 :       }
    2469              :     else
    2470              :       {
    2471           11 :         if (m_logger)
    2472            0 :           m_logger->log ("no invalid_after_bits");
    2473              :       }
    2474              : 
    2475           68 :     add_child (std::move (w));
    2476           68 :   }
    2477              : 
    2478              :   /* Subroutine of calc_req_size.
    2479              :      Try to allocate surplus canvas width to table columns to make the
    2480              :      per table-column canvas widths closer to being to scale.
    2481              :      See e.g.:
    2482              :        https://en.wikipedia.org/wiki/Fair_item_allocation
    2483              :        https://en.wikipedia.org/wiki/Mathematics_of_apportionment
    2484              :   */
    2485           68 :   void adjust_to_scale ()
    2486              :   {
    2487           68 :     LOG_SCOPE (m_logger);
    2488           68 :     const unsigned num_columns = m_btm.get_num_columns ();
    2489           68 :     std::vector<bit_offset_t> bit_sizes (num_columns);
    2490          624 :     for (unsigned table_x = 0; table_x < num_columns; table_x++)
    2491              :       {
    2492          556 :         access_range range_for_column (nullptr, bit_range (0, 0));
    2493          556 :         if (m_btm.maybe_get_access_range_for_table_x (table_x,
    2494              :                                                       &range_for_column))
    2495              :           {
    2496          455 :             bit_size_t size_in_bits;
    2497          455 :             if (!range_for_column.get_size_in_bits (&size_in_bits))
    2498           60 :               size_in_bits = BITS_PER_UNIT; // arbitrary non-zero value
    2499          455 :             gcc_assert (size_in_bits > 0);
    2500          455 :             bit_sizes[table_x] = size_in_bits;
    2501              :           }
    2502              :         else
    2503          101 :           bit_sizes[table_x] = 0;
    2504              :       }
    2505              : 
    2506          725 :     while (adjust_to_scale_once (bit_sizes))
    2507              :       {
    2508              :       }
    2509           68 :   }
    2510          725 :   bool adjust_to_scale_once (const std::vector<bit_offset_t> &bit_sizes)
    2511              :   {
    2512          725 :     LOG_SCOPE (m_logger);
    2513              : 
    2514          725 :     const unsigned num_columns = m_btm.get_num_columns ();
    2515              : 
    2516              :     /* Find the total canvas width currently required.
    2517              :        Require one extra canvas column for the right-hand border
    2518              :        of the table.  */
    2519          725 :     int total_width = 1;
    2520         4838 :     for (unsigned table_x = 0; table_x < num_columns; table_x++)
    2521              :       {
    2522         4113 :         int canvas_w = m_col_widths->m_requirements[table_x];
    2523         4113 :         gcc_assert (canvas_w >= 0);
    2524         4113 :         total_width += canvas_w + 1;
    2525              :       }
    2526              : 
    2527          725 :     const int max_width = param_analyzer_text_art_ideal_canvas_width;
    2528          725 :     if (total_width >= max_width)
    2529              :       {
    2530           68 :         if (m_logger)
    2531            0 :           m_logger->log ("bailing out: total_width=%i ,>= max_width (%i)\n",
    2532              :                          total_width, max_width);
    2533           68 :         return false;
    2534              :       }
    2535              : 
    2536          657 :     const int fixed_point = 1024;
    2537          657 :     std::vector<bit_offset_t> canvas_w_per_bit (num_columns);
    2538         4214 :     for (unsigned table_x = 0; table_x < num_columns; table_x++)
    2539              :       {
    2540         3557 :         bit_offset_t bit_size = bit_sizes[table_x];
    2541         3557 :         if (bit_size > 0)
    2542         5192 :           canvas_w_per_bit[table_x]
    2543         2596 :             = (m_col_widths->m_requirements[table_x] * fixed_point) / bit_size;
    2544              :         else
    2545          961 :           canvas_w_per_bit[table_x] = INT_MAX;
    2546              :       }
    2547              : 
    2548              :     /* Find the min canvas per bit, and give an extra canvas column to
    2549              :        the table column that has least.  */
    2550          657 :     size_t min_idx = std::distance (canvas_w_per_bit.begin (),
    2551              :                                     std::min_element (canvas_w_per_bit.begin (),
    2552          657 :                                                       canvas_w_per_bit.end ()));
    2553          657 :     m_col_widths->m_requirements[min_idx] += 1;
    2554          657 :     if (m_logger)
    2555            0 :       m_logger->log ("adding 1 canvas_w to column %i\n", (int)min_idx);
    2556              : 
    2557          657 :     return true; // keep going
    2558          725 :   }
    2559              : 
    2560              :   const access_operation &m_op;
    2561              :   diagnostics::paths::event_id_t m_region_creation_event_id;
    2562              :   style_manager &m_sm;
    2563              :   const theme &m_theme;
    2564              :   logger *m_logger;
    2565              :   /* In lieu of being able to throw exceptions, a flag to mark this object
    2566              :      as "invalid".  */
    2567              :   bool m_invalid;
    2568              : 
    2569              :   style::id_t m_valid_style_id;
    2570              :   style::id_t m_invalid_style_id;
    2571              : 
    2572              :   valid_region_spatial_item m_valid_region_spatial_item;
    2573              :   accessed_region_spatial_item m_accessed_region_spatial_item;
    2574              :   std::unique_ptr<spatial_item> m_written_svalue_spatial_item;
    2575              : 
    2576              :   std::unique_ptr<boundaries> m_boundaries;
    2577              : 
    2578              :   bit_to_table_map m_btm;
    2579              : 
    2580              :   bool m_calc_req_size_called;
    2581              : 
    2582              :   /* Column widths shared by all x_aligned_table_widget,
    2583              :      created once we know how many columns we need.  */
    2584              :   std::unique_ptr<table_dimension_sizes> m_col_widths;
    2585              : 
    2586              :   /* All of the child x_aligned_table_widget that share
    2587              :      column widths.  */
    2588              :   std::vector<x_aligned_table_widget *> m_aligned_table_widgets;
    2589              : 
    2590              : /* Mapping from table_x to canvas_x.  */
    2591              :   std::vector<int> m_col_start_x;
    2592              : };
    2593              : 
    2594              : x_ruler
    2595          136 : x_aligned_x_ruler_widget::make_x_ruler () const
    2596              : {
    2597          136 :   x_ruler r (x_ruler::label_dir::BELOW);
    2598          442 :   for (auto& iter : m_labels)
    2599              :     {
    2600          306 :       canvas::range_t canvas_x_range
    2601          306 :         = m_dia_impl.get_canvas_x_range (iter.m_table_x_range);
    2602              :       /* Include the end-point.  */
    2603          306 :       canvas_x_range.next++;
    2604          306 :       r.add_label (canvas_x_range, iter.m_text.copy (), iter.m_style_id,
    2605              :                    x_ruler::label_kind::TEXT_WITH_BORDER);
    2606              :     }
    2607          136 :   return r;
    2608              : }
    2609              : 
    2610              : /* class direction_widget : public leaf_widget.  */
    2611              : 
    2612              : /* Paint arrows indicating the direction of the access (read vs write),
    2613              :    but only in the X-extent corresponding to the region that's actually
    2614              :    accessed.  */
    2615              : 
    2616              : void
    2617           68 : direction_widget::paint_to_canvas (canvas &canvas)
    2618              : {
    2619           68 :   const access_range accessed_bits (m_dia_impl.get_op ().get_actual_bits ());
    2620              : 
    2621           68 :   const access_range valid_bits (m_dia_impl.get_op ().get_valid_bits ());
    2622              : 
    2623          624 :   for (unsigned table_x = 0; table_x < m_btm.get_num_columns (); table_x++)
    2624              :     {
    2625          556 :       access_range column_access_range;
    2626          556 :       if (m_btm.maybe_get_access_range_for_table_x (table_x,
    2627              :                                                     &column_access_range))
    2628              :         {
    2629              :           /* Only paint arrows in the accessed region.  */
    2630          455 :           if (!accessed_bits.contains_p (column_access_range))
    2631          175 :             continue;
    2632              : 
    2633              :           /* Are we within the valid region?  */
    2634          280 :           const bool is_valid (valid_bits.contains_p (column_access_range));
    2635          280 :           const style::id_t style_id
    2636          280 :             = m_dia_impl.get_style_id_for_validity (is_valid);
    2637          280 :           const canvas::range_t x_canvas_range
    2638          280 :             = m_dia_impl.get_canvas_x_range (table::range_t (table_x,
    2639          280 :                                                              table_x + 1));
    2640          280 :           const int canvas_x = x_canvas_range.get_midpoint ();
    2641          280 :           m_dia_impl.get_theme ().paint_y_arrow
    2642          280 :             (canvas,
    2643              :              canvas_x,
    2644              :              canvas::range_t (get_y_range ()),
    2645          280 :              (m_dia_impl.get_op ().m_dir == access_direction::read
    2646          280 :               ? theme::y_arrow_dir::UP
    2647              :               : theme::y_arrow_dir::DOWN),
    2648              :              style_id);
    2649              :         }
    2650              :     }
    2651           68 : }
    2652              : 
    2653              : /* class access_diagram : public text_art::wrapper_widget.  */
    2654              : 
    2655              : /* To hide the implementation details, this is merely a wrapper around
    2656              :    an access_diagram_impl.  */
    2657              : 
    2658           72 : access_diagram::access_diagram (const access_operation &op,
    2659              :                                 diagnostics::paths::event_id_t region_creation_event_id,
    2660              :                                 style_manager &sm,
    2661              :                                 const theme &theme,
    2662           72 :                                 logger *logger)
    2663              : : wrapper_widget
    2664           72 :     (std::make_unique <access_diagram_impl> (op,
    2665              :                                              region_creation_event_id,
    2666              :                                              sm,
    2667              :                                              theme,
    2668           72 :                                              logger))
    2669              : {
    2670           72 : }
    2671              : 
    2672              : #if CHECKING_P
    2673              : 
    2674              : namespace selftest {
    2675              : 
    2676              : /* Implementation detail of ASSERT_EQ_TYPELESS_INTEGER.  */
    2677              : 
    2678              : static void
    2679            8 : assert_eq_typeless_integer (const location &loc,
    2680              :                             const svalue *sval,
    2681              :                             int expected_int_val)
    2682              : {
    2683            8 :   ASSERT_NE_AT (loc, sval, nullptr);
    2684            8 :   ASSERT_EQ_AT (loc, sval->get_kind (), SK_CONSTANT);
    2685            8 :   ASSERT_EQ_AT (loc,
    2686              :                 wi::to_offset (sval->maybe_get_constant ()),
    2687              :                 expected_int_val);
    2688            8 :   ASSERT_EQ_AT (loc, sval->get_type (), NULL_TREE);
    2689            8 : }
    2690              : 
    2691              : /* Assert that SVAL is a constant_svalue equal to EXPECTED_INT_VAL,
    2692              :    with NULL_TREE as its type.  */
    2693              : 
    2694              : #define ASSERT_EQ_TYPELESS_INTEGER(SVAL, EXPECTED_INT_VAL) \
    2695              :   SELFTEST_BEGIN_STMT                                                  \
    2696              :   assert_eq_typeless_integer ((SELFTEST_LOCATION),                     \
    2697              :                               (SVAL),                                  \
    2698              :                               (EXPECTED_INT_VAL));                     \
    2699              :   SELFTEST_END_STMT
    2700              : 
    2701              : 
    2702              : /* Various tests of bit_size_expr::maybe_get_as_bytes.  */
    2703              : 
    2704              : static void
    2705            4 : test_bit_size_expr_to_bytes ()
    2706              : {
    2707            4 :   region_model_manager mgr;
    2708              : 
    2709              :   /* 40 bits: should be 5 bytes.  */
    2710            4 :   {
    2711            4 :     bit_size_expr num_bits (*mgr.get_or_create_int_cst (NULL_TREE, 40));
    2712            4 :     const svalue *as_bytes = num_bits.maybe_get_as_bytes (mgr);
    2713            4 :     ASSERT_EQ_TYPELESS_INTEGER (as_bytes, 5);
    2714              :   }
    2715              : 
    2716              :   /* 41 bits: should not convert to bytes.  */
    2717            4 :   {
    2718            4 :     bit_size_expr num_bits (*mgr.get_or_create_int_cst (NULL_TREE, 41));
    2719            4 :     const svalue *as_bytes = num_bits.maybe_get_as_bytes (mgr);
    2720            4 :     ASSERT_EQ (as_bytes, nullptr);
    2721              :   }
    2722              : 
    2723            4 :   tree n = build_global_decl ("n", size_type_node);
    2724              : 
    2725            4 :   const svalue *init_n
    2726            4 :     = mgr.get_or_create_initial_value (mgr.get_region_for_global (n));
    2727              : 
    2728            4 :   const svalue *n_times_8
    2729            4 :     = mgr.get_or_create_binop (NULL_TREE, MULT_EXPR,
    2730              :                                init_n,
    2731            4 :                                mgr.get_or_create_int_cst (NULL_TREE, 8));
    2732              : 
    2733              :   /* (n * 8) bits should be n bytes */
    2734            4 :   {
    2735            4 :     bit_size_expr num_bits (*n_times_8);
    2736            4 :     const svalue *as_bytes = num_bits.maybe_get_as_bytes (mgr);
    2737            4 :     ASSERT_EQ (as_bytes, mgr.get_or_create_cast (NULL_TREE, init_n));
    2738              :   }
    2739              : 
    2740              :   /* (n * 8) + 16 bits should be n + 2 bytes */
    2741            4 :   {
    2742            4 :     bit_size_expr num_bits
    2743            4 :       (*mgr.get_or_create_binop (NULL_TREE, PLUS_EXPR,
    2744              :                                  n_times_8,
    2745            4 :                                  mgr.get_or_create_int_cst (NULL_TREE, 16)));
    2746            4 :     const svalue *as_bytes = num_bits.maybe_get_as_bytes (mgr);
    2747            4 :     ASSERT_EQ (as_bytes->get_kind (), SK_BINOP);
    2748            4 :     const binop_svalue *binop = as_bytes->dyn_cast_binop_svalue ();
    2749            4 :     ASSERT_EQ (binop->get_op (), PLUS_EXPR);
    2750            4 :     ASSERT_EQ (binop->get_arg0 (), mgr.get_or_create_cast (NULL_TREE, init_n));
    2751            4 :     ASSERT_EQ_TYPELESS_INTEGER (binop->get_arg1 (), 2);
    2752              :   }
    2753            4 : }
    2754              : 
    2755              : /* Run all of the selftests within this file.  */
    2756              : 
    2757              : void
    2758            4 : analyzer_access_diagram_cc_tests ()
    2759              : {
    2760            4 :   test_bit_size_expr_to_bytes ();
    2761            4 : }
    2762              : 
    2763              : } // namespace selftest
    2764              : 
    2765              : #endif /* CHECKING_P */
    2766              : 
    2767              : } // namespace ana
    2768              : 
    2769              : #endif /* #if ENABLE_ANALYZER */
        

Generated by: LCOV version 2.4-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.