From cf9f3469ae5bb2f45faa32017d1b8abbddab43f4 Mon Sep 17 00:00:00 2001 From: shadow cat Date: Sat, 12 Oct 2024 02:24:48 -0400 Subject: [PATCH] WORKING ASM FUNCTION CALLS --- src/compiler/{riscv64 => }/elf.rs | 1 + src/compiler/mod.rs | 36 ++++- src/compiler/program.rs | 46 ++---- src/compiler/riscv64/asm.rs | 57 +++++++- src/compiler/riscv64/base.rs | 78 ++++++++++ src/compiler/riscv64/funct.rs | 24 +++ src/compiler/riscv64/instruction/base.rs | 58 -------- src/compiler/riscv64/instruction/func.rs | 20 --- src/compiler/riscv64/instruction/mod.rs | 53 ------- src/compiler/riscv64/mod.rs | 100 +++++++++---- .../riscv64/{instruction => }/opcode.rs | 1 + src/compiler/riscv64/{instruction => }/reg.rs | 1 + src/compiler/riscv64/single.rs | 51 +++++++ src/util/bits.rs | 137 ++++++------------ 14 files changed, 376 insertions(+), 287 deletions(-) rename src/compiler/{riscv64 => }/elf.rs (97%) create mode 100644 src/compiler/riscv64/base.rs create mode 100644 src/compiler/riscv64/funct.rs delete mode 100644 src/compiler/riscv64/instruction/base.rs delete mode 100644 src/compiler/riscv64/instruction/func.rs delete mode 100644 src/compiler/riscv64/instruction/mod.rs rename src/compiler/riscv64/{instruction => }/opcode.rs (89%) rename src/compiler/riscv64/{instruction => }/reg.rs (98%) create mode 100644 src/compiler/riscv64/single.rs diff --git a/src/compiler/riscv64/elf.rs b/src/compiler/elf.rs similarity index 97% rename from src/compiler/riscv64/elf.rs rename to src/compiler/elf.rs index bd4f6d7..3343d48 100644 --- a/src/compiler/riscv64/elf.rs +++ b/src/compiler/elf.rs @@ -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, start_offset: u64) -> Vec { let addr_start = 0x1000; let page_size = 0x1000; diff --git a/src/compiler/mod.rs b/src/compiler/mod.rs index 80a056c..a38eef0 100644 --- a/src/compiler/mod.rs +++ b/src/compiler/mod.rs @@ -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 - diff --git a/src/compiler/program.rs b/src/compiler/program.rs index cffb6c1..5cdd5b1 100644 --- a/src/compiler/program.rs +++ b/src/compiler/program.rs @@ -1,53 +1,35 @@ use std::collections::HashMap; -pub struct Program { - data: Vec, - ro_map: HashMap, -} - -impl Program { - pub fn new(data: HashMap>) -> 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( ro_data: HashMap>, functions: Vec>, -) -> Vec { +) -> (Vec, Option) { 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 { - label: String, - instructions: Vec, + pub label: String, + pub instructions: Vec, } pub trait Instr { - fn to_le_bytes(&self) -> impl IntoIterator; + fn push(&self, data: &mut Vec, ro_map: &HashMap, pos: u64) -> Option; } -struct SymbolInstr { - i: usize -} diff --git a/src/compiler/riscv64/asm.rs b/src/compiler/riscv64/asm.rs index 92ef7e7..ea3bf20 100644 --- a/src/compiler/riscv64/asm.rs +++ b/src/compiler/riscv64/asm.rs @@ -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, + sym_map: &std::collections::HashMap, + pos: u64, + ) -> Option { + 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 + } } diff --git a/src/compiler/riscv64/base.rs b/src/compiler/riscv64/base.rs new file mode 100644 index 0000000..9fd259a --- /dev/null +++ b/src/compiler/riscv64/base.rs @@ -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 { + self.0.to_le_bytes().into_iter() + } + pub fn to_be_bytes(&self) -> impl IntoIterator { + 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) +} diff --git a/src/compiler/riscv64/funct.rs b/src/compiler/riscv64/funct.rs new file mode 100644 index 0000000..39c9be9 --- /dev/null +++ b/src/compiler/riscv64/funct.rs @@ -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); +} diff --git a/src/compiler/riscv64/instruction/base.rs b/src/compiler/riscv64/instruction/base.rs deleted file mode 100644 index 59a69b7..0000000 --- a/src/compiler/riscv64/instruction/base.rs +++ /dev/null @@ -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 { - 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) -} diff --git a/src/compiler/riscv64/instruction/func.rs b/src/compiler/riscv64/instruction/func.rs deleted file mode 100644 index 684a425..0000000 --- a/src/compiler/riscv64/instruction/func.rs +++ /dev/null @@ -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; -} diff --git a/src/compiler/riscv64/instruction/mod.rs b/src/compiler/riscv64/instruction/mod.rs deleted file mode 100644 index dfc06b4..0000000 --- a/src/compiler/riscv64/instruction/mod.rs +++ /dev/null @@ -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) -} diff --git a/src/compiler/riscv64/mod.rs b/src/compiler/riscv64/mod.rs index e096644..30eec6e 100644 --- a/src/compiler/riscv64/mod.rs +++ b/src/compiler/riscv64/mod.rs @@ -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 { - 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!")) } diff --git a/src/compiler/riscv64/instruction/opcode.rs b/src/compiler/riscv64/opcode.rs similarity index 89% rename from src/compiler/riscv64/instruction/opcode.rs rename to src/compiler/riscv64/opcode.rs index d1e12e7..56ed50d 100644 --- a/src/compiler/riscv64/instruction/opcode.rs +++ b/src/compiler/riscv64/opcode.rs @@ -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; diff --git a/src/compiler/riscv64/instruction/reg.rs b/src/compiler/riscv64/reg.rs similarity index 98% rename from src/compiler/riscv64/instruction/reg.rs rename to src/compiler/riscv64/reg.rs index e161b37..ed71b46 100644 --- a/src/compiler/riscv64/instruction/reg.rs +++ b/src/compiler/riscv64/reg.rs @@ -1,5 +1,6 @@ #![allow(non_upper_case_globals)] +#[derive(Debug, Clone, Copy)] pub struct Reg(u8); /// hard wired 0 diff --git a/src/compiler/riscv64/single.rs b/src/compiler/riscv64/single.rs new file mode 100644 index 0000000..e5805dd --- /dev/null +++ b/src/compiler/riscv64/single.rs @@ -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) +} diff --git a/src/util/bits.rs b/src/util/bits.rs index a805adf..3b00463 100644 --- a/src/util/bits.rs +++ b/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(u32); +#[derive(Debug, Clone, Copy)] +pub struct BitsI32(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; - } - (bits(x, high - 1, low) | !mask(high - 1, low)) == u(x) - } else { - bits(x, high, low) << low == u(x) +impl Bits32 { + pub const fn new(val: u32) -> Self { + let lsh = 31 - H; + let rsh = lsh + L; + debug_assert!(((val << lsh) >> rsh) == (val >> L)); + Self(val) + } + 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) } } -// 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 + Shr>( -// 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) -// } -// } +impl BitsI32 { + 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 { + let lsh = 31 - H; + let rsh = lsh + L; + if ((val << lsh) >> rsh) == (val >> L) { + Some(Self(u(val) & mask(H, L))) + } else { + None + } + } + pub const fn to_u(self) -> Bits32 { + Bits32(self.0) + } +} +// I hate (love) rust https://github.com/rust-lang/rust-project-goals/issues/106 // pub struct Bits< // T: Shl + Shr, // const S: bool, @@ -78,7 +63,7 @@ pub const fn in_bit_range(x: i32, high: u32, low: u32) -> bool { // >(T); // pub struct U32Bits(u32); // -// impl + Shr + Add, const S: bool, const H: u8, const L: u8> +// impl + const Shr, const S: bool, const H: u8, const L: u8> // Bits // { // 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)); - } -}