Branch data Line data Source code
1 : : // Copyright (C) 2020-2024 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 : size_t next_out_id = 0;
57 : :
58 : 0 : for (size_t in_id = FIRST_VARIABLE_PLACE; in_id < func.place_db.size ();
59 : : ++in_id)
60 : : {
61 : 0 : const Place &place = func.place_db[in_id];
62 : 0 : if (place.kind == Place::VARIABLE || place.kind == Place::TEMPORARY)
63 : 0 : place_map[in_id] = next_out_id++;
64 : : else
65 : 0 : place_map[in_id] = INVALID_PLACE;
66 : : }
67 : 0 : }
68 : :
69 : : void
70 : 0 : simplify_cfg (Function &func, std::vector<BasicBlockId> &bb_fold_map)
71 : : {
72 : : // The BIR builder can generate many useless basic blocks, which contain only
73 : : // a goto.
74 : : // For actual borrow-checking, the folding has little value.
75 : :
76 : 0 : bool stabilized = false;
77 : 0 : while (!stabilized)
78 : : {
79 : : stabilized = true;
80 : : // BB0 cannot be folded as it is an entry block.
81 : 0 : for (size_t i = 1; i < func.basic_blocks.size (); ++i)
82 : : {
83 : 0 : const BasicBlock &bb = func.basic_blocks[bb_fold_map[i]];
84 : 0 : if (bb.statements.empty () && bb.is_goto_terminated ())
85 : : {
86 : 0 : auto dst = bb.successors.at (0);
87 : 0 : if (bb_fold_map[dst] != dst)
88 : : {
89 : 0 : rust_error_at (
90 : : UNKNOWN_LOCATION,
91 : : "BIR DUMP: Cannot fold CFG, because it contains an "
92 : : "infinite loop with no executable statements.");
93 : 0 : rust_inform (UNKNOWN_LOCATION,
94 : : "Continuing with an unfolded CFG.");
95 : : // Reverting the fold map to the original state.
96 : 0 : std::iota (bb_fold_map.begin (), bb_fold_map.end (), 0);
97 : : stabilized = true;
98 : : break;
99 : : }
100 : 0 : bb_fold_map[i] = dst;
101 : 0 : stabilized = false;
102 : : }
103 : : }
104 : : }
105 : 0 : }
106 : :
107 : : void
108 : 0 : Dump::go (bool enable_simplify_cfg)
109 : : {
110 : : // To avoid mutation of the BIR, we use indirection through bb_fold_map.
111 : 0 : std::iota (bb_fold_map.begin (), bb_fold_map.end (), 0);
112 : :
113 : 0 : std::iota (place_map.begin (), place_map.end (), 0);
114 : :
115 : 0 : if (enable_simplify_cfg)
116 : 0 : simplify_cfg (func, bb_fold_map);
117 : :
118 : 0 : renumber_places (func, place_map);
119 : :
120 : 0 : stream << "fn " << name << "(";
121 : 0 : print_comma_separated (stream, func.arguments, [this] (PlaceId place_id) {
122 : 0 : stream << "_" << place_map[place_id] << ": "
123 : 0 : << get_tyty_name (func.place_db[place_id].tyty);
124 : 0 : });
125 : 0 : stream << ") -> " << get_tyty_name (func.place_db[RETURN_VALUE_PLACE].tyty);
126 : 0 : stream << " {\n";
127 : :
128 : : // Print locals declaration.
129 : 0 : visit_scope (0);
130 : :
131 : : // Print BBs.
132 : 0 : for (statement_bb = 0; statement_bb < func.basic_blocks.size ();
133 : 0 : ++statement_bb)
134 : : {
135 : 0 : if (bb_fold_map[statement_bb] != statement_bb)
136 : 0 : continue; // This BB was folded.
137 : :
138 : 0 : if (func.basic_blocks[statement_bb].statements.empty ()
139 : 0 : && func.basic_blocks[statement_bb].successors.empty ())
140 : 0 : continue;
141 : :
142 : 0 : bb_terminated = false;
143 : :
144 : 0 : BasicBlock &bb = func.basic_blocks[statement_bb];
145 : 0 : stream << "\n";
146 : 0 : stream << indentation << "bb" << bb_fold_map[statement_bb] << ": {\n";
147 : 0 : size_t i = 0;
148 : 0 : for (auto &stmt : bb.statements)
149 : : {
150 : 0 : stream << indentation << i++ << indentation;
151 : 0 : visit (stmt);
152 : 0 : stream << ";\n";
153 : : }
154 : 0 : if (!bb_terminated)
155 : 0 : stream << indentation << indentation << "goto -> bb"
156 : 0 : << bb_fold_map[bb.successors.at (0)] << ";\t\t" << i++ << "\n";
157 : :
158 : 0 : stream << indentation << "}\n";
159 : : }
160 : :
161 : 0 : stream << "}\n";
162 : 0 : }
163 : : void
164 : 0 : Dump::visit (const Statement &stmt)
165 : : {
166 : 0 : statement_place = stmt.get_place ();
167 : 0 : switch (stmt.get_kind ())
168 : : {
169 : 0 : case Statement::Kind::ASSIGNMENT: {
170 : 0 : visit_place (stmt.get_place ());
171 : 0 : stream << " = ";
172 : 0 : stmt.get_expr ().accept_vis (*this);
173 : 0 : break;
174 : : }
175 : 0 : case Statement::Kind::SWITCH:
176 : 0 : stream << "switchInt(";
177 : 0 : visit_move_place (stmt.get_place ());
178 : 0 : stream << ") -> [";
179 : 0 : print_comma_separated (stream, func.basic_blocks[statement_bb].successors,
180 : 0 : [this] (BasicBlockId succ) {
181 : 0 : stream << "bb" << bb_fold_map[succ];
182 : 0 : });
183 : 0 : stream << "]";
184 : 0 : bb_terminated = true;
185 : 0 : break;
186 : 0 : case Statement::Kind::RETURN:
187 : 0 : stream << "return";
188 : 0 : bb_terminated = true;
189 : 0 : break;
190 : 0 : case Statement::Kind::GOTO:
191 : 0 : stream << "goto -> bb"
192 : 0 : << bb_fold_map[func.basic_blocks[statement_bb].successors.at (0)];
193 : 0 : bb_terminated = true;
194 : 0 : break;
195 : 0 : case Statement::Kind::STORAGE_DEAD:
196 : 0 : stream << "StorageDead(";
197 : 0 : visit_place (stmt.get_place ());
198 : 0 : stream << ")";
199 : 0 : break;
200 : 0 : case Statement::Kind::STORAGE_LIVE:
201 : 0 : stream << "StorageLive(";
202 : 0 : visit_place (stmt.get_place ());
203 : 0 : stream << ")";
204 : 0 : break;
205 : 0 : case Statement::Kind::USER_TYPE_ASCRIPTION:
206 : 0 : visit_place (stmt.get_place ());
207 : 0 : stream << " = ";
208 : 0 : stream << "UserTypeAscription(";
209 : 0 : stream << get_tyty_name (func.place_db[stmt.get_place ()].tyty);
210 : 0 : stream << ")";
211 : 0 : break;
212 : 0 : case Statement::Kind::FAKE_READ:
213 : 0 : stream << "FakeRead(";
214 : 0 : visit_place (stmt.get_place ());
215 : 0 : stream << ")";
216 : 0 : break;
217 : : }
218 : 0 : statement_place = INVALID_PLACE;
219 : 0 : }
220 : :
221 : : void
222 : 0 : Dump::visit_place (PlaceId place_id)
223 : : {
224 : 0 : const Place &place = func.place_db[place_id];
225 : 0 : switch (place.kind)
226 : : {
227 : 0 : case Place::TEMPORARY:
228 : 0 : case Place::VARIABLE:
229 : 0 : stream << "_" << place_map[place_id];
230 : 0 : break;
231 : 0 : case Place::DEREF:
232 : 0 : stream << "(";
233 : 0 : stream << "*";
234 : 0 : visit_place (place.path.parent);
235 : 0 : stream << ")";
236 : 0 : break;
237 : 0 : case Place::FIELD:
238 : 0 : stream << "(";
239 : 0 : visit_place (place.path.parent);
240 : 0 : stream << ".";
241 : 0 : stream << place.variable_or_field_index;
242 : 0 : stream << ": " << get_tyty_name (place.tyty) << ")";
243 : 0 : break;
244 : 0 : case Place::INDEX:
245 : 0 : stream << "(";
246 : 0 : visit_place (place.path.parent);
247 : 0 : stream << "[]";
248 : 0 : stream << ": " << get_tyty_name (place.tyty) << ")";
249 : 0 : break;
250 : 0 : case Place::CONSTANT:
251 : 0 : stream << "const " << get_tyty_name (place.tyty);
252 : 0 : break;
253 : 0 : case Place::INVALID:
254 : 0 : stream << "_INVALID";
255 : : }
256 : 0 : }
257 : :
258 : : void
259 : 0 : Dump::visit_move_place (PlaceId place_id)
260 : : {
261 : 0 : const Place &place = func.place_db[place_id];
262 : 0 : if (!place.is_constant ())
263 : 0 : stream << "move ";
264 : 0 : visit_place (place_id);
265 : 0 : }
266 : :
267 : : void
268 : 0 : Dump::visit (const BorrowExpr &expr)
269 : : {
270 : 0 : stream << "&";
271 : 0 : visit_place (expr.get_place ());
272 : 0 : }
273 : :
274 : : void
275 : 0 : Dump::visit_lifetime (PlaceId place_id)
276 : 0 : {}
277 : :
278 : : void
279 : 0 : Dump::visit (const InitializerExpr &expr)
280 : : {
281 : 0 : stream << "{";
282 : 0 : print_comma_separated (stream, expr.get_values (), [this] (PlaceId place_id) {
283 : 0 : visit_move_place (place_id);
284 : : });
285 : 0 : stream << "}";
286 : 0 : }
287 : :
288 : : void
289 : 0 : Dump::visit (const CallExpr &expr)
290 : : {
291 : 0 : stream << "Call(";
292 : 0 : auto maybe_fn_type
293 : 0 : = func.place_db[expr.get_callable ()].tyty->try_as<TyTy::FnType> ();
294 : 0 : if (maybe_fn_type)
295 : 0 : stream << maybe_fn_type->get_identifier ();
296 : : else
297 : 0 : visit_move_place (expr.get_callable ());
298 : :
299 : 0 : stream << ")(";
300 : 0 : print_comma_separated (stream, expr.get_arguments (),
301 : 0 : [this] (PlaceId place_id) {
302 : 0 : visit_move_place (place_id);
303 : : });
304 : 0 : stream << ") -> [";
305 : 0 : print_comma_separated (stream, func.basic_blocks[statement_bb].successors,
306 : 0 : [this] (BasicBlockId succ) {
307 : 0 : stream << "bb" << bb_fold_map[succ];
308 : 0 : });
309 : 0 : stream << "]";
310 : 0 : bb_terminated = true;
311 : 0 : }
312 : :
313 : : void
314 : 0 : Dump::visit (const Operator<1> &expr)
315 : : {
316 : 0 : stream << "Operator(";
317 : 0 : visit_move_place (expr.get_operand<0> ());
318 : 0 : stream << ")";
319 : 0 : }
320 : :
321 : : void
322 : 0 : Dump::visit (const Operator<2> &expr)
323 : : {
324 : 0 : stream << "Operator(";
325 : 0 : visit_move_place (expr.get_operand<0> ());
326 : 0 : stream << ", ";
327 : 0 : visit_move_place (expr.get_operand<1> ());
328 : 0 : stream << ")";
329 : 0 : }
330 : :
331 : : void
332 : 0 : Dump::visit (const Assignment &expr)
333 : : {
334 : 0 : if (func.place_db[expr.get_rhs ()].is_rvalue ())
335 : 0 : visit_move_place (expr.get_rhs ());
336 : : else
337 : 0 : visit_place (expr.get_rhs ());
338 : 0 : }
339 : :
340 : : std::ostream &
341 : 0 : Dump::indent (size_t depth)
342 : : {
343 : 0 : for (size_t i = 0; i < depth; ++i)
344 : 0 : stream << indentation;
345 : 0 : return stream;
346 : : }
347 : :
348 : : void
349 : 0 : Dump::visit_scope (ScopeId id, size_t depth)
350 : : {
351 : 0 : auto scope = func.place_db.get_scope (id);
352 : 0 : if (scope.locals.empty () && scope.children.empty ())
353 : 0 : return;
354 : :
355 : 0 : if (id > 1)
356 : 0 : indent (depth) << "scope " << id - 1 << " {\n";
357 : :
358 : 0 : for (auto &local : scope.locals)
359 : : {
360 : 0 : indent (depth + 1) << "let _";
361 : 0 : stream << place_map[local] << ": "
362 : 0 : << get_tyty_name (func.place_db[local].tyty);
363 : 0 : stream << ";\n";
364 : : }
365 : 0 : for (auto &child : scope.children)
366 : 0 : visit_scope (child, (id >= 1) ? depth + 1 : depth);
367 : :
368 : 0 : if (id > 1)
369 : 0 : indent (depth) << "}\n";
370 : 0 : }
371 : :
372 : : } // namespace BIR
373 : : } // namespace Rust
|