Skip to content

Commit bf9273a

Browse files
authored
cranelift-frontend: Propagate needs-stack-map from variables to values during finalization (bytecodealliance#10468)
* cranelift-frontend: Propagate needs-stack-map from variables to values during finalization Rather than trying to propagate the needs-stack-map bit from variables to values online, while we are building the IR and defining and using variables, wait until the function is being finalized, and then propagate everything all at once. This avoids potential repeated work and is easier to ensure that it is complete and covers all values associated with a variable, since by the time we are finalizing the function, we won't add any new values for a variable that we will need to keep track of and propagate this information to. This also means that we can remove the `params_added_to_blocks` vector from the SSA side effects structure, since it was only used to online-update the `stack_map_values` set. * Initialize the env-logger in `#[wasmtime_test]` * Fix needs-stack-map set iteration For reasons I do not understand, the `EntitySet::keys` method includes keys that are not in the set, and we have unit tests asserting this bizarre behavior. Very perplexing. So I added a new method to iterate over just the elements of the set.
1 parent 79f9e06 commit bf9273a

File tree

6 files changed

+91
-33
lines changed

6 files changed

+91
-33
lines changed

cranelift/entity/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ pub use self::keys::Keys;
287287
pub use self::list::{EntityList, ListPool};
288288
pub use self::map::SecondaryMap;
289289
pub use self::primary::PrimaryMap;
290-
pub use self::set::EntitySet;
290+
pub use self::set::{EntitySet, SetIter};
291291
pub use self::signed::Signed;
292292
pub use self::sparse::{SparseMap, SparseMapValue, SparseSet};
293293
pub use self::unsigned::Unsigned;

cranelift/entity/src/set.rs

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,56 @@ where
8989
self.bitset.clear()
9090
}
9191

92-
/// Iterate over all the keys in this set.
92+
/// Iterate over all the keys up to the maximum in this set.
93+
///
94+
/// This will yield intermediate keys on the way up to the max key, even if
95+
/// they are not contained within the set.
96+
///
97+
/// ```
98+
/// use cranelift_entity::{entity_impl, EntityRef, EntitySet};
99+
///
100+
/// #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
101+
/// struct Entity(u32);
102+
/// entity_impl!(Entity);
103+
///
104+
/// let mut set = EntitySet::new();
105+
/// set.insert(Entity::new(2));
106+
///
107+
/// let mut keys = set.keys();
108+
/// assert_eq!(keys.next(), Some(Entity::new(0)));
109+
/// assert_eq!(keys.next(), Some(Entity::new(1)));
110+
/// assert_eq!(keys.next(), Some(Entity::new(2)));
111+
/// assert!(keys.next().is_none());
112+
/// ```
93113
pub fn keys(&self) -> Keys<K> {
94114
Keys::with_len(self.bitset.max().map_or(0, |x| x + 1))
95115
}
96116

117+
/// Iterate over the elements of this set.
118+
///
119+
/// ```
120+
/// use cranelift_entity::{entity_impl, EntityRef, EntitySet};
121+
///
122+
/// #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
123+
/// struct Entity(u32);
124+
/// entity_impl!(Entity);
125+
///
126+
/// let mut set = EntitySet::new();
127+
/// set.insert(Entity::new(2));
128+
/// set.insert(Entity::new(3));
129+
///
130+
/// let mut iter = set.iter();
131+
/// assert_eq!(iter.next(), Some(Entity::new(2)));
132+
/// assert_eq!(iter.next(), Some(Entity::new(3)));
133+
/// assert!(iter.next().is_none());
134+
/// ```
135+
pub fn iter(&self) -> SetIter<K> {
136+
SetIter {
137+
inner: self.bitset.iter(),
138+
_phantom: PhantomData,
139+
}
140+
}
141+
97142
/// Insert the element at `k`.
98143
///
99144
/// Returns `true` if `k` was not present in the set, i.e. this is a
@@ -110,6 +155,25 @@ where
110155
}
111156
}
112157

158+
/// An iterator over the elements in an `EntitySet`.
159+
pub struct SetIter<'a, K> {
160+
inner: cranelift_bitset::compound::Iter<'a>,
161+
_phantom: PhantomData<K>,
162+
}
163+
164+
impl<K> Iterator for SetIter<'_, K>
165+
where
166+
K: EntityRef,
167+
{
168+
type Item = K;
169+
170+
#[inline]
171+
fn next(&mut self) -> Option<Self::Item> {
172+
let k = self.inner.next()?;
173+
Some(K::new(k))
174+
}
175+
}
176+
113177
#[cfg(test)]
114178
mod tests {
115179
use super::*;

cranelift/frontend/src/frontend.rs

Lines changed: 15 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -473,12 +473,6 @@ impl<'a> FunctionBuilder<'a> {
473473
};
474474
self.handle_ssa_side_effects(side_effects);
475475

476-
// If the variable was declared as needing stack maps, then we should
477-
// have propagated that to the value as well.
478-
if cfg!(debug_assertions) && self.func_ctx.stack_map_vars.contains(var) {
479-
assert!(self.func_ctx.stack_map_values.contains(val));
480-
}
481-
482476
Ok(val)
483477
}
484478

@@ -494,6 +488,8 @@ impl<'a> FunctionBuilder<'a> {
494488
/// an error if the value supplied does not match the type the variable was
495489
/// declared to have.
496490
pub fn try_def_var(&mut self, var: Variable, val: Value) -> Result<(), DefVariableError> {
491+
log::trace!("try_def_var: {var:?} = {val:?}");
492+
497493
let var_ty = *self
498494
.func_ctx
499495
.types
@@ -503,11 +499,6 @@ impl<'a> FunctionBuilder<'a> {
503499
return Err(DefVariableError::TypeMismatch(var, val));
504500
}
505501

506-
// If `var` needs inclusion in stack maps, then `val` does too.
507-
if self.func_ctx.stack_map_vars.contains(var) {
508-
self.declare_value_needs_stack_map(val);
509-
}
510-
511502
self.func_ctx.ssa.def_var(var, val, self.position.unwrap());
512503
Ok(())
513504
}
@@ -720,6 +711,19 @@ impl<'a> FunctionBuilder<'a> {
720711
}
721712
}
722713

714+
// Propagate the needs-stack-map bit from variables to each of their
715+
// associated values.
716+
for var in self.func_ctx.stack_map_vars.iter() {
717+
for val in self.func_ctx.ssa.values_for_var(var) {
718+
log::trace!("propagating needs-stack-map from {var:?} to {val:?}");
719+
debug_assert_eq!(self.func.dfg.value_type(val), self.func_ctx.types[var]);
720+
self.func_ctx.stack_map_values.insert(val);
721+
}
722+
}
723+
724+
// If we have any values that need inclusion in stack maps, then we need
725+
// to run our pass to spill those values to the stack at safepoints and
726+
// generate stack maps.
723727
if !self.func_ctx.stack_map_values.is_empty() {
724728
self.func_ctx
725729
.safepoints
@@ -1180,24 +1184,13 @@ impl<'a> FunctionBuilder<'a> {
11801184
fn handle_ssa_side_effects(&mut self, side_effects: SideEffects) {
11811185
let SideEffects {
11821186
instructions_added_to_blocks,
1183-
params_added_to_blocks,
11841187
} = side_effects;
11851188

11861189
for modified_block in instructions_added_to_blocks {
11871190
if self.is_pristine(modified_block) {
11881191
self.func_ctx.status[modified_block] = BlockStatus::Partial;
11891192
}
11901193
}
1191-
1192-
// Propagate needs-inclusion-in-stack-maps metadata from variables to
1193-
// their block parameter SSA values.
1194-
if !self.func_ctx.stack_map_vars.is_empty() {
1195-
for (val, var) in params_added_to_blocks {
1196-
if self.func_ctx.stack_map_vars.contains(var) {
1197-
self.func_ctx.stack_map_values.insert(val);
1198-
}
1199-
}
1200-
}
12011194
}
12021195
}
12031196

cranelift/frontend/src/ssa.rs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -67,21 +67,14 @@ pub struct SideEffects {
6767
/// This field signals if it is the case and return the `Block` to which the initialization has
6868
/// been added.
6969
pub instructions_added_to_blocks: Vec<Block>,
70-
71-
/// When a variable does not have a single, dominating definition of a use,
72-
/// we must insert block parameters to control-flow join points. This field
73-
/// contains all of the block parameters we inserted, along with the
74-
/// variable that they are associated with.
75-
pub params_added_to_blocks: Vec<(Value, Variable)>,
7670
}
7771

7872
impl SideEffects {
7973
fn is_empty(&self) -> bool {
8074
let Self {
8175
instructions_added_to_blocks,
82-
params_added_to_blocks,
8376
} = self;
84-
instructions_added_to_blocks.is_empty() && params_added_to_blocks.is_empty()
77+
instructions_added_to_blocks.is_empty()
8578
}
8679
}
8780

@@ -195,6 +188,12 @@ fn emit_zero(ty: Type, mut cur: FuncCursor) -> Value {
195188
/// Phi functions.
196189
///
197190
impl SSABuilder {
191+
/// Get all of the values associated with the given variable that we have
192+
/// inserted in the function thus far.
193+
pub fn values_for_var(&self, var: Variable) -> impl Iterator<Item = Value> + '_ {
194+
self.variables[var].values().filter_map(|v| v.expand())
195+
}
196+
198197
/// Declares a new definition of a variable in a given basic block.
199198
/// The SSA value is passed as an argument because it should be created with
200199
/// `ir::DataFlowGraph::append_result`.
@@ -318,7 +317,6 @@ impl SSABuilder {
318317
// find a usable definition. So create one.
319318
let val = func.dfg.append_block_param(block, ty);
320319
var_defs[block] = PackedOption::from(val);
321-
self.side_effects.params_added_to_blocks.push((val, var));
322320

323321
// Now every predecessor needs to pass its definition of this variable to the newly added
324322
// block parameter. To do that we have to "recursively" call `use_var`, but there are two

crates/test-macros/src/wasmtime_test.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ fn expand(test_config: &TestConfig, func: Fn) -> Result<TokenStream> {
279279
#should_panic
280280
#(#attrs)*
281281
#asyncness fn #test_name() {
282+
let _ = env_logger::try_init();
282283
let mut config = Config::new();
283284
wasmtime_test_util::wasmtime_wast::apply_test_config(
284285
&mut config,

fuzz/fuzz_targets/cranelift-icache.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ pub struct FunctionWithIsa {
4343

4444
impl FunctionWithIsa {
4545
pub fn generate(u: &mut Unstructured) -> anyhow::Result<Self> {
46+
let _ = env_logger::try_init();
47+
4648
// We filter out targets that aren't supported in the current build
4749
// configuration after randomly choosing one, instead of randomly choosing
4850
// a supported one, so that the same fuzz input works across different build

0 commit comments

Comments
 (0)