From 289997af5a7cfb10b2ba23e92d17190ae0ca9220 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Tue, 28 Mar 2023 22:16:48 -0500 Subject: [PATCH] Some cleanups. --- src/backend.rs | 136 +++++++++++++++++++++----------------- src/backend/error.rs | 47 +++++++++++++ src/backend/into_crane.rs | 73 +++++++------------- src/backend/object.rs | 8 +++ src/backend/runtime.rs | 13 ++++ src/bin/ngrc.rs | 7 +- src/bin/ngri.rs | 27 +++++--- src/jit.rs | 32 --------- src/jit/engine.rs | 92 -------------------------- src/lib.rs | 1 - 10 files changed, 190 insertions(+), 246 deletions(-) create mode 100644 src/backend/error.rs create mode 100644 src/backend/object.rs delete mode 100644 src/jit.rs delete mode 100644 src/jit/engine.rs diff --git a/src/backend.rs b/src/backend.rs index 348f949..756e934 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -1,85 +1,74 @@ +mod error; mod into_crane; mod runtime; use std::collections::HashMap; +pub use self::error::BackendError; pub use self::runtime::{RuntimeFunctionError, RuntimeFunctions}; -use crate::ir; -use codespan_reporting::diagnostic::Diagnostic; -use cranelift_codegen::isa::LookupError; -use cranelift_codegen::settings::{Configurable, SetError}; -use cranelift_codegen::{isa, settings, CodegenError}; -use cranelift_module::{default_libcall_names, FuncId, ModuleError}; +use cranelift_codegen::settings::{Configurable}; +use cranelift_codegen::{isa, settings}; +use cranelift_jit::{JITModule, JITBuilder}; +use cranelift_module::{default_libcall_names, DataContext, DataId, Module, Linkage, FuncId}; use cranelift_object::{object, ObjectBuilder, ObjectModule}; use target_lexicon::Triple; -use thiserror::Error; -pub struct Program { - _func_id: FuncId, - module: ObjectModule, +const EMPTY_DATUM: [u8; 8] = [0; 8]; + +pub struct Backend { + pub module: M, + data_ctx: DataContext, + runtime_functions: RuntimeFunctions, + defined_strings: HashMap, + defined_symbols: HashMap, } -#[derive(Debug, Error)] -pub enum BackendError { - #[error("Cranelift module error: {0}")] - Cranelift(#[from] ModuleError), - #[error("Builtin function error: {0}")] - BuiltinError(#[from] RuntimeFunctionError), - #[error("Internal variable lookup error")] - VariableLookupFailure, - #[error(transparent)] - CodegenError(#[from] CodegenError), - #[error(transparent)] - SetError(#[from] SetError), - #[error(transparent)] - LookupError(#[from] LookupError), -} +impl Backend { + pub fn jit() -> Result { + let platform = Triple::host(); + let isa_builder = isa::lookup(platform.clone())?; + let mut settings_builder = settings::builder(); + settings_builder.set("use_colocated_libcalls", "false")?; + settings_builder.set("is_pic", "false")?; + let isa = isa_builder.finish(settings::Flags::new(settings_builder))?; + let mut builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names()); -impl From for Diagnostic { - fn from(value: BackendError) -> Self { - match value { - BackendError::Cranelift(me) => { - Diagnostic::error().with_message(format!("Internal cranelift error: {}", me)) - } - BackendError::BuiltinError(me) => { - Diagnostic::error().with_message(format!("Internal runtime function error: {}", me)) - } - BackendError::VariableLookupFailure => { - Diagnostic::error().with_message("Internal variable lookup error!") - } - BackendError::CodegenError(me) => { - Diagnostic::error().with_message(format!("Internal codegen error: {}", me)) - } - BackendError::SetError(me) => { - Diagnostic::error().with_message(format!("Internal backend setup error: {}", me)) - } - BackendError::LookupError(me) => { - Diagnostic::error().with_message(format!("Internal error: {}", me)) - } - } + RuntimeFunctions::register_jit_implementations(&mut builder); + + let mut module = JITModule::new(builder); + let runtime_functions = RuntimeFunctions::new(&platform, &mut module)?; + + Ok(Backend { + module, + data_ctx: DataContext::new(), + runtime_functions, + defined_strings: HashMap::new(), + defined_symbols: HashMap::new(), + }) } + + pub fn bytes(&self, function_id: FuncId) -> *const u8 { + self.module.get_finalized_function(function_id) + } } -impl Program { - pub fn new(platform: Triple, ir: ir::Program) -> Result { +impl Backend { + pub fn object_file(platform: Triple) -> Result { let isa_builder = isa::lookup(platform.clone())?; let mut settings_builder = settings::builder(); settings_builder.set("is_pic", "true")?; let isa = isa_builder.finish(settings::Flags::new(settings_builder))?; let object_builder = ObjectBuilder::new(isa, "example", default_libcall_names())?; - let mut object_module = ObjectModule::new(object_builder); - let rtfuns = RuntimeFunctions::new(&platform, &mut object_module)?; + let mut module = ObjectModule::new(object_builder); + let runtime_functions = RuntimeFunctions::new(&platform, &mut module)?; - Ok(Program { - _func_id: ir.into_cranelift::( - &mut object_module, - "gogogo", - &rtfuns, - &HashMap::new(), - &HashMap::new(), - )?, - module: object_module, + Ok(Backend { + module, + data_ctx: DataContext::new(), + runtime_functions, + defined_strings: HashMap::new(), + defined_symbols: HashMap::new(), }) } @@ -87,3 +76,30 @@ impl Program { self.module.finish().emit() } } + +impl Backend +{ + pub fn define_string(&mut self, s: &str) -> Result { + let name = format!("{}", s); + let global_id = self + .module + .declare_data(&name, Linkage::Local, false, false)?; + let mut data_context = DataContext::new(); + data_context.set_align(8); + data_context.define(s.to_owned().into_boxed_str().into_boxed_bytes()); + self.module.define_data(global_id, &data_context)?; + self.defined_strings.insert(s.to_owned(), global_id); + Ok(global_id) + } + + pub fn define_variable(&mut self, name: String) -> Result { + self.data_ctx.define(Box::new(EMPTY_DATUM)); + let id = self + .module + .declare_data(&name, Linkage::Export, true, false)?; + self.module.define_data(id, &self.data_ctx)?; + self.data_ctx.clear(); + self.defined_symbols.insert(name, id); + Ok(id) + } +} diff --git a/src/backend/error.rs b/src/backend/error.rs new file mode 100644 index 0000000..ea55990 --- /dev/null +++ b/src/backend/error.rs @@ -0,0 +1,47 @@ +use codespan_reporting::diagnostic::Diagnostic; +use cranelift_codegen::{CodegenError, settings::SetError, isa::LookupError}; +use cranelift_module::ModuleError; +use crate::backend::runtime::RuntimeFunctionError; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum BackendError { + #[error("Cranelift module error: {0}")] + Cranelift(#[from] ModuleError), + #[error("Builtin function error: {0}")] + BuiltinError(#[from] RuntimeFunctionError), + #[error("Internal variable lookup error")] + VariableLookupFailure, + #[error(transparent)] + CodegenError(#[from] CodegenError), + #[error(transparent)] + SetError(#[from] SetError), + #[error(transparent)] + LookupError(#[from] LookupError), +} + +impl From for Diagnostic { + fn from(value: BackendError) -> Self { + match value { + BackendError::Cranelift(me) => { + Diagnostic::error().with_message(format!("Internal cranelift error: {}", me)) + } + BackendError::BuiltinError(me) => { + Diagnostic::error().with_message(format!("Internal runtime function error: {}", me)) + } + BackendError::VariableLookupFailure => { + Diagnostic::error().with_message("Internal variable lookup error!") + } + BackendError::CodegenError(me) => { + Diagnostic::error().with_message(format!("Internal codegen error: {}", me)) + } + BackendError::SetError(me) => { + Diagnostic::error().with_message(format!("Internal backend setup error: {}", me)) + } + BackendError::LookupError(me) => { + Diagnostic::error().with_message(format!("Internal error: {}", me)) + } + } + } +} + diff --git a/src/backend/into_crane.rs b/src/backend/into_crane.rs index 30d7c46..78887a0 100644 --- a/src/backend/into_crane.rs +++ b/src/backend/into_crane.rs @@ -1,6 +1,5 @@ use std::collections::HashMap; -use crate::backend::runtime::RuntimeFunctions; use crate::ir::{Expression, Primitive, Program, Statement, Value, ValueOrRef}; use cranelift_codegen::entity::EntityRef; use cranelift_codegen::ir::{ @@ -9,26 +8,20 @@ use cranelift_codegen::ir::{ use cranelift_codegen::isa::CallConv; use cranelift_codegen::Context; use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext, Variable}; -use cranelift_module::{DataContext, DataId, FuncId, Linkage, Module, ModuleError}; +use cranelift_module::{FuncId, Linkage, Module, ModuleError}; use internment::ArcIntern; -use super::RuntimeFunctionError; +use crate::backend::Backend; +use crate::backend::error::BackendError; type StringTable = HashMap, GlobalValue>; -impl Program { - pub fn into_cranelift( - mut self, - module: &mut M, +impl Backend { + pub fn compile_function( + &mut self, function_name: &str, - rtfuns: &RuntimeFunctions, - pre_defined_strings: &HashMap, - pre_defined_symbols: &HashMap, - ) -> Result - where - E: From, - E: From, - M: Module, + mut program: Program, + ) -> Result { let basic_signature = Signature { params: vec![], @@ -36,19 +29,20 @@ impl Program { call_conv: CallConv::SystemV, }; - let func_id = module.declare_function(function_name, Linkage::Export, &basic_signature)?; + let func_id = self.module.declare_function(function_name, Linkage::Export, &basic_signature)?; let mut ctx = Context::new(); ctx.func = Function::with_name_signature(UserFuncName::user(0, func_id.as_u32()), basic_signature); - let string_table = self.build_string_table(module, &mut ctx.func, pre_defined_strings)?; + let string_table = self.build_string_table(&mut ctx.func, &program)?; let mut variable_table = HashMap::new(); let mut next_var_num = 1; - let print_func_ref = rtfuns.include_runtime_function("print", module, &mut ctx.func)?; - let pre_defined_symbols: HashMap = pre_defined_symbols + let print_func_ref = self.runtime_functions.include_runtime_function("print", &mut self.module, &mut ctx.func)?; + let pre_defined_symbols: HashMap = self + .defined_symbols .iter() .map(|(k, v)| { - let local_data = module.declare_data_in_func(*v, &mut ctx.func); + let local_data = self.module.declare_data_in_func(*v, &mut ctx.func); (k.clone(), local_data) }) .collect(); @@ -58,7 +52,7 @@ impl Program { let main_block = builder.create_block(); builder.switch_to_block(main_block); - for stmt in self.statements.drain(..) { + for stmt in program.statements.drain(..) { match stmt { Statement::Print(ann, var) => { let local_name_ref = string_table.get(&var).unwrap(); @@ -121,43 +115,24 @@ impl Program { builder.seal_block(main_block); builder.finalize(); - let _ = module.define_function(func_id, &mut ctx)?; + let _ = self.module.define_function(func_id, &mut ctx)?; Ok(func_id) } - fn build_string_table( - &self, - module: &mut M, + fn build_string_table( + &mut self, func: &mut Function, - pre_defined_strings: &HashMap, - ) -> Result { + program: &Program, + ) -> Result { let mut string_table = HashMap::new(); - for (idx, interned_value) in self.strings().drain().enumerate() { - let global_id = match pre_defined_strings.get(interned_value.as_str()) { + for interned_value in program.strings().drain() { + let global_id = match self.defined_strings.get(interned_value.as_str()) { Some(x) => *x, - None => { - let global_id = module.declare_data( - &format!("local-string-{}", idx), - Linkage::Local, - false, - false, - )?; - let mut data_context = DataContext::new(); - data_context.set_align(8); - data_context.define( - interned_value - .as_str() - .to_owned() - .into_boxed_str() - .into_boxed_bytes(), - ); - module.define_data(global_id, &data_context)?; - global_id - } + None => self.define_string(interned_value.as_str())?, }; - let local_data = module.declare_data_in_func(global_id, func); + let local_data = self.module.declare_data_in_func(global_id, func); string_table.insert(interned_value, local_data); } diff --git a/src/backend/object.rs b/src/backend/object.rs new file mode 100644 index 0000000..d17fd03 --- /dev/null +++ b/src/backend/object.rs @@ -0,0 +1,8 @@ +struct BackendObject { +} + +impl BackendObject { + pub fn new() -> Result { + unimplemented!() + } +} \ No newline at end of file diff --git a/src/backend/runtime.rs b/src/backend/runtime.rs index 36d8ae6..2412b6b 100644 --- a/src/backend/runtime.rs +++ b/src/backend/runtime.rs @@ -1,7 +1,9 @@ use cranelift_codegen::ir::{types, AbiParam, FuncRef, Function, Signature}; use cranelift_codegen::isa::CallConv; +use cranelift_jit::JITBuilder; use cranelift_module::{FuncId, Linkage, Module, ModuleResult}; use std::collections::HashMap; +use std::ffi::CStr; use target_lexicon::Triple; use thiserror::Error; @@ -16,6 +18,12 @@ pub enum RuntimeFunctionError { CannotFindRuntimeFunction(String), } +extern "C" fn runtime_print(name: *const i8, value: u64) { + let cstr = unsafe { CStr::from_ptr(name) }; + let reconstituted = cstr.to_string_lossy(); + println!("{} = {}", reconstituted, value); +} + impl RuntimeFunctions { pub fn new(platform: &Triple, module: &mut M) -> ModuleResult { let mut builtin_functions = HashMap::new(); @@ -54,4 +62,9 @@ impl RuntimeFunctions { Some(func_id) => Ok(module.declare_func_in_func(*func_id, func)), } } + + pub fn register_jit_implementations(builder: &mut JITBuilder) { + builder.symbol("print", runtime_print as *const u8); + } + } diff --git a/src/bin/ngrc.rs b/src/bin/ngrc.rs index 435ed4d..d488530 100644 --- a/src/bin/ngrc.rs +++ b/src/bin/ngrc.rs @@ -6,7 +6,7 @@ use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; use cranelift_object::object; use ngr::backend::BackendError; -use ngr::backend::Program as Cranelift; +use ngr::backend::Backend; use ngr::ir::Program as IR; use ngr::syntax::{ParserError, Program as Syntax}; use target_lexicon::Triple; @@ -70,8 +70,9 @@ fn compile(file_database: &mut SimpleFiles) -> Result<(), MainEr } let ir = IR::from(syntax.simplify()); - let compiled = Cranelift::new(Triple::host(), ir)?; - let bytes = compiled.bytes()?; + let mut backend = Backend::object_file(Triple::host())?; + backend.compile_function("gogogo", ir)?; + let bytes = backend.bytes()?; std::fs::write(args.output.unwrap_or_else(|| "output.o".to_string()), bytes)?; Ok(()) } diff --git a/src/bin/ngri.rs b/src/bin/ngri.rs index b2ad037..772016b 100644 --- a/src/bin/ngri.rs +++ b/src/bin/ngri.rs @@ -1,8 +1,10 @@ use codespan_reporting::diagnostic::Diagnostic; use codespan_reporting::files::SimpleFiles; use codespan_reporting::term::{self, Config}; +use cranelift_jit::JITModule; +use cranelift_module::ModuleError; +use ngr::backend::{Backend, BackendError}; use ngr::ir::Program as IR; -use ngr::jit::{JITEngine, JITError}; use ngr::syntax::{Location, ParserError, Statement}; use pretty::termcolor::{ColorChoice, StandardStream, WriteColor}; use rustyline::error::ReadlineError; @@ -11,7 +13,7 @@ use std::collections::HashMap; pub struct RunLoop<'a> { file_database: SimpleFiles<&'a str, String>, - jitter: JITEngine, + jitter: Backend, variable_binding_sites: HashMap, gensym_index: usize, writer: &'a mut dyn WriteColor, @@ -24,7 +26,9 @@ enum REPLError { #[error("Error parsing statement: {0}")] Parser(#[from] ParserError), #[error("JIT error: {0}")] - JIT(#[from] JITError), + JIT(#[from] BackendError), + #[error("Internal cranelift error: {0}")] + Cranelift(#[from] ModuleError), #[error(transparent)] Reporting(#[from] codespan_reporting::files::Error), } @@ -34,16 +38,17 @@ impl From for Diagnostic { match value { REPLError::Parser(err) => Diagnostic::from(&err), REPLError::JIT(err) => Diagnostic::from(err), + REPLError::Cranelift(err) => Diagnostic::bug().with_message(format!("{}", err)), REPLError::Reporting(err) => Diagnostic::bug().with_message(format!("{}", err)), } } } impl<'a> RunLoop<'a> { - pub fn new(writer: &'a mut dyn WriteColor, config: Config) -> Result { + pub fn new(writer: &'a mut dyn WriteColor, config: Config) -> Result { Ok(RunLoop { file_database: SimpleFiles::new(), - jitter: JITEngine::new()?, + jitter: Backend::jit()?, variable_binding_sites: HashMap::new(), gensym_index: 1, writer, @@ -83,7 +88,7 @@ impl<'a> RunLoop<'a> { // then we won't use this definition until someone tries again. if let Statement::Binding(_, ref name, _) = syntax { if !self.variable_binding_sites.contains_key(name.as_str()) { - self.jitter.define_string(name.clone())?; + self.jitter.define_string(name)?; self.jitter.define_variable(name.clone())?; } }; @@ -104,13 +109,17 @@ impl<'a> RunLoop<'a> { } let ir = IR::from(syntax.simplify(&mut self.gensym_index)); - let compiled = self.jitter.compile(line_no, ir)?; - compiled(); + let name = format!("line{}", line_no); + let function_id = self.jitter.compile_function(&name, ir)?; + self.jitter.module.finalize_definitions()?; + let compiled_bytes = self.jitter.bytes(function_id); + let compiled_function = unsafe { std::mem::transmute::<_, fn() -> ()>(compiled_bytes) }; + compiled_function(); Ok(()) } } -fn main() -> Result<(), JITError> { +fn main() -> Result<(), BackendError> { let mut editor = DefaultEditor::new().expect("rustyline works"); let mut line_no = 0; let mut writer = StandardStream::stdout(ColorChoice::Auto); diff --git a/src/jit.rs b/src/jit.rs deleted file mode 100644 index eb03687..0000000 --- a/src/jit.rs +++ /dev/null @@ -1,32 +0,0 @@ -use crate::backend::RuntimeFunctionError; -use codespan_reporting::diagnostic::Diagnostic; -use cranelift_codegen::{isa, settings::SetError, CodegenError}; -use cranelift_module::ModuleError; - -pub mod engine; - -pub use self::engine::JITEngine; - -#[derive(Debug, thiserror::Error)] -pub enum JITError { - #[error("JIT code generation error: {0}")] - Codegen(#[from] CodegenError), - - #[error("JIT configuration flag error: {0}")] - Set(#[from] SetError), - - #[error("ISA lookup error: {0}")] - Lookup(#[from] isa::LookupError), - - #[error("Cranelift module error: {0}")] - Cranelift(#[from] ModuleError), - - #[error("Runtime function error: {0}")] - Runtime(#[from] RuntimeFunctionError), -} - -impl From for Diagnostic { - fn from(value: JITError) -> Self { - Diagnostic::bug().with_message(format!("{}", value)) - } -} diff --git a/src/jit/engine.rs b/src/jit/engine.rs deleted file mode 100644 index bd51a65..0000000 --- a/src/jit/engine.rs +++ /dev/null @@ -1,92 +0,0 @@ -use crate::backend::RuntimeFunctions; -use crate::ir::Program as IR; -use crate::jit::JITError; -use cranelift_codegen::{ - isa, - settings::{self, Configurable}, -}; -use cranelift_jit::{JITBuilder, JITModule}; -use cranelift_module::{DataContext, DataId, Linkage, Module}; -use std::{collections::HashMap, ffi::CStr}; -use target_lexicon::Triple; - -const EMPTY_DATUM: [u8; 8] = [0; 8]; - -pub struct JITEngine { - data_ctx: DataContext, - module: JITModule, - runtime_functions: RuntimeFunctions, - defined_strings: HashMap, - defined_symbols: HashMap, -} - -extern "C" fn runtime_print(name: *const i8, value: u64) { - let cstr = unsafe { CStr::from_ptr(name) }; - let reconstituted = cstr.to_string_lossy(); - println!("{} = {}", reconstituted, value); -} - -impl JITEngine { - pub fn new() -> Result { - let platform = Triple::host(); - let isa_builder = isa::lookup(platform.clone())?; - let mut settings_builder = settings::builder(); - settings_builder.set("use_colocated_libcalls", "false")?; - settings_builder.set("is_pic", "false")?; - let isa = isa_builder.finish(settings::Flags::new(settings_builder))?; - let mut builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names()); - - builder.symbol("print", runtime_print as *const u8); - - let mut module = JITModule::new(builder); - let runtime_functions = RuntimeFunctions::new(&platform, &mut module)?; - - Ok(JITEngine { - data_ctx: DataContext::new(), - module, - runtime_functions, - defined_strings: HashMap::new(), - defined_symbols: HashMap::new(), - }) - } - - pub fn define_string(&mut self, s: String) -> Result<(), JITError> { - let name = format!("{}", s); - let global_id = self - .module - .declare_data(&name, Linkage::Local, false, false)?; - let mut data_context = DataContext::new(); - data_context.set_align(8); - data_context.define(s.as_str().to_owned().into_boxed_str().into_boxed_bytes()); - self.module.define_data(global_id, &data_context)?; - self.defined_strings.insert(s, global_id); - Ok(()) - } - - pub fn define_variable(&mut self, name: String) -> Result<(), JITError> { - self.data_ctx.define(Box::new(EMPTY_DATUM)); - let id = self - .module - .declare_data(&name, Linkage::Export, true, false)?; - self.module.define_data(id, &self.data_ctx)?; - self.data_ctx.clear(); - self.module.finalize_definitions()?; - self.defined_symbols.insert(name, id); - Ok(()) - } - - pub fn compile(&mut self, line: usize, program: IR) -> Result (), JITError> { - let function_name = format!("line{}", line); - let function_id = program.into_cranelift::( - &mut self.module, - &function_name, - &self.runtime_functions, - &self.defined_strings, - &self.defined_symbols, - )?; - self.module.finalize_definitions()?; - let code_ptr = self.module.get_finalized_function(function_id); - - unsafe { Ok(std::mem::transmute::<_, fn() -> ()>(code_ptr)) } - } -} diff --git a/src/lib.rs b/src/lib.rs index 765542b..6ed733f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,3 @@ pub mod backend; pub mod ir; -pub mod jit; pub mod syntax;