new file mode 100644
@@ -0,0 +1,11 @@
+[package]
+name = "ffi-polonius"
+version = "0.1.0"
+edition = "2021"
+license = "GPL-3"
+
+[lib]
+crate-type = ["staticlib"]
+
+[dependencies]
+polonius-engine = "0.13.0"
\ No newline at end of file
new file mode 100644
@@ -0,0 +1,61 @@
+// 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/>.
+
+// This is an FFI interface to gccrs (c++).
+// It is a counterpart to `rust-polonius-ffi.h`.
+
+// Generated by rust-bindgen, remove unsafe phantoms and add Into impls.
+// ```shell
+// bindgen \
+// --generate types \
+// --allowlist-file rust-polonius-facts-ffi.h
+// --no-layout-tests
+// rust-polonius-ffi.h
+// -- -x c++
+// ```
+include!("gccrs_ffi_generated.rs");
+
+use crate::GccrsAtom;
+
+impl<T1, T2> Into<(GccrsAtom, GccrsAtom)> for Pair<T1, T2>
+where
+ GccrsAtom: From<T1> + From<T2>,
+{
+ fn into(self) -> (GccrsAtom, GccrsAtom) {
+ (self.first.into(), self.second.into())
+ }
+}
+
+impl<T1, T2, T3> Into<(GccrsAtom, GccrsAtom, GccrsAtom)> for Triple<T1, T2, T3>
+where
+ GccrsAtom: From<T1> + From<T2> + From<T3>,
+{
+ fn into(self) -> (GccrsAtom, GccrsAtom, GccrsAtom) {
+ (self.first.into(), self.second.into(), self.third.into())
+ }
+}
+
+impl<OUT, IN> Into<Vec<OUT>> for Slice<IN>
+where
+ IN: Into<OUT> + Copy,
+{
+ fn into(self) -> Vec<OUT> {
+ let slice = unsafe { std::slice::from_raw_parts(self.data, self.len as usize) };
+ slice.iter().map(|&e| e.into()).collect()
+ }
+}
new file mode 100644
@@ -0,0 +1,50 @@
+pub type Origin = usize;
+pub type Loan = usize;
+pub type Point = usize;
+pub type Variable = usize;
+pub type Path = usize;
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct Pair<T1, T2> {
+ pub first: T1,
+ pub second: T2,
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct Triple<T1, T2, T3> {
+ pub first: T1,
+ pub second: T2,
+ pub third: T3,
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct Slice<T> {
+ pub len: usize,
+ pub data: *const T,
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct FactsView {
+ pub loan_issued_at: Slice<Triple<Origin, Loan, Point>>,
+ pub universal_region: Slice<Origin>,
+ pub cfg_edge: Slice<Pair<Point, Point>>,
+ pub loan_killed_at: Slice<Pair<Loan, Point>>,
+ pub subset_base: Slice<Triple<Origin, Origin, Point>>,
+ pub loan_invalidated_at: Slice<Pair<Point, Loan>>,
+ pub var_used_at: Slice<Pair<Variable, Point>>,
+ pub var_defined_at: Slice<Pair<Variable, Point>>,
+ pub var_dropped_at: Slice<Pair<Variable, Point>>,
+ pub use_of_var_derefs_origin: Slice<Pair<Variable, Origin>>,
+ pub drop_of_var_derefs_origin: Slice<Pair<Variable, Origin>>,
+ pub child_path: Slice<Pair<Path, Path>>,
+ pub path_is_var: Slice<Pair<Path, Variable>>,
+ pub path_assigned_at_base: Slice<Pair<Path, Point>>,
+ pub path_moved_at_base: Slice<Pair<Path, Point>>,
+ pub path_accessed_at_base: Slice<Pair<Path, Point>>,
+ pub known_placeholder_subset: Slice<Pair<Origin, Origin>>,
+ pub placeholder: Slice<Pair<Origin, Loan>>,
+}
new file mode 100644
@@ -0,0 +1,133 @@
+// 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/>.
+
+mod gccrs_ffi;
+mod gccrs_ffi_generated;
+
+use polonius_engine::{AllFacts, Atom, FactTypes, Output};
+use std::fmt::Debug;
+use std::hash::Hash;
+
+/// A single fact value.
+/// For simplicity we use one type for all facts.
+#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+struct GccrsAtom(usize);
+
+impl Atom for GccrsAtom {
+ fn index(self) -> usize {
+ self.0
+ }
+}
+
+impl From<usize> for GccrsAtom {
+ fn from(inner: usize) -> GccrsAtom {
+ GccrsAtom(inner)
+ }
+}
+
+impl From<GccrsAtom> for usize {
+ fn from(atom: GccrsAtom) -> Self {
+ atom.index()
+ }
+}
+
+#[derive(Debug, Clone, Copy, Default)]
+struct GccrsFacts;
+
+impl FactTypes for GccrsFacts {
+ type Origin = GccrsAtom;
+ type Loan = GccrsAtom;
+ type Point = GccrsAtom;
+ type Variable = GccrsAtom;
+ type Path = GccrsAtom;
+}
+
+impl From<gccrs_ffi::FactsView> for AllFacts<GccrsFacts> {
+ fn from(input: gccrs_ffi::FactsView) -> Self {
+ AllFacts::<GccrsFacts> {
+ loan_issued_at: input.loan_issued_at.into(),
+ universal_region: input.universal_region.into(),
+ cfg_edge: input.cfg_edge.into(),
+ loan_killed_at: input.loan_killed_at.into(),
+ subset_base: input.subset_base.into(),
+ loan_invalidated_at: input.loan_invalidated_at.into(),
+ var_used_at: input.var_used_at.into(),
+ var_defined_at: input.var_defined_at.into(),
+ var_dropped_at: input.var_dropped_at.into(),
+ use_of_var_derefs_origin: input.use_of_var_derefs_origin.into(),
+ drop_of_var_derefs_origin: input.drop_of_var_derefs_origin.into(),
+ child_path: input.child_path.into(),
+ path_is_var: input.path_is_var.into(),
+ path_assigned_at_base: input.path_assigned_at_base.into(),
+ path_moved_at_base: input.path_moved_at_base.into(),
+ path_accessed_at_base: input.path_accessed_at_base.into(),
+ known_placeholder_subset: input.known_placeholder_subset.into(),
+ placeholder: input.placeholder.into(),
+ }
+ }
+}
+
+fn print_point(point: GccrsAtom) {
+ let val: usize = point.into();
+ let mid = val % 2 == 1;
+ let bb = val >> 16;
+ let stmt = (val >> 1) & 0xFFFF;
+ print!("{}(bb{}[{}])", if mid { "Mid" } else { "Start" }, bb, stmt);
+}
+
+/// Run the polonius analysis on the given facts (for a single function).
+/// Right now, results are only printed and not propagated back to the gccrs.
+#[no_mangle]
+pub unsafe extern "C" fn polonius_run(input: gccrs_ffi::FactsView, dump_enabled: bool) {
+ let facts = AllFacts::<GccrsFacts>::from(input);
+ let output = Output::compute(&facts, polonius_engine::Algorithm::Naive, dump_enabled);
+
+ // FIXME: Temporary output
+ println!("Polonius analysis completed. Results:");
+ println!("Errors: {:#?}", output.errors);
+ println!("Subset error: {:#?}", output.subset_errors);
+ println!("Move error: {:#?}", output.move_errors);
+
+ println!("Subsets:");
+ let mut subset_vec: Vec<_> = output.subset.iter().collect();
+ subset_vec.sort_by_key(|&(point, _)| point);
+ for (point, subsets) in subset_vec {
+ print_point(*point);
+ println!(": {{");
+ for (&lhs, rhss) in subsets {
+ for &rhs in rhss {
+ println!(" {} <= {}", usize::from(lhs), usize::from(rhs));
+ }
+ }
+ println!("}}");
+ }
+ println!("Subset anywhere: {:#?}", output.subset_anywhere);
+
+ // Print origin live on entry
+ println!("Origin live on entry:");
+ let mut origin_vec: Vec<_> = output.origin_live_on_entry.iter().collect();
+ origin_vec.sort_by_key(|&(point, _)| point);
+ for (point, origins) in origin_vec {
+ print_point(*point);
+ println!(": {{");
+ for &origin in origins {
+ println!(" {}", usize::from(origin));
+ }
+ println!("}}");
+ }
+}
new file mode 100644
@@ -0,0 +1,109 @@
+// 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_POLONIUS_FACTS_FFI_H
+#define RUST_POLONIUS_FACTS_FFI_H
+
+#include "rust-system.h"
+
+// This file defines the C++ side of the FFI interface to Polonius.
+// The corresponding Rust side is in `gccrs-ffi.rs`.
+
+// IMPORTANT:
+// This file intentionally does not include any C++ headers
+// to allow seamless binding generation on the Rust side.
+
+namespace Rust {
+namespace Polonius {
+
+using Origin = size_t;
+using Loan = size_t;
+/**
+ * Compressed CFG point
+ * Encoding:
+ * - (bit size - 16) bits: basic block index
+ * - 15 bits: stmt index inside basic block
+ * - 1bit: start or mid
+ *
+ * Note: Polonius requires the holding type to be `size_t`/`usize`.
+ */
+using Point = size_t;
+using Variable = size_t;
+using Path = size_t;
+
+namespace FFI {
+
+// NOTE: std::pair and std::tuple are complicating the bindings' generation.
+template <typename T1, typename T2> struct Pair
+{
+ T1 first;
+ T2 second;
+
+ Pair (T1 first, T2 second) : first (first), second (second) {}
+};
+
+template <typename T1, typename T2, typename T3> struct Triple
+{
+ T1 first;
+ T2 second;
+ T3 third;
+
+ Triple (T1 first, T2 second, T3 third)
+ : first (first), second (second), third (third)
+ {}
+};
+
+/** Frozen variant to vector for FFI */
+template <typename T> struct Slice
+{
+ size_t len;
+ const T *const data;
+
+ template <typename vector>
+ Slice (const vector &v) : len (v.size ()), data (v.data ())
+ {}
+};
+
+/** Frozen variant of the facts with C ABI and no methods. */
+struct FactsView
+{
+ Slice<Triple<Origin, Loan, Point>> loan_issued_at;
+ Slice<Origin> universal_region;
+ Slice<Pair<Point, Point>> cfg_edge;
+ Slice<Pair<Loan, Point>> loan_killed_at;
+ Slice<Triple<Origin, Origin, Point>> subset_base;
+ Slice<Pair<Point, Loan>> loan_invalidated_at;
+ Slice<Pair<Variable, Point>> var_used_at;
+ Slice<Pair<Variable, Point>> var_defined_at;
+ Slice<Pair<Variable, Point>> var_dropped_at;
+ Slice<Pair<Variable, Origin>> use_of_var_derefs_origin;
+ Slice<Pair<Variable, Origin>> drop_of_var_derefs_origin;
+ Slice<Pair<Path, Path>> child_path;
+ Slice<Pair<Path, Variable>> path_is_var;
+ Slice<Pair<Path, Point>> path_assigned_at_base;
+ Slice<Pair<Path, Point>> path_moved_at_base;
+ Slice<Pair<Path, Point>> path_accessed_at_base;
+ Slice<Pair<Origin, Origin>> known_placeholder_subset;
+ Slice<Pair<Origin, Loan>> placeholder;
+};
+
+} // namespace FFI
+} // namespace Polonius
+} // namespace Rust
+
+#endif // RUST_POLONIUS_FACTS_FFI_H
new file mode 100644
@@ -0,0 +1,232 @@
+// Copyright (C) 2020-2022 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_POLONIUS_H
+#define RUST_POLONIUS_H
+
+// Interface to the Polonius borrow checker engine.
+// See (https://github.com/rust-lang/polonius/blob/master/polonius-engine/)
+
+#include <ostream>
+#include "rust-polonius-ffi.h"
+
+namespace Rust {
+namespace Polonius {
+
+/** A point in the control flow graph. */
+struct FullPoint
+{
+ uint32_t bb;
+ uint32_t stmt;
+ bool mid;
+
+ /** Expands a compressed `Point` into its components.
+ * See `Point` docs for encoding details.
+ */
+ explicit FullPoint (Point point)
+ : bb (extract_bb (point)), stmt (extract_stmt (point)),
+ mid (extract_mid (point))
+ {}
+
+ static uint32_t extract_bb (Point point) { return point >> 16; }
+ static uint32_t extract_stmt (Point point)
+ {
+ return (point & ~(1 << 16)) >> 1;
+ }
+ static bool extract_mid (Point point) { return point & 1; }
+
+ friend std::ostream &operator<< (std::ostream &os, const FullPoint &point)
+ {
+ return os << ((point.mid) ? "Mid" : "Start") << "(bb" << point.bb << "["
+ << point.stmt << "])";
+ }
+};
+
+struct Facts
+{
+ // See (https://rust-lang.github.io/polonius/rules/relations.html)
+ std::vector<FFI::Triple<Origin, Loan, Point>> loan_issued_at;
+ std::vector<Origin> universal_region;
+ std::vector<FFI::Pair<Point, Point>> cfg_edge;
+ std::vector<FFI::Pair<Loan, Point>> loan_killed_at;
+ std::vector<FFI::Triple<Origin, Origin, Point>> subset_base;
+ std::vector<FFI::Pair<Point, Loan>> loan_invalidated_at;
+ std::vector<FFI::Pair<Variable, Point>> var_used_at;
+ std::vector<FFI::Pair<Variable, Point>> var_defined_at;
+ std::vector<FFI::Pair<Variable, Point>> var_dropped_at;
+ std::vector<FFI::Pair<Variable, Origin>> use_of_var_derefs_origin;
+ std::vector<FFI::Pair<Variable, Origin>> drop_of_var_derefs_origin;
+ std::vector<FFI::Pair<Path, Path>> child_path;
+ std::vector<FFI::Pair<Path, Variable>> path_is_var;
+ std::vector<FFI::Pair<Path, Point>> path_assigned_at_base;
+ std::vector<FFI::Pair<Path, Point>> path_moved_at_base;
+ std::vector<FFI::Pair<Path, Point>> path_accessed_at_base;
+ std::vector<FFI::Pair<Origin, Origin>> known_placeholder_subset;
+ std::vector<FFI::Pair<Origin, Loan>> placeholder;
+
+ /**
+ * Create a const view for the struct for FFI.
+ *
+ * This view uses the original vector storage.
+ * Therefore any resizing operation of Facts member may invalidate the view.
+ */
+ FFI::FactsView freeze ()
+ {
+ return FFI::FactsView{loan_issued_at,
+ universal_region,
+ cfg_edge,
+ loan_killed_at,
+ subset_base,
+ loan_invalidated_at,
+ var_used_at,
+ var_defined_at,
+ var_dropped_at,
+ use_of_var_derefs_origin,
+ drop_of_var_derefs_origin,
+ child_path,
+ path_is_var,
+ path_assigned_at_base,
+ path_moved_at_base,
+ path_accessed_at_base,
+ known_placeholder_subset,
+ placeholder};
+ }
+
+ void dump_loan_issued_at (std::ostream &os) const
+ {
+ for (auto &e : loan_issued_at)
+ os << "\"'?" << e.first << "\"\t\"bw" << e.second << "\"\t\""
+ << FullPoint (e.third) << "\"\n";
+ }
+
+ void dump_universal_region (std::ostream &os) const
+ {
+ for (auto &e : universal_region)
+ os << e << "\n";
+ }
+
+ void dump_cfg_edge (std::ostream &os) const
+ {
+ for (auto &e : cfg_edge)
+ os << FullPoint (e.first) << " " << FullPoint (e.second) << "\n";
+ }
+
+ void dump_loan_killed_at (std::ostream &os) const
+ {
+ for (auto &e : loan_killed_at)
+ os << e.first << " " << FullPoint (e.second) << "\n";
+ }
+
+ void dump_subset_base (std::ostream &os) const
+ {
+ for (auto &e : subset_base)
+ os << "\"'?" << e.first << "\"\t\"'?" << e.second << "\"\t\""
+ << FullPoint (e.third) << "\"\n";
+ }
+
+ void dump_loan_invalidated_at (std::ostream &os) const
+ {
+ for (auto &e : loan_invalidated_at)
+ os << FullPoint (e.first) << " " << e.second << "\n";
+ }
+
+ void dump_var_used_at (std::ostream &os) const
+ {
+ for (auto &e : var_used_at)
+ os << e.first - 1 << " " << FullPoint (e.second) << "\n";
+ }
+
+ void dump_var_defined_at (std::ostream &os) const
+ {
+ for (auto &e : var_defined_at)
+ os << e.first << " " << FullPoint (e.second) << "\n";
+ }
+
+ void dump_var_dropped_at (std::ostream &os) const
+ {
+ for (auto &e : var_dropped_at)
+ os << e.first << " " << FullPoint (e.second) << "\n";
+ }
+
+ void dump_use_of_var_derefs_origin (std::ostream &os) const
+ {
+ for (auto &e : use_of_var_derefs_origin)
+ os << e.first << " " << e.second << "\n";
+ }
+
+ void dump_drop_of_var_derefs_origin (std::ostream &os) const
+ {
+ for (auto &e : drop_of_var_derefs_origin)
+ os << e.first << " " << e.second << "\n";
+ }
+
+ void dump_child_path (std::ostream &os) const
+ {
+ for (auto &e : child_path)
+ os << e.first << " " << e.second << "\n";
+ }
+
+ void dump_path_is_var (std::ostream &os) const
+ {
+ for (auto &e : path_is_var)
+ os << e.first << " " << e.second << "\n";
+ }
+
+ void dump_path_assigned_at_base (std::ostream &os) const
+ {
+ for (auto &e : path_assigned_at_base)
+ os << e.first << " " << FullPoint (e.second) << "\n";
+ }
+
+ void dump_path_moved_at_base (std::ostream &os) const
+ {
+ for (auto &e : path_moved_at_base)
+ os << e.first << " " << FullPoint (e.second) << "\n";
+ }
+
+ void dump_path_accessed_at_base (std::ostream &os) const
+ {
+ for (auto &e : path_accessed_at_base)
+ os << e.first << " " << FullPoint (e.second) << "\n";
+ }
+
+ void dump_known_placeholder_subset (std::ostream &os) const
+ {
+ for (auto &e : known_placeholder_subset)
+ os << e.first << " " << e.second << "\n";
+ }
+
+ void dump_placeholder (std::ostream &os) const
+ {
+ for (auto &e : placeholder)
+ os << e.first << " " << e.second << "\n";
+ }
+};
+
+/**
+ * Check a single function for borrow errors.
+ *
+ * Output is not yet implemented and is only dumped to stdout.
+ */
+extern "C" void
+polonius_run (FFI::FactsView input, bool dump_enabled);
+
+} // namespace Polonius
+} // namespace Rust
+
+#endif /* !RUST_POLONIUS_H */