use std::path::Path; use cranelift::prelude::InstBuilder; use crate::{ codegen::{ context::{CompilerContext, CompilerFunctionContext, Mutability}, types::patio_type_to_cranelift_type, }, errors::CompileError, patio::{ compilation_unit::{ PatioCompilationUnit, PatioCompilationUnitElement, PatioDataElementImmutableDefinition, PatioDataElementMutableDeclaration, PatioDataElementMutableDefinition, PatioFunctionDeclaration, PatioFunctionDefinition, PatioLocal, PatioLocalType, PatioVisibility, }, instructions::{ PatioBlock, PatioBoolToInt, PatioCall, PatioCallPointer, PatioDataPtrToInt, PatioIf, PatioInstruction, PatioIntAdd, PatioIntEquals, PatioIntNotEqual, PatioIntSub, PatioIntToBool, PatioIntToDataPtr, PatioLocalStore, PatioLoop, PatioLoopBreak, PatioLoopContinue, PatioMemoryLoad, PatioMemoryStore, PatioReturn, PatioSwitch, PatioUnreachable, PatioVarLoad, PatioVarStore, }, operands::{PatioLocalRef, PatioOperand}, types::PatioType, values::{PatioDataPtrValue, PatioIntValue}, }, }; mod context; mod types; mod values; pub(crate) fn compile_compilation_unit( compilation_unit: PatioCompilationUnit, main: Option<&str>, source_path: &Path, target_triple: target_lexicon::Triple, ) -> Result, CompileError> { let mut context = CompilerContext::for_object(source_path, target_triple)?; for element in &compilation_unit.elements { declare_compilation_unit_element(&element, &mut context); } for element in &compilation_unit.elements { define_compilation_unit_element(&element, &mut context); } if let Some(main) = main { let mut main_signature = new_cranelift_signature(); main_signature .returns .push(cranelift_codegen::ir::AbiParam::new( cranelift_codegen::ir::types::I32, )); context.declare_function("main", cranelift_module::Linkage::Export, &main_signature); context.enter_function("main", main_signature, |function_context| { let func_ref = function_context.func_id(main); function_context.function_builder.ins().call(func_ref, &[]); // TODO: we ignore the return value and just return 0, which is fine // in practice since we use exit() for non-zero exit codes. However, // we should clarify this behaviour in shid-to-patio, and other // patio backends. let exit_code = function_context .function_builder .ins() .iconst(cranelift_codegen::ir::types::I32, 0); function_context .function_builder .ins() .return_(&[exit_code]); }); } let object = context.into_object(); object.emit().map_err(CompileError::ObjectEmitFailed) } fn declare_compilation_unit_element( element: &PatioCompilationUnitElement, context: &mut CompilerContext, ) { match element { PatioCompilationUnitElement::DataElementImmutableDefinition( data_element_immutable_definition, ) => { declare_data_element_immutable_definition(data_element_immutable_definition, context); } PatioCompilationUnitElement::DataElementMutableDeclaration( data_element_mutable_declaration, ) => { declare_data_element_mutable_declaration(data_element_mutable_declaration, context); } PatioCompilationUnitElement::DataElementMutableDefinition( data_element_mutable_definition, ) => { declare_data_element_mutable_definition(data_element_mutable_definition, context); } PatioCompilationUnitElement::FunctionDeclaration(function_declaration) => { declare_function_declaration(function_declaration, context); } PatioCompilationUnitElement::FunctionDefinition(function_definition) => { declare_function_definition(function_definition, context); } } } fn define_compilation_unit_element( element: &PatioCompilationUnitElement, context: &mut CompilerContext, ) { match element { PatioCompilationUnitElement::DataElementImmutableDefinition( data_element_immutable_definition, ) => { define_data_element_immutable_definition(data_element_immutable_definition, context); } PatioCompilationUnitElement::DataElementMutableDeclaration(_) => {} PatioCompilationUnitElement::DataElementMutableDefinition( data_element_mutable_definition, ) => { define_data_element_mutable_definition(data_element_mutable_definition, context); } PatioCompilationUnitElement::FunctionDeclaration(_) => {} PatioCompilationUnitElement::FunctionDefinition(function_definition) => { define_function_definition(&function_definition, context); } } } fn declare_data_element_immutable_definition( data_element_immutable_definition: &PatioDataElementImmutableDefinition, context: &mut CompilerContext, ) { context.declare_data( &data_element_immutable_definition.name, patio_visibility_to_cranelift_linkage(data_element_immutable_definition.visibility), Mutability::Immutable, ); } fn define_data_element_immutable_definition( data_element_immutable_definition: &PatioDataElementImmutableDefinition, context: &mut CompilerContext, ) { let bytes = values::data_value_to_bytes( &data_element_immutable_definition.value, &context.target_triple, ); context.define_data(&data_element_immutable_definition.name, bytes); } fn declare_data_element_mutable_declaration( data_element_mutable_declaration: &PatioDataElementMutableDeclaration, context: &mut CompilerContext, ) { context.declare_data( &data_element_mutable_declaration.name, cranelift_module::Linkage::Import, Mutability::Mutable, ); } fn declare_data_element_mutable_definition( data_element_mutable_definition: &PatioDataElementMutableDefinition, context: &mut CompilerContext, ) { context.declare_data( &data_element_mutable_definition.name, patio_visibility_to_cranelift_linkage(data_element_mutable_definition.visibility), Mutability::Mutable, ); } fn define_data_element_mutable_definition( data_element_mutable_definition: &PatioDataElementMutableDefinition, context: &mut CompilerContext, ) { let bytes = values::data_value_to_bytes( &data_element_mutable_definition.initial_value, &context.target_triple, ); context.define_data(&data_element_mutable_definition.name, bytes); } fn declare_function_declaration( function_declaration: &PatioFunctionDeclaration, context: &mut CompilerContext, ) { let cranelift_signature = cranelift_signature( function_declaration.params.iter(), function_declaration.return_types.iter(), &context.target_triple, ); context.declare_function( &function_declaration.name, cranelift_module::Linkage::Import, &cranelift_signature, ); } fn declare_function_definition( function_definition: &PatioFunctionDefinition, context: &mut CompilerContext, ) { let cranelift_signature = cranelift_signature( function_definition.locals.iter(), function_definition.return_types.iter(), &context.target_triple, ); let cranelift_linkage = patio_visibility_to_cranelift_linkage(function_definition.visibility); context.declare_function( &function_definition.name, cranelift_linkage, &cranelift_signature, ); } fn define_function_definition( function_definition: &PatioFunctionDefinition, context: &mut CompilerContext, ) { let cranelift_signature = cranelift_signature( function_definition.locals.iter(), function_definition.return_types.iter(), &context.target_triple, ); context.enter_function( &function_definition.name, cranelift_signature, |function_context| { let block_params: Vec<_> = function_context .function_builder .block_params(function_context.current_block()) .iter() .copied() .collect(); let mut block_params_iter = block_params.iter(); for local in &function_definition.locals { let cranelift_type = types::patio_type_to_cranelift_type( local.type_, &function_context.target_triple, ); match local.local_type { PatioLocalType::Param => { function_context.declare_local(&local.name, cranelift_type); function_context .local_store(&local.name, *block_params_iter.next().unwrap()); } PatioLocalType::Val => { function_context.declare_local(&local.name, cranelift_type); } PatioLocalType::Var => { function_context.declare_var(&local.name, cranelift_type); } } } for instruction in &function_definition.body { compile_instruction(instruction, function_context) } }, ); } fn compile_instruction(instruction: &PatioInstruction, context: &mut CompilerFunctionContext<'_>) { match instruction { PatioInstruction::Block(block) => { compile_block(block, context); } PatioInstruction::BoolToInt(bool_to_int) => { compile_bool_to_int(bool_to_int, context); } PatioInstruction::Call(call) => { compile_call(call, context); } PatioInstruction::CallPointer(call_pointer) => { compile_call_pointer(call_pointer, context); } PatioInstruction::DataPtrToInt(data_ptr_to_int) => { compile_data_ptr_to_int(data_ptr_to_int, context); } PatioInstruction::If(if_) => { compile_if(if_, context); } PatioInstruction::IntAdd(int_add) => { compile_int_add(int_add, context); } PatioInstruction::IntEquals(int_equals) => { compile_int_equals(int_equals, context); } PatioInstruction::IntNotEqual(int_not_equal) => { compile_int_not_equal(int_not_equal, context); } PatioInstruction::IntSub(int_sub) => { compile_int_sub(int_sub, context); } PatioInstruction::IntToBool(int_to_bool) => { compile_int_to_bool(int_to_bool, context); } PatioInstruction::IntToDataPtr(int_to_data_ptr) => { compile_int_to_data_ptr(int_to_data_ptr, context); } PatioInstruction::LocalStore(local_store) => { compile_local_store(local_store, context); } PatioInstruction::Loop(loop_) => { compile_loop(loop_, context); } PatioInstruction::LoopBreak(loop_break) => { compile_loop_break(loop_break, context); } PatioInstruction::LoopContinue(loop_continue) => { compile_loop_continue(loop_continue, context); } PatioInstruction::MemoryLoad(memory_load) => { compile_memory_load(memory_load, context); } PatioInstruction::MemoryStore(memory_store) => { compile_memory_store(memory_store, context); } PatioInstruction::Return(return_) => { compile_return(return_, context); } PatioInstruction::Switch(switch) => { compile_switch(switch, context); } PatioInstruction::Unreachable(unreachable) => { compile_unreachable(unreachable, context); } PatioInstruction::VarLoad(var_load) => { compile_var_load(var_load, context); } PatioInstruction::VarStore(var_store) => { compile_var_store(var_store, context); } } } fn compile_block( block: &PatioBlock, context: &mut CompilerFunctionContext<'_>, ) -> Vec { for instruction in &block.body { compile_instruction(instruction, context); } block .results .iter() .map(|result| compile_operand(&result, context)) .collect() } fn compile_bool_to_int(bool_to_int: &PatioBoolToInt, context: &mut CompilerFunctionContext<'_>) { let cranelift_value = compile_operand(&bool_to_int.value, context); let cranelift_result = context.function_builder.ins().uextend( types::cranelift_int_type(&context.target_triple), cranelift_value, ); def_destination(&bool_to_int.destination, cranelift_result, context) } fn compile_call(call: &PatioCall, context: &mut CompilerFunctionContext<'_>) { let cranelift_args = compile_operands(call.args.iter(), context); let func_ref = context.func_id(&call.function_name); let cranelift_call = context .function_builder .ins() .call(func_ref, &cranelift_args); let cranelift_results = Vec::from(context.function_builder.inst_results(cranelift_call)); assert_eq!(cranelift_results.len(), call.destinations.len()); for (cranelift_result, destination) in cranelift_results.iter().zip(&call.destinations) { def_destination(destination, *cranelift_result, context); } } fn compile_call_pointer( call_pointer: &PatioCallPointer, context: &mut CompilerFunctionContext<'_>, ) { let mut cranelift_signature = new_cranelift_signature(); for arg in &call_pointer.args { cranelift_signature .params .push(cranelift_codegen::ir::AbiParam::new( patio_operand_to_cranelift_type(arg, context), )); } for destination in &call_pointer.destinations { cranelift_signature .returns .push(cranelift_codegen::ir::AbiParam::new( context.local_type(&destination.name), )); } let cranelift_signature_ref = context .function_builder .import_signature(cranelift_signature); let cranelift_function_pointer = compile_operand(&call_pointer.function_pointer, context); let cranelift_args = compile_operands(call_pointer.args.iter(), context); let cranelift_call = context.function_builder.ins().call_indirect( cranelift_signature_ref, cranelift_function_pointer, &cranelift_args, ); let cranelift_results = Vec::from(context.function_builder.inst_results(cranelift_call)); assert_eq!(cranelift_results.len(), call_pointer.destinations.len()); for (cranelift_result, destination) in cranelift_results.iter().zip(&call_pointer.destinations) { def_destination(destination, *cranelift_result, context); } } fn compile_data_ptr_to_int( data_ptr_to_int: &PatioDataPtrToInt, context: &mut CompilerFunctionContext<'_>, ) { let cranelift_value = compile_operand(&data_ptr_to_int.value, context); def_destination(&data_ptr_to_int.destination, cranelift_value, context); } fn compile_if(if_: &PatioIf, context: &mut CompilerFunctionContext<'_>) { let cranelift_condition = compile_operand(&if_.condition, context); let cranelift_then_block = context.create_block(); let cranelift_else_block = context.create_block(); let cranelift_end_block = context.create_block(); let cranelift_end_params: Vec<_> = if_ .destinations .iter() .map(|destination| { let cranelift_type = context.local_type(&destination.name); context .function_builder .append_block_param(cranelift_end_block, cranelift_type) }) .collect(); context.function_builder.ins().brif( cranelift_condition, cranelift_then_block, &[], cranelift_else_block, &[], ); context.seal_block(cranelift_then_block); context.seal_block(cranelift_else_block); context.switch_to_block(cranelift_then_block); let cranelift_then_results = compile_block(&if_.true_body, context); if context.is_reachable() { context.function_builder.ins().jump( cranelift_end_block, cranelift_block_args(cranelift_then_results).iter(), ); } context.switch_to_block(cranelift_else_block); let cranelift_else_results = compile_block(&if_.false_body, context); if context.is_reachable() { context.function_builder.ins().jump( cranelift_end_block, cranelift_block_args(cranelift_else_results).iter(), ); } context.seal_block(cranelift_end_block); context.switch_to_block(cranelift_end_block); for (destination, cranelift_end_param) in if_.destinations.iter().zip(cranelift_end_params) { def_destination(destination, cranelift_end_param, context); } } fn compile_int_add(int_add: &PatioIntAdd, context: &mut CompilerFunctionContext<'_>) { let cranelift_left = compile_operand(&int_add.left, context); let cranelift_right = compile_operand(&int_add.right, context); let cranelift_result = context .function_builder .ins() .iadd(cranelift_left, cranelift_right); def_destination(&int_add.destination, cranelift_result, context); } fn compile_int_equals(int_equals: &PatioIntEquals, context: &mut CompilerFunctionContext<'_>) { let cranelift_left = compile_operand(&int_equals.left, context); let cranelift_right = compile_operand(&int_equals.right, context); let cranelift_result = context.function_builder.ins().icmp( cranelift_codegen::ir::condcodes::IntCC::Equal, cranelift_left, cranelift_right, ); def_destination(&int_equals.destination, cranelift_result, context); } fn compile_int_not_equal( int_not_equal: &PatioIntNotEqual, context: &mut CompilerFunctionContext<'_>, ) { let cranelift_left = compile_operand(&int_not_equal.left, context); let cranelift_right = compile_operand(&int_not_equal.right, context); let cranelift_result = context.function_builder.ins().icmp( cranelift_codegen::ir::condcodes::IntCC::NotEqual, cranelift_left, cranelift_right, ); def_destination(&int_not_equal.destination, cranelift_result, context); } fn compile_int_sub(int_sub: &PatioIntSub, context: &mut CompilerFunctionContext<'_>) { let cranelift_left = compile_operand(&int_sub.left, context); let cranelift_right = compile_operand(&int_sub.right, context); let cranelift_result = context .function_builder .ins() .isub(cranelift_left, cranelift_right); def_destination(&int_sub.destination, cranelift_result, context); } fn compile_int_to_bool(int_to_bool: &PatioIntToBool, context: &mut CompilerFunctionContext<'_>) { let cranelift_value = compile_operand(&int_to_bool.value, context); let cranelift_result = context .function_builder .ins() .ireduce(types::CRANELIFT_BOOL_TYPE, cranelift_value); def_destination(&int_to_bool.destination, cranelift_result, context); } fn compile_int_to_data_ptr( int_to_data_ptr: &PatioIntToDataPtr, context: &mut CompilerFunctionContext<'_>, ) { let cranelift_value = compile_operand(&int_to_data_ptr.value, context); def_destination(&int_to_data_ptr.destination, cranelift_value, context); } fn compile_local_store(local_store: &PatioLocalStore, context: &mut CompilerFunctionContext<'_>) { let cranelift_value = compile_operand(&local_store.value, context); def_destination(&local_store.destination, cranelift_value, context); } fn compile_loop(loop_: &PatioLoop, context: &mut CompilerFunctionContext<'_>) { let cranelift_loop_block = context.create_block(); let cranelift_end_block = context.create_block(); context .function_builder .ins() .jump(cranelift_loop_block, []); context.switch_to_block(cranelift_loop_block); context.enter_loop(cranelift_loop_block, cranelift_end_block); for instruction in &loop_.body { compile_instruction(instruction, context); } context.exit_loop(); context.seal_block(cranelift_loop_block); if context.is_reachable() { context.function_builder.ins().jump(cranelift_end_block, []); } context.seal_block(cranelift_end_block); context.switch_to_block(cranelift_end_block); } fn compile_loop_break(_: &PatioLoopBreak, context: &mut CompilerFunctionContext<'_>) { let cranelift_loop_end_block = context.current_loop().end_block; context .function_builder .ins() .jump(cranelift_loop_end_block, []); context.mark_unreachable(); } fn compile_loop_continue(_: &PatioLoopContinue, context: &mut CompilerFunctionContext<'_>) { let cranelift_loop_start_block = context.current_loop().start_block; context .function_builder .ins() .jump(cranelift_loop_start_block, []); context.mark_unreachable(); } fn compile_memory_load(memory_load: &PatioMemoryLoad, context: &mut CompilerFunctionContext<'_>) { let value_layout = types::data_layout_element_at(&memory_load.layout, &memory_load.element_offsets); let value_type = types::data_layout_to_type(value_layout); let cranelift_type = types::patio_type_to_cranelift_type(value_type, &context.target_triple); let cranelift_pointer = compile_operand(&memory_load.pointer, context); let cranelift_flags = cranelift_codegen::ir::MemFlags::new(); let byte_offset = types::data_layout_byte_offset( &memory_load.layout, &memory_load.element_offsets, &context.target_triple, ); let cranelift_offset = cranelift_codegen::ir::immediates::Offset32::new(byte_offset.try_into().unwrap()); let cranelift_value = context.function_builder.ins().load( cranelift_type, cranelift_flags, cranelift_pointer, cranelift_offset, ); def_destination(&memory_load.destination, cranelift_value, context); } fn compile_memory_store( memory_store: &PatioMemoryStore, context: &mut CompilerFunctionContext<'_>, ) { let cranelift_value = compile_operand(&memory_store.value, context); let cranelift_pointer = compile_operand(&memory_store.pointer, context); let cranelift_flags = cranelift_codegen::ir::MemFlags::new(); let byte_offset = types::data_layout_byte_offset( &memory_store.layout, &memory_store.element_offsets, &context.target_triple, ); let cranelift_offset = cranelift_codegen::ir::immediates::Offset32::new(byte_offset.try_into().unwrap()); context.function_builder.ins().store( cranelift_flags, cranelift_value, cranelift_pointer, cranelift_offset, ); } fn compile_return(return_: &PatioReturn, context: &mut CompilerFunctionContext<'_>) { let cranelift_values: Vec<_> = return_ .values .iter() .map(|value| compile_operand(value, context)) .collect(); context.function_builder.ins().return_(&cranelift_values); } fn compile_switch(switch: &PatioSwitch, context: &mut CompilerFunctionContext<'_>) { let cranelift_value = compile_operand(&switch.value, context); // Create the various blocks. let default_block = context.create_block(); let case_blocks: Vec<_> = switch .cases .iter() .map(|_| context.create_block()) .collect(); let end_block = context.create_block(); let cranelift_end_params: Vec<_> = switch .destinations .iter() .map(|destination| { let cranelift_type = context.local_type(&destination.name); context .function_builder .append_block_param(end_block, cranelift_type) }) .collect(); // Create the jump itself. let mut cranelift_switch = cranelift::frontend::Switch::new(); for (case, case_block) in switch.cases.iter().zip(&case_blocks) { cranelift_switch.set_entry(case.value.into(), *case_block); } cranelift_switch.emit( &mut context.function_builder, cranelift_value, default_block, ); for case_block in &case_blocks { context.seal_block(*case_block); } context.seal_block(default_block); // Compile the case and default blocks. for (case, case_block) in switch.cases.iter().zip(&case_blocks) { context.switch_to_block(*case_block); let end_args = compile_block(&case.body, context); if context.is_reachable() { context .function_builder .ins() .jump(end_block, &cranelift_block_args(end_args)); } } { context.switch_to_block(default_block); let end_args = compile_block(&switch.default_body, context); if context.is_reachable() { context .function_builder .ins() .jump(end_block, &cranelift_block_args(end_args)); } } context.seal_block(end_block); context.switch_to_block(end_block); for (destination, cranelift_end_param) in switch.destinations.iter().zip(cranelift_end_params) { def_destination(destination, cranelift_end_param, context); } } fn compile_unreachable(_: &PatioUnreachable, context: &mut CompilerFunctionContext<'_>) { context.function_builder.ins().trap(trap_codes::UNREACHABLE); context.mark_unreachable(); } fn compile_var_load(var_load: &PatioVarLoad, context: &mut CompilerFunctionContext<'_>) { let cranelift_variable = context.var(&var_load.var.name); let cranelift_value = context.function_builder.use_var(cranelift_variable); def_destination(&var_load.destination, cranelift_value, context); } fn compile_var_store(var_store: &PatioVarStore, context: &mut CompilerFunctionContext<'_>) { let cranelift_variable = context.var(&var_store.var.name); let cranelift_value = compile_operand(&var_store.value, context); context .function_builder .def_var(cranelift_variable, cranelift_value); } fn compile_operands<'a>( operands: impl Iterator, context: &mut CompilerFunctionContext<'_>, ) -> Vec { operands .map(|operand| compile_operand(operand, context)) .collect() } fn compile_operand( operand: &PatioOperand, context: &mut CompilerFunctionContext<'_>, ) -> cranelift::prelude::Value { match operand { PatioOperand::BoolConst(value) => { let integer_value = if *value { 1 } else { 0 }; context .function_builder .ins() .iconst(types::CRANELIFT_BOOL_TYPE, integer_value) } PatioOperand::DataPtrConst(value) => compile_data_ptr_value(&value, context), PatioOperand::FunctionPtrConst(name) => { let func_ref = context.func_id(name); let function_ptr_type = context.pointer_type(); context .function_builder .ins() .func_addr(function_ptr_type, func_ref) } PatioOperand::IntConst(value) => { let cranelift_int_type = types::cranelift_int_type(&context.target_triple); compile_int_value(&value, cranelift_int_type, context) } PatioOperand::Int32Const(value) => { compile_int_value(&value, types::CRANELIFT_INT_32_TYPE, context) } PatioOperand::LocalRef(local_ref) => context.local_load(&local_ref.name), _ => { todo!("Unhandled operand: {operand:?}",) } } } fn compile_data_ptr_value( data_ptr_value: &PatioDataPtrValue, context: &mut CompilerFunctionContext<'_>, ) -> cranelift_codegen::ir::Value { let data_ptr_type = context.pointer_type(); match data_ptr_value { PatioDataPtrValue::DataElement { name } => { let global_value = context.data_ptr(name); context .function_builder .ins() .global_value(data_ptr_type, global_value) } PatioDataPtrValue::Null => context.function_builder.ins().iconst(data_ptr_type, 0), } } fn compile_int_value( int_value: &PatioIntValue, cranelift_type: cranelift::prelude::Type, context: &mut CompilerFunctionContext<'_>, ) -> cranelift::prelude::Value { let value = values::eval_int_value(int_value, &context.target_triple); context .function_builder .ins() .iconst(cranelift_type, i64::from(value)) } fn cranelift_block_args( values: impl IntoIterator, ) -> Vec { values .into_iter() .map(|cranelift_result| cranelift_codegen::ir::BlockArg::Value(cranelift_result)) .collect() } fn def_destination( destination: &PatioLocalRef, value: cranelift_codegen::ir::Value, context: &mut CompilerFunctionContext<'_>, ) { context.local_store(&destination.name, value); } fn new_cranelift_signature() -> cranelift::prelude::Signature { cranelift_codegen::ir::Signature::new(cranelift_codegen::isa::CallConv::SystemV) } fn cranelift_signature<'a>( locals: impl Iterator, return_types: impl Iterator, target_triple: &target_lexicon::Triple, ) -> cranelift::prelude::Signature { let mut signature = new_cranelift_signature(); for local in locals { if local.local_type == PatioLocalType::Param { let param_type = types::patio_type_to_cranelift_type(local.type_, &target_triple); let param = cranelift::prelude::AbiParam::new(param_type); signature.params.push(param); } } for return_type in return_types { let cranelift_return_type = types::patio_type_to_cranelift_type(*return_type, &target_triple); 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, } } fn patio_operand_to_cranelift_type( operand: &PatioOperand, context: &mut CompilerFunctionContext<'_>, ) -> cranelift_codegen::ir::Type { match operand { PatioOperand::BoolConst(_) => { patio_type_to_cranelift_type(PatioType::Bool, &context.target_triple) } PatioOperand::ByteConst(_) => { patio_type_to_cranelift_type(PatioType::Byte, &context.target_triple) } PatioOperand::DataPtrConst(_) => { patio_type_to_cranelift_type(PatioType::DataPtr, &context.target_triple) } PatioOperand::FunctionPtrConst(_) => { patio_type_to_cranelift_type(PatioType::FunctionPtr, &context.target_triple) } PatioOperand::IntConst(_) => { patio_type_to_cranelift_type(PatioType::Int, &context.target_triple) } PatioOperand::Int32Const(_) => { patio_type_to_cranelift_type(PatioType::Int32, &context.target_triple) } PatioOperand::LocalRef(local_ref) => context.local_type(&local_ref.name), } } mod trap_codes { pub(super) const UNREACHABLE: cranelift_codegen::ir::TrapCode = cranelift_codegen::ir::TrapCode::unwrap_user(1); }