vyre-conform 0.1.0

Conformance suite for vyre backends — proves byte-identical output to CPU reference
Documentation
use crate::spec::engine::eval::types::*;

fn evaluate_program(
    program: &ConformProgram,
    runtime: &RuntimeProgram,
    file_bytes: &[u8],
    file_boundaries: &[u32],
    file_ctx: ConformFileContext,
) -> Result<bool, String> {
    let mut stack = Vec::with_capacity(32);
    for instruction in &program.instructions {
        let Some(kind) = instruction.kind() else {
            return Err(format!("unknown opcode {}", instruction.opcode));
        };
        match kind {
            ConformOpcode::PushTrue => stack.push(1),
            ConformOpcode::PushFalse => stack.push(0),
            ConformOpcode::PushImmediate => stack.push(instruction.operand),
            ConformOpcode::PushFileSize => stack.push(file_ctx.file_size),
            ConformOpcode::PushStringMatched => {
                stack.push(u32::from(count(runtime, instruction.operand) != 0));
            }
            ConformOpcode::PushStringCount => stack.push(count(runtime, instruction.operand)),
            ConformOpcode::PushStringOffset => {
                let slot = pop(&mut stack)?;
                stack.push(position(runtime, instruction.operand, slot));
            }
            ConformOpcode::PushStringLength => {
                let slot = pop(&mut stack)?;
                stack.push(length(runtime, instruction.operand, slot));
            }
            ConformOpcode::PushNumStrings | ConformOpcode::PushEntryCount => {
                stack.push(runtime.counts.iter().filter(|&&c| c != 0).count() as u32);
            }
            ConformOpcode::UniquePatternCount => stack.push(file_ctx.unique_pattern_count),
            ConformOpcode::TotalMatchCount => stack.push(file_ctx.total_match_count),
            ConformOpcode::PushEntropy => stack.push(file_ctx.entropy_bucket),
            ConformOpcode::PushIsPe => stack.push(file_ctx.is_pe),
            ConformOpcode::PushIsDll => stack.push(file_ctx.is_dll),
            ConformOpcode::PushIs64bit => stack.push(file_ctx.is_64bit),
            ConformOpcode::PushHasSignature => stack.push(file_ctx.has_signature),
            ConformOpcode::PushMagicU32 => stack.push(file_ctx.magic_u32),
            ConformOpcode::PushNumSections => stack.push(file_ctx.num_sections),
            ConformOpcode::PushNumImports => stack.push(file_ctx.num_imports),
            ConformOpcode::PushEntryPoint => stack.push(file_ctx.entry_point_rva),
            ConformOpcode::PushFileAge => stack.push(file_ctx.file_age_seconds),
            ConformOpcode::And => binary_bool(&mut stack, |a, b| a != 0 && b != 0)?,
            ConformOpcode::Or => binary_bool(&mut stack, |a, b| a != 0 || b != 0)?,
            ConformOpcode::Not => {
                let value = pop(&mut stack)?;
                stack.push(u32::from(value == 0));
            }
            ConformOpcode::Eq => binary_bool(&mut stack, |a, b| a == b)?,
            ConformOpcode::Neq => binary_bool(&mut stack, |a, b| a != b)?,
            ConformOpcode::Lt => binary_bool(&mut stack, |a, b| a < b)?,
            ConformOpcode::Gt => binary_bool(&mut stack, |a, b| a > b)?,
            ConformOpcode::Lte => binary_bool(&mut stack, |a, b| a <= b)?,
            ConformOpcode::Gte => binary_bool(&mut stack, |a, b| a >= b)?,
            ConformOpcode::Add => binary_value(&mut stack, u32::wrapping_add)?,
            ConformOpcode::Sub => binary_value(&mut stack, u32::wrapping_sub)?,
            ConformOpcode::Mul => binary_value(&mut stack, u32::wrapping_mul)?,
            ConformOpcode::Div => binary_value(&mut stack, |a, b| if b == 0 { 0 } else { a / b })?,
            ConformOpcode::Mod => binary_value(&mut stack, |a, b| if b == 0 { 0 } else { a % b })?,
            ConformOpcode::BitAnd => binary_value(&mut stack, |a, b| a & b)?,
            ConformOpcode::BitOr => binary_value(&mut stack, |a, b| a | b)?,
            ConformOpcode::BitXor => binary_value(&mut stack, |a, b| a ^ b)?,
            ConformOpcode::Shl => binary_value(&mut stack, |a, b| a << (b & 31))?,
            ConformOpcode::Shr => binary_value(&mut stack, |a, b| a >> (b & 31))?,
            ConformOpcode::AnyOf => reduce(&mut stack, instruction.operand, |values| {
                values.iter().any(|&v| v != 0)
            })?,
            ConformOpcode::AllOf => reduce(&mut stack, instruction.operand, |values| {
                values.iter().all(|&v| v != 0)
            })?,
            ConformOpcode::CountOf => {
                let needed = instruction.operand & 0xFFFF;
                let width = instruction.operand >> 16;
                reduce(&mut stack, width, |values| {
                    values.iter().filter(|&&v| v != 0).count() as u32 >= needed
                })?;
            }
            ConformOpcode::StringAt => {
                let wanted = pop(&mut stack)?;
                stack.push(u32::from(
                    positions(runtime, instruction.operand).contains(&wanted),
                ));
            }
            ConformOpcode::StringIn => {
                let hi = pop(&mut stack)?;
                let lo = pop(&mut stack)?;
                stack.push(u32::from(
                    positions(runtime, instruction.operand)
                        .iter()
                        .any(|&pos| pos >= lo && pos <= hi),
                ));
            }
            ConformOpcode::MatchOrder => {
                let b = pop(&mut stack)?;
                let a = pop(&mut stack)?;
                let pa = first_position(runtime, a);
                let pb = first_position(runtime, b);
                stack.push(u32::from(
                    pa != ABORT_SENTINEL && pb != ABORT_SENTINEL && pa < pb,
                ));
            }
            ConformOpcode::MatchDistance => {
                let b = pop(&mut stack)?;
                let a = pop(&mut stack)?;
                stack.push(match_distance(runtime, a, b));
            }
            ConformOpcode::MatchBetween => {
                let c = pop(&mut stack)?;
                let b = pop(&mut stack)?;
                let a = pop(&mut stack)?;
                let pa = first_position(runtime, a);
                let pb = first_position(runtime, b);
                let lo = pa.min(pb);
                let hi = pa.max(pb);
                stack.push(u32::from(
                    pa != ABORT_SENTINEL
                        && pb != ABORT_SENTINEL
                        && positions(runtime, c).iter().any(|&p| p >= lo && p <= hi),
                ));
            }
            ConformOpcode::SameFile | ConformOpcode::DifferentFile => {
                let b = pop(&mut stack)?;
                let a = pop(&mut stack)?;
                let pa = first_position(runtime, a);
                let pb = first_position(runtime, b);
                let same = pa != ABORT_SENTINEL
                    && pb != ABORT_SENTINEL
                    && file_id_at(file_boundaries, pa) == file_id_at(file_boundaries, pb);
                stack.push(u32::from(if kind == ConformOpcode::SameFile {
                    same
                } else {
                    !same && pa != ABORT_SENTINEL && pb != ABORT_SENTINEL
                }));
            }
            ConformOpcode::FileIdOf => {
                let pattern = pop(&mut stack)?;
                let pos = first_position(runtime, pattern);
                stack.push(if pos == ABORT_SENTINEL {
                    ABORT_SENTINEL
                } else {
                    file_id_at(file_boundaries, pos)
                });
            }
            ConformOpcode::CrossFileChain => {
                let c = pop(&mut stack)?;
                let b = pop(&mut stack)?;
                let a = pop(&mut stack)?;
                let pa = first_position(runtime, a);
                let pb = first_position(runtime, b);
                let pc = first_position(runtime, c);
                let fa = file_id_at(file_boundaries, pa);
                stack.push(u32::from(
                    pa != ABORT_SENTINEL
                        && pb != ABORT_SENTINEL
                        && pc != ABORT_SENTINEL
                        && fa == file_id_at(file_boundaries, pc)
                        && fa != file_id_at(file_boundaries, pb),
                ));
            }
            ConformOpcode::ReadByteAt | ConformOpcode::ByteAt => {
                let offset = pop(&mut stack)? as usize;
                stack.push(file_bytes.get(offset).copied().unwrap_or(0) as u32);
            }
            ConformOpcode::Halt => return Ok(stack.last().copied().unwrap_or(0) != 0),
        }
    }
    Ok(false)
}