forked from MightyPork/crsn
cleanup. remove conditional jumps, replaced by condition embedded in the enum
This commit is contained in:
@@ -0,0 +1 @@
|
||||
Files in this folder were developed for crsn but are not currently used by core or any extension.
|
||||
@@ -26,19 +26,19 @@ impl Display for Register {
|
||||
pub fn parse_reg(name: &str) -> anyhow::Result<Register> {
|
||||
// TODO deduplicate code
|
||||
if let Some(rn) = name.strip_prefix("arg") {
|
||||
if rn.chars().find(|c : &char| !c.is_ascii_digit()).is_some() {
|
||||
if rn.chars().find(|c: &char| !c.is_ascii_digit()).is_some() {
|
||||
Err(CrsnError::Parse(format!("Bad register: {}", name).into()))?;
|
||||
}
|
||||
let val: u8 = rn.parse()?;
|
||||
Ok(Register::Arg(val))
|
||||
} else if let Some(rn) = name.strip_prefix("res") {
|
||||
if rn.chars().find(|c : &char| !c.is_ascii_digit()).is_some() {
|
||||
if rn.chars().find(|c: &char| !c.is_ascii_digit()).is_some() {
|
||||
Err(CrsnError::Parse(format!("Bad register: {}", name).into()))?;
|
||||
}
|
||||
let val: u8 = rn.parse()?;
|
||||
Ok(Register::Res(val))
|
||||
} else if let Some(rn) = name.strip_prefix("r") {
|
||||
if rn.chars().find(|c : &char| !c.is_ascii_digit()).is_some() {
|
||||
if rn.chars().find(|c: &char| !c.is_ascii_digit()).is_some() {
|
||||
Err(CrsnError::Parse(format!("Bad register: {}", name).into()))?;
|
||||
}
|
||||
let val: u8 = rn.parse()?;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use std::borrow::Cow;
|
||||
use std::num::ParseIntError;
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::asm::data::{Mask, Register};
|
||||
use crate::asm::data::literal::Label;
|
||||
use crate::asm::instr::Cond;
|
||||
use std::num::ParseIntError;
|
||||
|
||||
/// csn_asm unified error type
|
||||
#[derive(Error, Debug)]
|
||||
|
||||
@@ -4,9 +4,10 @@ use std::sync::atomic::{AtomicU32, Ordering};
|
||||
use crate::asm::data::{Rd, RdData};
|
||||
use crate::asm::data::literal::{Label, Value};
|
||||
use crate::asm::error::{AsmError, CrsnError};
|
||||
use crate::asm::instr::{Cond, Instr, Op, Routine};
|
||||
use crate::builtin::defs::BuiltinOp;
|
||||
use crate::asm::instr::{Cond, InstrWithBranches, Op, Routine};
|
||||
use crate::asm::instr::op::OpKind;
|
||||
use crate::builtin::defs::Barrier;
|
||||
use crate::builtin::defs::BuiltinOp;
|
||||
|
||||
/// A trait for something that can turn into multiple instructions
|
||||
pub trait Flatten {
|
||||
@@ -19,7 +20,7 @@ impl Flatten for () {
|
||||
}
|
||||
}
|
||||
|
||||
impl Flatten for Instr {
|
||||
impl Flatten for InstrWithBranches {
|
||||
fn flatten(self: Box<Self>, label_num: &AtomicU32) -> Result<Vec<Op>, CrsnError> {
|
||||
let mut ops = vec![self.op];
|
||||
|
||||
@@ -37,8 +38,21 @@ impl Flatten for Instr {
|
||||
} else {
|
||||
Label::unique(label_num)
|
||||
};
|
||||
ops.push(BuiltinOp::JumpIf(!cond, next_lbl.clone()).into());
|
||||
ops.extend(branch.flatten(label_num)?);
|
||||
|
||||
let mut flattened = branch.flatten(label_num)?;
|
||||
|
||||
if flattened.len() == 0 {
|
||||
ops.push(Op { cond: Some(cond), kind: BuiltinOp::Jump(end_lbl.clone()).into() });
|
||||
} else if flattened.len() == 1 && flattened[0].cond.is_none() && branch_count == 1 {
|
||||
// optimization for single-branch conditionals with a single instruction
|
||||
ops.push(Op { cond: Some(cond), kind: flattened.remove(0).kind });
|
||||
} else {
|
||||
ops.push(Op {
|
||||
kind: OpKind::BuiltIn(BuiltinOp::Jump(next_lbl.clone())),
|
||||
cond: Some(!cond),
|
||||
});
|
||||
ops.extend(flattened);
|
||||
}
|
||||
|
||||
if cnt != branch_count - 1 {
|
||||
ops.push(BuiltinOp::Jump(end_lbl.clone()).into());
|
||||
@@ -64,14 +78,13 @@ impl Flatten for Vec<Box<dyn Flatten>> {
|
||||
|
||||
impl Flatten for Routine {
|
||||
fn flatten(self: Box<Self>, label_num: &AtomicU32) -> Result<Vec<Op>, CrsnError> {
|
||||
let skip_label = Label::Numbered(label_num.fetch_add(1, Ordering::Relaxed));
|
||||
let skip_label = Label::unique(label_num);
|
||||
|
||||
let mut ops : Vec<Op> = vec![
|
||||
let mut ops: Vec<Op> = vec![
|
||||
BuiltinOp::Barrier {
|
||||
kind: Barrier::Open(skip_label.clone()),
|
||||
msg: Some(format!("proc {} start", self.name).into())
|
||||
msg: Some(format!("proc {} start", self.name).into()),
|
||||
}.into(),
|
||||
|
||||
BuiltinOp::Routine(self.name.clone()).into(),
|
||||
];
|
||||
|
||||
@@ -80,7 +93,7 @@ impl Flatten for Routine {
|
||||
ops.push(
|
||||
BuiltinOp::Barrier {
|
||||
kind: Barrier::Close(skip_label.clone()),
|
||||
msg: Some(format!("proc {} end", self.name).into())
|
||||
msg: Some(format!("proc {} end", self.name).into()),
|
||||
}.into()
|
||||
);
|
||||
|
||||
@@ -92,7 +105,7 @@ impl Flatten for Routine {
|
||||
pub fn labels_to_skips(ops: Vec<Op>) -> Result<Vec<Op>, CrsnError> {
|
||||
let mut label_positions = HashMap::<Label, usize>::new();
|
||||
for (n, op) in ops.iter().enumerate() {
|
||||
if let Op::BuiltIn(BuiltinOp::Label(name)) = op {
|
||||
if let OpKind::BuiltIn(BuiltinOp::Label(name)) = &op.kind {
|
||||
label_positions.insert(name.clone(), n - label_positions.len());
|
||||
}
|
||||
}
|
||||
@@ -100,28 +113,23 @@ pub fn labels_to_skips(ops: Vec<Op>) -> Result<Vec<Op>, CrsnError> {
|
||||
let mut cleaned = vec![];
|
||||
let mut skipped = 0;
|
||||
for (n, op) in ops.into_iter().enumerate() {
|
||||
match op {
|
||||
Op::BuiltIn(BuiltinOp::Label(_)) => {
|
||||
match op.kind {
|
||||
OpKind::BuiltIn(BuiltinOp::Label(_)) => {
|
||||
skipped += 1;
|
||||
}
|
||||
Op::BuiltIn(BuiltinOp::Jump(target)) => {
|
||||
OpKind::BuiltIn(BuiltinOp::Jump(target)) => {
|
||||
if let Some(dest) = label_positions.get(&target) {
|
||||
let skip = *dest as isize - n as isize + skipped;
|
||||
cleaned.push(Op::BuiltIn(BuiltinOp::Skip(Rd::new(RdData::Immediate(skip as Value)))));
|
||||
cleaned.push(Op {
|
||||
cond: op.cond,
|
||||
kind: OpKind::BuiltIn(BuiltinOp::Skip(Rd::new(RdData::Immediate(skip as Value)))),
|
||||
});
|
||||
} else {
|
||||
return Err(CrsnError::Asm(AsmError::LabelNotDefined(target)));
|
||||
}
|
||||
}
|
||||
Op::BuiltIn(BuiltinOp::JumpIf(cond, target)) => {
|
||||
if let Some(dest) = label_positions.get(&target) {
|
||||
let skip = *dest as isize - n as isize + skipped;
|
||||
cleaned.push(Op::BuiltIn(BuiltinOp::SkipIf(cond, Rd::new(RdData::Immediate(skip as Value)))));
|
||||
} else {
|
||||
return Err(CrsnError::Asm(AsmError::LabelNotDefined(target)));
|
||||
}
|
||||
}
|
||||
other => {
|
||||
cleaned.push(other);
|
||||
_ => {
|
||||
cleaned.push(op);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ pub mod cond;
|
||||
mod flatten;
|
||||
|
||||
/// A higher-level instruction
|
||||
pub struct Instr {
|
||||
pub struct InstrWithBranches {
|
||||
pub op: Op,
|
||||
pub branches: Option<Vec<(Cond, Box<dyn Flatten>)>>,
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crate::asm::instr::Cond;
|
||||
use crate::builtin::defs::BuiltinOp;
|
||||
use crate::module::{EvalRes, OpTrait};
|
||||
use crate::runtime::fault::Fault;
|
||||
@@ -7,19 +8,34 @@ use crate::runtime::run_thread::{info::ThreadInfo, state::RunState};
|
||||
|
||||
/// A higher level simple opration
|
||||
#[derive(Debug)]
|
||||
pub enum Op {
|
||||
pub enum OpKind {
|
||||
BuiltIn(BuiltinOp),
|
||||
/// Instruction added by an extension
|
||||
Ext(Box<dyn OpTrait>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Op {
|
||||
pub cond: Option<Cond>,
|
||||
pub kind: OpKind,
|
||||
}
|
||||
|
||||
impl OpTrait for Op {
|
||||
fn execute(&self, ti: &ThreadInfo, state: &mut RunState) -> Result<EvalRes, Fault> {
|
||||
match self {
|
||||
Op::BuiltIn(op) => {
|
||||
if let Some(cond) = self.cond {
|
||||
if !state.test_cond(cond) {
|
||||
return Ok(EvalRes {
|
||||
cycles: 0,
|
||||
advance: 1,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
match &self.kind {
|
||||
OpKind::BuiltIn(op) => {
|
||||
op.execute(ti, state)
|
||||
}
|
||||
Op::Ext(op) => {
|
||||
OpKind::Ext(op) => {
|
||||
op.execute(ti, state)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,11 +24,5 @@ pub fn assemble(source: &str, parsers: Arc<Vec<Box<dyn CrsnExtension>>>) -> Resu
|
||||
|
||||
let ops = parse::parse(source, &pcx)?;
|
||||
|
||||
trace!("--- Compiled program ---");
|
||||
for (n, op) in ops.iter().enumerate() {
|
||||
trace!("{:04} : {:?}", n, op);
|
||||
}
|
||||
trace!("------------------------");
|
||||
|
||||
Ok(Program::new(ops, parsers)?)
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ fn is_valid_identifier(name: &str) -> bool {
|
||||
|
||||
/// Parse register alias
|
||||
pub fn parse_reg_alias(name: Option<Sexp>) -> Result<RegisterAlias, CrsnError> {
|
||||
trace!("parse reg alias: {:?}", name);
|
||||
// trace!("parse reg alias: {:?}", name);
|
||||
|
||||
let name = expect_string_atom(name)?;
|
||||
|
||||
@@ -29,7 +29,7 @@ pub fn parse_reg_alias(name: Option<Sexp>) -> Result<RegisterAlias, CrsnError> {
|
||||
|
||||
/// Parse constant name
|
||||
pub fn parse_constant_name(name: Option<Sexp>) -> Result<ConstantName, CrsnError> {
|
||||
trace!("parse const name: {:?}", name);
|
||||
// trace!("parse const name: {:?}", name);
|
||||
|
||||
let name = expect_string_atom(name)?;
|
||||
|
||||
@@ -42,7 +42,7 @@ pub fn parse_constant_name(name: Option<Sexp>) -> Result<ConstantName, CrsnError
|
||||
|
||||
/// Parse a label
|
||||
pub fn parse_label(name: Option<Sexp>) -> Result<Label, CrsnError> {
|
||||
trace!("parse label: {:?}", name);
|
||||
// trace!("parse label: {:?}", name);
|
||||
|
||||
let name = expect_string_atom(name)?;
|
||||
Ok(Label::Named(name.trim_start_matches(':').into()))
|
||||
@@ -50,7 +50,7 @@ pub fn parse_label(name: Option<Sexp>) -> Result<Label, CrsnError> {
|
||||
|
||||
/// Parse data disposition (address/value, without the read/write restriction)
|
||||
pub fn parse_data_disp(tok: Option<Sexp>, pcx: &ParserContext) -> Result<DataDisp, CrsnError> {
|
||||
trace!("parse data: {:?}", tok);
|
||||
// trace!("parse data: {:?}", tok);
|
||||
|
||||
let tok = if let Some(tok) = tok {
|
||||
tok
|
||||
@@ -103,7 +103,7 @@ pub fn parse_value(tok: Option<Sexp>) -> Result<Value, CrsnError> {
|
||||
return Err(CrsnError::Parse("Expected value token".into()));
|
||||
};
|
||||
|
||||
trace!("parse value: {:?}", tok);
|
||||
// trace!("parse value: {:?}", tok);
|
||||
|
||||
match &tok {
|
||||
Sexp::Atom(Atom::I(val)) => {
|
||||
@@ -120,7 +120,7 @@ pub fn parse_value(tok: Option<Sexp>) -> Result<Value, CrsnError> {
|
||||
|
||||
|
||||
pub fn parse_u64(literal: &str) -> anyhow::Result<u64> {
|
||||
trace!("parse u64 from {}", literal);
|
||||
// trace!("parse u64 from {}", literal);
|
||||
let mut without_underscores = Cow::Borrowed(literal);
|
||||
if without_underscores.contains('_') {
|
||||
without_underscores = without_underscores.replace('_', "").into();
|
||||
@@ -136,7 +136,7 @@ pub fn parse_u64(literal: &str) -> anyhow::Result<u64> {
|
||||
}
|
||||
|
||||
pub fn parse_i64(literal: &str) -> anyhow::Result<i64> {
|
||||
trace!("parse i64 from {}", literal);
|
||||
// trace!("parse i64 from {}", literal);
|
||||
if let Some(_value) = literal.strip_prefix("-") {
|
||||
Ok(-1 * i64::try_from(parse_u64(literal)?)?)
|
||||
} else {
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
use sexp::Sexp;
|
||||
|
||||
use crate::asm::data::literal::RoutineName;
|
||||
use crate::asm::data::Register;
|
||||
use crate::asm::error::CrsnError;
|
||||
use crate::asm::instr::{Flatten, Instr, Routine};
|
||||
use crate::asm::instr::{Flatten, InstrWithBranches};
|
||||
use crate::asm::parse::arg_parser::TokenParser;
|
||||
use crate::asm::parse::parse_cond::parse_cond_branch;
|
||||
use crate::asm::parse::parse_data::parse_reg_alias;
|
||||
use crate::asm::parse::parse_routine::parse_routine;
|
||||
use crate::asm::parse::ParserContext;
|
||||
use crate::asm::parse::sexp_expect::{expect_list, expect_string_atom};
|
||||
use crate::asm::patches::SexpIsA;
|
||||
use crate::module::ParseRes;
|
||||
|
||||
use super::parse_op::parse_op;
|
||||
use crate::asm::parse::parse_routine::parse_routine;
|
||||
|
||||
pub fn parse_instructions(items: impl Iterator<Item=Sexp>, pcx: &ParserContext) -> Result<Box<dyn Flatten>, CrsnError> {
|
||||
let mut parsed = vec![];
|
||||
@@ -66,7 +63,7 @@ pub fn parse_instructions(items: impl Iterator<Item=Sexp>, pcx: &ParserContext)
|
||||
};
|
||||
|
||||
if let Some(op) = parse_op(name.as_str(), arg_tokens)? {
|
||||
parsed.push(Box::new(Instr {
|
||||
parsed.push(Box::new(InstrWithBranches {
|
||||
op,
|
||||
branches,
|
||||
}));
|
||||
|
||||
@@ -1,16 +1,26 @@
|
||||
use crate::asm::error::CrsnError;
|
||||
use crate::asm::instr::cond::parse_cond;
|
||||
use crate::asm::instr::Op;
|
||||
use crate::asm::parse::arg_parser::TokenParser;
|
||||
use crate::builtin::parse::BuiltinOps;
|
||||
use crate::module::ParseRes;
|
||||
|
||||
pub fn parse_op<'a>(keyword: &str, mut arg_tokens: TokenParser<'a>) -> Result<Option<Op>, CrsnError> {
|
||||
pub fn parse_op<'a>(mut keyword: &str, mut arg_tokens: TokenParser<'a>) -> Result<Option<Op>, CrsnError> {
|
||||
// Include built-in instructions
|
||||
let builtins = [BuiltinOps::new()];
|
||||
let mut cond = None;
|
||||
|
||||
if let Some(pos) = keyword.find('.') {
|
||||
cond = Some(parse_cond(&keyword[(pos + 1)..])?);
|
||||
keyword = &keyword[..pos];
|
||||
}
|
||||
|
||||
for p in builtins.iter().chain(arg_tokens.pcx.parsers) {
|
||||
arg_tokens = match p.parse_op(keyword, arg_tokens) {
|
||||
Ok(ParseRes::Parsed(op)) => return Ok(Some(op)),
|
||||
Ok(ParseRes::Parsed(kind)) => return Ok(Some(Op {
|
||||
cond,
|
||||
kind,
|
||||
})),
|
||||
Ok(ParseRes::ParsedNone) => return Ok(None),
|
||||
Ok(ParseRes::Unknown(to_reuse)) => {
|
||||
if to_reuse.parsing_started() {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use sexp::Sexp;
|
||||
use crate::asm::parse::{ParserContext, parse_instructions};
|
||||
use crate::asm::instr::{Flatten, Routine};
|
||||
use crate::asm::error::CrsnError;
|
||||
use crate::asm::parse::sexp_expect::expect_string_atom;
|
||||
use crate::asm::parse::arg_parser::TokenParser;
|
||||
use crate::asm::patches::SexpIsA;
|
||||
use crate::asm::parse::parse_data::parse_reg_alias;
|
||||
|
||||
use crate::asm::data::Register;
|
||||
use crate::asm::data::literal::RoutineName;
|
||||
use crate::asm::error::CrsnError;
|
||||
use crate::asm::instr::{Flatten, Routine};
|
||||
use crate::asm::parse::{parse_instructions, ParserContext};
|
||||
use crate::asm::parse::arg_parser::TokenParser;
|
||||
use crate::asm::parse::parse_data::parse_reg_alias;
|
||||
use crate::asm::parse::sexp_expect::expect_string_atom;
|
||||
use crate::asm::patches::SexpIsA;
|
||||
use crate::builtin::parse::parse_routine_name;
|
||||
|
||||
pub fn parse_routine(mut toki: impl Iterator<Item=Sexp> + Clone, pcx: &ParserContext) -> Result<Box<dyn Flatten>, CrsnError> {
|
||||
|
||||
+14
-10
@@ -1,6 +1,7 @@
|
||||
use crate::asm::data::{Rd, RdObj, Wr};
|
||||
use crate::asm::data::literal::{DebugMsg, Label, RoutineName};
|
||||
use crate::asm::instr::{Cond, Op};
|
||||
use crate::asm::instr::Op;
|
||||
use crate::asm::instr::op::OpKind;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Barrier {
|
||||
@@ -9,7 +10,7 @@ pub enum Barrier {
|
||||
/// Closing counterpart to the Open barrier
|
||||
Close(Label),
|
||||
/// Stand-alone barrier
|
||||
Standalone
|
||||
Standalone,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -26,10 +27,6 @@ pub enum BuiltinOp {
|
||||
Label(Label),
|
||||
/// Jump to a label
|
||||
Jump(Label),
|
||||
/// Jump to a label if a flag is set
|
||||
JumpIf(Cond, Label),
|
||||
/// Far jump to a label if a flag is set
|
||||
FarJumpIf(Cond, Label),
|
||||
/// Mark a far jump target (can be jumped to from another routine).
|
||||
/// This label is preserved in optimized code.
|
||||
FarLabel(Label),
|
||||
@@ -45,12 +42,10 @@ pub enum BuiltinOp {
|
||||
Routine(RoutineName),
|
||||
/// Skip backward or forward. The skip count can be defined by an argument.
|
||||
Skip(Rd),
|
||||
/// Skip if a flag is set
|
||||
SkipIf(Cond, Rd),
|
||||
/// Deny jumps, skips and run across this address, producing a run-time fault.
|
||||
Barrier {
|
||||
kind: Barrier,
|
||||
msg: Option<DebugMsg>
|
||||
msg: Option<DebugMsg>,
|
||||
},
|
||||
/// Generate a run-time fault with a debugger message
|
||||
Fault(Option<DebugMsg>),
|
||||
@@ -67,6 +62,15 @@ pub enum BuiltinOp {
|
||||
|
||||
impl From<BuiltinOp> for Op {
|
||||
fn from(bo: BuiltinOp) -> Self {
|
||||
Op::BuiltIn(bo)
|
||||
Op {
|
||||
kind: bo.into(),
|
||||
cond: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BuiltinOp> for OpKind {
|
||||
fn from(bo: BuiltinOp) -> Self {
|
||||
OpKind::BuiltIn(bo)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::time::Duration;
|
||||
use crate::asm::data::{Rd, RdData};
|
||||
use crate::asm::data::literal::Addr;
|
||||
use crate::asm::instr::Cond;
|
||||
use crate::builtin::defs::{BuiltinOp, Barrier};
|
||||
use crate::builtin::defs::{Barrier, BuiltinOp};
|
||||
use crate::module::{EvalRes, OpTrait};
|
||||
use crate::runtime::fault::Fault;
|
||||
use crate::runtime::frame::StackFrame;
|
||||
@@ -59,41 +59,8 @@ impl OpTrait for BuiltinOp {
|
||||
}
|
||||
}
|
||||
}
|
||||
BuiltinOp::Jump(name) => {
|
||||
unimplemented!()
|
||||
// match program.find_local_label(state.get_pc(), name) {
|
||||
// Ok(pos) => {
|
||||
// state.set_pc(pos);
|
||||
// }
|
||||
// Err(e) => {
|
||||
// return Err(e);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
BuiltinOp::JumpIf(cond, name) => {
|
||||
unimplemented!()
|
||||
// if state.test_cond(*cond) {
|
||||
// match program.find_local_label(state.get_pc(), name) {
|
||||
// Ok(pos) => {
|
||||
// state.set_pc(pos);
|
||||
// }
|
||||
// Err(e) => {
|
||||
// return Err(e);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
BuiltinOp::FarJumpIf(cond, name) => {
|
||||
if state.test_cond(*cond) {
|
||||
match program.find_far_label(name) {
|
||||
Ok(pos) => {
|
||||
state.set_pc(pos);
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
BuiltinOp::Jump(_name) => {
|
||||
panic!("jump not translated to skip by assembler!");
|
||||
}
|
||||
BuiltinOp::Call(name, args) => {
|
||||
match program.find_routine(&name) {
|
||||
@@ -134,15 +101,6 @@ impl OpTrait for BuiltinOp {
|
||||
let pc = state.get_pc();
|
||||
program.validate_jump(pc, Addr((pc.0 as i64 + res.advance) as u64))?;
|
||||
}
|
||||
BuiltinOp::SkipIf(cond, val) => {
|
||||
if state.test_cond(*cond) {
|
||||
debug!("Skipping");
|
||||
let steps = state.read(*val)?;
|
||||
res.advance = i64::from_ne_bytes(steps.to_ne_bytes());
|
||||
let pc = state.get_pc();
|
||||
program.validate_jump(pc, Addr((pc.0 as i64 + res.advance) as u64))?;
|
||||
}
|
||||
}
|
||||
BuiltinOp::Move { dst, src } => {
|
||||
state.clear_status();
|
||||
let val = state.read(*src)?;
|
||||
|
||||
@@ -3,12 +3,11 @@ use sexp::{Atom, Sexp};
|
||||
use crate::asm::data::literal::{Label, RoutineName};
|
||||
use crate::asm::data::reg::parse_reg;
|
||||
use crate::asm::error::CrsnError;
|
||||
use crate::asm::instr::cond::parse_cond;
|
||||
use crate::asm::instr::Op;
|
||||
use crate::asm::instr::op::OpKind;
|
||||
use crate::asm::parse::arg_parser::TokenParser;
|
||||
use crate::asm::parse::parse_data::{parse_constant_name, parse_label, parse_rd, parse_reg_alias, parse_value};
|
||||
use crate::asm::parse::sexp_expect::expect_string_atom;
|
||||
use crate::builtin::defs::{BuiltinOp, Barrier};
|
||||
use crate::builtin::defs::{Barrier, BuiltinOp};
|
||||
use crate::module::{CrsnExtension, ParseRes};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -29,10 +28,10 @@ impl CrsnExtension for BuiltinOps {
|
||||
"builtin"
|
||||
}
|
||||
|
||||
fn parse_op<'a>(&self, keyword: &str, mut args: TokenParser<'a>) -> Result<ParseRes<'a, Op>, CrsnError> {
|
||||
fn parse_op<'a>(&self, keyword: &str, mut args: TokenParser<'a>) -> Result<ParseRes<'a, OpKind>, CrsnError> {
|
||||
let pcx = args.pcx;
|
||||
|
||||
Ok(ParseRes::Parsed(Op::BuiltIn(match keyword {
|
||||
Ok(ParseRes::Parsed(OpKind::BuiltIn(match keyword {
|
||||
"nop" => {
|
||||
BuiltinOp::Nop
|
||||
}
|
||||
@@ -142,37 +141,17 @@ impl CrsnExtension for BuiltinOps {
|
||||
BuiltinOp::Routine(parse_routine_name(name)?)
|
||||
}
|
||||
|
||||
"sk" => {
|
||||
"skip" => {
|
||||
BuiltinOp::Skip(args.next_rd()?)
|
||||
}
|
||||
|
||||
// TODO add jne-style names for jif ne ...
|
||||
|
||||
"skif" => {
|
||||
let cond = parse_cond(&args.next_string()?)?;
|
||||
let offs = args.next_rd()?;
|
||||
BuiltinOp::SkipIf(cond, offs)
|
||||
}
|
||||
|
||||
"jif" => {
|
||||
let cond = parse_cond(&args.next_string()?)?;
|
||||
let dest = parse_label(args.next())?;
|
||||
BuiltinOp::JumpIf(cond, dest)
|
||||
}
|
||||
|
||||
"fjif" => {
|
||||
let cond = parse_cond(&args.next_string()?)?;
|
||||
let dest = parse_label(args.next())?;
|
||||
BuiltinOp::FarJumpIf(cond, dest)
|
||||
}
|
||||
|
||||
"barrier" => {
|
||||
BuiltinOp::Barrier {
|
||||
kind: Barrier::Standalone,
|
||||
msg: match args.next() {
|
||||
None => None,
|
||||
Some(s) => Some(expect_string_atom(Some(s))?.into()),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,11 +210,11 @@ impl CrsnExtension for BuiltinOps {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn parse_routine_name(name : String) -> Result<RoutineName, CrsnError> {
|
||||
pub(crate) fn parse_routine_name(name: String) -> Result<RoutineName, CrsnError> {
|
||||
let (name, arity) = if let Some(n) = name.find('/') {
|
||||
(
|
||||
(&name[0..n]).to_string(),
|
||||
(&name[(n+1)..]).parse::<u8>()?
|
||||
(&name[(n + 1)..]).parse::<u8>()?
|
||||
)
|
||||
} else {
|
||||
(name, 0)
|
||||
|
||||
@@ -7,7 +7,8 @@ pub use eval_res::EvalRes;
|
||||
use crate::asm::data::literal::Value;
|
||||
use crate::asm::data::Mask;
|
||||
use crate::asm::error::CrsnError;
|
||||
use crate::asm::instr::{Flatten, Op};
|
||||
use crate::asm::instr::Flatten;
|
||||
use crate::asm::instr::op::OpKind;
|
||||
use crate::asm::parse::arg_parser::TokenParser;
|
||||
use crate::runtime::fault::Fault;
|
||||
use crate::runtime::run_thread::state::RunState;
|
||||
@@ -25,10 +26,10 @@ pub enum ParseRes<'a, T> {
|
||||
Unknown(TokenParser<'a>),
|
||||
}
|
||||
|
||||
impl<'a> ParseRes<'a, Op> {
|
||||
impl<'a> ParseRes<'a, OpKind> {
|
||||
/// Helper to construct an extension op
|
||||
pub fn ext(op: impl OpTrait) -> Self {
|
||||
Self::Parsed(Op::Ext(Box::new(op)))
|
||||
Self::Parsed(OpKind::Ext(Box::new(op)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +48,7 @@ pub trait CrsnExtension: Debug + Send + Sync + 'static {
|
||||
/// If the instruction keyword is not recognized, return Unknown with the unchanged argument list.
|
||||
///
|
||||
/// pcx is available on the arg_tokens parser
|
||||
fn parse_op<'a>(&self, keyword: &str, arg_tokens: TokenParser<'a>) -> Result<ParseRes<'a, Op>, CrsnError>;
|
||||
fn parse_op<'a>(&self, keyword: &str, arg_tokens: TokenParser<'a>) -> Result<ParseRes<'a, OpKind>, CrsnError>;
|
||||
|
||||
/// Parse a generic S-expression (non-op)
|
||||
///
|
||||
|
||||
@@ -2,7 +2,7 @@ pub mod run_thread;
|
||||
// pub mod mlock;
|
||||
// pub mod sparse;
|
||||
pub mod fault;
|
||||
pub mod span;
|
||||
// pub mod span;
|
||||
pub mod exec;
|
||||
pub mod frame;
|
||||
pub mod program;
|
||||
|
||||
+13
-14
@@ -3,7 +3,8 @@ use std::sync::Arc;
|
||||
|
||||
use crate::asm::data::literal::{Addr, Label, RoutineName};
|
||||
use crate::asm::instr::Op;
|
||||
use crate::builtin::defs::{BuiltinOp, Barrier};
|
||||
use crate::asm::instr::op::OpKind;
|
||||
use crate::builtin::defs::{Barrier, BuiltinOp};
|
||||
use crate::module::CrsnExtension;
|
||||
use crate::runtime::fault::Fault;
|
||||
|
||||
@@ -12,7 +13,6 @@ pub struct Program {
|
||||
pub ops: Vec<Op>,
|
||||
pub extensions: Arc<Vec<Box<dyn CrsnExtension>>>,
|
||||
routines: HashMap<RoutineName, Addr>,
|
||||
local_labels: HashMap<Label, Addr>,
|
||||
far_labels: HashMap<Label, Addr>,
|
||||
/// Barriers from-to (inclusive).
|
||||
/// Standalone barriers have both addresses the same.
|
||||
@@ -25,7 +25,6 @@ impl Program {
|
||||
ops,
|
||||
extensions,
|
||||
routines: Default::default(),
|
||||
local_labels: Default::default(),
|
||||
far_labels: Default::default(),
|
||||
barriers: Default::default(),
|
||||
};
|
||||
@@ -35,23 +34,23 @@ impl Program {
|
||||
|
||||
/// Find all the named things
|
||||
fn scan(&mut self) -> anyhow::Result<()> {
|
||||
let mut barrier_starts : HashMap<&Label, Addr> = HashMap::new();
|
||||
let mut barrier_starts: HashMap<&Label, Addr> = HashMap::new();
|
||||
for (pos, op) in self.ops.iter().enumerate() {
|
||||
match op {
|
||||
Op::BuiltIn(BuiltinOp::FarLabel(name)) => {
|
||||
match &op.kind {
|
||||
OpKind::BuiltIn(BuiltinOp::FarLabel(name)) => {
|
||||
self.far_labels.insert(name.clone(), pos.into());
|
||||
}
|
||||
Op::BuiltIn(BuiltinOp::Routine(name)) => {
|
||||
OpKind::BuiltIn(BuiltinOp::Routine(name)) => {
|
||||
self.routines.insert(name.clone(), pos.into());
|
||||
}
|
||||
Op::BuiltIn(
|
||||
OpKind::BuiltIn(
|
||||
BuiltinOp::Barrier {
|
||||
kind: Barrier::Open(lbl), ..
|
||||
}
|
||||
) => {
|
||||
barrier_starts.insert(lbl, pos.into());
|
||||
}
|
||||
Op::BuiltIn(
|
||||
OpKind::BuiltIn(
|
||||
BuiltinOp::Barrier {
|
||||
kind: Barrier::Close(lbl),
|
||||
msg,
|
||||
@@ -64,7 +63,7 @@ impl Program {
|
||||
anyhow::bail!("Block barrier \"{:?}\" closed without being open!", msg);
|
||||
}
|
||||
}
|
||||
Op::BuiltIn(
|
||||
OpKind::BuiltIn(
|
||||
BuiltinOp::Barrier {
|
||||
kind: Barrier::Standalone,
|
||||
..
|
||||
@@ -80,7 +79,7 @@ impl Program {
|
||||
anyhow::bail!("Some block barriers open without being closed!");
|
||||
}
|
||||
|
||||
debug!("Program scanned: {:?}", self);
|
||||
trace!("Program scanned: {:?}", self);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -88,7 +87,7 @@ impl Program {
|
||||
/// Read a program instruction at address
|
||||
pub fn read(&self, addr: Addr) -> &Op {
|
||||
if addr.0 >= self.ops.len() as u64 {
|
||||
&Op::BuiltIn(BuiltinOp::Halt)
|
||||
&Op { kind: OpKind::BuiltIn(BuiltinOp::Halt), cond: None }
|
||||
} else {
|
||||
&self.ops[addr.0 as usize]
|
||||
}
|
||||
@@ -104,7 +103,7 @@ impl Program {
|
||||
if b0 != b1 {
|
||||
// block barrier that only partially intersects the jump
|
||||
if (*b0 >= from && *b0 <= to) != (*b1 >= from && *b1 <= to) {
|
||||
if let Op::BuiltIn(BuiltinOp::Barrier { msg, .. }) = self.read(*b0) {
|
||||
if let OpKind::BuiltIn(BuiltinOp::Barrier { msg, .. }) = &self.read(*b0).kind {
|
||||
return Err(Fault::JumpThroughBarrier {
|
||||
msg: msg.clone().unwrap_or("BLOCK BARRIER".into())
|
||||
});
|
||||
@@ -115,7 +114,7 @@ impl Program {
|
||||
} else {
|
||||
// point barrier
|
||||
if *b0 >= from && *b0 <= to {
|
||||
if let Op::BuiltIn(BuiltinOp::Barrier { msg, .. }) = self.read(*b0) {
|
||||
if let OpKind::BuiltIn(BuiltinOp::Barrier { msg, .. }) = &self.read(*b0).kind {
|
||||
return Err(Fault::JumpThroughBarrier {
|
||||
msg: msg.clone().unwrap_or("POINT BARRIER".into())
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crsn::asm::error::CrsnError;
|
||||
use crsn::asm::instr::Op;
|
||||
use crsn::asm::instr::op::OpKind;
|
||||
use crsn::asm::parse::arg_parser::TokenParser;
|
||||
use crsn::module::{CrsnExtension, ParseRes};
|
||||
|
||||
@@ -21,7 +21,7 @@ impl CrsnExtension for ArithOps {
|
||||
"arith"
|
||||
}
|
||||
|
||||
fn parse_op<'a>(&self, keyword: &str, args: TokenParser<'a>) -> Result<ParseRes<'a, Op>, CrsnError> {
|
||||
fn parse_op<'a>(&self, keyword: &str, args: TokenParser<'a>) -> Result<ParseRes<'a, OpKind>, CrsnError> {
|
||||
parse::parse(keyword, args)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use crsn::asm::data::{Rd, Wr};
|
||||
use crsn::asm::error::CrsnError;
|
||||
use crsn::asm::instr::Op;
|
||||
use crsn::asm::instr::op::OpKind;
|
||||
use crsn::asm::parse::arg_parser::TokenParser;
|
||||
use crsn::module::ParseRes;
|
||||
|
||||
use crate::defs::ArithOp;
|
||||
|
||||
pub(crate) fn parse<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result<ParseRes<'a, Op>, CrsnError> {
|
||||
pub(crate) fn parse<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result<ParseRes<'a, OpKind>, CrsnError> {
|
||||
Ok(ParseRes::ext(match keyword {
|
||||
"cmp" => {
|
||||
ArithOp::Compare {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
extern crate log;
|
||||
|
||||
use crsn::asm::error::CrsnError;
|
||||
use crsn::asm::instr::Op;
|
||||
use crsn::asm::instr::op::OpKind;
|
||||
use crsn::asm::parse::arg_parser::TokenParser;
|
||||
use crsn::module::{CrsnExtension, ParseRes};
|
||||
|
||||
@@ -24,7 +24,7 @@ impl CrsnExtension for ScreenOps {
|
||||
"screen"
|
||||
}
|
||||
|
||||
fn parse_op<'a>(&self, keyword: &str, args: TokenParser<'a>) -> Result<ParseRes<'a, Op>, CrsnError> {
|
||||
fn parse_op<'a>(&self, keyword: &str, args: TokenParser<'a>) -> Result<ParseRes<'a, OpKind>, CrsnError> {
|
||||
parse::parse(keyword, args)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use crsn::asm::data::Rd;
|
||||
use crsn::asm::error::CrsnError;
|
||||
use crsn::asm::instr::Op;
|
||||
use crsn::asm::instr::op::OpKind;
|
||||
use crsn::asm::parse::arg_parser::TokenParser;
|
||||
use crsn::module::ParseRes;
|
||||
|
||||
use crate::defs::ScreenOp;
|
||||
|
||||
pub(crate) fn parse<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result<ParseRes<'a, Op>, CrsnError> {
|
||||
pub(crate) fn parse<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result<ParseRes<'a, OpKind>, CrsnError> {
|
||||
Ok(ParseRes::ext(match keyword {
|
||||
"sc-init" => {
|
||||
ScreenOp::ScreenInit {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crsn::asm::data::literal::Value;
|
||||
use crsn::asm::error::CrsnError;
|
||||
use crsn::asm::instr::Op;
|
||||
use crsn::asm::instr::op::OpKind;
|
||||
use crsn::asm::parse::arg_parser::TokenParser;
|
||||
use crsn::module::{CrsnExtension, ParseRes};
|
||||
use crsn::runtime::fault::Fault;
|
||||
@@ -24,7 +24,7 @@ impl CrsnExtension for StackOps {
|
||||
"stacks"
|
||||
}
|
||||
|
||||
fn parse_op<'a>(&self, keyword: &str, args: TokenParser<'a>) -> Result<ParseRes<'a, Op>, CrsnError> {
|
||||
fn parse_op<'a>(&self, keyword: &str, args: TokenParser<'a>) -> Result<ParseRes<'a, OpKind>, CrsnError> {
|
||||
parse::parse(keyword, args)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use crsn::asm::error::CrsnError;
|
||||
use crsn::asm::instr::Op;
|
||||
use crsn::asm::instr::op::OpKind;
|
||||
use crsn::asm::parse::arg_parser::TokenParser;
|
||||
use crsn::module::ParseRes;
|
||||
|
||||
use crate::defs::StackOp;
|
||||
|
||||
pub(crate) fn parse<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result<ParseRes<'a, Op>, CrsnError> {
|
||||
pub(crate) fn parse<'a>(keyword: &str, mut args: TokenParser<'a>) -> Result<ParseRes<'a, OpKind>, CrsnError> {
|
||||
Ok(ParseRes::ext(match keyword {
|
||||
"stack" => {
|
||||
StackOp::NewStack {
|
||||
|
||||
+25
-2
@@ -3,6 +3,7 @@ extern crate log;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use clappconfig::{AppConfig, clap};
|
||||
use clappconfig::clap::ArgMatches;
|
||||
@@ -13,7 +14,6 @@ use crsn::runtime::run_thread::{RunThread, ThreadToken};
|
||||
use crsn_arith::ArithOps;
|
||||
use crsn_screen::ScreenOps;
|
||||
use crsn_stacks::StackOps;
|
||||
use std::time::Duration;
|
||||
|
||||
mod read_file;
|
||||
mod serde_duration_millis;
|
||||
@@ -30,7 +30,9 @@ struct Config {
|
||||
log: LogConfig,
|
||||
#[serde(skip)]
|
||||
program_file: String,
|
||||
#[serde(with="serde_duration_millis")]
|
||||
#[serde(skip)]
|
||||
asm_only: bool,
|
||||
#[serde(with = "serde_duration_millis")]
|
||||
cycle_time: Duration,
|
||||
}
|
||||
|
||||
@@ -42,6 +44,7 @@ impl Default for Config {
|
||||
modules: Default::default(),
|
||||
},
|
||||
program_file: "".to_string(),
|
||||
asm_only: false,
|
||||
cycle_time: Duration::default(),
|
||||
}
|
||||
}
|
||||
@@ -69,6 +72,12 @@ impl AppConfig for Config {
|
||||
.required_unless("default-config")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::with_name("asm-only")
|
||||
.short("P")
|
||||
.long("asm")
|
||||
.help("Only assemble, do not run."),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::with_name("cycle")
|
||||
.long("cycle")
|
||||
@@ -95,6 +104,7 @@ impl AppConfig for Config {
|
||||
|
||||
fn configure(mut self, clap: &ArgMatches) -> anyhow::Result<Self> {
|
||||
self.program_file = clap.value_of("input").unwrap().to_string();
|
||||
self.asm_only = clap.is_present("asm-only");
|
||||
if let Some(c) = clap.value_of("cycle") {
|
||||
self.cycle_time = Duration::from_millis(c.parse().unwrap());
|
||||
}
|
||||
@@ -118,6 +128,19 @@ fn main() -> anyhow::Result<()> {
|
||||
|
||||
let parsed = crsn::asm::assemble(&source, parsers)?;
|
||||
|
||||
if config.asm_only {
|
||||
for (n, op) in parsed.ops.iter().enumerate() {
|
||||
println!("{:04} : {:?}", n, op);
|
||||
}
|
||||
return Ok(());
|
||||
} else {
|
||||
trace!("--- Compiled program ---");
|
||||
for (n, op) in parsed.ops.iter().enumerate() {
|
||||
trace!("{:04} : {:?}", n, op);
|
||||
}
|
||||
trace!("------------------------");
|
||||
}
|
||||
|
||||
info!("Start runtime");
|
||||
|
||||
let args = &[];
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use serde::{self, Deserialize, Deserializer, Serializer};
|
||||
use std::time::Duration;
|
||||
|
||||
use serde::{self, Deserialize, Deserializer, Serializer};
|
||||
|
||||
pub fn serialize<S>(value: &Duration, se: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
|
||||
Reference in New Issue
Block a user