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 : visit_place (stmt.get_place ());
187 : 0 : stream << " = ";
188 : 0 : stmt.get_expr ().accept_vis (*this);
189 : 0 : break;
190 : : }
191 : 0 : case Statement::Kind::SWITCH:
192 : 0 : stream << "switchInt(";
193 : 0 : visit_move_place (stmt.get_place ());
194 : 0 : stream << ") -> [";
195 : 0 : print_comma_separated (stream, func.basic_blocks[statement_bb].successors,
196 : 0 : [this] (BasicBlockId succ) {
197 : 0 : stream << "bb" << bb_fold_map[succ].value;
198 : 0 : });
199 : 0 : stream << "]";
200 : 0 : bb_terminated = true;
201 : 0 : break;
202 : 0 : case Statement::Kind::RETURN:
203 : 0 : stream << "return";
204 : 0 : bb_terminated = true;
205 : 0 : break;
206 : 0 : case Statement::Kind::GOTO:
207 : 0 : stream
208 : 0 : << "goto -> bb"
209 : 0 : << bb_fold_map[func.basic_blocks[statement_bb].successors.at (0)].value;
210 : 0 : bb_terminated = true;
211 : 0 : break;
212 : 0 : case Statement::Kind::STORAGE_DEAD:
213 : 0 : stream << "StorageDead(";
214 : 0 : visit_place (stmt.get_place ());
215 : 0 : stream << ")";
216 : 0 : break;
217 : 0 : case Statement::Kind::STORAGE_LIVE:
218 : 0 : stream << "StorageLive(";
219 : 0 : visit_place (stmt.get_place ());
220 : 0 : stream << ")";
221 : 0 : break;
222 : 0 : case Statement::Kind::USER_TYPE_ASCRIPTION:
223 : 0 : visit_place (stmt.get_place ());
224 : 0 : stream << " = ";
225 : 0 : stream << "UserTypeAscription(";
226 : 0 : stream << get_tyty_name (func.place_db[stmt.get_place ()].tyty);
227 : 0 : stream << ")";
228 : 0 : break;
229 : 0 : case Statement::Kind::FAKE_READ:
230 : 0 : stream << "FakeRead(";
231 : 0 : visit_place (stmt.get_place ());
232 : 0 : stream << ")";
233 : 0 : break;
234 : 0 : default:
235 : 0 : rust_internal_error_at (UNKNOWN_LOCATION, "Unknown statement kind.");
236 : : }
237 : 0 : statement_place = INVALID_PLACE;
238 : 0 : }
239 : :
240 : : void
241 : 0 : Dump::visit_place (PlaceId place_id)
242 : : {
243 : 0 : const Place &place = func.place_db[place_id];
244 : 0 : switch (place.kind)
245 : : {
246 : 0 : case Place::TEMPORARY:
247 : 0 : case Place::VARIABLE:
248 : 0 : stream << "_" << place_map[place_id].value;
249 : 0 : break;
250 : 0 : case Place::DEREF:
251 : 0 : stream << "(";
252 : 0 : stream << "*";
253 : 0 : visit_place (place.path.parent);
254 : 0 : stream << ")";
255 : 0 : break;
256 : 0 : case Place::FIELD:
257 : 0 : stream << "(";
258 : 0 : visit_place (place.path.parent);
259 : 0 : stream << ".";
260 : 0 : stream << place.variable_or_field_index;
261 : 0 : stream << ": " << get_tyty_name (place.tyty) << ")";
262 : 0 : break;
263 : 0 : case Place::INDEX:
264 : 0 : stream << "(";
265 : 0 : visit_place (place.path.parent);
266 : 0 : stream << "[]";
267 : 0 : stream << ": " << get_tyty_name (place.tyty) << ")";
268 : 0 : break;
269 : 0 : case Place::CONSTANT:
270 : 0 : stream << "const " << get_tyty_name (place.tyty);
271 : 0 : break;
272 : 0 : case Place::INVALID:
273 : 0 : if (place_id == INVALID_PLACE)
274 : 0 : stream << "_INVALID";
275 : : }
276 : 0 : }
277 : :
278 : : void
279 : 0 : Dump::visit_move_place (PlaceId place_id)
280 : : {
281 : 0 : const Place &place = func.place_db[place_id];
282 : 0 : if (place.should_be_moved ())
283 : 0 : stream << "move ";
284 : 0 : visit_place (place_id);
285 : 0 : }
286 : :
287 : : void
288 : 0 : Dump::visit (const BorrowExpr &expr)
289 : : {
290 : 0 : stream << "&"
291 : 0 : << "'?" << expr.get_origin () << " ";
292 : 0 : if (func.place_db.get_loan (expr.get_loan_id ()).mutability
293 : : == Mutability::Mut)
294 : 0 : stream << "mut ";
295 : 0 : visit_place (expr.get_place ());
296 : 0 : }
297 : :
298 : : void
299 : 0 : Dump::visit_lifetime (PlaceId place_id)
300 : 0 : {}
301 : :
302 : : void
303 : 0 : Dump::visit (const InitializerExpr &expr)
304 : : {
305 : 0 : stream << "{";
306 : 0 : print_comma_separated (stream, expr.get_values (), [this] (PlaceId place_id) {
307 : 0 : visit_move_place (place_id);
308 : : });
309 : 0 : stream << "}";
310 : 0 : }
311 : :
312 : : void
313 : 0 : Dump::visit (const CallExpr &expr)
314 : : {
315 : 0 : stream << "Call(";
316 : 0 : auto maybe_fn_type
317 : 0 : = func.place_db[expr.get_callable ()].tyty->try_as<TyTy::FnType> ();
318 : 0 : if (maybe_fn_type)
319 : 0 : stream << maybe_fn_type->get_identifier ();
320 : : else
321 : 0 : visit_move_place (expr.get_callable ());
322 : :
323 : 0 : stream << ")(";
324 : 0 : print_comma_separated (stream, expr.get_arguments (),
325 : 0 : [this] (PlaceId place_id) {
326 : 0 : visit_move_place (place_id);
327 : : });
328 : 0 : stream << ") -> [";
329 : 0 : print_comma_separated (stream, func.basic_blocks[statement_bb].successors,
330 : 0 : [this] (BasicBlockId succ) {
331 : 0 : stream << "bb" << bb_fold_map[succ].value;
332 : 0 : });
333 : 0 : stream << "]";
334 : 0 : bb_terminated = true;
335 : 0 : }
336 : :
337 : : void
338 : 0 : Dump::visit (const Operator<1> &expr)
339 : : {
340 : 0 : stream << "Operator(";
341 : 0 : visit_move_place (expr.get_operand<0> ());
342 : 0 : stream << ")";
343 : 0 : }
344 : :
345 : : void
346 : 0 : Dump::visit (const Operator<2> &expr)
347 : : {
348 : 0 : stream << "Operator(";
349 : 0 : visit_move_place (expr.get_operand<0> ());
350 : 0 : stream << ", ";
351 : 0 : visit_move_place (expr.get_operand<1> ());
352 : 0 : stream << ")";
353 : 0 : }
354 : :
355 : : void
356 : 0 : Dump::visit (const Assignment &expr)
357 : : {
358 : 0 : if (func.place_db[expr.get_rhs ()].is_rvalue ())
359 : 0 : visit_move_place (expr.get_rhs ());
360 : : else
361 : 0 : visit_place (expr.get_rhs ());
362 : 0 : }
363 : :
364 : : std::ostream &
365 : 0 : Dump::indent (size_t depth)
366 : : {
367 : 0 : for (size_t i = 0; i < depth; ++i)
368 : 0 : stream << indentation;
369 : 0 : return stream;
370 : : }
371 : :
372 : : void
373 : 0 : Dump::visit_scope (ScopeId id, size_t depth)
374 : : {
375 : 0 : auto scope = func.place_db.get_scope (id);
376 : 0 : if (scope.locals.empty () && scope.children.empty ())
377 : 0 : return;
378 : :
379 : 0 : if (id.value > 1)
380 : 0 : indent (depth) << "scope " << id.value - 1 << " {\n";
381 : :
382 : 0 : for (auto &local : scope.locals)
383 : : {
384 : 0 : indent (depth + 1) << "let _";
385 : 0 : stream << place_map[local].value << ": "
386 : 0 : << get_tyty_name (func.place_db[local].tyty);
387 : 0 : stream << ";\t";
388 : :
389 : 0 : stream << "[";
390 : 0 : print_comma_separated (stream,
391 : 0 : func.place_db[local].regions.get_regions (),
392 : 0 : [this] (FreeRegion region_id) {
393 : 0 : stream << "'?" << region_id.value;
394 : 0 : });
395 : 0 : stream << "]\n";
396 : : }
397 : 0 : for (auto &child : scope.children)
398 : 0 : visit_scope (child, (id.value >= 1) ? depth + 1 : depth);
399 : :
400 : 0 : if (id.value > 1)
401 : 0 : indent (depth) << "}\n";
402 : 0 : }
403 : :
404 : : } // namespace BIR
405 : : } // namespace Rust
|