From b87f6211f0db3e43f9a8109ac565215d6fb930ce Mon Sep 17 00:00:00 2001 From: Jakub Dupak <dev@jakubdupak.com> Date: Sun, 29 Oct 2023 17:34:05 +0100 Subject: [PATCH] borrowck: Polonius FFI gcc/rust/ChangeLog: * Make-lang.in: Build FFI-Polonius. * checks/errors/borrowck/rust-borrow-checker.cc (BorrowChecker::go): Invoke Polonius. * checks/errors/borrowck/ffi-polonius/Cargo.toml: New file. * checks/errors/borrowck/ffi-polonius/src/gccrs_ffi.rs: New file. * checks/errors/borrowck/ffi-polonius/src/lib.rs: New file. * checks/errors/borrowck/polonius/rust-polonius-ffi.h: New file. * checks/errors/borrowck/polonius/rust-polonius.h: New file. Signed-off-by: Jakub Dupak <dev@jakubdupak.com> --- gcc/rust/Make-lang.in | 19 ++- .../errors/borrowck/ffi-polonius/Cargo.toml | 11 ++ .../borrowck/ffi-polonius/src/gccrs_ffi.rs | 115 ++++++++++++++++++ .../errors/borrowck/ffi-polonius/src/lib.rs | 96 +++++++++++++++ .../borrowck/polonius/rust-polonius-ffi.h | 98 +++++++++++++++ .../errors/borrowck/polonius/rust-polonius.h | 92 ++++++++++++++ .../errors/borrowck/rust-borrow-checker.cc | 4 + 7 files changed, 432 insertions(+), 3 deletions(-) create mode 100644 gcc/rust/checks/errors/borrowck/ffi-polonius/Cargo.toml create mode 100644 gcc/rust/checks/errors/borrowck/ffi-polonius/src/gccrs_ffi.rs create mode 100644 gcc/rust/checks/errors/borrowck/ffi-polonius/src/lib.rs create mode 100644 gcc/rust/checks/errors/borrowck/polonius/rust-polonius-ffi.h create mode 100644 gcc/rust/checks/errors/borrowck/polonius/rust-polonius.h diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in index facdd43fd518..8ccab85eb2f1 100644 --- a/gcc/rust/Make-lang.in +++ b/gcc/rust/Make-lang.in @@ -20,6 +20,7 @@ # This file provides the language dependent support in the main Makefile. + #RUST_EXES = rust # Use strict warnings for this front end. @@ -199,14 +200,19 @@ RUST_ALL_OBJS = $(GRS_OBJS) $(RUST_TARGET_OBJS) rust_OBJS = $(RUST_ALL_OBJS) rust/rustspec.o -RUST_LDFLAGS = $(LDFLAGS) -L./../libgrust/libproc_macro_internal -RUST_LIBDEPS = $(LIBDEPS) ../libgrust/libproc_macro_internal/libproc_macro_internal.a +# FIXME: Experiment - move to configure +CLANG_TARGET := $(shell echo "$(target)" | sed 's/-pc-/-unknown-/') +POLONIUS_LDFLAGS = $(shell cargo rustc --manifest-path $(srcdir)/rust/checks/errors/borrowck/ffi-polonius/Cargo.toml -q --message-format short -- --target $(CLANG_TARGET) --print=native-static-libs 2>&1 | grep "note: native-static-libs:" | sed 's/note: native-static-libs: //g') +RUST_LDFLAGS = $(LDFLAGS) -L./../libgrust/libproc_macro_internal $(POLONIUS_LDFLAGS) +RUST_LIBDEPS = $(LIBDEPS) ../libgrust/libproc_macro_internal/libproc_macro_internal.a rust/libffi_polonius.a # The compiler itself is called crab1 crab1$(exeext): $(RUST_ALL_OBJS) attribs.o $(BACKEND) $(RUST_LIBDEPS) $(rust.prev) @$(call LINK_PROGRESS,$(INDEX.rust),start) +$(LLINKER) $(ALL_LINKERFLAGS) $(RUST_LDFLAGS) -o $@ \ - $(RUST_ALL_OBJS) attribs.o $(BACKEND) $(LIBS) ../libgrust/libproc_macro_internal/libproc_macro_internal.a $(BACKENDLIBS) + $(RUST_ALL_OBJS) attribs.o $(BACKEND) \ + $(LIBS) ../libgrust/libproc_macro_internal/libproc_macro_internal.a rust/libffi_polonius.a \ + $(BACKENDLIBS) @$(call LINK_PROGRESS,$(INDEX.rust),end) # Build hooks. @@ -240,6 +246,7 @@ rust.srcman: # Clean hooks. rust.mostlyclean: + rm -rf rust/ffi-polonius/release libffi_polonius.a # cd $(srcdir)/rust; rm -f *.o y.tab.h y.tab.c lex.yy.c rust.clean: rust.mostlyclean @@ -461,3 +468,9 @@ rust/%.o: rust/checks/errors/borrowck/%.cc rust/%.o: rust/metadata/%.cc $(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $< $(POSTCOMPILE) + +rust/libffi_polonius.a: \ + rust/checks/errors/borrowck/ffi-polonius/Cargo.toml \ + $(wildcard $(srcdir)/rust/checks/errors/borrowck/ffi-polonius/src/*) + cargo build --manifest-path $(srcdir)/rust/checks/errors/borrowck/ffi-polonius/Cargo.toml --release --target-dir rust/ffi-polonius + cp rust/ffi-polonius/release/libffi_polonius.a rust/libffi_polonius.a \ No newline at end of file diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/Cargo.toml b/gcc/rust/checks/errors/borrowck/ffi-polonius/Cargo.toml new file mode 100644 index 000000000000..71315c3b635e --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/Cargo.toml @@ -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 diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/src/gccrs_ffi.rs b/gcc/rust/checks/errors/borrowck/ffi-polonius/src/gccrs_ffi.rs new file mode 100644 index 000000000000..4720150f8b03 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/src/gccrs_ffi.rs @@ -0,0 +1,115 @@ +// Copyright (C) 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++ +// ``` + +// GENERATED START ============================================================ + +use crate::GccrsAtom; + +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: u64, + 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>>, +} + +// GENERATED END ============================================================== + +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() + } +} diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/src/lib.rs b/gcc/rust/checks/errors/borrowck/ffi-polonius/src/lib.rs new file mode 100644 index 000000000000..4d3f6b6d75b1 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/src/lib.rs @@ -0,0 +1,96 @@ +// Copyright (C) 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; + +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(), + } + } +} + +/// 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); +} diff --git a/gcc/rust/checks/errors/borrowck/polonius/rust-polonius-ffi.h b/gcc/rust/checks/errors/borrowck/polonius/rust-polonius-ffi.h new file mode 100644 index 000000000000..75635804ccb8 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/polonius/rust-polonius-ffi.h @@ -0,0 +1,98 @@ +// Copyright (C) 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 + +// 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. + +#include <cstdint> + +namespace Rust { +namespace Polonius { + +using Origin = size_t; +using Loan = size_t; +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) + {} +}; + +template <typename T> struct Slice +{ + uint64_t len; + const T *const data; + + template <typename vector> + Slice (const vector &v) : len (v.size ()), data (v.data ()) + {} +}; + +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 diff --git a/gcc/rust/checks/errors/borrowck/polonius/rust-polonius.h b/gcc/rust/checks/errors/borrowck/polonius/rust-polonius.h new file mode 100644 index 000000000000..9959aeb99b61 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/polonius/rust-polonius.h @@ -0,0 +1,92 @@ +// Copyright (C) 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_H +#define RUST_POLONIUS_H + +// Interface to the Polonius borrow checker engine. +// See (https://github.com/rust-lang/polonius/blob/master/polonius-engine/) + +#include "rust-polonius-ffi.h" + +namespace Rust { +namespace Polonius { + +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}; + } +}; + +/** + * 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 */ diff --git a/gcc/rust/checks/errors/borrowck/rust-borrow-checker.cc b/gcc/rust/checks/errors/borrowck/rust-borrow-checker.cc index 0c952ad32fa5..9a1865f3a252 100644 --- a/gcc/rust/checks/errors/borrowck/rust-borrow-checker.cc +++ b/gcc/rust/checks/errors/borrowck/rust-borrow-checker.cc @@ -20,6 +20,7 @@ #include "rust-function-collector.h" #include "rust-bir-builder.h" #include "rust-bir-dump.h" +#include "polonius/rust-polonius.h" namespace Rust { namespace HIR { @@ -86,6 +87,9 @@ BorrowChecker::go (HIR::Crate &crate) dump_function_bir (filename, bir, func->get_function_name ().as_string ()); } + + Polonius::Facts facts; // Dummy facts for now. + Polonius::polonius_run (facts.freeze (), true); } for (auto closure ATTRIBUTE_UNUSED : collector.get_closures ())