aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2026-03-30 21:19:26 +0100
committerMichael Williamson <mike@zwobble.org>2026-03-30 21:19:26 +0100
commite41262b82bcdfdb7857eab89f045cb917ab11305 (patch)
tree951640272103073470920eb79a781e3a5d02179b
parent7bdf8fc6b0df69f70929b63019b06a72fda25395 (diff)
Split up code
-rw-r--r--patio-to-cranelift/src/context.rs74
-rw-r--r--patio-to-cranelift/src/errors.rs31
-rw-r--r--patio-to-cranelift/src/lib.rs144
-rw-r--r--patio-to-cranelift/src/main.rs232
4 files changed, 251 insertions, 230 deletions
diff --git a/patio-to-cranelift/src/context.rs b/patio-to-cranelift/src/context.rs
new file mode 100644
index 00000000..65d7eb15
--- /dev/null
+++ b/patio-to-cranelift/src/context.rs
@@ -0,0 +1,74 @@
+use std::path::Path;
+
+use cranelift::prelude::Configurable;
+use cranelift_module::Module;
+
+use crate::errors::CompileError;
+
+pub(crate) struct CompilerContext {
+ cranelift_context: cranelift_codegen::Context,
+ module: cranelift_object::ObjectModule,
+ next_func_name_index: u32,
+ pub(crate) target_triple: target_lexicon::Triple,
+}
+
+impl CompilerContext {
+ pub(crate) fn for_object(
+ source_path: &Path,
+ target_triple: target_lexicon::Triple,
+ ) -> Result<Self, CompileError> {
+ let isa = Self::isa(target_triple.clone())?;
+
+ let object_builder = cranelift_object::ObjectBuilder::new(
+ isa,
+ source_path.as_os_str().as_encoded_bytes(),
+ cranelift_module::default_libcall_names()
+ ).map_err(CompileError::ObjectBuilderNewFailed)?;
+
+ let module = cranelift_object::ObjectModule::new(object_builder);
+
+ Ok(Self::new(module, target_triple))
+ }
+
+ fn isa(target: target_lexicon::Triple) -> Result<std::sync::Arc<dyn cranelift::prelude::isa::TargetIsa>, CompileError> {
+ let mut flags_builder = cranelift::prelude::settings::builder();
+ flags_builder.enable("is_pic").map_err(CompileError::EnablePicFailed)?;
+ cranelift::prelude::isa::lookup(target)
+ .map_err(CompileError::LookupIsa)?
+ .finish(cranelift::prelude::settings::Flags::new(flags_builder))
+ .map_err(CompileError::BuildIsa)
+ }
+
+
+ fn new(module: cranelift_object::ObjectModule, target_triple: target_lexicon::Triple) -> Self {
+ CompilerContext {
+ cranelift_context: cranelift_codegen::Context::new(),
+ module,
+ next_func_name_index: 0,
+ target_triple,
+ }
+ }
+
+ pub(crate) fn into_object(self) -> cranelift_object::ObjectProduct {
+ self.module.finish()
+ }
+
+ pub(crate) fn enter_function(&mut self, name: &str, signature: cranelift::prelude::Signature, linkage: cranelift_module::Linkage) {
+ let mut func = cranelift_codegen::ir::Function::with_name_signature(self.func_name(), signature);
+
+ let mut func_builder_context = cranelift::prelude::FunctionBuilderContext::new();
+ let _function_builder = cranelift::prelude::FunctionBuilder::new(&mut func, &mut func_builder_context);
+
+ let func_id = self.module.declare_function(name, linkage, &func.signature).unwrap();
+ self.module.define_function(func_id, &mut self.cranelift_context).unwrap();
+ }
+
+ /// Generate a function name for Cranelift.
+ fn func_name(&mut self) -> cranelift_codegen::ir::UserFuncName {
+ // Cranelift doesn't assign any particular meaning to the values, so we
+ // just use an incrementing index in a constant namespace.
+ let name = cranelift_codegen::ir::UserFuncName::user(0, self.next_func_name_index);
+ self.next_func_name_index += 1;
+ name
+ }
+}
diff --git a/patio-to-cranelift/src/errors.rs b/patio-to-cranelift/src/errors.rs
new file mode 100644
index 00000000..3a71da71
--- /dev/null
+++ b/patio-to-cranelift/src/errors.rs
@@ -0,0 +1,31 @@
+
+
+#[derive(Debug)]
+pub enum CompileError {
+ /// Reading the bytes of the patio source file failed.
+ ReadSourceBytes(#[allow(dead_code)] std::io::Error),
+
+ /// The bytes of the patio source were an invalid flatbuffer.
+ InvalidFlatbuffer(#[allow(dead_code)] flatbuffers::InvalidFlatbuffer),
+
+ /// Enabling PIC failed.
+ EnablePicFailed(#[allow(dead_code)] cranelift_codegen::settings::SetError),
+
+ /// Could not parse the target triple.
+ ParseTargetTriple(#[allow(dead_code)] target_lexicon::ParseError),
+
+ /// The ISA for the target triple could not be found.
+ LookupIsa(#[allow(dead_code)] cranelift_codegen::isa::LookupError),
+
+ /// Building the ISA failed.
+ BuildIsa(#[allow(dead_code)] cranelift_codegen::CodegenError),
+
+ /// Create a new object builder failed.
+ ObjectBuilderNewFailed(#[allow(dead_code)] cranelift_module::ModuleError),
+
+ /// Emitting the bytes for an object failed.
+ ObjectEmitFailed(#[allow(dead_code)] cranelift_object::object::write::Error),
+
+ /// Writing the object file to disk failed.
+ WriteObjectFile(#[allow(dead_code)] std::io::Error),
+}
diff --git a/patio-to-cranelift/src/lib.rs b/patio-to-cranelift/src/lib.rs
new file mode 100644
index 00000000..d3eeff8c
--- /dev/null
+++ b/patio-to-cranelift/src/lib.rs
@@ -0,0 +1,144 @@
+use std::{path::{Path}, str::FromStr};
+
+use crate::{context::CompilerContext, errors::CompileError, patio_generated::patio::{PatioCompilationUnitElement, PatioCompilationUnitElementValue, PatioFunctionDefinition, PatioLocal, PatioLocalType, PatioType, PatioTypeValue, PatioVisibility}};
+
+#[allow(dead_code, unused_imports)]
+#[path = "./gen/patio_generated.rs"]
+mod patio_generated;
+
+#[allow(dead_code, unused_imports)]
+#[path = "./gen/sources_generated.rs"]
+mod sources_generated;
+
+mod context;
+mod errors;
+
+pub fn compile(source_path: &Path, target_str: &str) -> Result<(), CompileError> {
+ let source_bytes = std::fs::read(source_path).map_err(CompileError::ReadSourceBytes)?;
+
+ let compilation_unit = patio_generated::patio::root_as_patio_compilation_unit(&source_bytes)
+ .map_err(CompileError::InvalidFlatbuffer)?;
+
+ let target_triple = target_lexicon::Triple::from_str(target_str)
+ .map_err(CompileError::ParseTargetTriple)?;
+
+ let mut context = CompilerContext::for_object(source_path, target_triple)?;
+
+ for element in compilation_unit.elements() {
+ compile_compilation_unit_element(&element, &mut context);
+ }
+
+ let object = context.into_object();
+ let object_bytes = object.emit().map_err(CompileError::ObjectEmitFailed)?;
+
+ let destination_path = source_path.with_extension("o");
+ std::fs::write(destination_path, object_bytes).map_err(CompileError::WriteObjectFile)?;
+
+ Ok(())
+}
+
+fn compile_compilation_unit_element(
+ element: &PatioCompilationUnitElement,
+ context: &mut CompilerContext,
+) {
+ match element.value_compilation_unit_element_value_type() {
+ PatioCompilationUnitElementValue::PatioFunctionDefinition => {
+ let function_definition = element
+ .value_compilation_unit_element_value_as_patio_function_definition()
+ .unwrap();
+
+ compile_function_definition(&function_definition, context);
+ }
+
+ _ => todo!(
+ "Unsupported compilation unit element type: {}",
+ element.value_compilation_unit_element_value_type().0
+ ),
+ }
+}
+
+fn compile_function_definition(
+ function_definition: &PatioFunctionDefinition,
+ context: &mut CompilerContext,
+) {
+ let cranelift_signature = cranelift_signature(
+ function_definition.locals().iter(),
+ function_definition.return_type(),
+ context,
+ );
+
+ let cranelift_linkage = patio_visibility_to_cranelift_linkage(function_definition.visibility());
+
+ context.enter_function(function_definition.name(), cranelift_signature, cranelift_linkage);
+}
+
+fn cranelift_signature<'a>(
+ locals: impl Iterator<Item = PatioLocal<'a>>,
+ return_type: PatioType,
+ context: &CompilerContext,
+) -> cranelift::prelude::Signature {
+ let mut signature = cranelift::prelude::Signature::new(cranelift_codegen::isa::CallConv::SystemV);
+
+ for local in locals {
+ if local.local_type() == PatioLocalType::PARAM {
+ let param_type = patio_type_to_cranelift_type(local.type_(), context);
+ let param = cranelift::prelude::AbiParam::new(param_type);
+ signature.params.push(param);
+ }
+ }
+
+ if return_type.value_patio_type_type() != PatioTypeValue::PatioVoidType {
+ let cranelift_return_type = patio_type_to_cranelift_type(return_type, context);
+ let returns = cranelift::prelude::AbiParam::new(cranelift_return_type);
+ signature.returns.push(returns);
+ }
+
+ signature
+}
+
+fn patio_visibility_to_cranelift_linkage(visibility: PatioVisibility) -> cranelift_module::Linkage {
+ match visibility {
+ PatioVisibility::PRIVATE =>
+ cranelift_module::Linkage::Local,
+
+ PatioVisibility::PUBLIC =>
+ cranelift_module::Linkage::Export,
+
+ _ =>
+ panic!("Unsupported visibility: {}", visibility.0)
+ }
+}
+
+fn patio_type_to_cranelift_type(
+ patio_type: PatioType,
+ context: &CompilerContext,
+) -> cranelift::prelude::Type {
+ match patio_type.value_patio_type_type() {
+ PatioTypeValue::PatioBoolType =>
+ cranelift::prelude::Type::int(1).unwrap(),
+
+ PatioTypeValue::PatioByteType =>
+ cranelift::prelude::Type::int(8).unwrap(),
+
+ PatioTypeValue::PatioDataPtrType =>
+ cranelift::prelude::Type::triple_pointer_type(&context.target_triple),
+
+ PatioTypeValue::PatioFunctionPtrType =>
+ cranelift::prelude::Type::triple_pointer_type(&context.target_triple),
+
+ PatioTypeValue::PatioIntType =>
+ // TODO: this assumes that pointers are at least as wide as i32s:
+ // probably safe, but we should check.
+ // TODO: this relies on cranelift pointer types just being integers.
+ cranelift::prelude::Type::triple_pointer_type(&context.target_triple),
+
+ PatioTypeValue::PatioInt32Type =>
+ cranelift::prelude::Type::int(32).unwrap(),
+
+ PatioTypeValue::PatioVoidType =>
+ todo!(),
+
+ _ =>
+ todo!(),
+ }
+}
diff --git a/patio-to-cranelift/src/main.rs b/patio-to-cranelift/src/main.rs
index 6fb81b61..6bb533a1 100644
--- a/patio-to-cranelift/src/main.rs
+++ b/patio-to-cranelift/src/main.rs
@@ -1,20 +1,6 @@
-use std::{path::{Path, PathBuf}, str::FromStr};
+use std::path::PathBuf;
use clap::Parser;
-use cranelift::prelude::{Configurable, FunctionBuilder, FunctionBuilderContext};
-use cranelift_codegen::ir::{Function, UserFuncName};
-use cranelift_module::Module;
-use cranelift_object::ObjectModule;
-
-use crate::patio_generated::patio::{PatioCompilationUnitElement, PatioCompilationUnitElementValue, PatioFunctionDefinition, PatioLocal, PatioLocalType, PatioType, PatioTypeValue, PatioVisibility};
-
-#[allow(dead_code, unused_imports)]
-#[path = "./gen/patio_generated.rs"]
-mod patio_generated;
-
-#[allow(dead_code, unused_imports)]
-#[path = "./gen/sources_generated.rs"]
-mod sources_generated;
/// Compile Patio compilation units into object files using Cranelift.
#[derive(clap::Parser, Debug)]
@@ -31,222 +17,8 @@ fn main() {
let args = Args::parse();
for source_path in args.source_paths {
- compile(&source_path, &args.target).unwrap();
+ patio_to_cranelift::compile(&source_path, &args.target).unwrap();
}
}
-fn compile(source_path: &Path, target_str: &str) -> Result<(), CompileError> {
- let source_bytes = std::fs::read(source_path).map_err(CompileError::ReadSourceBytes)?;
-
- let compilation_unit = patio_generated::patio::root_as_patio_compilation_unit(&source_bytes)
- .map_err(CompileError::InvalidFlatbuffer)?;
-
- let target_triple = target_lexicon::Triple::from_str(target_str)
- .map_err(CompileError::ParseTargetTriple)?;
-
- let object_builder = cranelift_object::ObjectBuilder::new(
- isa(target_triple.clone())?,
- source_path.as_os_str().as_encoded_bytes(),
- cranelift_module::default_libcall_names()
- ).map_err(CompileError::ObjectBuilderNewFailed)?;
- let module = cranelift_object::ObjectModule::new(object_builder);
- let mut context = CompilerContext::new(module, target_triple);
-
- for element in compilation_unit.elements() {
- compile_compilation_unit_element(&element, &mut context);
- }
-
- let object = context.module.finish();
- let object_bytes = object.emit().map_err(CompileError::ObjectEmitFailed)?;
-
- let destination_path = source_path.with_extension("o");
- std::fs::write(destination_path, object_bytes).map_err(CompileError::WriteObjectFile)?;
-
- Ok(())
-}
-
-fn isa(target: target_lexicon::Triple) -> Result<std::sync::Arc<dyn cranelift::prelude::isa::TargetIsa>, CompileError> {
- let mut flags_builder = cranelift::prelude::settings::builder();
- flags_builder.enable("is_pic").map_err(CompileError::EnablePicFailed)?;
- cranelift::prelude::isa::lookup(target)
- .map_err(CompileError::LookupIsa)?
- .finish(cranelift::prelude::settings::Flags::new(flags_builder))
- .map_err(CompileError::BuildIsa)
-}
-
-fn compile_compilation_unit_element(
- element: &PatioCompilationUnitElement,
- context: &mut CompilerContext,
-) {
- match element.value_compilation_unit_element_value_type() {
- PatioCompilationUnitElementValue::PatioFunctionDefinition => {
- let function_definition = element
- .value_compilation_unit_element_value_as_patio_function_definition()
- .unwrap();
-
- compile_function_definition(&function_definition, context);
- }
-
- _ => todo!(
- "Unsupported compilation unit element type: {}",
- element.value_compilation_unit_element_value_type().0
- ),
- }
-}
-
-fn compile_function_definition(
- function_definition: &PatioFunctionDefinition,
- context: &mut CompilerContext,
-) {
- let signature = cranelift_signature(
- function_definition.locals().iter(),
- function_definition.return_type(),
- context,
- );
-
- let mut func = Function::with_name_signature(context.func_name(), signature.clone());
-
- let mut func_builder_context = FunctionBuilderContext::new();
- let _function_builder = FunctionBuilder::new(&mut func, &mut func_builder_context);
-
- let func_id = context.module.declare_function(
- function_definition.name(),
- patio_visibility_to_cranelift_linkage(function_definition.visibility()),
- &signature,
- ).unwrap();
-
- context.enter_function(func);
- context.module.define_function(func_id, &mut context.cranelift_context).unwrap();
-}
-
-fn cranelift_signature<'a>(
- locals: impl Iterator<Item = PatioLocal<'a>>,
- return_type: PatioType,
- context: &CompilerContext,
-) -> cranelift::prelude::Signature {
- let mut signature = cranelift::prelude::Signature::new(cranelift_codegen::isa::CallConv::SystemV);
-
- for local in locals {
- if local.local_type() == PatioLocalType::PARAM {
- let param_type = patio_type_to_cranelift_type(local.type_(), context);
- let param = cranelift::prelude::AbiParam::new(param_type);
- signature.params.push(param);
- }
- }
-
- if return_type.value_patio_type_type() != PatioTypeValue::PatioVoidType {
- let cranelift_return_type = patio_type_to_cranelift_type(return_type, context);
- let returns = cranelift::prelude::AbiParam::new(cranelift_return_type);
- signature.returns.push(returns);
- }
-
- signature
-}
-
-fn patio_visibility_to_cranelift_linkage(visibility: PatioVisibility) -> cranelift_module::Linkage {
- match visibility {
- PatioVisibility::PRIVATE =>
- cranelift_module::Linkage::Local,
- PatioVisibility::PUBLIC =>
- cranelift_module::Linkage::Export,
-
- _ =>
- panic!("Unsupported visibility: {}", visibility.0)
- }
-}
-
-fn patio_type_to_cranelift_type(
- patio_type: PatioType,
- context: &CompilerContext,
-) -> cranelift::prelude::Type {
- match patio_type.value_patio_type_type() {
- PatioTypeValue::PatioBoolType =>
- cranelift::prelude::Type::int(1).unwrap(),
-
- PatioTypeValue::PatioByteType =>
- cranelift::prelude::Type::int(8).unwrap(),
-
- PatioTypeValue::PatioDataPtrType =>
- cranelift::prelude::Type::triple_pointer_type(&context.target_triple),
-
- PatioTypeValue::PatioFunctionPtrType =>
- cranelift::prelude::Type::triple_pointer_type(&context.target_triple),
-
- PatioTypeValue::PatioIntType =>
- // TODO: this assumes that pointers are at least as wide as i32s:
- // probably safe, but we should check.
- // TODO: this relies on cranelift pointer types just being integers.
- cranelift::prelude::Type::triple_pointer_type(&context.target_triple),
-
- PatioTypeValue::PatioInt32Type =>
- cranelift::prelude::Type::int(32).unwrap(),
-
- PatioTypeValue::PatioVoidType =>
- todo!(),
-
- _ =>
- todo!(),
- }
-}
-
-struct CompilerContext {
- cranelift_context: cranelift_codegen::Context,
- module: ObjectModule,
- next_func_name_index: u32,
- target_triple: target_lexicon::Triple,
-}
-
-impl CompilerContext {
- fn new(module: ObjectModule, target_triple: target_lexicon::Triple) -> Self {
- CompilerContext {
- cranelift_context: cranelift_codegen::Context::new(),
- module,
- next_func_name_index: 0,
- target_triple,
- }
- }
-
- /// Generate a function name for Cranelift.
- fn func_name(&mut self) -> UserFuncName {
- // Cranelift doesn't assign any particular meaning to the values, so we
- // just use an incrementing index in a constant namespace.
- let name = UserFuncName::user(0, self.next_func_name_index);
- self.next_func_name_index += 1;
- name
- }
-
- fn enter_function(&mut self, cranelift_function: cranelift_codegen::ir::Function) {
- self.cranelift_context.func = cranelift_function;
- }
-}
-
-#[derive(Debug)]
-enum CompileError {
- /// Reading the bytes of the patio source file failed.
- ReadSourceBytes(#[allow(dead_code)] std::io::Error),
-
- /// The bytes of the patio source were an invalid flatbuffer.
- InvalidFlatbuffer(#[allow(dead_code)] flatbuffers::InvalidFlatbuffer),
-
- /// Enabling PIC failed.
- EnablePicFailed(#[allow(dead_code)] cranelift_codegen::settings::SetError),
-
- /// Could not parse the target triple.
- ParseTargetTriple(#[allow(dead_code)] target_lexicon::ParseError),
-
- /// The ISA for the target triple could not be found.
- LookupIsa(#[allow(dead_code)] cranelift_codegen::isa::LookupError),
-
- /// Building the ISA failed.
- BuildIsa(#[allow(dead_code)] cranelift_codegen::CodegenError),
-
- /// Create a new object builder failed.
- ObjectBuilderNewFailed(#[allow(dead_code)] cranelift_module::ModuleError),
-
- /// Emitting the bytes for an object failed.
- ObjectEmitFailed(#[allow(dead_code)] cranelift_object::object::write::Error),
-
- /// Writing the object file to disk failed.
- WriteObjectFile(#[allow(dead_code)] std::io::Error),
-}