WORKING ASM FUNCTION CALLS
This commit is contained in:
@@ -48,6 +48,7 @@ pub struct SectionHeader {
|
||||
entry_size: u64,
|
||||
}
|
||||
|
||||
// this is currently specialized for riscv64; obviously add params later
|
||||
pub fn create(program: Vec<u8>, start_offset: u64) -> Vec<u8> {
|
||||
let addr_start = 0x1000;
|
||||
let page_size = 0x1000;
|
||||
@@ -5,13 +5,14 @@ use std::{
|
||||
process::Command,
|
||||
};
|
||||
|
||||
mod riscv64;
|
||||
mod elf;
|
||||
mod program;
|
||||
mod riscv64;
|
||||
mod target;
|
||||
|
||||
pub fn main() {
|
||||
use std::io::prelude::*;
|
||||
let dir = Path::new("build");
|
||||
let dir = Path::new("./build");
|
||||
create_dir_all(dir).expect("Failed to create or confirm build directory");
|
||||
let name = Path::new("test");
|
||||
let path = dir.join(name);
|
||||
@@ -26,9 +27,35 @@ pub fn main() {
|
||||
file.write_all(&riscv64::gen())
|
||||
.expect("Failed to write to file");
|
||||
file.sync_all().expect("Failed to sync file");
|
||||
if let Ok(mut process) = Command::new("qemu-riscv64").arg(path).spawn() {
|
||||
let mut p = Command::new("qemu-riscv64");
|
||||
let run_gdb = std::env::args().nth(1).is_some_and(|a| a == "d");
|
||||
let proc = if run_gdb {
|
||||
p.arg("-g").arg("1234").arg(path).spawn()
|
||||
} else {
|
||||
p.arg(path).spawn()
|
||||
};
|
||||
if let Ok(mut process) = proc {
|
||||
let mut print_exit = true;
|
||||
if run_gdb {
|
||||
match Command::new("gdb")
|
||||
.arg("-q")
|
||||
.arg("-ex")
|
||||
.arg("target remote :1234")
|
||||
.arg(path)
|
||||
.spawn()
|
||||
{
|
||||
Ok(mut gdb) => {
|
||||
gdb.wait().expect("xd");
|
||||
}
|
||||
Err(e) => {
|
||||
print_exit = false;
|
||||
println!("gdb error: {e:?}");
|
||||
process.kill().expect("uh oh");
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Ok(status) = process.wait() {
|
||||
if status.code().is_none_or(|c| c != 0) {
|
||||
if print_exit && status.code().is_none_or(|c| c != 0) {
|
||||
println!("{}", status);
|
||||
}
|
||||
}
|
||||
@@ -39,4 +66,3 @@ pub fn main() {
|
||||
// riscv64-linux-gnu-gdb -q \
|
||||
// -ex "target remote :1234" \
|
||||
// test
|
||||
|
||||
|
||||
@@ -1,53 +1,35 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub struct Program {
|
||||
data: Vec<u8>,
|
||||
ro_map: HashMap<String, usize>,
|
||||
}
|
||||
|
||||
impl Program {
|
||||
pub fn new(data: HashMap<String, Vec<u8>>) -> Self {
|
||||
let mut ro_data = Vec::new();
|
||||
let mut ro_map = HashMap::new();
|
||||
for (key, val) in data {
|
||||
ro_map.insert(key, ro_data.len());
|
||||
ro_data.extend(val);
|
||||
}
|
||||
Self {
|
||||
data: ro_data,
|
||||
ro_map,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_program<I: Instr>(
|
||||
ro_data: HashMap<String, Vec<u8>>,
|
||||
functions: Vec<Function<I>>,
|
||||
) -> Vec<u8> {
|
||||
) -> (Vec<u8>, Option<u64>) {
|
||||
let mut data = Vec::new();
|
||||
let mut ro_map = HashMap::new();
|
||||
let mut sym_map = HashMap::new();
|
||||
for (key, val) in ro_data {
|
||||
ro_map.insert(key, data.len());
|
||||
sym_map.insert(key, data.len() as u64);
|
||||
data.extend(val);
|
||||
}
|
||||
// let mut fn_map = HashMap::new();
|
||||
let mut start = None;
|
||||
for fun in functions {
|
||||
if fun.label == "_start" {
|
||||
start = Some(data.len() as u64);
|
||||
}
|
||||
sym_map.insert(fun.label, data.len() as u64);
|
||||
for i in fun.instructions {
|
||||
data.extend(i.to_le_bytes());
|
||||
let pos = data.len() as u64;
|
||||
i.push(&mut data, &sym_map, pos);
|
||||
}
|
||||
}
|
||||
data
|
||||
(data, start)
|
||||
}
|
||||
|
||||
pub struct Function<I: Instr> {
|
||||
label: String,
|
||||
instructions: Vec<I>,
|
||||
pub label: String,
|
||||
pub instructions: Vec<I>,
|
||||
}
|
||||
|
||||
pub trait Instr {
|
||||
fn to_le_bytes(&self) -> impl IntoIterator<Item = u8>;
|
||||
fn push(&self, data: &mut Vec<u8>, ro_map: &HashMap<String, u64>, pos: u64) -> Option<String>;
|
||||
}
|
||||
|
||||
struct SymbolInstr {
|
||||
i: usize
|
||||
}
|
||||
|
||||
@@ -1,2 +1,57 @@
|
||||
pub enum Instruction {
|
||||
use crate::compiler::program::Instr;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub enum AsmInstruction {
|
||||
Addi(Reg, Reg, i32),
|
||||
La(Reg, String),
|
||||
Jal(Reg, i32),
|
||||
Jala(String),
|
||||
Ja(String),
|
||||
Ret,
|
||||
Ecall,
|
||||
}
|
||||
|
||||
impl Instr for AsmInstruction {
|
||||
fn push(
|
||||
&self,
|
||||
data: &mut Vec<u8>,
|
||||
sym_map: &std::collections::HashMap<String, u64>,
|
||||
pos: u64,
|
||||
) -> Option<String> {
|
||||
match self {
|
||||
Self::Addi(dest, src, imm) => {
|
||||
data.extend(addi(*dest, *src, BitsI32::new(*imm)).to_le_bytes());
|
||||
}
|
||||
Self::La(dest, sym) => {
|
||||
if let Some(addr) = sym_map.get(sym) {
|
||||
let offset = *addr as i32 - pos as i32;
|
||||
data.extend(auipc(*dest, BitsI32::new(0)).to_le_bytes());
|
||||
data.extend(addi(*dest, *dest, BitsI32::new(offset)).to_le_bytes());
|
||||
} else {
|
||||
return Some(sym.to_string());
|
||||
}
|
||||
}
|
||||
Self::Jal(dest, offset) => data.extend(jal(*dest, BitsI32::new(*offset)).to_le_bytes()),
|
||||
Self::Ja(sym) => {
|
||||
if let Some(addr) = sym_map.get(sym) {
|
||||
let offset = *addr as i32 - pos as i32;
|
||||
data.extend(j(BitsI32::new(offset)).to_le_bytes());
|
||||
} else {
|
||||
return Some(sym.to_string());
|
||||
}
|
||||
}
|
||||
Self::Jala(sym) => {
|
||||
if let Some(addr) = sym_map.get(sym) {
|
||||
let offset = *addr as i32 - pos as i32;
|
||||
data.extend(jal(ra, BitsI32::new(offset)).to_le_bytes());
|
||||
} else {
|
||||
return Some(sym.to_string());
|
||||
}
|
||||
}
|
||||
Self::Ret => data.extend(ret().to_le_bytes()),
|
||||
Self::Ecall => data.extend(ecall().to_le_bytes()),
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
78
src/compiler/riscv64/base.rs
Normal file
78
src/compiler/riscv64/base.rs
Normal file
@@ -0,0 +1,78 @@
|
||||
use crate::util::Bits32;
|
||||
|
||||
use super::Reg;
|
||||
|
||||
pub struct Instruction(u32);
|
||||
|
||||
use Instruction as I;
|
||||
|
||||
impl Instruction {
|
||||
pub fn to_le_bytes(&self) -> impl IntoIterator<Item = u8> {
|
||||
self.0.to_le_bytes().into_iter()
|
||||
}
|
||||
pub fn to_be_bytes(&self) -> impl IntoIterator<Item = u8> {
|
||||
self.0.to_be_bytes().into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
pub type Funct3 = Bits32<2, 0>;
|
||||
|
||||
pub const fn r_type(
|
||||
funct7: u32,
|
||||
rs2: Reg,
|
||||
rs1: Reg,
|
||||
funct3: Bits32<2, 0>,
|
||||
rd: Reg,
|
||||
opcode: u32,
|
||||
) -> I {
|
||||
I((funct7 << 25)
|
||||
+ (rs2.val() << 20)
|
||||
+ (rs1.val() << 15)
|
||||
+ (funct3.val() << 12)
|
||||
+ (rd.val() << 7)
|
||||
+ opcode)
|
||||
}
|
||||
pub const fn i_type(imm: Bits32<11, 0>, rs1: Reg, funct: Funct3, rd: Reg, opcode: u32) -> I {
|
||||
I((imm.val() << 20) + (rs1.val() << 15) + (funct.val() << 12) + (rd.val() << 7) + opcode)
|
||||
}
|
||||
pub const fn s_type(
|
||||
rs2: Reg,
|
||||
rs1: Reg,
|
||||
funct3: Funct3,
|
||||
imm: Bits32<11, 0>,
|
||||
opcode: u32,
|
||||
) -> I {
|
||||
I((imm.bits(11, 5) << 25)
|
||||
+ (rs2.val() << 20)
|
||||
+ (rs1.val() << 15)
|
||||
+ (funct3.val() << 12)
|
||||
+ (imm.bits(4, 0) << 7)
|
||||
+ opcode)
|
||||
}
|
||||
pub const fn b_type(
|
||||
rs2: Reg,
|
||||
rs1: Reg,
|
||||
funct3: Funct3,
|
||||
imm: Bits32<12, 1>,
|
||||
opcode: u32,
|
||||
) -> I {
|
||||
I((imm.bit(12) << 31)
|
||||
+ (imm.bits(10, 5) << 25)
|
||||
+ (rs2.val() << 20)
|
||||
+ (rs1.val() << 15)
|
||||
+ (funct3.val() << 8)
|
||||
+ (imm.bits(4, 1) << 8)
|
||||
+ (imm.bit(11) << 7)
|
||||
+ opcode)
|
||||
}
|
||||
pub const fn u_type(imm: Bits32<31, 12>, rd: Reg, opcode: u32) -> I {
|
||||
I((imm.bits(31, 12) << 12) + (rd.val() << 7) + opcode)
|
||||
}
|
||||
pub const fn j_type(imm: Bits32<20, 1>, rd: Reg, opcode: u32) -> I {
|
||||
I((imm.bit(20) << 31)
|
||||
+ (imm.bits(10, 1) << 21)
|
||||
+ (imm.bit(11) << 20)
|
||||
+ (imm.bits(19, 12) << 12)
|
||||
+ (rd.val() << 7)
|
||||
+ opcode)
|
||||
}
|
||||
24
src/compiler/riscv64/funct.rs
Normal file
24
src/compiler/riscv64/funct.rs
Normal file
@@ -0,0 +1,24 @@
|
||||
use super::Funct3;
|
||||
|
||||
pub mod op {
|
||||
use super::*;
|
||||
pub const ADD : Funct3 = Funct3::new(0b000);
|
||||
pub const SLL : Funct3 = Funct3::new(0b001);
|
||||
pub const SLT : Funct3 = Funct3::new(0b010);
|
||||
pub const SLTU: Funct3 = Funct3::new(0b011);
|
||||
pub const XOR : Funct3 = Funct3::new(0b100);
|
||||
pub const SR : Funct3 = Funct3::new(0b101);
|
||||
pub const OR : Funct3 = Funct3::new(0b110);
|
||||
pub const AND : Funct3 = Funct3::new(0b111);
|
||||
}
|
||||
|
||||
pub mod width {
|
||||
use super::*;
|
||||
pub const B : Funct3 = Funct3::new(0b000);
|
||||
pub const H : Funct3 = Funct3::new(0b001);
|
||||
pub const W : Funct3 = Funct3::new(0b010);
|
||||
pub const D : Funct3 = Funct3::new(0b011);
|
||||
pub const BU: Funct3 = Funct3::new(0b100);
|
||||
pub const HU: Funct3 = Funct3::new(0b101);
|
||||
pub const WU: Funct3 = Funct3::new(0b110);
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
use super::{Reg, OPCODE_MASK};
|
||||
use crate::{compiler::program::Instr, util::{bit, bits, in_bit_range}};
|
||||
|
||||
pub struct Instruction(u32);
|
||||
|
||||
use Instruction as I;
|
||||
|
||||
impl Instr for Instruction {
|
||||
fn to_le_bytes(&self) -> impl IntoIterator<Item = u8> {
|
||||
self.0.to_le_bytes().into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn r_type(funct7: u32, rs2: Reg, rs1: Reg, funct3: u32, rd: Reg, opcode: u32) -> I {
|
||||
I((funct7 << 25)
|
||||
+ (rs2.val() << 20)
|
||||
+ (rs1.val() << 15)
|
||||
+ (funct3 << 12)
|
||||
+ (rd.val() << 7)
|
||||
+ opcode)
|
||||
}
|
||||
pub const fn i_type(imm: i32, rs1: Reg, funct3: u32, rd: Reg, opcode: u32) -> I {
|
||||
debug_assert!(in_bit_range(imm, 11, 0));
|
||||
I((bits(imm, 11, 0) << 20) + (rs1.val() << 15) + (funct3 << 12) + (rd.val() << 7) + opcode)
|
||||
}
|
||||
pub const fn s_type(rs2: Reg, rs1: Reg, funct3: u32, imm: i32, opcode: u32) -> I {
|
||||
debug_assert!(in_bit_range(imm, 11, 0));
|
||||
I((bits(imm, 11, 5) << 25)
|
||||
+ (rs2.val() << 20)
|
||||
+ (rs1.val() << 15)
|
||||
+ (funct3 << 12)
|
||||
+ (bits(imm, 4, 0) << 7)
|
||||
+ opcode)
|
||||
}
|
||||
pub const fn b_type(rs2: Reg, rs1: Reg, funct3: u32, imm: i32, opcode: u32) -> I {
|
||||
debug_assert!(in_bit_range(imm, 12, 1));
|
||||
I((bit(imm, 12) << 31)
|
||||
+ (bits(imm, 10, 5) << 25)
|
||||
+ (rs2.val() << 20)
|
||||
+ (rs1.val() << 15)
|
||||
+ (funct3 << 8)
|
||||
+ (bits(imm, 4, 1) << 8)
|
||||
+ (bit(imm, 11) << 7)
|
||||
+ opcode)
|
||||
}
|
||||
pub const fn u_type(imm: i32, rd: Reg, opcode: u32) -> I {
|
||||
debug_assert!(in_bit_range(imm, 31, 12));
|
||||
I((bits(imm, 31, 12) << 12) + (rd.val() << 7) + opcode)
|
||||
}
|
||||
pub const fn j_type(imm: i32, rd: Reg, opcode: u32) -> I {
|
||||
debug_assert!(in_bit_range(imm, 20, 1));
|
||||
I((bit(imm, 20) << 31)
|
||||
+ (bits(imm, 10, 1) << 21)
|
||||
+ (bit(imm, 11) << 20)
|
||||
+ (bits(imm, 19, 12) << 12)
|
||||
+ (rd.val() << 7)
|
||||
+ opcode)
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
pub mod op {
|
||||
pub const ADD : u32 = 0b000;
|
||||
pub const SLL : u32 = 0b001;
|
||||
pub const SLT : u32 = 0b010;
|
||||
pub const SLTU: u32 = 0b011;
|
||||
pub const XOR : u32 = 0b100;
|
||||
pub const SR : u32 = 0b101;
|
||||
pub const OR : u32 = 0b110;
|
||||
pub const AND : u32 = 0b111;
|
||||
}
|
||||
|
||||
pub mod width {
|
||||
pub const B : u32 = 0b000;
|
||||
pub const H : u32 = 0b001;
|
||||
pub const W : u32 = 0b010;
|
||||
pub const D : u32 = 0b011;
|
||||
pub const BU: u32 = 0b100;
|
||||
pub const HU: u32 = 0b101;
|
||||
pub const WU: u32 = 0b110;
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
mod base;
|
||||
mod reg;
|
||||
mod opcode;
|
||||
mod func;
|
||||
|
||||
use base::*;
|
||||
pub use reg::*;
|
||||
use Instruction as I;
|
||||
use opcode::*;
|
||||
use func::{op::*, width};
|
||||
|
||||
pub const fn ecall() -> I {
|
||||
i_type(0, zero, 0, zero, SYSTEM)
|
||||
}
|
||||
pub const fn ebreak() -> I {
|
||||
i_type(1, zero, 0, zero, SYSTEM)
|
||||
}
|
||||
pub const fn auipc(dest: Reg, imm: i32) -> I {
|
||||
u_type(imm, dest, AUIPC)
|
||||
}
|
||||
pub const fn ld(dest: Reg, offset: i32, base: Reg) -> I {
|
||||
i_type(offset, base, width::D, dest, LOAD)
|
||||
}
|
||||
pub const fn lw(dest: Reg, offset: i32, base: Reg) -> I {
|
||||
i_type(offset, base, width::W, dest, LOAD)
|
||||
}
|
||||
pub const fn lb(dest: Reg, offset: i32, base: Reg) -> I {
|
||||
i_type(offset, base, width::B, dest, LOAD)
|
||||
}
|
||||
pub const fn sb(src: Reg, offset: i32, base: Reg) -> I {
|
||||
s_type(src, base, width::B, offset, STORE)
|
||||
}
|
||||
pub const fn sw(src: Reg, offset: i32, base: Reg) -> I {
|
||||
s_type(src, base, width::W, offset, STORE)
|
||||
}
|
||||
pub const fn sd(src: Reg, offset: i32, base: Reg) -> I {
|
||||
s_type(src, base, width::D, offset, STORE)
|
||||
}
|
||||
pub const fn addi(dest: Reg, src: Reg, imm: i32) -> I {
|
||||
i_type(imm, src, ADD, dest, IMM_OP)
|
||||
}
|
||||
pub const fn jal(offset: i32, dest: Reg) -> I {
|
||||
j_type(offset, dest, JAL)
|
||||
}
|
||||
|
||||
// pseudo instructions that map to a single instruction
|
||||
|
||||
pub const fn j(offset: i32) -> I {
|
||||
jal(offset, zero)
|
||||
}
|
||||
pub const fn mv(dest: Reg, src: Reg) -> I {
|
||||
addi(dest, src, 0)
|
||||
}
|
||||
@@ -1,33 +1,81 @@
|
||||
use crate::compiler::program::Instr;
|
||||
mod elf;
|
||||
mod instruction;
|
||||
mod asm;
|
||||
mod base;
|
||||
mod funct;
|
||||
mod opcode;
|
||||
mod reg;
|
||||
mod single;
|
||||
|
||||
use instruction::*;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::{elf, program::{create_program, Function}};
|
||||
use crate::util::BitsI32;
|
||||
use base::*;
|
||||
use funct::{op::*, width};
|
||||
use opcode::*;
|
||||
pub use reg::*;
|
||||
|
||||
use single::*;
|
||||
|
||||
pub fn gen() -> Vec<u8> {
|
||||
let mut program = Vec::new();
|
||||
use asm::AsmInstruction as I;
|
||||
// let mut program = Vec::new();
|
||||
// let msg = b"Hello world!\n";
|
||||
// program.extend(msg);
|
||||
// program.resize(((program.len() - 1) / 4 + 1) * 4, 0);
|
||||
// let start = program.len() as u64;
|
||||
// let instructions = [
|
||||
// auipc(t0, BitsI32::new(0)),
|
||||
// addi(t0, t0, BitsI32::new(-(start as i32))),
|
||||
// addi(a0, zero, BitsI32::new(1)),
|
||||
// mv(a1, t0),
|
||||
// addi(a2, zero, BitsI32::new(msg.len() as i32)),
|
||||
// addi(a7, zero, BitsI32::new(64)),
|
||||
// addi(t0, zero, const { BitsI32::new(-10) }),
|
||||
// ecall(),
|
||||
// // exit
|
||||
// addi(a0, zero, BitsI32::new(0)),
|
||||
// addi(a7, zero, BitsI32::new(93)),
|
||||
// ecall(),
|
||||
// j(BitsI32::new(0)),
|
||||
// ];
|
||||
// for i in instructions {
|
||||
// program.extend(i.to_le_bytes());
|
||||
// }
|
||||
let msg = b"Hello world!\n";
|
||||
program.extend(msg);
|
||||
program.resize(((program.len() - 1) / 4 + 1) * 4, 0);
|
||||
let start = program.len() as u64;
|
||||
let instructions = [
|
||||
auipc(t0, 0),
|
||||
addi(t0, t0, -(start as i32)),
|
||||
addi(a0, zero, 1),
|
||||
mv(a1, t0),
|
||||
addi(a2, zero, msg.len() as i32),
|
||||
addi(a7, zero, 64),
|
||||
addi(t0, zero, 200),
|
||||
ecall(),
|
||||
// exit
|
||||
addi(a0, zero, 0),
|
||||
addi(a7, zero, 93),
|
||||
ecall(),
|
||||
j(0),
|
||||
let msg2 = b"IT WORKS!!!!\n";
|
||||
let ro_data = HashMap::from([
|
||||
("msg".to_string(), msg.to_vec()),
|
||||
("msg2".to_string(), msg2.to_vec()),
|
||||
]);
|
||||
let functions = vec![
|
||||
Function {
|
||||
label: "print_stuff".to_string(),
|
||||
instructions: vec![
|
||||
I::Addi(a0, zero, 1),
|
||||
I::La(a1, "msg".to_string()),
|
||||
I::Addi(a2, zero, msg.len() as i32),
|
||||
I::Addi(a7, zero, 64),
|
||||
I::Ecall,
|
||||
I::Addi(a0, zero, 1),
|
||||
I::La(a1, "msg2".to_string()),
|
||||
I::Addi(a2, zero, msg2.len() as i32),
|
||||
I::Addi(a7, zero, 64),
|
||||
I::Ecall,
|
||||
I::Ret,
|
||||
]
|
||||
},
|
||||
Function {
|
||||
label: "_start".to_string(),
|
||||
instructions: vec![
|
||||
I::Jala("print_stuff".to_string()),
|
||||
I::Ecall,
|
||||
I::Addi(a0, zero, 0),
|
||||
I::Addi(a7, zero, 93),
|
||||
I::Ecall,
|
||||
I::Jal(zero, 0),
|
||||
]
|
||||
},
|
||||
];
|
||||
for i in instructions {
|
||||
program.extend(i.to_le_bytes());
|
||||
}
|
||||
elf::create(program, start)
|
||||
let (program, start) = create_program(ro_data, functions);
|
||||
elf::create(program, start.expect("no start!"))
|
||||
}
|
||||
|
||||
@@ -7,3 +7,4 @@ pub const AUIPC : u32 = 0b0010111;
|
||||
pub const IMM_OP: u32 = 0b0010011;
|
||||
pub const OP : u32 = 0b0110011;
|
||||
pub const JAL : u32 = 0b1101111;
|
||||
pub const JALR : u32 = 0b1100111;
|
||||
@@ -1,5 +1,6 @@
|
||||
#![allow(non_upper_case_globals)]
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Reg(u8);
|
||||
|
||||
/// hard wired 0
|
||||
51
src/compiler/riscv64/single.rs
Normal file
51
src/compiler/riscv64/single.rs
Normal file
@@ -0,0 +1,51 @@
|
||||
use crate::util::{Bits32, BitsI32};
|
||||
|
||||
use super::*;
|
||||
|
||||
use Instruction as I;
|
||||
|
||||
pub const fn ecall() -> I {
|
||||
i_type(Bits32::new(0), zero, Bits32::new(0), zero, SYSTEM)
|
||||
}
|
||||
pub const fn ebreak() -> I {
|
||||
i_type(Bits32::new(1), zero, Bits32::new(0), zero, SYSTEM)
|
||||
}
|
||||
pub const fn auipc(dest: Reg, imm: BitsI32<31, 12>) -> I {
|
||||
u_type(imm.to_u(), dest, AUIPC)
|
||||
}
|
||||
pub const fn ld(dest: Reg, offset: BitsI32<11, 0>, base: Reg) -> I {
|
||||
i_type(offset.to_u(), base, width::D, dest, LOAD)
|
||||
}
|
||||
pub const fn lw(dest: Reg, offset: BitsI32<11, 0>, base: Reg) -> I {
|
||||
i_type(offset.to_u(), base, width::W, dest, LOAD)
|
||||
}
|
||||
pub const fn lb(dest: Reg, offset: BitsI32<11, 0>, base: Reg) -> I {
|
||||
i_type(offset.to_u(), base, width::B, dest, LOAD)
|
||||
}
|
||||
pub const fn sb(src: Reg, offset: BitsI32<11, 0>, base: Reg) -> I {
|
||||
s_type(src, base, width::B, offset.to_u(), STORE)
|
||||
}
|
||||
pub const fn sw(src: Reg, offset: BitsI32<11, 0>, base: Reg) -> I {
|
||||
s_type(src, base, width::W, offset.to_u(), STORE)
|
||||
}
|
||||
pub const fn sd(src: Reg, offset: BitsI32<11, 0>, base: Reg) -> I {
|
||||
s_type(src, base, width::D, offset.to_u(), STORE)
|
||||
}
|
||||
pub const fn addi(dest: Reg, src: Reg, imm: BitsI32<11, 0>) -> I {
|
||||
i_type(imm.to_u(), src, ADD, dest, IMM_OP)
|
||||
}
|
||||
pub const fn jal(dest: Reg, offset: BitsI32<20, 1>) -> I {
|
||||
j_type(offset.to_u(), dest, JAL)
|
||||
}
|
||||
pub const fn jalr(dest: Reg, offset: BitsI32<11, 0>, base: Reg) -> I {
|
||||
i_type(offset.to_u(), base, Bits32::new(0), dest, JALR)
|
||||
}
|
||||
|
||||
// pseudoinstructions that map to a single instruction
|
||||
|
||||
pub const fn j(offset: BitsI32<20, 1>) -> I {
|
||||
jal(zero, offset)
|
||||
}
|
||||
pub const fn ret() -> I {
|
||||
jalr(zero, BitsI32::new(0), ra)
|
||||
}
|
||||
135
src/util/bits.rs
135
src/util/bits.rs
@@ -2,74 +2,59 @@ pub const fn u(x: i32) -> u32 {
|
||||
unsafe { std::mem::transmute(x) }
|
||||
}
|
||||
|
||||
pub const fn low_mask(high: u32, low: u32) -> u32 {
|
||||
(2u32.unbounded_shl(high - low)).wrapping_sub(1)
|
||||
pub const fn base_mask(len: u8) -> u32 {
|
||||
(2 << len) - 1
|
||||
}
|
||||
|
||||
pub const fn mask(high: u32, low: u32) -> u32 {
|
||||
low_mask(high, low).unbounded_shl(low)
|
||||
pub const fn mask(h: u8, l: u8) -> u32 {
|
||||
base_mask(h - l) << l
|
||||
}
|
||||
|
||||
pub const fn bits(x: i32, high: u32, low: u32) -> u32 {
|
||||
let x = u(x);
|
||||
x.unbounded_shr(low) & low_mask(high, low)
|
||||
}
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Bits32<const H: u8, const L: u8>(u32);
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct BitsI32<const H: u8, const L: u8>(u32);
|
||||
|
||||
pub const fn bit(x: i32, i: u32) -> u32 {
|
||||
let x = u(x);
|
||||
x.unbounded_shr(i) & 2u32 << i
|
||||
}
|
||||
|
||||
pub const fn in_bit_range(x: i32, high: u32, low: u32) -> bool {
|
||||
if x < 0 {
|
||||
if high == low {
|
||||
return false;
|
||||
impl<const H: u8, const L: u8> Bits32<H, L> {
|
||||
pub const fn new(val: u32) -> Self {
|
||||
let lsh = 31 - H;
|
||||
let rsh = lsh + L;
|
||||
debug_assert!(((val << lsh) >> rsh) == (val >> L));
|
||||
Self(val)
|
||||
}
|
||||
(bits(x, high - 1, low) | !mask(high - 1, low)) == u(x)
|
||||
pub const fn val(&self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
pub const fn bit(&self, i: u8) -> u32 {
|
||||
(self.0 >> i) & 1
|
||||
}
|
||||
pub const fn bits(&self, h: u8, l: u8) -> u32 {
|
||||
(self.0 >> l) & base_mask(h - l)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const H: u8, const L: u8> BitsI32<H, L> {
|
||||
pub const fn new(val: i32) -> Self {
|
||||
let lsh = 31 - H;
|
||||
let rsh = lsh + L;
|
||||
assert!(((val << lsh) >> rsh) == (val >> L));
|
||||
Self(u(val) & mask(H, L))
|
||||
}
|
||||
pub const fn tryy(val: i32) -> Option<Self> {
|
||||
let lsh = 31 - H;
|
||||
let rsh = lsh + L;
|
||||
if ((val << lsh) >> rsh) == (val >> L) {
|
||||
Some(Self(u(val) & mask(H, L)))
|
||||
} else {
|
||||
bits(x, high, low) << low == u(x)
|
||||
None
|
||||
}
|
||||
}
|
||||
pub const fn to_u(self) -> Bits32<H, L> {
|
||||
Bits32(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
// use std::ops::{Add, Shl, Shr};
|
||||
//
|
||||
// pub const fn u(x: i32) -> u32 {
|
||||
// unsafe { std::mem::transmute(x) }
|
||||
// }
|
||||
//
|
||||
// pub const fn low_mask(high: u8, low: u8) -> u32 {
|
||||
// (2u32.unbounded_shl(high - low)).wrapping_sub(1)
|
||||
// }
|
||||
//
|
||||
// pub const fn mask(high: u8, low: u8) -> u32 {
|
||||
// low_mask(high, low).unbounded_shl(low)
|
||||
// }
|
||||
//
|
||||
// pub const fn bits(x: i32, high: u8, low: u8) -> u32 {
|
||||
// let x = u(x);
|
||||
// x.unbounded_shr(low) & low_mask(high, low)
|
||||
// }
|
||||
//
|
||||
// pub const fn bit(x: i32, i: u32) -> u32 {
|
||||
// let x = u(x);
|
||||
// x.unbounded_shr(i) & 2u32 << i
|
||||
// }
|
||||
//
|
||||
// pub const fn in_bit_range<T: Shl<u8, Output = T> + Shr<u8, Output = T>>(
|
||||
// x: T,
|
||||
// high: u8,
|
||||
// low: u8,
|
||||
// ) -> bool {
|
||||
// if x < 0 {
|
||||
// if high == low {
|
||||
// return false;
|
||||
// }
|
||||
// (bits(x, high - 1, low) | !mask(high - 1, low)) == u(x)
|
||||
// } else {
|
||||
// bits(x, high, low) << low == u(x)
|
||||
// }
|
||||
// }
|
||||
|
||||
// I hate (love) rust https://github.com/rust-lang/rust-project-goals/issues/106
|
||||
// pub struct Bits<
|
||||
// T: Shl<u8, Output = T> + Shr<u8, Output = T>,
|
||||
// const S: bool,
|
||||
@@ -78,7 +63,7 @@ pub const fn in_bit_range(x: i32, high: u32, low: u32) -> bool {
|
||||
// >(T);
|
||||
// pub struct U32Bits<const H: u32, const L: u32>(u32);
|
||||
//
|
||||
// impl<T: Shl<u8, Output = T> + Shr<u8, Output = T> + Add<u8, Output = T>, const S: bool, const H: u8, const L: u8>
|
||||
// impl<T: const Shl<u8, Output = T> + const Shr<u8, Output = T>, const S: bool, const H: u8, const L: u8>
|
||||
// Bits<T, S, H, L>
|
||||
// {
|
||||
// pub const fn new(val: T) -> Self {
|
||||
@@ -86,35 +71,3 @@ pub const fn in_bit_range(x: i32, high: u32, low: u32) -> bool {
|
||||
// Self(val + L)
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn get_bits() {
|
||||
assert_eq!(bits(0b10111010, 5, 3), 0b111);
|
||||
assert_eq!(bits(0b10111010, 7, 5), 0b101);
|
||||
assert_eq!(bits(0b10111010, 2, 0), 0b010);
|
||||
assert_eq!(bits(0b10111010, 7, 7), 0b1);
|
||||
assert_eq!(bits(0b1, 0, 0), 0b1);
|
||||
assert_eq!(bits(0b1, 1, 1), 0b0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn range() {
|
||||
assert!(!in_bit_range(0b00111100, 5, 3));
|
||||
assert!(!in_bit_range(0b00111100, 4, 2));
|
||||
assert!(in_bit_range(0b000111100, 5, 2));
|
||||
assert!(in_bit_range(0b000000001, 0, 0));
|
||||
assert!(in_bit_range(0b000001000, 3, 3));
|
||||
|
||||
assert!(!in_bit_range(-3, 1, 0));
|
||||
assert!(!in_bit_range(-5, 2, 2));
|
||||
assert!(!in_bit_range(-5, 4, 3));
|
||||
assert!(in_bit_range(-1, 1, 0));
|
||||
assert!(in_bit_range(-4, 2, 0));
|
||||
assert!(in_bit_range(-5, 3, 2));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user