https://gcc.gnu.org/g:f5b9bab688ae04d84ba83269b21e80d671453e45
commit f5b9bab688ae04d84ba83269b21e80d671453e45 Author: Jakub Dupak <d...@jakubdupak.com> Date: Wed Oct 18 22:36:38 2023 +0200 borrowck: BIR dump gcc/rust/ChangeLog: * Make-lang.in: Build BIR dump. * checks/errors/borrowck/rust-borrow-checker.cc (mkdir_wrapped): Cross-platform mkdir. (dump_function_bir): Save dump to file. (BorrowChecker::go): Run dump during borrowck. * checks/errors/borrowck/rust-bir-dump.cc: New file. * checks/errors/borrowck/rust-bir-dump.h: New file. Signed-off-by: Jakub Dupak <d...@jakubdupak.com> Diff: --- gcc/rust/Make-lang.in | 1 + gcc/rust/checks/errors/borrowck/rust-bir-dump.cc | 239 +++++++++++++++++++++ gcc/rust/checks/errors/borrowck/rust-bir-dump.h | 62 ++++++ .../checks/errors/borrowck/rust-borrow-checker.cc | 56 ++++- 4 files changed, 357 insertions(+), 1 deletion(-) diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in index 1e462ef6a1bf..facdd43fd518 100644 --- a/gcc/rust/Make-lang.in +++ b/gcc/rust/Make-lang.in @@ -150,6 +150,7 @@ GRS_OBJS = \ rust/rust-hir-type-check-implitem.o \ rust/rust-borrow-checker.o \ rust/rust-bir-builder-expr-stmt.o \ + rust/rust-bir-dump.o \ rust/rust-hir-dot-operator.o \ rust/rust-hir-path-probe.o \ rust/rust-type-util.o \ diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-dump.cc b/gcc/rust/checks/errors/borrowck/rust-bir-dump.cc new file mode 100644 index 000000000000..cebed2485a31 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/rust-bir-dump.cc @@ -0,0 +1,239 @@ +#include "rust-bir-dump.h" + +namespace Rust { +namespace BIR { + +constexpr auto indentation = " "; + +uint32_t +get_place_name (PlaceId place_id) +{ + rust_assert (place_id >= FIRST_VARIABLE_PLACE); + return place_id - FIRST_VARIABLE_PLACE; +} + +uint32_t +get_lifetime_name (Lifetime lifetime_id) +{ + rust_assert (lifetime_id.id >= FIRST_NORMAL_LIFETIME_ID); + // Start from 1 as rustc does. + return lifetime_id.id - FIRST_NORMAL_LIFETIME_ID + 1; +} + +std::string +get_tyty_name (TyTy::BaseType *tyty) +{ + if (tyty) + return tyty->get_name (); + return "unknown"; +} + +void +Dump::go () +{ + stream << "fn " << name << "("; + for (PlaceId arg : func.arguments) + { + stream << "_" << get_place_name (arg) << ": " + << get_tyty_name (place_db[arg].tyty) << ", "; + } + stream << ") -> " << get_tyty_name (place_db[RETURN_VALUE_PLACE].tyty) + << " {\n"; + + for (PlaceId id = FIRST_VARIABLE_PLACE; id < place_db.size (); ++id) + { + const Place &place = place_db[id]; + if (place.kind == Place::VARIABLE || place.kind == Place::TEMPORARY) + { + if (std::find (func.arguments.begin (), func.arguments.end (), id) + != func.arguments.end ()) + continue; + stream << indentation << "let _" << get_place_name (id) << ": " + << get_tyty_name (place_db[id].tyty) << ";\n"; + } + } + + for (BasicBlockId id = 0; id < func.basic_blocks.size (); ++id) + { + if (func.basic_blocks[id].statements.empty () + && func.basic_blocks[id].successors.empty ()) + continue; + + BasicBlock &bb = func.basic_blocks[id]; + stream << "\n"; + stream << indentation << "bb" << id << ": {\n"; + for (auto &stmt : bb.statements) + { + stream << indentation << indentation; + visit (stmt); + stream << ";\n"; + } + stream << indentation << "} -> ["; + for (auto succ : bb.successors) + stream << "bb" << succ << ", "; + stream << "]\n"; + } + + stream << "}\n\n"; +} +void +Dump::visit (Node &node) +{ + node_place = node.get_place (); + switch (node.get_kind ()) + { + case Node::Kind::ASSIGNMENT: { + stream << "_" << get_place_name (node.get_place ()) << " = "; + node.get_expr ().accept_vis (*this); + break; + } + case Node::Kind::SWITCH: + stream << "switch "; + visit_move_place (node.get_place ()); + break; + case Node::Kind::RETURN: + stream << "return"; + break; + case Node::Kind::GOTO: + stream << "goto"; + break; + case Node::Kind::STORAGE_DEAD: + stream << "StorageDead("; + visit_move_place (node.get_place ()); + stream << ")"; + break; + case Node::Kind::STORAGE_LIVE: + stream << "StorageLive("; + visit_move_place (node.get_place ()); + stream << ")"; + break; + } + node_place = INVALID_PLACE; +} + +void +Dump::visit_place (PlaceId place_id) +{ + const Place &place = place_db[place_id]; + switch (place.kind) + { + case Place::TEMPORARY: + case Place::VARIABLE: + stream << "_" << get_place_name (place_id); + break; + case Place::DEREF: + stream << "("; + stream << "*"; + visit_place (place.path.parent); + stream << ")"; + break; + case Place::FIELD: + stream << "("; + visit_place (place.path.parent); + stream << "."; + stream << place.variable_or_field_index; + stream << ": " << get_tyty_name (place.tyty) << ")"; + break; + case Place::INDEX: + stream << "("; + visit_place (place.path.parent); + stream << "[]"; + stream << ": " << get_tyty_name (place.tyty) << ")"; + break; + case Place::CONSTANT: + stream << "const " << get_tyty_name (place.tyty); + break; + case Place::INVALID: + stream << "_INVALID"; + } +} + +void +Dump::visit_move_place (PlaceId place_id) +{ + const Place &place = place_db[place_id]; + if (!place.is_copy) + stream << "move "; + visit_place (place_id); +} + +void +Dump::visit (BorrowExpr &expr) +{ + stream << "&"; + visit_lifetime (node_place); + visit_place (expr.get_place ()); +} + +void +Dump::visit_lifetime (PlaceId place_id) +{ + const Place &place = place_db[place_id]; + if (place.lifetime.has_lifetime ()) + { + if (place.lifetime.id == STATIC_LIFETIME_ID) + stream << "'static "; + else + stream << "'#" << get_lifetime_name (place.lifetime) << " "; + } +} + +void +Dump::visit (InitializerExpr &expr) +{ + stream << "{"; + for (auto &place : expr.get_values ()) + { + visit_move_place (place); + stream << ", "; + } + stream << "}"; +} + +void +Dump::visit (CallExpr &expr) +{ + stream << "Call("; + if (auto fn_type + = place_db[expr.get_callable ()].tyty->try_as<TyTy::FnType> ()) + { + stream << fn_type->get_identifier (); + } + else + { + visit_move_place (expr.get_callable ()); + } + stream << ")("; + for (auto &place : expr.get_arguments ()) + { + visit_move_place (place); + stream << ", "; + } + stream << ")"; +} + +void +Dump::visit (Operator<1> &expr) +{ + stream << "Operator("; + visit_move_place (expr.get_operand<0> ()); + stream << ")"; +} + +void +Dump::visit (Operator<2> &expr) +{ + stream << "Operator("; + visit_move_place (expr.get_operand<0> ()); + stream << ", "; + visit_move_place (expr.get_operand<1> ()); + stream << ")"; +} +void +Dump::visit (Assignment &expr) +{ + visit_move_place (expr.get_rhs ()); +} + +} // namespace BIR +} // namespace Rust diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-dump.h b/gcc/rust/checks/errors/borrowck/rust-bir-dump.h new file mode 100644 index 000000000000..7c24749fed55 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/rust-bir-dump.h @@ -0,0 +1,62 @@ +// Copyright (C) 2020-2023 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +#ifndef RUST_BIR_DUMP_H +#define RUST_BIR_DUMP_H + +#include <ostream> +#include <utility> +#include "rust-bir-place.h" +#include "rust-bir-visitor.h" +#include "rust-bir.h" + +namespace Rust { +namespace BIR { + +class Dump : public Visitor +{ + std::ostream &stream; + const PlaceDB &place_db; + Function &func; + const std::string &name; + + PlaceId node_place = INVALID_PLACE; + +public: + Dump (std::ostream &os, Function &func, const std::string &name) + : stream (os), place_db (func.place_db), func (func), name (name) + {} + void go (); + +protected: + void visit (Node &node) override; + void visit_place (PlaceId place_id); + void visit_move_place (PlaceId place_id); + void visit (BorrowExpr &expr) override; + void visit_lifetime (PlaceId place_id); + void visit (InitializerExpr &expr) override; + void visit (CallExpr &expr) override; + void visit (Operator<1> &expr) override; + void visit (Operator<2> &expr) override; + void visit (Assignment &expr) override; +}; + +} // namespace BIR +} // namespace Rust + +#endif // RUST_BIR_DUMP_H diff --git a/gcc/rust/checks/errors/borrowck/rust-borrow-checker.cc b/gcc/rust/checks/errors/borrowck/rust-borrow-checker.cc index 4b9712538df3..0c952ad32fa5 100644 --- a/gcc/rust/checks/errors/borrowck/rust-borrow-checker.cc +++ b/gcc/rust/checks/errors/borrowck/rust-borrow-checker.cc @@ -19,13 +19,56 @@ #include "rust-borrow-checker.h" #include "rust-function-collector.h" #include "rust-bir-builder.h" +#include "rust-bir-dump.h" namespace Rust { namespace HIR { +void +mkdir_wrapped (const std::string &dirname) +{ + int ret; +#ifdef _WIN32 + ret = _mkdir (dirname.c_str ()); +#elif unix + ret = mkdir (dirname.c_str (), 0775); +#elif __APPLE__ + ret = mkdir (dirname.c_str (), 0775); +#endif + (void) ret; +} + +void +dump_function_bir (const std::string &filename, BIR::Function &func, + const std::string &name) +{ + std::ofstream file; + file.open (filename); + if (file.fail ()) + { + rust_error_at (UNKNOWN_LOCATION, "Failed to open file %s", + filename.c_str ()); + return; + } + BIR::Dump (file, func, name).go (); + file.close (); +} + void BorrowChecker::go (HIR::Crate &crate) { + std::string crate_name; + + if (enable_dump_bir) + { + mkdir_wrapped ("bir_dump"); + auto mappings = Analysis::Mappings::get (); + bool ok + = mappings->get_crate_name (crate.get_mappings ().get_crate_num (), + crate_name); + rust_assert (ok); + } + FunctionCollector collector; collector.go (crate); @@ -33,11 +76,22 @@ BorrowChecker::go (HIR::Crate &crate) { BIR::BuilderContext ctx; BIR::Builder builder (ctx); - builder.build (*func); + auto bir = builder.build (*func); + + if (enable_dump_bir) + { + std::string filename = "bir_dump/" + crate_name + "." + + func->get_function_name ().as_string () + + ".bir.dump"; + dump_function_bir (filename, bir, + func->get_function_name ().as_string ()); + } } for (auto closure ATTRIBUTE_UNUSED : collector.get_closures ()) { + rust_sorry_at (closure->get_locus (), + "Closure borrow checking is not implemented yet."); } }