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: 0.0 % 249 0
Test Date: 2026-02-28 14:20:25 Functions: 0.0 % 26 0
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            0 : get_tyty_name (TyTy::BaseType *tyty)
      30              : {
      31            0 :   if (tyty)
      32            0 :     return tyty->get_name ();
      33            0 :   return "unknown";
      34              : }
      35              : 
      36              : template <typename T, typename FN>
      37              : void
      38            0 : print_comma_separated (std::ostream &stream, const std::vector<T> &collection,
      39              :                        FN printer)
      40              : {
      41            0 :   if (collection.empty ())
      42              :     return;
      43            0 :   printer (collection[0]);
      44            0 :   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            0 : Dump::go (bool enable_simplify_cfg)
     117              : {
     118              :   // To avoid mutation of the BIR, we use indirection through bb_fold_map.
     119            0 :   for (BasicBlockId i = ENTRY_BASIC_BLOCK; i.value < bb_fold_map.size ();
     120              :        ++i.value)
     121              :     {
     122            0 :       bb_fold_map[i] = i;
     123              :     }
     124            0 :   for (PlaceId i = INVALID_PLACE; i.value < place_map.size (); ++i.value)
     125              :     {
     126            0 :       place_map[i] = i;
     127              :     }
     128              : 
     129            0 :   if (enable_simplify_cfg)
     130            0 :     simplify_cfg (func, bb_fold_map);
     131              : 
     132              :   // renumber_places (func, place_map);
     133              : 
     134            0 :   stream << "fn " << name << "(";
     135            0 :   print_comma_separated (stream, func.arguments, [this] (PlaceId place_id) {
     136            0 :     stream << "_" << place_map[place_id].value << ": "
     137            0 :            << get_tyty_name (func.place_db[place_id].tyty);
     138            0 :   });
     139            0 :   stream << ") -> " << get_tyty_name (func.place_db[RETURN_VALUE_PLACE].tyty);
     140            0 :   stream << " {\n";
     141              : 
     142              :   // Print locals declaration.
     143            0 :   visit_scope (ROOT_SCOPE);
     144              : 
     145              :   // Print BBs.
     146            0 :   for (statement_bb = ENTRY_BASIC_BLOCK;
     147            0 :        statement_bb.value < func.basic_blocks.size (); ++statement_bb.value)
     148              :     {
     149            0 :       if (bb_fold_map[statement_bb] != statement_bb)
     150            0 :         continue; // This BB was folded.
     151              : 
     152            0 :       if (func.basic_blocks[statement_bb].statements.empty ()
     153            0 :           && func.basic_blocks[statement_bb].successors.empty ())
     154            0 :         continue;
     155              : 
     156            0 :       bb_terminated = false;
     157              : 
     158            0 :       BasicBlock &bb = func.basic_blocks[statement_bb];
     159            0 :       stream << "\n";
     160            0 :       stream << indentation << "bb" << bb_fold_map[statement_bb].value
     161            0 :              << ": {\n";
     162            0 :       size_t i = 0;
     163            0 :       for (auto &stmt : bb.statements)
     164              :         {
     165            0 :           stream << indentation << i++ << indentation;
     166            0 :           visit (stmt);
     167            0 :           stream << ";\n";
     168              :         }
     169            0 :       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            0 :       stream << indentation << "}\n";
     175              :     }
     176              : 
     177            0 :   stream << "}\n";
     178            0 : }
     179              : void
     180            0 : Dump::visit (const Statement &stmt)
     181              : {
     182            0 :   statement_place = stmt.get_place ();
     183            0 :   switch (stmt.get_kind ())
     184              :     {
     185            0 :     case Statement::Kind::ASSIGNMENT:
     186            0 :       {
     187            0 :         visit_place (stmt.get_place ());
     188            0 :         stream << " = ";
     189            0 :         stmt.get_expr ().accept_vis (*this);
     190            0 :         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            0 :     case Statement::Kind::RETURN:
     204            0 :       stream << "return";
     205            0 :       bb_terminated = true;
     206            0 :       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            0 :     case Statement::Kind::STORAGE_DEAD:
     214            0 :       stream << "StorageDead(";
     215            0 :       visit_place (stmt.get_place ());
     216            0 :       stream << ")";
     217            0 :       break;
     218            0 :     case Statement::Kind::STORAGE_LIVE:
     219            0 :       stream << "StorageLive(";
     220            0 :       visit_place (stmt.get_place ());
     221            0 :       stream << ")";
     222            0 :       break;
     223            0 :     case Statement::Kind::USER_TYPE_ASCRIPTION:
     224            0 :       visit_place (stmt.get_place ());
     225            0 :       stream << " = ";
     226            0 :       stream << "UserTypeAscription(";
     227            0 :       stream << get_tyty_name (func.place_db[stmt.get_place ()].tyty);
     228            0 :       stream << ")";
     229            0 :       break;
     230            0 :     case Statement::Kind::FAKE_READ:
     231            0 :       stream << "FakeRead(";
     232            0 :       visit_place (stmt.get_place ());
     233            0 :       stream << ")";
     234            0 :       break;
     235            0 :     default:
     236            0 :       rust_internal_error_at (UNKNOWN_LOCATION, "Unknown statement kind.");
     237              :     }
     238            0 :   statement_place = INVALID_PLACE;
     239            0 : }
     240              : 
     241              : void
     242            0 : Dump::visit_place (PlaceId place_id)
     243              : {
     244            0 :   const Place &place = func.place_db[place_id];
     245            0 :   switch (place.kind)
     246              :     {
     247            0 :     case Place::TEMPORARY:
     248            0 :     case Place::VARIABLE:
     249            0 :       stream << "_" << place_map[place_id].value;
     250            0 :       break;
     251            0 :     case Place::DEREF:
     252            0 :       stream << "(";
     253            0 :       stream << "*";
     254            0 :       visit_place (place.path.parent);
     255            0 :       stream << ")";
     256            0 :       break;
     257            0 :     case Place::FIELD:
     258            0 :       stream << "(";
     259            0 :       visit_place (place.path.parent);
     260            0 :       stream << ".";
     261            0 :       stream << place.variable_or_field_index;
     262            0 :       stream << ": " << get_tyty_name (place.tyty) << ")";
     263            0 :       break;
     264            0 :     case Place::INDEX:
     265            0 :       stream << "(";
     266            0 :       visit_place (place.path.parent);
     267            0 :       stream << "[]";
     268            0 :       stream << ": " << get_tyty_name (place.tyty) << ")";
     269            0 :       break;
     270            0 :     case Place::CONSTANT:
     271            0 :       stream << "const " << get_tyty_name (place.tyty);
     272            0 :       break;
     273            0 :     case Place::INVALID:
     274            0 :       if (place_id == INVALID_PLACE)
     275            0 :         stream << "_INVALID";
     276              :     }
     277            0 : }
     278              : 
     279              : void
     280            0 : Dump::visit_move_place (PlaceId place_id)
     281              : {
     282            0 :   const Place &place = func.place_db[place_id];
     283            0 :   if (place.should_be_moved ())
     284            0 :     stream << "move ";
     285            0 :   visit_place (place_id);
     286            0 : }
     287              : 
     288              : void
     289            0 : Dump::visit (const BorrowExpr &expr)
     290              : {
     291            0 :   stream << "&"
     292            0 :          << "'?" << expr.get_origin () << " ";
     293            0 :   if (func.place_db.get_loan (expr.get_loan_id ()).mutability
     294              :       == Mutability::Mut)
     295            0 :     stream << "mut ";
     296            0 :   visit_place (expr.get_place ());
     297            0 : }
     298              : 
     299              : void
     300            0 : Dump::visit_lifetime (PlaceId place_id)
     301            0 : {}
     302              : 
     303              : void
     304            0 : Dump::visit (const InitializerExpr &expr)
     305              : {
     306            0 :   stream << "{";
     307            0 :   print_comma_separated (stream, expr.get_values (), [this] (PlaceId place_id) {
     308            0 :     visit_move_place (place_id);
     309              :   });
     310            0 :   stream << "}";
     311            0 : }
     312              : 
     313              : void
     314            0 : Dump::visit (const CallExpr &expr)
     315              : {
     316            0 :   stream << "Call(";
     317            0 :   auto maybe_fn_type
     318            0 :     = func.place_db[expr.get_callable ()].tyty->try_as<TyTy::FnType> ();
     319            0 :   if (maybe_fn_type)
     320            0 :     stream << maybe_fn_type->get_identifier ();
     321              :   else
     322            0 :     visit_move_place (expr.get_callable ());
     323              : 
     324            0 :   stream << ")(";
     325            0 :   print_comma_separated (stream, expr.get_arguments (),
     326            0 :                          [this] (PlaceId place_id) {
     327            0 :                            visit_move_place (place_id);
     328              :                          });
     329            0 :   stream << ") -> [";
     330            0 :   print_comma_separated (stream, func.basic_blocks[statement_bb].successors,
     331            0 :                          [this] (BasicBlockId succ) {
     332            0 :                            stream << "bb" << bb_fold_map[succ].value;
     333            0 :                          });
     334            0 :   stream << "]";
     335            0 :   bb_terminated = true;
     336            0 : }
     337              : 
     338              : void
     339            0 : Dump::visit (const Operator<1> &expr)
     340              : {
     341            0 :   stream << "Operator(";
     342            0 :   visit_move_place (expr.get_operand<0> ());
     343            0 :   stream << ")";
     344            0 : }
     345              : 
     346              : void
     347            0 : Dump::visit (const Operator<2> &expr)
     348              : {
     349            0 :   stream << "Operator(";
     350            0 :   visit_move_place (expr.get_operand<0> ());
     351            0 :   stream << ", ";
     352            0 :   visit_move_place (expr.get_operand<1> ());
     353            0 :   stream << ")";
     354            0 : }
     355              : 
     356              : void
     357            0 : Dump::visit (const Assignment &expr)
     358              : {
     359            0 :   if (func.place_db[expr.get_rhs ()].is_rvalue ())
     360            0 :     visit_move_place (expr.get_rhs ());
     361              :   else
     362            0 :     visit_place (expr.get_rhs ());
     363            0 : }
     364              : 
     365              : std::ostream &
     366            0 : Dump::indent (size_t depth)
     367              : {
     368            0 :   for (size_t i = 0; i < depth; ++i)
     369            0 :     stream << indentation;
     370            0 :   return stream;
     371              : }
     372              : 
     373              : void
     374            0 : Dump::visit_scope (ScopeId id, size_t depth)
     375              : {
     376            0 :   auto scope = func.place_db.get_scope (id);
     377            0 :   if (scope.locals.empty () && scope.children.empty ())
     378            0 :     return;
     379              : 
     380            0 :   if (id.value > 1)
     381            0 :     indent (depth) << "scope " << id.value - 1 << " {\n";
     382              : 
     383            0 :   for (auto &local : scope.locals)
     384              :     {
     385            0 :       indent (depth + 1) << "let _";
     386            0 :       stream << place_map[local].value << ": "
     387            0 :              << get_tyty_name (func.place_db[local].tyty);
     388            0 :       stream << ";\t";
     389              : 
     390            0 :       stream << "[";
     391            0 :       print_comma_separated (stream,
     392            0 :                              func.place_db[local].regions.get_regions (),
     393            0 :                              [this] (FreeRegion region_id) {
     394            0 :                                stream << "'?" << region_id.value;
     395            0 :                              });
     396            0 :       stream << "]\n";
     397              :     }
     398            0 :   for (auto &child : scope.children)
     399            0 :     visit_scope (child, (id.value >= 1) ? depth + 1 : depth);
     400              : 
     401            0 :   if (id.value > 1)
     402            0 :     indent (depth) << "}\n";
     403            0 : }
     404              : 
     405              : } // namespace BIR
     406              : } // 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.