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
|