Skip to content

Commit 3755a2f

Browse files
committed
Migrate arm emissions to vixl
1 parent 7d7a486 commit 3755a2f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+5495
-12844
lines changed

build.rs

+17-7
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use regex::Regex;
33
use std::collections::{HashMap, HashSet};
44
use std::fs::File;
55
use std::io::Write;
6-
use std::path::PathBuf;
6+
use std::path::{Path, PathBuf};
77
use std::process::Command;
88
use std::{env, fs};
99

@@ -62,9 +62,12 @@ fn main() {
6262
vixl_build.include(vixl_path.join("src")).compiler("clang").cpp(true);
6363

6464
if let Ok(vitasdk_path) = &vitasdk_path {
65-
vixl_build
66-
.include(vitasdk_path.join("arm-vita-eabi").join("include/c++/13.3.0").to_str().unwrap())
67-
.include(vitasdk_path.join("arm-vita-eabi").join("include/c++/13.3.0/arm-vita-eabi").to_str().unwrap());
65+
let cpp_include_path = vitasdk_path.join("arm-vita-eabi").join("include/c++");
66+
let dir = fs::read_dir(cpp_include_path).unwrap();
67+
let version = dir.into_iter().next().unwrap().unwrap();
68+
let cpp_include_path = version.path();
69+
70+
vixl_build.include(cpp_include_path.to_str().unwrap()).include(cpp_include_path.join("arm-vita-eabi").to_str().unwrap());
6871
}
6972

7073
for flag in vixl_flags {
@@ -89,7 +92,10 @@ fn main() {
8992
let vixl_masm_file = out_path.join("vixl_masm.cpp");
9093
File::create(&vixl_masm_file).unwrap().write_all(&out).unwrap();
9194

92-
let clang_format_output = Command::new("clang-format").arg("-style").arg("{ColumnLimit: 99999}").arg(vixl_masm_file).output().unwrap();
95+
let clang_format_output = match Command::new("clang-format").arg("-style").arg("{ColumnLimit: 99999}").arg(vixl_masm_file).output() {
96+
Ok(output) => output,
97+
Err(err) => panic!("Failed to run clang-format: {err}"),
98+
};
9399
assert!(clang_format_output.status.success(), "{clang_format_output:?}");
94100

95101
let output = String::from_utf8(clang_format_output.stdout).unwrap();
@@ -239,6 +245,8 @@ fn main() {
239245
rust_type = "Cond".to_string();
240246
} else if t == "Register" {
241247
rust_type = "Reg".to_string();
248+
} else if t == "RegisterList" {
249+
rust_type = "RegReserve".to_string();
242250
}
243251

244252
let has_ptr_inner = t == "DOperand" || t == "QOperand" || t == "SOperand" || t == "RawLiteral" || t == "Label";
@@ -256,6 +264,8 @@ fn main() {
256264
delegate_params += &format!("Condition::from({name}), ");
257265
} else if t == "Register" {
258266
delegate_params += &format!("Register::from({name}), ");
267+
} else if t == "RegisterList" {
268+
delegate_params += &format!("RegisterList::from({name}), ");
259269
} else {
260270
delegate_params += &format!("{name}, ");
261271
}
@@ -304,7 +314,7 @@ fn main() {
304314
if !generic_types.is_empty() {
305315
writeln!(
306316
vixl_inst_wrapper_file,
307-
r"impl Masm{fun_name}{}<{generic_types}> for MarcoAssembler {{
317+
r"impl Masm{fun_name}{}<{generic_types}> for MacroAssembler {{
308318
fn {}{}(&mut self, {fun_params}) {{
309319
unsafe {{ masm_{}{}(self.inner, {delegate_params}) }}
310320
}}
@@ -320,7 +330,7 @@ fn main() {
320330
} else {
321331
writeln!(
322332
vixl_inst_wrapper_file,
323-
r"impl Masm{fun_name} for MarcoAssembler {{
333+
r"impl Masm{fun_name} for MacroAssembler {{
324334
fn {}0(&mut self) {{
325335
unsafe {{ masm_{}{}(self.inner) }}
326336
}}

src/core/exception_handler.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ mod handler {
2020
use crate::logging::debug_println;
2121
use bilge::prelude::u5;
2222

23-
pub fn handle<const CPU: CpuType, const THUMB: bool>(emu: &mut Emu, opcode: u32, vector: ExceptionVector) {
23+
pub fn handle<const CPU: CpuType, const THUMB: bool>(emu: &mut Emu, comment: u8, vector: ExceptionVector) {
2424
if CPU == CpuType::ARM7 || emu.cp15.exception_addr != 0 {
2525
match vector {
26-
ExceptionVector::SoftwareInterrupt => bios::swi::<CPU>(((opcode >> if THUMB { 0 } else { 16 }) & 0xFF) as u8, emu),
26+
ExceptionVector::SoftwareInterrupt => bios::swi::<CPU>(comment, emu),
2727
ExceptionVector::NormalInterrupt => bios::interrupt::<CPU>(emu),
2828
_ => todo!(),
2929
}

src/jit/analyzer/asm_analyzer.rs

+159
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
use crate::jit::analyzer::basic_block::BasicBlock;
2+
use crate::jit::inst_info::InstInfo;
3+
use crate::jit::op::Op;
4+
use crate::jit::reg::{reg_reserve, Reg, RegReserve};
5+
use crate::jit::Cond;
6+
use crate::logging::block_asm_println;
7+
use bilge::prelude::*;
8+
9+
pub enum JitBranchInfo {
10+
Idle(usize),
11+
Local(usize),
12+
None,
13+
}
14+
15+
// Taken from https://github.com/melonDS-emu/melonDS/blob/24c402af51fe9c0537582173fc48d1ad3daff459/src/ARMJIT.cpp#L352
16+
fn is_idle_loop(insts: &[InstInfo]) -> bool {
17+
let mut regs_written_to = RegReserve::new();
18+
let mut regs_disallowed_to_write = RegReserve::new();
19+
for (i, inst) in insts.iter().enumerate() {
20+
if (inst.is_branch() && i < insts.len() - 1)
21+
|| matches!(inst.op, Op::Swi | Op::SwiT | Op::Mcr | Op::Mrc | Op::MrsRc | Op::MrsRs | Op::MsrIc | Op::MsrIs | Op::MsrRc | Op::MsrRs)
22+
|| inst.op.is_write_mem_transfer()
23+
{
24+
return false;
25+
}
26+
27+
let src_regs = inst.src_regs & !reg_reserve!(Reg::PC);
28+
let out_regs = inst.out_regs & !reg_reserve!(Reg::PC);
29+
regs_disallowed_to_write |= src_regs & !regs_written_to;
30+
31+
if !(out_regs & regs_disallowed_to_write).is_empty() {
32+
return false;
33+
}
34+
regs_written_to |= out_regs;
35+
}
36+
true
37+
}
38+
39+
fn analyze_branch_label(insts: &[InstInfo], thumb: bool, branch_index: usize, cond: Cond, pc: u32, target_pc: u32) -> JitBranchInfo {
40+
if (cond as u8) < (Cond::AL as u8) && target_pc < pc {
41+
let diff = (pc - target_pc) >> if thumb { 1 } else { 2 };
42+
if diff as usize <= branch_index {
43+
let jump_to_index = branch_index - diff as usize;
44+
if is_idle_loop(&insts[jump_to_index..branch_index + 1]) {
45+
return JitBranchInfo::Idle(jump_to_index);
46+
}
47+
}
48+
}
49+
50+
let relative_index = (target_pc as i32 - pc as i32) >> if thumb { 1 } else { 2 };
51+
let target_index = branch_index as i32 + relative_index;
52+
if target_index >= 0 && (target_index as usize) < insts.len() {
53+
JitBranchInfo::Local(target_index as usize)
54+
} else {
55+
JitBranchInfo::None
56+
}
57+
}
58+
59+
#[bitsize(8)]
60+
#[derive(Copy, Clone, FromBits)]
61+
pub struct InstMetadata {
62+
pub idle_loop: bool,
63+
pub external_branch: bool,
64+
pub local_branch_entry: bool,
65+
not_used: u5,
66+
}
67+
68+
impl Default for InstMetadata {
69+
fn default() -> Self {
70+
InstMetadata::from(0)
71+
}
72+
}
73+
74+
#[derive(Default)]
75+
pub struct AsmAnalyzer {
76+
pub basic_blocks: Vec<BasicBlock>,
77+
pub insts_metadata: Vec<InstMetadata>,
78+
}
79+
80+
impl AsmAnalyzer {
81+
fn create_basic_blocks(&mut self, start_pc: u32, insts: &[InstInfo], thumb: bool) {
82+
self.basic_blocks.clear();
83+
self.insts_metadata.clear();
84+
self.insts_metadata.resize(insts.len(), InstMetadata::default());
85+
86+
let pc_shift = if thumb { 1 } else { 2 };
87+
for i in 0..insts.len() {
88+
if insts[i].op.is_labelled_branch() && !insts[i].out_regs.is_reserved(Reg::LR) {
89+
let cond = insts[i].get_branch_cond();
90+
let pc = start_pc + ((i as u32) << pc_shift);
91+
let relative_pc = insts[i].operands()[0].as_imm().unwrap() as i32 + (2 << pc_shift);
92+
let target_pc = (pc as i32 + relative_pc) as u32;
93+
94+
match analyze_branch_label(insts, thumb, i, cond, pc, target_pc) {
95+
JitBranchInfo::Idle(target_index) => {
96+
self.insts_metadata[i].set_idle_loop(true);
97+
self.insts_metadata[target_index].set_local_branch_entry(true);
98+
}
99+
JitBranchInfo::Local(target_index) => {
100+
self.insts_metadata[target_index].set_local_branch_entry(true);
101+
}
102+
JitBranchInfo::None => {
103+
self.insts_metadata[i].set_external_branch(true);
104+
}
105+
}
106+
}
107+
}
108+
109+
let mut block_start = 0;
110+
for i in 0..insts.len() {
111+
if self.insts_metadata[i].local_branch_entry() {
112+
if i > block_start {
113+
self.basic_blocks.push(BasicBlock::new(start_pc + ((block_start as u32) << pc_shift), block_start, i - 1));
114+
}
115+
block_start = i;
116+
}
117+
118+
if insts[i].op.is_labelled_branch() && !insts[i].out_regs.is_reserved(Reg::LR) {
119+
self.basic_blocks.push(BasicBlock::new(start_pc + ((block_start as u32) << pc_shift), block_start, i));
120+
block_start = i + 1;
121+
}
122+
}
123+
if block_start < insts.len() {
124+
self.basic_blocks.push(BasicBlock::new(start_pc + ((block_start as u32) << pc_shift), block_start, insts.len() - 1));
125+
}
126+
127+
for basic_block in &mut self.basic_blocks {
128+
basic_block.resolve_live_regs(insts);
129+
}
130+
}
131+
132+
pub fn get_basic_block_metadata(&self, basic_block_index: usize) -> InstMetadata {
133+
self.insts_metadata[self.basic_blocks[basic_block_index].start_index]
134+
}
135+
136+
pub fn get_next_live_regs(&self, basic_block_index: usize, inst_index: usize) -> RegReserve {
137+
let basic_block = &self.basic_blocks[basic_block_index];
138+
basic_block.live_regs[inst_index - basic_block.start_index + 1]
139+
}
140+
141+
pub fn get_basic_block_from_inst(&self, inst_index: usize) -> usize {
142+
for (i, basic_block) in self.basic_blocks.iter().enumerate() {
143+
if inst_index >= basic_block.start_index && inst_index <= basic_block.end_index {
144+
return i;
145+
}
146+
}
147+
unreachable!()
148+
}
149+
150+
pub fn analyze(&mut self, start_pc: u32, insts: &[InstInfo], thumb: bool) {
151+
self.create_basic_blocks(start_pc, insts, thumb);
152+
153+
for (i, basic_block) in self.basic_blocks.iter().enumerate() {
154+
block_asm_println!("basic block {i} start inst {} - {}", basic_block.start_index, basic_block.end_index);
155+
block_asm_println!("{:?}", basic_block.debug(insts, thumb));
156+
block_asm_println!("basic block {i} end");
157+
}
158+
}
159+
}

src/jit/analyzer/basic_block.rs

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
use crate::jit::inst_info::InstInfo;
2+
use crate::jit::reg::RegReserve;
3+
use crate::jit::Cond;
4+
use std::fmt::{Debug, Formatter};
5+
6+
pub struct BasicBlock {
7+
pub start_pc: u32,
8+
pub start_index: usize,
9+
pub end_index: usize,
10+
pub live_regs: Vec<RegReserve>,
11+
}
12+
13+
impl BasicBlock {
14+
pub fn new(start_pc: u32, start_index: usize, end_index: usize) -> Self {
15+
BasicBlock {
16+
start_pc,
17+
start_index,
18+
end_index,
19+
live_regs: Vec::new(),
20+
}
21+
}
22+
23+
pub fn debug<'a>(&'a self, insts: &'a [InstInfo], thumb: bool) -> BasicBlockDebug<'a> {
24+
BasicBlockDebug { basic_block: self, insts, thumb }
25+
}
26+
27+
pub fn get_inputs(&self) -> RegReserve {
28+
self.live_regs[0]
29+
}
30+
31+
pub fn resolve_live_regs(&mut self, insts: &[InstInfo]) {
32+
let inst_length = self.end_index - self.start_index + 1;
33+
self.live_regs.clear();
34+
self.live_regs.resize(inst_length + 1, RegReserve::new());
35+
36+
for i in (0..inst_length).rev() {
37+
let inst = &insts[i + self.start_index];
38+
let mut previous_ranges = self.live_regs[i + 1];
39+
previous_ranges -= inst.out_regs;
40+
self.live_regs[i] = previous_ranges + inst.src_regs;
41+
if inst.cond != Cond::AL {
42+
self.live_regs[i] += inst.out_regs;
43+
}
44+
}
45+
}
46+
}
47+
48+
pub struct BasicBlockDebug<'a> {
49+
basic_block: &'a BasicBlock,
50+
insts: &'a [InstInfo],
51+
thumb: bool,
52+
}
53+
54+
impl Debug for BasicBlockDebug<'_> {
55+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
56+
let pc_shift = if self.thumb { 1 } else { 2 };
57+
for i in self.basic_block.start_index..self.basic_block.end_index + 1 {
58+
let inst = &self.insts[i];
59+
let i = i - self.basic_block.start_index;
60+
let pc = self.basic_block.start_pc + ((i as u32) << pc_shift);
61+
writeln!(f, "{pc:x}: live regs: {:?}", self.basic_block.live_regs[i])?;
62+
writeln!(f, "{inst:?}")?;
63+
}
64+
write!(f, "outputs: {:?}", self.basic_block.live_regs.last().unwrap())
65+
}
66+
}

src/jit/analyzer/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pub mod asm_analyzer;
2+
mod basic_block;

0 commit comments

Comments
 (0)