Branch data Line data Source code
1 : : // Copyright (C) 2020-2025 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
|