diff options
| author | Michael Williamson <mike@zwobble.org> | 2026-03-30 21:19:26 +0100 |
|---|---|---|
| committer | Michael Williamson <mike@zwobble.org> | 2026-03-30 21:19:26 +0100 |
| commit | e41262b82bcdfdb7857eab89f045cb917ab11305 (patch) | |
| tree | 951640272103073470920eb79a781e3a5d02179b | |
| parent | 7bdf8fc6b0df69f70929b63019b06a72fda25395 (diff) | |
Split up code
| -rw-r--r-- | patio-to-cranelift/src/context.rs | 74 | ||||
| -rw-r--r-- | patio-to-cranelift/src/errors.rs | 31 | ||||
| -rw-r--r-- | patio-to-cranelift/src/lib.rs | 144 | ||||
| -rw-r--r-- | patio-to-cranelift/src/main.rs | 232 |
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), -} |
