🧪 Add evaluation tests to ensure that passes retain NGR semantics. (#2)
This change adds `Arbitrary` instances to the key IR data types (both as syntax and as native IR), as well as evaluator functions for both. This way, we can ensure that the evaluation of one version of the IR is observationally equivalent to another version of the IR, or even a later IR. It also adds a similar ability through both static file compilation and the JIT, to ensure that the translation through Cranelift and our runtime works as expected. This actually found a couple issues in its creation, and I hope is helpful extensions into more interesting programs.
This commit was merged in pull request #2.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
mod error;
|
||||
mod eval;
|
||||
mod into_crane;
|
||||
mod runtime;
|
||||
|
||||
@@ -10,7 +11,7 @@ use cranelift_codegen::settings::Configurable;
|
||||
use cranelift_codegen::{isa, settings};
|
||||
use cranelift_jit::{JITBuilder, JITModule};
|
||||
use cranelift_module::{default_libcall_names, DataContext, DataId, FuncId, Linkage, Module};
|
||||
use cranelift_object::{object, ObjectBuilder, ObjectModule};
|
||||
use cranelift_object::{ObjectBuilder, ObjectModule};
|
||||
use target_lexicon::Triple;
|
||||
|
||||
const EMPTY_DATUM: [u8; 8] = [0; 8];
|
||||
@@ -21,10 +22,11 @@ pub struct Backend<M: Module> {
|
||||
runtime_functions: RuntimeFunctions,
|
||||
defined_strings: HashMap<String, DataId>,
|
||||
defined_symbols: HashMap<String, DataId>,
|
||||
output_buffer: Option<String>,
|
||||
}
|
||||
|
||||
impl Backend<JITModule> {
|
||||
pub fn jit() -> Result<Self, BackendError> {
|
||||
pub fn jit(output_buffer: Option<String>) -> Result<Self, BackendError> {
|
||||
let platform = Triple::host();
|
||||
let isa_builder = isa::lookup(platform.clone())?;
|
||||
let mut settings_builder = settings::builder();
|
||||
@@ -44,6 +46,7 @@ impl Backend<JITModule> {
|
||||
runtime_functions,
|
||||
defined_strings: HashMap::new(),
|
||||
defined_symbols: HashMap::new(),
|
||||
output_buffer,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -69,23 +72,26 @@ impl Backend<ObjectModule> {
|
||||
runtime_functions,
|
||||
defined_strings: HashMap::new(),
|
||||
defined_symbols: HashMap::new(),
|
||||
output_buffer: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn bytes(self) -> Result<Vec<u8>, object::write::Error> {
|
||||
self.module.finish().emit()
|
||||
pub fn bytes(self) -> Result<Vec<u8>, BackendError> {
|
||||
self.module.finish().emit().map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl<M: Module> Backend<M> {
|
||||
pub fn define_string(&mut self, s: &str) -> Result<DataId, BackendError> {
|
||||
let name = format!("<string_constant>{}", s);
|
||||
let s0 = format!("{}\0", 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());
|
||||
data_context.define(s0.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)
|
||||
@@ -101,4 +107,20 @@ impl<M: Module> Backend<M> {
|
||||
self.defined_symbols.insert(name, id);
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
pub fn output_buffer_ptr(&mut self) -> *mut String {
|
||||
if let Some(str) = self.output_buffer.as_mut() {
|
||||
str as *mut String
|
||||
} else {
|
||||
std::ptr::null_mut()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn output(self) -> String {
|
||||
if let Some(s) = self.output_buffer {
|
||||
s
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user