From 68186ec0ce272e4ff35f1173b123a722a1629fc8 Mon Sep 17 00:00:00 2001 From: Jakub Doka Date: Sun, 29 Dec 2024 16:37:24 +0100 Subject: [PATCH] supporting translation of all nodes TODO: handle ABI argument passing Signed-off-by: Jakub Doka --- cranelift-backend/src/lib.rs | 336 ++++++++++++++++++++++++++++++----- lang/src/lib.rs | 15 +- smh.hb | 14 +- 3 files changed, 320 insertions(+), 45 deletions(-) diff --git a/cranelift-backend/src/lib.rs b/cranelift-backend/src/lib.rs index 6d98f5b..5e961aa 100644 --- a/cranelift-backend/src/lib.rs +++ b/cranelift-backend/src/lib.rs @@ -1,19 +1,21 @@ #![feature(if_let_guard)] #![feature(slice_take)] use { + core::panic, cranelift_codegen::{ CodegenError, Final, FinalizedMachReloc, MachBufferFinalized, - ir::{InstBuilder, UserExternalName}, - isa::LookupError, + ir::{InstBuilder, MemFlags, UserExternalName}, + isa::{LookupError, TargetIsa}, settings::Configurable, }, cranelift_frontend::FunctionBuilder, cranelift_module::{Module, ModuleError}, hblang::{ + lexer::TokenKind, nodes::Kind, utils::{Ent, EntVec}, }, - std::{fmt::Display, ops::Range}, + std::{fmt::Display, ops::Range, usize}, }; mod x86_64; @@ -85,6 +87,9 @@ impl hblang::backend::Backend for Backend { match itm.expand() { hblang::ty::Kind::Func(func) => { let fuc = &mut self.funcs.headers[func]; + if fuc.module_id.is_some() { + continue; + } self.asm.funcs.push(func); self.asm.frontier.extend( fuc.external_names.clone().map(|r| { @@ -96,7 +101,7 @@ impl hblang::backend::Backend for Backend { self.asm.name.push_str("main"); } else { let file = &files[types.ins.funcs[func].file]; - self.asm.name.push_str(&file.path); + self.asm.name.push_str(hblang::strip_cwd(&file.path)); self.asm.name.push('.'); self.asm.name.push_str(file.ident_str(types.ins.funcs[func].name)); } @@ -119,10 +124,13 @@ impl hblang::backend::Backend for Backend { ); } hblang::ty::Kind::Global(glob) => { + if self.globals[glob].module_id.is_some() { + continue; + } self.asm.globals.push(glob); self.asm.name.clear(); let file = &files[types.ins.globals[glob].file]; - self.asm.name.push_str(&file.path); + 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)); self.globals[glob].module_id = Some( @@ -146,7 +154,10 @@ impl hblang::backend::Backend for Backend { let names = &mut self.funcs.external_names [fuc.external_names.start as usize..fuc.external_names.end as usize]; names.iter_mut().for_each(|nm| { - nm.index = fuc.module_id.unwrap().as_u32(); + nm.index = self.funcs.headers[hblang::ty::Func::new(nm.index as _)] + .module_id + .unwrap() + .as_u32(); self.ctx.func.params.ensure_user_func_name(nm.clone()); }); module @@ -191,10 +202,11 @@ impl hblang::backend::Backend for Backend { files: &hblang::utils::EntSlice, ) { self.ctx.clear(); + let isa = self.module.as_ref().unwrap().isa(); let mut lens = vec![]; let stack_ret = build_signature( - self.module.as_ref().unwrap().isa().default_call_conv(), + isa.default_call_conv(), tys.ins.funcs[id].sig, tys, &mut self.ctx.func.signature, @@ -203,6 +215,7 @@ impl hblang::backend::Backend for Backend { FuncBuilder { bl: FunctionBuilder::new(&mut self.ctx.func, &mut self.fb_ctx), + isa, nodes, tys, files, @@ -210,7 +223,15 @@ impl hblang::backend::Backend for Backend { } .build(tys.ins.funcs[id].sig, &lens, stack_ret); - self.ctx.compile(self.module.as_ref().unwrap().isa(), &mut self.ctrl_plane).unwrap(); + self.ctx.func.name = + cranelift_codegen::ir::UserFuncName::User(cranelift_codegen::ir::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); } @@ -234,9 +255,9 @@ fn build_signature( struct FuncBuilder<'a, 'b> { bl: cranelift_frontend::FunctionBuilder<'b>, + isa: &'a dyn TargetIsa, nodes: &'a hblang::nodes::Nodes, tys: &'a hblang::ty::Types, - #[expect(unused)] files: &'a hblang::utils::EntSlice, values: &'b mut [Option>], } @@ -265,7 +286,7 @@ impl FuncBuilder<'_, '_> { if ty.is_aggregate(tys) { todo!() } else { - debug_assert_eq!(loc.len(), 0); + debug_assert_eq!(loc.len(), 1); self.values[arg as usize] = Some(Ok(loc[0])); } } @@ -278,7 +299,7 @@ impl FuncBuilder<'_, '_> { } fn value_of(&self, nid: hblang::nodes::Nid) -> cranelift_codegen::ir::Value { - self.values[nid as usize].unwrap().unwrap() + self.values[nid as usize].unwrap_or_else(|| panic!("{:?}", self.nodes[nid])).unwrap() } fn block_of(&self, nid: hblang::nodes::Nid) -> cranelift_codegen::ir::Block { @@ -286,7 +307,7 @@ impl FuncBuilder<'_, '_> { } fn close_block(&mut self, nid: hblang::nodes::Nid) { - if matches!(self.nodes[nid].kind, Kind::Loop | Kind::Region) { + if matches!(self.nodes[nid].kind, Kind::Loop) { return; } self.bl.seal_block(self.block_of(nid)); @@ -300,13 +321,14 @@ impl FuncBuilder<'_, '_> { 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[0].inputs[side])); + 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) => { @@ -320,7 +342,7 @@ impl FuncBuilder<'_, '_> { } } self.bl.ins().jump(next, &args); - self.bl.seal_block(next); + self.close_block(block); self.values[nid as usize] = Some(Err(next)); return; } @@ -353,19 +375,16 @@ impl FuncBuilder<'_, '_> { self.emit_node(else_, else_); Err(self.block_of(block)) } - Kind::Region | Kind::Loop => { - if node.kind == 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, - ty_to_clif_ty(self.nodes[o].ty, self.tys), - ))); - } + 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, ty_to_clif_ty(self.nodes[o].ty, self.tys)))); } - self.values[nid as usize] = Some(Err(next)); } + 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()); @@ -374,6 +393,15 @@ impl FuncBuilder<'_, '_> { } 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::Return { .. } | Kind::Die => { let ret = self.value_of(node.inputs[1]); self.bl.ins().return_(&[ret]); @@ -393,19 +421,64 @@ impl FuncBuilder<'_, '_> { } Err(self.block_of(block)) } - Kind::Call { func: _, unreachable, .. } => { + Kind::Call { func, unreachable, args } => { if unreachable { todo!() } else { - todo!(); - //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); - // } - //} + let mut arg_lens = vec![]; + let mut signature = + cranelift_codegen::ir::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, + ); + assert!(!stack_ret, "TODO"); + let func_ref = 'b: { + let user_name_ref = self.bl.func.declare_imported_user_function( + cranelift_codegen::ir::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 + == cranelift_codegen::ir::ExternalName::user(user_name_ref) + }) { + break 'b id; + } + + let signature = self.bl.func.import_signature(signature); + + self.bl.func.import_function(cranelift_codegen::ir::ExtFuncData { + name: cranelift_codegen::ir::ExternalName::user(user_name_ref), + signature, + colocated: true, + }) + }; + + let args = node.inputs[1..][..args.len()] + .iter() + .map(|&n| self.value_of(n)) + .collect::>(); + let inst = self.bl.ins().call(func_ref, &args); + match *self.bl.inst_results(inst) { + [] => {} + [scala] => self.values[nid as usize] = Some(Ok(scala)), + _ => todo!(), + } + 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_integer() => Ok(self.bl.ins().iconst( @@ -418,13 +491,192 @@ impl FuncBuilder<'_, '_> { 8 => self.bl.ins().f64const(f64::from_bits(value as _)), _ => unimplemented!(), }), - Kind::BinOp { .. } - | Kind::UnOp { .. } - | Kind::Global { .. } - | Kind::Load { .. } - | Kind::Stre - | Kind::RetVal - | Kind::Stck => todo!(), + Kind::BinOp { op } => { + let &[_, lhs, rhs] = node.inputs.as_slice() else { unreachable!() }; + let [lhs, rhs] = [self.value_of(lhs), self.value_of(rhs)]; + assert!( + node.ty.is_integer() || node.ty == hblang::ty::Id::BOOL, + "TODO: unsupported binary type {}", + hblang::ty::Display::new(self.tys, self.files, node.ty) + ); + + use cranelift_codegen::ir::condcodes::IntCC as ICC; + fn icc_of(op: TokenKind, signed: bool) -> ICC { + match op { + TokenKind::Lt if signed => ICC::SignedLessThan, + TokenKind::Gt if signed => ICC::SignedGreaterThan, + TokenKind::Le if signed => ICC::SignedLessThanOrEqual, + TokenKind::Ge if signed => ICC::SignedGreaterThanOrEqual, + + TokenKind::Lt => ICC::UnsignedLessThan, + TokenKind::Gt => ICC::UnsignedGreaterThan, + TokenKind::Le => ICC::UnsignedLessThanOrEqual, + TokenKind::Ge => ICC::UnsignedGreaterThanOrEqual, + + TokenKind::Eq => ICC::Equal, + TokenKind::Ne => ICC::NotEqual, + _ => unreachable!(), + } + } + + use cranelift_codegen::ir::condcodes::FloatCC as FCC; + fn fcc_of(op: TokenKind) -> FCC { + match op { + TokenKind::Lt => FCC::LessThan, + TokenKind::Gt => FCC::GreaterThan, + TokenKind::Le => FCC::LessThanOrEqual, + TokenKind::Ge => FCC::GreaterThanOrEqual, + TokenKind::Eq => FCC::Equal, + TokenKind::Ne => FCC::NotEqual, + _ => unreachable!(), + } + } + + Ok(if node.ty.is_integer() { + let signed = node.ty.is_signed(); + match op { + TokenKind::Add => self.bl.ins().iadd(lhs, rhs), + TokenKind::Sub => self.bl.ins().isub(lhs, rhs), + TokenKind::Mul => self.bl.ins().imul(lhs, rhs), + TokenKind::Shl => self.bl.ins().ishl(lhs, rhs), + TokenKind::Xor => self.bl.ins().bxor(lhs, rhs), + TokenKind::Band => self.bl.ins().band(lhs, rhs), + TokenKind::Bor => self.bl.ins().bor(lhs, rhs), + + TokenKind::Div if signed => self.bl.ins().sdiv(lhs, rhs), + TokenKind::Mod if signed => self.bl.ins().srem(lhs, rhs), + TokenKind::Shr if signed => self.bl.ins().sshr(lhs, rhs), + + TokenKind::Div => self.bl.ins().udiv(lhs, rhs), + TokenKind::Mod => self.bl.ins().urem(lhs, rhs), + TokenKind::Shr => self.bl.ins().ushr(lhs, rhs), + + TokenKind::Lt + | TokenKind::Gt + | TokenKind::Le + | TokenKind::Ge + | TokenKind::Eq + | TokenKind::Ne => self.bl.ins().icmp(icc_of(op, signed), lhs, rhs), + op => todo!("{op}"), + } + } else if node.ty.is_float() { + match op { + TokenKind::Add => self.bl.ins().fadd(lhs, rhs), + TokenKind::Sub => self.bl.ins().fsub(lhs, rhs), + TokenKind::Mul => self.bl.ins().fmul(lhs, rhs), + TokenKind::Div => self.bl.ins().fdiv(lhs, rhs), + + TokenKind::Lt + | TokenKind::Gt + | TokenKind::Le + | TokenKind::Ge + | TokenKind::Eq + | TokenKind::Ne => self.bl.ins().fcmp(fcc_of(op), lhs, rhs), + op => todo!("{op}"), + } + } else { + todo!() + }) + } + 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 = ty_to_clif_ty(dst, 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 == hblang::ty::Id::BOOL) + && (dst.is_integer() || dst.is_pointer()) => + { + self.bl.ins().uextend(dty, oper) + } + TokenKind::Float if dst == hblang::ty::Id::F64 && src.is_float() => { + self.bl.ins().fpromote(dty, oper) + } + TokenKind::Float if dst == hblang::ty::Id::F32 && src.is_float() => { + self.bl.ins().fdemote(dty, oper) + } + _ => todo!(), + }) + } + Kind::Stck => { + let slot = self.bl.create_sized_stack_slot(cranelift_codegen::ir::StackSlotData { + kind: cranelift_codegen::ir::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(cranelift_codegen::ir::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( + cranelift_codegen::ir::UserExternalName { + namespace: 1, + index: global.index() as u32, + }, + ); + self.bl.func.create_global_value( + cranelift_codegen::ir::GlobalValueData::Symbol { + name: cranelift_codegen::ir::ExternalName::user(user_name_ref), + offset: cranelift_codegen::ir::immediates::Imm64::new(0), + colocated, + tls: false, + }, + ) + }; + + Ok(self.bl.ins().global_value(cranelift_codegen::ir::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(ty_to_clif_ty(node.ty, 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!(), }); diff --git a/lang/src/lib.rs b/lang/src/lib.rs index 3e8d328..daf0250 100644 --- a/lang/src/lib.rs +++ b/lang/src/lib.rs @@ -532,10 +532,21 @@ fn test_parse_files( ) } +#[cfg(feature = "std")] +static CWD: std::sync::LazyLock = + std::sync::LazyLock::new(|| std::env::current_dir().unwrap_or_default()); + +#[cfg(feature = "std")] +pub fn strip_cwd(path: &str) -> &str { + std::path::Path::new(path) + .strip_prefix(&*CWD) + .unwrap_or(std::path::Path::new(path)) + .to_str() + .unwrap() +} + #[cfg(feature = "std")] pub fn display_rel_path(path: &(impl AsRef + ?Sized)) -> std::path::Display { - static CWD: std::sync::LazyLock = - std::sync::LazyLock::new(|| std::env::current_dir().unwrap_or_default()); std::path::Path::new(path).strip_prefix(&*CWD).unwrap_or(std::path::Path::new(path)).display() } diff --git a/smh.hb b/smh.hb index 935e4eb..c969e3e 100644 --- a/smh.hb +++ b/smh.hb @@ -1,3 +1,15 @@ main := fn(): uint { - return 69 + return fib(10) +} + +fib := fn(x: uint): uint { + a := 0 + b := 1 + loop if x == 0 break else { + c := a + b + a = b + b = c + x -= 1 + } + return a }