Handle Pseudo-Instructions, ensure labels are properly setup before assembling

This commit is contained in:
Lumi Kalt 2024-01-22 22:17:20 +00:00
parent 08c79d3ab7
commit 1338f93898
7 changed files with 282 additions and 54 deletions

View file

@ -2,7 +2,7 @@ use std::collections::HashMap;
use crate::{ use crate::{
err::RuntimeErr, err::RuntimeErr,
instructions::{instruction, with, Arg}, instructions::{handle_pseudo, instruction, kind::Kind, with, Arg},
parser::{Loc, Token}, parser::{Loc, Token},
}; };
@ -18,8 +18,8 @@ pub enum SymbolValue {
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: [u64; 32], registers: [u32; 32],
pub stack: Vec<u64>, // TODO: Find the size of the stack pub stack: Vec<u32>, // TODO: Find the size of the stack
pub instructions: Vec<u32>, pub instructions: Vec<u32>,
} }
@ -74,11 +74,11 @@ impl Env {
} }
} }
pub fn set_register(&mut self, reg: u32, value: u64) { pub fn set_register(&mut self, reg: u32, value: u32) {
self.registers[reg as usize] = value; self.registers[reg as usize] = value;
} }
pub fn get_register(&self, reg: u32) -> u64 { pub fn get_register(&self, reg: u32) -> u32 {
self.registers[reg as usize] self.registers[reg as usize]
} }
@ -121,9 +121,17 @@ impl Env {
pub fn assemble_op( pub fn assemble_op(
&self, &self,
op: (Token, Loc), op: (Token, Loc),
) -> Result<u32, (RuntimeErr, Loc, Option<String>)> { ) -> Result<Vec<u32>, (RuntimeErr, Loc, Option<String>)> {
if let (Token::Op(name, args), loc) = op { if let (Token::Op(name, args), loc) = op {
let i = instruction(&name); let i = if let Some(i) = instruction(&name) {
i
} else {
return Err((
RuntimeErr::InvalidOp,
loc,
Some("no implementation exists".to_string()),
));
};
let mut imm = 0u32; let mut imm = 0u32;
let mut regs = vec![0; 4]; let mut regs = vec![0; 4];
if args.len() != i.1.len() { if args.len() != i.1.len() {
@ -145,7 +153,10 @@ impl Env {
Ok(()) Ok(())
} else { } else {
Err(( Err((
RuntimeErr::InvalidType("immediate".to_string(), v.kind()), RuntimeErr::InvalidType(
Arg::from(args[k].0.clone()).kind(),
v.kind(),
),
args[k].1, args[k].1,
None, None,
)) ))
@ -157,7 +168,10 @@ impl Env {
Ok(()) Ok(())
} else { } else {
Err(( Err((
RuntimeErr::InvalidType("register".to_string(), v.kind()), RuntimeErr::InvalidType(
Arg::from(args[k].0.clone()).kind(),
v.kind(),
),
args[k].1, args[k].1,
None, None,
)) ))
@ -184,17 +198,80 @@ impl Env {
Ok(()) Ok(())
} else { } else {
Err(( Err((
RuntimeErr::InvalidType("memory".to_string(), v.kind()), RuntimeErr::InvalidType(
Arg::from(args[k].0.clone()).kind(),
v.kind(),
),
args[k].1,
None,
))
}
}
Arg::Symbol => {
if let Token::Symbol(s) = &args[k].0 {
if let Some(v) = self.get_label(&s) {
imm = v;
Ok(())
} else {
Err((
RuntimeErr::InvalidType(
Arg::from(args[k].0.clone()).kind(),
v.kind(),
),
args[k].1,
None,
))
}
} else {
Err((
RuntimeErr::InvalidType(
Arg::from(args[k].0.clone()).kind(),
v.kind(),
),
args[k].1, args[k].1,
None, None,
)) ))
} }
} }
_ => unimplemented!(),
})?; })?;
Ok(u32::from_str_radix(dbg!(&with(i, imm, regs).0.to_string()), 2).unwrap()) Ok({
if let Kind::Pseudo(_) = i.0 {
handle_pseudo(i, imm, regs)
.into_iter()
.map(|x| u32::from_str_radix(&x.0.to_string(), 2).unwrap())
.collect()
} else {
vec![u32::from_str_radix(&with(i, imm, regs).0.to_string(), 2).unwrap()]
}
})
} else { } else {
unreachable!() unreachable!()
} }
} }
pub fn handle_labels(&mut self, tokens: Vec<(Token, Loc)>) {
let mut i = 0;
// Calculate the instruction position for all opcodes to
// allow for labels to be used before they are defined
tokens.into_iter().for_each(|(token, _)| match token {
Token::Op(name, _) => {
if let Some((kind, args)) = instruction(&name) {
if let Kind::Pseudo(_) = kind {
handle_pseudo((kind, args), 0, vec![0; 4])
.into_iter()
.for_each(|_| i += 1);
} else {
i += 1;
}
}
}
Token::Label(name) => {
self.add_label(&name, i * 4);
}
other => {
dbg!(other);
unreachable!()
}
});
}
} }

View file

@ -54,7 +54,7 @@ pub enum RuntimeErr {
impl Display for RuntimeErr { impl Display for RuntimeErr {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self { match self {
RuntimeErr::InvalidOp => write!(f, "invalid operation"), RuntimeErr::InvalidOp => write!(f, "invalid opcode"),
RuntimeErr::InvalidOpArity(op, actual, expected) => { RuntimeErr::InvalidOpArity(op, actual, expected) => {
write!(f, "`{}` expected {} args, got {}", op, expected, actual) write!(f, "`{}` expected {} args, got {}", op, expected, actual)
} }
@ -70,7 +70,7 @@ impl RuntimeErr {
match self { match self {
RuntimeErr::InvalidOp => "check the ref sheet for the avaliable opcodes".to_string(), RuntimeErr::InvalidOp => "check the ref sheet for the avaliable opcodes".to_string(),
RuntimeErr::InvalidOpArity(op, actual, expected) => { RuntimeErr::InvalidOpArity(op, actual, expected) => {
let args = instruction(op).1; let args = instruction(op).unwrap().1;
match actual.cmp(expected) { match actual.cmp(expected) {
Ordering::Equal => unreachable!(), Ordering::Equal => unreachable!(),
Ordering::Greater if actual - expected == 1 => { Ordering::Greater if actual - expected == 1 => {

View file

@ -4,7 +4,7 @@ pub mod kind {
use crate::instructions::to_u32; use crate::instructions::to_u32;
/// will be converted by the engine to a real instruction /// will be converted by the engine to a real instruction
pub struct Pseudo {} pub struct Pseudo(pub &'static str);
pub struct R { pub struct R {
pub funct7: [bool; 7], pub funct7: [bool; 7],
@ -110,7 +110,6 @@ pub mod kind {
impl Display for Pseudo { impl Display for Pseudo {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
// (pseudo) padded on either side with - to make it 32 characters
write!(f, "{:0^32}", 0) write!(f, "{:0^32}", 0)
} }
} }
@ -229,6 +228,7 @@ pub enum Arg {
Immediate, Immediate,
/// always ra /// always ra
Memory, Memory,
// Apperently a symbol is a label but in respect to the current pc
Symbol, Symbol,
} }
@ -244,17 +244,31 @@ impl Arg {
} }
} }
impl From<Token> for Arg {
fn from(token: Token) -> Self {
match token {
Token::Immediate(_) => Arg::Immediate,
Token::Register(_) => Arg::Register(0),
Token::Memory(_, _) => Arg::Memory,
Token::Symbol(_) => Arg::Symbol,
_ => unreachable!(),
}
}
}
use kind::*; use kind::*;
use crate::parser::Token;
/// (kind, (arity, Vec<token kind>)) /// (kind, (arity, Vec<token kind>))
pub fn instruction(op: &str) -> (Kind, Vec<Arg>) { pub fn instruction(op: &str) -> Option<(Kind, Vec<Arg>)> {
match op { Some(match op {
// - // -
"nop" => (Kind::Pseudo(Pseudo {}), vec![]), "nop" => (Kind::Pseudo(Pseudo("nop")), vec![]),
// Move // Move
"li" => ( "li" => (
Kind::Pseudo(Pseudo {}), Kind::Pseudo(Pseudo("li")),
vec![Arg::Register(0), Arg::Immediate], vec![Arg::Register(0), Arg::Immediate],
), ),
"lui" => ( "lui" => (
@ -301,7 +315,30 @@ pub fn instruction(op: &str) -> (Kind, Vec<Arg>) {
}), }),
vec![Arg::Register(0), Arg::Register(1), Arg::Immediate], vec![Arg::Register(0), Arg::Register(1), Arg::Immediate],
), ),
// Multiply, Divide // Multiply, Divide
"mul" => (
Kind::R(R {
funct7: to_bits(0b0000001),
rb: to_bits(0),
ra: to_bits(0),
funct3: to_bits(0b000),
rd: to_bits(0),
opcode: to_bits(0b0110011),
}),
vec![Arg::Register(0), Arg::Register(1), Arg::Register(2)],
),
"div" => (
Kind::R(R {
funct7: to_bits(0b0000001),
rb: to_bits(0),
ra: to_bits(0),
funct3: to_bits(0b100),
rd: to_bits(0),
opcode: to_bits(0b0110011),
}),
vec![Arg::Register(0), Arg::Register(1), Arg::Register(2)],
),
// Compare // Compare
@ -317,8 +354,31 @@ pub fn instruction(op: &str) -> (Kind, Vec<Arg>) {
}), }),
vec![Arg::Register(1), Arg::Register(2), Arg::Immediate], vec![Arg::Register(1), Arg::Register(2), Arg::Immediate],
), ),
"bne" => (
Kind::B(B {
imm: to_bits(0),
rb: to_bits(0),
ra: to_bits(0),
funct3: to_bits(0b001),
imm2: to_bits(0),
opcode: to_bits(0b1100011),
}),
vec![Arg::Register(1), Arg::Register(2), Arg::Immediate],
),
"beqz" => (
Kind::Pseudo(Pseudo("beqz")),
vec![Arg::Register(1), Arg::Symbol],
),
"bnez" => (
Kind::Pseudo(Pseudo("bnez")),
vec![Arg::Register(1), Arg::Symbol],
),
op => unimplemented!("{}", op), op => unimplemented!("{}", op),
})
} }
pub fn get_instruction(op: &str) -> (Kind, Vec<Arg>) {
unsafe { instruction(op).unwrap_unchecked() }
} }
/// regs order: rd, ra, rb, rc /// regs order: rd, ra, rb, rc
@ -447,6 +507,55 @@ pub fn with((kind, args): (Kind, Vec<Arg>), imm: u32, regs: Vec<u32>) -> (Kind,
} }
} }
/// regs order: rd, ra, rb, rc
pub fn handle_pseudo(
(kind, args): (Kind, Vec<Arg>),
imm: u32,
regs: Vec<u32>,
) -> Vec<(Kind, Vec<Arg>)> {
let op = if let Kind::Pseudo(Pseudo(op)) = kind {
op
} else {
return vec![(kind, args)];
};
match op {
"nop" => vec![
// addi x0, x0, 0
with(get_instruction("addi"), 0, vec![0, 0]),
],
"li" => {
match imm {
// if the immediate is small enough (12 bits), use addi
_ if imm >> 12 == 0 => {
vec![with(get_instruction("addi"), imm, regs)]
}
// if the immediate is a multiple of 0x1000, use lui
_ if imm & 0xfff == 0 => {
vec![with(get_instruction("lui"), imm, regs)]
}
// otherwise, use lui and addi
_ => vec![
with(get_instruction("lui"), imm >> 12, regs.clone()),
with(get_instruction("addi"), imm & 0xfff, regs),
],
}
}
"beqz" => vec![
// beq ra, x0, imm
with(get_instruction("beq"), imm, vec![0, regs[0], 0]),
],
"bnez" => vec![
// bne ra, x0, imm
with(get_instruction("bne"), imm, vec![0, regs[0], 0]),
],
other => {
dbg!(other);
unimplemented!()
}
}
}
fn to_bits<const N: usize>(val: u32) -> [bool; N] { fn to_bits<const N: usize>(val: u32) -> [bool; N] {
let mut bits = [false; N]; let mut bits = [false; N];
for i in 0..N { for i in 0..N {

View file

@ -19,7 +19,7 @@ fn main() -> anyhow::Result<()> {
let file = SimpleFile::new("test.s", input.clone()); let file = SimpleFile::new("test.s", input.clone());
let env = Env::new(); let mut env = Env::new();
match parse(&env, &input) { match parse(&env, &input) {
Ok(tokens) => { Ok(tokens) => {
@ -28,18 +28,29 @@ fn main() -> anyhow::Result<()> {
tokens.iter().enumerate().for_each(|(line, token)| { tokens.iter().enumerate().for_each(|(line, token)| {
let token = token.clone(); let token = token.clone();
let mut formatted = format!("{:<1$}", lines[line].to_string() + ":", size + 3); env.handle_labels(tokens.clone());
match token.0 { match token.0 {
Token::Op(..) => match env.assemble_op(token) { Token::Op(..) => match env.assemble_op(token) {
Ok(op) => { Ok(op) => {
formatted += &format!("{:032b}", op); let mut formatted = format!(
"{:<1$} {2:032b}",
lines[line].to_string() + ":",
size + 3,
op[0]
);
if op.len() > 1 {
for op in op[1..].iter() {
formatted += &format!("{:<1$} {2:032b}", "", size + 3, op);
}
}
println!("{}", formatted); println!("{}", formatted);
} }
Err(err) => { Err(err) => {
let diagnostic = Diagnostic::error() let diagnostic = Diagnostic::error()
.with_message("Runtime Error") .with_message("Runtime Error")
.with_labels(vec![Label::primary((), err.1.start..err.1.end) .with_labels(vec![Label::primary((), err.1.start..(err.1.end + 1))
.with_message(err.0.to_string())]) .with_message(err.0.to_string())])
.with_notes({ .with_notes({
let mut notes = Vec::new(); let mut notes = Vec::new();
@ -53,7 +64,7 @@ fn main() -> anyhow::Result<()> {
term::emit(&mut writer.lock(), &config, &file, &diagnostic).unwrap(); term::emit(&mut writer.lock(), &config, &file, &diagnostic).unwrap();
} }
}, },
Token::LabelDef(name) => { Token::Label(name) => {
println!("{name}:"); println!("{name}:");
} }
_ => unreachable!(), _ => unreachable!(),

View file

@ -4,10 +4,8 @@ use itertools::Itertools;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Token { pub enum Token {
/// ' ', '\t', '\r' /// ' ', '\t', '\r', \# blablabla
Spacing, Spacing,
/// \# blablabla,
Comment,
/// 1, 2, -1 /// 1, 2, -1
Immediate(u32), Immediate(u32),
/// zero, r1, pc /// zero, r1, pc
@ -16,8 +14,8 @@ pub enum Token {
Register(String), Register(String),
/// add, xor, j /// add, xor, j
Op(String, Vec<(Token, Loc)>), Op(String, Vec<(Token, Loc)>),
/// <label>: /// \<label>:
LabelDef(String), Label(String),
/// 0(a0) /// 0(a0)
Memory(Box<Token>, Option<Box<Token>>), Memory(Box<Token>, Option<Box<Token>>),
/// symbol /// symbol
@ -34,11 +32,10 @@ impl Token {
use Token::*; use Token::*;
match self { match self {
Spacing => "spacing", Spacing => "spacing",
Comment => "comment",
Immediate(_) => "immediate", Immediate(_) => "immediate",
Register(_) => "register", Register(_) => "register",
Op(_, _) => "op", Op(_, _) => "op",
LabelDef(_) => "label def", Label(_) => "label",
Memory(_, _) => "memory", Memory(_, _) => "memory",
Symbol(_) => "symbol", Symbol(_) => "symbol",
String(_) => "string", String(_) => "string",
@ -71,7 +68,7 @@ fn parse_line(env: &Env, input: &str, loc: &mut Loc) -> Result<Vec<(Token, Loc)>
chars.next(); chars.next();
loc.end += 1; loc.end += 1;
} }
Comment Spacing
} }
'0'..='9' => { '0'..='9' => {
@ -83,7 +80,7 @@ fn parse_line(env: &Env, input: &str, loc: &mut Loc) -> Result<Vec<(Token, Loc)>
if let Some('(') | Some(' ') | None = chars.peek() { if let Some('(') | Some(' ') | None = chars.peek() {
Immediate(num.parse().unwrap()) Immediate(num.parse().unwrap())
} else { } else {
return Err(( let err = Err((
SyntaxErr::UnexpectedChar, SyntaxErr::UnexpectedChar,
Loc { Loc {
line: loc.line, line: loc.line,
@ -93,6 +90,8 @@ fn parse_line(env: &Env, input: &str, loc: &mut Loc) -> Result<Vec<(Token, Loc)>
tokens.clone(), tokens.clone(),
None, None,
)); ));
advance_to_next_line(&mut chars, loc);
return err;
} }
} }
'-' => { '-' => {
@ -110,12 +109,14 @@ fn parse_line(env: &Env, input: &str, loc: &mut Loc) -> Result<Vec<(Token, Loc)>
imm = Box::new(tokens.pop().unwrap()); imm = Box::new(tokens.pop().unwrap());
loc.start = imm.1.start; loc.start = imm.1.start;
} else { } else {
return Err(( let err = Err((
SyntaxErr::UnexpectedChar, SyntaxErr::UnexpectedChar,
loc.clone(), loc.clone(),
tokens.clone(), tokens.clone(),
Some("a memory index must be of the form imm(reg) or imm".to_string()), Some("a memory index must be of the form imm(reg) or imm".to_string()),
)); ));
advance_to_next_line(&mut chars, loc);
return err;
} }
let mut reg = std::string::String::new(); let mut reg = std::string::String::new();
@ -129,7 +130,7 @@ fn parse_line(env: &Env, input: &str, loc: &mut Loc) -> Result<Vec<(Token, Loc)>
let reg = reg.trim(); let reg = reg.trim();
if !env.is_valid_register(reg) { if !env.is_valid_register(reg) {
return Err(( let err = Err((
SyntaxErr::MemoryInvalidRegister, SyntaxErr::MemoryInvalidRegister,
Loc { Loc {
line: loc.line, line: loc.line,
@ -139,14 +140,18 @@ fn parse_line(env: &Env, input: &str, loc: &mut Loc) -> Result<Vec<(Token, Loc)>
tokens.clone(), tokens.clone(),
None, None,
)); ));
advance_to_next_line(&mut chars, loc);
return err;
} }
if chars.next() != Some(')') { if chars.next() != Some(')') {
return Err(( let err = Err((
SyntaxErr::UnmatchedParen(false), SyntaxErr::UnmatchedParen(false),
loc.clone(), loc.clone(),
tokens.clone(), tokens.clone(),
None, None,
)); ));
advance_to_next_line(&mut chars, loc);
return err;
} }
loc.end += 2; loc.end += 2;
@ -156,12 +161,14 @@ fn parse_line(env: &Env, input: &str, loc: &mut Loc) -> Result<Vec<(Token, Loc)>
) )
} }
')' => { ')' => {
return Err(( let err = Err((
SyntaxErr::UnmatchedParen(true), SyntaxErr::UnmatchedParen(true),
loc.clone(), loc.clone(),
tokens.clone(), tokens.clone(),
None, None,
)) ));
advance_to_next_line(&mut chars, loc);
return err;
} }
// Opcode or Label definition // Opcode or Label definition
@ -176,14 +183,18 @@ fn parse_line(env: &Env, input: &str, loc: &mut Loc) -> Result<Vec<(Token, Loc)>
if let Some(':') = chars.peek() { if let Some(':') = chars.peek() {
chars.next(); chars.next();
loc.end += 1; loc.end += 1;
LabelDef(str[..str.len()].to_string()) Label(str[..str.len()].to_string())
} else { } else {
// These Registers may actually be ops, label references or symbols, but there's ambiguity // These Registers may actually be ops, label references or symbols, but there's ambiguity
// between them and registers, so we'll just assume they're registers for now // between them and registers, so we'll just assume they're registers for now
Register(str.trim().to_owned()) Register(str.trim().to_owned())
} }
} }
_ => return Err((SyntaxErr::UnexpectedChar, loc.clone(), tokens.clone(), None)), _ => {
let err = Err((SyntaxErr::UnexpectedChar, loc.clone(), tokens.clone(), None));
advance_to_next_line(&mut chars, loc);
return err;
}
}; };
tokens.push((token, loc.clone())); tokens.push((token, loc.clone()));
loc.end += 1; loc.end += 1;
@ -232,8 +243,18 @@ fn parse_line(env: &Env, input: &str, loc: &mut Loc) -> Result<Vec<(Token, Loc)>
loc.clone(), loc.clone(),
)]; )];
} }
for (token, loc) in group[1..].iter() {
args.extend_from_slice(&group[1..]); match token.clone() {
Token::Register(name) => {
if env.is_valid_register(&name) {
args.push((token.clone(), loc.clone()));
} else {
args.push((Token::Symbol(name.to_owned()), *loc))
}
}
others => args.push((others, *loc)),
}
}
vec![(Op(name, args), loc)] vec![(Op(name, args), loc)]
} else { } else {
@ -281,3 +302,12 @@ pub fn parse(env: &Env, input: &str) -> Result<Vec<(Token, Loc)>, Vec<ParseErr>>
Err(err.into_iter().map(|line| line.unwrap_err()).collect()) Err(err.into_iter().map(|line| line.unwrap_err()).collect())
} }
} }
fn advance_to_next_line(chars: &mut std::iter::Peekable<std::str::Chars>, loc: &mut Loc) {
while let Some(_) = chars.peek() {
chars.next();
loc.end += 1;
}
loc.end += 1; // Newline
loc.start = loc.end;
}

View file

@ -2,7 +2,7 @@
/// Test values come from Ripes /// Test values come from Ripes
use crate::{ use crate::{
env::Env, env::Env,
instructions::{instruction, with}, instructions::{get_instruction, with},
}; };
#[test] #[test]
@ -19,7 +19,7 @@ fn nop() {
assert_eq!( assert_eq!(
u32::from_str_radix( u32::from_str_radix(
&with( &with(
instruction("nop"), get_instruction("nop"),
0, // imm 0, // imm
vec![] vec![]
) )
@ -46,7 +46,7 @@ fn sb() {
assert_eq!( assert_eq!(
u32::from_str_radix( u32::from_str_radix(
&with( &with(
instruction("sb"), get_instruction("sb"),
-4i32 as u32, // imm -4i32 as u32, // imm
vec![ vec![
0, // rd 0, // rd
@ -77,7 +77,7 @@ fn add() {
assert_eq!( assert_eq!(
u32::from_str_radix( u32::from_str_radix(
&with( &with(
instruction("add"), get_instruction("add"),
0, // imm 0, // imm
vec![ vec![
env.alias_to_register("a0").unwrap(), // rd env.alias_to_register("a0").unwrap(), // rd
@ -109,7 +109,7 @@ fn addi() {
assert_eq!( assert_eq!(
u32::from_str_radix( u32::from_str_radix(
with( with(
instruction("addi"), get_instruction("addi"),
1, 1,
vec![ vec![
env.alias_to_register("a0").unwrap(), env.alias_to_register("a0").unwrap(),
@ -141,7 +141,7 @@ fn beq() {
assert_eq!( assert_eq!(
u32::from_str_radix( u32::from_str_radix(
&with( &with(
instruction("beq"), get_instruction("beq"),
4, 4,
vec![ vec![
0, // no rd 0, // no rd

9
test.s
View file

@ -2,8 +2,9 @@
li a1 1 li a1 1
factorial: factorial:
beqz a0 end # beqz a0 end
mul a1 a1 a0 mul a1 a1 a0 -1
addi a0 a0 -1 a0 addi a0 a0 -1
bneqz factorial
bnez factorial
end: end: