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