Skip to content

Commit 688a545

Browse files
authored
Remove conditional return instructions (#1486)
* comment-out or remove conditional return tests * remove conditional return instructions * remove conditional return instruction execution * adjust Wasmi translation for conditional return removal * fix bug * remove broken doc links * fix found fuzz bug * update fuzz tests * update some of the commented-out tests * convert another translation test * update the remaining translation tests * convert missing translation test
1 parent 8e8495b commit 688a545

File tree

10 files changed

+179
-454
lines changed

10 files changed

+179
-454
lines changed

crates/ir/src/enum.rs

-18
Original file line numberDiff line numberDiff line change
@@ -166,24 +166,6 @@ impl Instruction {
166166
Self::return_many([reg0.into(), reg1.into(), reg2.into()])
167167
}
168168

169-
/// Creates a new [`Instruction::ReturnNezReg2`] for the given `condition` and `value`.
170-
pub fn return_nez_reg2_ext(
171-
condition: impl Into<Reg>,
172-
value0: impl Into<Reg>,
173-
value1: impl Into<Reg>,
174-
) -> Self {
175-
Self::return_nez_reg2(condition, [value0.into(), value1.into()])
176-
}
177-
178-
/// Creates a new [`Instruction::ReturnNezMany`] for the given `condition` and `value`.
179-
pub fn return_nez_many_ext(
180-
condition: impl Into<Reg>,
181-
head0: impl Into<Reg>,
182-
head1: impl Into<Reg>,
183-
) -> Self {
184-
Self::return_nez_many(condition, [head0.into(), head1.into()])
185-
}
186-
187169
/// Creates a new [`Instruction::Copy2`].
188170
pub fn copy2_ext(results: RegSpan, value0: impl Into<Reg>, value1: impl Into<Reg>) -> Self {
189171
let span = FixedRegSpan::new(results).unwrap_or_else(|_| {

crates/ir/src/for_each_op.rs

-111
Original file line numberDiff line numberDiff line change
@@ -122,117 +122,6 @@ macro_rules! for_each_op_grouped {
122122
values: [Reg; 3],
123123
},
124124

125-
/// A conditional `return` instruction.
126-
///
127-
/// # Note
128-
///
129-
/// This is used to translate certain conditional Wasm branches such as `br_if`.
130-
/// Returns back to the caller if and only if the `condition` value is non zero.
131-
#[snake_name(return_nez)]
132-
ReturnNez {
133-
/// The register holding the condition to evaluate against zero.
134-
condition: Reg,
135-
},
136-
/// A conditional `return` instruction.
137-
///
138-
/// # Note
139-
///
140-
/// Variant of [`Instruction::ReturnNez`] returning a single
141-
/// [`Reg`] value if the `condition` evaluates to `true`.
142-
#[snake_name(return_nez_reg)]
143-
ReturnNezReg {
144-
/// The register holding the condition to evaluate against zero.
145-
condition: Reg,
146-
/// The returned value.
147-
value: Reg,
148-
},
149-
/// A conditional `return` instruction.
150-
///
151-
/// # Note
152-
///
153-
/// Variant of [`Instruction::ReturnNez`] returning two
154-
/// [`Reg`] value if the `condition` evaluates to `true`.
155-
#[snake_name(return_nez_reg2)]
156-
ReturnNezReg2 {
157-
/// The register holding the condition to evaluate against zero.
158-
condition: Reg,
159-
/// The returned value.
160-
values: [Reg; 2],
161-
},
162-
/// A conditional `return` instruction.
163-
///
164-
/// # Note
165-
///
166-
/// Variant of [`Instruction::ReturnNez`] returning a single
167-
/// [`AnyConst32`] value if the `condition` evaluates to `true`.
168-
#[snake_name(return_nez_imm32)]
169-
ReturnNezImm32 {
170-
/// The register holding the condition to evaluate against zero.
171-
condition: Reg,
172-
/// The returned value.
173-
value: AnyConst32,
174-
},
175-
/// A conditional `return` instruction.
176-
///
177-
/// # Note
178-
///
179-
/// Variant of [`Instruction::ReturnNez`] returning a single
180-
/// 32-bit encoded [`i64`] value if the `condition` evaluates to `true`.
181-
#[snake_name(return_nez_i64imm32)]
182-
ReturnNezI64Imm32 {
183-
/// The register holding the condition to evaluate against zero.
184-
condition: Reg,
185-
/// The returned value.
186-
value: Const32<i64>,
187-
},
188-
/// A conditional `return` instruction.
189-
///
190-
/// # Note
191-
///
192-
/// Variant of [`Instruction::ReturnNez`] returning a single
193-
/// 32-bit encoded [`f64`] value if the `condition` evaluates to `true`.
194-
#[snake_name(return_nez_f64imm32)]
195-
ReturnNezF64Imm32 {
196-
/// The register holding the condition to evaluate against zero.
197-
condition: Reg,
198-
/// The returned value.
199-
value: Const32<f64>,
200-
},
201-
/// A conditional `return` instruction.
202-
///
203-
/// # Note
204-
///
205-
/// Variant of [`Instruction::ReturnNez`] returning two or more values.
206-
#[snake_name(return_nez_span)]
207-
ReturnNezSpan {
208-
/// The register holding the condition to evaluate against zero.
209-
condition: Reg,
210-
/// The returned values.
211-
values: BoundedRegSpan,
212-
},
213-
/// A conditional `return` instruction.
214-
///
215-
/// # Note
216-
///
217-
/// Variant of [`Instruction::ReturnNez`] returning multiple register values.
218-
///
219-
/// # Encoding
220-
///
221-
/// Must be followed by
222-
///
223-
/// 1. Zero or more [`Instruction::RegisterList`]
224-
/// 2. Followed by one of
225-
/// - [`Instruction::Register`]
226-
/// - [`Instruction::Register2`]
227-
/// - [`Instruction::Register3`]
228-
#[snake_name(return_nez_many)]
229-
ReturnNezMany {
230-
/// The register holding the condition to evaluate against zero.
231-
condition: Reg,
232-
/// The first returned value.
233-
values: [Reg; 2],
234-
},
235-
236125
/// A Wasm `br` instruction.
237126
#[snake_name(branch)]
238127
Branch {

crates/wasmi/src/engine/executor/instrs.rs

-52
Original file line numberDiff line numberDiff line change
@@ -153,58 +153,6 @@ impl<'engine> Executor<'engine> {
153153
Instr::ReturnMany { values } => {
154154
forward_return!(self.execute_return_many(store.inner_mut(), values))
155155
}
156-
Instr::ReturnNez { condition } => {
157-
forward_return!(self.execute_return_nez(store.inner_mut(), condition))
158-
}
159-
Instr::ReturnNezReg { condition, value } => {
160-
forward_return!(self.execute_return_nez_reg(
161-
store.inner_mut(),
162-
condition,
163-
value
164-
))
165-
}
166-
Instr::ReturnNezReg2 { condition, values } => {
167-
forward_return!(self.execute_return_nez_reg2(
168-
store.inner_mut(),
169-
condition,
170-
values
171-
))
172-
}
173-
Instr::ReturnNezImm32 { condition, value } => {
174-
forward_return!(self.execute_return_nez_imm32(
175-
store.inner_mut(),
176-
condition,
177-
value
178-
))
179-
}
180-
Instr::ReturnNezI64Imm32 { condition, value } => {
181-
forward_return!(self.execute_return_nez_i64imm32(
182-
store.inner_mut(),
183-
condition,
184-
value
185-
))
186-
}
187-
Instr::ReturnNezF64Imm32 { condition, value } => {
188-
forward_return!(self.execute_return_nez_f64imm32(
189-
store.inner_mut(),
190-
condition,
191-
value
192-
))
193-
}
194-
Instr::ReturnNezSpan { condition, values } => {
195-
forward_return!(self.execute_return_nez_span(
196-
store.inner_mut(),
197-
condition,
198-
values
199-
))
200-
}
201-
Instr::ReturnNezMany { condition, values } => {
202-
forward_return!(self.execute_return_nez_many(
203-
store.inner_mut(),
204-
condition,
205-
values
206-
))
207-
}
208156
Instr::Branch { offset } => self.execute_branch(offset),
209157
Instr::BranchTable0 { index, len_targets } => {
210158
self.execute_branch_table_0(index, len_targets)

crates/wasmi/src/engine/executor/instrs/return_.rs

-107
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,6 @@ impl Executor<'_> {
187187
/// Used by the execution logic for
188188
///
189189
/// - [`Instruction::ReturnMany`]
190-
/// - [`Instruction::ReturnNezMany`]
191190
/// - [`Instruction::BranchTableMany`]
192191
pub fn copy_many_return_values(&mut self, ip: InstructionPtr, values: &[Reg]) {
193192
let (mut caller_sp, results) = self.return_caller_results();
@@ -224,110 +223,4 @@ impl Executor<'_> {
224223
};
225224
copy_results(values);
226225
}
227-
228-
/// Execute a generic conditional return [`Instruction`].
229-
fn execute_return_nez_impl<T>(
230-
&mut self,
231-
store: &mut StoreInner,
232-
condition: Reg,
233-
value: T,
234-
f: fn(&mut Self, &mut StoreInner, T) -> ControlFlow,
235-
) -> ControlFlow {
236-
let condition = self.get_register(condition);
237-
match bool::from(condition) {
238-
true => f(self, store, value),
239-
false => {
240-
self.next_instr();
241-
ControlFlow::Continue(())
242-
}
243-
}
244-
}
245-
246-
/// Execute an [`Instruction::ReturnNez`].
247-
pub fn execute_return_nez(&mut self, store: &mut StoreInner, condition: Reg) -> ControlFlow {
248-
self.execute_return_nez_impl(store, condition, (), |this, store, _| {
249-
this.execute_return(store)
250-
})
251-
}
252-
253-
/// Execute an [`Instruction::ReturnNezReg`] returning a single [`Reg`] value.
254-
pub fn execute_return_nez_reg(
255-
&mut self,
256-
store: &mut StoreInner,
257-
condition: Reg,
258-
value: Reg,
259-
) -> ControlFlow {
260-
self.execute_return_nez_impl(store, condition, value, Self::execute_return_reg)
261-
}
262-
263-
/// Execute an [`Instruction::ReturnNezReg2`] returning two [`Reg`] values.
264-
pub fn execute_return_nez_reg2(
265-
&mut self,
266-
store: &mut StoreInner,
267-
condition: Reg,
268-
value: [Reg; 2],
269-
) -> ControlFlow {
270-
self.execute_return_nez_impl(store, condition, value, Self::execute_return_reg2)
271-
}
272-
273-
/// Execute an [`Instruction::ReturnNezImm32`] returning a single 32-bit immediate value.
274-
pub fn execute_return_nez_imm32(
275-
&mut self,
276-
store: &mut StoreInner,
277-
condition: Reg,
278-
value: AnyConst32,
279-
) -> ControlFlow {
280-
self.execute_return_nez_impl(store, condition, value, Self::execute_return_imm32)
281-
}
282-
283-
/// Execute an [`Instruction::ReturnNezI64Imm32`] returning a single 32-bit encoded immediate `i64` value.
284-
pub fn execute_return_nez_i64imm32(
285-
&mut self,
286-
store: &mut StoreInner,
287-
condition: Reg,
288-
value: Const32<i64>,
289-
) -> ControlFlow {
290-
self.execute_return_nez_impl(store, condition, value, Self::execute_return_i64imm32)
291-
}
292-
293-
/// Execute an [`Instruction::ReturnNezF64Imm32`] returning a single 32-bit encoded immediate `f64` value.
294-
pub fn execute_return_nez_f64imm32(
295-
&mut self,
296-
store: &mut StoreInner,
297-
condition: Reg,
298-
value: Const32<f64>,
299-
) -> ControlFlow {
300-
self.execute_return_nez_impl(store, condition, value, Self::execute_return_f64imm32)
301-
}
302-
303-
/// Execute an [`Instruction::ReturnNezSpan`] returning many values.
304-
pub fn execute_return_nez_span(
305-
&mut self,
306-
store: &mut StoreInner,
307-
condition: Reg,
308-
values: BoundedRegSpan,
309-
) -> ControlFlow {
310-
self.execute_return_nez_impl(store, condition, values, Self::execute_return_span)
311-
}
312-
313-
/// Execute an [`Instruction::ReturnNezMany`] returning many values.
314-
pub fn execute_return_nez_many(
315-
&mut self,
316-
store: &mut StoreInner,
317-
condition: Reg,
318-
values: [Reg; 2],
319-
) -> ControlFlow {
320-
let condition = self.get_register(condition);
321-
self.ip.add(1);
322-
match bool::from(condition) {
323-
true => {
324-
self.copy_many_return_values(self.ip, &values);
325-
self.return_impl(store)
326-
}
327-
false => {
328-
self.ip = Self::skip_register_list(self.ip);
329-
ControlFlow::Continue(())
330-
}
331-
}
332-
}
333226
}

crates/wasmi/src/engine/translator/instr_encoder.rs

-62
Original file line numberDiff line numberDiff line change
@@ -641,75 +641,13 @@ impl InstrEncoder {
641641
Ok(())
642642
}
643643

644-
/// Encodes an conditional `return` instruction.
645-
pub fn encode_return_nez(
646-
&mut self,
647-
stack: &mut ValueStack,
648-
condition: Reg,
649-
values: &[TypedProvider],
650-
fuel_info: FuelInfo,
651-
) -> Result<(), Error> {
652-
// Note: We bump fuel unconditionally even if the conditional return is not taken.
653-
// This is very conservative and may lead to more fuel costs than
654-
// actually needed for the computation. We might revisit this decision
655-
// later. An alternative solution would consume fuel during execution
656-
// time only when the return is taken.
657-
let instr = match values {
658-
[] => Instruction::return_nez(condition),
659-
[TypedProvider::Register(reg)] => Instruction::return_nez_reg(condition, *reg),
660-
[TypedProvider::Const(value)] => match value.ty() {
661-
ValType::I32 => Instruction::return_nez_imm32(condition, i32::from(*value)),
662-
ValType::I64 => match <Const32<i64>>::try_from(i64::from(*value)).ok() {
663-
Some(value) => Instruction::return_nez_i64imm32(condition, value),
664-
None => Instruction::return_nez_reg(condition, stack.alloc_const(*value)?),
665-
},
666-
ValType::F32 => Instruction::return_nez_imm32(condition, f32::from(*value)),
667-
ValType::F64 => match <Const32<f64>>::try_from(f64::from(*value)).ok() {
668-
Some(value) => Instruction::return_nez_f64imm32(condition, value),
669-
None => Instruction::return_nez_reg(condition, stack.alloc_const(*value)?),
670-
},
671-
ValType::V128 | ValType::FuncRef | ValType::ExternRef => {
672-
Instruction::return_nez_reg(condition, stack.alloc_const(*value)?)
673-
}
674-
},
675-
[v0, v1] => {
676-
let reg0 = stack.provider2reg(v0)?;
677-
let reg1 = stack.provider2reg(v1)?;
678-
Instruction::return_nez_reg2_ext(condition, reg0, reg1)
679-
}
680-
[v0, v1, rest @ ..] => {
681-
debug_assert!(!rest.is_empty());
682-
// Note: The fuel for return values might result in 0 charges if there aren't
683-
// enough return values to account for at least 1 fuel. Therefore we need
684-
// to also bump by `FuelCosts::base` to charge at least 1 fuel.
685-
self.bump_fuel_consumption(&fuel_info, FuelCostsProvider::base)?;
686-
self.bump_fuel_consumption(&fuel_info, |costs| {
687-
costs.fuel_for_copying_values(rest.len() as u64 + 3)
688-
})?;
689-
if let Some(span) = BoundedRegSpan::from_providers(values) {
690-
self.push_instr(Instruction::return_nez_span(condition, span))?;
691-
return Ok(());
692-
}
693-
let reg0 = stack.provider2reg(v0)?;
694-
let reg1 = stack.provider2reg(v1)?;
695-
self.push_instr(Instruction::return_nez_many_ext(condition, reg0, reg1))?;
696-
self.encode_register_list(stack, rest)?;
697-
return Ok(());
698-
}
699-
};
700-
self.bump_fuel_consumption(&fuel_info, FuelCostsProvider::base)?;
701-
self.push_instr(instr)?;
702-
Ok(())
703-
}
704-
705644
/// Encode the given slice of [`TypedProvider`] as a list of [`Reg`].
706645
///
707646
/// # Note
708647
///
709648
/// This is used for the following n-ary instructions:
710649
///
711650
/// - [`Instruction::ReturnMany`]
712-
/// - [`Instruction::ReturnNezMany`]
713651
/// - [`Instruction::CopyMany`]
714652
/// - [`Instruction::CallInternal`]
715653
/// - [`Instruction::CallImported`]

0 commit comments

Comments
 (0)