// Copyright (c) 2016-2021 Fabian Schuiki //! This module implements LLHD code generation. #![allow(unreachable_code)] use crate::{ crate_prelude::*, hir::{AccessedNode, HirNode}, port_list::PortList, resolver::InstTarget, ty::UnpackedType, value::{Value, ValueKind}, ParamEnv, }; use moore_circt::{self as circt, comb::CmpPred, mlir, prelude::*}; use num::{BigInt, FromPrimitive, One, ToPrimitive, Zero}; use std::{ collections::{HashMap, HashSet}, iter::{once, repeat}, ops::{Deref, DerefMut}, rc::Rc, }; pub type HybridValue = (llhd::ir::Value, mlir::Value); pub type HybridType = (llhd::Type, mlir::Type); pub type HybridBlock = (llhd::ir::Block, mlir::Block); /// A code generator. /// /// Use this struct to emit LLHD code for nodes in a [`Context`]. pub struct CodeGenerator<'gcx, C> { /// The compilation context. cx: C, /// The MLIR compilation context. mcx: mlir::Context, /// The LLHD module to be populated. into: llhd::ir::Module, /// THe MLIR module to be populated. into_mlir: circt::ModuleOp, /// Tables holding mappings and interned values. tables: Tables<'gcx>, } impl<'gcx, C> CodeGenerator<'gcx, C> { /// Create a new code generator. pub fn new(cx: C, into_mlir: circt::ModuleOp) -> Self { CodeGenerator { cx, mcx: into_mlir.context(), into: llhd::ir::Module::new(), into_mlir, tables: Default::default(), } } /// Finalize code generation and return the generated LLHD module. pub fn finalize(self) -> llhd::ir::Module { self.into } } #[derive(Default)] struct Tables<'gcx> { module_defs: HashMap>>>, module_signatures: HashMap, interned_types: HashMap<&'gcx UnpackedType<'gcx>, Result>, function_defs: HashMap>>, } impl<'gcx, C> Deref for CodeGenerator<'gcx, C> { type Target = C; fn deref(&self) -> &C { &self.cx } } impl<'a, 'gcx, C: Context<'gcx>> CodeGenerator<'gcx, &'a C> { /// Emit the global unparametrized things which are unambiguous, like free /// functions. pub fn emit_globals(&mut self, ast: &ast::Root) -> Result<()> { for file in &ast.files { for item in &file.items { match &item.data { ast::ItemData::SubroutineDecl(decl) => { self.emit_function(decl.id(), self.default_param_env())?; } _ => (), } } } Ok(()) } /// Emit the code for a module and all its dependent modules. pub fn emit_module(&mut self, id: NodeId) -> Result>> { self.emit_module_with_env(id, self.default_param_env()) } /// Emit the code for a module and all its dependent modules. pub fn emit_module_with_env( &mut self, id: NodeId, env: ParamEnv, ) -> Result>> { if let Some(x) = self.tables.module_defs.get(&id.env(env)) { return x.clone(); } let hir = match self.hir_of(id)? { HirNode::Module(m) => m, _ => panic!("expected {:?} to be a module", id), }; info!("Emit module `{}` with {:?}", hir.name, env); // Emit detailed port information if requested. if self.sess().has_verbosity(Verbosity::PORTS) { emit_port_details(self.cx, hir, env); } // Determine entity type and port names. let ports = self.determine_module_ports(&hir.ports_new.int, env)?; // Pick an entity name. let mut entity_name: String = hir.name.value.into(); if env != self.default_param_env() { entity_name.push_str(&format!(".param{}", env.0)); } let name = llhd::ir::UnitName::Global(entity_name.clone()); // Create MLIR entity. let mlir_cx = self.into_mlir.context(); let mut mlir_builder = mlir::Builder::new(mlir_cx); mlir_builder.set_loc(span_to_loc(mlir_cx, hir.span())); mlir_builder.set_insertion_point_to_end(self.into_mlir.block()); let mut entity_op = circt::llhd::EntityLikeBuilder::new(&entity_name); for port in ports.inputs.iter() { entity_op.add_input(&port.name, port.mty); } for port in ports.outputs.iter() { entity_op.add_output(&port.name, port.mty); } let entity_op = entity_op.build_entity(&mut mlir_builder); mlir_builder.set_insertion_point_to_start(entity_op.block()); // Create entity. let mut ent = llhd::ir::UnitData::new(llhd::ir::UnitKind::Entity, name.clone(), ports.sig.clone()); let mut builder = llhd::ir::UnitBuilder::new_anonymous(&mut ent); self.tables .module_signatures .insert(id.env(env), (name, ports.sig.clone())); let mut values = HashMap::new(); let mut gen = UnitGenerator::new(self, &mut builder, &mut values, &mut mlir_builder); // Assign proper port names and collect ports into a lookup table. for (index, port) in ports.inputs.iter().enumerate() { let arg = gen.builder.input_arg(index); gen.builder.set_name(arg, port.name.clone()); gen.values .insert(port.accnode, (arg, entity_op.input(index))); } for (index, port) in ports.outputs.iter().enumerate() { let arg = gen.builder.output_arg(index); gen.builder.set_name(arg, port.name.clone()); gen.values .insert(port.accnode, (arg, entity_op.output(index))); } debug!(" Ports:"); for (node, value) in gen.values.iter() { debug!( " {:?} = {:?} (type {})", node, value, gen.llhd_type(value.0), ); } // Emit the actual contents of the entity. gen.emit_module_block(id, env, &hir.block, &entity_name)?; // Assign default values to undriven output ports. for port in ports.outputs.iter() { let value = gen.values[&port.accnode]; let driven = gen .builder .all_insts() .any(|inst| match gen.builder[inst].opcode() { llhd::ir::Opcode::Drv => gen.builder[inst].args()[0] == value.0, llhd::ir::Opcode::Inst => gen.builder[inst].output_args().contains(&value.0), _ => false, }); if driven { continue; } let default_value = gen.emit_const( if let Some(default) = port.default { gen.constant_value_of(default, env) } else { gen.type_default_value(port.ty) }, env, port.port.span(), )?; let zero_time = gen.mk_const_time(&num::zero(), 1, 0); gen.mk_drv(value, default_value, zero_time); } let unit = self.into.add_unit(ent); let result = Ok(Rc::new(EmittedModule { unit, mlir_symbol: entity_name.clone(), ports, })); self.tables.module_defs.insert(id.env(env), result.clone()); result } fn determine_module_ports( &mut self, ports: &'gcx [port_list::IntPort<'gcx>], env: ParamEnv, ) -> Result> { debug!("Determining ports with {:?}", env); let mut sig = llhd::ir::Signature::new(); let mut inputs = vec![]; let mut outputs = vec![]; // Go through the ports and expand each to one or more entity inputs and // outputs. for port in ports { let ty = self.type_of_int_port(Ref(port), env); debug!(" Port `{}` has type `{}`", port.name, ty); // Distinguish interfaces and regular ports. if let Some(intf) = ty.resolve_full().core.get_interface() { trace!(" Expanding interface {:?}", intf); // If a modport was specified, make a list of directions for // each port by name. let mut dirs = HashMap::new(); for port in intf.modport.iter().flat_map(|modport| modport.ports.iter()) { match port.data { ast::ModportPortData::Simple { dir, ref port } => { for port_name in port { if port_name.expr.is_none() { dirs.insert(port_name.name.value, dir.value); } } } } } trace!(" Modport-derived directions: {:?}", dirs); let signals = self.determine_interface_signals(intf, &ty.dims)?; for signal in signals { let (llty, mty) = signal_ty(self.emit_type_both(signal.ty)?); let name = format!("{}.{}", port.name, signal.name); trace!( " Signal `{}` of type `{}` / `{}` / `{}`", name, signal.ty, llty, mty ); let port = ModulePort { port, ty: signal.ty, mty, name, accnode: AccessedNode::Intf(port.id, signal.decl_id), default: signal.default, kind: ModulePortKind::IntfSignal { intf: intf.ast, env: intf.env, decl_id: signal.decl_id, }, }; match dirs.get(&signal.name.value).copied() { Some(ast::PortDir::Input) | Some(ast::PortDir::Ref) => { sig.add_input(llty); inputs.push(port); } Some(ast::PortDir::Output) | Some(ast::PortDir::Inout) | None => { sig.add_output(llty); outputs.push(port); } } } } else { trace!(" Regular port"); let (llty, mty) = signal_ty(self.emit_type_both(ty)?); let name = port.name.to_string(); let mp = ModulePort { port, ty, mty, name, accnode: AccessedNode::Regular(port.id), default: port.data.as_ref().and_then(|pd| pd.default), kind: ModulePortKind::Port, }; match port.dir { ast::PortDir::Input | ast::PortDir::Ref => { sig.add_input(llty); inputs.push(mp); } ast::PortDir::Inout | ast::PortDir::Output => { sig.add_output(llty); outputs.push(mp); } } } } debug!(" Signature: {}", sig); Ok(ModuleIntf { sig, inputs, outputs, }) } /// Map an interface to a list of signals defined by that interface. fn determine_interface_signals( &mut self, intf: &'gcx ty::InterfaceType<'gcx>, dims: &'gcx [ty::UnpackedDim<'gcx>], ) -> Result>> { let port_list = self.canonicalize_ports(intf.ast); let intf_hir = self.hir_of_interface(intf.ast)?; let signals = port_list .int .iter() .map(|p| Ok((p.id, p.name, p.data.as_ref().and_then(|d| d.default)))) .chain(intf_hir.block.decls.iter().map(|&id| { Ok(match self.hir_of(id)? { HirNode::VarDecl(x) => (id, x.name, x.init), _ => unreachable!(), }) })); let mut result = vec![]; for x in signals { let (decl_id, name, default) = x?; let mut sig_ty = self.type_of(decl_id, intf.env)?.clone(); sig_ty.dims.extend(dims); let sig_ty = sig_ty.intern(self.cx); result.push(IntfSignal { decl_id, ty: sig_ty, name, default, }); } Ok(result) } /// Emit the code for a procedure. pub fn emit_procedure( &mut self, id: NodeId, env: ParamEnv, name_prefix: &str, ) -> Result { let hir = match self.hir_of(id)? { HirNode::Proc(x) => x, _ => unreachable!(), }; // Find the accessed nodes. let acc = self.accessed_nodes(hir.stmt, env)?; trace!("Process accesses {:#?}", acc); let mut sig = llhd::ir::Signature::new(); let mut inputs = vec![]; let mut outputs = vec![]; let mut mlir_inputs = vec![]; let mut mlir_outputs = vec![]; for &id in acc.read.iter().filter(|id| !acc.written.contains(id)) { let ty = self.emit_type_both(match id { AccessedNode::Regular(id) => self.type_of(id, env)?, AccessedNode::Intf(intf, id) => { let intf_ty = self.type_of(intf, env)?; let intf_ty_inner = intf_ty.resolve_full().core.get_interface().unwrap(); let mut sig_ty = self.type_of(id, intf_ty_inner.env)?.clone(); sig_ty.dims.extend(&intf_ty.dims); sig_ty.intern(self.cx) } })?; sig.add_input(llhd::signal_ty(ty.0)); mlir_inputs.push(ty.1); inputs.push(id); } for &id in acc.written.iter() { let ty = self.emit_type_both(match id { AccessedNode::Regular(id) => self.type_of(id, env)?, AccessedNode::Intf(intf, id) => { let intf_ty = self.type_of(intf, env)?; let intf_ty_inner = intf_ty.resolve_full().core.get_interface().unwrap(); let mut sig_ty = self.type_of(id, intf_ty_inner.env)?.clone(); sig_ty.dims.extend(&intf_ty.dims); sig_ty.intern(self.cx) } })?; sig.add_output(llhd::signal_ty(ty.0)); mlir_outputs.push(ty.1); outputs.push(id); } trace!("Process Inputs: {:?}", inputs); trace!("Process Outputs: {:?}", outputs); trace!("Process Signature: {}", sig); trace!("Process MLIR Inputs: {:?}", mlir_inputs); trace!("Process MLIR Outputs: {:?}", mlir_outputs); trace!("Process Env: {:?}", self.param_env_data(env)); // Create process and entry block. let proc_name = format!( "{}.{}.{}.{}", name_prefix, match hir.kind { ast::ProcedureKind::Initial => "initial", ast::ProcedureKind::Always => "always", ast::ProcedureKind::AlwaysComb => "always_comb", ast::ProcedureKind::AlwaysLatch => "always_latch", ast::ProcedureKind::AlwaysFf => "always_ff", ast::ProcedureKind::Final => "final", }, id.as_usize(), env.0, ); let mut prok = llhd::ir::UnitData::new( llhd::ir::UnitKind::Process, llhd::ir::UnitName::Local(proc_name.clone()), sig, ); let mut builder = llhd::ir::UnitBuilder::new_anonymous(&mut prok); // Assign names to inputs and outputs. let guess_name = |id| { let (prefix, id) = match id { AccessedNode::Regular(id) => (None, id), AccessedNode::Intf(inst_id, id) => { let inst_name = match self.hir_of(inst_id).ok()? { HirNode::IntPort(x) => Some(x.name), HirNode::Inst(x) => Some(x.name), _ => None, }; (inst_name, id) } }; let name = match self.hir_of(id).ok()? { HirNode::VarDecl(x) => Some(x.name), HirNode::IntPort(x) => Some(x.name), _ => None, }; match (prefix, name) { (Some(prefix), Some(name)) => Some(format!("{}.{}", prefix, name)), (None, Some(name)) => Some(format!("{}", name)), _ => None, } }; for (i, &id) in inputs.iter().enumerate() { if let Some(name) = guess_name(id) { let value = builder.input_arg(i); builder.set_name(value, name); } } for (i, &id) in outputs.iter().enumerate() { if let Some(name) = guess_name(id) { let value = builder.output_arg(i); builder.set_name(value, name); } } // Create MLIR process. let mut mlir_builder = mlir::Builder::new(self.mcx); mlir_builder.set_loc(span_to_loc(self.mcx, hir.span())); mlir_builder.set_insertion_point_to_end(self.into_mlir.block()); let mut proc_op = circt::llhd::EntityLikeBuilder::new(&proc_name); for ty in mlir_inputs { proc_op.add_input("", circt::llhd::get_signal_type(ty)); } for ty in mlir_outputs { proc_op.add_output("", circt::llhd::get_signal_type(ty)); } let proc_op = proc_op.build_process(&mut mlir_builder); mlir_builder.set_insertion_point_to_start(proc_op.first_block()); // Create a mapping from read/written nodes to process parameters. let mut values = HashMap::<_, HybridValue>::new(); mlir_builder.set_loc(span_to_loc(self.mcx, hir.span())); for ((&id, arg), mlir_port) in inputs .iter() .zip(builder.input_args()) .chain(outputs.iter().zip(builder.output_args())) .zip(proc_op.ports()) { values.insert(id.into(), (arg, mlir_port)); } let mut pg = UnitGenerator::new(self, &mut builder, &mut values, &mut mlir_builder); let entry_blk = pg.builder.block(); pg.builder.append_to(entry_blk); // Determine which values are both read and written. These require // shadow variables to emulate the expected behaviour under blocking // assignments. let input_set: HashSet<_> = acc.read.iter().cloned().collect(); let output_set: HashSet<_> = acc.written.iter().cloned().collect(); for &id in input_set.intersection(&output_set) { let value = pg.values[&id.into()]; let init = pg.mk_prb(value); let shadow = pg.mk_var(init); if let Some(name) = pg .builder .get_name(value.0) .map(|name| format!("{}.shadow", name)) { pg.builder.set_name(shadow.0, name); } pg.shadows.insert(id.into(), shadow); } // Emit prologue and determine which basic block to jump back to. let head_blk = match hir.kind { ast::ProcedureKind::AlwaysComb | ast::ProcedureKind::AlwaysLatch => { let body_blk = pg.mk_block(Some("body")); let check_blk = pg.mk_block(Some("check")); pg.mk_br(body_blk); pg.append_to(check_blk); let trigger_on: Vec<_> = inputs .iter() .map(|&id| pg.emitted_value(id).clone()) .collect(); pg.mk_wait(body_blk, trigger_on, None); pg.append_to(body_blk); pg.flush_mir(); // ensure we don't reuse earlier expr probe pg.emit_shadow_update(); Some(check_blk) } ast::ProcedureKind::Final => { // TODO(fschuiki): Replace this with a cleverer way to implement a trigger-on-end. let body_blk = pg.mk_block(Some("body")); let endtimes = ( pg.builder.ins().const_time(llhd::TimeValue::new( "9001".parse().unwrap(), 0, 0, )), circt::llhd::ConstantTimeOp::with_seconds( pg.mlir_builder, &BigInt::from_i64(std::i64::MAX / 1_000_000_000_000) .unwrap() .into(), ) .into(), ); pg.builder.set_name(endtimes.0, "endtimes".to_string()); pg.mk_wait(body_blk, None, Some(endtimes)); pg.append_to(body_blk); pg.flush_mir(); // ensure we don't reuse earlier expr probe pg.emit_shadow_update(); None } ast::ProcedureKind::Initial => None, _ => { let mlir_entry_blk = pg.mlir_builder.add_block(); circt::std::BranchOp::new(pg.mlir_builder, mlir_entry_blk); pg.mlir_builder.set_insertion_point_to_end(mlir_entry_blk); Some((entry_blk, mlir_entry_blk)) } }; // Emit the main statement. pg.emit_stmt(hir.stmt, env)?; // Emit epilogue. match hir.kind { ast::ProcedureKind::Initial | ast::ProcedureKind::Final => { pg.builder.ins().halt(); circt::llhd::HaltOp::new(pg.mlir_builder); } ast::ProcedureKind::Always | ast::ProcedureKind::AlwaysComb | ast::ProcedureKind::AlwaysLatch | ast::ProcedureKind::AlwaysFf => { pg.builder.ins().br(head_blk.unwrap().0); circt::std::BranchOp::new(pg.mlir_builder, head_blk.unwrap().1); } } Ok(EmittedProcedure { unit: self.into.add_unit(prok), mlir_symbol: proc_name.clone(), inputs, outputs, }) } /// Map a type to an LLHD type (interned). fn emit_type(&mut self, ty: &'gcx UnpackedType<'gcx>) -> Result { self.emit_type_both(ty).map(|x| x.0) } /// Map a type to an LLHD type (interned). fn emit_type_both(&mut self, ty: &'gcx UnpackedType<'gcx>) -> Result { if let Some(x) = self.tables.interned_types.get(&ty) { x.clone() } else { let x = self.emit_type_uninterned(ty); self.tables.interned_types.insert(ty, x.clone()); x } } /// Map a type to an LLHD type. fn emit_type_uninterned(&mut self, ty: &'gcx UnpackedType<'gcx>) -> Result { if ty.is_error() { return Err(()); } let ty = ty.resolve_full(); // Handle things that coalesce easily to scalars. if ty.coalesces_to_llhd_scalar() { let bits = ty.get_bit_size().unwrap(); return Ok((llhd::int_ty(bits), mlir::get_integer_type(self.mcx, bits))); } // Handle arrays. if let Some(dim) = ty.outermost_dim() { let size = match dim.get_size() { Some(size) => size, None => panic!("cannot map unsized array `{}` to LLHD", ty), }; let inner = ty.pop_dim(self.cx).unwrap(); let (llty, mty) = self.emit_type_both(inner)?; return Ok(( llhd::array_ty(size, llty), circt::hw::get_array_type(mty, size), )); } // Handle structs. if let Some(strukt) = ty.get_struct() { let mut types = vec![]; let mut mtypes: Vec<(crate::common::name::RcStr, mlir::Type)> = vec![]; for member in &strukt.members { let (llty, mty) = self.emit_type_both(member.ty)?; types.push(llty); mtypes.push((member.name.value.as_str(), mty)); } return Ok(( llhd::struct_ty(types), circt::hw::get_struct_type(self.mcx, mtypes), )); } // Handle packed types. if let Some(packed) = ty.get_packed() { let packed = packed.resolve_full(); return match packed.core { // CAVEAT: We just use an empty HW dialect struct type as a // stand-in for an error or void type. ty::PackedCore::Void | ty::PackedCore::Error => Ok(( llhd::void_ty(), circt::hw::get_struct_type(self.mcx, Option::<(String, mlir::Type)>::None), )), ty::PackedCore::IntAtom(ty::IntAtomType::Time) => { Ok((llhd::time_ty(), circt::llhd::get_time_type(self.mcx))) } ty::PackedCore::Enum(ref enm) => self.emit_type_both(enm.base.to_unpacked(self.cx)), _ => unreachable!("emitting `{}` should have been handled above", packed), }; } // Everything else we cannot do. error!("Cannot map type {:#?}", ty); panic!("cannot map `{}` to LLHD", ty); } /// Execute the initialization step of a generate loop. fn execute_genvar_init(&mut self, id: NodeId, env: ParamEnv) -> Result { let hir = self.hir_of(id)?; match hir { HirNode::GenvarDecl(_) => Ok(env), HirNode::Stmt(stmt) => match stmt.kind { hir::StmtKind::Assign { lhs, rhs, kind: hir::AssignKind::Block(ast::AssignOp::Identity), } => { let target_id = self.resolve_node(lhs, env)?; let init_value = self.constant_value_of(rhs, env); let mut env_data = self.param_env_data(env).clone(); env_data.set_value(target_id, init_value); Ok(self.intern_param_env(env_data)) } _ => unreachable!(), }, _ => unreachable!(), } } /// Execute the iteration step of a generate loop. fn execute_genvar_step(&mut self, id: NodeId, env: ParamEnv) -> Result { let hir = self.hir_of(id)?; let mut env_data = self.param_env_data(env).clone(); let next = match hir { HirNode::Expr(expr) => match expr.kind { hir::ExprKind::Unary(op, target_id) => { let target_id = self.resolve_node(target_id, env)?; let current_value = self.constant_value_of(target_id, env); let next_value = match current_value.kind { ValueKind::Int(ref v, ..) => match op { hir::UnaryOp::PostInc | hir::UnaryOp::PreInc => Some(v + 1), hir::UnaryOp::PostDec | hir::UnaryOp::PreDec => Some(v - 1), _ => None, } .map(|v| value::make_int(current_value.ty, v)), _ => unreachable!(), }; next_value.map(|v| (target_id, self.intern_value(v))) } hir::ExprKind::Assign { .. } => { let mir = self.mir_rvalue(id, env); match mir.kind { mir::RvalueKind::Error => return Err(()), mir::RvalueKind::Assignment { lvalue, rvalue, .. } => { let target_id = match lvalue.kind { mir::LvalueKind::Error => return Err(()), mir::LvalueKind::Genvar(id) => id, _ => unreachable!(), }; let next_value = self.const_mir_rvalue(Ref(rvalue)); Some((target_id, next_value)) } _ => unreachable!(), } } _ => None, }, _ => None, }; match next { Some((target_id, next_value)) => { env_data.set_value(target_id, next_value); return Ok(self.intern_param_env(env_data)); } None => { self.emit( DiagBuilder2::error(format!( "{} is not a valid genvar iteration step", hir.desc_full() )) .span(hir.human_span()), ); Err(()) } } } /// Emit the code for a function or task. pub fn emit_function(&mut self, id: NodeId, env: ParamEnv) -> Result> { if let Some(x) = self.tables.function_defs.get(&id.env(env)) { return x.clone(); } let ast = self.ast_for_id(id).as_all().get_subroutine_decl().unwrap(); info!("Emit function `{}` with {:?}", ast.prototype.name, env); // Gather the port details and return type of the function. let args = self.canonicalize_func_args(Ref(ast)); let return_ty = typeck::return_type_of_function(self.cx, &ast.prototype, env); let lowered_return_ty = self.emit_type_both(return_ty)?; // Create the function signature and function builder. let mut sig = llhd::ir::Signature::new(); sig.set_return_type(lowered_return_ty.0.clone()); let func_name = ast.prototype.name.to_string(); let mut mlir_builder = mlir::Builder::new(self.mcx); mlir_builder.set_loc(span_to_loc(self.mcx, ast.span())); mlir_builder.set_insertion_point_to_end(self.into_mlir.block()); let mut func_op = circt::builtin::FunctionBuilder::new(&func_name); // Gather up the arguments. let mut arguments = vec![]; for arg in &args.args { let ty = self.type_of_func_arg(Ref(arg), env); let ty = self.emit_type_both(ty)?; let ty = match arg.dir { ast::SubroutinePortDir::Input => ty, ast::SubroutinePortDir::Output | ast::SubroutinePortDir::Inout | ast::SubroutinePortDir::Ref | ast::SubroutinePortDir::ConstRef => pointer_ty(ty), }; func_op.add_arg(arg.name.map(|x| x.value.as_str().to_string()), ty.1); sig.add_input(ty.0); arguments.push(arg); } // Gather up the results. if !return_ty.is_void() { func_op.add_result(None, lowered_return_ty.1); } // Create the function itself. let func_op = func_op.build(&mut mlir_builder); let mut func = llhd::ir::UnitData::new( llhd::ir::UnitKind::Function, llhd::ir::UnitName::Local(func_name.clone()), sig, ); let mut builder = llhd::ir::UnitBuilder::new_anonymous(&mut func); // Create a unit generator that we will use to populate the function // with instructions. let mut values = Default::default(); let mut gen = UnitGenerator::new(self, &mut builder, &mut values, &mut mlir_builder); let entry_blk = (gen.builder.block(), func_op.first_block()); gen.append_to(entry_blk); // Add entries for the function arguments to the value table such that // things we emit know how to use them. Apply the default values for // output operands. for (value, arg) in gen .builder .input_args() .zip(func_op.arguments()) .zip(args.args.iter()) { gen.values.insert(arg.ast.id().into(), value); if arg.dir == ast::SubroutinePortDir::Output { let default = match arg.default { Some(expr) => gen.emit_rvalue(expr.id(), env)?, None => { let ty = gen.type_of_func_arg(Ref(arg), env); let ty = gen.emit_type_both(ty)?; gen.emit_zero_for_type_both(ty) } }; gen.emit_blocking_assign_llhd((value, None), default)?; } } // Emit the body of the function. for item in &ast.items { if let ast::SubroutineItem::Stmt(stmt) = item { gen.emit_stmt(stmt.id(), env)?; } } // If the function body did not provide proper termination, add a // default return. if !gen.terminated { if return_ty.is_void() { gen.mk_ret(None); } else { let mut return_values = vec![]; let zero = gen.emit_zero_for_type_both(lowered_return_ty); return_values.push(zero); gen.mk_ret(return_values); } } // Add the function to the module and return a handle. let x = Ok(Rc::new(EmittedFunction { unit: self.into.add_unit(func), mlir_symbol: func_name, })); self.tables.function_defs.insert(id.env(env), x.clone()); x } } /// A name uniquifier. #[derive(Default)] struct NameUniquifier { names: HashSet, } impl NameUniquifier { pub fn add(&mut self, name: &str) -> String { if !self.names.contains(name) { let name = name.to_string(); self.names.insert(name.clone()); name } else { for i in 0.. { let current_name = format!("{}_{}", name, i); if self.names.insert(current_name.clone()) { return current_name; } } unreachable!() } } pub fn add_tmp(&mut self) -> String { self.add("_tmp") } } /// A code generator for functions, processes, and entities. struct UnitGenerator<'a, 'gcx, C> { /// The global code generator. gen: &'a mut CodeGenerator<'gcx, C>, /// The builder into which instructions are emitted. builder: &'a mut llhd::ir::UnitBuilder<'a>, /// The emitted LLHD values for various nodes. values: &'a mut HashMap, /// The MLIR builder which is used to create new operations. mlir_builder: &'a mut mlir::Builder, /// The constant values emitted into the unit. interned_consts: HashMap, Result>, /// The MIR lvalues emitted into the unit. interned_lvalues: HashMap)>>, /// The MIR rvalues emitted into the unit. interned_rvalues: HashMap<(NodeId, Mode), Result<(HybridValue, Mode)>>, /// The shadow variables introduced to handle signals which are both read /// and written in a process. shadows: HashMap, /// The emitted signal and instance names. unique_names: NameUniquifier, /// Whether the last emitted statement was a terminator. This indicates /// whether additional fall-through control flow ops are necessary, and /// whether a new (unreachable) block needs to be inserted to capture /// additional ops after a terminator. terminated: bool, /// A stack of blocks, the last of which will be branched to by a `continue` /// statement. continue_stack: Vec, /// A stack of blocks, the last of which will be branched to by a `break` /// statement. break_stack: Vec, } impl<'a, 'gcx, C> Deref for UnitGenerator<'a, 'gcx, C> { type Target = CodeGenerator<'gcx, C>; fn deref(&self) -> &CodeGenerator<'gcx, C> { &self.gen } } impl<'a, 'gcx, C> DerefMut for UnitGenerator<'a, 'gcx, C> { fn deref_mut(&mut self) -> &mut CodeGenerator<'gcx, C> { &mut self.gen } } impl<'a, 'gcx, C> UnitGenerator<'a, 'gcx, C> { fn new( gen: &'a mut CodeGenerator<'gcx, C>, builder: &'a mut llhd::ir::UnitBuilder<'a>, values: &'a mut HashMap, mlir_builder: &'a mut mlir::Builder, ) -> Self { Self { gen, builder, values, mlir_builder, interned_consts: Default::default(), interned_lvalues: Default::default(), interned_rvalues: Default::default(), shadows: Default::default(), unique_names: Default::default(), terminated: false, break_stack: Default::default(), continue_stack: Default::default(), } } } impl<'a, 'b, 'gcx, C> UnitGenerator<'a, 'gcx, &'b C> where C: Context<'gcx> + 'b, { fn emitted_value(&self, src: impl Into) -> HybridValue { let src = src.into(); match self.values.get(&src) { Some(&v) => v, None => bug_span!( self.span(match src { AccessedNode::Regular(id) => id, AccessedNode::Intf(_, id) => id, }), self.cx, "no value emitted for {:?}", src ), } } fn set_emitted_value(&mut self, src: impl Into, value: HybridValue) { let src = src.into(); self.values.insert(src, value); } /// Clear the cached MIR lvalues and rvalues. This should be called before /// or after emitting an expression, and at least for every statement. /// Otherwise MIR codegen might reuse values that have become out-of-date /// due to time passing in between statements. fn flush_mir(&mut self) { self.interned_lvalues.clear(); self.interned_rvalues.clear(); } /// Emit the code for the contents of a module. fn emit_module_block( &mut self, id: NodeId, env: ParamEnv, hir: &hir::ModuleBlock, name_prefix: &str, ) -> Result<()> { // Emit declarations. for &decl_id in &hir.decls { let hir = match self.hir_of(decl_id)? { HirNode::VarDecl(x) => x, _ => unreachable!(), }; let ty = self.type_of(decl_id, env)?; let value = self.emit_varnet_decl(decl_id, ty, env, hir.init)?; self.builder.set_name(value.0, hir.name.value.into()); self.values.insert(decl_id.into(), value); } // Emit interface instances. for &inst_id in &hir.insts { // Resolve the instantiation details. let inst = match self.hir_of(inst_id)? { HirNode::Inst(x) => x, _ => unreachable!(), }; let inst = self.inst_details(Ref(inst), env)?; // Compute the array dimensions for the signals. // let mut dims = vec![]; let inst_ty = self.type_of_inst(Ref(inst.hir), env); let intf_ty = match inst_ty.resolve_full().core.get_interface() { Some(x) => x, None => continue, }; trace!( "Interface instance is of type `{}` ({:?})", inst_ty, intf_ty ); // Expand the interface declarations. let signals = self.determine_interface_signals(intf_ty, &inst_ty.dims)?; let mut signal_lookup = HashMap::new(); for signal in signals { let value = self.emit_varnet_decl(signal.decl_id, signal.ty, intf_ty.env, signal.default)?; self.builder .set_name(value.0, format!("{}.{}", inst.hir.name, signal.name)); let src = AccessedNode::Intf(inst_id, signal.decl_id); trace!( "Emitted value for {:?} {}.{}", src, inst.hir.name, signal.name ); self.values.insert(src, value); signal_lookup.insert(signal.decl_id, value); } // Generate the code for the port assignments. let port_list = self.canonicalize_ports(intf_ty.ast); let ports = self.determine_module_ports(&port_list.int, intf_ty.env)?; let (inputs, outputs) = self.emit_port_connections( port_list, inst.as_ref(), &ports.inputs, &ports.outputs, )?; trace!("Attaching interface inputs {:?}", inputs); trace!("Attaching interface outputs {:?}", outputs); trace!("Signal lookup: {:?}", signal_lookup); // Actually wire up the ports. let inputs = inputs.into_iter().zip(ports.inputs.iter()); let outputs = outputs.into_iter().zip(ports.outputs.iter()); for (assigned, port) in inputs.chain(outputs) { trace!( "Assign `{}` ({:?}) = {:?}", port.name, port.port.id, assigned ); let sig = signal_lookup[&port.port.id]; self.builder.ins().con(sig.0, assigned.0); circt::llhd::ConnectOp::new(self.mlir_builder, sig.1, assigned.1); } } // Emit assignments. for &assign_id in &hir.assigns { let hir = match self.hir_of(assign_id)? { HirNode::Assign(x) => x, _ => unreachable!(), }; // Map the assignment to an MIR node. let assign_mir = self.mir_assignment_from_concurrent(Ref(hir), env); debug!("Concurrent assignment: {:#?}", assign_mir); // Simplify the assignment to eliminate concatenations on the // left-hand side. let simplified = self.mir_simplify_assignment(Ref(assign_mir)); debug!("Simplified to: {:#?}", simplified); // Check for sanity. for &assign in &simplified { assert_type!(assign.rhs.ty, assign.lhs.ty, assign.rhs.span, self.cx); if assign.is_error() { return Err(()); } } // Emit the assignments. let delay = self.mk_const_time(&num::zero(), 0, 1); for &assign in &simplified { let lhs = self.emit_mir_lvalue(assign.lhs)?; let rhs = self.emit_mir_rvalue(assign.rhs)?; self.mk_drv(lhs.0, rhs, delay); } } // Emit module instantiations. for &inst_id in &hir.insts { // Resolve the instantiation details. let inst = match self.hir_of(inst_id)? { HirNode::Inst(x) => x, _ => unreachable!(), }; let inst = self.inst_details(Ref(inst), env)?; let target_module = match inst.target.kind { InstTarget::Module(x) => self.hir_of_module(x)?, _ => continue, }; // Emit the instantiated module. let target = self.emit_module_with_env(target_module.id, inst.inner_env)?; // Prepare the port assignments. let (inputs, outputs) = self.emit_port_connections( target_module.ports_new, inst.as_ref(), &target.ports.inputs, &target.ports.outputs, )?; // Instantiate the module. let ext_unit = self.builder.add_extern( self.into.unit(target.unit).name().clone(), self.into.unit(target.unit).sig().clone(), ); if !inst.hir.ast.dims.is_empty() { bug_span!( inst.hir.ast.span(), self.cx, "instance arrays of modules not supported" ); } self.builder.ins().inst( ext_unit, inputs.iter().map(|x| x.0).collect(), outputs.iter().map(|x| x.0).collect(), ); circt::llhd::InstanceOp::new( self.mlir_builder, &self.unique_names.add(&inst.hir.name.value.to_string()), &target.mlir_symbol, inputs.iter().map(|x| x.1), outputs.iter().map(|x| x.1), ); } // Emit generate blocks. for &gen_id in &hir.gens { let hir = match self.hir_of(gen_id)? { HirNode::Gen(x) => x, _ => unreachable!(), }; #[allow(unreachable_patterns)] match hir.kind { hir::GenKind::If { cond, ref main_body, ref else_body, } => { let k = self.constant_value_of(cond, env); if k.is_false() { if let Some(else_body) = else_body { self.emit_module_block(id, env, else_body, name_prefix)?; } } else { self.emit_module_block(id, env, main_body, name_prefix)?; } } hir::GenKind::For { ref init, cond, step, ref body, } => { let mut local_env = env; for &i in init { local_env = self.execute_genvar_init(i, local_env)?; } while self.constant_value_of(cond, local_env).is_true() { self.emit_module_block(id, local_env, body, name_prefix)?; local_env = self.execute_genvar_step(step, local_env)?; } } _ => return self.unimp_msg("code generation for", hir), } } // Emit and instantiate procedures. for &proc_id in &hir.procs { let prok = self.emit_procedure(proc_id, env, name_prefix)?; let lookup_value = |&id: &AccessedNode| match self.values.get(&id) { Some(v) => v.clone(), None => { self.emit( DiagBuilder2::bug(format!( "{} used as input/output of {}, but no value has been emitted", self.hir_of(id.id()).unwrap().desc_full(), self.hir_of(proc_id).unwrap().desc_full(), )) .span(self.span(id.id())), ); panic!("no value emitted for {:?}", id); } }; let inputs: Vec<_> = prok.inputs.iter().map(lookup_value).collect(); let outputs: Vec<_> = prok.outputs.iter().map(lookup_value).collect(); let ext_unit = self.builder.add_extern( self.into.unit(prok.unit).name().clone(), self.into.unit(prok.unit).sig().clone(), ); self.builder.ins().inst( ext_unit, inputs.iter().map(|x| x.0).collect(), outputs.iter().map(|x| x.0).collect(), ); circt::llhd::InstanceOp::new( self.mlir_builder, &self.unique_names.add(&format!("{}_inst", prok.mlir_symbol)), &prok.mlir_symbol, inputs.iter().map(|x| x.1), outputs.iter().map(|x| x.1), ); } Ok(()) } /// Emit code for the connections made in a port list. fn emit_port_connections( &mut self, port_list: &PortList<'gcx>, inst: &InstDetails<'gcx>, inputs: &[ModulePort<'gcx>], outputs: &[ModulePort<'gcx>], ) -> Result<(Vec, Vec)> { // Map the values associated with the external ports to internal // ports. let mut port_mapping_int: HashMap = HashMap::new(); for port in &port_list.ext_pos { let mapping = match inst.ports.find(port.id) { Some(m) => m, None => continue, }; if port.exprs.len() > 1 { self.emit( DiagBuilder2::bug("port expressions with concatenations not supported") .span(inst.hir.span()) .add_note("Port declared here:") .span(port.span), ); continue; } let expr = match port.exprs.iter().next() { Some(m) => m, None => continue, }; if !expr.selects.is_empty() { self.emit( DiagBuilder2::bug("port expressions with selections not supported") .span(inst.hir.span()) .add_note("Port declared here:") .span(port.span), ); continue; } let int = &port_list.int[expr.port]; if port_mapping_int.insert(int.id, mapping).is_some() { self.emit( DiagBuilder2::error(format!("port `{}` connected multiple times", int.name)) .span(self.span(mapping.id())), ); } } trace!("Internal Port Mapping: {:?}", port_mapping_int); // Connect to the actual internal ports emitted as the module's port // interface. let mut map_port = |port: &ModulePort<'gcx>, lvalue: bool| { trace!( "Mapping port `{}` of type `{}` as {}", port.name, port.ty, match lvalue { true => "lvalue", false => "rvalue", } ); if let Some(&mapping) = port_mapping_int.get(&port.port.id) { // Emit the assigned node as rvalue or lvalue, depending on // the port direction. if lvalue { let mir = self.mir_lvalue(mapping.id(), mapping.env()); if mir.is_error() { return Err(()); } let mir = match port.kind { ModulePortKind::Port => mir, ModulePortKind::IntfSignal { decl_id, env, .. } => { self.arena().alloc_mir_lvalue(mir::Lvalue { id: NodeId::alloc(), origin: mir.origin, env, span: mir.span, ty: port.ty, kind: mir::LvalueKind::IntfSignal(mir, decl_id), }) } }; self.emit_mir_lvalue(mir).map(|x| x.0) } else { let mir = self.mir_rvalue(mapping.id(), mapping.env()); if mir.is_error() { return Err(()); } let mir = match port.kind { ModulePortKind::Port => mir, ModulePortKind::IntfSignal { decl_id, env, .. } => { self.arena().alloc_mir_rvalue(mir::Rvalue { id: NodeId::alloc(), origin: mir.origin, env, span: mir.span, ty: port.ty, kind: mir::RvalueKind::IntfSignal(mir, decl_id), konst: false, }) } }; self.emit_mir_rvalue_mode(mir, Mode::Signal) } } else { // Emit an auxiliary signal with the default value for this // port or type. let ty = self.type_of_int_port(Ref(port.port), inst.inner_env); let value = match port.port.data.as_ref().and_then(|d| d.default) { Some(default) => { self.emit_rvalue_mode(default, inst.inner_env, Mode::Signal)? } None => { let v = self.type_default_value(ty); let v = self.emit_const(v, inst.inner_env, port.port.span)?; ( self.builder.ins().sig(v.0), circt::llhd::SignalOp::new( self.mlir_builder, &self.unique_names.add_tmp(), v.1, ) .into(), ) } }; self.builder .set_name(value.0, format!("{}.{}.default", inst.hir.name, port.name)); Ok(value) } }; let inputs = inputs .iter() .map(|p| map_port(p, false)) .collect::>>()?; let outputs = outputs .iter() .map(|p| map_port(p, true)) .collect::>>()?; Ok((inputs, outputs)) } /// Map a value to an LLHD constant (interned). fn emit_const(&mut self, value: Value<'gcx>, env: ParamEnv, span: Span) -> Result { if let Some(x) = self.interned_consts.get(value) { x.clone() } else { let x = self.emit_const_uninterned(value, env, span); debug!("Emitted constant {:?}", x); // self.interned_consts.insert(value, x.clone()); x } } /// Map a value to an LLHD constant. fn emit_const_uninterned( &mut self, value: Value<'gcx>, env: ParamEnv, span: Span, ) -> Result { if value.ty.is_error() { return Err(()); } match value.kind { ValueKind::Int(ref k, ..) => { let size = value.ty.simple_bit_vector(self.cx, span).size; Ok(( self.builder.ins().const_int((size, k.clone())), circt::hw::ConstantOp::new(self.mlir_builder, std::cmp::max(size, 1), k).into(), )) } ValueKind::Time(ref k) => Ok(( self.builder .ins() .const_time(llhd::value::TimeValue::new(k.clone(), 0, 0)), circt::llhd::ConstantTimeOp::with_seconds(self.mlir_builder, k).into(), )), ValueKind::StructOrArray(ref v) => { let ty = self.emit_type_both(value.ty)?; let mut ll_fields = vec![]; let mut mlir_fields = vec![]; for field in v { let field = self.emit_const(field, env, span)?; ll_fields.push(field.0); mlir_fields.push(field.1); } if let Some(_dim) = value.ty.outermost_dim() { Ok(( self.builder.ins().array(ll_fields), circt::hw::ArrayCreateOp::new(self.mlir_builder, ty.1, mlir_fields).into(), )) } else if let Some(_strukt) = value.ty.get_struct() { Ok(( self.builder.ins().strukt(ll_fields), circt::hw::StructCreateOp::new(self.mlir_builder, ty.1, mlir_fields).into(), )) } else { panic!( "invalid type `{}` for const struct/array value {:#?}", value.ty, value ); } } ValueKind::Error => Err(()), _ => panic!( "invalid combination of type `{}` and value {:#?}", value.ty, value ), } } /// Emit the zero value for an LLHD type. /// /// This function is ultimately expected to be moved into LLHD. fn emit_zero_for_type(&mut self, ty: &llhd::Type) -> llhd::ir::Value { match **ty { llhd::TimeType => { self.builder .ins() .const_time(llhd::value::TimeValue::new(num::zero(), 0, 0)) } llhd::IntType(w) => self.builder.ins().const_int((w, 0)), llhd::SignalType(ref ty) => { let inner = self.emit_zero_for_type(ty); self.builder.ins().sig(inner) } llhd::PointerType(ref ty) => { let inner = self.emit_zero_for_type(ty); self.builder.ins().var(inner) } llhd::ArrayType(l, ref ty) => { let inner = self.emit_zero_for_type(ty); self.builder.ins().array_uniform(l, inner) } llhd::StructType(ref tys) => { let inner = tys.iter().map(|ty| self.emit_zero_for_type(ty)).collect(); self.builder.ins().strukt(inner) } _ => panic!("no zero-value for type {}", ty), } } /// Emit the zero value for an MLIR type. fn emit_zero_for_type_mlir(&mut self, ty: mlir::Type) -> mlir::Value { if circt::hw::is_array_type(ty) { let inner = self.emit_zero_for_type_mlir(circt::hw::array_type_element(ty)); circt::hw::ArrayCreateOp::new( self.mlir_builder, ty, (0..circt::hw::array_type_size(ty)).map(|_| inner), ) .into() } else if circt::hw::is_struct_type(ty) { let inner: Vec<_> = circt::hw::struct_type_fields(ty) .map(|(_, ty)| self.emit_zero_for_type_mlir(ty)) .collect(); circt::hw::StructCreateOp::new(self.mlir_builder, ty, inner).into() } else if circt::llhd::is_pointer_type(ty) { let inner = self.emit_zero_for_type_mlir(circt::llhd::pointer_type_element(ty)); circt::llhd::VariableOp::new(self.mlir_builder, inner).into() } else if circt::llhd::is_signal_type(ty) { let inner = self.emit_zero_for_type_mlir(circt::llhd::signal_type_element(ty)); circt::llhd::SignalOp::new(self.mlir_builder, &self.unique_names.add_tmp(), inner) .into() } else if circt::llhd::is_time_type(ty) { circt::llhd::ConstantTimeOp::with_delta(self.mlir_builder, 0).into() } else if mlir::is_integer_type(ty) { circt::hw::ConstantOp::new( self.mlir_builder, std::cmp::max(mlir::integer_type_width(ty), 1), &BigInt::zero(), ) .into() } else { panic!("no zero value for type {}", ty) } } /// Emit the zero value for a type. fn emit_zero_for_type_both(&mut self, ty: HybridType) -> HybridValue { ( self.emit_zero_for_type(&ty.0), self.emit_zero_for_type_mlir(ty.1), ) } /// Get the type of an LLHD value. fn llhd_type(&self, value: llhd::ir::Value) -> llhd::Type { self.builder.value_type(value) } /// Get the type of an emitted value. fn value_type(&self, value: HybridValue) -> HybridType { (self.builder.value_type(value.0), value.1.ty()) } /// Emit the code for an rvalue. fn emit_rvalue(&mut self, expr_id: NodeId, env: ParamEnv) -> Result { self.emit_rvalue_mode(expr_id, env, Mode::Value) } /// Emit the code for an rvalue. fn emit_rvalue_mode( &mut self, expr_id: NodeId, env: ParamEnv, mode: Mode, ) -> Result { let mir = self.mir_rvalue(expr_id, env); self.emit_mir_rvalue_mode(mir, mode) } /// Emit the code for an MIR rvalue. fn emit_mir_rvalue(&mut self, mir: &'gcx mir::Rvalue<'gcx>) -> Result { self.emit_mir_rvalue_mode(mir, Mode::Value) } /// Emit the code for an MIR rvalue. fn emit_mir_rvalue_mode( &mut self, mir: &'gcx mir::Rvalue<'gcx>, mode: Mode, ) -> Result { let (value, actual_mode) = if let Some(x) = self.interned_rvalues.get(&(mir.id, mode)) { x.clone()? } else { let x = self.emit_mir_rvalue_uninterned(mir, mode); self.interned_rvalues.insert((mir.id, mode), x); x? }; match (mode, actual_mode) { (Mode::Signal, Mode::Value) => { let ty = self.value_type(value); let init = self.emit_zero_for_type_both(ty); let sig = ( self.builder.ins().sig(init.0), circt::llhd::SignalOp::new( self.mlir_builder, &self.unique_names.add_tmp(), init.1, ) .into(), ); let delay = self.mk_const_time(&num::zero(), 1, 0); self.mk_drv(sig, value, delay); Ok(sig) } (Mode::Value, Mode::Signal) => unreachable!(), _ => Ok(value), } } /// Emit the code for an MIR rvalue. /// /// Wrapper around `emit_mir_rvalue_inner` that asserts the LLHD type of the /// result matches expectations. fn emit_mir_rvalue_uninterned( &mut self, mir: &'gcx mir::Rvalue<'gcx>, mode_hint: Mode, ) -> Result<(HybridValue, Mode)> { let result = self.emit_mir_rvalue_inner(mir, mode_hint); match result { Ok((result, actual_mode)) => { let llty_exp = self.emit_type(mir.ty)?; let llty_exp = match actual_mode { Mode::Value => llty_exp, Mode::Signal => llhd::signal_ty(llty_exp), }; let llty_act = self.llhd_type(result.0); assert_span!( llty_exp == llty_act, mir.span, self.cx, "codegen for MIR rvalue `{}` should produce `{}`, but got `{}`; {:#?}", mir.span.extract(), llty_exp, llty_act, mir ); } Err(()) => (), } result } /// Emit the code for an MIR rvalue. /// /// The `mode_hint` parameter provides a hint if the caller ultimately wants /// the result to be a signal or a value. For example, port connections to /// an instance would set the hint to `Mode::Signal`, while a binary expr /// would set it to `Mode::Value`. The function is free to ignore the hint, /// but will provide the actual mode it produced as part of the return /// value. fn emit_mir_rvalue_inner( &mut self, mir: &'gcx mir::Rvalue<'gcx>, mode_hint: Mode, ) -> Result<(HybridValue, Mode)> { self.mlir_builder.set_loc(span_to_loc(self.mcx, mir.span)); // If the value is a constant, emit the fully folded constant value. if mir.is_const() { let value = self.const_mir_rvalue(mir.into()); return self .emit_const(value, mir.env, mir.span) .map(|v| (v, Mode::Value)); } let value: HybridValue = match mir.kind { mir::RvalueKind::Var(id) | mir::RvalueKind::Port(id) | mir::RvalueKind::Arg(id) => { let sig = self .shadows .get(&id.into()) .cloned() .unwrap_or_else(|| self.emitted_value(id)); if mode_hint == Mode::Signal && self.llhd_type(sig.0).is_signal() { return Ok((sig, Mode::Signal)); } else { self.emit_prb_or_var(sig) } } mir::RvalueKind::Intf(_) => { self.emit( DiagBuilder2::error("interface cannot be used in an expression").span(mir.span), ); return Err(()); } // Interface signals require special care, because they are emitted // in a transposed fashion. mir::RvalueKind::IntfSignal(value, signal) => { return self.emit_rvalue_interface(value, signal, mode_hint); } mir::RvalueKind::CastValueDomain { value, .. } => { // TODO(fschuiki): Turn this into an actual `iN` to `lN` cast. return self.emit_mir_rvalue_inner(value, mode_hint); } mir::RvalueKind::Transmute(value) => { // Transmute is a simple no-op. return self.emit_mir_rvalue_inner(value, mode_hint); } mir::RvalueKind::CastSign(_, value) => { // Sign conversions are no-ops in LLHD since they merely // influence the type system. return self.emit_mir_rvalue_inner(value, mode_hint); } mir::RvalueKind::CastToBool(value) => { let value = self.emit_mir_rvalue(value)?; let ty = self.value_type(value); let zero = self.emit_zero_for_type_both(ty); self.mk_cmp(CmpPred::Neq, value, zero) } mir::RvalueKind::Truncate(target_width, value) => { let llvalue = self.emit_mir_rvalue(value)?; self.mk_ext_slice(llvalue, 0, target_width) } mir::RvalueKind::ZeroExtend(_, value) => { let width = value.ty.simple_bit_vector(self.cx, value.span).size; let llty = self.emit_type_both(mir.ty)?; let result = self.emit_zero_for_type_both(llty); let value = self.emit_mir_rvalue(value)?; let result = self.mk_ins_slice(result, value, 0, width); self.builder.set_name(result.0, "zext".to_string()); result } mir::RvalueKind::SignExtend(_, value) => { let width = value.ty.simple_bit_vector(self.cx, value.span).size; let llty = self.emit_type_both(mir.ty)?; let value = self.emit_mir_rvalue(value)?; let sign = self.mk_ext_slice(value, width - 1, 1); let zeros = self.emit_zero_for_type_both(llty); let ones = self.mk_not(zeros); let mux = self.mk_mux(sign, ones, zeros); let result = self.mk_ins_slice(mux, value, 0, width); self.builder.set_name(result.0, "sext".to_string()); result } mir::RvalueKind::ConstructArray(ref indices) => { let values = (0..indices.len()) .map(|i| self.emit_mir_rvalue(indices[&i])) .collect::>>()?; let llty = self.emit_type_both(mir.ty)?; self.mk_array(llty, &values) } mir::RvalueKind::ConstructStruct(ref members) => { let members = members .iter() .map(|&v| self.emit_mir_rvalue(v)) .collect::>>()?; let llty = self.emit_type_both(mir.ty)?; self.mk_struct(llty, &members) } mir::RvalueKind::Const(k) => self.emit_const(k, mir.env, mir.span)?, mir::RvalueKind::Index { value, base, length, } => { let target = self.emit_mir_rvalue(value)?; self.emit_rvalue_index(value.ty, target, base, length)? } mir::RvalueKind::Member { value, field } => { let target = self.emit_mir_rvalue(value)?; let value = self.mk_ext_field(target, field); value } mir::RvalueKind::UnaryBitwise { op, arg } => { let arg = self.emit_mir_rvalue(arg)?; match op { mir::UnaryBitwiseOp::Not => self.mk_not(arg), } } mir::RvalueKind::BinaryBitwise { op, lhs, rhs } => { let lhs = self.emit_mir_rvalue(lhs)?; let rhs = self.emit_mir_rvalue(rhs)?; match op { mir::BinaryBitwiseOp::And => self.mk_and(lhs, rhs), mir::BinaryBitwiseOp::Or => self.mk_or(lhs, rhs), mir::BinaryBitwiseOp::Xor => self.mk_xor(lhs, rhs), } } mir::RvalueKind::IntComp { op, lhs, rhs, sign, .. } => { let lhs = self.emit_mir_rvalue(lhs)?; let rhs = self.emit_mir_rvalue(rhs)?; let signed = sign.is_signed(); match op { mir::IntCompOp::Eq => self.mk_cmp(CmpPred::Eq, lhs, rhs), mir::IntCompOp::Neq => self.mk_cmp(CmpPred::Neq, lhs, rhs), mir::IntCompOp::Lt if signed => self.mk_cmp(CmpPred::Slt, lhs, rhs), mir::IntCompOp::Leq if signed => self.mk_cmp(CmpPred::Sle, lhs, rhs), mir::IntCompOp::Gt if signed => self.mk_cmp(CmpPred::Sgt, lhs, rhs), mir::IntCompOp::Geq if signed => self.mk_cmp(CmpPred::Sge, lhs, rhs), mir::IntCompOp::Lt => self.mk_cmp(CmpPred::Ult, lhs, rhs), mir::IntCompOp::Leq => self.mk_cmp(CmpPred::Ule, lhs, rhs), mir::IntCompOp::Gt => self.mk_cmp(CmpPred::Ugt, lhs, rhs), mir::IntCompOp::Geq => self.mk_cmp(CmpPred::Uge, lhs, rhs), } } mir::RvalueKind::IntUnaryArith { op, arg, .. } => { let arg = self.emit_mir_rvalue(arg)?; match op { mir::IntUnaryArithOp::Neg => self.mk_neg(arg), } } mir::RvalueKind::IntBinaryArith { op, lhs, rhs, sign, .. } => { let lhs_ll = self.emit_mir_rvalue(lhs)?; let rhs_ll = self.emit_mir_rvalue(rhs)?; let signed = sign.is_signed(); match op { mir::IntBinaryArithOp::Add => self.mk_add(lhs_ll, rhs_ll), mir::IntBinaryArithOp::Sub => self.mk_sub(lhs_ll, rhs_ll), mir::IntBinaryArithOp::Mul if signed => self.mk_smul(lhs_ll, rhs_ll), mir::IntBinaryArithOp::Div if signed => self.mk_sdiv(lhs_ll, rhs_ll), mir::IntBinaryArithOp::Mod if signed => self.mk_smod(lhs_ll, rhs_ll), mir::IntBinaryArithOp::Mul => self.mk_umul(lhs_ll, rhs_ll), mir::IntBinaryArithOp::Div => self.mk_udiv(lhs_ll, rhs_ll), mir::IntBinaryArithOp::Mod => self.mk_umod(lhs_ll, rhs_ll), // The `x**y` operator requires special love, because there // is no direct equivalent for it in LLHD. mir::IntBinaryArithOp::Pow => { // If the exponent is a constant, we simply unroll. if rhs.is_const() { let count = self.const_mir_rvalue_int(Ref(rhs))?; let mut value = lhs_ll; for _ in 1..count.to_usize().unwrap() { value = self.mk_umul(value, lhs_ll); } return Ok((value, Mode::Value)); } // If the base is a constant power of two, we translate // `x**y` into `1 << (y * log2(x))`. if lhs.is_const() { let base = self.const_mir_rvalue_int(Ref(lhs))?.to_usize(); // `log2(base)` let lg2 = base.and_then(|base| { if base.is_power_of_two() { Some(base.trailing_zeros()) } else { None } }); // `y * log2(base)` let rhs_ll = lg2.map(|lg2| { let width = self.llhd_type(rhs_ll.0).len(); let lg2 = self.mk_const_int(width, &BigInt::from(lg2)); self.mk_umul(lg2, rhs_ll) }); if let Some(rhs_ll) = rhs_ll { // `1` let width = self.llhd_type(lhs_ll.0).len(); let lhs_ll = self.mk_const_int(width, &BigInt::one()); // `1 << (y * log2(base))` let zeros = self.emit_zero_for_type_both(( self.llhd_type(lhs_ll.0), lhs_ll.1.ty(), )); return Ok((self.mk_shl(lhs_ll, zeros, rhs_ll), Mode::Value)); } } // Otherwise we just complain. self.emit( DiagBuilder2::error("`**` operator on non-constants not supported") .span(mir.span), ); return Err(()); } } } mir::RvalueKind::Concat(ref values) => { let mut offset = 0; let llty = self.emit_type_both(mir.ty)?; trace!( "Concatenating {} values into `{}` (as {:?})", values.len(), mir.ty, llty ); let mut result = self.emit_zero_for_type_both(llty); for value in values.iter().rev() { let width = value.ty.simple_bit_vector(self.cx, value.span).size; let llval = self.emit_mir_rvalue(value)?; trace!( " - Value has width {}, type `{}`, in LLHD `{}`", width, value.ty, self.llhd_type(llval.0) ); if width > 0 { result = self.mk_ins_slice(result, llval, offset, width); offset += width; } } self.builder.set_name(result.0, "concat".to_string()); result } mir::RvalueKind::Repeat(times, value) => { let width = value.ty.simple_bit_vector(self.cx, value.span).size; let value = self.emit_mir_rvalue(value)?; let llty = self.emit_type_both(mir.ty)?; let mut result = self.emit_zero_for_type_both(llty); for i in 0..times { result = self.mk_ins_slice(result, value, i * width, width); } self.builder.set_name(result.0, "repeat".to_string()); result } mir::RvalueKind::Shift { op, arith, value, amount, } => { let value = self.emit_mir_rvalue(value)?; let amount = self.emit_mir_rvalue(amount)?; let value_ty = self.builder.unit().value_type(value.0); let hidden = self.emit_zero_for_type_both((value_ty.clone(), value.1.ty())); let hidden = if arith && op == mir::ShiftOp::Right { let ones = self.mk_not(hidden); let sign = self.mk_ext_slice(value, value_ty.unwrap_int() - 1, 1); self.mk_mux(sign, ones, hidden) } else { hidden }; match op { mir::ShiftOp::Left => self.mk_shl(value, hidden, amount), mir::ShiftOp::Right => self.mk_shr(value, hidden, amount), } } mir::RvalueKind::Ternary { cond, true_value, false_value, } => { let cond = self.emit_mir_rvalue(cond)?; let true_value = self.emit_mir_rvalue(true_value)?; let false_value = self.emit_mir_rvalue(false_value)?; self.mk_mux(cond, true_value, false_value) } mir::RvalueKind::Reduction { op, arg } => { let width = arg.ty.simple_bit_vector(self.cx, arg.span).size; let arg = self.emit_mir_rvalue(arg)?; let mut value = self.mk_ext_slice(arg, 0, 1); for i in 1..width { let bit = self.mk_ext_slice(arg, i, 1); value = match op { mir::BinaryBitwiseOp::And => self.mk_and(value, bit), mir::BinaryBitwiseOp::Or => self.mk_or(value, bit), mir::BinaryBitwiseOp::Xor => self.mk_xor(value, bit), }; } value } mir::RvalueKind::Assignment { lvalue, rvalue, result, } => { self.emit_mir_blocking_assign(lvalue, rvalue)?; self.emit_mir_rvalue(result)? } mir::RvalueKind::PackString(value) | mir::RvalueKind::UnpackString(value) => bug_span!( value.span, self.cx, "codegen for string packing/unpacking not implemented" ), mir::RvalueKind::StringComp { .. } => bug_span!( mir.span, self.cx, "runtime string comparisons not implemented" ), mir::RvalueKind::ApplyTimescale(..) => bug_span!( mir.span, self.cx, "runtime int-to-time conversion not implemented" ), mir::RvalueKind::Call { target, ref args } => { // Ensure the function is emitted. let func_env = self.default_param_env(); let func = self.emit_function(target.id(), func_env)?; let ext_unit = self.builder.add_extern( self.into.unit(func.unit).name().clone(), self.into.unit(func.unit).sig().clone(), ); // Assemble the arguments passed into the function. let mut input_args = vec![]; for arg in args { match arg { mir::CallArg::Input(rv) => { input_args.push(self.emit_mir_rvalue(rv)?); } mir::CallArg::Output(ty, _) => { let ty = self.emit_type_both(ty)?; let zero = self.emit_zero_for_type_both(ty); let var = self.mk_var(zero); input_args.push(var); } mir::CallArg::Inout(rv, _) => { let init = self.emit_mir_rvalue(rv)?; let var = self.mk_var(init); input_args.push(var); } mir::CallArg::Ref(lv) => { input_args.push(self.emit_mir_lvalue(lv)?.0); } } } // Determine the return types of the function. let return_ty = typeck::return_type_of_function(self.cx, &target.prototype, mir.env); let mut result_tys = vec![]; if !return_ty.is_void() { result_tys.push(self.emit_type_both(return_ty)?.1); } // Emit the call. let call_op = circt::std::CallOp::new( self.mlir_builder, &func.mlir_symbol, input_args.iter().map(|&(_, mlir)| mlir), result_tys, ); // Perform assignments for arguments that copy back out of the // function. for (arg, &value) in args.iter().zip(input_args.iter()) { match arg { mir::CallArg::Output(_, Some(lv)) | mir::CallArg::Inout(_, Some(lv)) => { let rvid = self.alloc_id(lv.span); self.set_emitted_value(rvid, value); let rv = self.arena().alloc_mir_rvalue(mir::Rvalue { id: rvid, origin: lv.id, env: mir.env, span: lv.span, ty: lv.ty, kind: mir::RvalueKind::Var(rvid), konst: false, }); let assign = self.arena().alloc_mir_assignment(mir::Assignment { id: lv.id, env: mir.env, span: lv.span, ty: lv.ty, lhs: lv, rhs: rv, }); let simplified = self.mir_simplify_assignment(Ref(assign)); for assign in simplified { let lhs = self.emit_mir_lvalue(assign.lhs)?; let rhs = self.emit_mir_rvalue(assign.rhs)?; self.emit_blocking_assign_llhd(lhs, rhs)?; } } mir::CallArg::Output(_, None) | mir::CallArg::Inout(_, None) | mir::CallArg::Input(..) | mir::CallArg::Ref(..) => (), } } // Unpack the results. let result = if !return_ty.is_void() { call_op.result(0) } else { mlir::Value::from_raw(circt::sys::MlirValue { ptr: std::ptr::null_mut(), }) }; (self.builder.ins().call(ext_unit, vec![]), result) } // Propagate tombstones. mir::RvalueKind::Error => return Err(()), }; Ok((value, Mode::Value)) } fn emit_prb_or_var(&mut self, sig: HybridValue) -> HybridValue { match *self.llhd_type(sig.0) { llhd::SignalType(_) => { let value = self.mk_prb(sig); if let Some(name) = self.builder.get_name(sig.0) { self.builder.set_name(value.0, format!("{}.prb", name)); } value } llhd::PointerType(_) => { let value = self.mk_ld(sig); if let Some(name) = self.builder.get_name(sig.0) { self.builder.set_name(value.0, format!("{}.ld", name)); } value } _ => sig, } } /// Emit the code for an rvalue converted to a boolean.. fn emit_rvalue_bool(&mut self, expr_id: NodeId, env: ParamEnv) -> Result { let mir = self.mir_rvalue(expr_id, env); assert_span!( mir.ty .get_simple_bit_vector() .map(|sbv| sbv.size == 1) .unwrap_or(false), mir.span, self, "value of type `{}` should be a bool", mir.ty ); self.emit_mir_rvalue(mir) } /// Emit the code for an MIR rvalue interface. /// /// This is a bit tricky, since we transpose interface arrays to array /// signals, which means from an MIR perspective, an access `a[0][1].x` /// looks rather like `a.x[0][1]` during codegen. fn emit_rvalue_interface( &mut self, mir: &mir::Rvalue<'gcx>, signal: NodeId, mode_hint: Mode, ) -> Result<(HybridValue, Mode)> { match mir.kind { mir::RvalueKind::Intf(intf) => { let id = AccessedNode::Intf(intf, signal); let sig = self .shadows .get(&id) .cloned() .unwrap_or_else(|| self.emitted_value(id)); debug!( "{:?} emitted value is {:?} (type {})", id, sig, self.llhd_type(sig.0) ); if mode_hint == Mode::Signal && self.llhd_type(sig.0).is_signal() { Ok((sig, Mode::Signal)) } else { Ok((self.emit_prb_or_var(sig), Mode::Value)) } } mir::RvalueKind::Index { value, base, length, } => { let (inner, actual_mode) = self.emit_rvalue_interface(value, signal, mode_hint)?; self.emit_rvalue_index(value.ty, inner, base, length) .map(|v| (v, actual_mode)) } _ => bug_span!( mir.span, self.cx, "found MIR rvalue which cannot appear in a transposed interface signal access: \ {:#?}", mir ), } } /// Emit the code for an indexing operation on an already emitted rvalue. fn emit_rvalue_index( &mut self, ty: &'gcx UnpackedType<'gcx>, value: HybridValue, base: &'gcx mir::Rvalue<'gcx>, length: usize, ) -> Result { let base = self.emit_mir_rvalue(base)?; let hidden = self.emit_zero_for_type_both(self.value_type(value)); // TODO(fschuiki): make the above a constant of all `x`. let shifted = self.mk_shr(value, hidden, base); if ty.coalesces_to_llhd_scalar() { let length = std::cmp::max(1, length); Ok(self.mk_ext_slice(shifted, 0, length)) } else { if length == 0 { Ok(self.mk_ext_field(shifted, 0)) } else { Ok(self.mk_ext_slice(shifted, 0, length)) } } } /// Emit the code for an MIR lvalue. fn emit_mir_lvalue( &mut self, mir: &mir::Lvalue<'gcx>, ) -> Result<(HybridValue, Option)> { if let Some(x) = self.interned_lvalues.get(&mir.id) { x.clone() } else { let x = self.emit_mir_lvalue_uninterned(mir); self.interned_lvalues.insert(mir.id, x); x } } /// Emit the code for an MIR lvalue. /// /// The first returned value is the actually targeted lvalue. The second is /// a potential shadow variable that must be kept up-to-date. fn emit_mir_lvalue_uninterned( &mut self, mir: &mir::Lvalue<'gcx>, ) -> Result<(HybridValue, Option)> { self.mlir_builder.set_loc(span_to_loc(self.mcx, mir.span)); let result = self.emit_mir_lvalue_inner(mir); match result { Ok((sig, var)) => { let llty_exp1 = llhd::signal_ty(self.emit_type(mir.ty)?); let llty_exp2 = llhd::pointer_ty(self.emit_type(mir.ty)?); let llty_act = self.llhd_type(sig.0); assert_span!( llty_exp1 == llty_act || llty_exp2 == llty_act, mir.span, self.cx, "codegen for MIR lvalue `{}` should produce `{}` or `{}`, but got `{}`", mir.span.extract(), llty_exp1, llty_exp2, llty_act ); if let Some(var) = var { let llty_exp = llhd::pointer_ty(self.emit_type(mir.ty)?); let llty_act = self.llhd_type(var.0); assert_span!( llty_exp == llty_act, mir.span, self.cx, "codegen for MIR lvalue `{}` should produce `{}`, but got `{}`", mir.span.extract(), llty_exp, llty_act ); } } Err(()) => (), } result } /// Emit the code for an MIR lvalue. /// /// The first returned value is the actually targeted lvalue. The second is /// a potential shadow variable that must be kept up-to-date. fn emit_mir_lvalue_inner( &mut self, mir: &mir::Lvalue<'gcx>, ) -> Result<(HybridValue, Option)> { match mir.kind { // Transmutes are no-ops in code generation. mir::LvalueKind::Transmute(value) => self.emit_mir_lvalue(value), // Variables and ports trivially return their declaration value. // This is either the `var` or `sig` instruction which introduced // them. mir::LvalueKind::Var(id) | mir::LvalueKind::Port(id) | mir::LvalueKind::Arg(id) => { Ok(( self.emitted_value(id).clone(), self.shadows.get(&id.into()).cloned(), )) } // Interface signals require special care, because they are emitted // in a transposed fashion. mir::LvalueKind::IntfSignal(value, signal) => self.emit_lvalue_interface(value, signal), // Member accesses simply look up their inner lvalue and extract the // signal or pointer to the respective subfield. mir::LvalueKind::Member { value, field } => { let target = self.emit_mir_lvalue(value)?; let value_real = self.mk_ext_field(target.0, field); let value_shadow = target.1.map(|target| self.mk_ext_field(target, field)); Ok((value_real, value_shadow)) } // Index accesses shift and extract the accessed slice as a signal // or pointer. mir::LvalueKind::Index { value, base, length, } => { let inner = self.emit_mir_lvalue(value)?; self.emit_lvalue_index(value.ty, inner, base, length) } mir::LvalueKind::Repeat(..) | mir::LvalueKind::Concat(..) | mir::LvalueKind::DestructArray(..) | mir::LvalueKind::DestructStruct(..) => { bug_span!( mir.span, self.cx, "mir lvalue should have been simplified by its parent assignment; {:#?}", mir ); } // Errors from MIR lowering have already been reported. Just abort. mir::LvalueKind::Error => Err(()), mir::LvalueKind::Genvar(..) | mir::LvalueKind::Intf(..) => { bug_span!( mir.span, self.cx, "cannot codegen assignment to interface or genvar; {:#?}", mir ); } } } /// Emit the code for an MIR lvalue interface. /// /// This is a bit tricky, since we transpose interface arrays to array /// signals, which means from an MIR perspective, an access `a[0][1].x` /// looks rather like `a.x[0][1]` during codegen. fn emit_lvalue_interface( &mut self, mir: &mir::Lvalue<'gcx>, signal: NodeId, ) -> Result<(HybridValue, Option)> { match mir.kind { mir::LvalueKind::Intf(intf) => { let id = AccessedNode::Intf(intf, signal); Ok(( self.emitted_value(id).clone(), self.shadows.get(&id).cloned(), )) } mir::LvalueKind::Index { value, base, length, } => { let inner = self.emit_lvalue_interface(value, signal)?; self.emit_lvalue_index(value.ty, inner, base, length) } _ => bug_span!( mir.span, self.cx, "found MIR lvalue which cannot appear in a transposed interface signal access: \ {:#?}", mir ), } } /// Emit the code for an indexing operation on an already emitted lvalue. fn emit_lvalue_index( &mut self, ty: &'gcx UnpackedType<'gcx>, value: (HybridValue, Option), base: &'gcx mir::Rvalue<'gcx>, length: usize, ) -> Result<(HybridValue, Option)> { let (target_real, target_shadow) = value; let base = self.emit_mir_rvalue(base)?; let shifted_real = { let hidden = self.emit_zero_for_type_both(self.value_type(target_real)); self.mk_shr(target_real, hidden, base) }; let shifted_shadow = target_shadow.map(|target| { let hidden = self.emit_zero_for_type_both(self.value_type(target)); self.mk_shr(target, hidden, base) }); if ty.coalesces_to_llhd_scalar() { let length = std::cmp::max(1, length); Ok(( self.mk_ext_slice(shifted_real, 0, length), shifted_shadow.map(|s| self.mk_ext_slice(s, 0, length)), )) } else { if length == 0 { Ok(( self.mk_ext_field(shifted_real, 0), shifted_shadow.map(|s| self.mk_ext_field(s, 0)), )) } else { Ok(( self.mk_ext_slice(shifted_real, 0, length), shifted_shadow.map(|s| self.mk_ext_slice(s, 0, length)), )) } } } /// Emit the code for a statement. fn emit_stmt(&mut self, stmt_id: NodeId, env: ParamEnv) -> Result<()> { // If we attempt to emit additional statements after a terminator, // create a new block to emit into. if self.terminated { let block = self.mk_block(None); self.append_to(block); } self.mlir_builder .set_loc(span_to_loc(self.mcx, self.span(stmt_id))); self.flush_mir(); match self.hir_of(stmt_id)? { HirNode::Stmt(x) => self.emit_stmt_regular(stmt_id, x, env), HirNode::VarDecl(x) => self.emit_stmt_var_decl(stmt_id, x, env), _ => unreachable!(), } } /// Emit the code for a statement, given its HIR. fn emit_stmt_regular(&mut self, stmt_id: NodeId, hir: &hir::Stmt, env: ParamEnv) -> Result<()> { debug!("Emit stmt `{}`", { let s = hir.span.extract(); if s.len() > 40 { format!("{} [...]", &s[0..40]) } else { s } }); #[allow(unreachable_patterns)] match hir.kind { hir::StmtKind::Null => (), hir::StmtKind::Block(ref ids) => { for &id in ids { self.emit_stmt(id, env)?; } } hir::StmtKind::Assign { lhs, rhs, kind } => { // Map the assignment to an MIR node. let assign_mir = self.mir_assignment_from_procedural(stmt_id, lhs, rhs, env, hir.span, kind); debug!("Procedural assignment: {:#?}", assign_mir); // Simplify the assignment to eliminate concatenations on the // left-hand side. let simplified = self.mir_simplify_assignment(Ref(assign_mir)); debug!("Simplified to: {:#?}", simplified); // Check for sanity. for &assign in &simplified { assert_type!(assign.rhs.ty, assign.lhs.ty, assign.rhs.span, self.cx); if assign.is_error() { return Err(()); } } // Emit the appropriate assignments based on the assignment // kind. match kind { hir::AssignKind::Block(_) => { for &assign in &simplified { let lhs_lv = self.emit_mir_lvalue(assign.lhs)?; let rhs_rv = self.emit_mir_rvalue(assign.rhs)?; self.emit_blocking_assign_llhd(lhs_lv, rhs_rv)?; } } hir::AssignKind::Nonblock => { let delay = self.mk_const_time(&num::zero(), 1, 0); for &assign in &simplified { let lhs_lv = self.emit_mir_lvalue(assign.lhs)?; let rhs_rv = self.emit_mir_rvalue(assign.rhs)?; self.mk_drv(lhs_lv.0, rhs_rv, delay); } } hir::AssignKind::NonblockDelay(delay) => { let delay = self.emit_rvalue(delay, env)?; for &assign in &simplified { let lhs_lv = self.emit_mir_lvalue(assign.lhs)?; let rhs_rv = self.emit_mir_rvalue(assign.rhs)?; self.mk_drv(lhs_lv.0, rhs_rv, delay); } } } } hir::StmtKind::Timed { control: hir::TimingControl::Delay(expr_id), stmt, } => { let resume_blk = self.mk_block(None); let duration = self.emit_rvalue(expr_id, env)?; self.builder .ins() .wait_time(resume_blk.0, duration.0, vec![]); circt::llhd::WaitOp::new(self.mlir_builder, resume_blk.1, vec![], Some(duration.1)); self.append_to(resume_blk); self.flush_mir(); // ensure we don't reuse earlier expr probe self.emit_shadow_update(); self.emit_stmt(stmt, env)?; } hir::StmtKind::Timed { control: hir::TimingControl::ExplicitEvent(expr_id), stmt, } => { let expr_hir = match self.hir_of(expr_id)? { HirNode::EventExpr(x) => x, _ => unreachable!(), }; trace!("emit event checking code for {:#?}", expr_hir); // Store initial values of the expressions the event is // sensitive to. let init_blk = self.mk_block(Some("init")); self.mk_br(init_blk); self.append_to(init_blk); let mut init_values = vec![]; for event in &expr_hir.events { init_values.push(self.emit_rvalue(event.expr, env)?); } // Wait for any of the inputs to those expressions to change. let check_blk = self.mk_block(Some("check")); let mut trigger_on = vec![]; for event in &expr_hir.events { let acc = self.accessed_nodes(event.expr, env)?; for &id in &acc.read { trigger_on.push(self.emitted_value(id).clone()); } } self.mk_wait(check_blk, trigger_on, None); self.append_to(check_blk); self.flush_mir(); // ensure we don't reuse earlier expr probe self.emit_shadow_update(); // Check if any of the events happened and produce a single bit // value that represents this. let mut event_cond = None; for (event, init_value) in expr_hir.events.iter().zip(init_values.into_iter()) { trace!( "emit check if {:?} changed according to {:#?}", init_value, event ); let now_value = self.emit_rvalue(event.expr, env)?; let mut trigger = self.emit_event_trigger(event.edge, init_value, now_value)?; for &iff in &event.iff { let iff_value = self.emit_rvalue_bool(iff, env)?; trigger = self.mk_and(trigger, iff_value); self.builder.set_name(trigger.0, "iff".to_string()); } event_cond = Some(match event_cond { Some(chain) => { let value = self.mk_or(chain, trigger); self.builder.set_name(value.0, "event_or".to_string()); value } None => trigger, }); } // If the event happened, branch to a new block which will // contain the subsequent statements. Otherwise jump back up to // the initial block. if let Some(event_cond) = event_cond { let event_blk = self.mk_block(Some("event")); self.mk_cond_br(event_cond, init_blk, event_blk); self.append_to(event_blk); } // Emit the actual statement. self.emit_stmt(stmt, env)?; } hir::StmtKind::Timed { control: hir::TimingControl::ImplicitEvent, stmt, } => { // Wait for any of the inputs to the statement to change. let trigger_blk = self.mk_block(Some("trigger")); let acc = self.accessed_nodes(stmt, env)?; let trigger_on: Vec<_> = acc .read .iter() .map(|&id| self.emitted_value(id).clone()) .collect(); self.mk_wait(trigger_blk, trigger_on, None); self.append_to(trigger_blk); self.flush_mir(); // ensure we don't reuse earlier expr probe self.emit_shadow_update(); // Emit the actual statement. self.emit_stmt(stmt, env)?; } hir::StmtKind::Expr(expr_id) => { self.emit_rvalue(expr_id, env)?; } hir::StmtKind::If { cond, main_stmt, else_stmt, } => { let main_blk = self.mk_block(Some("if_true")); let else_blk = self.mk_block(Some("if_false")); let cond = self.emit_rvalue_bool(cond, env)?; self.mk_cond_br(cond, main_blk, else_blk); let final_blk = self.mk_block(Some("if_exit")); self.append_to(main_blk); self.emit_stmt(main_stmt, env)?; self.mk_br(final_blk); self.append_to(else_blk); if let Some(else_stmt) = else_stmt { self.emit_stmt(else_stmt, env)?; }; self.mk_br(final_blk); self.append_to(final_blk); } hir::StmtKind::Loop { kind, body } => { let body_blk = self.mk_block(Some("loop_body")); let exit_blk = self.mk_block(Some("loop_exit")); self.continue_stack.push(body_blk); self.break_stack.push(exit_blk); let result = self.emit_loop_stmt(env, kind, body, body_blk, exit_blk); assert_eq!(self.continue_stack.pop(), Some(body_blk)); assert_eq!(self.break_stack.pop(), Some(exit_blk)); result?; } hir::StmtKind::InlineGroup { ref stmts, .. } => { for &stmt in stmts { self.emit_stmt(stmt, env)?; } } hir::StmtKind::Case { expr, ref ways, default, kind, } => { let expr = self.emit_rvalue(expr, env)?; let final_blk = self.mk_block(Some("case_exit")); for &(ref way_exprs, stmt) in ways { let mut last_check = None; for &way_expr in way_exprs { // Determine the constant value of the label. let way_const = self.constant_value_of(way_expr, env); let (_, special_bits, x_bits) = match &way_const.kind { ValueKind::Int(v, s, x) => (v, s, x), _ => panic!("case constant evaluates to non-integer"), }; let way_expr = self.emit_const(way_const, env, self.span(way_expr))?; let way_width = self.llhd_type(way_expr.0).unwrap_int(); // Generate the comparison mask based on the case kind. let mask = match kind { ast::CaseKind::Normal => None, ast::CaseKind::DontCareZ => { let mut mask = special_bits.clone(); mask.difference(x_bits); mask.negate(); Some(mask) } ast::CaseKind::DontCareXZ => { let mut mask = special_bits.clone(); mask.negate(); Some(mask) } }; let mask = mask.map(|bits| { let mut mask = BigInt::zero(); for b in &bits { mask <<= 1; if b { mask |= BigInt::one(); } } self.mk_const_int(way_width, &mask) }); // Filter the comparison values through the mask. let (lhs, rhs) = match mask { Some(mask) => (self.mk_and(expr, mask), self.mk_and(way_expr, mask)), None => (expr, way_expr), }; // Perform the comparison and branch. let check = self.mk_cmp(CmpPred::Eq, lhs, rhs); last_check = Some(match last_check { Some(last_check) => self.mk_or(last_check, check), None => check, }); } if let Some(last_check) = last_check { let taken_blk = self.mk_block(Some("case_body")); let untaken_blk = self.mk_block(None); self.mk_cond_br(last_check, taken_blk, untaken_blk); self.append_to(taken_blk); self.emit_stmt(stmt, env)?; self.mk_br(final_blk); self.append_to(untaken_blk); } } if let Some(default) = default { self.emit_stmt(default, env)?; } self.mk_br(final_blk); self.append_to(final_blk); } hir::StmtKind::Ast(ast) => { self.emit_stmt_ast(ast, env)?; } _ => { error!("{:#?}", hir); return self.unimp_msg("code generation for", hir); } } Ok(()) } /// Emit the code for a statement for which no HIR node exists. fn emit_stmt_ast(&mut self, stmt: &ast::Stmt, env: ParamEnv) -> Result<()> { match &stmt.kind { ast::ReturnStmt(None) => { self.mk_ret(None); } ast::ReturnStmt(Some(expr)) => { let expr = self.emit_rvalue(expr.id(), env)?; self.mk_ret(Some(expr)); } ast::BreakStmt => match self.break_stack.last() { Some(&block) => { self.mk_br(block); } None => { self.emit( DiagBuilder2::error("break statement outside of loop") .span(stmt.human_span()), ); return Err(()); } }, ast::ContinueStmt => match self.continue_stack.last() { Some(&block) => { self.mk_br(block); } None => { self.emit( DiagBuilder2::error("continue statement outside of loop") .span(stmt.human_span()), ); return Err(()); } }, _ => { error!("{:#?}", stmt); bug_span!( stmt.span(), self.cx, "code generation for {} not implemented", stmt.format_indefinite() ); } } Ok(()) } /// Emit the code for a loop statement. fn emit_loop_stmt( &mut self, env: ParamEnv, kind: hir::LoopKind, body: NodeId, body_blk: HybridBlock, exit_blk: HybridBlock, ) -> Result<()> { // Emit the loop initialization. let repeat_var = match kind { hir::LoopKind::Forever => None, hir::LoopKind::Repeat(count) => { let ty = self.type_of(count, env)?; let count = self.emit_rvalue(count, env)?; let var = self.mk_var(count); self.builder.set_name(var.0, "loop_count".to_string()); Some((var, ty)) } hir::LoopKind::While(_) => None, hir::LoopKind::Do(_) => None, hir::LoopKind::For(init, _, _) => { self.emit_stmt(init, env)?; None } }; // Emit the loop prologue. self.mk_br(body_blk); self.append_to(body_blk); let enter_cond = match kind { hir::LoopKind::Forever => None, hir::LoopKind::Repeat(_) => { let (repeat_var, ty) = repeat_var.clone().unwrap(); let lty = self.emit_type_both(ty)?; let value = self.mk_ld(repeat_var); let zero = self.emit_zero_for_type_both(lty); Some(self.mk_cmp(CmpPred::Neq, value, zero)) } hir::LoopKind::While(cond) => Some(self.emit_rvalue_bool(cond, env)?), hir::LoopKind::Do(_) => None, hir::LoopKind::For(_, cond, _) => Some(self.emit_rvalue_bool(cond, env)?), }; if let Some(enter_cond) = enter_cond { let entry_blk = self.mk_block(Some("loop_continue")); self.mk_cond_br(enter_cond, entry_blk, exit_blk); self.append_to(entry_blk); } // Emit the loop body. self.emit_stmt(body, env)?; // Emit the epilogue. let continue_cond = match kind { hir::LoopKind::Forever => None, hir::LoopKind::Repeat(_) => { let (repeat_var, ty) = repeat_var.clone().unwrap(); let value = self.mk_ld(repeat_var); let one = self.mk_const_int(ty.get_bit_size().unwrap(), &BigInt::one()); let value = self.mk_sub(value, one); self.mk_st(repeat_var, value); None } hir::LoopKind::While(_) => None, hir::LoopKind::Do(cond) => Some(self.emit_rvalue_bool(cond, env)?), hir::LoopKind::For(_, _, step) => { self.emit_rvalue(step, env)?; None } }; if !self.terminated { match continue_cond { Some(cond) => self.mk_cond_br(cond, body_blk, exit_blk), None => self.mk_br(body_blk), }; } self.append_to(exit_blk); Ok(()) } /// Emit the code for a variable declaration statement, given its HIR. fn emit_stmt_var_decl( &mut self, decl_id: NodeId, hir: &hir::VarDecl, env: ParamEnv, ) -> Result<()> { let ty = self.type_of_var_decl( Ref(self .ast_for_id(decl_id) .as_all() .get_var_decl_name() .unwrap()), env, ); let ty = self.emit_type_both(ty)?; let init = match hir.init { Some(expr) => self.emit_rvalue(expr, env)?, None => self.emit_zero_for_type_both(ty), }; let value = self.mk_var(init); self.builder.set_name(value.0, hir.name.value.to_string()); self.set_emitted_value(decl_id, value); Ok(()) } /// Emit the code to check if a certain edge occurred between two values. fn emit_event_trigger( &mut self, edge: ast::EdgeIdent, prev: HybridValue, now: HybridValue, ) -> Result { let ty = self.value_type(now); // Check if a posedge happened. let posedge = match edge { ast::EdgeIdent::Posedge | ast::EdgeIdent::Edge => { let zero = self.emit_zero_for_type_both(ty.clone()); let prev_eq_0 = self.mk_cmp(CmpPred::Eq, prev, zero); let now_neq_0 = self.mk_cmp(CmpPred::Neq, now, zero); let value = self.mk_and(prev_eq_0, now_neq_0); self.builder.set_name(value.0, "posedge".to_string()); Some(value) } _ => None, }; // Check if a negedge happened. let negedge = match edge { ast::EdgeIdent::Negedge | ast::EdgeIdent::Edge => { let zero = self.emit_zero_for_type_both(ty.clone()); let prev_neq_0 = self.mk_cmp(CmpPred::Neq, prev, zero); let now_eq_0 = self.mk_cmp(CmpPred::Eq, now, zero); let value = self.mk_and(prev_neq_0, now_eq_0); self.builder.set_name(value.0, "negedge".to_string()); Some(value) } _ => None, }; // Combine the two edge triggers, or emit an implicit edge check if none // of the above edges was checked. Ok(match (posedge, negedge) { (Some(a), Some(b)) => { let value = self.mk_or(a, b); self.builder.set_name(value.0, "edge".to_string()); value } (Some(a), None) => a, (None, Some(b)) => b, (None, None) => { let value = self.mk_cmp(CmpPred::Neq, prev, now); self.builder.set_name(value.0, "impledge".to_string()); value } }) } /// Emit a blocking assignment on MIR nodes. fn emit_mir_blocking_assign( &mut self, lvalue: &'gcx mir::Lvalue<'gcx>, rvalue: &'gcx mir::Rvalue<'gcx>, ) -> Result<()> { let lv = self.emit_mir_lvalue(lvalue)?; let rv = self.emit_mir_rvalue(rvalue)?; self.emit_blocking_assign_llhd(lv, rv) } /// Emit a blocking assignment to a variable or signal. fn emit_blocking_assign_llhd( &mut self, lvalue: (HybridValue, Option), rvalue: HybridValue, ) -> Result<()> { let ty = lvalue.0 .1.ty(); if circt::llhd::is_signal_type(ty) { let delay = self.mk_const_time(&num::zero(), 0, 1); self.mk_drv(lvalue.0, rvalue, delay); } else if circt::llhd::is_pointer_type(ty) { self.mk_st(lvalue.0, rvalue); } else { panic!("value of type `{}` cannot be assigned to", ty); } if let Some(lv) = lvalue.1 { self.mk_st(lv, rvalue); } Ok(()) } /// Emit the code to update the shadow variables of signals. fn emit_shadow_update(&mut self) { for (id, shadow) in self.shadows.clone() { let value = self.values[&id.into()]; let value = self.mk_prb(value); self.mk_st(shadow, value); } } /// Emit the code for a variable or net declaration. fn emit_varnet_decl( &mut self, decl_id: NodeId, ty: &'gcx UnpackedType<'gcx>, env: ParamEnv, default: Option, ) -> Result { // Check if this is a variable or a net declaration. let (is_var, name) = match self.hir_of(decl_id)? { HirNode::VarDecl(x) => (x.kind.is_var(), x.name.value.as_str()), HirNode::IntPort(x) => (x.kind.is_var(), x.name.value.as_str()), x => unreachable!("emit_varnet_decl on HIR {:?}", x), }; // Differentiate between variable and net declarations, which have // slightly different semantics regarding their initial value. if is_var { // For variables we require that the initial value is a // constant. let init = self.emit_const( match default { Some(expr) => self.constant_value_of(expr, env), None => self.type_default_value(ty), }, env, self.span(default.unwrap_or(decl_id)), )?; Ok(( self.builder.ins().sig(init.0), circt::llhd::SignalOp::new( self.mlir_builder, &self.unique_names.add(&name), init.1, ) .into(), )) } else { // For nets we simply emit the initial value as a signal, then // short-circuit it with the net declaration. let zero = self.emit_const(self.type_default_value(ty), env, self.span(decl_id))?; let net = ( self.builder.ins().sig(zero.0), circt::llhd::SignalOp::new( self.mlir_builder, &self.unique_names.add(&name), zero.1, ) .into(), ); if let Some(default) = default { let init = self.emit_rvalue_mode(default, env, Mode::Signal)?; self.builder.ins().con(net.0, init.0); circt::llhd::ConnectOp::new(self.mlir_builder, net.1, init.1); } Ok(net) } } fn mk_block(&mut self, name: Option<&str>) -> HybridBlock { let bb = self.builder.block(); if let Some(name) = name { self.builder.set_block_name(bb, name.into()); } (bb, self.mlir_builder.add_block()) } fn mk_br(&mut self, target: HybridBlock) { self.builder.ins().br(target.0); circt::std::BranchOp::new(self.mlir_builder, target.1); self.terminated = true; } fn mk_cond_br(&mut self, cond: HybridValue, true_block: HybridBlock, false_block: HybridBlock) { self.builder .ins() .br_cond(cond.0, false_block.0, true_block.0); circt::std::CondBranchOp::new(self.mlir_builder, cond.1, true_block.1, false_block.1); self.terminated = true; } fn mk_ret(&mut self, values: impl IntoIterator) { let mut values_llhd = vec![]; let mut values_mlir = vec![]; for v in values { values_llhd.push(v.0); values_mlir.push(v.1); } self.builder.ins().ret(); circt::std::ReturnOp::new(self.mlir_builder, values_mlir); self.terminated = true; } fn append_to(&mut self, block: HybridBlock) { self.builder.append_to(block.0); self.mlir_builder.set_insertion_point_to_end(block.1); self.terminated = false; } fn mk_wait( &mut self, block: HybridBlock, on: impl IntoIterator, time: Option, ) { let mut on_llhd = vec![]; let mut on_mlir = vec![]; for v in on { on_llhd.push(v.0); on_mlir.push(v.1); } ( if let Some(time) = time { self.builder.ins().wait_time(block.0, time.0, on_llhd); } else { self.builder.ins().wait(block.0, on_llhd); }, circt::llhd::WaitOp::new(self.mlir_builder, block.1, on_mlir, time.map(|x| x.1)), ); } fn mk_ext_slice(&mut self, arg: HybridValue, offset: usize, length: usize) -> HybridValue { ( self.builder.ins().ext_slice(arg.0, offset, length), if mlir::is_integer_type(arg.1.ty()) { circt::comb::ExtractOp::with_sizes(self.mlir_builder, arg.1, offset, length).into() } else { if let Some(ty) = circt::llhd::get_signal_type_element(arg.1.ty()) { if mlir::is_integer_type(ty) { circt::llhd::SigExtractOp::with_const_offset( self.mlir_builder, arg.1, offset, length, ) .into() } else { circt::llhd::SigArraySliceOp::with_const_offset( self.mlir_builder, arg.1, offset, length, ) .into() } } else if let Some(ty) = circt::llhd::get_pointer_type_element(arg.1.ty()) { if mlir::is_integer_type(ty) { circt::llhd::PtrExtractOp::with_const_offset( self.mlir_builder, arg.1, offset, length, ) .into() } else { circt::llhd::PtrArraySliceOp::with_const_offset( self.mlir_builder, arg.1, offset, length, ) .into() } } else { circt::hw::ArraySliceOp::with_const_offset( self.mlir_builder, arg.1, offset, length, ) .into() } }, ) } fn mk_ins_slice_mlir( &mut self, into: mlir::Value, value: mlir::Value, offset: usize, length: usize, ) -> mlir::Value { let is_int = mlir::is_integer_type(into.ty()); let len = match is_int { true => mlir::integer_type_width(into.ty()), false => circt::hw::array_type_size(into.ty()), }; let mut concats = vec![]; if offset > 0 { let ext = match is_int { true => { circt::comb::ExtractOp::with_sizes(self.mlir_builder, into, 0, offset).into() } false => { circt::hw::ArraySliceOp::with_const_offset(self.mlir_builder, into, 0, offset) .into() } }; concats.push(ext); } concats.push(value); if offset + length < len { let rest = len - offset - length; let ext = match is_int { true => circt::comb::ExtractOp::with_sizes( self.mlir_builder, into, offset + length, rest, ) .into(), false => circt::hw::ArraySliceOp::with_const_offset( self.mlir_builder, into, offset + length, rest, ) .into(), }; concats.push(ext); } match is_int { true => circt::comb::ConcatOp::new(self.mlir_builder, concats).into(), false => circt::hw::ArrayConcatOp::new(self.mlir_builder, concats).into(), } } fn mk_ins_slice( &mut self, into: HybridValue, value: HybridValue, offset: usize, length: usize, ) -> HybridValue { ( self.builder .ins() .ins_slice(into.0, value.0, offset, length), self.mk_ins_slice_mlir(into.1, value.1, offset, length), ) } fn mk_ext_field(&mut self, arg: HybridValue, offset: usize) -> HybridValue { let mlir = if circt::hw::is_array_type(arg.1.ty()) { circt::hw::ArrayGetOp::with_const_offset(self.mlir_builder, arg.1, offset).into() } else if circt::hw::is_struct_type(arg.1.ty()) { circt::hw::StructExtractOp::new(self.mlir_builder, arg.1, offset).into() } else if let Some(ty) = circt::llhd::get_signal_type_element(arg.1.ty()) { if circt::hw::is_array_type(ty) { circt::llhd::SigArrayGetOp::with_const_offset(self.mlir_builder, arg.1, offset) .into() } else if circt::hw::is_struct_type(ty) { circt::llhd::SigStructExtractOp::new(self.mlir_builder, arg.1, offset).into() } else { panic!("unsupported type {}", ty); } } else if let Some(ty) = circt::llhd::get_pointer_type_element(arg.1.ty()) { if circt::hw::is_array_type(ty) { circt::llhd::PtrArrayGetOp::with_const_offset(self.mlir_builder, arg.1, offset) .into() } else if circt::hw::is_struct_type(ty) { circt::llhd::PtrStructExtractOp::new(self.mlir_builder, arg.1, offset).into() } else { panic!("unsupported type {}", ty); } } else { panic!("unsupported type {}", arg.1.ty()); }; (self.builder.ins().ext_field(arg.0, offset), mlir) } #[allow(dead_code)] fn mk_ins_field( &mut self, into: HybridValue, value: HybridValue, offset: usize, ) -> HybridValue { let mlir = if circt::hw::is_struct_type(into.1.ty()) { circt::hw::StructInjectOp::new(self.mlir_builder, into.1, value.1, offset).into() } else { let value = circt::hw::ArrayCreateOp::new( self.mlir_builder, circt::hw::get_array_type(value.1.ty(), 1), vec![value.1], ) .into(); self.mk_ins_slice_mlir(into.1, value, offset, 1) }; (self.builder.ins().ins_field(into.0, value.0, offset), mlir) } fn mk_not(&mut self, arg: HybridValue) -> HybridValue { (self.builder.ins().not(arg.0), { let ones = circt::hw::ConstantOp::new( self.mlir_builder, mlir::integer_type_width(arg.1.ty()), &(-1).into(), ) .into(); circt::comb::XorOp::new(self.mlir_builder, ones, arg.1).into() }) } fn mk_neg(&mut self, arg: HybridValue) -> HybridValue { (self.builder.ins().neg(arg.0), { let zero = self.emit_zero_for_type_mlir(arg.1.ty()); circt::comb::SubOp::new(self.mlir_builder, zero, arg.1).into() }) } fn mk_and(&mut self, lhs: HybridValue, rhs: HybridValue) -> HybridValue { ( self.builder.ins().and(lhs.0, rhs.0), circt::comb::AndOp::new(self.mlir_builder, lhs.1, rhs.1).into(), ) } fn mk_or(&mut self, lhs: HybridValue, rhs: HybridValue) -> HybridValue { ( self.builder.ins().or(lhs.0, rhs.0), circt::comb::OrOp::new(self.mlir_builder, lhs.1, rhs.1).into(), ) } fn mk_xor(&mut self, lhs: HybridValue, rhs: HybridValue) -> HybridValue { ( self.builder.ins().xor(lhs.0, rhs.0), circt::comb::XorOp::new(self.mlir_builder, lhs.1, rhs.1).into(), ) } fn mk_mux( &mut self, cond: HybridValue, true_value: HybridValue, false_value: HybridValue, ) -> HybridValue { let array = self.builder.ins().array(vec![false_value.0, true_value.0]); ( self.builder.ins().mux(array, cond.0), circt::comb::MuxOp::new(self.mlir_builder, cond.1, true_value.1, false_value.1).into(), ) } fn mk_shl( &mut self, value: HybridValue, hidden: HybridValue, amount: HybridValue, ) -> HybridValue { ( self.builder.ins().shl(value.0, hidden.0, amount.0), circt::llhd::ShlOp::new(self.mlir_builder, value.1, hidden.1, amount.1).into(), ) } fn mk_shr( &mut self, value: HybridValue, hidden: HybridValue, amount: HybridValue, ) -> HybridValue { ( self.builder.ins().shr(value.0, hidden.0, amount.0), circt::llhd::ShrOp::new(self.mlir_builder, value.1, hidden.1, amount.1).into(), ) } fn mk_add(&mut self, lhs: HybridValue, rhs: HybridValue) -> HybridValue { ( self.builder.ins().add(lhs.0, rhs.0), circt::comb::AddOp::new(self.mlir_builder, lhs.1, rhs.1).into(), ) } fn mk_sub(&mut self, lhs: HybridValue, rhs: HybridValue) -> HybridValue { ( self.builder.ins().sub(lhs.0, rhs.0), circt::comb::SubOp::new(self.mlir_builder, lhs.1, rhs.1).into(), ) } fn mk_smul(&mut self, lhs: HybridValue, rhs: HybridValue) -> HybridValue { ( self.builder.ins().smul(lhs.0, rhs.0), circt::comb::MulOp::new(self.mlir_builder, lhs.1, rhs.1).into(), ) } fn mk_umul(&mut self, lhs: HybridValue, rhs: HybridValue) -> HybridValue { ( self.builder.ins().umul(lhs.0, rhs.0), circt::comb::MulOp::new(self.mlir_builder, lhs.1, rhs.1).into(), ) } fn mk_sdiv(&mut self, lhs: HybridValue, rhs: HybridValue) -> HybridValue { ( self.builder.ins().sdiv(lhs.0, rhs.0), circt::comb::DivSOp::new(self.mlir_builder, lhs.1, rhs.1).into(), ) } fn mk_udiv(&mut self, lhs: HybridValue, rhs: HybridValue) -> HybridValue { ( self.builder.ins().udiv(lhs.0, rhs.0), circt::comb::DivUOp::new(self.mlir_builder, lhs.1, rhs.1).into(), ) } fn mk_smod(&mut self, lhs: HybridValue, rhs: HybridValue) -> HybridValue { ( self.builder.ins().smod(lhs.0, rhs.0), circt::comb::ModSOp::new(self.mlir_builder, lhs.1, rhs.1).into(), ) } fn mk_umod(&mut self, lhs: HybridValue, rhs: HybridValue) -> HybridValue { ( self.builder.ins().umod(lhs.0, rhs.0), circt::comb::ModUOp::new(self.mlir_builder, lhs.1, rhs.1).into(), ) } fn mk_const_int(&mut self, width: usize, value: &BigInt) -> HybridValue { ( self.builder.ins().const_int((width, value.clone())), circt::hw::ConstantOp::new(self.mlir_builder, width, value).into(), ) } fn mk_const_time( &mut self, seconds: &num::rational::Ratio, delta: usize, epsilon: usize, ) -> HybridValue { ( self.builder.ins().const_time(llhd::value::TimeValue::new( seconds.clone(), delta, epsilon, )), circt::llhd::ConstantTimeOp::new(self.mlir_builder, seconds, delta, epsilon).into(), ) } fn mk_cmp(&mut self, pred: CmpPred, mut lhs: HybridValue, mut rhs: HybridValue) -> HybridValue { if (pred == CmpPred::Eq || pred == CmpPred::Neq) && !mlir::is_integer_type(lhs.1.ty()) { let ty = mlir::get_integer_type(self.mcx, circt::hw::bit_width(lhs.1.ty()).unwrap()); lhs.1 = circt::hw::BitcastOp::new(self.mlir_builder, ty, lhs.1).into(); rhs.1 = circt::hw::BitcastOp::new(self.mlir_builder, ty, rhs.1).into(); } let mut ins = self.builder.ins(); let old = match pred { CmpPred::Eq => ins.eq(lhs.0, rhs.0), CmpPred::Neq => ins.neq(lhs.0, rhs.0), CmpPred::Slt => ins.slt(lhs.0, rhs.0), CmpPred::Sle => ins.sle(lhs.0, rhs.0), CmpPred::Sgt => ins.sgt(lhs.0, rhs.0), CmpPred::Sge => ins.sge(lhs.0, rhs.0), CmpPred::Ult => ins.ult(lhs.0, rhs.0), CmpPred::Ule => ins.ule(lhs.0, rhs.0), CmpPred::Ugt => ins.ugt(lhs.0, rhs.0), CmpPred::Uge => ins.uge(lhs.0, rhs.0), }; ( old, circt::comb::ICmpOp::new(self.mlir_builder, pred, lhs.1, rhs.1).into(), ) } fn mk_array(&mut self, ty: HybridType, elements: &[HybridValue]) -> HybridValue { ( self.builder .ins() .array(elements.iter().map(|v| v.0.clone()).collect()), circt::hw::ArrayCreateOp::new(self.mlir_builder, ty.1, elements.iter().map(|v| v.1)) .into(), ) } fn mk_struct(&mut self, ty: HybridType, elements: &[HybridValue]) -> HybridValue { ( self.builder .ins() .strukt(elements.iter().map(|v| v.0.clone()).collect()), circt::hw::StructCreateOp::new(self.mlir_builder, ty.1, elements.iter().map(|v| v.1)) .into(), ) } fn mk_prb(&mut self, value: HybridValue) -> HybridValue { ( self.builder.ins().prb(value.0), circt::llhd::ProbeOp::new(self.mlir_builder, value.1).into(), ) } fn mk_drv(&mut self, lhs: HybridValue, rhs: HybridValue, delay: HybridValue) { ( self.builder.ins().drv(lhs.0, rhs.0, delay.0), circt::llhd::DriveOp::new(self.mlir_builder, lhs.1, rhs.1, delay.1), ); } fn mk_var(&mut self, init: HybridValue) -> HybridValue { ( self.builder.ins().var(init.0), circt::llhd::VariableOp::new(self.mlir_builder, init.1).into(), ) } fn mk_ld(&mut self, var: HybridValue) -> HybridValue { ( self.builder.ins().ld(var.0), circt::llhd::LoadOp::new(self.mlir_builder, var.1).into(), ) } fn mk_st(&mut self, var: HybridValue, value: HybridValue) { ( self.builder.ins().st(var.0, value.0), circt::llhd::StoreOp::new(self.mlir_builder, var.1, value.1), ); } } /// An rvalue emission mode. /// /// Upon code emission, rvalues may be emitted either as direct values, /// pointers, or signals. This enum is used to communicate the intent to the /// corresopnding functions. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] enum Mode { Value, // Pointer, Signal, } // /// Different binary ops that may be emitted. // #[derive(Copy, Clone, Debug, PartialEq, Eq)] // enum BinaryOp { // Add, // Sub, // And, // Or, // Xor, // } /// Emit a detailed description of a module's ports. /// /// Called when the PORTS verbosity flag is set. fn emit_port_details<'gcx>(cx: &impl Context<'gcx>, hir: &hir::Module<'gcx>, env: ParamEnv) { trace!("Port details of {:#?}", hir.ports_new); println!("Ports of `{}`:", hir.name); // Dump the internal ports. println!(" internal:"); for (i, port) in hir.ports_new.int.iter().enumerate() { println!( " {}: {} {} {} {}", i, port.dir, port.kind, cx.type_of_int_port(Ref(port), env), port.name ); } // Dump the external ports. println!(" external:"); for (i, port) in hir.ports_new.ext_pos.iter().enumerate() { print!(" {}: ", i); if let Some(name) = port.name { print!(".{}(", name); } if port.exprs.len() > 1 { print!("{{"); } for (expr, sep) in port.exprs.iter().zip(once("").chain(repeat(", "))) { print!("{}{}", sep, hir.ports_new.int[expr.port].name); for select in &expr.selects { match select { hir::ExtPortSelect::Error => (), hir::ExtPortSelect::Index(_mode) => { print!("[..]"); } } } } if port.exprs.len() > 1 { print!("}}"); } if port.name.is_some() { print!(")"); } // TODO: Dump the external port type. println!(); } } /// Result of emitting a module. pub struct EmittedModule<'a> { /// The emitted LLHD unit. unit: llhd::ir::UnitId, /// The emitted MLIR symbol name. mlir_symbol: String, /// The module's ports. ports: ModuleIntf<'a>, } /// Result of emitting a procedure. pub struct EmittedProcedure { /// The emitted LLHD unit. unit: llhd::ir::UnitId, /// The emitted MLIR symbol name. mlir_symbol: String, /// The nodes used exclusively as rvalues. inputs: Vec, /// The nodes used as lvalues. outputs: Vec, } /// Result of emitting a function. pub struct EmittedFunction { /// The emitted LLHD unit. unit: llhd::ir::UnitId, /// The emitted MLIR symbol name. mlir_symbol: String, } /// A module's port interface. #[derive(Debug)] pub struct ModuleIntf<'a> { /// The signature of the module. pub sig: llhd::ir::Signature, /// The inputs to the module. pub inputs: Vec>, /// The outputs of the module. pub outputs: Vec>, } /// A canonicalized port of a module. #[derive(Debug)] pub struct ModulePort<'a> { /// The original port that generated this port. One `IntPort`s may spawn /// multiple module ports, e.g. in an interface. pub port: &'a port_list::IntPort<'a>, /// The type of the port. pub ty: &'a UnpackedType<'a>, /// The lowered MLIR type. pub mty: mlir::Type, /// The preferred name in the LLHD IR. pub name: String, /// The corresponding `AccessedNode` specifier. pub accnode: AccessedNode, /// The default value assigned to this port. pub default: Option, /// The kind of port. pub kind: ModulePortKind<'a>, } /// The different kinds of module ports. #[derive(Debug)] pub enum ModulePortKind<'a> { /// A regular port. Port, /// An interface signal. IntfSignal { intf: &'a ast::Interface<'a>, env: ParamEnv, decl_id: NodeId, }, } /// An signal within an interface. #[derive(Debug)] pub struct IntfSignal<'a> { /// The node that declared this signal. Usually a `VarDecl`, `NetDecl`, or /// `PortDecl`. pub decl_id: NodeId, /// The type of the signal. pub ty: &'a UnpackedType<'a>, /// The name of the signal. pub name: Spanned, /// The expression assigned as default to the signal. pub default: Option, } /// Convert a `Span` to a corresponding MLIR location. fn span_to_loc(cx: mlir::Context, span: Span) -> mlir::Location { let l = span.begin(); mlir::Location::file_line_col(cx, &l.source.get_path(), l.human_line(), l.human_column()) } /// Make a type a signal type. /// /// This is a convenience function that processes old LLHD types and the newer /// MLIR types in parallel. fn signal_ty(ty: HybridType) -> HybridType { (llhd::signal_ty(ty.0), circt::llhd::get_signal_type(ty.1)) } /// Make a type a pointer type. /// /// This is a convenience function that processes old LLHD types and the newer /// MLIR types in parallel. fn pointer_ty(ty: HybridType) -> HybridType { (llhd::pointer_ty(ty.0), circt::llhd::get_pointer_type(ty.1)) }