Split the monolithic interpreter into cohesive modules: dedicated decoder (decoder.rs) producing 8-byte DecodedInstr; opcode tables (opcode.rs); explicit traps (trap.rs); FPSCR helpers (fpscr.rs); overflow/carry helpers (overflow.rs); a 4 KiB-page-versioned decode cache and basic-block cache (block_cache.rs); and a full VMX/VMX128 implementation (vmx.rs) covering AltiVec + Xenon's 128-bit extensions. Add the parallel-execution substrate behind --parallel: a 7-party phaser (phaser.rs) for round-based barrier sync, ReservationTable (reservation.rs) for guest LL/SC, and the per-HW-thread scheduler core (scheduler.rs) that owns ThreadRefs, runqueues, and pending IRQs. Disassembler is now the single source of truth: disasm.rs gains the full base + extended + VMX128 mnemonic set, with golden JSON fixtures and a disasm_goldens test suite. Add a criterion-style interpreter bench. context.rs grows the per-thread state the new modules need (reservation slot, FPSCR, vector regs). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
96 lines
3.1 KiB
Rust
96 lines
3.1 KiB
Rust
//! TO-field evaluation for `tw`, `twi`, `td`, `tdi`.
|
|
//!
|
|
//! The TO field (5 bits) encodes which comparison outcomes trigger a trap:
|
|
//!
|
|
//! | bit | condition |
|
|
//! |-----|-----------|
|
|
//! | 0 | a < b (signed) |
|
|
//! | 1 | a > b (signed) |
|
|
//! | 2 | a == b |
|
|
//! | 3 | a < b (unsigned) |
|
|
//! | 4 | a > b (unsigned) |
|
|
//!
|
|
//! The bit numbering matches PowerISA ("MSB is bit 0"): TO[0] corresponds to
|
|
//! the high bit of the 5-bit field, i.e. (to >> 4) & 1.
|
|
//!
|
|
//! `tw` / `twi` compare the low 32 bits of the operands (sign-extended back to
|
|
//! 64 for the signed comparison); `td` / `tdi` compare the full 64 bits.
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub enum TrapWidth {
|
|
Word, // tw, twi: 32-bit
|
|
Doubleword, // td, tdi: 64-bit
|
|
}
|
|
|
|
const TO_SLT: u32 = 1 << 4; // a < b signed
|
|
const TO_SGT: u32 = 1 << 3; // a > b signed
|
|
const TO_EQ: u32 = 1 << 2; // a == b
|
|
const TO_ULT: u32 = 1 << 1; // a < b unsigned
|
|
const TO_UGT: u32 = 1 << 0; // a > b unsigned
|
|
|
|
/// Returns true when the trap should fire.
|
|
pub fn evaluate(to: u32, a: u64, b: u64, width: TrapWidth) -> bool {
|
|
let (sa, sb, ua, ub): (i64, i64, u64, u64) = match width {
|
|
TrapWidth::Word => (
|
|
a as i32 as i64,
|
|
b as i32 as i64,
|
|
a as u32 as u64,
|
|
b as u32 as u64,
|
|
),
|
|
TrapWidth::Doubleword => (a as i64, b as i64, a, b),
|
|
};
|
|
|
|
if (to & TO_SLT) != 0 && sa < sb { return true; }
|
|
if (to & TO_SGT) != 0 && sa > sb { return true; }
|
|
if (to & TO_EQ) != 0 && ua == ub { return true; }
|
|
if (to & TO_ULT) != 0 && ua < ub { return true; }
|
|
if (to & TO_UGT) != 0 && ua > ub { return true; }
|
|
false
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn to_zero_never_traps() {
|
|
assert!(!evaluate(0, 0, 0, TrapWidth::Doubleword));
|
|
assert!(!evaluate(0, 5, 3, TrapWidth::Doubleword));
|
|
assert!(!evaluate(0, !0, 0, TrapWidth::Doubleword));
|
|
}
|
|
|
|
#[test]
|
|
fn to_31_always_traps_when_any_condition_holds() {
|
|
// 31 = 0b11111 = all conditions enabled
|
|
assert!(evaluate(31, 1, 2, TrapWidth::Doubleword)); // slt+ult
|
|
assert!(evaluate(31, 2, 1, TrapWidth::Doubleword)); // sgt+ugt
|
|
assert!(evaluate(31, 7, 7, TrapWidth::Doubleword)); // eq
|
|
}
|
|
|
|
#[test]
|
|
fn to_eq_only() {
|
|
// TO[2] = 0b00100 = 4
|
|
assert!(evaluate(4, 5, 5, TrapWidth::Doubleword));
|
|
assert!(!evaluate(4, 5, 6, TrapWidth::Doubleword));
|
|
}
|
|
|
|
#[test]
|
|
fn to_signed_vs_unsigned_on_negative() {
|
|
// a=-1 (as u64 = all-ones). TO[0]=slt enabled = 0b10000 = 16
|
|
// Signed: -1 < 0 → true
|
|
let neg1 = (-1i64) as u64;
|
|
assert!(evaluate(16, neg1, 0, TrapWidth::Doubleword));
|
|
// TO[3]=ult enabled = 0b00010 = 2 → unsigned: all-ones < 0 is false
|
|
assert!(!evaluate(2, neg1, 0, TrapWidth::Doubleword));
|
|
}
|
|
|
|
#[test]
|
|
fn word_width_ignores_high_32_bits() {
|
|
// a's low 32 = 1, high 32 = different; b = 1. With TO=eq, should trap.
|
|
let a = 0xDEAD_BEEF_0000_0001u64;
|
|
assert!(evaluate(4, a, 1, TrapWidth::Word));
|
|
// In doubleword, different.
|
|
assert!(!evaluate(4, a, 1, TrapWidth::Doubleword));
|
|
}
|
|
}
|