diff --git a/README.md b/README.md index 2b4163a..9670641 100644 --- a/README.md +++ b/README.md @@ -41,9 +41,12 @@ The runtime is built as a register machine with a stack and status flags. ## Registers -- 8 general purpose registers `r0`-`r7` -- 8 argument registers `arg0`-`arg7` -- 8 result registers `res0`-`res7` +- 16 general purpose registers `r0`-`r15` +- 16 argument registers `arg0`-`arg15` +- 16 result registers `res0`-`res15` +- 16 global registers `g0`-`g15` + +Global registers are accessible everywhere. Other registers are only valid within an execution frame (in a routine, or the initial scope). All registers are 64-bit unsigned integers that can be treated as signed, if you want to. Overflow is allowed and reported by status flags. @@ -336,7 +339,9 @@ Jumping to a label is always safer than a manual skip. ; Load status flags from a register (ldf Rd) -; Define a register alias. The alias is only valid in the current routine or in the root of the program. +; Define a register alias. +; The alias is only valid in the current routine or in the root of the program. +; However, if the register is a global register, then the alias is valid everywhere. (sym SYM REG) ; Define a constant. These are valid in the whole program. diff --git a/crsn/src/asm/data/reg.rs b/crsn/src/asm/data/reg.rs index a43acba..050c1df 100644 --- a/crsn/src/asm/data/reg.rs +++ b/crsn/src/asm/data/reg.rs @@ -14,6 +14,17 @@ pub enum Register { Res(u8), /// General purpose register Gen(u8), + /// Global register + Global(u8), +} + +impl Register { + pub fn is_global(self) -> bool { + match self { + Register::Global(_) => true, + _ => false, + } + } } impl Display for Register { @@ -22,6 +33,7 @@ impl Display for Register { Register::Arg(n) => write!(f, "arg{}", n), Register::Res(n) => write!(f, "res{}", n), Register::Gen(n) => write!(f, "r{}", n), + Register::Global(n) => write!(f, "g{}", n), } } } @@ -46,6 +58,12 @@ pub fn parse_reg(name: &str, at: &SourcePosition) -> Result } let val: u8 = rn.parse().err_pos(at)?; Ok(Register::Gen(val)) + } else if let Some(rn) = name.strip_prefix("g") { + if rn.chars().find(|c: &char| !c.is_ascii_digit()).is_some() { + return Err(CrsnError::Parse(format!("Bad register: {}", name).into(), at.clone()))?; + } + let val: u8 = rn.parse().err_pos(at)?; + Ok(Register::Global(val)) } else { Err(CrsnError::Parse(format!("Bad reg name: {}", name).into(), at.clone()))? } diff --git a/crsn/src/asm/mod.rs b/crsn/src/asm/mod.rs index 48d998b..6798920 100644 --- a/crsn/src/asm/mod.rs +++ b/crsn/src/asm/mod.rs @@ -39,6 +39,7 @@ pub fn assemble(source: &str, uniq : &CrsnUniq, mut parsers: Vec { #[derive(Default, Debug)] pub struct ParserState { + /// Register aliases valid globally + pub global_reg_aliases: HashMap, + /// Register aliases within the routine pub reg_aliases: HashMap, diff --git a/crsn/src/asm/parse/parse_data.rs b/crsn/src/asm/parse/parse_data.rs index ac2a7f3..dbc3ffe 100644 --- a/crsn/src/asm/parse/parse_data.rs +++ b/crsn/src/asm/parse/parse_data.rs @@ -110,12 +110,16 @@ pub fn parse_data_disp(tok: Sexp, pcx: &ParserContext) -> Result Result Result(op_pos: &SourcePosition, keyword: &str, mut args: Tok let mut pstate = pcx.state.borrow_mut(); if pstate.reg_aliases.contains_key(&alias) { return Err(CrsnError::Parse(format!("Register alias \"{}\" already defined!", alias).into(), rpos)); - } - - if pstate.constants.contains_key(&alias) { + } else if pstate.global_reg_aliases.contains_key(&alias) { + return Err(CrsnError::Parse(format!("Register alias \"{}\" already defined (global)!", alias).into(), rpos)); + } else if pstate.constants.contains_key(&alias) { return Err(CrsnError::Parse(format!("Name \"{}\" already used for a constant!", alias).into(), aliaspos)); + } else if let Some((name, _)) = pstate.reg_aliases.iter().find(|x| x.1 == ®ister) { + return Err(CrsnError::Parse(format!("Register \"{}\" already aliased as \"{}\"!", register, name).into(), rpos)); + } else if let Some((name, _)) = pstate.global_reg_aliases.iter().find(|x| x.1 == ®ister) { + return Err(CrsnError::Parse(format!("Register \"{}\" already aliased (globally) as \"{}\"!", register, name).into(), rpos)); } - if pstate.reg_aliases.iter().find(|x| x.1 == ®ister).is_some() { - return Err(CrsnError::Parse(format!("Register \"{}\" already aliased!", register).into(), rpos)); + if register.is_global() { + pstate.global_reg_aliases.insert(alias, register); + } else { + pstate.reg_aliases.insert(alias, register); } - pstate.reg_aliases.insert(alias, register); - return Ok(ParseRes::ParsedNone); } @@ -75,7 +79,9 @@ pub(crate) fn parse_op<'a>(op_pos: &SourcePosition, keyword: &str, mut args: Tok let mut pstate = pcx.state.borrow_mut(); if pstate.reg_aliases.remove(&alias.0).is_none() { - return Err(CrsnError::Parse(format!("Register alias \"{}\" not defined!", alias.0).into(), alias.1)); + if pstate.global_reg_aliases.remove(&alias.0).is_none() { + return Err(CrsnError::Parse(format!("Register alias \"{}\" not defined!", alias.0).into(), alias.1)); + } } return Ok(ParseRes::ParsedNone); } @@ -87,10 +93,10 @@ pub(crate) fn parse_op<'a>(op_pos: &SourcePosition, keyword: &str, mut args: Tok let mut pstate = pcx.state.borrow_mut(); if pstate.constants.contains_key(&name) { return Err(CrsnError::Parse(format!("Constant \"{}\" already defined!", name).into(), namepos)); - } - - if pstate.reg_aliases.contains_key(&name) { + } else if pstate.reg_aliases.contains_key(&name) { return Err(CrsnError::Parse(format!("Name \"{}\" already used for a register alias!", name).into(), namepos)); + } else if pstate.global_reg_aliases.contains_key(&name) { + return Err(CrsnError::Parse(format!("Name \"{}\" already used for a global register alias!", name).into(), namepos)); } pstate.constants.insert(name, value); diff --git a/crsn/src/runtime/frame.rs b/crsn/src/runtime/frame.rs index 950b682..b71e79f 100644 --- a/crsn/src/runtime/frame.rs +++ b/crsn/src/runtime/frame.rs @@ -4,7 +4,7 @@ use crate::asm::data::literal::{Addr, Value}; pub(crate) mod status; -pub const REG_COUNT: usize = 8; +pub const REG_COUNT: usize = 16; pub type CallStack = Vec; diff --git a/crsn/src/runtime/run_thread.rs b/crsn/src/runtime/run_thread.rs index baecf59..f34a946 100644 --- a/crsn/src/runtime/run_thread.rs +++ b/crsn/src/runtime/run_thread.rs @@ -9,7 +9,7 @@ pub use state::RunState; use crate::asm::data::literal::Addr; use crate::module::{EvalRes, CrsnUniq}; use crate::runtime::fault::Fault; -use crate::runtime::frame::StackFrame; +use crate::runtime::frame::{StackFrame, REG_COUNT}; use crate::runtime::program::Program; #[derive(Clone, Copy, Eq, PartialEq, Debug, Ord, PartialOrd)] @@ -48,6 +48,7 @@ impl RunThread { thread_info: ti.clone(), frame: StackFrame::new(params.pc, params.args), call_stack: vec![], + global_regs: [0; REG_COUNT], ext_data: Default::default(), }; diff --git a/crsn/src/runtime/run_thread/state.rs b/crsn/src/runtime/run_thread/state.rs index ca29bfc..53be796 100644 --- a/crsn/src/runtime/run_thread/state.rs +++ b/crsn/src/runtime/run_thread/state.rs @@ -17,6 +17,8 @@ pub struct RunState { pub frame: StackFrame, /// Call stack pub call_stack: CallStack, + /// General purpose registers that stay valid for the entire run-time of the thread + pub global_regs: [Value; REG_COUNT], /// Extension data pub ext_data: ExtensionDataStore, } @@ -95,6 +97,7 @@ impl RunState { /// Read a `Rd` value pub fn read(&mut self, rd: impl Into) -> Result { let rd = rd.into(); + // TODO dedupe match rd.0 { RdData::Register(Register::Gen(rn)) => { if likely(rn < REG_COUNT as u8) { @@ -104,6 +107,14 @@ impl RunState { Err(Fault::RegisterNotExist { reg: Register::Gen(rn) }) } } + RdData::Register(Register::Global(rn)) => { + if likely(rn < REG_COUNT as u8) { + trace!("Rd {:?} = {}", rd, self.global_regs[rn as usize]); + Ok(self.global_regs[rn as usize]) + } else { + Err(Fault::RegisterNotExist { reg: Register::Global(rn) }) + } + } RdData::Immediate(v) => Ok(v), RdData::Register(Register::Arg(rn)) => { if likely(rn < REG_COUNT as u8) { @@ -167,6 +178,7 @@ impl RunState { trace!("WR {:?} := {}", wr, val); + // TODO dedupe match wr.0 { WrData::Register(Register::Gen(rn)) => { if likely(rn < REG_COUNT as u8) { @@ -176,6 +188,14 @@ impl RunState { Err(Fault::RegisterNotExist { reg: Register::Gen(rn) }) } } + WrData::Register(Register::Global(rn)) => { + if likely(rn < REG_COUNT as u8) { + self.global_regs[rn as usize] = val; + Ok(()) + } else { + Err(Fault::RegisterNotExist { reg: Register::Global(rn) }) + } + } WrData::Register(Register::Arg(rn)) => { if likely(rn < REG_COUNT as u8) { Err(Fault::RegisterNotWritable { reg: Register::Res(rn) }) diff --git a/examples/globals.csn b/examples/globals.csn new file mode 100644 index 0000000..fb54c19 --- /dev/null +++ b/examples/globals.csn @@ -0,0 +1,11 @@ +( + (ld g0 15) + (call add_cat) + (cmp g0 16 + (ne? (fault))) + + (proc add_cat + (add g0 1) + (ret) + ) +) diff --git a/examples/globals_sym.csn b/examples/globals_sym.csn new file mode 100644 index 0000000..efde1d5 --- /dev/null +++ b/examples/globals_sym.csn @@ -0,0 +1,12 @@ +( + (sym cats g0) + (ld cats 15) + (call add_cat) + (cmp cats 16 + (ne? (fault))) + + (proc add_cat + (add cats 1) + (ret) + ) +)