use super::super::abstract_instruction_set::AbstractInstructionSet;
use crate::asm_lang::{
ConstantRegister, ControlFlowOp, JumpType, Label, Op, VirtualImmediate18, VirtualOp,
VirtualRegister,
};
use either::Either;
use rustc_hash::FxHashMap;
use std::{
collections::hash_map::Entry,
ops::{BitAnd, BitOr, BitXor, Not},
};
use sway_types::Span;
#[derive(Clone, Debug, PartialEq, Eq)]
enum KnownRegValue {
Const(u64),
Eq(VirtualRegister),
}
impl KnownRegValue {
fn register(&self) -> Option<VirtualRegister> {
match self {
KnownRegValue::Const(0) => Some(VirtualRegister::Constant(ConstantRegister::Zero)),
KnownRegValue::Const(1) => Some(VirtualRegister::Constant(ConstantRegister::One)),
KnownRegValue::Eq(v) => Some(v.clone()),
_ => None,
}
}
fn value(&self) -> Option<u64> {
match self {
KnownRegValue::Const(v) => Some(*v),
KnownRegValue::Eq(VirtualRegister::Constant(ConstantRegister::Zero)) => Some(0),
KnownRegValue::Eq(VirtualRegister::Constant(ConstantRegister::One)) => Some(1),
KnownRegValue::Eq(_) => None,
}
}
fn value_as_imm18(&self) -> Option<VirtualImmediate18> {
let raw = self.value()?;
VirtualImmediate18::try_new(raw, Span::dummy()).ok()
}
fn depends_on(&self, reg: &VirtualRegister) -> bool {
match self {
KnownRegValue::Const(_) => false,
KnownRegValue::Eq(v) => v == reg,
}
}
}
#[derive(Clone, Debug, Default)]
struct KnownValues {
values: FxHashMap<VirtualRegister, KnownRegValue>,
}
impl KnownValues {
fn resolve(&self, v: &VirtualRegister) -> Option<KnownRegValue> {
match v {
VirtualRegister::Constant(ConstantRegister::Zero) => Some(KnownRegValue::Const(0)),
VirtualRegister::Constant(ConstantRegister::One) => Some(KnownRegValue::Const(1)),
other => self.values.get(other).cloned(),
}
}
fn remove_reg_and_dependents(&mut self, reg: &VirtualRegister) {
let mut worklist = vec![reg.clone()];
while let Some(reg) = worklist.pop() {
let keys = self
.values
.extract_if(|_, v| v.depends_on(®))
.map(|(k, _)| k);
worklist.extend(keys);
self.values.remove(®);
}
}
fn assign(&mut self, dst: VirtualRegister, value: KnownRegValue) {
self.remove_reg_and_dependents(&dst);
self.values.insert(dst, value);
}
}
#[derive(Clone, Debug)]
enum ResetKnown {
Nothing,
Defs,
DefsAndNonVirtuals,
All,
}
impl ResetKnown {
fn apply(&self, op: &Op, known_values: &mut KnownValues) {
known_values
.remove_reg_and_dependents(&VirtualRegister::Constant(ConstantRegister::Overflow));
known_values.remove_reg_and_dependents(&VirtualRegister::Constant(ConstantRegister::Error));
match self {
ResetKnown::Nothing => {}
ResetKnown::Defs => {
for d in op.def_registers() {
known_values.remove_reg_and_dependents(d);
}
for d in op.def_const_registers() {
known_values.remove_reg_and_dependents(d);
}
}
ResetKnown::DefsAndNonVirtuals => {
Self::Defs.apply(op, known_values);
known_values
.values
.retain(|k, _| matches!(k, VirtualRegister::Virtual(_)));
}
ResetKnown::All => {
known_values.values.clear();
}
}
}
}
impl AbstractInstructionSet {
pub(crate) fn constant_propagate(
mut self,
mut log: impl FnMut(&str),
) -> AbstractInstructionSet {
if self.ops.is_empty() {
return self;
}
let mut jump_target_labels = FxHashMap::<Label, usize>::default();
for op in &self.ops {
match &op.opcode {
Either::Right(ControlFlowOp::Jump { to, .. }) => {
*jump_target_labels.entry(*to).or_default() += 1;
}
Either::Right(ControlFlowOp::JumpToAddr(..)) => {
return self;
}
_ => {}
}
}
let mut known_values = KnownValues::default();
for op in &mut self.ops {
for reg in op.use_registers_mut() {
if !reg.is_virtual() {
continue;
}
if let Some(r) = known_values.resolve(reg).and_then(|r| r.register()) {
*reg = r;
}
}
if let Either::Right(ControlFlowOp::Jump {
to,
type_: JumpType::NotZero(reg),
}) = &mut op.opcode
{
if let Some(con) = known_values.resolve(reg).and_then(|r| r.value()) {
if con == 0 {
let Entry::Occupied(mut count) = jump_target_labels.entry(*to) else {
unreachable!("Jump target label not found in jump_target_labels");
};
*count.get_mut() -= 1;
if *count.get() == 0 {
jump_target_labels.remove(to);
}
op.opcode = Either::Left(VirtualOp::NOOP);
} else {
op.opcode = Either::Right(ControlFlowOp::Jump {
to: *to,
type_: JumpType::Unconditional,
});
}
}
}
macro_rules! transform_operator {
(assign_value; $dst:ident, $l:ident, $r:ident; left) => {
known_values.assign($dst.clone(), KnownRegValue::Eq($l.clone()));
};
(assign_value; $dst:ident, $l:ident, $r:ident; right) => {
known_values.assign($dst.clone(), KnownRegValue::Eq($r.clone()));
};
(assign_value; $dst:ident, $l:ident, $r:ident; $v:literal) => {
known_values.assign($dst.clone(), KnownRegValue::Const($v));
};
(new_opcode; $dst:ident, $l:ident, $r:ident; left) => {
Either::Left(VirtualOp::MOVE($dst.clone(), $l.clone()))
};
(new_opcode; $dst:ident, $l:ident, $r:ident; right) => {
Either::Left(VirtualOp::MOVE($dst.clone(), $r.clone()))
};
(new_opcode; $dst:ident, $l:ident, $r:ident; $v:literal) => {{
let imm = VirtualImmediate18::try_new($v, Span::dummy()).ok()?;
Either::Left(VirtualOp::MOVI($dst.clone(), imm))
}};
(gen; $op:ident, $opI:ident; $dst:ident, $l:ident, $r:ident, $lv: ident, $rv:ident; if left is $initial_value:literal assign $end_value:tt; $($rest:tt)*) => {
if let (Some(KnownRegValue::Const($initial_value)), _) = (&$lv, &$rv) {
let new_opcode = transform_operator!{new_opcode; $dst, $l, $r; $end_value};
transform_operator!{assign_value; $dst, $l, $r; $end_value};
op.opcode = new_opcode;
return Some(ResetKnown::Nothing);
}
transform_operator!{gen; $op, $opI; $dst, $l, $r, $lv, $rv; $($rest)*}
};
(gen; $op:ident, $opI:ident; $dst:ident, $l:ident, $r:ident, $lv: ident, $rv:ident; if right is $initial_value:literal assign $end_value:tt; $($rest:tt)*) => {
if let (_, Some(KnownRegValue::Const($initial_value))) = (&$lv, &$rv) {
let new_opcode = transform_operator!{new_opcode; $dst, $l, $r; $end_value};
transform_operator!{assign_value; $dst, $l, $r; $end_value};
op.opcode = new_opcode;
return Some(ResetKnown::Nothing);
}
transform_operator!{gen; $op, $opI; $dst, $l, $r, $lv, $rv; $($rest)*}
};
(gen; $op:ident, $opI:ident; $dst:ident, $l:ident, $r:ident, $lv: ident, $rv:ident; both_known: $f:path; $($rest:tt)*) => {
if let (Some(KnownRegValue::Const($lv)), Some(KnownRegValue::Const($rv))) = (&$lv, &$rv) {
let raw = $f(*$lv, (*$rv).try_into().ok()?)?.into();
let imm = VirtualImmediate18::try_new(raw, Span::dummy()).ok()?;
known_values.assign($dst.clone(), KnownRegValue::Const(raw));
op.opcode = Either::Left(VirtualOp::MOVI($dst.clone(), imm));
return Some(ResetKnown::Nothing);
}
transform_operator!{gen; $op, $opI; $dst, $l, $r, $lv, $rv; $($rest)*}
};
(gen; $op:ident, $opI:ident; $dst:ident, $l:ident, $r:ident, $lv: ident, $rv:ident; commutative: true; $($rest:tt)*) => {
if let (Some(KnownRegValue::Const($lv)), _) = (&$lv, &$rv) {
op.opcode = Either::Left(VirtualOp::$opI($dst.clone(), $r.clone(), (*$lv).try_into().ok()?));
return Some(ResetKnown::Defs);
}
transform_operator!{gen; $op, $opI; $dst, $l, $r, $lv, $rv; $($rest)*}
};
(gen; $op:ident, $opI:ident; $dst:ident, $l:ident, $r:ident, $lv: ident, $rv:ident;) => {
};
(gen; $op:ident, $opI:ident; $($rest:tt)*) => {
compile_error!(stringify!($($rest)*))
};
(replace_imm; None; $dst:ident, $l:ident, $r:ident, $lv: ident, $rv:ident) => {
};
(replace_imm; $opI:ident; $dst:ident, $l:ident, $r:ident, $lv: ident, $rv:ident) => {
if let (_, Some(KnownRegValue::Const(rv))) = (&$lv, &$rv) {
op.opcode = Either::Left(VirtualOp::$opI($dst.clone(), $l.clone(), (*rv).try_into().ok()?));
return Some(ResetKnown::Defs);
}
};
($op:ident, $opI:ident; $($rest:tt)*) => {{
let mut f = || -> Option<ResetKnown> {
match &op.opcode {
Either::Left(VirtualOp::$op(dst, l, r)) => {
let lv = known_values.resolve(&l);
let rv = known_values.resolve(&r);
log(&format!(" {:?} {:?}\n", lv, rv));
transform_operator!{gen; $op, $opI; dst, l, r, lv, rv; $($rest)*}
transform_operator!{replace_imm; $opI; dst, l, r, lv, rv}
}
_ => {}
};
None
};
f()
}};
}
let before = format!("{op}");
log(&format!("{op}\n"));
let reset = match op.opcode.clone() {
Either::Left(VirtualOp::MOVI(dst, imm)) => {
let imm = KnownRegValue::Const(imm.value() as u64);
if known_values.resolve(&dst) == Some(imm.clone()) {
op.opcode = Either::Left(VirtualOp::NOOP);
} else {
known_values.assign(dst, imm);
}
Some(ResetKnown::Nothing)
}
Either::Left(VirtualOp::MOVE(dst, src)) => {
if let Some(known_src) = known_values.resolve(&src) {
if known_values.resolve(&dst) == Some(known_src.clone()) {
op.opcode = Either::Left(VirtualOp::NOOP);
} else {
if let Some(imm) = known_src.value_as_imm18() {
op.opcode = Either::Left(VirtualOp::MOVI(dst.clone(), imm));
}
known_values.assign(dst.clone(), known_src);
}
} else {
known_values.assign(dst, KnownRegValue::Eq(src));
}
Some(ResetKnown::Nothing)
}
Either::Left(VirtualOp::ADD(..)) => transform_operator! {ADD, ADDI;
both_known: u64::checked_add;
if left is 0 assign right;
if right is 0 assign left;
commutative: true;
},
Either::Left(VirtualOp::SUB(..)) => transform_operator! {SUB, SUBI;
both_known: u64::checked_sub;
if right is 0 assign left;
},
Either::Left(VirtualOp::MUL(..)) => transform_operator! {MUL, MULI;
both_known: u64::checked_mul;
if left is 1 assign right;
if right is 1 assign left;
if left is 0 assign 0;
if right is 0 assign 0;
commutative: true;
},
Either::Left(VirtualOp::DIV(..)) => transform_operator! {DIV, DIVI;
both_known: u64::checked_div;
if right is 1 assign left;
if left is 0 assign 0;
},
Either::Left(VirtualOp::EXP(..)) => transform_operator! {EXP, EXPI;
both_known: u64::checked_pow;
if right is 0 assign 1;
if right is 1 assign left;
if left is 0 assign 0;
if left is 1 assign 1;
},
Either::Left(VirtualOp::MLOG(..)) => transform_operator! {MLOG, None;
both_known: u64::checked_ilog;
},
Either::Left(VirtualOp::MOD(..)) => transform_operator! {MOD, MODI;
both_known: u64::checked_rem;
if right is 1 assign 0;
},
Either::Left(VirtualOp::MROO(..)) => transform_operator! {MROO, None;
both_known: checked_nth_root;
if right is 1 assign left;
},
Either::Left(VirtualOp::NOT(dst, arg)) => {
let argv = known_values.resolve(&arg);
log(&format!(" {:?}\n", argv));
if let Some(KnownRegValue::Const(argv)) = argv {
let raw = argv.not();
known_values.assign(dst.clone(), KnownRegValue::Const(raw));
let span = op.owning_span.clone();
if let Ok(v) =
VirtualImmediate18::try_new(raw, span.unwrap_or_else(Span::dummy))
{
op.opcode = Either::Left(VirtualOp::MOVI(dst.clone(), v));
}
Some(ResetKnown::Nothing)
} else {
None
}
}
Either::Left(VirtualOp::AND(..)) => transform_operator! {AND, ANDI;
both_known: u64_bitand;
if left is 0 assign 0;
if right is 0 assign 0;
commutative: true;
},
Either::Left(VirtualOp::OR(..)) => transform_operator! {OR, ORI;
both_known: u64_bitor;
if left is 0 assign right;
if right is 0 assign left;
commutative: true;
},
Either::Left(VirtualOp::XOR(..)) => transform_operator! {XOR, XORI;
both_known: u64_bitxor;
if left is 0 assign right;
if right is 0 assign left;
commutative: true;
},
Either::Left(VirtualOp::SLL(..)) => transform_operator! {SLL, SLLI;
both_known: u64::checked_shl;
if right is 0 assign left;
},
Either::Left(VirtualOp::SRL(..)) => transform_operator! {SRL, SRLI;
both_known: u64::checked_shr;
if right is 0 assign left;
},
Either::Left(VirtualOp::EQ(..)) => transform_operator! {EQ, None;
both_known: u64_eq;
},
Either::Left(VirtualOp::GT(..)) => transform_operator! {GT, None;
both_known: u64_gt;
},
Either::Left(VirtualOp::LT(..)) => transform_operator! {LT, None;
both_known: u64_lt;
},
_ => None,
};
let after = format!("{op}");
if before != after {
log(&format!(" changed to: {op}\n"));
}
let reset = match reset {
None => {
match &op.opcode {
Either::Left(op) => match op {
VirtualOp::ECAL(_, _, _, _) => ResetKnown::All,
_ if op.has_side_effect() => ResetKnown::All,
_ => ResetKnown::Defs,
},
Either::Right(op) => match op {
ControlFlowOp::Label(label) => {
if jump_target_labels.contains_key(label) {
ResetKnown::All
} else {
ResetKnown::Defs
}
}
ControlFlowOp::Jump { type_, .. } => match type_ {
JumpType::Call => ResetKnown::All,
_ => ResetKnown::Defs,
},
ControlFlowOp::Comment
| ControlFlowOp::ConfigurablesOffsetPlaceholder
| ControlFlowOp::DataSectionOffsetPlaceholder => ResetKnown::Defs,
ControlFlowOp::PushAll(_) => ResetKnown::DefsAndNonVirtuals,
ControlFlowOp::PopAll(_) => ResetKnown::All,
ControlFlowOp::JumpToAddr(_) => ResetKnown::All,
ControlFlowOp::ReturnFromCall { .. } => ResetKnown::All,
},
}
}
Some(reset) => reset,
};
log(&format!(" {reset:?}\n"));
reset.apply(op, &mut known_values);
}
self
}
}
#[inline(always)]
fn u64_bitand(l: u64, r: u64) -> Option<u64> {
Some(l.bitand(r))
}
#[inline(always)]
fn u64_bitor(l: u64, r: u64) -> Option<u64> {
Some(l.bitor(r))
}
#[inline(always)]
fn u64_bitxor(l: u64, r: u64) -> Option<u64> {
Some(l.bitxor(r))
}
#[inline(always)]
fn u64_eq(l: u64, r: u64) -> Option<u64> {
Some(if l == r { 1 } else { 0 })
}
#[inline(always)]
fn u64_gt(l: u64, r: u64) -> Option<u64> {
Some(if l > r { 1 } else { 0 })
}
#[inline(always)]
fn u64_lt(l: u64, r: u64) -> Option<u64> {
Some(if l < r { 1 } else { 0 })
}
pub fn checked_nth_root(target: u64, nth_root: u64) -> Option<u64> {
if nth_root == 0 {
return None;
}
if nth_root == 1 || target <= 1 {
return Some(target);
}
if nth_root >= target || nth_root > 64 {
return Some(1);
}
let nth_root = u32::try_from(nth_root).expect("Never loses bits, checked above");
let powf = f64::powf;
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
let guess = powf(target as f64, (nth_root as f64).recip()) as u64;
debug_assert!(guess != 0, "This should never occur for {{target, n}} > 1");
let is_nth_power_below_target = |v: u64| match v.checked_pow(nth_root) {
Some(pow) => target < pow,
None => true, };
if is_nth_power_below_target(guess) {
return Some(guess.saturating_sub(1));
}
let guess_plus_one = guess
.checked_add(1)
.expect("Guess cannot be u64::MAX, as we have taken a root > 2 of a value to get it");
if is_nth_power_below_target(guess_plus_one) {
return Some(guess);
}
Some(guess_plus_one)
}
#[cfg(test)]
mod tests {
use super::*;
use expect_test::expect;
fn optimise(
ops: impl IntoIterator<Item = Op>,
f: impl FnOnce(AbstractInstructionSet) -> AbstractInstructionSet,
) {
let mut ops = AbstractInstructionSet {
function: None,
ops: ops.into_iter().collect(),
};
for i in 0..ops.ops.len() {
ops.ops[i].comment = i.to_string();
}
f(ops);
}
#[test]
fn constant_propagate_transform() {
let mut str = String::new();
let capture = |s: &str| {
str.push_str(s);
};
optimise(
[
VirtualOp::movi("0", 10).into(),
VirtualOp::movi("1", 10).into(),
VirtualOp::movi("2", 2).into(),
VirtualOp::movi("3", 8).into(),
VirtualOp::movi("4", 9).into(),
VirtualOp::movi("0", 10).into(),
VirtualOp::r#move("0", "1").into(),
VirtualOp::add(ConstantRegister::FuncArg0, "0", "1").into(),
VirtualOp::add(ConstantRegister::FuncArg1, ConstantRegister::Zero, "0").into(),
VirtualOp::add(ConstantRegister::FuncArg2, "0", ConstantRegister::Zero).into(),
VirtualOp::add(ConstantRegister::FuncArg3, "5", "0").into(),
VirtualOp::add(ConstantRegister::FuncArg4, "0", "5").into(),
VirtualOp::not("10", ConstantRegister::Zero).into(),
VirtualOp::add("11", "10", ConstantRegister::One).into(),
VirtualOp::sub(ConstantRegister::FuncArg0, "0", "1").into(),
VirtualOp::sub(ConstantRegister::FuncArg1, "0", ConstantRegister::Zero).into(),
VirtualOp::sub("11", ConstantRegister::Zero, ConstantRegister::One).into(),
VirtualOp::mul(ConstantRegister::FuncArg0, "0", "1").into(),
VirtualOp::mul(ConstantRegister::FuncArg1, ConstantRegister::One, "0").into(),
VirtualOp::mul(ConstantRegister::FuncArg2, "0", ConstantRegister::One).into(),
VirtualOp::mul(ConstantRegister::FuncArg3, ConstantRegister::Zero, "0").into(),
VirtualOp::mul(ConstantRegister::FuncArg4, "0", ConstantRegister::Zero).into(),
VirtualOp::mul(ConstantRegister::FuncArg5, "5", "0").into(),
VirtualOp::mul(ConstantRegister::FuncArg0, "0", "5").into(),
VirtualOp::not("10", ConstantRegister::Zero).into(),
VirtualOp::mul("11", "10", "10").into(),
VirtualOp::div(ConstantRegister::FuncArg0, "0", "1").into(),
VirtualOp::div(ConstantRegister::FuncArg1, "0", ConstantRegister::One).into(),
VirtualOp::div(ConstantRegister::FuncArg2, ConstantRegister::Zero, "0").into(),
VirtualOp::div("11", ConstantRegister::Zero, ConstantRegister::Zero).into(),
VirtualOp::exp(ConstantRegister::FuncArg0, "0", "2").into(),
VirtualOp::exp(
ConstantRegister::FuncArg0,
ConstantRegister::Zero,
ConstantRegister::Zero,
)
.into(),
VirtualOp::exp(ConstantRegister::FuncArg1, "0", ConstantRegister::Zero).into(),
VirtualOp::exp(ConstantRegister::FuncArg2, "0", ConstantRegister::One).into(),
VirtualOp::exp(ConstantRegister::FuncArg3, ConstantRegister::Zero, "0").into(),
VirtualOp::exp(ConstantRegister::FuncArg4, ConstantRegister::One, "0").into(),
VirtualOp::not("10", ConstantRegister::Zero).into(),
VirtualOp::exp("11", "10", "10").into(),
VirtualOp::mlog(ConstantRegister::FuncArg0, "3", "2").into(),
VirtualOp::mlog("11", ConstantRegister::Zero, "2").into(),
VirtualOp::mlog("11", "3", ConstantRegister::Zero).into(),
VirtualOp::mlog("11", "3", ConstantRegister::One).into(),
VirtualOp::r#mod(ConstantRegister::FuncArg0, "4", "2").into(),
VirtualOp::r#mod(ConstantRegister::FuncArg1, "4", ConstantRegister::One).into(),
VirtualOp::r#mod("11", "4", ConstantRegister::Zero).into(),
VirtualOp::mroo(ConstantRegister::FuncArg0, "4", "2").into(),
VirtualOp::mroo(ConstantRegister::FuncArg1, "4", ConstantRegister::One).into(),
VirtualOp::mroo("11", "4", ConstantRegister::Zero).into(),
VirtualOp::and(ConstantRegister::FuncArg0, "0", "2").into(),
VirtualOp::and(ConstantRegister::FuncArg1, ConstantRegister::Zero, "2").into(),
VirtualOp::and(ConstantRegister::FuncArg2, "2", ConstantRegister::Zero).into(),
VirtualOp::and(ConstantRegister::FuncArg3, "5", "0").into(),
VirtualOp::and(ConstantRegister::FuncArg4, "0", "5").into(),
VirtualOp::or(ConstantRegister::FuncArg0, "0", "2").into(),
VirtualOp::or(ConstantRegister::FuncArg1, ConstantRegister::Zero, "2").into(),
VirtualOp::or(ConstantRegister::FuncArg2, "2", ConstantRegister::Zero).into(),
VirtualOp::or(ConstantRegister::FuncArg3, "5", "0").into(),
VirtualOp::or(ConstantRegister::FuncArg4, "0", "5").into(),
VirtualOp::xor(ConstantRegister::FuncArg0, "0", "2").into(),
VirtualOp::xor(ConstantRegister::FuncArg1, ConstantRegister::Zero, "2").into(),
VirtualOp::xor(ConstantRegister::FuncArg2, "2", ConstantRegister::Zero).into(),
VirtualOp::xor(ConstantRegister::FuncArg3, "5", "0").into(),
VirtualOp::xor(ConstantRegister::FuncArg4, "0", "5").into(),
VirtualOp::sll(ConstantRegister::FuncArg0, "0", "2").into(),
VirtualOp::sll(ConstantRegister::FuncArg2, "2", ConstantRegister::Zero).into(),
VirtualOp::srl(ConstantRegister::FuncArg0, "0", "2").into(),
VirtualOp::srl(ConstantRegister::FuncArg2, "2", ConstantRegister::Zero).into(),
VirtualOp::eq(ConstantRegister::FuncArg0, "0", "2").into(),
VirtualOp::gt(ConstantRegister::FuncArg0, "0", "2").into(),
VirtualOp::lt(ConstantRegister::FuncArg0, "0", "2").into(),
VirtualOp::r#move("0", ConstantRegister::Error).into(),
VirtualOp::eq("1", "0", ConstantRegister::One).into(),
],
|ops| ops.constant_propagate(capture),
);
expect![[r#"
movi $r0 i10 ; 0
Nothing
movi $r1 i10 ; 1
Nothing
movi $r2 i2 ; 2
Nothing
movi $r3 i8 ; 3
Nothing
movi $r4 i9 ; 4
Nothing
movi $r0 i10 ; 5
changed to: noop ; 5
Nothing
move $r0 $r1 ; 6
changed to: noop ; 6
Nothing
add $$arg0 $r0 $r1 ; 7
Some(Const(10)) Some(Const(10))
changed to: movi $$arg0 i20 ; 7
Nothing
add $$arg1 $zero $r0 ; 8
Some(Const(0)) Some(Const(10))
changed to: movi $$arg1 i10 ; 8
Nothing
add $$arg2 $r0 $zero ; 9
Some(Const(10)) Some(Const(0))
changed to: movi $$arg2 i10 ; 9
Nothing
add $$arg3 $r5 $r0 ; 10
None Some(Const(10))
changed to: addi $$arg3 $r5 i10 ; 10
Defs
add $$arg4 $r0 $r5 ; 11
Some(Const(10)) None
changed to: addi $$arg4 $r5 i10 ; 11
Defs
not $r10 $zero ; 12
Some(Const(0))
Nothing
add $r11 $r10 $one ; 13
Some(Const(18446744073709551615)) Some(Const(1))
Defs
sub $$arg0 $r0 $r1 ; 14
Some(Const(10)) Some(Const(10))
changed to: movi $$arg0 i0 ; 14
Nothing
sub $$arg1 $r0 $zero ; 15
Some(Const(10)) Some(Const(0))
changed to: movi $$arg1 i10 ; 15
Nothing
sub $r11 $zero $one ; 16
Some(Const(0)) Some(Const(1))
Defs
mul $$arg0 $r0 $r1 ; 17
Some(Const(10)) Some(Const(10))
changed to: movi $$arg0 i100 ; 17
Nothing
mul $$arg1 $one $r0 ; 18
Some(Const(1)) Some(Const(10))
changed to: movi $$arg1 i10 ; 18
Nothing
mul $$arg2 $r0 $one ; 19
Some(Const(10)) Some(Const(1))
changed to: movi $$arg2 i10 ; 19
Nothing
mul $$arg3 $zero $r0 ; 20
Some(Const(0)) Some(Const(10))
changed to: movi $$arg3 i0 ; 20
Nothing
mul $$arg4 $r0 $zero ; 21
Some(Const(10)) Some(Const(0))
changed to: movi $$arg4 i0 ; 21
Nothing
mul $$arg5 $r5 $r0 ; 22
None Some(Const(10))
changed to: muli $$arg5 $r5 i10 ; 22
Defs
mul $$arg0 $r0 $r5 ; 23
Some(Const(10)) None
changed to: muli $$arg0 $r5 i10 ; 23
Defs
not $r10 $zero ; 24
Some(Const(0))
Nothing
mul $r11 $r10 $r10 ; 25
Some(Const(18446744073709551615)) Some(Const(18446744073709551615))
Defs
div $$arg0 $r0 $r1 ; 26
Some(Const(10)) Some(Const(10))
changed to: movi $$arg0 i1 ; 26
Nothing
div $$arg1 $r0 $one ; 27
Some(Const(10)) Some(Const(1))
changed to: movi $$arg1 i10 ; 27
Nothing
div $$arg2 $zero $r0 ; 28
Some(Const(0)) Some(Const(10))
changed to: movi $$arg2 i0 ; 28
Nothing
div $r11 $zero $zero ; 29
Some(Const(0)) Some(Const(0))
Defs
exp $$arg0 $r0 $r2 ; 30
Some(Const(10)) Some(Const(2))
changed to: movi $$arg0 i100 ; 30
Nothing
exp $$arg0 $zero $zero ; 31
Some(Const(0)) Some(Const(0))
changed to: movi $$arg0 i1 ; 31
Nothing
exp $$arg1 $r0 $zero ; 32
Some(Const(10)) Some(Const(0))
changed to: movi $$arg1 i1 ; 32
Nothing
exp $$arg2 $r0 $one ; 33
Some(Const(10)) Some(Const(1))
changed to: movi $$arg2 i10 ; 33
Nothing
exp $$arg3 $zero $r0 ; 34
Some(Const(0)) Some(Const(10))
changed to: movi $$arg3 i0 ; 34
Nothing
exp $$arg4 $one $r0 ; 35
Some(Const(1)) Some(Const(10))
changed to: movi $$arg4 i1 ; 35
Nothing
not $r10 $zero ; 36
Some(Const(0))
Nothing
exp $r11 $r10 $r10 ; 37
Some(Const(18446744073709551615)) Some(Const(18446744073709551615))
Defs
mlog $$arg0 $r3 $r2 ; 38
Some(Const(8)) Some(Const(2))
changed to: movi $$arg0 i3 ; 38
Nothing
mlog $r11 $zero $r2 ; 39
Some(Const(0)) Some(Const(2))
Defs
mlog $r11 $r3 $zero ; 40
Some(Const(8)) Some(Const(0))
Defs
mlog $r11 $r3 $one ; 41
Some(Const(8)) Some(Const(1))
Defs
mod $$arg0 $r4 $r2 ; 42
Some(Const(9)) Some(Const(2))
changed to: movi $$arg0 i1 ; 42
Nothing
mod $$arg1 $r4 $one ; 43
Some(Const(9)) Some(Const(1))
changed to: movi $$arg1 i0 ; 43
Nothing
mod $r11 $r4 $zero ; 44
Some(Const(9)) Some(Const(0))
Defs
mroo $$arg0 $r4 $r2 ; 45
Some(Const(9)) Some(Const(2))
changed to: movi $$arg0 i3 ; 45
Nothing
mroo $$arg1 $r4 $one ; 46
Some(Const(9)) Some(Const(1))
changed to: movi $$arg1 i9 ; 46
Nothing
mroo $r11 $r4 $zero ; 47
Some(Const(9)) Some(Const(0))
Defs
and $$arg0 $r0 $r2 ; 48
Some(Const(10)) Some(Const(2))
changed to: movi $$arg0 i2 ; 48
Nothing
and $$arg1 $zero $r2 ; 49
Some(Const(0)) Some(Const(2))
changed to: movi $$arg1 i0 ; 49
Nothing
and $$arg2 $r2 $zero ; 50
Some(Const(2)) Some(Const(0))
changed to: movi $$arg2 i0 ; 50
Nothing
and $$arg3 $r5 $r0 ; 51
None Some(Const(10))
changed to: andi $$arg3 $r5 i10 ; 51
Defs
and $$arg4 $r0 $r5 ; 52
Some(Const(10)) None
changed to: andi $$arg4 $r5 i10 ; 52
Defs
or $$arg0 $r0 $r2 ; 53
Some(Const(10)) Some(Const(2))
changed to: movi $$arg0 i10 ; 53
Nothing
or $$arg1 $zero $r2 ; 54
Some(Const(0)) Some(Const(2))
changed to: movi $$arg1 i2 ; 54
Nothing
or $$arg2 $r2 $zero ; 55
Some(Const(2)) Some(Const(0))
changed to: movi $$arg2 i2 ; 55
Nothing
or $$arg3 $r5 $r0 ; 56
None Some(Const(10))
changed to: ori $$arg3 $r5 i10 ; 56
Defs
or $$arg4 $r0 $r5 ; 57
Some(Const(10)) None
changed to: ori $$arg4 $r5 i10 ; 57
Defs
xor $$arg0 $r0 $r2 ; 58
Some(Const(10)) Some(Const(2))
changed to: movi $$arg0 i8 ; 58
Nothing
xor $$arg1 $zero $r2 ; 59
Some(Const(0)) Some(Const(2))
changed to: movi $$arg1 i2 ; 59
Nothing
xor $$arg2 $r2 $zero ; 60
Some(Const(2)) Some(Const(0))
changed to: movi $$arg2 i2 ; 60
Nothing
xor $$arg3 $r5 $r0 ; 61
None Some(Const(10))
changed to: xori $$arg3 $r5 i10 ; 61
Defs
xor $$arg4 $r0 $r5 ; 62
Some(Const(10)) None
changed to: xori $$arg4 $r5 i10 ; 62
Defs
sll $$arg0 $r0 $r2 ; 63
Some(Const(10)) Some(Const(2))
changed to: movi $$arg0 i40 ; 63
Nothing
sll $$arg2 $r2 $zero ; 64
Some(Const(2)) Some(Const(0))
changed to: movi $$arg2 i2 ; 64
Nothing
srl $$arg0 $r0 $r2 ; 65
Some(Const(10)) Some(Const(2))
changed to: movi $$arg0 i2 ; 65
Nothing
srl $$arg2 $r2 $zero ; 66
Some(Const(2)) Some(Const(0))
changed to: movi $$arg2 i2 ; 66
Nothing
eq $$arg0 $r0 $r2 ; 67
Some(Const(10)) Some(Const(2))
changed to: movi $$arg0 i0 ; 67
Nothing
gt $$arg0 $r0 $r2 ; 68
Some(Const(10)) Some(Const(2))
changed to: movi $$arg0 i1 ; 68
Nothing
lt $$arg0 $r0 $r2 ; 69
Some(Const(10)) Some(Const(2))
changed to: movi $$arg0 i0 ; 69
Nothing
move $r0 $err ; 70
Nothing
eq $r1 $r0 $one ; 71
None Some(Const(1))
Defs
"#]]
.assert_eq(&str);
}
}