diff --git a/cranelift/codegen/src/alias_analysis.rs b/cranelift/codegen/src/alias_analysis.rs index cc45328f7c95..e6cb6fce467f 100644 --- a/cranelift/codegen/src/alias_analysis.rs +++ b/cranelift/codegen/src/alias_analysis.rs @@ -867,20 +867,26 @@ impl<'a> AliasAnalysis<'a> { } fn compute_block_input_states(&mut self, func: &Function) { - let mut queue = vec![]; - let mut queue_set = FxHashSet::default(); - let entry = func.layout.entry_block().unwrap(); - queue.push(entry); - queue_set.insert(entry); + self.block_input.insert(entry, LastStores::default()); + + // Seed the worklist in reverse post-order (well, post order, but + // visited in reverse due to popping). This visits a block's + // predecessors before the block itself, which minimizes the number of + // times we need to reprocess a block to reach the fixed point (ignoring + // backedges). + let mut queue = self.domtree.cfg_postorder().to_vec(); + let mut queue_set: FxHashSet = queue.iter().copied().collect(); while let Some(block) = queue.pop() { queue_set.remove(&block); - let mut state = self - .block_input - .entry(block) - .or_insert_with(|| LastStores::default()) - .clone(); + + let Some(mut state) = self.block_input.get(&block).cloned() else { + // Nothing has propagated into this block yet, so there is + // nothing to propagate out of it. If/when some predecessor gets + // state to propagate to this block, it will be re-enqueued. + continue; + }; trace!("analyzing {block:?}"); trace!(" initial block state = {state:?}"); diff --git a/cranelift/codegen/src/isa/aarch64/inst.isle b/cranelift/codegen/src/isa/aarch64/inst.isle index b91d7657006f..281ab5d80a10 100644 --- a/cranelift/codegen/src/isa/aarch64/inst.isle +++ b/cranelift/codegen/src/isa/aarch64/inst.isle @@ -4992,7 +4992,7 @@ ;; give an opportunistic def of the other output. (rule 4 (is_nonzero - (second_result uadd @ (uadd_overflow (ty_32_or_64 ty) x y))) + (is_second_result uadd @ (uadd_overflow (ty_32_or_64 ty) x y))) (if-let (first_result sum_value) uadd) (let ((producer ProducesFlags (alu_rrr_with_flags_paired ty x y (ALUOp.AddS)))) (CondResult.Cond @@ -5012,7 +5012,7 @@ ;; cmp out, out, uxt{b,h} (rule 5 (is_nonzero - (second_result umul @ (umul_overflow (fits_in_16 ty) a b))) + (is_second_result umul @ (umul_overflow (fits_in_16 ty) a b))) (if-let (first_result prod_value) umul) (let ((a_uext Reg (put_in_reg_zext32 a)) (b_uext Reg (put_in_reg_zext32 b)) @@ -5031,7 +5031,7 @@ ;; cmp out, out, uxtw (rule 6 (is_nonzero - (second_result umul @ (umul_overflow $I32 a b))) + (is_second_result umul @ (umul_overflow $I32 a b))) (if-let (first_result prod_value) umul) (let ((dst WritableReg (temp_writable_reg $I64)) (producer ProducesFlags @@ -5048,7 +5048,7 @@ ;; cmp tmp, #0 (rule 7 (is_nonzero - (second_result umul @ (umul_overflow $I64 a b))) + (is_second_result umul @ (umul_overflow $I64 a b))) (let ((tmp Reg (umulh $I64 a b)) (producer ProducesFlags (cmp64_imm tmp (u8_into_imm12 0)))) (CondResult.Cond producer (Cond.Ne)))) @@ -5057,7 +5057,7 @@ ;; cmp out, out, sxt{b,h} (rule 8 (is_nonzero - (second_result smul @ (smul_overflow (fits_in_16 ty) a b))) + (is_second_result smul @ (smul_overflow (fits_in_16 ty) a b))) (if-let (first_result prod_value) smul) (let ((a_sext Reg (put_in_reg_sext32 a)) (b_sext Reg (put_in_reg_sext32 b)) @@ -5076,7 +5076,7 @@ ;; cmp out, out, sxtw (rule 9 (is_nonzero - (second_result smul @ (smul_overflow $I32 a b))) + (is_second_result smul @ (smul_overflow $I32 a b))) (if-let (first_result prod_value) smul) (let ((dst WritableReg (temp_writable_reg $I64)) (producer ProducesFlags @@ -5093,7 +5093,7 @@ ;; cmp tmp, out, asr #63 (rule 10 (is_nonzero - (second_result smul @ (smul_overflow $I64 a b))) + (is_second_result smul @ (smul_overflow $I64 a b))) (if-let (first_result prod_value) smul) (let ((prod Reg (put_in_reg prod_value)) (tmp Reg (smulh $I64 a b)) diff --git a/cranelift/codegen/src/isa/x64/inst.isle b/cranelift/codegen/src/isa/x64/inst.isle index d1744469c855..711c68fe8456 100644 --- a/cranelift/codegen/src/isa/x64/inst.isle +++ b/cranelift/codegen/src/isa/x64/inst.isle @@ -3879,7 +3879,7 @@ ;; opportunistic def of the other output. (rule 3 (is_nonzero - (second_result uadd @ (uadd_overflow (fits_in_64 ty) x y))) + (is_second_result uadd @ (uadd_overflow (fits_in_64 ty) x y))) (if-let (first_result sum_value) uadd) (let ((producer ProducesFlags (x64_add_with_flags_paired ty x y))) (CondResult.CC @@ -3892,7 +3892,7 @@ ;; `mul` of narrow types), so no extra compare is needed. (rule 4 (is_nonzero - (second_result umul @ (umul_overflow _ x y @ (value_type (ty_int_ref_16_to_64 ty))))) + (is_second_result umul @ (umul_overflow _ x y @ (value_type (ty_int_ref_16_to_64 ty))))) (if-let (first_result prod_value) umul) (let ((producer ProducesFlags (x64_mul_lo_with_flags_paired ty false x y))) (CondResult.CC @@ -3901,7 +3901,7 @@ (rule 5 (is_nonzero - (second_result umul @ (umul_overflow _ x y @ (value_type $I8)))) + (is_second_result umul @ (umul_overflow _ x y @ (value_type $I8)))) (if-let (first_result prod_value) umul) (let ((producer ProducesFlags (x64_mul8_with_flags_paired false x y))) (CondResult.CC @@ -3910,7 +3910,7 @@ (rule 6 (is_nonzero - (second_result smul @ (smul_overflow _ x y @ (value_type (ty_int_ref_16_to_64 ty))))) + (is_second_result smul @ (smul_overflow _ x y @ (value_type (ty_int_ref_16_to_64 ty))))) (if-let (first_result prod_value) smul) (let ((producer ProducesFlags (x64_mul_lo_with_flags_paired ty true x y))) (CondResult.CC @@ -3919,7 +3919,7 @@ (rule 7 (is_nonzero - (second_result smul @ (smul_overflow _ x y @ (value_type $I8)))) + (is_second_result smul @ (smul_overflow _ x y @ (value_type $I8)))) (if-let (first_result prod_value) smul) (let ((producer ProducesFlags (x64_mul8_with_flags_paired true x y))) (CondResult.CC diff --git a/cranelift/codegen/src/machinst/isle.rs b/cranelift/codegen/src/machinst/isle.rs index 0d4288bd6dba..a8c81dd3cbbe 100644 --- a/cranelift/codegen/src/machinst/isle.rs +++ b/cranelift/codegen/src/machinst/isle.rs @@ -213,6 +213,20 @@ macro_rules! isle_lower_prelude_methods { .copied() } + #[inline] + fn is_second_result(&mut self, val: Value) -> Option { + let inst = self.def_inst(val)?; + let is_match = self + .lower_ctx + .dfg() + .inst_results(inst) + .iter() + .skip(1) + .next() + == Some(&val); + if is_match { Some(val) } else { None } + } + #[inline] fn second_result_used(&mut self, inst: Inst) -> bool { let second_result = self.lower_ctx.dfg().inst_results(inst).get(1).copied(); diff --git a/cranelift/codegen/src/prelude_lower.isle b/cranelift/codegen/src/prelude_lower.isle index 46ce41d47ae2..3e0d88672d9b 100644 --- a/cranelift/codegen/src/prelude_lower.isle +++ b/cranelift/codegen/src/prelude_lower.isle @@ -301,10 +301,23 @@ (extern extractor first_result first_result) ;; Extract the second result value of the given instruction. +;; +;; Be careful using this: if used to match on a *`Value`*, the +;; auto-converter will take that `Value` to its defining `Inst`, then +;; the inner result of this extractor will be the second def of the +;; value; in other words, it will *fetch* the second result but will +;; not *assert/only match if* the initial `Value` is the second +;; result. Use `is_second_result` for that. (spec (second_result value) (provide (= result value))) (decl second_result (Value) Inst) (extern extractor second_result second_result) +;; Determine whether the given value is the second result of its +;; defining instruction. +(spec (is_second_result value) (provide (= result value))) +(decl is_second_result (Value) Value) +(extern extractor is_second_result is_second_result) + ;; Returns whether an instruction's second result still has uses to serve in ;; the lowering scan. Overflow operations use this while lowering their first ;; (non-boolean) result. diff --git a/cranelift/filetests/filetests/runtests/issue-14293.clif b/cranelift/filetests/filetests/runtests/issue-14293.clif new file mode 100644 index 000000000000..54ed9aca90cc --- /dev/null +++ b/cranelift/filetests/filetests/runtests/issue-14293.clif @@ -0,0 +1,15 @@ +test run +set opt_level=speed_and_size +target x86_64 +target aarch64 + +function %a(i64) -> i8 { +block0(v0: i64): + v1, v2 = smul_overflow.i64 v0, v0 + v3 = iconst.i64 0 + v4 = icmp eq v1, v3 + return v4 +} + +; run: %a(0) == 1 +; run: %a(8192) == 0 diff --git a/crates/wasmtime/src/runtime/component/concurrent.rs b/crates/wasmtime/src/runtime/component/concurrent.rs index ce8e46eb6488..b7f8593a1f93 100644 --- a/crates/wasmtime/src/runtime/component/concurrent.rs +++ b/crates/wasmtime/src/runtime/component/concurrent.rs @@ -1224,7 +1224,13 @@ impl StoreContextMut<'_, T> { trap_on_idle: bool, ) -> Result { debug_assert!(self.0.concurrency_support()); - check_recursive_run(); + let already_running = self + .0 + .concurrent_state_mut_already_forced_current_thread() + .event_loop_running; + if already_running { + bail!("Recursive `StoreContextMut::run_concurrent` calls not supported") + } let token = StoreToken::new(self.as_context_mut()); struct Dropper<'a, T: 'static, V> { @@ -6103,16 +6109,6 @@ fn check_ambient_store(id: StoreId) { }); } -/// Assert that `StoreContextMut::run_concurrent` has not been called from -/// within an store's event loop. -fn check_recursive_run() { - tls::try_get(|store| { - if !matches!(store, tls::TryGet::None) { - panic!("Recursive `StoreContextMut::run_concurrent` calls not supported") - } - }); -} - fn unpack_callback_code(code: u32) -> (u32, u32) { (code & 0xF, code >> 4) }