use std::time::Duration; use sexp::Sexp; use crate::asm::data::literal::{Addr, Value}; use crate::builtin::defs::{Barrier, BuiltinOp, LdsValue}; use crate::module::{EvalRes, OpTrait}; use crate::runtime::fault::Fault; use crate::runtime::frame::StackFrame; use crate::runtime::run_thread::{state::RunState, ThreadInfo}; use crate::asm::instr::cond::Flag; impl OpTrait for BuiltinOp { fn execute(&self, info: &ThreadInfo, state: &mut RunState) -> Result { let program = &info.program; let mut res = EvalRes::default(); match self { BuiltinOp::Nop => {} BuiltinOp::Halt => { trace!("HALT - stop execution"); return Err(Fault::Halt); } BuiltinOp::Label(_) | BuiltinOp::FarLabel(_) | BuiltinOp::Routine(_) => { /* this is nop, but without any cost - just markers */ res.cycles = 0; } BuiltinOp::Barrier { kind: Barrier::Open(lbl), .. } => { match program.find_far_label(lbl) { Ok(pos) => { res.cycles = 0; state.set_pc(pos); } Err(e) => { return Err(e); } } } BuiltinOp::Barrier { msg, .. } => { return Err(Fault::FaultInstr { 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) => { 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) { Ok(pos) => { let mut values = Vec::with_capacity(args.len()); for arg in args { values.push(state.read(arg)?); } let mut frame2 = StackFrame::new(pos, &values); std::mem::swap(&mut state.frame, &mut frame2); state.call_stack.push(frame2); res.advance = 0; } Err(e) => { return Err(e); } } } BuiltinOp::Ret(retvals) => { match state.call_stack.pop() { Some(previous) => { let mut values = Vec::with_capacity(retvals.len()); for arg in retvals { values.push(state.read(arg)?); } state.frame = previous; state.frame.set_retvals(&values); } None => { return Err(Fault::CallStackUnderflow); } } } BuiltinOp::Skip(val) => { 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::Load { dst, src } => { state.clear_status(); let val = state.read(src)?; state.update_status(val); state.write(dst, val)?; } BuiltinOp::LoadMultiple { dst, src, count } => { state.clear_status(); let mut count = state.read(count)?; let mut last = 0; while count > 0 { last = state.read(src)?; state.write(dst, last)?; count -= 1; } state.update_status(last); } BuiltinOp::LoadBits { dst, a, b, mask: slice, } => { state.clear_status(); let new = state.read(b)?; let old = state.read(a)?; let ones : u64 = (1 << slice.width) - 1; let val = (old & !(ones << slice.src_pos)) | (((new & (ones << slice.src2_pos)) >> slice.src2_pos) << slice.src_pos); state.update_status(val); state.write(dst, val)?; } BuiltinOp::LoadSequence { dst, value } => { match value { LdsValue::Values(vals) => { for val in vals { let v = state.read(val)?; state.write(dst, v)?; } } LdsValue::Chars(string) => { for val in string.chars() { state.write(dst, val as Value)?; } } LdsValue::Handle(h) => { let hv = h.read(state)?; let mut found = false; for ex in info.program.extensions.iter() { if ex.read_obj_all(state, *dst, hv)?.is_some() { found = true; } } if !found { warn!("Object {:#x} to read does not exist!", hv); state.set_flag(Flag::Invalid, true); } } } } BuiltinOp::Exchange { a, b, mask: slice } => { let aa = state.read(a)?; let bb = state.read(b)?; if slice.is_full() { state.write(a, bb)?; state.write(b, aa)?; } else { let ones: u64 = (1 << slice.width) - 1; let aa2 = (aa & !(ones << slice.dst_pos)) | (((bb & (ones << slice.src_pos)) >> slice.src_pos) << slice.dst_pos); let bb2 = (bb & !(ones << slice.src_pos)) | (((aa & (ones << slice.dst_pos)) >> slice.dst_pos) << slice.src_pos); state.write(a, aa2)?; state.write(b, bb2)?; } } BuiltinOp::StoreFlags { dst } => { let packed = state.frame.status.store(); state.write(dst, packed)?; } BuiltinOp::LoadFlags { src } => { let x = state.read(src)?; state.frame.status.load(x); } BuiltinOp::Sleep { count: micros, unit_us } => { std::thread::sleep(Duration::from_micros(state.read(micros)? * unit_us.micros())) } BuiltinOp::Delete(obj) => { let hv = obj.read(state)?; trace!("Del object: {:#x}", hv); let mut dropped = false; for ex in info.program.extensions.iter() { if ex.drop_obj(info, state, hv)?.is_some() { dropped = true; } } if !dropped { warn!("Object {:#x} to del does not exist!", hv); state.set_flag(Flag::Invalid, true); } } } Ok(res) } fn is_compile_time_evaluable(&self) -> bool { match self { BuiltinOp::Load { .. } | BuiltinOp::LoadBits { .. } => true, _ => false } } fn to_sexp(&self) -> Sexp { super::parse::to_sexp(self) } }