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.");
     }
 }

Reply via email to