From 0ee818173384f32da152db2d542fbb7aba3a3862 Mon Sep 17 00:00:00 2001 From: Lofty Date: Thu, 25 Sep 2025 09:09:55 +0100 Subject: [PATCH] rust: rework ownership model --- rust/example_printnets/src/lib.rs | 7 +- rust/nextpnr/src/lib.rs | 188 +++++++++++++++++------------- rust/rust.cc | 44 ++++--- 3 files changed, 134 insertions(+), 105 deletions(-) diff --git a/rust/example_printnets/src/lib.rs b/rust/example_printnets/src/lib.rs index 7a21af50..83ced774 100644 --- a/rust/example_printnets/src/lib.rs +++ b/rust/example_printnets/src/lib.rs @@ -1,12 +1,9 @@ -use nextpnr::{Context, Nets}; +use nextpnr::Context; #[no_mangle] pub extern "C" fn rust_example_printnets(ctx: &mut Context) { - let nets = Nets::new(ctx); - let nets_vec = nets.to_vec(); - println!("Nets in design:"); - for (&name, _net) in nets_vec { + for (name, _net) in &ctx.nets() { println!(" {}", ctx.name_of(name).to_str().unwrap()); } } diff --git a/rust/nextpnr/src/lib.rs b/rust/nextpnr/src/lib.rs index e8bae386..719a860c 100644 --- a/rust/nextpnr/src/lib.rs +++ b/rust/nextpnr/src/lib.rs @@ -1,5 +1,4 @@ -use core::slice; -use std::{collections::HashMap, ffi::CStr, marker::PhantomData, sync::Mutex}; +use std::{ffi::CStr, marker::PhantomData, sync::Mutex}; use libc::c_char; @@ -392,6 +391,11 @@ impl Context { pub fn verbose(&self) -> bool { unsafe { npnr_context_verbose(self) } } + + #[must_use] + pub fn nets(&self) -> Nets<'_> { + Nets { ctx: self } + } } unsafe extern "C" { @@ -455,12 +459,6 @@ unsafe extern "C" { n: u32, ) -> WireId; - fn npnr_context_nets_leak( - ctx: &Context, - names: *mut *mut libc::c_int, - nets: *mut *mut *mut NetInfo, - ) -> u32; - fn npnr_netinfo_driver(net: &mut NetInfo) -> Option<&mut PortRef>; fn npnr_netinfo_users_leak(net: &NetInfo, users: *mut *mut *const PortRef) -> u32; fn npnr_netinfo_is_global(net: &NetInfo) -> bool; @@ -499,94 +497,54 @@ unsafe extern "C" { fn npnr_inc_wire_iter(iter: &mut RawWireIter); fn npnr_deref_wire_iter(iter: &mut RawWireIter) -> WireId; fn npnr_is_wire_iter_done(iter: &mut RawWireIter) -> bool; + + fn npnr_context_net_iter(ctx: &Context) -> &mut RawNetIter; + fn npnr_delete_net_iter(iter: &mut RawNetIter); + fn npnr_inc_net_iter(iter: &mut RawNetIter); + fn npnr_deref_net_iter_first(iter: &RawNetIter) -> libc::c_int; + fn npnr_deref_net_iter_second(iter: &RawNetIter) -> &mut NetInfo; + fn npnr_is_net_iter_done(iter: &RawNetIter) -> bool; + + fn npnr_context_cell_iter(ctx: &Context) -> &mut RawCellIter; + fn npnr_delete_cell_iter(iter: &mut RawCellIter); + fn npnr_inc_cell_iter(iter: &mut RawCellIter); + fn npnr_deref_cell_iter_first(iter: &mut RawCellIter) -> libc::c_int; + fn npnr_deref_cell_iter_second(iter: &mut RawCellIter) -> &mut CellInfo; + fn npnr_is_cell_iter_done(iter: &mut RawCellIter) -> bool; } /// Store for the nets of a context. pub struct Nets<'a> { - nets: HashMap, - users: HashMap, - index_to_net: Vec, - _data: PhantomData<&'a Context>, + ctx: &'a Context, } -impl<'a> Nets<'a> { - /// Create a new store for the nets of a context. - /// - /// Note that this leaks memory created by nextpnr; the intention is this is called once. - #[must_use] - pub fn new(ctx: &'a Context) -> Self { - let mut names: *mut libc::c_int = std::ptr::null_mut(); - let mut nets_ptr: *mut *mut NetInfo = std::ptr::null_mut(); - let size = unsafe { - npnr_context_nets_leak( - ctx, - &raw mut names, - &raw mut nets_ptr, - ) - }; - let mut nets = HashMap::new(); - let mut users = HashMap::new(); - let mut index_to_net = Vec::new(); - for i in 0..size { - let name = unsafe { IdString(*names.add(i as usize)) }; - let net = unsafe { &mut **nets_ptr.add(i as usize) }; - let mut users_ptr = std::ptr::null_mut(); - // SAFETY: net is not null because it's a &mut, and users is only written to. - // Leaking memory is the most convenient FFI I could think of. - let len = - unsafe { npnr_netinfo_users_leak(net, &raw mut users_ptr) }; - let users_slice = - unsafe { slice::from_raw_parts(users_ptr.cast::<&PortRef>(), len as usize) }; - let index = index_to_net.len() as i32; - index_to_net.push(name); - unsafe { - npnr_netinfo_udata_set(net, NetIndex(index)); - } - nets.insert(name, net); - users.insert(name, users_slice); - } - // Note: the contents of `names` and `nets_ptr` are now lost. - Self { - nets, - users, - index_to_net, - _data: PhantomData, +impl<'a> IntoIterator for &'a Nets<'a> { + type Item = (IdString, *const NetInfo); + + type IntoIter = NetIter<'a>; + + fn into_iter(self) -> Self::IntoIter { + NetIter { + iter: unsafe { npnr_context_net_iter(self.ctx) }, + phantom_data: PhantomData, } } +} - /// Find net users given a net's name. - #[must_use] - pub fn users_by_name(&self, net: IdString) -> Option<&&[&PortRef]> { - self.users.get(&net) - } +pub struct Cells<'a> { + ctx: &'a Context, +} - /// Return the number of nets in the store. - #[must_use] - pub fn len(&self) -> usize { - self.nets.len() - } +impl<'a> IntoIterator for &'a Cells<'a> { + type Item = (IdString, *const CellInfo); - #[must_use] - pub fn is_empty(&self) -> bool { - self.nets.len() == 0 - } + type IntoIter = CellIter<'a>; - #[must_use] - pub fn name_from_index(&self, index: NetIndex) -> IdString { - self.index_to_net[index.0 as usize] - } - - #[must_use] - pub fn net_from_index(&self, index: NetIndex) -> &NetInfo { - self.nets.get(&self.name_from_index(index)).unwrap() - } - - #[must_use] - pub fn to_vec(&self) -> Vec<(&IdString, &&mut NetInfo)> { - let mut v = Vec::new(); - v.extend(self.nets.iter()); - v.sort_by_key(|(name, _net)| name.0); - v + fn into_iter(self) -> Self::IntoIter { + CellIter { + iter: unsafe { npnr_context_cell_iter(self.ctx) }, + phantom_data: PhantomData, + } } } @@ -761,6 +719,68 @@ impl Drop for WireIter<'_> { } } +#[repr(C)] +struct RawNetIter { + content: [u8; 0], +} + +pub struct NetIter<'a> { + iter: &'a mut RawNetIter, + phantom_data: PhantomData<&'a NetInfo>, +} + +impl Iterator for NetIter<'_> { + type Item = (IdString, *const NetInfo); + + fn next(&mut self) -> Option { + if unsafe { npnr_is_net_iter_done(self.iter) } { + None + } else { + let s = IdString(unsafe { npnr_deref_net_iter_first(self.iter) }); + let net = unsafe { &raw const *npnr_deref_net_iter_second(self.iter) }; + unsafe { npnr_inc_net_iter(self.iter) }; + Some((s, net)) + } + } +} + +impl Drop for NetIter<'_> { + fn drop(&mut self) { + unsafe { npnr_delete_net_iter(self.iter) }; + } +} + +#[repr(C)] +struct RawCellIter { + content: [u8; 0], +} + +pub struct CellIter<'a> { + iter: &'a mut RawCellIter, + phantom_data: PhantomData<&'a CellInfo>, +} + +impl Iterator for CellIter<'_> { + type Item = (IdString, *const CellInfo); + + fn next(&mut self) -> Option { + if unsafe { npnr_is_cell_iter_done(self.iter) } { + None + } else { + let s = IdString(unsafe { npnr_deref_cell_iter_first(self.iter) }); + let cell = unsafe { &raw const *npnr_deref_cell_iter_second(self.iter) }; + unsafe { npnr_inc_cell_iter(self.iter) }; + Some((s, cell)) + } + } +} + +impl Drop for CellIter<'_> { + fn drop(&mut self) { + unsafe { npnr_delete_cell_iter(self.iter) }; + } +} + macro_rules! log_info { ($($t:tt)*) => { let s = std::ffi::CString::new(format!($($t)*)).unwrap(); diff --git a/rust/rust.cc b/rust/rust.cc index 7531353d..804c11f7 100644 --- a/rust/rust.cc +++ b/rust/rust.cc @@ -17,7 +17,8 @@ */ #include -#include +#include "arch.h" +#include "context.h" #include "log.h" #include "nextpnr.h" @@ -78,6 +79,12 @@ using PipIterWrapper = IterWrapper; using WireIter = decltype(Context(ArchArgs()).getWires().begin()); using WireIterWrapper = IterWrapper; +using NetIter = decltype(Context(ArchArgs()).nets.begin()); +using NetIterWrapper = IterWrapper; + +using CellIter = decltype(Context(ArchArgs()).cells.begin()); +using CellIterWrapper = IterWrapper; + extern "C" { USING_NEXTPNR_NAMESPACE; @@ -150,21 +157,6 @@ uint64_t npnr_context_get_netinfo_sink_wire(const Context *ctx, const NetInfo *n return wrap(ctx->getNetinfoSinkWire(net, *sink, n)); } -uint32_t npnr_context_nets_leak(const Context *ctx, int **names, NetInfo ***nets) -{ - auto size = ctx->nets.size(); - *names = new int[size]; - *nets = new NetInfo *[size]; - auto idx = 0; - for (auto &item : ctx->nets) { - (*names)[idx] = item.first.index; - (*nets)[idx] = item.second.get(); - idx++; - } - // Yes, by never deleting `names` and `nets` we leak memory. - return size; -} - DownhillIterWrapper *npnr_context_get_pips_downhill(Context *ctx, uint64_t wire_id) { auto wire = unwrap_wire(wire_id); @@ -217,6 +209,26 @@ void npnr_inc_wire_iter(WireIterWrapper *iter) { ++iter->current; } uint64_t npnr_deref_wire_iter(WireIterWrapper *iter) { return wrap(*iter->current); } bool npnr_is_wire_iter_done(WireIterWrapper *iter) { return !(iter->current != iter->end); } +NetIterWrapper *npnr_context_net_iter(Context *ctx) +{ + return new NetIterWrapper(ctx->nets.begin(), ctx->nets.end()); +} +void npnr_delete_net_iter(NetIterWrapper *iter) { delete iter; } +void npnr_inc_net_iter(NetIterWrapper *iter) { ++iter->current; } +int npnr_deref_net_iter_first(NetIterWrapper *iter) { return wrap(iter->current->first.index); } +NetInfo *npnr_deref_net_iter_second(NetIterWrapper *iter) { return iter->current->second.get(); } +bool npnr_is_net_iter_done(NetIterWrapper *iter) { return !(iter->current != iter->end); } + +CellIterWrapper *npnr_context_cell_iter(Context *ctx) +{ + return new CellIterWrapper(ctx->cells.begin(), ctx->cells.end()); +} +void npnr_delete_cell_iter(CellIterWrapper *iter) { delete iter; } +void npnr_inc_cell_iter(CellIterWrapper *iter) { ++iter->current; } +int npnr_deref_cell_iter_first(CellIterWrapper *iter) { return wrap(iter->current->first.index); } +CellInfo *npnr_deref_cell_iter_second(CellIterWrapper *iter) { return iter->current->second.get(); } +bool npnr_is_cell_iter_done(CellIterWrapper *iter) { return !(iter->current != iter->end); } + PortRef *npnr_netinfo_driver(NetInfo *net) { if (net == nullptr) {