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