|
1 | 1 | use std::{collections::HashMap, ops::Range, vec};
|
2 | 2 |
|
3 |
| -use crate::{instruction_builder as ib, to_field::ToField}; |
| 3 | +use crate::{const_collapse::can_turn_to_lt, instruction_builder as ib, to_field::ToField}; |
4 | 4 | use arrayvec::ArrayVec;
|
5 | 5 | use itertools::Itertools;
|
6 | 6 | use openvm_circuit::{
|
@@ -888,23 +888,32 @@ impl<'a, F: PrimeField32> Settings<'a> for OpenVMSettings<F> {
|
888 | 888 | }
|
889 | 889 | [WasmOpInput::Register(reg), WasmOpInput::Constant(c)]
|
890 | 890 | | [WasmOpInput::Constant(c), WasmOpInput::Register(reg)] => {
|
891 |
| - // This is a little confusing, because GE and LE are using the same opcode, |
892 |
| - // but this is correct, as the operands are reversed between GE and LE. |
893 |
| - // Const folding step guarantees that only GE will have the constant on |
894 |
| - // the right side, and only LE will have the constant on the left side. |
895 |
| - // |
896 |
| - // In another words: the order of the operands will guarantee that the inverse |
897 |
| - // operation is always a less-than comparison: reg_value < constant_value. |
898 |
| - let inverse_op = match op { |
| 891 | + let opcode = match op { |
899 | 892 | Op::I32GeS | Op::I32LeS => LessThanOpcode::SLT.global_opcode(),
|
900 | 893 | Op::I32GeU | Op::I32LeU => LessThanOpcode::SLTU.global_opcode(),
|
901 | 894 | Op::I64GeS | Op::I64LeS => LessThan64Opcode::SLT.global_opcode(),
|
902 | 895 | Op::I64GeU | Op::I64LeU => LessThan64Opcode::SLTU.global_opcode(),
|
903 | 896 | _ => unreachable!(),
|
904 | 897 | };
|
905 | 898 |
|
| 899 | + // Check whether this is the case where "c >= r" or "r <= c" can be turned into "r < c + 1". |
| 900 | + if ((matches!(op, Op::I32GeS | Op::I32GeU | Op::I64GeS | Op::I64GeU) |
| 901 | + && matches!(&inputs[0], WasmOpInput::Constant(_))) |
| 902 | + || (matches!(op, Op::I32LeS | Op::I32LeU | Op::I64LeS | Op::I64LeU) |
| 903 | + && matches!(&inputs[0], WasmOpInput::Register(_)))) |
| 904 | + && let Some(inc_c) = can_turn_to_lt(&op, c) |
| 905 | + { |
| 906 | + return Directive::Instruction(ib::instr_i( |
| 907 | + opcode.as_usize(), |
| 908 | + output, |
| 909 | + reg.start as usize, |
| 910 | + i16_to_imm_field(inc_c), |
| 911 | + )) |
| 912 | + .into(); |
| 913 | + } |
| 914 | + |
906 | 915 | let c = const_i16_as_field(c);
|
907 |
| - ib::instr_i(inverse_op.as_usize(), inverse_result, reg.start as usize, c) |
| 916 | + ib::instr_i(opcode.as_usize(), inverse_result, reg.start as usize, c) |
908 | 917 | }
|
909 | 918 | _ => unreachable!("combination of inputs not possible for GE/LE operations"),
|
910 | 919 | };
|
@@ -2155,7 +2164,11 @@ fn const_i16_as_field<F: PrimeField32>(value: &WasmValue) -> F {
|
2155 | 2164 | WasmValue::I64(v) => v as i16,
|
2156 | 2165 | _ => panic!("not valid i16"),
|
2157 | 2166 | };
|
2158 |
| - F::from_canonical_u32((c as i32 as u32) & 0xff_ff_ff) |
| 2167 | + i16_to_imm_field(c) |
| 2168 | +} |
| 2169 | + |
| 2170 | +fn i16_to_imm_field<F: PrimeField32>(value: i16) -> F { |
| 2171 | + F::from_canonical_u32((value as i32 as u32) & 0xff_ff_ff) |
2159 | 2172 | }
|
2160 | 2173 |
|
2161 | 2174 | fn mem_offset<F: PrimeField32>(memarg: MemArg, c: &Ctx<F>) -> i32 {
|
|
0 commit comments