LCOV - code coverage report
Current view: top level - gcc/rust/checks/errors/borrowck - rust-bir-dump.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 48.5 % 266 129
Test Date: 2026-09-12 16:25:28 Functions: 50.0 % 26 13
Legend: Lines:     hit not hit

            Line data    Source code
       1              : // Copyright (C) 2020-2026 Free Software Foundation, Inc.
       2              : 
       3              : // This file is part of GCC.
       4              : 
       5              : // GCC is free software; you can redistribute it and/or modify it under
       6              : // the terms of the GNU General Public License as published by the Free
       7              : // Software Foundation; either version 3, or (at your option) any later
       8              : // version.
       9              : 
      10              : // GCC is distributed in the hope that it will be useful, but WITHOUT ANY
      11              : // WARRANTY; without even the implied warranty of MERCHANTABILITY or
      12              : // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      13              : // for more details.
      14              : 
      15              : // You should have received a copy of the GNU General Public License
      16              : // along with GCC; see the file COPYING3.  If not see
      17              : // <http://www.gnu.org/licenses/>
      18              : 
      19              : #include "rust-system.h"
      20              : #include "rust-bir-dump.h"
      21              : #include "rust-diagnostics.h"
      22              : 
      23              : namespace Rust {
      24              : namespace BIR {
      25              : 
      26              : constexpr auto indentation = "    ";
      27              : 
      28              : std::string
      29           24 : get_tyty_name (TyTy::BaseType *tyty)
      30              : {
      31           24 :   if (tyty)
      32           24 :     return tyty->get_name ();
      33            0 :   return "unknown";
      34              : }
      35              : 
      36              : template <typename T, typename FN>
      37              : void
      38           16 : print_comma_separated (std::ostream &stream, const std::vector<T> &collection,
      39              :                        FN printer)
      40              : {
      41           16 :   if (collection.empty ())
      42              :     return;
      43            5 :   printer (collection[0]);
      44            3 :   for (auto it = collection.begin () + 1; it != collection.end (); ++it)
      45              :     {
      46            0 :       stream << ", ";
      47            0 :       printer (*it);
      48              :     }
      49              : }
      50              : 
      51              : void
      52            0 : renumber_places (const Function &func, std::vector<PlaceId> &place_map)
      53              : {
      54              :   // Renumbering places to avoid gaps in the place id space.
      55              :   // This is needed to match MIR's shape.
      56            0 :   PlaceId next_out_id = INVALID_PLACE;
      57              : 
      58            0 :   for (PlaceId in_id = FIRST_VARIABLE_PLACE;
      59            0 :        in_id.value < func.place_db.size (); ++in_id.value)
      60              :     {
      61            0 :       const Place &place = func.place_db[in_id];
      62            0 :       if (place.kind == Place::VARIABLE || place.kind == Place::TEMPORARY)
      63              :         {
      64            0 :           place_map[in_id.value] = next_out_id;
      65            0 :           ++next_out_id.value;
      66              :         }
      67              : 
      68              :       else
      69            0 :         place_map[in_id.value] = INVALID_PLACE;
      70              :     }
      71            0 : }
      72              : 
      73              : void
      74            0 : simplify_cfg (Function &func, IndexVec<BasicBlockId, BasicBlockId> &bb_fold_map)
      75              : {
      76              :   // The BIR builder can generate many useless basic blocks, which contain only
      77              :   // a goto.
      78              :   // For actual borrow-checking, the folding has little value.
      79              : 
      80            0 :   bool stabilized = false;
      81            0 :   while (!stabilized)
      82              :     {
      83              :       stabilized = true;
      84              :       // BB0 cannot be folded as it is an entry block.
      85            0 :       for (BasicBlockId i = {1}; i.value < func.basic_blocks.size (); ++i.value)
      86              :         {
      87            0 :           const BasicBlock &bb = func.basic_blocks[bb_fold_map[i]];
      88            0 :           if (bb.statements.empty () && bb.is_goto_terminated ())
      89              :             {
      90            0 :               auto dst = bb.successors.at (0);
      91            0 :               if (bb_fold_map[dst] != dst)
      92              :                 {
      93            0 :                   rust_error_at (
      94              :                     UNKNOWN_LOCATION,
      95              :                     "BIR DUMP: Cannot fold CFG, because it contains an "
      96              :                     "infinite loop with no executable statements.");
      97            0 :                   rust_inform (UNKNOWN_LOCATION,
      98              :                                "Continuing with an unfolded CFG.");
      99              :                   // Reverting the fold map to the original state.
     100            0 :                   for (BasicBlockId i = ENTRY_BASIC_BLOCK;
     101            0 :                        i.value < bb_fold_map.size (); ++i.value)
     102              :                     {
     103            0 :                       bb_fold_map[i] = i;
     104              :                     }
     105              :                   stabilized = true;
     106            0 :                   break;
     107              :                 }
     108            0 :               bb_fold_map[i] = dst;
     109            0 :               stabilized = false;
     110              :             }
     111              :         }
     112              :     }
     113            0 : }
     114              : 
     115              : void
     116            4 : Dump::go (bool enable_simplify_cfg)
     117              : {
     118              :   // To avoid mutation of the BIR, we use indirection through bb_fold_map.
     119            8 :   for (BasicBlockId i = ENTRY_BASIC_BLOCK; i.value < bb_fold_map.size ();
     120              :        ++i.value)
     121              :     {
     122            4 :       bb_fold_map[i] = i;
     123              :     }
     124           25 :   for (PlaceId i = INVALID_PLACE; i.value < place_map.size (); ++i.value)
     125              :     {
     126           21 :       place_map[i] = i;
     127              :     }
     128              : 
     129            4 :   if (enable_simplify_cfg)
     130            0 :     simplify_cfg (func, bb_fold_map);
     131              : 
     132              :   // renumber_places (func, place_map);
     133              : 
     134            8 :   stream << "fn " << name << "(";
     135            4 :   print_comma_separated (stream, func.arguments, [this] (PlaceId place_id) {
     136            1 :     stream << "_" << place_map[place_id].value << ": "
     137            2 :            << get_tyty_name (func.place_db[place_id].tyty);
     138            1 :   });
     139            8 :   stream << ") -> " << get_tyty_name (func.place_db[RETURN_VALUE_PLACE].tyty);
     140            4 :   stream << " {\n";
     141              : 
     142              :   // Print locals declaration.
     143            4 :   visit_scope (ROOT_SCOPE);
     144              : 
     145              :   // Print BBs.
     146            4 :   for (statement_bb = ENTRY_BASIC_BLOCK;
     147            8 :        statement_bb.value < func.basic_blocks.size (); ++statement_bb.value)
     148              :     {
     149            4 :       if (bb_fold_map[statement_bb] != statement_bb)
     150            0 :         continue; // This BB was folded.
     151              : 
     152            4 :       if (func.basic_blocks[statement_bb].statements.empty ()
     153            4 :           && func.basic_blocks[statement_bb].successors.empty ())
     154            0 :         continue;
     155              : 
     156            4 :       bb_terminated = false;
     157              : 
     158            4 :       BasicBlock &bb = func.basic_blocks[statement_bb];
     159            4 :       stream << "\n";
     160            4 :       stream << indentation << "bb" << bb_fold_map[statement_bb].value
     161            4 :              << ": {\n";
     162            4 :       size_t i = 0;
     163           36 :       for (auto &stmt : bb.statements)
     164              :         {
     165           32 :           stream << indentation << i++ << indentation;
     166           32 :           visit (stmt);
     167           32 :           stream << ";\n";
     168              :         }
     169            4 :       if (!bb_terminated)
     170            0 :         stream << indentation << indentation << "goto -> bb"
     171            0 :                << bb_fold_map[bb.successors.at (0)].value << ";\t\t" << i++
     172            0 :                << "\n";
     173              : 
     174            4 :       stream << indentation << "}\n";
     175              :     }
     176              : 
     177            4 :   stream << "}\n";
     178            4 : }
     179              : void
     180           32 : Dump::visit (const Statement &stmt)
     181              : {
     182           32 :   statement_place = stmt.get_place ();
     183           32 :   switch (stmt.get_kind ())
     184              :     {
     185            9 :     case Statement::Kind::ASSIGNMENT:
     186            9 :       {
     187            9 :         visit_place (stmt.get_place ());
     188            9 :         stream << " = ";
     189            9 :         stmt.get_expr ().accept_vis (*this);
     190            9 :         break;
     191              :       }
     192            0 :     case Statement::Kind::SWITCH:
     193            0 :       stream << "switchInt(";
     194            0 :       visit_move_place (stmt.get_place ());
     195            0 :       stream << ") -> [";
     196            0 :       print_comma_separated (stream, func.basic_blocks[statement_bb].successors,
     197            0 :                              [this] (BasicBlockId succ) {
     198            0 :                                stream << "bb" << bb_fold_map[succ].value;
     199            0 :                              });
     200            0 :       stream << "]";
     201            0 :       bb_terminated = true;
     202            0 :       break;
     203            4 :     case Statement::Kind::RETURN:
     204            4 :       stream << "return";
     205            4 :       bb_terminated = true;
     206            4 :       break;
     207            0 :     case Statement::Kind::GOTO:
     208            0 :       stream
     209            0 :         << "goto -> bb"
     210            0 :         << bb_fold_map[func.basic_blocks[statement_bb].successors.at (0)].value;
     211            0 :       bb_terminated = true;
     212            0 :       break;
     213              : 
     214            6 :     case Statement::Kind::DROP:
     215            6 :       stream << "Drop(";
     216            6 :       visit_place (stmt.get_place ());
     217            6 :       stream << "): ";
     218              : 
     219            6 :       switch (stmt.get_drop_style ())
     220              :         {
     221            0 :         case Statement::DropStyle::UNCLASSIFIED:
     222            0 :           stream << "Unclassified";
     223            0 :           break;
     224              : 
     225            5 :         case Statement::DropStyle::STATIC:
     226            5 :           stream << "Static";
     227            5 :           break;
     228              : 
     229            1 :         case Statement::DropStyle::DEAD:
     230            1 :           stream << "Dead";
     231            1 :           break;
     232              : 
     233            0 :         case Statement::DropStyle::CONDITIONAL:
     234            0 :           stream << "Conditional";
     235            0 :           break;
     236              :         }
     237              :       break;
     238              : 
     239            5 :     case Statement::Kind::STORAGE_DEAD:
     240            5 :       stream << "StorageDead(";
     241            5 :       visit_place (stmt.get_place ());
     242            5 :       stream << ")";
     243            5 :       break;
     244            5 :     case Statement::Kind::STORAGE_LIVE:
     245            5 :       stream << "StorageLive(";
     246            5 :       visit_place (stmt.get_place ());
     247            5 :       stream << ")";
     248            5 :       break;
     249            0 :     case Statement::Kind::USER_TYPE_ASCRIPTION:
     250            0 :       visit_place (stmt.get_place ());
     251            0 :       stream << " = ";
     252            0 :       stream << "UserTypeAscription(";
     253            0 :       stream << get_tyty_name (func.place_db[stmt.get_place ()].tyty);
     254            0 :       stream << ")";
     255            0 :       break;
     256            3 :     case Statement::Kind::FAKE_READ:
     257            3 :       stream << "FakeRead(";
     258            3 :       visit_place (stmt.get_place ());
     259            3 :       stream << ")";
     260            3 :       break;
     261            0 :     default:
     262            0 :       rust_internal_error_at (UNKNOWN_LOCATION, "Unknown statement kind.");
     263              :     }
     264           32 :   statement_place = INVALID_PLACE;
     265           32 : }
     266              : 
     267              : void
     268           37 : Dump::visit_place (PlaceId place_id)
     269              : {
     270           37 :   const Place &place = func.place_db[place_id];
     271           37 :   switch (place.kind)
     272              :     {
     273           28 :     case Place::TEMPORARY:
     274           28 :     case Place::VARIABLE:
     275           28 :       stream << "_" << place_map[place_id].value;
     276           28 :       break;
     277            0 :     case Place::DEREF:
     278            0 :       stream << "(";
     279            0 :       stream << "*";
     280            0 :       visit_place (place.path.parent);
     281            0 :       stream << ")";
     282            0 :       break;
     283            0 :     case Place::FIELD:
     284            0 :       stream << "(";
     285            0 :       visit_place (place.path.parent);
     286            0 :       stream << ".";
     287            0 :       stream << place.variable_or_field_index;
     288            0 :       stream << ": " << get_tyty_name (place.tyty) << ")";
     289            0 :       break;
     290            0 :     case Place::INDEX:
     291            0 :       stream << "(";
     292            0 :       visit_place (place.path.parent);
     293            0 :       stream << "[]";
     294            0 :       stream << ": " << get_tyty_name (place.tyty) << ")";
     295            0 :       break;
     296            9 :     case Place::CONSTANT:
     297           18 :       stream << "const " << get_tyty_name (place.tyty);
     298            9 :       break;
     299            0 :     case Place::INVALID:
     300            0 :       if (place_id == INVALID_PLACE)
     301            0 :         stream << "_INVALID";
     302              :     }
     303           37 : }
     304              : 
     305              : void
     306            2 : Dump::visit_move_place (PlaceId place_id)
     307              : {
     308            2 :   const Place &place = func.place_db[place_id];
     309            2 :   if (place.should_be_moved ())
     310            0 :     stream << "move ";
     311            2 :   visit_place (place_id);
     312            2 : }
     313              : 
     314              : void
     315            0 : Dump::visit (const BorrowExpr &expr)
     316              : {
     317            0 :   stream << "&"
     318            0 :          << "'?" << expr.get_origin () << " ";
     319            0 :   if (func.place_db.get_loan (expr.get_loan_id ()).mutability
     320              :       == Mutability::Mut)
     321            0 :     stream << "mut ";
     322            0 :   visit_place (expr.get_place ());
     323            0 : }
     324              : 
     325              : void
     326            0 : Dump::visit_lifetime (PlaceId place_id)
     327            0 : {}
     328              : 
     329              : void
     330            2 : Dump::visit (const InitializerExpr &expr)
     331              : {
     332            2 :   stream << "{";
     333            2 :   print_comma_separated (stream, expr.get_values (), [this] (PlaceId place_id) {
     334            2 :     visit_move_place (place_id);
     335              :   });
     336            2 :   stream << "}";
     337            2 : }
     338              : 
     339              : void
     340            0 : Dump::visit (const CallExpr &expr)
     341              : {
     342            0 :   stream << "Call(";
     343            0 :   auto maybe_fn_type
     344            0 :     = func.place_db[expr.get_callable ()].tyty->try_as<TyTy::FnType> ();
     345            0 :   if (maybe_fn_type)
     346            0 :     stream << maybe_fn_type->get_identifier ();
     347              :   else
     348            0 :     visit_move_place (expr.get_callable ());
     349              : 
     350            0 :   stream << ")(";
     351            0 :   print_comma_separated (stream, expr.get_arguments (),
     352            0 :                          [this] (PlaceId place_id) {
     353            0 :                            visit_move_place (place_id);
     354              :                          });
     355            0 :   stream << ") -> [";
     356            0 :   print_comma_separated (stream, func.basic_blocks[statement_bb].successors,
     357            0 :                          [this] (BasicBlockId succ) {
     358            0 :                            stream << "bb" << bb_fold_map[succ].value;
     359            0 :                          });
     360            0 :   stream << "]";
     361            0 :   bb_terminated = true;
     362            0 : }
     363              : 
     364              : void
     365            0 : Dump::visit (const Operator<1> &expr)
     366              : {
     367            0 :   stream << "Operator(";
     368            0 :   visit_move_place (expr.get_operand<0> ());
     369            0 :   stream << ")";
     370            0 : }
     371              : 
     372              : void
     373            0 : Dump::visit (const Operator<2> &expr)
     374              : {
     375            0 :   stream << "Operator(";
     376            0 :   visit_move_place (expr.get_operand<0> ());
     377            0 :   stream << ", ";
     378            0 :   visit_move_place (expr.get_operand<1> ());
     379            0 :   stream << ")";
     380            0 : }
     381              : 
     382              : void
     383            7 : Dump::visit (const Assignment &expr)
     384              : {
     385            7 :   if (func.place_db[expr.get_rhs ()].is_rvalue ())
     386            0 :     visit_move_place (expr.get_rhs ());
     387              :   else
     388            7 :     visit_place (expr.get_rhs ());
     389            7 : }
     390              : 
     391              : std::ostream &
     392           10 : Dump::indent (size_t depth)
     393              : {
     394           30 :   for (size_t i = 0; i < depth; ++i)
     395           20 :     stream << indentation;
     396           10 :   return stream;
     397              : }
     398              : 
     399              : void
     400            8 : Dump::visit_scope (ScopeId id, size_t depth)
     401              : {
     402            8 :   auto scope = func.place_db.get_scope (id);
     403            8 :   if (scope.locals.empty () && scope.children.empty ())
     404            1 :     return;
     405              : 
     406            7 :   if (id.value > 1)
     407            0 :     indent (depth) << "scope " << id.value - 1 << " {\n";
     408              : 
     409           17 :   for (auto &local : scope.locals)
     410              :     {
     411           10 :       indent (depth + 1) << "let _";
     412           10 :       stream << place_map[local].value << ": "
     413           20 :              << get_tyty_name (func.place_db[local].tyty);
     414           10 :       stream << ";\t";
     415              : 
     416           10 :       stream << "[";
     417           10 :       print_comma_separated (stream,
     418           10 :                              func.place_db[local].regions.get_regions (),
     419            0 :                              [this] (FreeRegion region_id) {
     420            0 :                                stream << "'?" << region_id.value;
     421            0 :                              });
     422           10 :       stream << "]\n";
     423              :     }
     424           11 :   for (auto &child : scope.children)
     425            4 :     visit_scope (child, (id.value >= 1) ? depth + 1 : depth);
     426              : 
     427            7 :   if (id.value > 1)
     428            0 :     indent (depth) << "}\n";
     429            8 : }
     430              : 
     431              : } // namespace BIR
     432              : } // namespace Rust
        

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.