use std::mem::offset_of;
use cranelift_codegen::ir::condcodes::IntCC;
use cranelift_codegen::ir::{
self, AbiParam, Block, BlockArg, InstBuilder, MemFlags, Signature, Value, types,
};
use cranelift_codegen::isa::CallConv;
use cranelift_frontend::{FunctionBuilder, Variable};
#[cfg(target_os = "windows")]
const HOST_CALL_CONV: CallConv = CallConv::WindowsFastcall;
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
const HOST_CALL_CONV: CallConv = CallConv::AppleAarch64;
#[cfg(not(any(
target_os = "windows",
all(target_os = "macos", target_arch = "aarch64")
)))]
const HOST_CALL_CONV: CallConv = CallConv::SystemV;
use crate::core::{
DspState, InterruptState, MemSpace, MemoryMap, PowerState, RegionKind, interrupt,
jit_read_accu24, jit_read_ssh, jit_rnd56, jit_update_rn, jit_write_mem, jit_write_sp,
jit_write_ssh, jit_write_ssl, reg, sr,
};
use dsp56300_core::{
Accumulator, CondCode, Instruction, MulShiftOp, PERIPH_BASE, ParallelAlu, ParallelMoveType,
REG_MASKS, decode, mask_pc,
};
mod agu;
mod alu;
mod bitops;
mod control;
mod flags;
mod logical;
mod loops;
mod mem;
mod moves;
mod regs;
fn inst_len_for_ea(ea_mode: u8) -> u32 {
if (ea_mode >> 3) == 6 { 2 } else { 1 }
}
const OFF_PC: i32 = offset_of!(DspState, pc) as i32;
const OFF_REGS: i32 = offset_of!(DspState, registers) as i32;
const OFF_STACK: i32 = offset_of!(DspState, stack) as i32;
const OFF_LOOP_REP: i32 = offset_of!(DspState, loop_rep) as i32;
const OFF_PC_ON_REP: i32 = offset_of!(DspState, pc_on_rep) as i32;
const OFF_PC_ADVANCE: i32 = offset_of!(DspState, pc_advance) as i32;
const OFF_INTERRUPT_STATE: i32 = offset_of!(DspState, interrupts.state) as i32;
const OFF_INTERRUPT_PENDING_BITS: i32 = offset_of!(DspState, interrupts.pending_bits) as i32;
const PROMOTED_REGS: [usize; 36] = [
reg::SR,
reg::X0,
reg::X1,
reg::Y0,
reg::Y1,
reg::R0,
reg::R1,
reg::R2,
reg::R3,
reg::R4,
reg::R5,
reg::R6,
reg::R7,
reg::N0,
reg::N1,
reg::N2,
reg::N3,
reg::N4,
reg::N5,
reg::N6,
reg::N7,
reg::M0,
reg::M1,
reg::M2,
reg::M3,
reg::M4,
reg::M5,
reg::M6,
reg::M7,
reg::LC,
reg::LA,
reg::SP,
reg::SSH,
reg::SSL,
reg::OMR,
reg::TEMP,
];
struct PromotedRegs {
vars: [Option<Variable>; 64],
dirty: [bool; 64],
valid: [bool; 64],
acc: [Variable; 2],
acc_dirty: [bool; 2],
acc_valid: [bool; 2],
}
struct DeferredScope {
pre_block: Block,
deferred: [bool; 64],
acc_deferred: [bool; 2],
entry_valid: [bool; 64],
entry_acc_valid: [bool; 2],
}
struct ConditionalState {
saved_dirty: [bool; 64],
saved_acc_dirty: [bool; 2],
saved_valid: [bool; 64],
saved_acc_valid: [bool; 2],
modified: [bool; 64],
modified_acc: [bool; 2],
}
#[derive(Clone, Copy, PartialEq)]
enum BitOp {
Clear,
Set,
Toggle,
Test,
}
enum LogicalOp {
And,
Or,
Eor,
}
enum BitTestAddr {
Pp { space: MemSpace, pp_offset: u8 },
Qq { space: MemSpace, qq_offset: u8 },
Aa { space: MemSpace, addr: u8 },
Reg { reg_idx: u8 },
Ea { space: MemSpace, ea_mode: u8 },
}
impl BitTestAddr {
fn pp(space: MemSpace, pp_offset: u8) -> Self {
Self::Pp { space, pp_offset }
}
fn qq(space: MemSpace, qq_offset: u8) -> Self {
Self::Qq { space, qq_offset }
}
fn aa(space: MemSpace, addr: u8) -> Self {
Self::Aa { space, addr }
}
fn reg(reg_idx: u8) -> Self {
Self::Reg { reg_idx }
}
fn ea(space: MemSpace, ea_mode: u8) -> Self {
Self::Ea { space, ea_mode }
}
}
enum BitTestBranch {
Jump,
JumpSub { pc: u32 },
Branch { pc: u32 },
BranchSub { pc: u32 },
}
enum PendingFlags {
AluAddSub {
result56: Value,
source: Value,
dest: Value,
result_raw: Value,
is_sub: bool,
},
NzClearV { result56: Value },
NzOnly { result56: Value },
NzClearVSm { result56: Value },
MacVlSm {
result56: Value,
product: Value,
acc: Value,
},
NzVlSm { result56: Value, overflow: Value },
NzSm { result56: Value },
NzVclSub {
result56: Value,
source: Value,
dest: Value,
result_raw: Value,
},
AddlSubl {
result56: Value,
source: Value,
dest_shifted: Value,
result_raw: Value,
is_sub: bool,
asl_carry: Value,
asl_v: Value,
},
DmacVl {
result56: Value,
product: Value,
acc: Value,
},
Shift24 {
carry: Value,
n_val: Option<Value>,
result: Value,
},
Logical { result24: Value },
}
pub struct Emitter<'a> {
builder: FunctionBuilder<'a>,
state_ptr: Value,
ptr_ty: ir::Type,
total_cycles: Variable,
inst_len: Variable,
promoted: PromotedRegs,
map: &'a MemoryMap,
pending_cycles: i64,
scope_stack: Vec<DeferredScope>,
instructions_block: Block,
sm_needs_sat_var: Variable,
pending_flags: Option<PendingFlags>,
}
impl<'a> Emitter<'a> {
pub fn new(mut builder: FunctionBuilder<'a>, ptr_ty: ir::Type, map: &'a MemoryMap) -> Self {
let total_cycles = builder.declare_var(types::I32);
let inst_len = builder.declare_var(types::I32);
let sm_needs_sat_var = builder.declare_var(types::I32);
let entry_block = builder.create_block();
builder.append_block_params_for_function_params(entry_block);
builder.switch_to_block(entry_block);
builder.seal_block(entry_block);
let state_ptr = builder.block_params(entry_block)[0];
let zero = builder.ins().iconst(types::I32, 0);
builder.def_var(total_cycles, zero);
builder.def_var(inst_len, zero);
builder.def_var(sm_needs_sat_var, zero);
let mut promoted = PromotedRegs {
vars: [None; 64],
dirty: [false; 64],
valid: [false; 64],
acc: [Variable::from_u32(0); 2], acc_dirty: [false; 2],
acc_valid: [false; 2],
};
let zero_i32 = builder.ins().iconst(types::I32, 0);
for &idx in &PROMOTED_REGS {
let var = builder.declare_var(types::I32);
builder.def_var(var, zero_i32);
promoted.vars[idx] = Some(var);
}
let zero_i64 = builder.ins().iconst(types::I64, 0);
for i in 0..2 {
let var = builder.declare_var(types::I64);
builder.def_var(var, zero_i64);
promoted.acc[i] = var;
}
let pre_entry = builder.create_block();
builder.ins().jump(pre_entry, &[]);
let instructions_block = builder.create_block();
builder.switch_to_block(instructions_block);
let entry_scope = DeferredScope {
pre_block: pre_entry,
deferred: [false; 64],
acc_deferred: [false; 2],
entry_valid: [false; 64],
entry_acc_valid: [false; 2],
};
Self {
builder,
state_ptr,
ptr_ty,
total_cycles,
inst_len,
promoted,
map,
pending_cycles: 0,
scope_stack: vec![entry_scope],
instructions_block,
sm_needs_sat_var,
pending_flags: None,
}
}
pub fn emit_instruction(&mut self, inst: &Instruction, pc: u32, next_word: u32) {
match inst {
Instruction::Nop => self.emit_nop(),
Instruction::AddImm { imm, d } => self.emit_add_imm(*imm, *d),
Instruction::SubImm { imm, d } => self.emit_sub_imm(*imm, *d),
Instruction::Inc { d } => self.emit_inc(*d),
Instruction::Dec { d } => self.emit_dec(*d),
Instruction::AndI { imm, dest } => self.emit_andi(*imm, *dest),
Instruction::OrI { imm, dest } => self.emit_ori(*imm, *dest),
Instruction::Jmp { addr } => self.emit_jmp(*addr),
Instruction::Jcc { cc, addr } => self.emit_jcc(*cc, *addr),
Instruction::Jsr { addr } => self.emit_jsr(*addr, pc),
Instruction::Rts => self.emit_rts(),
Instruction::Rti => self.emit_rti(),
Instruction::Bra { addr } => self.emit_bra(*addr, pc),
Instruction::Bcc { cc, addr } => self.emit_bcc(*cc, *addr, pc),
Instruction::MovecImm { imm, dest } => self.emit_movec_imm(*imm, *dest),
Instruction::MovecReg {
src_reg,
dst_reg,
w,
} => self.emit_movec_reg(*src_reg, *dst_reg, *w),
Instruction::BclrPp {
space,
pp_offset,
bit_num,
} => self.emit_bit_op_pp(*space, *pp_offset, *bit_num, BitOp::Clear),
Instruction::BclrQq {
space,
qq_offset,
bit_num,
} => self.emit_bit_op_qq(*space, *qq_offset, *bit_num, BitOp::Clear),
Instruction::BsetPp {
space,
pp_offset,
bit_num,
} => self.emit_bit_op_pp(*space, *pp_offset, *bit_num, BitOp::Set),
Instruction::BsetQq {
space,
qq_offset,
bit_num,
} => self.emit_bit_op_qq(*space, *qq_offset, *bit_num, BitOp::Set),
Instruction::BtstPp {
space,
pp_offset,
bit_num,
} => self.emit_bit_op_pp(*space, *pp_offset, *bit_num, BitOp::Test),
Instruction::BtstQq {
space,
qq_offset,
bit_num,
} => self.emit_bit_op_qq(*space, *qq_offset, *bit_num, BitOp::Test),
Instruction::BclrReg { reg_idx, bit_num } => {
self.emit_bit_op_reg(*reg_idx, *bit_num, BitOp::Clear)
}
Instruction::BsetReg { reg_idx, bit_num } => {
self.emit_bit_op_reg(*reg_idx, *bit_num, BitOp::Set)
}
Instruction::JclrPp {
space,
pp_offset,
bit_num,
} => self.emit_bit_test_branch(
&BitTestAddr::pp(*space, *pp_offset),
*bit_num,
next_word,
false,
BitTestBranch::Jump,
),
Instruction::JclrQq {
space,
qq_offset,
bit_num,
} => self.emit_bit_test_branch(
&BitTestAddr::qq(*space, *qq_offset),
*bit_num,
next_word,
false,
BitTestBranch::Jump,
),
Instruction::JsetPp {
space,
pp_offset,
bit_num,
} => self.emit_bit_test_branch(
&BitTestAddr::pp(*space, *pp_offset),
*bit_num,
next_word,
true,
BitTestBranch::Jump,
),
Instruction::JsetQq {
space,
qq_offset,
bit_num,
} => self.emit_bit_test_branch(
&BitTestAddr::qq(*space, *qq_offset),
*bit_num,
next_word,
true,
BitTestBranch::Jump,
),
Instruction::DoImm { count } => self.emit_do_imm(*count, pc, next_word),
Instruction::DoForever => self.emit_do_forever(pc, next_word),
Instruction::RepImm { count } => self.emit_rep_imm(*count),
Instruction::EndDo => self.emit_enddo(),
Instruction::Movep0 {
pp_offset,
reg_idx,
w,
space,
} => self.emit_movep_0(*space, *pp_offset, *reg_idx, *w),
Instruction::Movep1 {
pp_offset,
ea_mode,
w,
space,
} => self.emit_movep_1(*space, *pp_offset, *ea_mode, *w, next_word),
Instruction::Movep23 {
pp_offset,
ea_mode,
w,
perspace,
easpace,
} => self.emit_movep_23(*pp_offset, *ea_mode, *w, *perspace, *easpace, next_word),
Instruction::AddLong { d } => self.emit_add_long(*d, next_word),
Instruction::SubLong { d } => self.emit_sub_long(*d, next_word),
Instruction::CmpLong { d } => self.emit_cmp_long(*d, next_word),
Instruction::CmpImm { imm, d } => self.emit_cmp_imm(*imm, *d),
Instruction::AndImm { imm, d } => {
self.emit_logical_op_acc(*d, *imm as i64, 1, LogicalOp::And)
}
Instruction::AndLong { d } => {
self.emit_logical_op_acc(*d, next_word as i64, 2, LogicalOp::And)
}
Instruction::OrImm { imm, d } => {
self.emit_logical_op_acc(*d, *imm as i64, 1, LogicalOp::Or)
}
Instruction::OrLong { d } => {
self.emit_logical_op_acc(*d, next_word as i64, 2, LogicalOp::Or)
}
Instruction::EorImm { imm, d } => {
self.emit_logical_op_acc(*d, *imm as i64, 1, LogicalOp::Eor)
}
Instruction::EorLong { d } => {
self.emit_logical_op_acc(*d, next_word as i64, 2, LogicalOp::Eor)
}
Instruction::BraLong => self.emit_bra_long(pc, next_word),
Instruction::BccLong { cc } => self.emit_bcc_long(*cc, pc, next_word),
Instruction::BsrLong => self.emit_bsr_long(pc, next_word),
Instruction::Bsr { addr } => self.emit_bsr(*addr, pc),
Instruction::BccRn { cc, rn } => self.emit_bcc_rn(*cc, *rn, pc),
Instruction::BraRn { rn } => self.emit_bra_rn(*rn, pc),
Instruction::BsrRn { rn } => self.emit_bsr_rn(*rn, pc),
Instruction::Bscc { cc, addr } => self.emit_bscc(*cc, *addr, pc),
Instruction::BsccLong { cc } => self.emit_bscc_long(*cc, pc, next_word),
Instruction::BsccRn { cc, rn } => self.emit_bscc_rn(*cc, *rn, pc),
Instruction::Brkcc { cc } => self.emit_brkcc(*cc),
Instruction::MoveLongDisp {
space,
w,
offreg_idx,
numreg,
} => self.emit_move_long_disp(*space, *w, *offreg_idx, *numreg, next_word),
Instruction::Tcc { cc, acc, r } => self.emit_tcc(*cc, *acc, *r),
Instruction::Lua { ea_mode, dst_reg } => self.emit_lua(*ea_mode, *dst_reg),
Instruction::LuaRel {
aa,
addr_reg,
dst_reg,
dest_is_n,
} => self.emit_lua_rel(*aa, *addr_reg, *dst_reg, *dest_is_n),
Instruction::LraRn { addr_reg, dst_reg } => self.emit_lra_rn(*addr_reg, *dst_reg, pc),
Instruction::LraDisp { dst_reg } => self.emit_lra_disp(*dst_reg, pc, next_word),
Instruction::DoReg { reg_idx } => self.emit_do_reg(*reg_idx, pc, next_word),
Instruction::RepReg { reg_idx } => self.emit_rep_reg(*reg_idx),
Instruction::JmpEa { ea_mode } => self.emit_jmp_ea(*ea_mode, next_word),
Instruction::JsrEa { ea_mode } => self.emit_jsr_ea(*ea_mode, pc, next_word),
Instruction::JccEa { cc, ea_mode } => self.emit_jcc_ea(*cc, *ea_mode, next_word),
Instruction::MovecEa {
ea_mode,
numreg,
w,
space,
} => self.emit_movec_ea(*ea_mode, *numreg, *w, *space, next_word),
Instruction::MovecAa {
addr,
numreg,
w,
space,
} => self.emit_movec_aa(*addr, *numreg, *w, *space),
Instruction::MovemEa { ea_mode, numreg, w } => {
self.emit_movem_ea(*ea_mode, *numreg, *w, next_word)
}
Instruction::AslImm { shift, s, d } => self.emit_asl_imm(*shift, *s, *d),
Instruction::AsrImm { shift, s, d } => self.emit_asr_imm(*shift, *s, *d),
Instruction::AslReg { src, s, d } => self.emit_asl_reg(*src, *s, *d),
Instruction::AsrReg { src, s, d } => self.emit_asr_reg(*src, *s, *d),
Instruction::JclrReg { reg_idx, bit_num } => self.emit_bit_test_branch(
&BitTestAddr::reg(*reg_idx),
*bit_num,
next_word,
false,
BitTestBranch::Jump,
),
Instruction::JsetReg { reg_idx, bit_num } => self.emit_bit_test_branch(
&BitTestAddr::reg(*reg_idx),
*bit_num,
next_word,
true,
BitTestBranch::Jump,
),
Instruction::BclrAa {
space,
addr,
bit_num,
} => self.emit_bit_op_aa(*space, *addr, *bit_num, BitOp::Clear),
Instruction::BsetAa {
space,
addr,
bit_num,
} => self.emit_bit_op_aa(*space, *addr, *bit_num, BitOp::Set),
Instruction::BtstAa {
space,
addr,
bit_num,
} => self.emit_bit_op_aa(*space, *addr, *bit_num, BitOp::Test),
Instruction::BtstReg { reg_idx, bit_num } => {
self.emit_bit_op_reg(*reg_idx, *bit_num, BitOp::Test)
}
Instruction::BchgPp {
space,
pp_offset,
bit_num,
} => self.emit_bit_op_pp(*space, *pp_offset, *bit_num, BitOp::Toggle),
Instruction::BchgQq {
space,
qq_offset,
bit_num,
} => self.emit_bit_op_qq(*space, *qq_offset, *bit_num, BitOp::Toggle),
Instruction::BchgAa {
space,
addr,
bit_num,
} => self.emit_bit_op_aa(*space, *addr, *bit_num, BitOp::Toggle),
Instruction::BchgReg { reg_idx, bit_num } => {
self.emit_bit_op_reg(*reg_idx, *bit_num, BitOp::Toggle)
}
Instruction::JclrAa {
space,
addr,
bit_num,
} => self.emit_bit_test_branch(
&BitTestAddr::aa(*space, *addr),
*bit_num,
next_word,
false,
BitTestBranch::Jump,
),
Instruction::JsetAa {
space,
addr,
bit_num,
} => self.emit_bit_test_branch(
&BitTestAddr::aa(*space, *addr),
*bit_num,
next_word,
true,
BitTestBranch::Jump,
),
Instruction::JsclrPp {
space,
pp_offset,
bit_num,
} => self.emit_bit_test_branch(
&BitTestAddr::pp(*space, *pp_offset),
*bit_num,
next_word,
false,
BitTestBranch::JumpSub { pc },
),
Instruction::JsclrQq {
space,
qq_offset,
bit_num,
} => self.emit_bit_test_branch(
&BitTestAddr::qq(*space, *qq_offset),
*bit_num,
next_word,
false,
BitTestBranch::JumpSub { pc },
),
Instruction::JsclrAa {
space,
addr,
bit_num,
} => self.emit_bit_test_branch(
&BitTestAddr::aa(*space, *addr),
*bit_num,
next_word,
false,
BitTestBranch::JumpSub { pc },
),
Instruction::JsclrReg { reg_idx, bit_num } => self.emit_bit_test_branch(
&BitTestAddr::reg(*reg_idx),
*bit_num,
next_word,
false,
BitTestBranch::JumpSub { pc },
),
Instruction::JssetPp {
space,
pp_offset,
bit_num,
} => self.emit_bit_test_branch(
&BitTestAddr::pp(*space, *pp_offset),
*bit_num,
next_word,
true,
BitTestBranch::JumpSub { pc },
),
Instruction::JssetQq {
space,
qq_offset,
bit_num,
} => self.emit_bit_test_branch(
&BitTestAddr::qq(*space, *qq_offset),
*bit_num,
next_word,
true,
BitTestBranch::JumpSub { pc },
),
Instruction::JssetAa {
space,
addr,
bit_num,
} => self.emit_bit_test_branch(
&BitTestAddr::aa(*space, *addr),
*bit_num,
next_word,
true,
BitTestBranch::JumpSub { pc },
),
Instruction::JssetReg { reg_idx, bit_num } => self.emit_bit_test_branch(
&BitTestAddr::reg(*reg_idx),
*bit_num,
next_word,
true,
BitTestBranch::JumpSub { pc },
),
Instruction::BrclrEa {
space,
ea_mode,
bit_num,
} => self.emit_bit_test_branch(
&BitTestAddr::ea(*space, *ea_mode),
*bit_num,
next_word,
false,
BitTestBranch::Branch { pc },
),
Instruction::BrclrAa {
space,
addr,
bit_num,
} => self.emit_bit_test_branch(
&BitTestAddr::aa(*space, *addr),
*bit_num,
next_word,
false,
BitTestBranch::Branch { pc },
),
Instruction::BrclrPp {
space,
pp_offset,
bit_num,
} => self.emit_bit_test_branch(
&BitTestAddr::pp(*space, *pp_offset),
*bit_num,
next_word,
false,
BitTestBranch::Branch { pc },
),
Instruction::BrclrQq {
space,
qq_offset,
bit_num,
} => self.emit_bit_test_branch(
&BitTestAddr::qq(*space, *qq_offset),
*bit_num,
next_word,
false,
BitTestBranch::Branch { pc },
),
Instruction::BrclrReg { reg_idx, bit_num } => self.emit_bit_test_branch(
&BitTestAddr::reg(*reg_idx),
*bit_num,
next_word,
false,
BitTestBranch::Branch { pc },
),
Instruction::BrsetEa {
space,
ea_mode,
bit_num,
} => self.emit_bit_test_branch(
&BitTestAddr::ea(*space, *ea_mode),
*bit_num,
next_word,
true,
BitTestBranch::Branch { pc },
),
Instruction::BrsetAa {
space,
addr,
bit_num,
} => self.emit_bit_test_branch(
&BitTestAddr::aa(*space, *addr),
*bit_num,
next_word,
true,
BitTestBranch::Branch { pc },
),
Instruction::BrsetPp {
space,
pp_offset,
bit_num,
} => self.emit_bit_test_branch(
&BitTestAddr::pp(*space, *pp_offset),
*bit_num,
next_word,
true,
BitTestBranch::Branch { pc },
),
Instruction::BrsetQq {
space,
qq_offset,
bit_num,
} => self.emit_bit_test_branch(
&BitTestAddr::qq(*space, *qq_offset),
*bit_num,
next_word,
true,
BitTestBranch::Branch { pc },
),
Instruction::BrsetReg { reg_idx, bit_num } => self.emit_bit_test_branch(
&BitTestAddr::reg(*reg_idx),
*bit_num,
next_word,
true,
BitTestBranch::Branch { pc },
),
Instruction::BsclrEa {
space,
ea_mode,
bit_num,
} => self.emit_bit_test_branch(
&BitTestAddr::ea(*space, *ea_mode),
*bit_num,
next_word,
false,
BitTestBranch::BranchSub { pc },
),
Instruction::BsclrAa {
space,
addr,
bit_num,
} => self.emit_bit_test_branch(
&BitTestAddr::aa(*space, *addr),
*bit_num,
next_word,
false,
BitTestBranch::BranchSub { pc },
),
Instruction::BsclrPp {
space,
pp_offset,
bit_num,
} => self.emit_bit_test_branch(
&BitTestAddr::pp(*space, *pp_offset),
*bit_num,
next_word,
false,
BitTestBranch::BranchSub { pc },
),
Instruction::BsclrQq {
space,
qq_offset,
bit_num,
} => self.emit_bit_test_branch(
&BitTestAddr::qq(*space, *qq_offset),
*bit_num,
next_word,
false,
BitTestBranch::BranchSub { pc },
),
Instruction::BsclrReg { reg_idx, bit_num } => self.emit_bit_test_branch(
&BitTestAddr::reg(*reg_idx),
*bit_num,
next_word,
false,
BitTestBranch::BranchSub { pc },
),
Instruction::BssetEa {
space,
ea_mode,
bit_num,
} => self.emit_bit_test_branch(
&BitTestAddr::ea(*space, *ea_mode),
*bit_num,
next_word,
true,
BitTestBranch::BranchSub { pc },
),
Instruction::BssetAa {
space,
addr,
bit_num,
} => self.emit_bit_test_branch(
&BitTestAddr::aa(*space, *addr),
*bit_num,
next_word,
true,
BitTestBranch::BranchSub { pc },
),
Instruction::BssetPp {
space,
pp_offset,
bit_num,
} => self.emit_bit_test_branch(
&BitTestAddr::pp(*space, *pp_offset),
*bit_num,
next_word,
true,
BitTestBranch::BranchSub { pc },
),
Instruction::BssetQq {
space,
qq_offset,
bit_num,
} => self.emit_bit_test_branch(
&BitTestAddr::qq(*space, *qq_offset),
*bit_num,
next_word,
true,
BitTestBranch::BranchSub { pc },
),
Instruction::BssetReg { reg_idx, bit_num } => self.emit_bit_test_branch(
&BitTestAddr::reg(*reg_idx),
*bit_num,
next_word,
true,
BitTestBranch::BranchSub { pc },
),
Instruction::Jscc { cc, addr } => self.emit_jscc(*cc, *addr, pc),
Instruction::DoAa { space, addr } => self.emit_do_aa(*space, *addr, pc, next_word),
Instruction::DoEa { space, ea_mode } => {
self.emit_do_ea(*space, *ea_mode, pc, next_word)
}
Instruction::DorImm { count } => self.emit_dor_imm(*count, pc, next_word),
Instruction::DorReg { reg_idx } => self.emit_dor_reg(*reg_idx, pc, next_word),
Instruction::DorForever => self.emit_dor_forever(pc, next_word),
Instruction::DorAa { space, addr } => self.emit_dor_aa(*space, *addr, pc, next_word),
Instruction::DorEa { space, ea_mode } => {
self.emit_dor_ea(*space, *ea_mode, pc, next_word)
}
Instruction::RepAa { space, addr } => self.emit_rep_aa(*space, *addr),
Instruction::RepEa { space, ea_mode } => self.emit_rep_ea(*space, *ea_mode),
Instruction::MoveShortDisp {
space,
offset,
w,
offreg_idx,
numreg,
} => self.emit_move_short_disp(*offset, *w, *offreg_idx, *numreg, *space),
Instruction::MovemAa { addr, numreg, w } => self.emit_movem_aa(*addr, *numreg, *w),
Instruction::LslImm { shift, d } => self.emit_lsl_imm(*shift, *d),
Instruction::LsrImm { shift, d } => self.emit_lsr_imm(*shift, *d),
Instruction::LslReg { src, d } => self.emit_lsl_reg(*src, *d),
Instruction::LsrReg { src, d } => self.emit_lsr_reg(*src, *d),
Instruction::CmpU { src, d } => self.emit_cmpu(*src, *d),
Instruction::MulShift {
op,
shift,
src,
d,
k,
} => self.emit_mul_shift(*op, *shift, *src, *d, *k),
Instruction::MpyI { k, d, src } => self.emit_mpyi(*k, *d, *src, next_word),
Instruction::MpyrI { k, d, src } => self.emit_mpyri(*k, *d, *src, next_word),
Instruction::MacI { k, d, src } => self.emit_maci(*k, *d, *src, next_word),
Instruction::MacrI { k, d, src } => self.emit_macri(*k, *d, *src, next_word),
Instruction::Dmac { ss, k, d, s1, s2 } => self.emit_dmac(*ss, *k, *d, *s1, *s2),
Instruction::MacSU { s, k, d, s1, s2 } => self.emit_mac_su(*s, *k, *d, *s1, *s2),
Instruction::MpySU { s, k, d, s1, s2 } => self.emit_mpy_su(*s, *k, *d, *s1, *s2),
Instruction::Div { src, d } => self.emit_div(*src, *d),
Instruction::Norm { rreg_idx, d } => self.emit_norm(*rreg_idx, *d),
Instruction::BclrEa {
space,
ea_mode,
bit_num,
} => self.emit_bit_op_ea(*space, *ea_mode, *bit_num, BitOp::Clear, next_word),
Instruction::BsetEa {
space,
ea_mode,
bit_num,
} => self.emit_bit_op_ea(*space, *ea_mode, *bit_num, BitOp::Set, next_word),
Instruction::BtstEa {
space,
ea_mode,
bit_num,
} => self.emit_bit_op_ea(*space, *ea_mode, *bit_num, BitOp::Test, next_word),
Instruction::BchgEa {
space,
ea_mode,
bit_num,
} => self.emit_bit_op_ea(*space, *ea_mode, *bit_num, BitOp::Toggle, next_word),
Instruction::JclrEa {
space,
ea_mode,
bit_num,
} => self.emit_bit_test_branch(
&BitTestAddr::ea(*space, *ea_mode),
*bit_num,
next_word,
false,
BitTestBranch::Jump,
),
Instruction::JsetEa {
space,
ea_mode,
bit_num,
} => self.emit_bit_test_branch(
&BitTestAddr::ea(*space, *ea_mode),
*bit_num,
next_word,
true,
BitTestBranch::Jump,
),
Instruction::JsclrEa {
space,
ea_mode,
bit_num,
} => self.emit_bit_test_branch(
&BitTestAddr::ea(*space, *ea_mode),
*bit_num,
next_word,
false,
BitTestBranch::JumpSub { pc },
),
Instruction::JssetEa {
space,
ea_mode,
bit_num,
} => self.emit_bit_test_branch(
&BitTestAddr::ea(*space, *ea_mode),
*bit_num,
next_word,
true,
BitTestBranch::JumpSub { pc },
),
Instruction::JsccEa { cc, ea_mode } => self.emit_jscc_ea(*cc, *ea_mode, pc, next_word),
Instruction::MovepQq {
qq_offset,
ea_mode,
w,
qqspace,
easpace,
} => self.emit_movep_qq(*qq_offset, *ea_mode, *w, *qqspace, *easpace, next_word),
Instruction::MovepQqPea {
qq_offset,
ea_mode,
w,
space,
} => self.emit_movep_qq_pea(*qq_offset, *ea_mode, *w, *space, next_word),
Instruction::MovepQqR {
qq_offset,
reg_idx,
w,
space,
} => self.emit_movep_qq_r(*qq_offset, *reg_idx, *w, *space),
Instruction::Reset => self.emit_reset(),
Instruction::Stop => self.emit_stop(),
Instruction::Parallel {
alu,
move_type,
opcode,
} => self.emit_parallel(*opcode, *move_type, alu, next_word),
Instruction::Wait => self.emit_wait(),
Instruction::Illegal => self.emit_illegal(),
Instruction::Clb { s, d } => self.emit_clb(*s, *d),
Instruction::Normf { src, d } => self.emit_normf(*src, *d),
Instruction::Merge { src, d } => self.emit_merge(*src, *d),
Instruction::ExtractReg { s1, s2, d } => {
self.emit_extract(Some(*s1), *s2, *d, next_word, false)
}
Instruction::ExtractImm { s2, d } => self.emit_extract(None, *s2, *d, next_word, false),
Instruction::ExtractuReg { s1, s2, d } => {
self.emit_extract(Some(*s1), *s2, *d, next_word, true)
}
Instruction::ExtractuImm { s2, d } => self.emit_extract(None, *s2, *d, next_word, true),
Instruction::InsertReg { s1, s2, d } => self.emit_insert(Some(*s1), *s2, *d, next_word),
Instruction::InsertImm { s2, d } => self.emit_insert(None, *s2, *d, next_word),
Instruction::Vsl { s, ea_mode, i_bit } => {
self.emit_vsl(*s, *ea_mode, *i_bit, next_word)
}
Instruction::Debug => self.emit_nop_like(1),
Instruction::Debugcc { .. } => {
self.set_inst_len(1);
self.set_cycles(5);
}
Instruction::Trap => {
self.set_inst_len(1);
self.set_cycles(9);
self.emit_add_interrupt(interrupt::TRAP);
}
Instruction::Trapcc { cc } => {
self.set_inst_len(1);
self.set_cycles(9);
self.emit_trapcc(*cc);
}
Instruction::Pflush | Instruction::Pflushun | Instruction::Pfree => {
self.emit_nop_like(decode::instruction_length(inst))
}
Instruction::Plockr | Instruction::Punlockr => {
self.set_inst_len(decode::instruction_length(inst));
self.set_cycles(4);
}
Instruction::PlockEa { ea_mode, .. } | Instruction::PunlockEa { ea_mode, .. } => {
self.set_inst_len(inst_len_for_ea(*ea_mode));
self.set_cycles(2);
self.emit_calc_ea_ext(*ea_mode as u32, next_word);
}
_ => {
let len = decode::instruction_length(inst);
self.set_inst_len(len);
self.set_cycles(2);
}
}
}
fn push_loop_scope(&mut self, pre_loop: Block) {
self.scope_stack.push(DeferredScope {
pre_block: pre_loop,
deferred: [false; 64],
acc_deferred: [false; 2],
entry_valid: self.promoted.valid,
entry_acc_valid: self.promoted.acc_valid,
});
}
fn pop_loop_scope(&mut self, loop_header: Block) {
let ctx = self.scope_stack.pop().unwrap();
let parent_entry_valid = self.scope_stack.last().unwrap().entry_valid;
let parent_entry_acc_valid = self.scope_stack.last().unwrap().entry_acc_valid;
self.builder.switch_to_block(ctx.pre_block);
self.builder.seal_block(ctx.pre_block);
for &idx in &PROMOTED_REGS {
if ctx.deferred[idx] {
if !parent_entry_valid[idx] {
self.scope_stack.last_mut().unwrap().deferred[idx] = true;
} else {
let val = self.load_u32(Self::reg_offset(idx));
self.builder.def_var(self.promoted.vars[idx].unwrap(), val);
}
}
}
for (i, &acc) in [Accumulator::A, Accumulator::B].iter().enumerate() {
if ctx.acc_deferred[i] {
if !parent_entry_acc_valid[i] {
self.scope_stack.last_mut().unwrap().acc_deferred[i] = true;
} else {
self.reload_acc(acc);
}
}
}
self.builder.ins().jump(loop_header, &[]);
self.builder.seal_block(loop_header);
}
pub fn finalize_and_return(mut self) {
self.flush_pending_flags();
self.flush_all_to_memory();
self.flush_pending_cycles();
let cycles = self.builder.use_var(self.total_cycles);
self.builder.ins().return_(&[cycles]);
let entry_ctx = self.scope_stack.pop().unwrap();
debug_assert!(self.scope_stack.is_empty());
self.builder.switch_to_block(entry_ctx.pre_block);
self.builder.seal_block(entry_ctx.pre_block);
for &idx in &PROMOTED_REGS {
if entry_ctx.deferred[idx] {
let val = self.load_u32(Self::reg_offset(idx));
self.builder.def_var(self.promoted.vars[idx].unwrap(), val);
}
}
for (i, &acc) in [Accumulator::A, Accumulator::B].iter().enumerate() {
if entry_ctx.acc_deferred[i] {
self.reload_acc(acc);
}
}
self.builder.ins().jump(self.instructions_block, &[]);
self.builder.seal_block(self.instructions_block);
self.builder.finalize();
}
fn is_block_terminator(inst: &Instruction) -> bool {
matches!(
inst,
Instruction::Jmp { .. }
| Instruction::Jcc { .. }
| Instruction::Jsr { .. }
| Instruction::Rts
| Instruction::Rti
| Instruction::Bra { .. }
| Instruction::Bcc { .. }
| Instruction::BraLong
| Instruction::BsrLong
| Instruction::BccLong { .. }
| Instruction::DoImm { .. }
| Instruction::DoEa { .. }
| Instruction::DoAa { .. }
| Instruction::DoReg { .. }
| Instruction::DoForever
| Instruction::DorImm { .. }
| Instruction::DorReg { .. }
| Instruction::DorForever
| Instruction::DorAa { .. }
| Instruction::DorEa { .. }
| Instruction::EndDo
| Instruction::JclrPp { .. }
| Instruction::JclrQq { .. }
| Instruction::JsetPp { .. }
| Instruction::JsetQq { .. }
| Instruction::JclrReg { .. }
| Instruction::JsetReg { .. }
| Instruction::JclrEa { .. }
| Instruction::JsetEa { .. }
| Instruction::JclrAa { .. }
| Instruction::JsetAa { .. }
| Instruction::JsclrPp { .. }
| Instruction::JsclrQq { .. }
| Instruction::JsclrAa { .. }
| Instruction::JsclrReg { .. }
| Instruction::JsclrEa { .. }
| Instruction::JssetPp { .. }
| Instruction::JssetQq { .. }
| Instruction::JssetAa { .. }
| Instruction::JssetReg { .. }
| Instruction::JssetEa { .. }
| Instruction::BccRn { .. }
| Instruction::BraRn { .. }
| Instruction::BsrRn { .. }
| Instruction::Bscc { .. }
| Instruction::BsccLong { .. }
| Instruction::BsccRn { .. }
| Instruction::Brkcc { .. }
| Instruction::BrclrEa { .. }
| Instruction::BrclrAa { .. }
| Instruction::BrclrPp { .. }
| Instruction::BrclrQq { .. }
| Instruction::BrclrReg { .. }
| Instruction::BrsetEa { .. }
| Instruction::BrsetAa { .. }
| Instruction::BrsetPp { .. }
| Instruction::BrsetQq { .. }
| Instruction::BrsetReg { .. }
| Instruction::BsclrEa { .. }
| Instruction::BsclrAa { .. }
| Instruction::BsclrPp { .. }
| Instruction::BsclrQq { .. }
| Instruction::BsclrReg { .. }
| Instruction::BssetEa { .. }
| Instruction::BssetAa { .. }
| Instruction::BssetPp { .. }
| Instruction::BssetQq { .. }
| Instruction::BssetReg { .. }
| Instruction::Jscc { .. }
| Instruction::JsccEa { .. }
| Instruction::JmpEa { .. }
| Instruction::JsrEa { .. }
| Instruction::JccEa { .. }
| Instruction::Bsr { .. }
| Instruction::Wait
| Instruction::Stop
| Instruction::Illegal
)
}
fn needs_exit_check(inst: &Instruction) -> bool {
matches!(
inst,
Instruction::Movep0 { .. }
| Instruction::Movep1 { .. }
| Instruction::Movep23 { .. }
| Instruction::MovepQq { .. }
| Instruction::MovepQqPea { .. }
| Instruction::MovepQqR { .. }
| Instruction::BsetPp { .. }
| Instruction::BclrPp { .. }
| Instruction::BchgPp { .. }
| Instruction::BsetQq { .. }
| Instruction::BclrQq { .. }
| Instruction::BchgQq { .. }
)
}
fn writes_p_memory(inst: &Instruction) -> bool {
match inst {
Instruction::MovemEa { w, .. } | Instruction::MovemAa { w, .. } => !w,
Instruction::Movep1 { w, .. } | Instruction::MovepQqPea { w, .. } => !w,
_ => false,
}
}
pub fn emit_block(&mut self, start_pc: u32, max_len: u32, stop_pc: u32) -> u32 {
let p_end = self.map.p_space_end();
let mut pc = start_pc;
let mut count = 0u32;
let mut ended_with_terminator = false;
loop {
if count >= max_len || pc >= p_end {
break;
}
let opcode = self.map.read_pram(pc);
let next_word = self.map.read_pram(mask_pc(pc + 1));
let inst = decode::decode(opcode);
let inst_len = decode::instruction_length(&inst);
if Self::is_rep_instruction(&inst) {
self.emit_rep_inline(&inst, pc);
let rep_inst_pc = pc + 1;
let rep_opcode = self.map.read_pram(rep_inst_pc);
let rep_inst = decode::decode(rep_opcode);
let rep_inst_len = decode::instruction_length(&rep_inst);
pc += 1 + rep_inst_len;
count += 2; } else if Self::is_do_instruction(&inst) {
let is_forever = matches!(inst, Instruction::DoForever | Instruction::DorForever);
let la = Self::compute_do_la(&inst, pc, next_word);
if !is_forever && Self::is_do_body_inlineable(self.map, pc + 2, la, stop_pc) {
self.emit_do_inline(&inst, pc, la);
let body_count = Self::count_body_instructions(self.map, pc + 2, la);
count += 1 + body_count; pc = la + 1;
} else {
self.emit_instruction(&inst, pc, next_word);
pc += inst_len;
ended_with_terminator = true;
break;
}
} else {
let is_terminator = Self::is_block_terminator(&inst);
let check_exit = Self::needs_exit_check(&inst);
self.emit_instruction(&inst, pc, next_word);
count += 1;
pc += inst_len;
if check_exit && !is_terminator {
self.flush_pending_cycles();
let off_exit = offset_of!(DspState, exit_requested) as i32;
let exit_val =
self.builder
.ins()
.load(types::I8, Self::flags(), self.state_ptr, off_exit);
let early_ret = self.builder.create_block();
let cont = self.builder.create_block();
self.builder.ins().brif(exit_val, early_ret, &[], cont, &[]);
self.builder.switch_to_block(early_ret);
self.builder.seal_block(early_ret);
self.flush_all_to_memory();
let ret_cycles = self.builder.use_var(self.total_cycles);
let next_pc_val = self.builder.ins().iconst(types::I32, pc as i64);
self.store_pc(next_pc_val);
self.builder.ins().return_(&[ret_cycles]);
self.builder.switch_to_block(cont);
self.builder.seal_block(cont);
}
if is_terminator || pc >= stop_pc || Self::writes_p_memory(&inst) {
ended_with_terminator = true;
break;
}
}
}
if ended_with_terminator {
let cur_il = self.builder.use_var(self.inst_len);
let fallthrough = self.builder.ins().iconst(types::I32, pc as i64);
let zero = self.builder.ins().iconst(types::I32, 0);
let branch_taken = self.builder.ins().icmp(IntCC::Equal, cur_il, zero);
let already_set_pc = self.load_u32(OFF_PC);
let final_pc = self
.builder
.ins()
.select(branch_taken, already_set_pc, fallthrough);
self.store_pc(final_pc);
} else {
let fallthrough = self.builder.ins().iconst(types::I32, pc as i64);
self.store_pc(fallthrough);
}
pc
}
fn flags() -> MemFlags {
MemFlags::trusted()
}
fn reg_offset(idx: usize) -> i32 {
OFF_REGS + (idx as i32) * 4
}
fn load_u32(&mut self, off: i32) -> Value {
self.builder
.ins()
.load(types::I32, Self::flags(), self.state_ptr, off)
}
fn store_u32(&mut self, off: i32, v: Value) {
self.builder
.ins()
.store(Self::flags(), v, self.state_ptr, off);
}
fn store_bool(&mut self, off: i32, v: Value) {
self.builder
.ins()
.store(Self::flags(), v, self.state_ptr, off);
}
fn set_inst_len(&mut self, len: u32) {
let v = self.builder.ins().iconst(types::I32, len as i64);
self.builder.def_var(self.inst_len, v);
}
fn set_cycles(&mut self, n: i64) {
self.pending_cycles += n;
}
fn flush_pending_cycles(&mut self) {
if self.pending_cycles > 0 {
let prev = self.builder.use_var(self.total_cycles);
let add = self.builder.ins().iconst(types::I32, self.pending_cycles);
let new = self.builder.ins().iadd(prev, add);
self.builder.def_var(self.total_cycles, new);
self.pending_cycles = 0;
}
}
fn shift_to_bit(&mut self, val: Value, flag: u32) -> Value {
let shift = self.builder.ins().iconst(types::I32, flag as i64);
self.builder.ins().ishl(val, shift)
}
fn extract_bit(&mut self, sr_val: Value, bit: u32) -> Value {
let c = self.builder.ins().iconst(types::I32, bit as i64);
let shifted = self.builder.ins().ushr(sr_val, c);
let one = self.builder.ins().iconst(types::I32, 1);
self.builder.ins().band(shifted, one)
}
fn extract_bit_i64(&mut self, val: Value, bit: u32) -> Value {
let shift = self.builder.ins().iconst(types::I32, bit as i64);
let shifted = self.builder.ins().ushr(val, shift);
let reduced = self.builder.ins().ireduce(types::I32, shifted);
let one = self.builder.ins().iconst(types::I32, 1);
self.builder.ins().band(reduced, one)
}
fn mask24(&mut self, val: Value) -> Value {
let mask = self.builder.ins().iconst(types::I32, 0x00FFFFFF);
self.builder.ins().band(val, mask)
}
fn sp_counter(&mut self, sp: Value) -> Value {
self.builder.ins().band_imm(sp, 0xF)
}
fn stack_slot_addr(&mut self, idx: Value, is_ssl: bool) -> Value {
let base_off = if is_ssl { OFF_STACK + 64 } else { OFF_STACK } as i64;
let masked = self.builder.ins().band_imm(idx, 0xF);
let four = self.builder.ins().iconst(self.ptr_ty, 4);
let idx_ext = self.builder.ins().uextend(self.ptr_ty, masked);
let byte_off = self.builder.ins().imul(idx_ext, four);
let slot_base = self.builder.ins().iconst(self.ptr_ty, base_off);
let slot_off = self.builder.ins().iadd(slot_base, byte_off);
self.builder.ins().iadd(self.state_ptr, slot_off)
}
fn stack_push(&mut self, ssh_val: Value, ssl_val: Value) {
let sp = self.load_reg(reg::SP);
let c0x10 = self.builder.ins().iconst(types::I32, 0x10); let c0x20 = self.builder.ins().iconst(types::I32, 0x20); let one = self.builder.ins().iconst(types::I32, 1);
let c0x3f = self.builder.ins().iconst(types::I32, 0x3F);
let stack_error = self.builder.ins().band(sp, c0x10);
let underflow = self.builder.ins().band(sp, c0x20);
let counter = self.sp_counter(sp);
let new_counter = self.builder.ins().iadd(counter, one);
let overflow_bit = self.builder.ins().band(new_counter, c0x10);
let zero = self.builder.ins().iconst(types::I32, 0);
let se_is_zero = self.builder.ins().icmp(IntCC::Equal, stack_error, zero);
let of_is_set = self.builder.ins().icmp(IntCC::NotEqual, overflow_bit, zero);
let do_error = self.builder.ins().band(se_is_zero, of_is_set);
let error_blk = self.builder.create_block();
let cont_blk = self.builder.create_block();
self.builder
.ins()
.brif(do_error, error_blk, &[], cont_blk, &[]);
self.builder.switch_to_block(error_blk);
self.builder.seal_block(error_blk);
self.emit_add_interrupt(interrupt::STACK_ERROR);
self.builder.ins().jump(cont_blk, &[]);
self.builder.switch_to_block(cont_blk);
self.builder.seal_block(cont_blk);
let new_sp = self.builder.ins().bor(underflow, stack_error);
let new_sp = self.builder.ins().bor(new_sp, new_counter);
let new_sp = self.builder.ins().band(new_sp, c0x3f);
self.store_reg(reg::SP, new_sp);
let mask24 = self.builder.ins().iconst(types::I32, 0x00FF_FFFFu32 as i64);
let ssh_masked = self.builder.ins().band(ssh_val, mask24);
let ssl_masked = self.builder.ins().band(ssl_val, mask24);
let ssh_addr = self.stack_slot_addr(new_counter, false);
self.builder
.ins()
.store(Self::flags(), ssh_masked, ssh_addr, 0);
let ssl_addr = self.stack_slot_addr(new_counter, true);
self.builder
.ins()
.store(Self::flags(), ssl_masked, ssl_addr, 0);
self.store_reg(reg::SSH, ssh_val);
self.store_reg(reg::SSL, ssl_val);
}
fn stack_pop(&mut self) -> (Value, Value) {
let sp = self.load_reg(reg::SP);
let c0x10 = self.builder.ins().iconst(types::I32, 0x10);
let c0x20 = self.builder.ins().iconst(types::I32, 0x20);
let one = self.builder.ins().iconst(types::I32, 1);
let c0x3f = self.builder.ins().iconst(types::I32, 0x3F);
let counter = self.sp_counter(sp);
let ssh_addr = self.stack_slot_addr(counter, false);
let ssh = self
.builder
.ins()
.load(types::I32, Self::flags(), ssh_addr, 0);
let ssl_addr = self.stack_slot_addr(counter, true);
let ssl = self
.builder
.ins()
.load(types::I32, Self::flags(), ssl_addr, 0);
let stack_error = self.builder.ins().band(sp, c0x10);
let underflow = self.builder.ins().band(sp, c0x20);
let new_counter = self.builder.ins().isub(counter, one);
let uf_bit = self.builder.ins().band(new_counter, c0x10);
let zero = self.builder.ins().iconst(types::I32, 0);
let se_is_zero = self.builder.ins().icmp(IntCC::Equal, stack_error, zero);
let uf_is_set = self.builder.ins().icmp(IntCC::NotEqual, uf_bit, zero);
let do_error = self.builder.ins().band(se_is_zero, uf_is_set);
let error_blk = self.builder.create_block();
let cont_blk = self.builder.create_block();
self.builder
.ins()
.brif(do_error, error_blk, &[], cont_blk, &[]);
self.builder.switch_to_block(error_blk);
self.builder.seal_block(error_blk);
self.emit_add_interrupt(interrupt::STACK_ERROR);
self.builder.ins().jump(cont_blk, &[]);
self.builder.switch_to_block(cont_blk);
self.builder.seal_block(cont_blk);
let new_sp = self.builder.ins().bor(underflow, stack_error);
let new_sp = self.builder.ins().bor(new_sp, new_counter);
let new_sp = self.builder.ins().band(new_sp, c0x3f);
self.store_reg(reg::SP, new_sp);
let new_ssh_addr = self.stack_slot_addr(new_counter, false);
let new_ssh = self
.builder
.ins()
.load(types::I32, Self::flags(), new_ssh_addr, 0);
self.store_reg(reg::SSH, new_ssh);
let new_ssl_addr = self.stack_slot_addr(new_counter, true);
let new_ssl = self
.builder
.ins()
.load(types::I32, Self::flags(), new_ssl_addr, 0);
self.store_reg(reg::SSL, new_ssl);
(ssh, ssl)
}
fn eval_cc(&mut self, cc: CondCode) -> Value {
let sr_val = self.load_reg(reg::SR);
let one = self.builder.ins().iconst(types::I32, 1);
match cc {
CondCode::CC | CondCode::CS => {
let c = self.extract_bit(sr_val, sr::C);
if cc == CondCode::CC {
self.builder.ins().bxor(c, one)
} else {
c
}
}
CondCode::EQ | CondCode::NE => {
let z = self.extract_bit(sr_val, sr::Z);
if cc == CondCode::EQ {
z
} else {
self.builder.ins().bxor(z, one)
}
}
CondCode::PL | CondCode::MI => {
let n = self.extract_bit(sr_val, sr::N);
if cc == CondCode::PL {
self.builder.ins().bxor(n, one)
} else {
n
}
}
CondCode::GE | CondCode::LT => {
let n = self.extract_bit(sr_val, sr::N);
let v = self.extract_bit(sr_val, sr::V);
let nxv = self.builder.ins().bxor(n, v);
if cc == CondCode::GE {
self.builder.ins().bxor(nxv, one)
} else {
nxv
}
}
CondCode::GT | CondCode::LE => {
let n = self.extract_bit(sr_val, sr::N);
let v = self.extract_bit(sr_val, sr::V);
let z = self.extract_bit(sr_val, sr::Z);
let nxv = self.builder.ins().bxor(n, v);
let expr = self.builder.ins().bor(z, nxv);
if cc == CondCode::GT {
self.builder.ins().bxor(expr, one)
} else {
expr
}
}
CondCode::NN | CondCode::NR => {
let z = self.extract_bit(sr_val, sr::Z);
let u = self.extract_bit(sr_val, sr::U);
let e = self.extract_bit(sr_val, sr::E);
let not_u = self.builder.ins().bxor(u, one);
let not_u_and_e = self.builder.ins().band(not_u, e);
let expr = self.builder.ins().bor(z, not_u_and_e);
if cc == CondCode::NN {
self.builder.ins().bxor(expr, one)
} else {
expr
}
}
CondCode::EC | CondCode::ES => {
let e = self.extract_bit(sr_val, sr::E);
if cc == CondCode::EC {
self.builder.ins().bxor(e, one)
} else {
e
}
}
CondCode::LC | CondCode::LS => {
let l = self.extract_bit(sr_val, sr::L);
if cc == CondCode::LC {
self.builder.ins().bxor(l, one)
} else {
l
}
}
}
}
fn eval_cc_bool(&mut self, cc: CondCode) -> Value {
let cond = self.eval_cc(cc);
let zero = self.builder.ins().iconst(types::I32, 0);
self.builder.ins().icmp(IntCC::NotEqual, cond, zero)
}
fn emit_call_read_accu24(&mut self, acc_idx: u32) -> Value {
self.flush_pending_flags();
let regs = if acc_idx == 0 {
[reg::A2, reg::A1, reg::A0]
} else {
[reg::B2, reg::B1, reg::B0]
};
for &r in ®s {
self.flush_reg(r);
self.promoted.dirty[r] = false;
}
self.flush_reg(reg::SR);
self.promoted.dirty[reg::SR] = false;
self.flush_acc_to_memory(if acc_idx == 0 {
Accumulator::A
} else {
Accumulator::B
});
let fn_addr = jit_read_accu24 as *const () as usize;
let fn_ptr = self.builder.ins().iconst(self.ptr_ty, fn_addr as i64);
let acc_val = self.builder.ins().iconst(types::I32, acc_idx as i64);
let mut sig = Signature::new(HOST_CALL_CONV);
sig.params.push(AbiParam::new(self.ptr_ty)); sig.params.push(AbiParam::new(types::I32)); sig.returns.push(AbiParam::new(types::I32)); let sig_ref = self.builder.import_signature(sig);
let call = self
.builder
.ins()
.call_indirect(sig_ref, fn_ptr, &[self.state_ptr, acc_val]);
self.promoted.valid[reg::SR] = false;
if let Some(scope) = self.scope_stack.last_mut() {
scope.entry_valid[reg::SR] = true;
}
self.builder.inst_results(call)[0]
}
fn emit_call_extern_val(&mut self, fn_addr: usize, val: Value) {
self.flush_promoted();
let fn_ptr = self.builder.ins().iconst(self.ptr_ty, fn_addr as i64);
let mut sig = Signature::new(HOST_CALL_CONV);
sig.params.push(AbiParam::new(self.ptr_ty)); sig.params.push(AbiParam::new(types::I32)); let sig_ref = self.builder.import_signature(sig);
self.builder
.ins()
.call_indirect(sig_ref, fn_ptr, &[self.state_ptr, val]);
self.invalidate_promoted();
}
fn emit_call_sr_helper_i64(&mut self, fn_addr: usize, val: Value) {
self.flush_reg(reg::SR);
self.promoted.dirty[reg::SR] = false;
let fn_ptr = self.builder.ins().iconst(self.ptr_ty, fn_addr as i64);
let mut sig = Signature::new(HOST_CALL_CONV);
sig.params.push(AbiParam::new(self.ptr_ty)); sig.params.push(AbiParam::new(types::I64)); let sig_ref = self.builder.import_signature(sig);
self.builder
.ins()
.call_indirect(sig_ref, fn_ptr, &[self.state_ptr, val]);
self.promoted.valid[reg::SR] = false;
if let Some(scope) = self.scope_stack.last_mut() {
scope.entry_valid[reg::SR] = true;
}
}
fn emit_call_extern_ret(&mut self, fn_addr: usize) -> Value {
self.flush_promoted();
let fn_ptr = self.builder.ins().iconst(self.ptr_ty, fn_addr as i64);
let mut sig = Signature::new(HOST_CALL_CONV);
sig.params.push(AbiParam::new(self.ptr_ty)); sig.returns.push(AbiParam::new(types::I32)); let sig_ref = self.builder.import_signature(sig);
let call = self
.builder
.ins()
.call_indirect(sig_ref, fn_ptr, &[self.state_ptr]);
self.invalidate_promoted();
self.builder.inst_results(call)[0]
}
fn emit_interrupt_aware_stack_push(&mut self, ret_addr: Value) {
let int_state = self.builder.ins().load(
types::I8,
Self::flags(),
self.state_ptr,
OFF_INTERRUPT_STATE,
);
let long_val = self
.builder
.ins()
.iconst(types::I8, InterruptState::Long as i64);
let is_long = self.builder.ins().icmp(IntCC::Equal, int_state, long_val);
let normal_blk = self.builder.create_block();
let long_blk = self.builder.create_block();
let merge_blk = self.builder.create_block();
let mut cond_state = self.begin_conditional();
self.builder
.ins()
.brif(is_long, long_blk, &[], normal_blk, &[]);
self.builder.switch_to_block(normal_blk);
self.builder.seal_block(normal_blk);
let sr_val = self.load_reg(reg::SR);
self.stack_push(ret_addr, sr_val);
self.end_conditional_arm(&mut cond_state);
self.builder.ins().jump(merge_blk, &[]);
self.builder.switch_to_block(long_blk);
self.builder.seal_block(long_blk);
let disabled = self
.builder
.ins()
.iconst(types::I8, InterruptState::Fast as i64);
self.builder
.ins()
.store(Self::flags(), disabled, self.state_ptr, OFF_INTERRUPT_STATE);
self.end_conditional_arm(&mut cond_state);
self.builder.ins().jump(merge_blk, &[]);
self.builder.switch_to_block(merge_blk);
self.builder.seal_block(merge_blk);
self.merge_conditional(&cond_state);
}
}