#![feature(if_let_guard)] #![feature(slice_take)] use { core::panic, cranelift_codegen::{ self as cc, CodegenError, Final, FinalizedMachReloc, MachBufferFinalized, ir::{self as cir, InstBuilder, MemFlags, TrapCode, UserExternalName, condcodes}, isa::{LookupError, TargetIsa}, settings::{Configurable, SetError}, }, cranelift_frontend::{self as cf, FunctionBuilder}, cranelift_module::{self as cm, Module, ModuleError}, hblang::{ lexer::TokenKind, nodes::{self as hbnodes}, ty as hbty, utils::{self as hbutils, Ent, EntVec}, }, std::{ collections::HashSet, fmt::{Display, Write}, ops::Range, }, }; mod x86_64; pub struct Backend { ctx: cc::Context, dt_ctx: cm::DataDescription, fb_ctx: cf::FunctionBuilderContext, module: Option<cranelift_object::ObjectModule>, ctrl_plane: cc::control::ControlPlane, funcs: Functions, globals: EntVec<hbty::Global, Global>, asm: Assembler, } impl Backend { pub fn new(triple: target_lexicon::Triple, flags: &str) -> Result<Self, BackendCreationError> { Ok(Self { ctx: cc::Context::new(), dt_ctx: cm::DataDescription::new(), fb_ctx: cf::FunctionBuilderContext::default(), ctrl_plane: cc::control::ControlPlane::default(), module: cranelift_object::ObjectModule::new(cranelift_object::ObjectBuilder::new( cc::isa::lookup(triple)?.finish(cc::settings::Flags::new({ let mut bl = cc::settings::builder(); for (k, v) in flags.split(',').filter_map(|s| s.split_once('=')) { bl.set(k, v).map_err(|err| BackendCreationError::InvalidFlag { key: k.to_owned(), value: v.to_owned(), err, })?; } bl }))?, "main", cm::default_libcall_names(), )?) .into(), funcs: Default::default(), globals: Default::default(), asm: Default::default(), }) } } impl hblang::backend::Backend for Backend { fn triple(&self) -> String { self.module.as_ref().unwrap().isa().triple().to_string() } fn assemble_reachable( &mut self, from: hbty::Func, types: &hbty::Types, files: &hbutils::EntSlice<hbty::Module, hblang::parser::Ast>, to: &mut Vec<u8>, ) -> hblang::backend::AssemblySpec { debug_assert!(self.asm.frontier.is_empty()); debug_assert!(self.asm.funcs.is_empty()); debug_assert!(self.asm.globals.is_empty()); let mut module = self.module.take().expect("backend can assemble only once"); fn clif_name_to_ty(name: UserExternalName) -> hbty::Id { match name.namespace { 0 => hbty::Kind::Func(hbty::Func::new(name.index as _)), 1 => hbty::Kind::Global(hbty::Global::new(name.index as _)), _ => unreachable!(), } .compress() } self.globals.shadow(types.ins.globals.len()); let mut seen_names = HashSet::new(); self.asm.frontier.push(from.into()); while let Some(itm) = self.asm.frontier.pop() { match itm.expand() { hbty::Kind::Func(func) => { let fd = &types.ins.funcs[func]; if fd.is_import { self.funcs.headers.shadow(func.index() + 1); } let fuc = &mut self.funcs.headers[func]; let file = &files[fd.file]; if fuc.module_id.is_some() { continue; } self.asm.frontier.extend( fuc.external_names.clone().map(|r| { clif_name_to_ty(self.funcs.external_names[r as usize].clone()) }), ); self.asm.name.clear(); if func == from { self.asm.name.push_str("main"); } else if fd.is_import { self.asm.name.push_str(file.ident_str(fd.name)); } else { self.asm.name.push_str(hblang::strip_cwd(&file.path)); self.asm.name.push('.'); if fd.parent != hbty::Id::from(fd.file) { write!( self.asm.name, "{}", hbty::Display::new(types, files, fd.parent) ) .unwrap(); } self.asm.name.push_str(file.ident_str(fd.name)); if fd.is_generic { let mut args = fd.sig.args.args(); self.asm.name.push('('); while let Some(arg) = args.next(types) { if let hbty::Arg::Type(ty) = arg { write!( self.asm.name, "{},", hbty::Display::new(types, files, ty) ) .unwrap(); } } self.asm.name.pop().unwrap(); self.asm.name.push(')'); } } let linkage = if func == from { cm::Linkage::Export } else if fd.is_import { cm::Linkage::Import } else { cm::Linkage::Local }; build_signature( module.isa().default_call_conv(), fd.sig, types, &mut self.ctx.func.signature, &mut vec![], ); debug_assert!(seen_names.insert(self.asm.name.clone()), "{}", self.asm.name); fuc.module_id = Some( module .declare_function(&self.asm.name, linkage, &self.ctx.func.signature) .unwrap(), ); if !fd.is_import { self.asm.funcs.push(func); } } hbty::Kind::Global(glob) => { if self.globals[glob].module_id.is_some() { continue; } self.asm.globals.push(glob); self.asm.name.clear(); let mutable = if types.ins.globals[glob].file == Default::default() { writeln!(self.asm.name, "anon{}", glob.index()).unwrap(); false } else { let file = &files[types.ins.globals[glob].file]; self.asm.name.push_str(hblang::strip_cwd(&file.path)); self.asm.name.push('.'); self.asm.name.push_str(file.ident_str(types.ins.globals[glob].name)); true }; self.globals[glob].module_id = Some( module .declare_data(&self.asm.name, cm::Linkage::Local, mutable, false) .unwrap(), ); } _ => unreachable!(), } } for &func in &self.asm.funcs { let fuc = &self.funcs.headers[func]; assert!(!types.ins.funcs[func].is_import); debug_assert!(!fuc.code.is_empty()); let names = &mut self.funcs.external_names [fuc.external_names.start as usize..fuc.external_names.end as usize]; self.ctx.func.clear(); names.iter().for_each(|nm| { let mut nm = nm.clone(); if nm.namespace == 0 { nm.index = self.funcs.headers[hbty::Func::new(nm.index as _)] .module_id .unwrap() .as_u32(); } else { nm.index = self.globals[hbty::Global::new(nm.index as _)].module_id.unwrap().as_u32(); } let prev_len = self.ctx.func.params.user_named_funcs().len(); self.ctx.func.params.ensure_user_func_name(nm.clone()); debug_assert_ne!(self.ctx.func.params.user_named_funcs().len(), prev_len, "{}", nm); }); module .define_function_bytes( fuc.module_id.unwrap(), &self.ctx.func, fuc.alignment as _, &self.funcs.code[fuc.code.start as usize..fuc.code.end as usize], &self.funcs.relocs[fuc.relocs.start as usize..fuc.relocs.end as usize], ) .unwrap(); } for global in self.asm.globals.drain(..) { let glob = &self.globals[global]; self.dt_ctx.clear(); self.dt_ctx.define(types.ins.globals[global].data.clone().into()); module.define_data(glob.module_id.unwrap(), &self.dt_ctx).unwrap(); } module.finish().object.write_stream(to).unwrap(); hblang::backend::AssemblySpec { code_length: 0, data_length: 0, entry: 0 } } fn disasm<'a>( &'a self, _sluce: &[u8], _eca_handler: &mut dyn FnMut(&mut &[u8]), _types: &'a hbty::Types, _files: &'a hbutils::EntSlice<hbty::Module, hblang::parser::Ast>, _output: &mut String, ) -> Result<(), std::boxed::Box<dyn core::error::Error + Send + Sync + 'a>> { unimplemented!() } fn emit_body( &mut self, id: hbty::Func, nodes: &hbnodes::Nodes, tys: &hbty::Types, files: &hbutils::EntSlice<hbty::Module, hblang::parser::Ast>, ) { let isa = self.module.as_ref().unwrap().isa(); let mut lens = vec![]; let stack_ret = build_signature( isa.default_call_conv(), tys.ins.funcs[id].sig, tys, &mut self.ctx.func.signature, &mut lens, ); FuncBuilder { bl: FunctionBuilder::new(&mut self.ctx.func, &mut self.fb_ctx), isa, nodes, tys, files, values: &mut vec![None; nodes.len()], arg_lens: &lens, stack_ret, } .build(tys.ins.funcs[id].sig); self.ctx.func.name = cir::UserFuncName::User(cir::UserExternalName { namespace: 0, index: id.index() as _ }); //std::eprintln!("{}", self.ctx.func.display()); self.ctx.compile(isa, &mut self.ctrl_plane).unwrap(); let code = self.ctx.compiled_code().unwrap(); self.funcs.push(id, &self.ctx.func, &code.buffer); self.ctx.clear(); } } fn build_signature( call_conv: cc::isa::CallConv, sig: hbty::Sig, types: &hbty::Types, signature: &mut cir::Signature, arg_meta: &mut Vec<AbiMeta>, ) -> bool { signature.clear(call_conv); match call_conv { cc::isa::CallConv::SystemV => { x86_64::build_systemv_signature(sig, types, signature, arg_meta) } _ => todo!(), } } #[derive(Clone, Copy)] struct AbiMeta { trough_mem: bool, arg_count: usize, } struct FuncBuilder<'a, 'b> { bl: cf::FunctionBuilder<'b>, isa: &'a dyn TargetIsa, nodes: &'a hbnodes::Nodes, tys: &'a hbty::Types, files: &'a hbutils::EntSlice<hbty::Module, hblang::parser::Ast>, values: &'b mut [Option<Result<cir::Value, cir::Block>>], arg_lens: &'a [AbiMeta], stack_ret: bool, } impl FuncBuilder<'_, '_> { pub fn build(mut self, sig: hbty::Sig) { let entry = self.bl.create_block(); self.bl.append_block_params_for_function_params(entry); self.bl.switch_to_block(entry); let mut arg_vals = &self.bl.block_params(entry).to_vec()[..]; if self.stack_ret { let ret_ptr = *arg_vals.take_first().unwrap(); self.values[hbnodes::MEM as usize] = Some(Ok(ret_ptr)); } let Self { nodes, tys, .. } = self; let mut parama_len = self.arg_lens[1..].iter(); let mut typs = sig.args.args(); let mut args = nodes[hbnodes::VOID].outputs[hbnodes::ARG_START..].iter(); while let Some(aty) = typs.next(tys) { let hbty::Arg::Value(ty) = aty else { continue }; let abi_meta = parama_len.next().unwrap(); let &arg = args.next().unwrap(); if !abi_meta.trough_mem && ty.is_aggregate(tys) { let slot = self.bl.create_sized_stack_slot(cir::StackSlotData { kind: cir::StackSlotKind::ExplicitSlot, size: self.tys.size_of(ty), align_shift: self.tys.align_of(ty).ilog2() as _, }); let loc = arg_vals.take(..abi_meta.arg_count).unwrap(); assert!(loc.len() <= 2, "NEED handling"); let align = loc.iter().map(|&p| self.bl.func.dfg.value_type(p).bytes()).max().unwrap(); let mut offset = 0i32; for &v in loc { self.bl.ins().stack_store(v, slot, offset); offset += align as i32; } self.values[arg as usize] = Some(Ok(self.bl.ins().stack_addr(cir::types::I64, slot, 0))) } else { let loc = arg_vals.take(..abi_meta.arg_count).unwrap(); debug_assert_eq!(loc.len(), 1); self.values[arg as usize] = Some(Ok(loc[0])); } } self.values[hbnodes::ENTRY as usize] = Some(Err(entry)); self.emit_node(hbnodes::VOID, hbnodes::VOID); self.bl.finalize(); } fn value_of(&self, nid: hbnodes::Nid) -> cir::Value { self.values[nid as usize].unwrap_or_else(|| panic!("{:?}", self.nodes[nid])).unwrap() } fn block_of(&self, nid: hbnodes::Nid) -> cir::Block { self.values[nid as usize].unwrap().unwrap_err() } fn close_block(&mut self, nid: hbnodes::Nid) { if matches!(self.nodes[nid].kind, hbnodes::Kind::Loop) { return; } self.bl.seal_block(self.block_of(nid)); } fn emit_node(&mut self, nid: hbnodes::Nid, block: hbnodes::Nid) { use hbnodes::*; let mut args = vec![]; if matches!(self.nodes[nid].kind, Kind::Region | Kind::Loop) { let side = 1 + self.values[nid as usize].is_some() as usize; for &o in self.nodes[nid].outputs.iter() { if self.nodes[o].is_data_phi() { args.push(self.value_of(self.nodes[o].inputs[side])); } } match (self.nodes[nid].kind, self.values[nid as usize]) { (Kind::Loop, Some(blck)) => { self.bl.ins().jump(blck.unwrap_err(), &args); self.bl.seal_block(blck.unwrap_err()); self.close_block(block); return; } (Kind::Region, None) => { let next = self.bl.create_block(); for &o in self.nodes[nid].outputs.iter() { if self.nodes[o].is_data_phi() { self.values[o as usize] = Some(Ok(self .bl .append_block_param(next, self.nodes[o].ty.to_clif(self.tys)))); } } self.bl.ins().jump(next, &args); self.close_block(block); self.values[nid as usize] = Some(Err(next)); return; } _ => {} } } let node = &self.nodes[nid]; self.values[nid as usize] = Some(match node.kind { Kind::Start => { debug_assert_eq!(self.nodes[node.outputs[0]].kind, Kind::Entry); self.emit_node(node.outputs[0], block); return; } Kind::If => { let &[_, cnd] = node.inputs.as_slice() else { unreachable!() }; let &[then, else_] = node.outputs.as_slice() else { unreachable!() }; let then_bl = self.bl.create_block(); let else_bl = self.bl.create_block(); let c = self.value_of(cnd); self.bl.ins().brif(c, then_bl, &[], else_bl, &[]); self.values[then as usize] = Some(Err(then_bl)); self.values[else_ as usize] = Some(Err(else_bl)); self.close_block(block); self.bl.switch_to_block(then_bl); self.emit_node(then, then); self.bl.switch_to_block(else_bl); self.emit_node(else_, else_); Err(self.block_of(block)) } Kind::Loop => { let next = self.bl.create_block(); for &o in self.nodes[nid].outputs.iter() { if self.nodes[o].is_data_phi() { self.values[o as usize] = Some(Ok(self .bl .append_block_param(next, self.nodes[o].ty.to_clif(self.tys)))); } } self.values[nid as usize] = Some(Err(next)); self.bl.ins().jump(self.values[nid as usize].unwrap().unwrap_err(), &args); self.close_block(block); self.bl.switch_to_block(self.values[nid as usize].unwrap().unwrap_err()); for &o in node.outputs.iter().rev() { self.emit_node(o, nid); } Err(self.block_of(block)) } Kind::Region => { self.bl.ins().jump(self.values[nid as usize].unwrap().unwrap_err(), &args); self.close_block(block); self.bl.switch_to_block(self.values[nid as usize].unwrap().unwrap_err()); for &o in node.outputs.iter().rev() { self.emit_node(o, nid); } return; } Kind::Die => { self.bl.ins().trap(TrapCode::unwrap_user(1)); self.close_block(block); self.emit_node(node.outputs[0], block); Err(self.block_of(block)) } Kind::Return { .. } => { let mut ir_args = vec![]; if node.inputs[1] == hbnodes::VOID { } else { let abi_meta = self.arg_lens[0]; let arg = node.inputs[1]; if !abi_meta.trough_mem && self.nodes[node.inputs[1]].ty.is_aggregate(self.tys) { let loc = self.bl.func.signature.returns.clone(); assert!(loc.len() <= 2, "NEED handling"); let align = loc.iter().map(|&p| p.value_type.bytes()).max().unwrap(); let mut offset = 0i32; let src = self.value_of(self.nodes[arg].inputs[1]); debug_assert!(self.nodes[arg].kind == Kind::Load); for &v in &loc { ir_args.push(self.bl.ins().load( v.value_type, MemFlags::new(), src, offset, )); offset += align as i32; } } else if self.stack_ret { let src = self.value_of(self.nodes[arg].inputs[1]); let dest = self.value_of(MEM); self.bl.emit_small_memory_copy( self.isa.frontend_config(), dest, src, self.tys.size_of(self.nodes[arg].ty) as _, self.tys.align_of(self.nodes[arg].ty) as _, self.tys.align_of(self.nodes[arg].ty) as _, false, MemFlags::new(), ); } else { ir_args.push(self.value_of(arg)); } } self.bl.ins().return_(&ir_args); self.close_block(block); self.emit_node(node.outputs[0], block); Err(self.block_of(block)) } Kind::Entry => { for &o in node.outputs.iter().rev() { self.emit_node(o, nid); } return; } Kind::Then | Kind::Else => { for &o in node.outputs.iter().rev() { self.emit_node(o, block); } Err(self.block_of(block)) } Kind::Call { func, unreachable, args } => { assert_ne!(func, hbty::Func::ECA, "@eca is not supported"); if unreachable { todo!() } else { let mut arg_lens = vec![]; let mut signature = cir::Signature::new(self.isa.default_call_conv()); let stack_ret = build_signature( self.isa.default_call_conv(), self.tys.ins.funcs[func].sig, self.tys, &mut signature, &mut arg_lens, ); let func_ref = 'b: { let user_name_ref = self.bl.func.declare_imported_user_function( cir::UserExternalName { namespace: 0, index: func.index() as _ }, ); if let Some(id) = self.bl.func.dfg.ext_funcs.keys().find(|&k| { self.bl.func.dfg.ext_funcs[k].name == cir::ExternalName::user(user_name_ref) }) { break 'b id; } let signature = self.bl.func.import_signature(signature.clone()); self.bl.func.import_function(cir::ExtFuncData { name: cir::ExternalName::user(user_name_ref), signature, // somehow, this works colocated: true, // !self.tys.ins.funcs[func].is_import, }) }; let mut ir_args = vec![]; if stack_ret { ir_args.push(self.value_of(*node.inputs.last().unwrap())); } let mut params = signature.params.as_slice(); let mut parama_len = arg_lens[1..].iter(); let mut typs = args.args(); let mut args = node.inputs[1..].iter(); while let Some(aty) = typs.next(self.tys) { let hbty::Arg::Value(ty) = aty else { continue }; let abi_meta = parama_len.next().unwrap(); if abi_meta.arg_count == 0 { continue; } let &arg = args.next().unwrap(); if !abi_meta.trough_mem && ty.is_aggregate(self.tys) { let loc = params.take(..abi_meta.arg_count).unwrap(); assert!(loc.len() <= 2, "NEED handling"); let align = loc.iter().map(|&p| p.value_type.bytes()).max().unwrap(); let mut offset = 0i32; let src = self.value_of(self.nodes[arg].inputs[1]); debug_assert!(self.nodes[arg].kind == Kind::Load); for &v in loc { ir_args.push(self.bl.ins().load( v.value_type, MemFlags::new(), src, offset, )); offset += align as i32; } } else { let loc = params.take(..abi_meta.arg_count).unwrap(); debug_assert_eq!(loc.len(), 1); ir_args.push(self.value_of(arg)); } } let inst = self.bl.ins().call(func_ref, &ir_args); match *self.bl.inst_results(inst) { [] => {} [scala] => self.values[nid as usize] = Some(Ok(scala)), [a, b] => { assert!(!stack_ret); let slot = self.value_of(*node.inputs.last().unwrap()); let loc = [a, b]; assert!(loc.len() <= 2, "NEED handling"); let align = loc .iter() .map(|&p| self.bl.func.dfg.value_type(p).bytes()) .max() .unwrap(); let mut offset = 0i32; for v in loc { self.bl.ins().store(MemFlags::new(), v, slot, offset); offset += align as i32; } } _ => unimplemented!(), } for &o in node.outputs.iter().rev() { if self.nodes[o].inputs[0] == nid || (matches!(self.nodes[o].kind, Kind::Loop | Kind::Region) && self.nodes[o].inputs[1] == nid) { self.emit_node(o, block); } } return; } } Kind::CInt { value } if self.nodes[nid].ty.is_float() => { Ok(match self.tys.size_of(self.nodes[nid].ty) { 4 => self.bl.ins().f32const(f64::from_bits(value as _) as f32), 8 => self.bl.ins().f64const(f64::from_bits(value as _)), _ => unimplemented!(), }) } Kind::CInt { value } => Ok(self.bl.ins().iconst( cir::Type::int(self.tys.size_of(node.ty) as u16 * 8).unwrap_or_else(|| { panic!("{}", hbty::Display::new(self.tys, self.files, node.ty),) }), value, )), Kind::BinOp { op } => { let &[_, lhs, rhs] = node.inputs.as_slice() else { unreachable!() }; let [lh, rh] = [self.value_of(lhs), self.value_of(rhs)]; let is_int_op = node.ty.is_integer() || node.ty.is_pointer() || (node.ty == hbty::Id::BOOL && (self.nodes[lhs].ty.is_integer() || node.ty.is_pointer() || self.nodes[lhs].ty == hbty::Id::BOOL)); let is_float_op = node.ty.is_float() || (node.ty == hbty::Id::BOOL && self.nodes[lhs].ty.is_float()); Ok(if is_int_op { let signed = node.ty.is_signed(); match op { TokenKind::Add => self.bl.ins().iadd(lh, rh), TokenKind::Sub => self.bl.ins().isub(lh, rh), TokenKind::Mul => self.bl.ins().imul(lh, rh), TokenKind::Shl => self.bl.ins().ishl(lh, rh), TokenKind::Xor => self.bl.ins().bxor(lh, rh), TokenKind::Band => self.bl.ins().band(lh, rh), TokenKind::Bor => self.bl.ins().bor(lh, rh), TokenKind::Div if signed => self.bl.ins().sdiv(lh, rh), TokenKind::Mod if signed => self.bl.ins().srem(lh, rh), TokenKind::Shr if signed => self.bl.ins().sshr(lh, rh), TokenKind::Div => self.bl.ins().udiv(lh, rh), TokenKind::Mod => self.bl.ins().urem(lh, rh), TokenKind::Shr => self.bl.ins().ushr(lh, rh), TokenKind::Lt | TokenKind::Gt | TokenKind::Le | TokenKind::Ge | TokenKind::Eq | TokenKind::Ne => self.bl.ins().icmp(op.to_int_cc(signed), lh, rh), op => todo!("{op}"), } } else if is_float_op { match op { TokenKind::Add => self.bl.ins().fadd(lh, rh), TokenKind::Sub => self.bl.ins().fsub(lh, rh), TokenKind::Mul => self.bl.ins().fmul(lh, rh), TokenKind::Div => self.bl.ins().fdiv(lh, rh), TokenKind::Lt | TokenKind::Gt | TokenKind::Le | TokenKind::Ge | TokenKind::Eq | TokenKind::Ne => self.bl.ins().fcmp(op.to_float_cc(), lh, rh), op => todo!("{op}"), } } else { todo!("{}", hbty::Display::new(self.tys, self.files, node.ty)) }) } Kind::RetVal => Ok(self.value_of(node.inputs[0])), Kind::UnOp { op } => { let oper = self.value_of(node.inputs[1]); let dst = node.ty; let src = self .tys .inner_of(self.nodes[node.inputs[1]].ty) .unwrap_or(self.nodes[node.inputs[1]].ty); let dty = dst.to_clif(self.tys); Ok(match op { TokenKind::Sub => self.bl.ins().ineg(oper), TokenKind::Not => self.bl.ins().bnot(oper), TokenKind::Float if dst.is_float() && src.is_unsigned() => { self.bl.ins().fcvt_from_uint(dty, oper) } TokenKind::Float if dst.is_float() && src.is_signed() => { self.bl.ins().fcvt_from_sint(dty, oper) } TokenKind::Number if src.is_float() && dst.is_unsigned() => { self.bl.ins().fcvt_to_uint(dty, oper) } TokenKind::Number if src.is_signed() && (dst.is_integer() || dst.is_pointer()) => { self.bl.ins().sextend(dty, oper) } TokenKind::Number if (src.is_unsigned() || src == hbty::Id::BOOL) && (dst.is_integer() || dst.is_pointer()) => { self.bl.ins().uextend(dty, oper) } TokenKind::Float if dst == hbty::Id::F64 && src.is_float() => { self.bl.ins().fpromote(dty, oper) } TokenKind::Float if dst == hbty::Id::F32 && src.is_float() => { self.bl.ins().fdemote(dty, oper) } _ => todo!(), }) } Kind::Stck => { let slot = self.bl.create_sized_stack_slot(cir::StackSlotData { kind: cir::StackSlotKind::ExplicitSlot, size: self.tys.size_of(node.ty), align_shift: self.tys.align_of(node.ty).ilog2() as _, }); Ok(self.bl.ins().stack_addr(cir::types::I64, slot, 0)) } Kind::Global { global } => { let glob_ref = { // already deduplicated by the SoN let colocated = true; let user_name_ref = self.bl.func.declare_imported_user_function(cir::UserExternalName { namespace: 1, index: global.index() as u32, }); self.bl.func.create_global_value(cir::GlobalValueData::Symbol { name: cir::ExternalName::user(user_name_ref), offset: cir::immediates::Imm64::new(0), colocated, tls: false, }) }; Ok(self.bl.ins().global_value(cir::types::I64, glob_ref)) } Kind::Load if node.ty.is_aggregate(self.tys) => return, Kind::Load => { let ptr = self.value_of(node.inputs[1]); Ok(self.bl.ins().load(node.ty.to_clif(self.tys), MemFlags::new(), ptr, 0)) } Kind::Stre if node.ty.is_aggregate(self.tys) => { let src = self.value_of(self.nodes[node.inputs[1]].inputs[1]); let dest = self.value_of(node.inputs[2]); self.bl.emit_small_memory_copy( self.isa.frontend_config(), dest, src, self.tys.size_of(node.ty) as _, self.tys.align_of(node.ty) as _, self.tys.align_of(node.ty) as _, false, MemFlags::new(), ); return; } Kind::Stre => { let value = self.value_of(node.inputs[1]); let ptr = self.value_of(node.inputs[2]); self.bl.ins().store(MemFlags::new(), value, ptr, 0); return; } Kind::End | Kind::Phi | Kind::Arg | Kind::Mem | Kind::Loops | Kind::Join => return, Kind::Assert { .. } => unreachable!(), }); } } trait ToCondcodes { fn to_int_cc(self, signed: bool) -> condcodes::IntCC; fn to_float_cc(self) -> condcodes::FloatCC; } impl ToCondcodes for TokenKind { fn to_int_cc(self, signed: bool) -> condcodes::IntCC { use condcodes::IntCC as ICC; match self { Self::Lt if signed => ICC::SignedLessThan, Self::Gt if signed => ICC::SignedGreaterThan, Self::Le if signed => ICC::SignedLessThanOrEqual, Self::Ge if signed => ICC::SignedGreaterThanOrEqual, Self::Lt => ICC::UnsignedLessThan, Self::Gt => ICC::UnsignedGreaterThan, Self::Le => ICC::UnsignedLessThanOrEqual, Self::Ge => ICC::UnsignedGreaterThanOrEqual, Self::Eq => ICC::Equal, Self::Ne => ICC::NotEqual, _ => unreachable!(), } } fn to_float_cc(self) -> condcodes::FloatCC { use condcodes::FloatCC as FCC; match self { Self::Lt => FCC::LessThan, Self::Gt => FCC::GreaterThan, Self::Le => FCC::LessThanOrEqual, Self::Ge => FCC::GreaterThanOrEqual, Self::Eq => FCC::Equal, Self::Ne => FCC::NotEqual, _ => unreachable!(), } } } trait ToClifTy { fn to_clif(self, cx: &hbty::Types) -> cir::Type; } impl ToClifTy for hbty::Id { fn to_clif(self, cx: &hbty::Types) -> cir::Type { debug_assert!(!self.is_aggregate(cx)); if self.is_integer() | self.is_pointer() | self.is_optional() || self == hbty::Id::BOOL { cir::Type::int(cx.size_of(self) as u16 * 8).unwrap() } else if self == hbty::Id::F32 { cir::types::F32 } else if self == hbty::Id::F64 { cir::types::F64 } else { unimplemented!("{:?}", self) } } } #[derive(Default)] struct Global { module_id: Option<cm::DataId>, } #[derive(Default)] struct FuncHeaders { module_id: Option<cm::FuncId>, alignment: u32, code: Range<u32>, relocs: Range<u32>, external_names: Range<u32>, } #[derive(Default)] struct Functions { headers: EntVec<hbty::Func, FuncHeaders>, code: Vec<u8>, relocs: Vec<FinalizedMachReloc>, external_names: Vec<UserExternalName>, } impl Functions { fn push(&mut self, id: hbty::Func, func: &cir::Function, code: &MachBufferFinalized<Final>) { self.headers.shadow(id.index() + 1); self.headers[id] = FuncHeaders { module_id: None, alignment: code.alignment, code: self.code.len() as u32..self.code.len() as u32 + code.data().len() as u32, relocs: self.relocs.len() as u32..self.relocs.len() as u32 + code.relocs().len() as u32, external_names: self.external_names.len() as u32 ..self.external_names.len() as u32 + func.params.user_named_funcs().len() as u32, }; self.code.extend(code.data()); self.relocs.extend(code.relocs().iter().cloned()); self.external_names.extend(func.params.user_named_funcs().values().cloned()); } } #[derive(Default)] struct Assembler { name: String, frontier: Vec<hbty::Id>, globals: Vec<hbty::Global>, funcs: Vec<hbty::Func>, } #[derive(Debug)] pub enum BackendCreationError { UnsupportedTriplet(LookupError), InvalidFlags(CodegenError), UnsupportedModuleConfig(ModuleError), InvalidFlag { key: String, value: String, err: SetError }, } impl Display for BackendCreationError { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { BackendCreationError::UnsupportedTriplet(err) => { write!(f, "Unsupported triplet: {}", err) } BackendCreationError::InvalidFlags(err) => { write!(f, "Invalid flags: {}", err) } BackendCreationError::UnsupportedModuleConfig(err) => { write!(f, "Unsupported module configuration: {}", err) } BackendCreationError::InvalidFlag { key, value, err } => { write!( f, "Problem setting a '{key}' to '{value}': {err}\navailable flags: {}", cc::settings::Flags::new(cc::settings::builder()) ) } } } } impl core::error::Error for BackendCreationError {} impl From<LookupError> for BackendCreationError { fn from(value: LookupError) -> Self { Self::UnsupportedTriplet(value) } } impl From<CodegenError> for BackendCreationError { fn from(value: CodegenError) -> Self { Self::InvalidFlags(value) } } impl From<ModuleError> for BackendCreationError { fn from(value: ModuleError) -> Self { Self::UnsupportedModuleConfig(value) } }