From 153c8a9c6cef7b0b49d74c024b49405a394f3a4a Mon Sep 17 00:00:00 2001 From: Lofty Date: Wed, 23 Nov 2022 23:02:30 +0000 Subject: [PATCH] awooter: 'better' FFI API --- common/route/awooter.cc | 36 ++++++-- common/route/awooter/rust/src/lib.rs | 25 +++--- common/route/awooter/rust/src/npnr.rs | 117 +++++++++++++++++--------- 3 files changed, 120 insertions(+), 58 deletions(-) diff --git a/common/route/awooter.cc b/common/route/awooter.cc index 3ce596a4..329b6078 100644 --- a/common/route/awooter.cc +++ b/common/route/awooter.cc @@ -140,6 +140,24 @@ extern "C" { return wrap(ctx->getNetinfoSinkWire(net, *sink, n)); } + uint32_t npnr_context_nets_leak(const Context *const ctx, int **names, NetInfo ***nets) { + auto name_vec = std::vector{}; + auto nets_vec = std::vector{}; + for (auto& item : ctx->nets) { + name_vec.push_back(item.first.hash()); + nets_vec.push_back(item.second.get()); + } + name_vec.shrink_to_fit(); + nets_vec.shrink_to_fit(); + auto size = name_vec.size(); + *names = name_vec.data(); + *nets = nets_vec.data(); + // Yes, by placement-newing over `name_vec` and `nets_vec` we leak memory. + new (&name_vec) std::vector; + new (&nets_vec) std::vector; + return size; + } + // Yes, this is quadratic. It gets imported once and then never worried about again. // There are bigger fish to fry. int npnr_context_nets_key(const Context *const ctx, uint32_t n) { @@ -174,17 +192,17 @@ extern "C" { return &net->driver; } - PortRef* npnr_netinfo_users_value(NetInfo *const net, uint32_t n) { - if (net == nullptr) { - return nullptr; - } + uint32_t npnr_netinfo_users_leak(NetInfo *const net, PortRef ***users) { + auto x = std::vector{}; for (auto& item : net->users) { - if (n == 0) { - return &item; - } - n--; + x.push_back(&item); } - return nullptr; + x.shrink_to_fit(); + *users = x.data(); + auto size = x.size(); + // Yes, by placement-newing over `x` we leak memory. + new (&x) std::vector{}; + return size; } #ifdef ARCH_ECP5 diff --git a/common/route/awooter/rust/src/lib.rs b/common/route/awooter/rust/src/lib.rs index d65da2e1..803c37ca 100644 --- a/common/route/awooter/rust/src/lib.rs +++ b/common/route/awooter/rust/src/lib.rs @@ -212,14 +212,15 @@ fn route(ctx: &mut npnr::Context) -> bool { ctx.grid_dim_y() ); - let nets = ctx.net_iter().collect::>(); + let nets = npnr::Nets::new(ctx); log_info!("Found {} nets\n", nets.len()); let mut count = 0; - for (_name, net) in &nets { + for (name, net) in nets.iter() { let _src = ctx.source_wire(*net); let net = unsafe { net.as_mut().unwrap() }; - for user in net.users() { + let users = nets.users_by_name(*name).unwrap().iter(); + for user in users { count += ctx.sink_wires(net, user).count(); } } @@ -228,20 +229,24 @@ fn route(ctx: &mut npnr::Context) -> bool { let (name, net) = nets .iter() - .max_by_key(|(_name, net)| { + .max_by_key(|(name, net)| { let net = unsafe { net.as_mut().unwrap() }; if net.is_global() { 0 } else { - net.users() + nets.users_by_name(**name) + .unwrap() + .iter() .fold(0, |acc, sink| acc + ctx.sink_wires(net, sink).count()) } }) .unwrap(); let net = unsafe { net.as_mut().unwrap() }; - let count = net - .users() + let count = nets + .users_by_name(*name) + .unwrap() + .iter() .fold(0, |acc, sink| acc + ctx.sink_wires(net, sink).count()); log_info!( @@ -255,7 +260,7 @@ fn route(ctx: &mut npnr::Context) -> bool { let mut x1 = 0; let mut y1 = 0; - for sink in net.users() { + for sink in nets.users_by_name(*name).unwrap().iter() { let sink = unsafe { sink.as_ref().unwrap() }; let cell = sink.cell().unwrap(); x0 = x0.min(cell.location_x()); @@ -268,7 +273,7 @@ fn route(ctx: &mut npnr::Context) -> bool { let mut arcs = Vec::new(); - for (_name, net) in &nets { + for (name, net) in nets.iter() { let net = unsafe { net.as_mut().unwrap() }; let source = unsafe { net.driver().as_ref().unwrap() }; @@ -280,7 +285,7 @@ fn route(ctx: &mut npnr::Context) -> bool { let source_x = source_cell.location_x(); let source_y = source_cell.location_y(); - for sink in net.users() { + for sink in nets.users_by_name(*name).unwrap().iter() { let sink = unsafe { sink.as_ref().unwrap() }; let sink_x = sink.cell().unwrap().location_x(); let sink_y = sink.cell().unwrap().location_y(); diff --git a/common/route/awooter/rust/src/npnr.rs b/common/route/awooter/rust/src/npnr.rs index 1ceab4c4..15283b6e 100644 --- a/common/route/awooter/rust/src/npnr.rs +++ b/common/route/awooter/rust/src/npnr.rs @@ -1,4 +1,4 @@ -use std::ffi::CStr; +use std::{ffi::CStr, collections::{HashMap}, ptr::{NonNull}, marker::PhantomData}; use libc::c_char; @@ -39,10 +39,6 @@ impl NetInfo { unsafe { npnr_netinfo_driver(self) } } - pub fn users(&mut self) -> NetUserIter { - NetUserIter { net: self, n: 0 } - } - pub fn is_global(&self) -> bool { unsafe { npnr_netinfo_is_global(self) } } @@ -66,7 +62,7 @@ impl PortRef { } } -#[derive(Clone, Copy)] +#[derive(Clone, Copy, PartialEq, Eq, Hash)] #[repr(transparent)] pub struct IdString(libc::c_int); @@ -78,10 +74,13 @@ pub struct BelId { } impl BelId { + /// Return a sentinel value that represents an invalid bel. pub fn null() -> Self { + // SAFETY: BelId() has no safety requirements. unsafe { npnr_belid_null() } } + /// Check if this bel is invalid. pub fn is_null(self) -> bool { self == Self::null() } @@ -104,10 +103,13 @@ impl PipId { pub struct WireId(u64); impl WireId { + /// Return a sentinel value that represents an invalid wire. pub fn null() -> Self { + // SAFETY: WireId() has no safety requirements. unsafe { npnr_wireid_null() } } + /// Check if this wire is invalid. pub fn is_null(self) -> bool { self == Self::null() } @@ -217,10 +219,6 @@ impl Context { pub fn verbose(&self) -> bool { unsafe { npnr_context_verbose(self) } } - - pub fn net_iter(&self) -> NetIter { - NetIter { ctx: self, n: 0 } - } } extern "C" { @@ -273,11 +271,10 @@ extern "C" { n: u32, ) -> WireId; - fn npnr_context_nets_key(ctx: *const Context, n: u32) -> IdString; - fn npnr_context_nets_value(ctx: *const Context, n: u32) -> *mut NetInfo; + fn npnr_context_nets_leak(ctx: *const Context, names: *mut *mut libc::c_int, nets: *mut *mut *mut NetInfo) -> u32; fn npnr_netinfo_driver(net: *mut NetInfo) -> *mut PortRef; - fn npnr_netinfo_users_value(net: *mut NetInfo, n: u32) -> *mut PortRef; + fn npnr_netinfo_users_leak(net: *mut NetInfo, users: *mut *mut *mut PortRef) -> u32; fn npnr_netinfo_is_global(net: *const NetInfo) -> bool; fn npnr_portref_cell(port: *const PortRef) -> *mut CellInfo; @@ -285,47 +282,89 @@ extern "C" { fn npnr_cellinfo_get_location_y(info: *const CellInfo) -> libc::c_int; } -/// Iterate over the nets in a context. -/// -/// In case you missed the C++ comment; this is `O(n^2)` because FFI is misery. -/// It's probably best to run it exactly once. -pub struct NetIter<'a> { - ctx: &'a Context, - n: u32, +/// Store for the users of a net. +pub struct NetUsers<'a> { + users: NonNull<*mut PortRef>, + size: u32, + _data: PhantomData<&'a NetInfo>, } -impl<'a> Iterator for NetIter<'a> { - type Item = (IdString, *mut NetInfo); +impl<'a> NetUsers<'a> { + pub fn new(net: &'a mut NetInfo) -> NetUsers<'a> { + let mut users = 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 size = unsafe { npnr_netinfo_users_leak(net, &mut users as *mut *mut *mut PortRef) }; + let users = unsafe { NonNull::new_unchecked(users) }; + Self { users, size, _data: PhantomData } + } - fn next(&mut self) -> Option { - let str = unsafe { npnr_context_nets_key(self.ctx, self.n) }; - let val = unsafe { npnr_context_nets_value(self.ctx, self.n) }; - if val.is_null() { - return None; - } - self.n += 1; - Some((str, val)) + pub fn iter(&self) -> NetUsersIter<'_> { + NetUsersIter { users: self, n: 0 } } } -/// Iterate over the users field of a net. -/// -/// In case you missed the C++ comment; this is `O(n^2)` because FFI is misery. -pub struct NetUserIter { - net: *mut NetInfo, +pub struct NetUsersIter<'a> { + users: &'a NetUsers<'a>, n: u32, } -impl Iterator for NetUserIter { +impl Iterator for NetUsersIter<'_> { type Item = *mut PortRef; fn next(&mut self) -> Option { - let item = unsafe { npnr_netinfo_users_value(self.net, self.n) }; - if item.is_null() { + if self.n >= self.users.size { return None; } + let user = unsafe { *self.users.users.as_ptr().add(self.n as usize) }; self.n += 1; - Some(item) + Some(user) + } +} + +/// Store for the nets of a context. +pub struct Nets<'a> { + nets: HashMap, + users: HashMap>, + _data: PhantomData<&'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. + pub fn new(ctx: &'a Context) -> Nets<'a> { + 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, &mut names as *mut *mut libc::c_int, &mut nets_ptr as *mut *mut *mut NetInfo) }; + let mut nets = HashMap::new(); + let mut users = HashMap::new(); + for i in 0..size { + let name = unsafe { IdString(*names.add(i as usize)) }; + let net = unsafe { *nets_ptr.add(i as usize) }; + nets.insert(name, net); + users.insert(name, NetUsers::new(unsafe { &mut *net })); + } + // Note: the contents of `names` and `nets_ptr` are now lost. + Self { + nets, + users, + _data: PhantomData, + } + } + + /// Find net users given a net's name. + pub fn users_by_name(&self, net: IdString) -> Option<&NetUsers> { + self.users.get(&net) + } + + /// Return the number of nets in the store. + pub fn len(&self) -> usize { + self.nets.len() + } + + pub fn iter(&self) -> impl Iterator { + self.nets.iter() } }