use crate::program::{Instruction, InstructionVec, Opcode};
use crate::register::{NUM_REGISTERS, RegisterId, RegisterSet};
use crate::scheduler::Scheduler;
pub(crate) use model::{Pass, RegisterWriter};
mod model {
use crate::program::{NUM_INSTRUCTIONS, Opcode};
use crate::register::{self, RegisterId};
pub(super) const REQUIRED_INSTRUCTIONS: usize = NUM_INSTRUCTIONS;
pub(super) const REQUIRED_OVERALL_RESULT_AT_CYCLE: usize = 194;
pub(super) const REQUIRED_MULTIPLIES: usize = 192;
#[inline(always)]
pub(super) fn is_multiply(op: Opcode) -> bool {
matches!(op, Opcode::Mul | Opcode::SMulH | Opcode::UMulH)
}
#[inline(always)]
pub(super) fn disallow_src_is_dst(op: Opcode) -> bool {
matches!(
op,
Opcode::AddShift | Opcode::Mul | Opcode::Sub | Opcode::Xor
)
}
pub(super) const DISALLOW_REGISTER_FOR_ADDSHIFT: RegisterId = register::R5;
#[inline(always)]
pub(super) fn disallow_opcode_pair(previous: Opcode, proposed: Opcode) -> bool {
match proposed {
Opcode::Mul | Opcode::UMulH | Opcode::SMulH | Opcode::Target | Opcode::Branch => false,
Opcode::AddConst | Opcode::Xor | Opcode::XorConst | Opcode::Rotate => {
previous == proposed
}
Opcode::AddShift | Opcode::Sub => {
previous == Opcode::AddShift || previous == Opcode::Sub
}
}
}
#[inline(always)]
pub(super) fn writer_pair_allowed(
pass: Pass,
last_writer: RegisterWriter,
this_writer: RegisterWriter,
) -> bool {
match (last_writer, this_writer) {
(RegisterWriter::Mul(_), RegisterWriter::Mul(_)) if matches!(pass, Pass::Original) => {
false
}
(last_writer, this_writer) => last_writer != this_writer,
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub(crate) enum Pass {
Original,
Retry,
}
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
pub(crate) enum RegisterWriter {
#[default]
None,
Mul(RegisterId),
UMulH(u32),
SMulH(u32),
AddSub(RegisterId),
AddConst,
Xor(RegisterId),
XorConst,
Rotate,
}
}
#[derive(Debug, Clone)]
pub(crate) struct Validator {
writer_map: RegisterWriterMap,
multiply_count: usize,
}
impl Validator {
#[inline(always)]
pub(crate) fn new() -> Self {
Self {
writer_map: RegisterWriterMap::new(),
multiply_count: 0,
}
}
#[inline(always)]
pub(crate) fn commit_instruction(&mut self, inst: &Instruction, regw: RegisterWriter) {
if model::is_multiply(inst.opcode()) {
self.multiply_count += 1;
}
match inst.destination() {
None => debug_assert_eq!(regw, RegisterWriter::None),
Some(dst) => self.writer_map.insert(dst, regw),
}
}
#[inline(always)]
pub(crate) fn check_whole_program(
&self,
scheduler: &Scheduler,
instructions: &InstructionVec,
) -> Result<(), ()> {
if instructions.len() == model::REQUIRED_INSTRUCTIONS
&& scheduler.overall_latency().as_usize() == model::REQUIRED_OVERALL_RESULT_AT_CYCLE
&& self.multiply_count == model::REQUIRED_MULTIPLIES
{
Ok(())
} else {
Err(())
}
}
#[inline(always)]
pub(crate) fn dst_registers_allowed(
&self,
op: Opcode,
pass: Pass,
writer_info: RegisterWriter,
src: Option<RegisterId>,
) -> DstRegisterChecker<'_> {
DstRegisterChecker {
pass,
writer_info,
writer_map: &self.writer_map,
op_is_add_shift: op == Opcode::AddShift,
disallow_equal: if model::disallow_src_is_dst(op) {
src
} else {
None
},
}
}
}
#[derive(Debug, Clone)]
pub(crate) struct DstRegisterChecker<'v> {
pass: Pass,
writer_map: &'v RegisterWriterMap,
writer_info: RegisterWriter,
op_is_add_shift: bool,
disallow_equal: Option<RegisterId>,
}
impl<'v> DstRegisterChecker<'v> {
#[inline(always)]
pub(crate) fn check(&self, dst: RegisterId) -> bool {
if self.op_is_add_shift && dst == model::DISALLOW_REGISTER_FOR_ADDSHIFT {
return false;
}
if Some(dst) == self.disallow_equal {
return false;
}
model::writer_pair_allowed(self.pass, self.writer_map.get(dst), self.writer_info)
}
}
#[inline(always)]
pub(crate) fn src_registers_allowed(available: RegisterSet, op: Opcode) -> RegisterSet {
if op == Opcode::AddShift
&& available.contains(model::DISALLOW_REGISTER_FOR_ADDSHIFT)
&& available.len() == 2
{
RegisterSet::from_filter(
#[inline(always)]
|reg| reg == model::DISALLOW_REGISTER_FOR_ADDSHIFT,
)
} else {
available
}
}
#[inline(always)]
pub(crate) fn opcode_pair_allowed(previous: Option<Opcode>, proposed: Opcode) -> Result<(), ()> {
match previous {
None => Ok(()),
Some(previous) => {
if model::disallow_opcode_pair(previous, proposed) {
Err(())
} else {
Ok(())
}
}
}
}
#[derive(Default, Debug, Clone)]
struct RegisterWriterMap([RegisterWriter; NUM_REGISTERS]);
impl RegisterWriterMap {
#[inline(always)]
fn new() -> Self {
Default::default()
}
#[inline(always)]
fn insert(&mut self, reg: RegisterId, writer: RegisterWriter) {
self.0[reg.as_usize()] = writer;
}
#[inline(always)]
fn get(&self, reg: RegisterId) -> RegisterWriter {
self.0[reg.as_usize()]
}
}