use core::{fmt, slice};
use std::fmt::Debug;
use std::io;
use ahash::{HashMap, HashMapExt};
use cranelift::prelude::{AbiParam, Signature, types::I64};
use cranelift_jit::{ArenaMemoryProvider, JITBuilder, JITModule};
use cranelift_module::{FuncId, Module};
use thiserror::Error;
use crate::builtins;
use crate::jit::jit_builtins;
pub type Value = i64;
pub const CRANELIFT_VALUE: cranelift::prelude::Type = I64;
#[derive(Debug, Clone)]
pub(crate) struct FuncRef(pub(crate) String);
#[derive(Debug, Clone)]
pub(crate) enum ArithOp {
Add,
Sub,
Mul,
Div,
Rem,
Lt,
Pow,
}
#[derive(Debug, Clone)]
pub(crate) enum MemOp {
Read8,
ReadNative,
Write8,
WriteNative,
WidthNative,
}
#[derive(Debug, Clone)]
pub(crate) enum Instr {
Literal(Value),
FunctionCall(FuncRef),
Quit,
Print,
Syscall,
Drop,
Swap,
Rot,
DropRange,
Pick,
Arith(ArithOp),
Mem(MemOp),
If,
Skip,
}
#[derive(Debug, Clone)]
pub enum Token {
Literal(Value),
Identifier(String),
Quit,
Print,
Drop,
Swap,
Rot,
If,
Pick,
Skip,
Colon,
Semicolon,
}
impl Token {
pub(crate) fn to_instruction(self) -> Instr {
match self {
Token::Literal(n) => Instr::Literal(n),
Token::Identifier(name) if let Some(inst) = builtins::FUNCTIONS.get(name.as_str()) => {
inst.clone()
}
Token::Identifier(name) => Instr::FunctionCall(FuncRef(name)),
Token::Quit => Instr::Quit,
Token::Print => Instr::Print,
Token::Drop => Instr::Drop,
Token::Swap => Instr::Swap,
Token::Rot => Instr::Rot,
Token::If => Instr::If,
Token::Skip => Instr::Skip,
Token::Pick => Instr::Pick,
_ => unreachable!("Tried to convert function syntax into an instruction"),
}
}
}
pub(crate) type Code = Vec<Instr>;
pub(crate) type JITFunction = unsafe extern "C" fn(*mut Value) -> *mut Value;
pub(crate) type CallStack<'a> = Vec<&'a [Instr]>;
pub(crate) struct Imports {
pub(crate) printfunc: FuncId,
pub(crate) quitfunc: FuncId,
pub(crate) powfunc: FuncId,
pub(crate) syscallfunc: FuncId,
}
pub(crate) struct Compiler<T> {
pub(crate) module: T,
pub(crate) imports: Imports,
}
pub(crate) type FuncMap = HashMap<String, Code>;
pub struct ClacState {
pub(crate) jit: Option<(JITModule, HashMap<String, FuncId>)>,
pub(crate) undefined_functions: Vec<(String, Code)>,
pub(crate) stack: Stack,
pub(crate) funcmap: FuncMap, }
pub(crate) struct Stack {
data: memmap2::MmapMut,
pub(crate) rsp: *mut Value,
}
impl Debug for Stack {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let head = self.data.as_ptr() as *const Value;
let len = unsafe {
self.rsp
.offset_from_unsigned(self.data.as_ptr() as *const Value)
};
<[Value] as Debug>::fmt(unsafe { slice::from_raw_parts(head, len) }, fmt)
}
}
impl Stack {
fn new(capacity: usize) -> io::Result<Self> {
let mut alloced = memmap2::MmapMut::map_anon(capacity)?;
Ok(Self {
rsp: alloced.as_mut_ptr() as *mut Value,
data: alloced,
})
}
pub(crate) fn push(&mut self, val: Value) {
unsafe {
*self.rsp = val;
}
self.rsp = self.rsp.wrapping_offset(1);
}
pub(crate) fn pop(&mut self) -> Option<Value> {
if self.rsp == self.data.as_mut_ptr() as *mut Value {
None
} else {
self.rsp = self.rsp.wrapping_offset(-1);
Some(unsafe { *self.rsp })
}
}
}
#[derive(Debug, Error)]
pub enum InitError {
#[error("Module error: {0}")]
ModuleError(#[from] cranelift_module::ModuleError),
#[error("IO Error: {0}")]
IoError(#[from] io::Error),
}
pub(crate) fn declare_imports(
module: &mut impl Module,
) -> Result<Imports, cranelift_module::ModuleError> {
let valparam = AbiParam::new(CRANELIFT_VALUE);
let printfunc = module.declare_function(
"__rprint__",
cranelift_module::Linkage::Import,
&Signature {
params: vec![valparam],
returns: vec![],
call_conv: module.isa().default_call_conv(),
},
)?;
let syscallfunc = module.declare_function(
"__syscall__",
cranelift_module::Linkage::Import,
&Signature {
params: vec![
valparam, valparam, valparam, valparam, valparam, valparam, valparam,
],
returns: vec![valparam],
call_conv: module.isa().default_call_conv(),
},
)?;
let quitfunc = module.declare_function(
"__rquit__",
cranelift_module::Linkage::Import,
&Signature {
params: vec![],
returns: vec![],
call_conv: module.isa().default_call_conv(),
},
)?;
let powfunc = module.declare_function(
"__rpow__",
cranelift_module::Linkage::Import,
&Signature {
params: vec![valparam, valparam],
returns: vec![valparam],
call_conv: module.isa().default_call_conv(),
},
)?;
Ok(Imports {
printfunc,
quitfunc,
powfunc,
syscallfunc,
})
}
impl Compiler<JITModule> {
pub(crate) fn new() -> Result<Self, InitError> {
let mut builder = JITBuilder::with_flags(
&[
("opt_level", "speed"),
("enable_alias_analysis", "true"),
("preserve_frame_pointers", "true"),
],
cranelift_module::default_libcall_names(),
)?;
builder.memory_provider(Box::new(
ArenaMemoryProvider::new_with_size(1_000_000_000).unwrap(),
));
builder.symbol("__rprint__", jit_builtins::print_value as *const u8);
builder.symbol("__rquit__", jit_builtins::quit as *const u8);
builder.symbol("__rpow__", jit_builtins::pow as *const u8);
builder.symbol("__syscall__", builtins::syscall as *const u8);
let mut module = cranelift_jit::JITModule::new(builder);
let imports = declare_imports(&mut module)?;
Ok(Compiler { module, imports })
}
}
#[derive(Debug, Error)]
pub enum ReplError {
#[error("Execution Error: {0}")]
ExecError(#[from] ExecError),
#[error("Readline Error: {0}")]
LineError(#[from] rustyline::error::ReadlineError),
#[error("Init error: {0}")]
InitError(#[from] InitError),
}
impl ClacState {
pub fn new(capacity: usize) -> Result<Self, InitError> {
Ok(ClacState {
jit: None,
stack: Stack::new(capacity)?,
undefined_functions: Vec::new(),
funcmap: HashMap::new(),
})
}
pub fn repl(&mut self, hide_stack: bool) -> Result<(), ReplError> {
println!("clac++ {} by stanleymw", env!("CARGO_PKG_VERSION"),);
let mut editor = rustyline::DefaultEditor::new()?;
loop {
let read = match editor.readline("clac++> ") {
Err(rustyline::error::ReadlineError::Eof)
| Err(rustyline::error::ReadlineError::Interrupted) => {
return Ok(());
}
Err(e) => return Err(e.into()),
Ok(res) => {
editor.add_history_entry(&res)?;
res
}
};
if cfg!(feature = "debug") && read == "int3" {
unsafe { std::arch::asm!("int3") };
continue;
}
match self.execute_str(&read) {
Err(ExecError::Quit) => return Ok(()),
Err(x) => return Err(x.into()),
Ok(()) => {}
};
if !hide_stack {
println!("{:?}", self.stack)
}
}
}
}
pub(crate) enum ExecRes {
Executed,
Skip(usize),
}
#[derive(Debug, Error)]
pub enum ExecError {
#[error("Unknown function {0}")]
UnknownFunction(String),
#[error("Missing arguments. Not enough elements on stack")]
MissingArguments,
#[error("Invalid Skip")]
InvalidSkip,
#[error("Invalid Pick")]
InvalidPick,
#[error("Invalid DropRange")]
InvalidDropRange,
#[error("Bad function definition")]
BadFunctionDefinition,
#[error("Invalid exponent, must have non-negative exponent")]
InvalidExponent,
#[error("Quit")]
Quit,
}