performance improvements

This commit is contained in:
2020-10-07 23:40:00 +02:00
parent a3eae0a8a9
commit e3fe3c6d72
18 changed files with 151 additions and 90 deletions
+1
View File
@@ -13,3 +13,4 @@ dyn-clonable = "0.9.0"
log = "0.4.11"
num-traits = "0.2.12"
parking_lot = "0.11.0"
nudge = "0.2.1"
+7 -3
View File
@@ -7,15 +7,18 @@ pub type DebugMsg = Cow<'static, str>;
/// Immediate value
pub type Value = u64;
pub fn is_positive(val: Value) -> bool {
#[inline(always)]
pub const fn is_positive(val: Value) -> bool {
0 == (val & 0x8000_0000_0000_0000)
}
pub fn is_negative(val: Value) -> bool {
#[inline(always)]
pub const fn is_negative(val: Value) -> bool {
0 != (val & 0x8000_0000_0000_0000)
}
pub fn as_signed(val: Value) -> i64 {
#[inline(always)]
pub const fn as_signed(val: Value) -> i64 {
i64::from_ne_bytes(val.to_ne_bytes())
}
@@ -24,6 +27,7 @@ pub fn as_signed(val: Value) -> i64 {
pub struct Addr(pub u64);
impl Addr {
#[inline(always)]
pub fn advance(&mut self, add: i64) {
if add < 0 {
self.0 = self.0.wrapping_sub(-add as u64);
+7 -5
View File
@@ -12,15 +12,17 @@ impl Rd {
pub const fn new(src: RdData) -> Self {
Rd(src, Mask::FULL)
}
pub fn data(self) -> RdData {
pub const fn data(self) -> RdData {
self.0
}
pub fn mask(self) -> Mask {
pub const fn mask(self) -> Mask {
self.1
}
pub fn immediate(val: Value) -> Rd {
Rd(RdData::Immediate(val), Mask::default())
pub const fn immediate(val: Value) -> Rd {
Rd(RdData::Immediate(val), Mask::FULL)
}
}
@@ -48,7 +50,7 @@ impl Debug for Rd {
pub struct RdObj(Register);
impl RdObj {
pub fn new(reg: Register) -> Self {
pub const fn new(reg: Register) -> Self {
RdObj(reg)
}
pub const fn reg(self) -> Register {
+10 -3
View File
@@ -5,8 +5,9 @@ use sexp::SourcePosition;
use crate::asm::instr::flatten::labels_to_skips;
use crate::asm::parse::{ParserContext, ParserState};
use crate::module::CrsnExtension;
use crate::module::{CrsnExtension, CrsnUniq};
use crate::runtime::program::Program;
use crate::builtin::BuiltinOps;
pub mod data;
pub mod error;
@@ -15,7 +16,13 @@ pub mod parse;
pub mod patches;
/// Parse a program from string and assemble a low level instruction sequence from it.
pub fn assemble(source: &str, parsers: Arc<Vec<Box<dyn CrsnExtension>>>) -> Result<Arc<Program>, error::CrsnError> {
pub fn assemble(source: &str, uniq : &CrsnUniq, mut parsers: Vec<Box<dyn CrsnExtension>>) -> Result<Arc<Program>, error::CrsnError> {
parsers.insert(0, BuiltinOps::new());
for p in &mut parsers {
p.init(uniq);
}
let pcx = ParserContext {
parsers: &parsers,
state: RefCell::new(ParserState {
@@ -28,5 +35,5 @@ pub fn assemble(source: &str, parsers: Arc<Vec<Box<dyn CrsnExtension>>>) -> Resu
let ops = parse::parse(source, &SourcePosition::default(), &pcx)?;
let ops = labels_to_skips(ops)?;
Ok(Program::new(ops, parsers)?)
Ok(Program::new(ops, Arc::new(parsers))?)
}
+2 -3
View File
@@ -4,12 +4,11 @@ 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::BuiltinOps;
use crate::module::ParseRes;
pub fn parse_op<'a>(mut keyword: &str, mut arg_tokens: TokenParser<'a>, spos: &SourcePosition) -> Result<Option<Op>, CrsnError> {
// Include built-in instructions
let builtins = [BuiltinOps::new()];
let mut cond = None;
if let Some(pos) = keyword.find('.') {
@@ -17,7 +16,7 @@ pub fn parse_op<'a>(mut keyword: &str, mut arg_tokens: TokenParser<'a>, spos: &S
keyword = &keyword[..pos];
}
for p in builtins.iter().chain(arg_tokens.pcx.parsers) {
for p in arg_tokens.pcx.parsers {
arg_tokens = match p.parse_op(spos, keyword, arg_tokens) {
Ok(ParseRes::Parsed(kind)) => return Ok(Some(Op {
cond,
+28
View File
@@ -14,6 +14,8 @@ use crate::asm::parse::arg_parser::TokenParser;
use crate::runtime::fault::Fault;
use crate::runtime::run_thread::state::RunState;
use crate::runtime::run_thread::ThreadInfo;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
mod eval_res;
@@ -41,7 +43,33 @@ pub trait OpTrait: Debug + Send + Sync + 'static {
fn to_sexp(&self) -> Sexp;
}
/// CRSN initializer object.
/// Only one should be created for the lifespan of the parser and runtime.
#[derive(Default)]
pub struct CrsnUniq {
object_handle_counter : AtomicU64
}
pub const UNIQ_BASE: u64 = 0x6372_736e_0000_0000;
impl CrsnUniq {
pub fn new() -> Arc<Self> {
Arc::new(Self {
object_handle_counter: AtomicU64::new(UNIQ_BASE),
})
}
pub fn unique_handle(&self) -> u64 {
self.object_handle_counter.fetch_add(1, Ordering::Relaxed)
}
}
pub trait CrsnExtension: Debug + Send + Sync + 'static {
fn init(&mut self, uniq: &CrsnUniq) {
//
}
/// Get name of the module
fn name(&self) -> &'static str;
+1 -1
View File
@@ -8,7 +8,7 @@ impl RunThread {
let state = &mut self.state;
let info = &self.info;
let op = info.program.read(state.frame.pc);
let op = info.program.fetch_instr(state.frame.pc);
trace!("### {:04} : {:?}", state.frame.pc.0, op);
+1
View File
@@ -33,6 +33,7 @@ impl StackFrame {
sf
}
#[inline(always)]
pub fn set_retvals(&mut self, vals: &[Value]) {
for n in 0..(vals.len().min(REG_COUNT)) {
self.res[n] = vals[n];
+4
View File
@@ -27,6 +27,7 @@ pub struct StatusFlags {
}
impl StatusFlags {
#[inline(always)]
pub fn clear(&mut self) {
*self = Self::default();
}
@@ -57,12 +58,14 @@ impl StatusFlags {
if val & 0x100 != 0 { self.carry = true; }
}
#[inline(always)]
pub fn update(&mut self, val: Value) {
self.zero = val == 0;
self.positive = is_positive(val);
self.negative = is_negative(val);
}
#[inline(always)]
pub fn test(&self, cond: Cond) -> bool {
match cond {
Cond::Equal => self.equal,
@@ -86,6 +89,7 @@ impl StatusFlags {
}
}
#[inline(always)]
pub fn set(&mut self, cond: Cond) {
match cond {
Cond::Equal => {
+6 -6
View File
@@ -90,8 +90,10 @@ impl Program {
}
/// Read a program instruction at address
pub fn read(&self, addr: Addr) -> &Op {
if addr.0 >= self.ops.len() as u64 {
pub fn fetch_instr(&self, addr: Addr) -> &Op {
if (addr.0 as usize) < self.ops.len() {
unsafe { self.ops.get_unchecked(addr.0 as usize) }
} else {
&Op {
kind: OpKind::BuiltIn(BuiltinOp::Halt),
pos: SourcePosition {
@@ -101,8 +103,6 @@ impl Program {
},
cond: None,
}
} else {
&self.ops[addr.0 as usize]
}
}
@@ -116,7 +116,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 OpKind::BuiltIn(BuiltinOp::Barrier { msg, .. }) = &self.read(*b0).kind {
if let OpKind::BuiltIn(BuiltinOp::Barrier { msg, .. }) = &self.fetch_instr(*b0).kind {
return Err(Fault::JumpThroughBarrier {
msg: msg.clone().unwrap_or("BLOCK BARRIER".into())
});
@@ -127,7 +127,7 @@ impl Program {
} else {
// point barrier
if *b0 >= from && *b0 <= to {
if let OpKind::BuiltIn(BuiltinOp::Barrier { msg, .. }) = &self.read(*b0).kind {
if let OpKind::BuiltIn(BuiltinOp::Barrier { msg, .. }) = &self.fetch_instr(*b0).kind {
return Err(Fault::JumpThroughBarrier {
msg: msg.clone().unwrap_or("POINT BARRIER".into())
});
+7 -12
View File
@@ -1,5 +1,5 @@
use std::sync::Arc;
use std::sync::atomic::AtomicU64;
use std::thread::JoinHandle;
use std::time::Duration;
@@ -7,7 +7,7 @@ pub use info::ThreadInfo;
pub use state::RunState;
use crate::asm::data::literal::Addr;
use crate::module::EvalRes;
use crate::module::{EvalRes, CrsnUniq};
use crate::runtime::fault::Fault;
use crate::runtime::frame::StackFrame;
use crate::runtime::program::Program;
@@ -23,13 +23,9 @@ pub struct RunThread {
pub mod info;
pub mod state;
pub fn new_uniq() -> Arc<AtomicU64> {
Arc::new(AtomicU64::new(info::UNIQ_BASE))
}
pub struct ThreadParams<'a> {
pub id: ThreadToken,
pub uniq: Option<Arc<AtomicU64>>,
pub uniq: Arc<CrsnUniq>,
pub program: Arc<Program>,
pub pc: Addr,
pub cycle_time: Duration,
@@ -42,7 +38,7 @@ impl RunThread {
let ti = Arc::new(ThreadInfo {
id: params.id,
uniq: params.uniq.unwrap_or_else(new_uniq),
uniq: params.uniq,
program: params.program,
cycle_time: params.cycle_time,
extensions,
@@ -73,12 +69,11 @@ impl RunThread {
'run: loop {
match self.eval_op() {
Ok(EvalRes { cycles, advance }) => {
std::thread::sleep(self.info.cycle_time * (cycles as u32));
if cycles > 0 {
std::thread::sleep(self.info.cycle_time * (cycles as u32));
}
trace!("Step {}; Status = {}", advance, self.state.frame.status);
self.state.frame.pc.advance(advance);
if self.state.frame.status.invalid {
warn!("Operation failed with INVALID status!");
}
}
Err(Fault::Halt) => {
// TODO implement coordinated shutdown when more threads are running!
+6 -8
View File
@@ -1,17 +1,17 @@
use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::Duration;
use crate::asm::data::literal::Value;
use crate::runtime::program::Program;
use crate::runtime::run_thread::ThreadToken;
use crate::module::CrsnExtension;
use crate::module::{CrsnExtension, CrsnUniq};
pub struct ThreadInfo {
/// Thread ID
pub id: ThreadToken,
/// Thread ID
pub(crate) uniq: Arc<AtomicU64>,
/// Initializer, used to generate unique IDs
pub(crate) uniq: Arc<CrsnUniq>,
/// Program to run
pub program: Arc<Program>,
/// Program to run
@@ -20,10 +20,8 @@ pub struct ThreadInfo {
pub extensions: Arc<Vec<Box<dyn CrsnExtension>>>,
}
pub const UNIQ_BASE: u64 = 0x6372_736e_0000_0000;
impl ThreadInfo {
pub fn unique_value(&self) -> Value {
self.uniq.fetch_add(1, Ordering::Relaxed)
pub fn unique_handle(&self) -> Value {
self.uniq.unique_handle()
}
}
+41 -36
View File
@@ -8,6 +8,7 @@ use crate::runtime::fault::Fault;
use crate::runtime::frame::{CallStack, REG_COUNT, StackFrame};
use std::sync::Arc;
use crate::runtime::run_thread::ThreadInfo;
use nudge::{likely};
pub struct RunState {
pub thread_info: Arc<ThreadInfo>,
@@ -49,6 +50,7 @@ impl RunState {
}
/// Set a status flag. Only supports simple, positive conds (i.e. not GreaterOrEqual)
#[inline(always)]
pub fn set_flag(&mut self, cond: Cond, set: bool) {
if set {
self.frame.status.set(cond);
@@ -56,6 +58,7 @@ impl RunState {
}
/// Check status flags for a condition
#[inline(always)]
pub fn test_cond(&self, cond: Cond) -> bool {
self.frame.status.test(cond)
}
@@ -72,12 +75,14 @@ impl RunState {
}
/// Clear status flags
#[inline(always)]
pub fn clear_status(&mut self) {
self.frame.status.clear();
}
/// Update status flags using a variable.
/// The update is additive - call `clear_status()` first if desired!
#[inline(always)]
pub fn update_status(&mut self, val: Value) {
self.frame.status.update(val);
}
@@ -90,29 +95,29 @@ impl RunState {
/// Read a `Rd` value
pub fn read(&mut self, rd: Rd) -> Result<Value, Fault> {
match rd.data() {
RdData::Immediate(v) => Ok(v),
RdData::Register(Register::Res(rn)) => {
if rn >= REG_COUNT as u8 {
Err(Fault::RegisterNotExist { reg: Register::Res(rn) }) // TODO use match after @ when stabilized https://github.com/rust-lang/rust/issues/65490
} else {
trace!("Rd {:?} = {}", rd, self.frame.res[rn as usize]);
Ok(self.frame.res[rn as usize])
}
}
RdData::Register(Register::Arg(rn)) => {
if rn >= REG_COUNT as u8 {
Err(Fault::RegisterNotExist { reg: Register::Arg(rn) })
} else {
trace!("Rd {:?} = {}", rd, self.frame.arg[rn as usize]);
Ok(self.frame.arg[rn as usize])
}
}
RdData::Register(Register::Gen(rn)) => {
if rn >= REG_COUNT as u8 {
Err(Fault::RegisterNotExist { reg: Register::Gen(rn) })
} else {
if likely(rn < REG_COUNT as u8) {
trace!("Rd {:?} = {}", rd, self.frame.gen[rn as usize]);
Ok(self.frame.gen[rn as usize])
} else {
Err(Fault::RegisterNotExist { reg: Register::Gen(rn) })
}
}
RdData::Immediate(v) => Ok(v),
RdData::Register(Register::Arg(rn)) => {
if likely(rn < REG_COUNT as u8) {
trace!("Rd {:?} = {}", rd, self.frame.arg[rn as usize]);
Ok(self.frame.arg[rn as usize])
} else {
Err(Fault::RegisterNotExist { reg: Register::Arg(rn) })
}
}
RdData::Register(Register::Res(rn)) => {
if likely(rn < REG_COUNT as u8) {
trace!("Rd {:?} = {}", rd, self.frame.res[rn as usize]);
Ok(self.frame.res[rn as usize])
} else {
Err(Fault::RegisterNotExist { reg: Register::Res(rn) }) // TODO use match after @ when stabilized https://github.com/rust-lang/rust/issues/65490
}
}
RdData::RegObject(register) => {
@@ -160,32 +165,32 @@ impl RunState {
trace!("WR {:?} := {}", wr, val);
match wr.d() {
WrData::Discard => {
/* Discard */
Ok(())
}
WrData::Register(Register::Res(rn)) => {
if rn >= REG_COUNT as u8 {
Err(Fault::RegisterNotExist { reg: Register::Res(rn) }) // TODO use match after @ when stabilized https://github.com/rust-lang/rust/issues/65490
WrData::Register(Register::Gen(rn)) => {
if likely(rn < REG_COUNT as u8) {
self.frame.gen[rn as usize] = val;
Ok(())
} else {
Err(Fault::RegisterNotWritable { reg: Register::Res(rn) })
Err(Fault::RegisterNotExist { reg: Register::Gen(rn) })
}
}
WrData::Register(Register::Arg(rn)) => {
if rn >= REG_COUNT as u8 {
Err(Fault::RegisterNotExist { reg: Register::Arg(rn) })
} else {
if likely(rn < REG_COUNT as u8) {
Err(Fault::RegisterNotWritable { reg: Register::Res(rn) })
} else {
Err(Fault::RegisterNotExist { reg: Register::Arg(rn) })
}
}
WrData::Register(Register::Gen(rn)) => {
if rn >= REG_COUNT as u8 {
Err(Fault::RegisterNotExist { reg: Register::Gen(rn) })
WrData::Register(Register::Res(rn)) => {
if likely(rn < REG_COUNT as u8) {
Err(Fault::RegisterNotWritable { reg: Register::Res(rn) })
} else {
self.frame.gen[rn as usize] = val;
Ok(())
Err(Fault::RegisterNotExist { reg: Register::Res(rn) }) // TODO use match after @ when stabilized https://github.com/rust-lang/rust/issues/65490
}
}
WrData::Discard => {
/* Discard */
Ok(())
}
WrData::RegObject(register) => {
let reference = self.read(Rd::new(RdData::Register(register)))?;
self.write_object(reference, wr.mask(), val)