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
|