Merge pull request #614 from ShoyuVanilla/issue-600
Some checks failed
Book / Book (push) Has been cancelled
Test / Test (push) Has been cancelled
Test / Miri (push) Has been cancelled
Test / Benchmarks (push) Has been cancelled
Book / Deploy (push) Has been cancelled

Assign memo ingredients per salsa-struct-ingredient
This commit is contained in:
David Barsky 2024-12-18 16:21:47 +00:00 committed by GitHub
commit 0ac5c1c984
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 156 additions and 26 deletions

View file

@ -124,6 +124,9 @@ macro_rules! setup_input_struct {
} }
impl $zalsa::SalsaStructInDb for $Struct { impl $zalsa::SalsaStructInDb for $Struct {
fn lookup_ingredient_index(aux: &dyn $zalsa::JarAux) -> core::option::Option<$zalsa::IngredientIndex> {
aux.lookup_jar_by_type(&<$zalsa_struct::JarImpl<$Configuration>>::default())
}
} }
impl $Struct { impl $Struct {

View file

@ -141,6 +141,9 @@ macro_rules! setup_interned_struct {
} }
impl $zalsa::SalsaStructInDb for $Struct<'_> { impl $zalsa::SalsaStructInDb for $Struct<'_> {
fn lookup_ingredient_index(aux: &dyn $zalsa::JarAux) -> core::option::Option<$zalsa::IngredientIndex> {
aux.lookup_jar_by_type(&<$zalsa_struct::JarImpl<$Configuration>>::default())
}
} }
unsafe impl $zalsa::Update for $Struct<'_> { unsafe impl $zalsa::Update for $Struct<'_> {

View file

@ -99,6 +99,9 @@ macro_rules! setup_tracked_fn {
$zalsa::IngredientCache::new(); $zalsa::IngredientCache::new();
impl $zalsa::SalsaStructInDb for $InternedData<'_> { impl $zalsa::SalsaStructInDb for $InternedData<'_> {
fn lookup_ingredient_index(_aux: &dyn $zalsa::JarAux) -> core::option::Option<$zalsa::IngredientIndex> {
None
}
} }
impl $zalsa::interned::Configuration for $Configuration { impl $zalsa::interned::Configuration for $Configuration {
@ -199,7 +202,19 @@ macro_rules! setup_tracked_fn {
aux: &dyn $zalsa::JarAux, aux: &dyn $zalsa::JarAux,
first_index: $zalsa::IngredientIndex, first_index: $zalsa::IngredientIndex,
) -> Vec<Box<dyn $zalsa::Ingredient>> { ) -> Vec<Box<dyn $zalsa::Ingredient>> {
let struct_index = $zalsa::macro_if! {
if $needs_interner {
first_index.successor(0)
} else {
<$InternedData as $zalsa::SalsaStructInDb>::lookup_ingredient_index(aux)
.expect(
"Salsa struct is passed as an argument of a tracked function, but its ingredient hasn't been added!"
)
}
};
let fn_ingredient = <$zalsa::function::IngredientImpl<$Configuration>>::new( let fn_ingredient = <$zalsa::function::IngredientImpl<$Configuration>>::new(
struct_index,
first_index, first_index,
aux, aux,
); );
@ -219,6 +234,10 @@ macro_rules! setup_tracked_fn {
} }
} }
} }
fn salsa_struct_type_id(&self) -> Option<core::any::TypeId> {
None
}
} }
#[allow(non_local_definitions)] #[allow(non_local_definitions)]

View file

@ -152,6 +152,9 @@ macro_rules! setup_tracked_struct {
} }
impl $zalsa::SalsaStructInDb for $Struct<'_> { impl $zalsa::SalsaStructInDb for $Struct<'_> {
fn lookup_ingredient_index(aux: &dyn $zalsa::JarAux) -> core::option::Option<$zalsa::IngredientIndex> {
aux.lookup_jar_by_type(&<$zalsa_struct::JarImpl<$Configuration>>::default())
}
} }
impl $zalsa::TrackedStructInDb for $Struct<'_> { impl $zalsa::TrackedStructInDb for $Struct<'_> {

View file

@ -53,6 +53,10 @@ impl<A: Accumulator> Jar for JarImpl<A> {
) -> Vec<Box<dyn Ingredient>> { ) -> Vec<Box<dyn Ingredient>> {
vec![Box::new(<IngredientImpl<A>>::new(first_index))] vec![Box::new(<IngredientImpl<A>>::new(first_index))]
} }
fn salsa_struct_type_id(&self) -> Option<std::any::TypeId> {
None
}
} }
pub struct IngredientImpl<A: Accumulator> { pub struct IngredientImpl<A: Accumulator> {

View file

@ -126,10 +126,10 @@ impl<C> IngredientImpl<C>
where where
C: Configuration, C: Configuration,
{ {
pub fn new(index: IngredientIndex, aux: &dyn JarAux) -> Self { pub fn new(struct_index: IngredientIndex, index: IngredientIndex, aux: &dyn JarAux) -> Self {
Self { Self {
index, index,
memo_ingredient_index: aux.next_memo_ingredient_index(index), memo_ingredient_index: aux.next_memo_ingredient_index(struct_index, index),
lru: Default::default(), lru: Default::default(),
deleted_entries: Default::default(), deleted_entries: Default::default(),
} }

View file

@ -23,10 +23,33 @@ pub trait Jar: Any {
aux: &dyn JarAux, aux: &dyn JarAux,
first_index: IngredientIndex, first_index: IngredientIndex,
) -> Vec<Box<dyn Ingredient>>; ) -> Vec<Box<dyn Ingredient>>;
/// If this jar's first ingredient is a salsa struct, return its `TypeId`
fn salsa_struct_type_id(&self) -> Option<TypeId>;
} }
/// Methods on the Salsa database available to jars while they are creating their ingredients.
pub trait JarAux { pub trait JarAux {
fn next_memo_ingredient_index(&self, ingredient_index: IngredientIndex) -> MemoIngredientIndex; /// Return index of first ingredient from `jar` (based on the dynamic type of `jar`).
/// Returns `None` if the jar has not yet been added.
/// Used by tracked functions to lookup the ingredient index for the salsa struct they take as argument.
fn lookup_jar_by_type(&self, jar: &dyn Jar) -> Option<IngredientIndex>;
/// Returns the memo ingredient index that should be used to attach data from the given tracked function
/// to the given salsa struct (which the fn accepts as argument).
///
/// The memo ingredient indices for a given function must be distinct from the memo indices
/// of all other functions that take the same salsa struct.
///
/// # Parameters
///
/// * `struct_ingredient_index`, the index of the salsa struct the memo will be attached to
/// * `ingredient_index`, the index of the tracked function whose data is stored in the memo
fn next_memo_ingredient_index(
&self,
struct_ingredient_index: IngredientIndex,
ingredient_index: IngredientIndex,
) -> MemoIngredientIndex;
} }
pub trait Ingredient: Any + std::fmt::Debug + Send + Sync { pub trait Ingredient: Any + std::fmt::Debug + Send + Sync {

View file

@ -1,4 +1,8 @@
use std::{any::Any, fmt, ops::DerefMut}; use std::{
any::{Any, TypeId},
fmt,
ops::DerefMut,
};
pub mod input_field; pub mod input_field;
pub mod setter; pub mod setter;
@ -61,6 +65,10 @@ impl<C: Configuration> Jar for JarImpl<C> {
})) }))
.collect() .collect()
} }
fn salsa_struct_type_id(&self) -> Option<std::any::TypeId> {
Some(TypeId::of::<<C as Configuration>::Struct>())
}
} }
pub struct IngredientImpl<C: Configuration> { pub struct IngredientImpl<C: Configuration> {

View file

@ -10,6 +10,7 @@ use crate::table::Slot;
use crate::zalsa::IngredientIndex; use crate::zalsa::IngredientIndex;
use crate::zalsa_local::QueryOrigin; use crate::zalsa_local::QueryOrigin;
use crate::{Database, DatabaseKeyIndex, Id}; use crate::{Database, DatabaseKeyIndex, Id};
use std::any::TypeId;
use std::fmt; use std::fmt;
use std::hash::{BuildHasher, Hash, Hasher}; use std::hash::{BuildHasher, Hash, Hasher};
use std::marker::PhantomData; use std::marker::PhantomData;
@ -93,6 +94,10 @@ impl<C: Configuration> Jar for JarImpl<C> {
) -> Vec<Box<dyn Ingredient>> { ) -> Vec<Box<dyn Ingredient>> {
vec![Box::new(IngredientImpl::<C>::new(first_index)) as _] vec![Box::new(IngredientImpl::<C>::new(first_index)) as _]
} }
fn salsa_struct_type_id(&self) -> Option<std::any::TypeId> {
Some(TypeId::of::<<C as Configuration>::Struct<'static>>())
}
} }
impl<C> IngredientImpl<C> impl<C> IngredientImpl<C>

View file

@ -1 +1,5 @@
pub trait SalsaStructInDb {} use crate::{plumbing::JarAux, IngredientIndex};
pub trait SalsaStructInDb {
fn lookup_ingredient_index(aux: &dyn JarAux) -> Option<IngredientIndex>;
}

View file

@ -1,4 +1,4 @@
use std::{fmt, hash::Hash, marker::PhantomData, ops::DerefMut}; use std::{any::TypeId, fmt, hash::Hash, marker::PhantomData, ops::DerefMut};
use crossbeam::{atomic::AtomicCell, queue::SegQueue}; use crossbeam::{atomic::AtomicCell, queue::SegQueue};
use tracked_field::FieldIngredientImpl; use tracked_field::FieldIngredientImpl;
@ -113,6 +113,10 @@ impl<C: Configuration> Jar for JarImpl<C> {
})) }))
.collect() .collect()
} }
fn salsa_struct_type_id(&self) -> Option<TypeId> {
Some(TypeId::of::<<C as Configuration>::Struct<'static>>())
}
} }
pub trait TrackedStructInDb: SalsaStructInDb { pub trait TrackedStructInDb: SalsaStructInDb {
@ -502,7 +506,8 @@ where
// and the code that references the memo-table has a read-lock. // and the code that references the memo-table has a read-lock.
let memo_table = unsafe { (*data).take_memo_table() }; let memo_table = unsafe { (*data).take_memo_table() };
for (memo_ingredient_index, memo) in memo_table.into_memos() { for (memo_ingredient_index, memo) in memo_table.into_memos() {
let ingredient_index = zalsa.ingredient_index_for_memo(memo_ingredient_index); let ingredient_index =
zalsa.ingredient_index_for_memo(self.ingredient_index, memo_ingredient_index);
let executor = DatabaseKeyIndex { let executor = DatabaseKeyIndex {
ingredient_index, ingredient_index,

View file

@ -1,5 +1,5 @@
use append_only_vec::AppendOnlyVec; use append_only_vec::AppendOnlyVec;
use parking_lot::Mutex; use parking_lot::{Mutex, RwLock};
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use std::any::{Any, TypeId}; use std::any::{Any, TypeId};
use std::marker::PhantomData; use std::marker::PhantomData;
@ -123,8 +123,10 @@ pub struct Zalsa {
nonce: Nonce<StorageNonce>, nonce: Nonce<StorageNonce>,
/// Number of memo ingredient indices created by calls to [`next_memo_ingredient_index`](`Self::next_memo_ingredient_index`) /// Map from the [`IngredientIndex::as_usize`][] of a salsa struct to a list of
memo_ingredients: Mutex<Vec<IngredientIndex>>, /// [ingredient-indices](`IngredientIndex`) for tracked functions that have this salsa struct
/// as input.
memo_ingredient_indices: RwLock<Vec<Vec<IngredientIndex>>>,
/// Map from the type-id of an `impl Jar` to the index of its first ingredient. /// Map from the type-id of an `impl Jar` to the index of its first ingredient.
/// This is using a `Mutex<FxHashMap>` (versus, say, a `FxDashMap`) /// This is using a `Mutex<FxHashMap>` (versus, say, a `FxDashMap`)
@ -156,7 +158,7 @@ impl Zalsa {
ingredients_vec: AppendOnlyVec::new(), ingredients_vec: AppendOnlyVec::new(),
ingredients_requiring_reset: AppendOnlyVec::new(), ingredients_requiring_reset: AppendOnlyVec::new(),
runtime: Runtime::default(), runtime: Runtime::default(),
memo_ingredients: Default::default(), memo_ingredient_indices: Default::default(),
} }
} }
@ -190,11 +192,20 @@ impl Zalsa {
{ {
let jar_type_id = jar.type_id(); let jar_type_id = jar.type_id();
let mut jar_map = self.jar_map.lock(); let mut jar_map = self.jar_map.lock();
*jar_map let mut should_create = false;
.entry(jar_type_id) // First record the index we will use into the map and then go and create the ingredients.
.or_insert_with(|| { // Those ingredients may invoke methods on the `JarAux` trait that read from this map
let index = IngredientIndex::from(self.ingredients_vec.len()); // to lookup ingredient indices for already created jars.
let ingredients = jar.create_ingredients(self, index); //
// Note that we still hold the lock above so only one jar is being created at a time and hence
// ingredient indices cannot overlap.
let index = *jar_map.entry(jar_type_id).or_insert_with(|| {
should_create = true;
IngredientIndex::from(self.ingredients_vec.len())
});
if should_create {
let aux = JarAuxImpl(self, &jar_map);
let ingredients = jar.create_ingredients(&aux, index);
for ingredient in ingredients { for ingredient in ingredients {
let expected_index = ingredient.ingredient_index(); let expected_index = ingredient.ingredient_index();
@ -202,9 +213,7 @@ impl Zalsa {
self.ingredients_requiring_reset.push(expected_index); self.ingredients_requiring_reset.push(expected_index);
} }
let actual_index = self let actual_index = self.ingredients_vec.push(ingredient);
.ingredients_vec
.push(ingredient);
assert_eq!( assert_eq!(
expected_index.as_usize(), expected_index.as_usize(),
actual_index, actual_index,
@ -213,10 +222,10 @@ impl Zalsa {
expected_index, expected_index,
actual_index, actual_index,
); );
} }
index }
})
index
} }
} }
@ -294,15 +303,34 @@ impl Zalsa {
pub(crate) fn ingredient_index_for_memo( pub(crate) fn ingredient_index_for_memo(
&self, &self,
struct_ingredient_index: IngredientIndex,
memo_ingredient_index: MemoIngredientIndex, memo_ingredient_index: MemoIngredientIndex,
) -> IngredientIndex { ) -> IngredientIndex {
self.memo_ingredients.lock()[memo_ingredient_index.as_usize()] self.memo_ingredient_indices.read()[struct_ingredient_index.as_usize()]
[memo_ingredient_index.as_usize()]
} }
} }
impl JarAux for Zalsa { struct JarAuxImpl<'a>(&'a Zalsa, &'a FxHashMap<TypeId, IngredientIndex>);
fn next_memo_ingredient_index(&self, ingredient_index: IngredientIndex) -> MemoIngredientIndex {
let mut memo_ingredients = self.memo_ingredients.lock(); impl JarAux for JarAuxImpl<'_> {
fn lookup_jar_by_type(&self, jar: &dyn Jar) -> Option<IngredientIndex> {
self.1.get(&jar.type_id()).map(ToOwned::to_owned)
}
fn next_memo_ingredient_index(
&self,
struct_ingredient_index: IngredientIndex,
ingredient_index: IngredientIndex,
) -> MemoIngredientIndex {
let mut memo_ingredients = self.0.memo_ingredient_indices.write();
let idx = struct_ingredient_index.as_usize();
let memo_ingredients = if let Some(memo_ingredients) = memo_ingredients.get_mut(idx) {
memo_ingredients
} else {
memo_ingredients.resize_with(idx + 1, Vec::new);
memo_ingredients.get_mut(idx).unwrap()
};
let mi = MemoIngredientIndex(u32::try_from(memo_ingredients.len()).unwrap()); let mi = MemoIngredientIndex(u32::try_from(memo_ingredients.len()).unwrap());
memo_ingredients.push(ingredient_index); memo_ingredients.push(ingredient_index);
mi mi

View file

@ -0,0 +1,25 @@
//! Test that a `tracked` fn on multiple salsa struct args
//! compiles and executes successfully.
#[salsa::input]
struct MyInput {
field: u32,
}
#[salsa::interned]
struct MyInterned<'db> {
field: u32,
}
#[salsa::tracked]
fn tracked_fn<'db>(db: &'db dyn salsa::Database, input: MyInput, interned: MyInterned<'db>) -> u32 {
input.field(db) + interned.field(db)
}
#[test]
fn execute() {
let db = salsa::DatabaseImpl::new();
let input = MyInput::new(&db, 22);
let interned = MyInterned::new(&db, 33);
assert_eq!(tracked_fn(&db, input, interned), 55);
}