holey-bytes/cranelift-backend/src/lib.rs

787 lines
31 KiB
Rust
Raw Normal View History

#![feature(if_let_guard)]
#![feature(slice_take)]
use {
core::panic,
cranelift_codegen::{
CodegenError, Final, FinalizedMachReloc, MachBufferFinalized,
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, usize},
};
mod x86_64;
pub struct Backend {
ctx: cranelift_codegen::Context,
dt_ctx: cranelift_module::DataDescription,
fb_ctx: cranelift_frontend::FunctionBuilderContext,
module: Option<cranelift_object::ObjectModule>,
ctrl_plane: cranelift_codegen::control::ControlPlane,
funcs: Functions,
globals: EntVec<hblang::ty::Global, Global>,
asm: Assembler,
}
impl Backend {
pub fn new(triple: target_lexicon::Triple) -> Result<Self, BackendCreationError> {
Ok(Self {
ctx: cranelift_codegen::Context::new(),
dt_ctx: cranelift_module::DataDescription::new(),
fb_ctx: cranelift_frontend::FunctionBuilderContext::default(),
ctrl_plane: cranelift_codegen::control::ControlPlane::default(),
module: cranelift_object::ObjectModule::new(cranelift_object::ObjectBuilder::new(
cranelift_codegen::isa::lookup(triple)?.finish(
cranelift_codegen::settings::Flags::new({
let mut bl = cranelift_codegen::settings::builder();
bl.set("enable_verifier", "true").unwrap();
bl
}),
)?,
"main",
cranelift_module::default_libcall_names(),
)?)
.into(),
funcs: Default::default(),
globals: Default::default(),
asm: Default::default(),
})
}
}
impl hblang::backend::Backend for Backend {
fn assemble_reachable(
&mut self,
from: hblang::ty::Func,
types: &hblang::ty::Types,
files: &hblang::utils::EntSlice<hblang::ty::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) -> hblang::ty::Id {
match name.namespace {
0 => hblang::ty::Kind::Func(hblang::ty::Func::new(name.index as _)),
1 => hblang::ty::Kind::Global(hblang::ty::Global::new(name.index as _)),
_ => unreachable!(),
}
.compress()
}
self.globals.shadow(types.ins.globals.len());
self.asm.frontier.push(from.into());
while let Some(itm) = self.asm.frontier.pop() {
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| {
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 {
let file = &files[types.ins.funcs[func].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.funcs[func].name));
}
let linkage = if func == from {
cranelift_module::Linkage::Export
} else {
cranelift_module::Linkage::Local
};
build_signature(
module.isa().default_call_conv(),
types.ins.funcs[func].sig,
types,
&mut self.ctx.func.signature,
&mut vec![],
);
fuc.module_id = Some(
module
.declare_function(&self.asm.name, linkage, &self.ctx.func.signature)
.unwrap(),
);
}
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(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(
module
.declare_data(
&self.asm.name,
cranelift_module::Linkage::Local,
true,
false,
)
.unwrap(),
);
}
_ => unreachable!(),
}
}
for &func in &self.asm.funcs {
let fuc = &self.funcs.headers[func];
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];
names.iter_mut().for_each(|nm| {
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
.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 hblang::ty::Types,
_files: &'a hblang::utils::EntSlice<hblang::ty::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: hblang::ty::Func,
nodes: &hblang::nodes::Nodes,
tys: &hblang::ty::Types,
files: &hblang::utils::EntSlice<hblang::ty::Module, hblang::parser::Ast>,
) {
self.ctx.clear();
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()],
}
.build(tys.ins.funcs[id].sig, &lens, stack_ret);
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);
}
}
fn build_signature(
call_conv: cranelift_codegen::isa::CallConv,
sig: hblang::ty::Sig,
types: &hblang::ty::Types,
signature: &mut cranelift_codegen::ir::Signature,
arg_lens: &mut Vec<usize>,
) -> bool {
signature.clear(call_conv);
match call_conv {
cranelift_codegen::isa::CallConv::SystemV => {
x86_64::build_systemv_signature(sig, types, signature, arg_lens)
}
_ => todo!(),
}
}
struct FuncBuilder<'a, 'b> {
bl: cranelift_frontend::FunctionBuilder<'b>,
isa: &'a dyn TargetIsa,
nodes: &'a hblang::nodes::Nodes,
tys: &'a hblang::ty::Types,
files: &'a hblang::utils::EntSlice<hblang::ty::Module, hblang::parser::Ast>,
values: &'b mut [Option<Result<cranelift_codegen::ir::Value, cranelift_codegen::ir::Block>>],
}
impl FuncBuilder<'_, '_> {
pub fn build(mut self, sig: hblang::ty::Sig, arg_lens: &[usize], stack_ret: bool) {
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);
if stack_ret {
let ret_ptr = *arg_vals.take_first().unwrap();
self.values[hblang::nodes::MEM as usize] = Some(Ok(ret_ptr));
}
let Self { nodes, tys, .. } = self;
let mut parama_len = arg_lens.iter();
let mut typs = sig.args.args();
let mut args = nodes[hblang::nodes::VOID].outputs[hblang::nodes::ARG_START..].iter();
while let Some(aty) = typs.next(tys) {
let hblang::ty::Arg::Value(ty) = aty else { continue };
let loc = arg_vals.take(..*parama_len.next().unwrap()).unwrap();
let &arg = args.next().unwrap();
if ty.is_aggregate(tys) {
todo!()
} else {
debug_assert_eq!(loc.len(), 1);
self.values[arg as usize] = Some(Ok(loc[0]));
}
}
self.values[hblang::nodes::ENTRY as usize] = Some(Err(entry));
self.emit_node(hblang::nodes::VOID, hblang::nodes::VOID);
self.bl.finalize();
}
fn value_of(&self, nid: hblang::nodes::Nid) -> cranelift_codegen::ir::Value {
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 {
self.values[nid as usize].unwrap().unwrap_err()
}
fn close_block(&mut self, nid: hblang::nodes::Nid) {
if matches!(self.nodes[nid].kind, Kind::Loop) {
return;
}
self.bl.seal_block(self.block_of(nid));
}
fn emit_node(&mut self, nid: hblang::nodes::Nid, block: hblang::nodes::Nid) {
use hblang::nodes::*;
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,
ty_to_clif_ty(self.nodes[o].ty, 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, ty_to_clif_ty(self.nodes[o].ty, 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::Return { .. } | Kind::Die => {
let ret = self.value_of(node.inputs[1]);
self.bl.ins().return_(&[ret]);
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 } => {
if unreachable {
todo!()
} else {
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::<Vec<_>>();
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(
cranelift_codegen::ir::Type::int(self.tys.size_of(self.nodes[nid].ty) as u16 * 8)
.unwrap(),
value,
)),
Kind::CInt { value } => 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::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!(),
});
}
}
fn ty_to_clif_ty(ty: hblang::ty::Id, tys: &hblang::ty::Types) -> cranelift_codegen::ir::Type {
if ty.is_integer() {
cranelift_codegen::ir::Type::int(tys.size_of(ty) as u16 * 8).unwrap()
} else {
unimplemented!()
}
}
#[derive(Default)]
struct Global {
module_id: Option<cranelift_module::DataId>,
}
#[derive(Default)]
struct FuncHeaders {
module_id: Option<cranelift_module::FuncId>,
alignment: u32,
code: Range<u32>,
relocs: Range<u32>,
external_names: Range<u32>,
}
#[derive(Default)]
struct Functions {
headers: EntVec<hblang::ty::Func, FuncHeaders>,
code: Vec<u8>,
relocs: Vec<FinalizedMachReloc>,
external_names: Vec<UserExternalName>,
}
impl Functions {
fn push(
&mut self,
id: hblang::ty::Func,
func: &cranelift_codegen::ir::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<hblang::ty::Id>,
globals: Vec<hblang::ty::Global>,
funcs: Vec<hblang::ty::Func>,
}
#[derive(Debug)]
pub enum BackendCreationError {
UnsupportedTriplet(LookupError),
InvalidFlags(CodegenError),
UnsupportedModuleConfig(ModuleError),
}
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)
}
}
}
}
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)
}
}