parent
							
								
									1d444fd516
								
							
						
					
					
						commit
						3e0aaa71e9
					
				@ -0,0 +1,50 @@ | 
				
			||||
use crate::asm::data::literal::{Label, RoutineName, DebugMsg}; | 
				
			||||
use crate::asm::instr::{Cond, Op}; | 
				
			||||
use crate::asm::data::{Rd, Wr}; | 
				
			||||
 | 
				
			||||
#[derive(Debug)] | 
				
			||||
pub enum BuiltinOp { | 
				
			||||
    /// Do nothing
 | 
				
			||||
    Nop, | 
				
			||||
    /// Mark a jump target.
 | 
				
			||||
    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), | 
				
			||||
    /// Jump to a label that can be in another function
 | 
				
			||||
    FarJump(Label), | 
				
			||||
    /// Call a routine with arguments.
 | 
				
			||||
    /// The arguments are passed as argX. Return values are stored in resX registers.
 | 
				
			||||
    Call(RoutineName, Vec<Rd>), | 
				
			||||
    /// Exit the current routine with return values
 | 
				
			||||
    Ret(Vec<Rd>), | 
				
			||||
    /// Mark a routine entry point (call target).
 | 
				
			||||
    /// Kept in the low level instruction file for position-independent code
 | 
				
			||||
    Routine(RoutineName), | 
				
			||||
    /// Skip backward or forward
 | 
				
			||||
    Skip(Rd), | 
				
			||||
    /// Skip if a flag is set
 | 
				
			||||
    SkipIf(Cond, Rd), | 
				
			||||
    /// Deny jumps, skips and run across this address, producing a run-time fault with a message.
 | 
				
			||||
    Barrier(Option<DebugMsg>), | 
				
			||||
    /// Generate a run-time fault with a debugger message
 | 
				
			||||
    Fault(Option<DebugMsg>), | 
				
			||||
    /// Copy value
 | 
				
			||||
    Move { dst: Wr, src: Rd }, | 
				
			||||
    /// Store runtime status to a register
 | 
				
			||||
    StoreStatus { dst: Wr }, | 
				
			||||
    /// Load runtime status from a register
 | 
				
			||||
    LoadStatus { src: Rd }, | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl From<BuiltinOp> for Op { | 
				
			||||
    fn from(bo: BuiltinOp) -> Self { | 
				
			||||
        Op::BuiltIn(bo) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
@ -0,0 +1,141 @@ | 
				
			||||
use std::ops::Rem; | 
				
			||||
 | 
				
			||||
use num_traits::PrimInt; | 
				
			||||
 | 
				
			||||
use crate::asm::instr::op::{OpTrait, EvalRes}; | 
				
			||||
use crate::runtime::fault::Fault; | 
				
			||||
use crate::runtime::frame::{CallStack, StackFrame}; | 
				
			||||
use crate::runtime::program::Program; | 
				
			||||
use crate::builtin::defs::BuiltinOp; | 
				
			||||
use crate::asm::data::literal::Addr; | 
				
			||||
 | 
				
			||||
impl OpTrait for BuiltinOp { | 
				
			||||
    fn execute(&self, program: &Program, call_stack: &mut CallStack, frame: &mut StackFrame) -> Result<EvalRes, Fault> { | 
				
			||||
        let mut res = EvalRes::default(); | 
				
			||||
        match self { | 
				
			||||
            BuiltinOp::Nop => {} | 
				
			||||
            BuiltinOp::Label(_) | BuiltinOp::FarLabel(_) | BuiltinOp::Routine(_) => { | 
				
			||||
                /* this is nop, but without any cost - just markers */ | 
				
			||||
                res.cycles = 0; | 
				
			||||
            } | 
				
			||||
            BuiltinOp::Barrier(msg) => { | 
				
			||||
                return Err(Fault::Barrier { | 
				
			||||
                    msg: msg.clone().unwrap_or_else(|| "BARRIER".into()) | 
				
			||||
                }); | 
				
			||||
            } | 
				
			||||
            BuiltinOp::Fault(msg) => { | 
				
			||||
                return Err(Fault::FaultInstr { | 
				
			||||
                    msg: msg.clone().unwrap_or_else(|| "FAULT".into()) | 
				
			||||
                }); | 
				
			||||
            } | 
				
			||||
            BuiltinOp::FarJump(name) => { | 
				
			||||
                match program.find_far_label(name) { | 
				
			||||
                    Ok(pos) => { | 
				
			||||
                        frame.pc = pos; | 
				
			||||
                    } | 
				
			||||
                    Err(e) => { | 
				
			||||
                        return Err(e); | 
				
			||||
                    } | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
            BuiltinOp::Jump(name) => { | 
				
			||||
                match program.find_local_label(frame.pc, name) { | 
				
			||||
                    Ok(pos) => { | 
				
			||||
                        frame.pc = pos; | 
				
			||||
                    } | 
				
			||||
                    Err(e) => { | 
				
			||||
                        return Err(e); | 
				
			||||
                    } | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
            BuiltinOp::JumpIf(cond, name) => { | 
				
			||||
                if frame.status.test(*cond) { | 
				
			||||
                    match program.find_local_label(frame.pc, name) { | 
				
			||||
                        Ok(pos) => { | 
				
			||||
                            frame.pc = pos; | 
				
			||||
                        } | 
				
			||||
                        Err(e) => { | 
				
			||||
                            return Err(e); | 
				
			||||
                        } | 
				
			||||
                    } | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
            BuiltinOp::FarJumpIf(cond, name) => { | 
				
			||||
                if frame.status.test(*cond) { | 
				
			||||
                    match program.find_far_label(name) { | 
				
			||||
                        Ok(pos) => { | 
				
			||||
                            frame.pc = pos; | 
				
			||||
                        } | 
				
			||||
                        Err(e) => { | 
				
			||||
                            return Err(e); | 
				
			||||
                        } | 
				
			||||
                    } | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
            BuiltinOp::Call(name, args) => { | 
				
			||||
                match program.find_routine(&name) { | 
				
			||||
                    Ok(pos) => { | 
				
			||||
                        let mut values = Vec::with_capacity(args.len()); | 
				
			||||
                        for arg in args { | 
				
			||||
                            values.push(frame.read(*arg)?); | 
				
			||||
                        } | 
				
			||||
 | 
				
			||||
                        let mut frame2 = StackFrame::new(pos, &values); | 
				
			||||
                        std::mem::swap(frame, &mut frame2); | 
				
			||||
                        call_stack.push(frame2); | 
				
			||||
                        res.advance = 0; | 
				
			||||
                    } | 
				
			||||
                    Err(e) => { | 
				
			||||
                        return Err(e); | 
				
			||||
                    } | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
            BuiltinOp::Ret(retvals) => { | 
				
			||||
                match call_stack.pop() { | 
				
			||||
                    Some(previous) => { | 
				
			||||
                        let mut values = Vec::with_capacity(retvals.len()); | 
				
			||||
                        for arg in retvals { | 
				
			||||
                            values.push(frame.read(*arg)?); | 
				
			||||
                        } | 
				
			||||
                        *frame = previous; | 
				
			||||
                        frame.set_retvals(&values); | 
				
			||||
                    } | 
				
			||||
                    None => { | 
				
			||||
                        return Err(Fault::CallStackUnderflow); | 
				
			||||
                    } | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
            BuiltinOp::Skip(val) => { | 
				
			||||
                let steps = frame.read(*val)?; | 
				
			||||
                res.advance = i64::from_ne_bytes(steps.to_ne_bytes()); | 
				
			||||
                program.validate_jump(frame.pc, Addr((frame.pc.0 as i64 + res.advance) as u64))?; | 
				
			||||
            } | 
				
			||||
            BuiltinOp::SkipIf(cond, val) => { | 
				
			||||
                if frame.status.test(*cond) { | 
				
			||||
                    debug!("Skipping"); | 
				
			||||
                    let steps = frame.read(*val)?; | 
				
			||||
                    res.advance = i64::from_ne_bytes(steps.to_ne_bytes()); | 
				
			||||
                    program.validate_jump(frame.pc, Addr((frame.pc.0 as i64 + res.advance) as u64))?; | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
            BuiltinOp::Move { dst, src } => { | 
				
			||||
                frame.status.clear(); | 
				
			||||
                let val = frame.read(*src)?; | 
				
			||||
                frame.status.update(val); | 
				
			||||
                frame.write(*dst, val)?; | 
				
			||||
            } | 
				
			||||
            BuiltinOp::StoreStatus { dst } => { | 
				
			||||
                let packed = frame.status.store(); | 
				
			||||
                frame.write(*dst, packed)?; | 
				
			||||
            } | 
				
			||||
            BuiltinOp::LoadStatus { src } => { | 
				
			||||
                let x = frame.read(*src)?; | 
				
			||||
                frame.status.load(x); | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        Ok(res) | 
				
			||||
    } | 
				
			||||
    //
 | 
				
			||||
} | 
				
			||||
 | 
				
			||||
@ -0,0 +1,3 @@ | 
				
			||||
pub mod defs; | 
				
			||||
pub mod exec; | 
				
			||||
pub mod parse; | 
				
			||||
@ -0,0 +1,132 @@ | 
				
			||||
use crate::asm::data::{Rd, Wr}; | 
				
			||||
use crate::asm::error::{Error}; | 
				
			||||
use crate::asm::instr::op::{AsmModule, ParseOpResult}; | 
				
			||||
use crate::asm::parse::arg_parser::ArgParser; | 
				
			||||
use crate::asm::data::literal::{Label, RoutineName}; | 
				
			||||
use crate::asm::parse::sexp_expect::expect_string_atom; | 
				
			||||
use crate::asm::parse::parse_data::{parse_label, parse_rd}; | 
				
			||||
use crate::asm::instr::cond::parse_cond; | 
				
			||||
use crate::asm::instr::Op; | 
				
			||||
use crate::builtin::defs::BuiltinOp; | 
				
			||||
use sexp::{Sexp, Atom}; | 
				
			||||
 | 
				
			||||
#[derive(Debug, Clone)] | 
				
			||||
pub struct BuiltinOpParser { | 
				
			||||
    _internal: () | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl BuiltinOpParser { | 
				
			||||
    pub fn new() -> Box<dyn AsmModule> { | 
				
			||||
        Box::new(Self { | 
				
			||||
            _internal: () | 
				
			||||
        }) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
 | 
				
			||||
impl AsmModule for BuiltinOpParser { | 
				
			||||
    fn name(&self) -> &'static str { | 
				
			||||
        "builtin" | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    fn parse_op(&self, keyword: &str, mut args: ArgParser) -> Result<ParseOpResult, Error> { | 
				
			||||
        Ok(ParseOpResult::Parsed(Op::BuiltIn(match keyword { | 
				
			||||
            "j" => { | 
				
			||||
                let dest = parse_label(args.next())?; | 
				
			||||
                BuiltinOp::Jump(dest) | 
				
			||||
            } | 
				
			||||
 | 
				
			||||
            "fj" => { | 
				
			||||
                let dest = parse_label(args.next())?; | 
				
			||||
                BuiltinOp::FarJump(dest) | 
				
			||||
            } | 
				
			||||
 | 
				
			||||
            "call" => { | 
				
			||||
                let dest = RoutineName(args.next_string()?); | 
				
			||||
 | 
				
			||||
                let mut call_args = vec![]; | 
				
			||||
                for t in args { | 
				
			||||
                    call_args.push(parse_rd(Some(t))?); | 
				
			||||
                } | 
				
			||||
                BuiltinOp::Call(dest, call_args) | 
				
			||||
            } | 
				
			||||
 | 
				
			||||
            "ret" => { | 
				
			||||
                let mut ret_vals = vec![]; | 
				
			||||
                for t in args { | 
				
			||||
                    ret_vals.push(parse_rd(Some(t))?); | 
				
			||||
                } | 
				
			||||
                BuiltinOp::Ret(ret_vals) | 
				
			||||
            } | 
				
			||||
 | 
				
			||||
            "routine" => { | 
				
			||||
                let dest = RoutineName(args.next_string()?); | 
				
			||||
                BuiltinOp::Routine(dest) | 
				
			||||
            } | 
				
			||||
 | 
				
			||||
            "s" => { | 
				
			||||
                BuiltinOp::Skip(args.next_rd()?) | 
				
			||||
            } | 
				
			||||
 | 
				
			||||
            "sif" => { | 
				
			||||
                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(match args.next() { | 
				
			||||
                    None => None, | 
				
			||||
                    Some(s) => Some(expect_string_atom(Some(s))?.into()), | 
				
			||||
                }) | 
				
			||||
            } | 
				
			||||
 | 
				
			||||
            "fault" => { | 
				
			||||
                BuiltinOp::Fault(match args.next() { | 
				
			||||
                    None => None, | 
				
			||||
                    Some(s) => Some(expect_string_atom(Some(s))?.into()), | 
				
			||||
                }) | 
				
			||||
            } | 
				
			||||
 | 
				
			||||
            "ld" => { | 
				
			||||
                BuiltinOp::Move { | 
				
			||||
                    dst: args.next_wr()?, | 
				
			||||
                    src: args.next_rd()?, | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
 | 
				
			||||
            "far" => { | 
				
			||||
                if let Some(Sexp::Atom(Atom::S(ref label))) = args.peek() { | 
				
			||||
                    if let Some(label) = label.strip_prefix(':') { | 
				
			||||
                        let label = Label::Named(label.to_string()); | 
				
			||||
                        BuiltinOp::FarLabel(label) | 
				
			||||
                    } else { | 
				
			||||
                        return Ok(ParseOpResult::Unknown(args)); | 
				
			||||
                    } | 
				
			||||
                } else { | 
				
			||||
                    return Ok(ParseOpResult::Unknown(args)); | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
 | 
				
			||||
            other => { | 
				
			||||
                if let Some(label) = other.strip_prefix(':') { | 
				
			||||
                    let label = Label::Named(label.to_string()); | 
				
			||||
                    BuiltinOp::Label(label) | 
				
			||||
                } else { | 
				
			||||
                    return Ok(ParseOpResult::Unknown(args)); | 
				
			||||
                } | 
				
			||||
            } | 
				
			||||
        }))) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
					Loading…
					
					
				
		Reference in new issue