use core::arch::asm;
macro_rules! by_step {
() => {
concat!(
"lea {t}, [{g} + {f}]\n",
"mov {s}, {g}\n",
"sub {s}, {f}\n",
"mov {a:e}, 1\n",
"sub {a}, {d}\n",
"lea {e}, [{d} + 1]\n",
"mov {c}, {f}\n",
"mov {b}, {e}\n",
"test {d}, {d}\n",
"cmovg {t}, {s}\n",
"cmovg {c}, {g}\n",
"cmovg {b}, {a}\n",
"test {g:e}, 1\n",
"cmovz {t}, {g}\n",
"cmovz {c}, {f}\n",
"cmovz {b}, {e}\n",
"sar {t}, 1\n",
"mov {f}, {c}\n",
"mov {g}, {t}\n",
"mov {d}, {b}\n",
)
};
}
macro_rules! hd_step {
() => {
concat!(
"lea {t}, [{g} + {f}]\n",
"mov {s}, {g}\n",
"sub {s}, {f}\n",
"mov {a}, {d}\n",
"not {a}\n",
"lea {a}, [{a} - 1]\n",
"lea {e}, [{d} - 1]\n",
"mov {c}, {f}\n",
"mov {b}, {e}\n",
"test {d}, {d}\n",
"cmovs {t}, {s}\n",
"cmovs {c}, {g}\n",
"cmovs {b}, {a}\n",
"test {g:e}, 1\n",
"cmovz {t}, {g}\n",
"cmovz {c}, {f}\n",
"cmovz {b}, {e}\n",
"sar {t}, 1\n",
"mov {f}, {c}\n",
"mov {g}, {t}\n",
"mov {d}, {b}\n",
)
};
}
macro_rules! x2 {
($s:ident) => {
concat!($s!(), $s!())
};
}
macro_rules! x4 {
($s:ident) => {
concat!(x2!($s), x2!($s))
};
}
macro_rules! x16 {
($s:ident) => {
concat!(x4!($s), x4!($s), x4!($s), x4!($s))
};
}
macro_rules! runner {
($name:ident, $doc:literal, $($body:tt)*) => {
#[doc = $doc]
#[inline(always)]
pub(super) fn $name(d: &mut i64, f: &mut i64, g: &mut i64) {
unsafe {
asm!(
".p2align 6",
$($body)*,
d = inout(reg) *d,
f = inout(reg) *f,
g = inout(reg) *g,
t = out(reg) _,
s = out(reg) _,
a = out(reg) _,
e = out(reg) _,
c = out(reg) _,
b = out(reg) _,
options(pure, nomem, nostack),
);
}
}
};
}
runner!(
by_19,
"Nineteen divsteps.",
x16!(by_step),
x2!(by_step),
by_step!()
);
runner!(by_5, "Five divsteps.", x4!(by_step), by_step!());
runner!(
hd_19,
"Nineteen half-delta divsteps.",
x16!(hd_step),
x2!(hd_step),
hd_step!()
);
runner!(hd_2, "Two half-delta divsteps.", x2!(hd_step));