📜 Add better documentation across the compiler. (#3)
These changes pay particular attention to API endpoints, to try to ensure that any rustdocs generated are detailed and sensible. A good next step, eventually, might be to include doctest examples, as well. For the moment, it's not clear that they would provide a lot of value, though. In addition, this does a couple refactors to simplify the code base in ways that make things clearer or, at least, briefer.
This commit is contained in:
@@ -8,9 +8,14 @@ use std::fmt::Write;
|
||||
use target_lexicon::Triple;
|
||||
use thiserror::Error;
|
||||
|
||||
/// An object for querying / using functions built into the runtime.
|
||||
///
|
||||
/// Right now, this is a quite a bit of boilerplate for very nebulous
|
||||
/// value. However, as the number of built-in functions gets large, it's
|
||||
/// nice to have a single point to register and query them, so here we
|
||||
/// go.
|
||||
pub struct RuntimeFunctions {
|
||||
builtin_functions: HashMap<String, FuncId>,
|
||||
_referenced_functions: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error, PartialEq)]
|
||||
@@ -19,25 +24,27 @@ pub enum RuntimeFunctionError {
|
||||
CannotFindRuntimeFunction(String),
|
||||
}
|
||||
|
||||
extern "C" fn runtime_print(output_buffer: *mut String, name: *const i8, value: i64) {
|
||||
let cstr = unsafe { CStr::from_ptr(name) };
|
||||
let reconstituted = cstr.to_string_lossy();
|
||||
|
||||
if let Some(output_buffer) = unsafe { output_buffer.as_mut() } {
|
||||
writeln!(output_buffer, "{} = {}i64", reconstituted, value).unwrap();
|
||||
} else {
|
||||
println!("{} = {}", reconstituted, value);
|
||||
}
|
||||
}
|
||||
|
||||
impl RuntimeFunctions {
|
||||
/// Generate a new runtime function table for the given platform, and
|
||||
/// declare them within the provided Cranelift module.
|
||||
///
|
||||
/// Note that this is very conservative: it assumes that your module
|
||||
/// will want to use every runtime function. Unless the Cranelift object
|
||||
/// builder is smart, this might inject a bunch of references (and thus
|
||||
/// linker requirements) that aren't actually needed by your program.
|
||||
///
|
||||
/// Then again, right now there's exactly one runtime function, so ...
|
||||
/// not a big deal.
|
||||
pub fn new<M: Module>(platform: &Triple, module: &mut M) -> ModuleResult<RuntimeFunctions> {
|
||||
let mut builtin_functions = HashMap::new();
|
||||
let _referenced_functions = Vec::new();
|
||||
|
||||
let string_param = AbiParam::new(types::I64);
|
||||
let int64_param = AbiParam::new(types::I64);
|
||||
|
||||
// declare print for Cranelift; it's something we're going to import
|
||||
// into the current module (it's compiled separately), and takes two
|
||||
// strings and an integer. (Which ... turn out to all be the same
|
||||
// underlying type, which is weird but the way it is.)
|
||||
let print_id = module.declare_function(
|
||||
"print",
|
||||
Linkage::Import,
|
||||
@@ -47,14 +54,19 @@ impl RuntimeFunctions {
|
||||
call_conv: CallConv::triple_default(platform),
|
||||
},
|
||||
)?;
|
||||
|
||||
// Toss this function in our internal dictionary, as well.
|
||||
builtin_functions.insert("print".to_string(), print_id);
|
||||
|
||||
Ok(RuntimeFunctions {
|
||||
builtin_functions,
|
||||
_referenced_functions,
|
||||
})
|
||||
Ok(RuntimeFunctions { builtin_functions })
|
||||
}
|
||||
|
||||
/// Include the named runtime function into the current Function context.
|
||||
///
|
||||
/// This is necessary for every runtime function reference within each
|
||||
/// function. The returned `FuncRef` can be used in `call` invocations.
|
||||
/// The only reason for this function to error is if you pass a name that
|
||||
/// the runtime isn't familiar with.
|
||||
pub fn include_runtime_function<M: Module>(
|
||||
&self,
|
||||
name: &str,
|
||||
@@ -69,7 +81,30 @@ impl RuntimeFunctions {
|
||||
}
|
||||
}
|
||||
|
||||
/// Register live, local versions of the runtime functions into the JIT.
|
||||
///
|
||||
/// Note that these implementations are *not* the same as the ones defined
|
||||
/// in `CARGO_MANIFEST_DIR/runtime/`, for ... reasons. It might be a good
|
||||
/// change, in the future, to find a way to unify these implementations into
|
||||
/// one; both to reduce the chance that they deviate, and to reduce overall
|
||||
/// maintenance burden.
|
||||
pub fn register_jit_implementations(builder: &mut JITBuilder) {
|
||||
builder.symbol("print", runtime_print as *const u8);
|
||||
}
|
||||
}
|
||||
|
||||
// Print! This implementation is used in the JIT compiler, to actually print data. We
|
||||
// use the `output_buffer` argument as an aid for testing; if it's non-NULL, it's a string
|
||||
// we extend with the output, so that multiple JIT'd `Program`s can run concurrently
|
||||
// without stomping over each other's output. If `output_buffer` is NULL, we just print
|
||||
// to stdout.
|
||||
extern "C" fn runtime_print(output_buffer: *mut String, name: *const i8, value: i64) {
|
||||
let cstr = unsafe { CStr::from_ptr(name) };
|
||||
let reconstituted = cstr.to_string_lossy();
|
||||
|
||||
if let Some(output_buffer) = unsafe { output_buffer.as_mut() } {
|
||||
writeln!(output_buffer, "{} = {}i64", reconstituted, value).unwrap();
|
||||
} else {
|
||||
println!("{} = {}", reconstituted, value);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user