Full encoding supported, add tests, start encoding the parsed code

This commit is contained in:
Lumi Kalt 2024-01-22 00:12:59 +00:00
parent 4509218b49
commit f0b5be8c63
8 changed files with 342 additions and 173 deletions

View file

@ -1,13 +1,33 @@
use std::collections::HashMap; use std::{collections::HashMap, env::Args};
use crate::{err::RuntimeErr, parser::{Loc, Token}}; use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator};
use crate::{
err::RuntimeErr,
instructions::{instruction, with, Arg},
parser::{Loc, Token},
};
pub enum SymbolValue {
Byte(u8),
Half(u16),
Word(u32),
DWord(u64),
String(String),
}
pub enum Value {
Immediate(i64),
Register(u32),
Symbol(String, SymbolValue),
}
#[derive(Debug)] #[derive(Debug)]
pub struct Env { pub struct Env {
pub register_alias: HashMap<String, u32>, pub register_alias: HashMap<String, u32>,
labels: HashMap<String, u32>, labels: HashMap<String, u32>,
registers: [i64; 32], registers: [u64; 32],
pub stack: Vec<i64>, // TODO: Find the size of the stack pub stack: Vec<u64>, // TODO: Find the size of the stack
pub instructions: Vec<u32>, pub instructions: Vec<u32>,
} }
@ -24,6 +44,7 @@ impl Env {
("t1", 6), ("t1", 6),
("t2", 7), ("t2", 7),
("s0", 8), ("s0", 8),
("fp", 8),
("s1", 9), ("s1", 9),
("a0", 10), ("a0", 10),
("a1", 11), ("a1", 11),
@ -56,16 +77,16 @@ impl Env {
register_alias, register_alias,
labels: HashMap::new(), labels: HashMap::new(),
registers: [0; 32], registers: [0; 32],
stack: Vec::new(), stack: Vec::from([0; 1024]),
instructions: Vec::new(), instructions: Vec::new(),
} }
} }
pub fn set_register(&mut self, reg: u32, value: i64) { pub fn set_register(&mut self, reg: u32, value: u64) {
self.registers[reg as usize] = value; self.registers[reg as usize] = value;
} }
pub fn get_register(&self, reg: u32) -> i64 { pub fn get_register(&self, reg: u32) -> u64 {
self.registers[reg as usize] self.registers[reg as usize]
} }
@ -84,6 +105,13 @@ impl Env {
None None
} }
} }
pub fn reg_to_register(&self, reg: &str) -> Option<u32> {
if reg.starts_with("x") {
self.xn_to_register(reg)
} else {
self.alias_to_register(reg)
}
}
pub fn is_valid_register(&self, reg: &str) -> bool { pub fn is_valid_register(&self, reg: &str) -> bool {
self.alias_to_register(reg) self.alias_to_register(reg)
.or_else(|| self.xn_to_register(reg)) .or_else(|| self.xn_to_register(reg))
@ -98,12 +126,53 @@ impl Env {
self.labels.get(label).copied() self.labels.get(label).copied()
} }
pub fn to_instruction(&self, tokens: Vec<(Token, Loc)>) -> Result<u32, RuntimeErr> { pub fn assemble_op(&mut self, op: (Token, Loc)) -> Result<u32, RuntimeErr> {
let (op, args) = match &tokens[0].0 { if let (Token::Op(name, args), loc) = op {
Token::Op(op, args) => (op, args), let i = instruction(&name);
_ => unreachable!(), let mut imm = 0u32;
}; let mut regs = vec![0; 4];
if args.len() != i.1.len() {
return Err(RuntimeErr::InvalidOpArity(
name,
args.len(),
i.1.len(),
));
}
todo!() let _ = i.1.into_par_iter()
.enumerate()
.try_for_each(|(k, v)| match v {
Arg::Immediate => {
if let Token::Immediate(i) = args[k].0 {
imm = i as u32;
Ok(())
} else {
Err(RuntimeErr::InvalidType("Immediate".to_string(), v.kind()))
}
}
Arg::Register => {
if let Token::Register(r) = args[k].0 {
regs[k] = self.reg_to_register(&r).unwrap();
Ok(())
} else {
Err(RuntimeErr::InvalidType("Register".to_string(), v.kind()))
}
}
Arg::Memory => {
if let Token::Memory(i, r) = args[k].0 {
if r.is_some() {
regs[k] = self.reg_to_register(&r.unwrap()).unwrap();
}
Ok(())
} else {
Err(RuntimeErr::InvalidType("Memory".to_string(), v.kind()))
}
}
_ => unimplemented!()
})?;
Ok(u32::from_str_radix(&with(i, imm, regs).0.to_string(), 2).unwrap())
} else {
unreachable!()
}
} }
} }

View file

@ -30,7 +30,10 @@ impl SyntaxErr {
SyntaxErr::UnmatchedParen(true) => "add '(' before the register name".to_string(), SyntaxErr::UnmatchedParen(true) => "add '(' before the register name".to_string(),
SyntaxErr::UnexpectedChar => "ensure the input is well-formed".to_string(), SyntaxErr::UnexpectedChar => "ensure the input is well-formed".to_string(),
SyntaxErr::OutsideOp(_) => format!("add arguments after the opcode"), SyntaxErr::OutsideOp(_) => format!("add arguments after the opcode"),
SyntaxErr::MemoryInvalidRegister => "registers are either xN (N < 32 with no leading 0) or the standard aliases".to_string(), SyntaxErr::MemoryInvalidRegister => {
"registers are either xN (N < 32 with no leading 0) or the standard aliases"
.to_string()
}
} }
} }
} }
@ -42,6 +45,7 @@ pub enum RuntimeErr {
UnexpectedRegister, UnexpectedRegister,
InvalidOp(String), InvalidOp(String),
InvalidOpArity(String, usize, usize), InvalidOpArity(String, usize, usize),
InvalidType(String, String),
} }
impl Display for RuntimeErr { impl Display for RuntimeErr {
@ -51,11 +55,14 @@ impl Display for RuntimeErr {
RuntimeErr::UnexpectedImmediate => write!(f, "unexpected immediate"), RuntimeErr::UnexpectedImmediate => write!(f, "unexpected immediate"),
RuntimeErr::UnexpectedRegister => write!(f, "unexpected register"), RuntimeErr::UnexpectedRegister => write!(f, "unexpected register"),
RuntimeErr::InvalidOp(op) => write!(f, "invalid operation {}", op), RuntimeErr::InvalidOp(op) => write!(f, "invalid operation {}", op),
RuntimeErr::InvalidOpArity(op, expected, actual) => write!( RuntimeErr::InvalidOpArity(op, actual, expected) => write!(
f, f,
"invalid operation arity {} expected {} got {}", "invalid operation arity {} expected {} got {}",
op, expected, actual op, expected, actual
), ),
RuntimeErr::InvalidType(actual, expected) => {
write!(f, "expected {}, got {}", expected, actual)
}
} }
} }
} }

View file

@ -222,36 +222,56 @@ pub mod kind {
} }
} }
pub enum Value { pub enum Arg {
Register, Register,
Immediate, Immediate,
Memory, Memory,
Symbol, Symbol,
} }
impl Arg {
pub fn kind(&self) -> String {
match self {
Arg::Register => "register",
Arg::Immediate => "immediate",
Arg::Memory => "memory",
Arg::Symbol => "symbol",
}
.to_string()
}
}
use kind::*; use kind::*;
/// (kind, (arity, Vec<token kind>)) /// (kind, (arity, Vec<token kind>))
pub fn instruction(op: &str) -> (Kind, Vec<Value>) { pub fn instruction(op: &str) -> (Kind, Vec<Arg>) {
match op { match op {
// - // -
"nop" => (Kind::Pseudo(Pseudo {}), vec![]), "nop" => (Kind::Pseudo(Pseudo {}), vec![]),
// Move // Move
"li" => ( "li" => (Kind::Pseudo(Pseudo {}), vec![Arg::Register, Arg::Immediate]),
Kind::Pseudo(Pseudo {}),
vec![Value::Register, Value::Immediate],
),
"lui" => ( "lui" => (
Kind::U(U { Kind::U(U {
imm: to_bits(0), imm: to_bits(0),
rd: to_bits(0), rd: to_bits(0),
opcode: to_bits(0b0110111), opcode: to_bits(0b0110111),
}), }),
vec![Value::Register, Value::Immediate], vec![Arg::Register, Arg::Immediate],
), ),
// Memory // Memory
"sb" => (
Kind::S(S {
imm: to_bits(0),
rb: to_bits(0),
ra: to_bits(0),
funct3: to_bits(0b000),
imm2: to_bits(0),
opcode: to_bits(0b0100011),
}),
vec![Arg::Register, Arg::Memory],
),
// Arithmetic, Logic, Shift // Arithmetic, Logic, Shift
"add" => ( "add" => (
@ -263,7 +283,7 @@ pub fn instruction(op: &str) -> (Kind, Vec<Value>) {
rd: to_bits(0), rd: to_bits(0),
opcode: to_bits(0b0110011), opcode: to_bits(0b0110011),
}), }),
vec![Value::Register, Value::Register, Value::Register], vec![Arg::Register, Arg::Register, Arg::Register],
), ),
"addi" => ( "addi" => (
Kind::I(I { Kind::I(I {
@ -273,14 +293,30 @@ pub fn instruction(op: &str) -> (Kind, Vec<Value>) {
rd: to_bits(0), rd: to_bits(0),
opcode: to_bits(0b0010011), opcode: to_bits(0b0010011),
}), }),
vec![Value::Register, Value::Register, Value::Immediate], vec![Arg::Register, Arg::Register, Arg::Immediate],
),
// Multiply, Divide
// Compare
// Flow control (branch, jump, call, ret)
"beq" => (
Kind::B(B {
imm: to_bits(0),
rb: to_bits(0),
ra: to_bits(0),
funct3: to_bits(0b000),
imm2: to_bits(0),
opcode: to_bits(0b1100011),
}),
vec![Arg::Register, Arg::Register, Arg::Immediate],
), ),
_ => unimplemented!(), _ => unimplemented!(),
} }
} }
/// Order: rd, ra, rb, rc /// regs order: rd, ra, rb, rc
pub fn with_reg_args((kind, args): (Kind, Vec<Value>), regs: Vec<u32>) -> (Kind, Vec<Value>) { pub fn with((kind, args): (Kind, Vec<Arg>), imm: u32, regs: Vec<u32>) -> (Kind, Vec<Arg>) {
match kind { match kind {
Kind::Pseudo(_) => (kind, args), Kind::Pseudo(_) => (kind, args),
Kind::R(r) => ( Kind::R(r) => (
@ -308,7 +344,7 @@ pub fn with_reg_args((kind, args): (Kind, Vec<Value>), regs: Vec<u32>) -> (Kind,
), ),
Kind::I(i) => ( Kind::I(i) => (
Kind::I(I { Kind::I(I {
imm: i.imm, imm: to_bits(imm),
ra: to_bits(regs[1]), ra: to_bits(regs[1]),
funct3: i.funct3, funct3: i.funct3,
rd: to_bits(regs[0]), rd: to_bits(regs[0]),
@ -319,7 +355,7 @@ pub fn with_reg_args((kind, args): (Kind, Vec<Value>), regs: Vec<u32>) -> (Kind,
Kind::I2(i2) => ( Kind::I2(i2) => (
Kind::I2(I2 { Kind::I2(I2 {
funct6: i2.funct6, funct6: i2.funct6,
imm: i2.imm, imm: to_bits(imm),
ra: to_bits(regs[1]), ra: to_bits(regs[1]),
funct3: i2.funct3, funct3: i2.funct3,
rd: to_bits(regs[0]), rd: to_bits(regs[0]),
@ -327,88 +363,21 @@ pub fn with_reg_args((kind, args): (Kind, Vec<Value>), regs: Vec<u32>) -> (Kind,
}), }),
args, args,
), ),
Kind::S(s) => (
Kind::S(S {
imm: s.imm,
rb: to_bits(regs[2]),
ra: to_bits(regs[1]),
funct3: s.funct3,
imm2: s.imm2,
opcode: s.opcode,
}),
args,
),
Kind::B(b) => (
Kind::B(B {
imm: b.imm,
rb: to_bits(regs[2]),
ra: to_bits(regs[1]),
funct3: b.funct3,
imm2: b.imm2,
opcode: b.opcode,
}),
args,
),
Kind::U(u) => (
Kind::U(U {
imm: u.imm,
rd: to_bits(regs[0]),
opcode: u.opcode,
}),
args,
),
Kind::J(j) => (
Kind::J(J {
imm: j.imm,
rd: to_bits(regs[0]),
opcode: j.opcode,
}),
args,
),
}
}
pub fn with_imm((kind, args): (Kind, Vec<Value>), imm: u32) -> (Kind, Vec<Value>) {
match kind {
Kind::Pseudo(_) => (kind, args),
Kind::R(r) => (Kind::R(r), args),
Kind::R4(r4) => (Kind::R4(r4), args),
Kind::I(i) => (
Kind::I(I {
imm: to_bits(imm),
ra: i.ra,
funct3: i.funct3,
rd: i.rd,
opcode: i.opcode,
}),
args,
),
Kind::I2(i2) => (
Kind::I2(I2 {
funct6: i2.funct6,
imm: to_bits(imm),
ra: i2.ra,
funct3: i2.funct3,
rd: i2.rd,
opcode: i2.opcode,
}),
args,
),
Kind::S(s) => { Kind::S(s) => {
let bits = to_bits::<32>(imm); let bits = to_bits::<32>(imm);
( (
Kind::S(S { Kind::S(S {
imm: { imm: {
let mut imm = [false; 7]; let mut imm = [false; 7];
imm.copy_from_slice(&bits[11..=5]); imm.copy_from_slice(&bits[5..=11]); //.into_iter().rev().map(|&b| b).collect::<Vec<_>>()[..]);
imm imm
}, },
rb: s.rb, rb: to_bits(regs[2]),
ra: s.ra, ra: to_bits(regs[1]),
funct3: s.funct3, funct3: s.funct3,
imm2: { imm2: {
let mut imm2 = [false; 5]; let mut imm2 = [false; 5];
imm2.copy_from_slice(&bits[4..=0]); imm2.copy_from_slice(&bits[0..=4]); //.into_iter().rev().map(|&b| b).collect::<Vec<_>>()[..]);
imm2 imm2
}, },
opcode: s.opcode, opcode: s.opcode,
@ -423,15 +392,15 @@ pub fn with_imm((kind, args): (Kind, Vec<Value>), imm: u32) -> (Kind, Vec<Value>
imm: { imm: {
let mut imm = [false; 7]; let mut imm = [false; 7];
imm[6] = bits[12]; imm[6] = bits[12];
imm[5..=0].copy_from_slice(&bits[10..=5]); imm[0..=5].copy_from_slice(&bits[5..=10]);
imm imm
}, },
rb: b.rb, rb: to_bits(regs[2]),
ra: b.ra, ra: to_bits(regs[1]),
funct3: b.funct3, funct3: b.funct3,
imm2: { imm2: {
let mut imm2 = [false; 5]; let mut imm2 = [false; 5];
imm2[4..=1].copy_from_slice(&bits[4..=1]); imm2[1..=4].copy_from_slice(&bits[1..=4]);
imm2[0] = bits[11]; imm2[0] = bits[11];
imm2 imm2
}, },
@ -448,7 +417,7 @@ pub fn with_imm((kind, args): (Kind, Vec<Value>), imm: u32) -> (Kind, Vec<Value>
imm.copy_from_slice(&bits[31..=12]); imm.copy_from_slice(&bits[31..=12]);
imm imm
}, },
rd: u.rd, rd: to_bits(regs[0]),
opcode: u.opcode, opcode: u.opcode,
}), }),
args, args,
@ -464,7 +433,7 @@ pub fn with_imm((kind, args): (Kind, Vec<Value>), imm: u32) -> (Kind, Vec<Value>
imm[7..=0].copy_from_slice(&bits[19..=12]); imm[7..=0].copy_from_slice(&bits[19..=12]);
imm imm
}, },
rd: j.rd, rd: to_bits(regs[0]),
opcode: j.opcode, opcode: j.opcode,
}), }),
args, args,

View file

@ -2,3 +2,4 @@ pub mod env;
pub mod err; pub mod err;
pub mod instructions; pub mod instructions;
pub mod parser; pub mod parser;
pub mod tests;

View file

@ -9,7 +9,7 @@ use codespan_reporting::{
}; };
use riscv_interpreter::{ use riscv_interpreter::{
env::Env, env::Env,
instructions::{instruction, with_imm, with_reg_args}, instructions::{instruction, with},
parser::parse, parser::parse,
}; };
@ -22,74 +22,35 @@ fn main() -> anyhow::Result<()> {
let env = Env::new(); let env = Env::new();
// match parse(&env, &input) { match parse(&env, &input) {
// Ok(tokens) => { Ok(tokens) => {
// println!("{:#?} -> {:#?}", input, tokens); println!("{:#?} -> {:#?}", input, tokens);
// } }
// Err(errs) => { Err(errs) => {
// for err in errs { for err in errs {
// let start = err.1.start; let start = err.1.start;
// let end = err.1.end + 1; let end = err.1.end + 1;
// let diagnostic = Diagnostic::error() let diagnostic = Diagnostic::error()
// .with_message("Syntax Error") .with_message("Syntax Error")
// .with_labels(vec![ .with_labels(vec![
// Label::primary((), start..end).with_message(err.0.to_string()) Label::primary((), start..end).with_message(err.0.to_string())
// ]) ])
// .with_notes({ .with_notes({
// let mut notes = Vec::new(); let mut notes = Vec::new();
// if let Some(note) = err.3 { if let Some(note) = err.3 {
// notes.push(note); notes.push(note);
// } }
// notes.push(err.0.note()); notes.push(err.0.note());
// notes notes
// }); });
// term::emit(&mut writer.lock(), &config, &file, &diagnostic).unwrap(); term::emit(&mut writer.lock(), &config, &file, &diagnostic).unwrap();
// } }
// } return Ok(());
// }; }
};
println!("nop: {}", instruction("nop").0);
println!(
"add a0 a0 a1: {}",
with_reg_args(
instruction("add"),
vec![
env.alias_to_register("a0").unwrap() as u32,
env.alias_to_register("a0").unwrap() as u32,
env.alias_to_register("a1").unwrap() as u32
]
)
.0 // 011001101010000001010010110000000
// Ripes: 011001101010000001010010110000000
);
println!(
"addi a0 a0 1: {}",
with_imm(
with_reg_args(
instruction("addi"),
vec![
env.alias_to_register("a0").unwrap(),
env.alias_to_register("a0").unwrap()
]
),
1
)
.0 // 00000000000101010000010100010011
// Ripes: 00000000000101010000010100010011
);
println!(
"lui a0 1: {}",
with_imm(
with_reg_args(
instruction("lui"),
vec![env.alias_to_register("a0").unwrap()]
),
1
)
.0
);
Ok(()) Ok(())
} }

View file

@ -10,7 +10,7 @@ pub enum Token {
/// \# blablabla, /// \# blablabla,
Comment, Comment,
/// 1, 2, -1 /// 1, 2, -1
Immediate(i64), Immediate(u32),
/// zero, r1, pc /// zero, r1, pc
/// ///
/// Technically also label references and symbols, but we'll handle those later /// Technically also label references and symbols, but we'll handle those later

159
src/tests.rs Normal file
View file

@ -0,0 +1,159 @@
#[cfg(test)]
/// Test values come from Ripes
use crate::{
env::Env,
instructions::{instruction, with},
};
#[test]
#[ignore = "TODO"]
fn nop() {
#[rustfmt::skip]
{
// nop (pseudo) -> addi x0, x0, 0
// I-Type
// | imm12 | ra |f3 | rd | opcode
// 000000000000 00000 000 00000 0010011
};
// nop
assert_eq!(
u32::from_str_radix(
&with(
instruction("nop"),
0, // imm
vec![]
)
.0
.to_string(),
2
)
.unwrap(),
0b00000000000000000000000000010011u32
);
}
#[test]
fn sb() {
let env = Env::new();
#[rustfmt::skip]
{
// S-Type
// | imm | rb | ra |f3 | rd | opcode
// 1111111 11110 00010 000 11100 0100011
};
// sb t5 -4(sp)
assert_eq!(
u32::from_str_radix(
&with(
instruction("sb"),
-4i32 as u32, // imm
vec![
0, // rd
env.alias_to_register("sp").unwrap(), // ra
env.alias_to_register("t5").unwrap() // rb
],
)
.0
.to_string(),
2
)
.unwrap(),
0b11111111111000010000111000100011u32
);
}
#[test]
fn add() {
let env = Env::new();
#[rustfmt::skip]
{
// R-Type
// | f7 | rb | ra |f3 | rd | opcode
// 0000000 01011 01010 000 01010 0110011
};
// add a0 a0 a1
assert_eq!(
u32::from_str_radix(
&with(
instruction("add"),
0, // imm
vec![
env.alias_to_register("a0").unwrap(), // rd
env.alias_to_register("a0").unwrap(), // ra
env.alias_to_register("a1").unwrap() // rb
]
)
.0
.to_string(),
2
)
.unwrap(),
0b00000000101101010000010100110011u32
);
}
#[test]
fn addi() {
let env = Env::new();
#[rustfmt::skip]
{
// I-Type
// | imm12 | ra |f3 | rd | opcode
// 000000000001 01010 000 01010 0010011
// 000000000001 01010 000 01010 0010011 (Ripes)
};
// addi a0 a0 1
assert_eq!(
u32::from_str_radix(
with(
instruction("addi"),
1,
vec![
env.alias_to_register("a0").unwrap(),
env.alias_to_register("a0").unwrap()
],
)
.0
.to_string()
.as_str(),
2
)
.unwrap(),
0b00000000000101010000010100010011u32
);
}
#[test]
fn beq() {
let env = Env::new();
#[rustfmt::skip]
{
// B-Type
// | imm7 | rb | ra |f3 |imm5 | opcode
// 0000000 01011 01010 000 00100 1100011
// 0000000 01011 01010 000 00100 1100011 (Ripes)
};
// beq a0 a1 4
assert_eq!(
u32::from_str_radix(
&with(
instruction("beq"),
4,
vec![
0, // no rd
env.alias_to_register("a0").unwrap(),
env.alias_to_register("a1").unwrap()
]
)
.0
.to_string(),
2
)
.unwrap(),
0b00000000101101010000001001100011u32
);
}

5
test.s
View file

@ -1 +1,4 @@
x0 li 8(x0) a:
addi a0 zero 1
addi a1 zero 2
add a2 a1 a0