Path: blob/main/cranelift/codegen/meta/src/gen_asm.rs
1692 views
//! Generate the Cranelift-specific integration of the x64 assembler.12use cranelift_assembler_x64_meta::dsl::{3Feature, Format, Inst, Location, Mutability, Operand, OperandKind, RegClass,4};5use cranelift_srcgen::{Formatter, fmtln};67/// This factors out use of the assembler crate name.8const ASM: &str = "cranelift_assembler_x64";910fn include_inst(inst: &Inst) -> bool {11// No need to worry about this instruction shape in ISLE as it's generated12// in ABI code, not ISLE.13if inst.mnemonic.starts_with("push") {14return false;15}1617true18}1920/// Returns the Rust type used for the `IsleConstructorRaw` variants.21fn rust_param_raw(op: &Operand) -> String {22match op.location.kind() {23OperandKind::Imm(loc) => {24let bits = loc.bits();25if op.extension.is_sign_extended() {26format!("i{bits}")27} else {28format!("u{bits}")29}30}31OperandKind::RegMem(rm) => {32let reg = rm.reg_class().unwrap();33let aligned = if op.align { "Aligned" } else { "" };34format!("&{reg}Mem{aligned}")35}36OperandKind::Mem(_) => {37format!("&SyntheticAmode")38}39OperandKind::Reg(r) | OperandKind::FixedReg(r) => r.reg_class().unwrap().to_string(),40}41}4243/// Returns the conversion function, if any, when converting the ISLE type for44/// this parameter to the assembler type for this parameter. Effectively45/// converts `self.rust_param_raw()` to the assembler type.46fn rust_convert_isle_to_assembler(op: &Operand) -> String {47match op.location.kind() {48OperandKind::Imm(loc) => {49let bits = loc.bits();50let ty = if op.extension.is_sign_extended() {51"Simm"52} else {53"Imm"54};55format!("{ASM}::{ty}{bits}::new({loc})")56}57OperandKind::FixedReg(r) => {58let reg = r.reg_class().unwrap().to_string().to_lowercase();59match op.mutability {60Mutability::Read => format!("{ASM}::Fixed({r})"),61Mutability::Write => {62format!("{ASM}::Fixed(self.temp_writable_{reg}())")63}64Mutability::ReadWrite => {65format!("self.convert_{reg}_to_assembler_fixed_read_write_{reg}({r})")66}67}68}69OperandKind::Reg(r) => {70let reg = r.reg_class().unwrap();71let reg_lower = reg.to_string().to_lowercase();72match op.mutability {73Mutability::Read => {74format!("{ASM}::{reg}::new({r})")75}76Mutability::Write => {77format!("{ASM}::{reg}::new(self.temp_writable_{reg_lower}())")78}79Mutability::ReadWrite => {80format!("self.convert_{reg_lower}_to_assembler_read_write_{reg_lower}({r})")81}82}83}84OperandKind::RegMem(rm) => {85let reg = rm.reg_class().unwrap().to_string().to_lowercase();86let mut_ = op.mutability.generate_snake_case();87let align = if op.align { "_aligned" } else { "" };88format!("self.convert_{reg}_mem_to_assembler_{mut_}_{reg}_mem{align}({rm})")89}90OperandKind::Mem(mem) => format!("self.convert_amode_to_assembler_amode({mem})"),91}92}9394/// `fn x64_<inst>(&mut self, <params>) -> Inst<R> { ... }`95///96/// # Panics97///98/// This function panics if the instruction has no operands.99fn generate_macro_inst_fn(f: &mut Formatter, inst: &Inst) {100use OperandKind::*;101102let struct_name = inst.name();103let operands = inst.format.operands.iter().cloned().collect::<Vec<_>>();104let results = operands105.iter()106.filter(|o| o.mutability.is_write())107.collect::<Vec<_>>();108let rust_params = operands109.iter()110.filter(|o| is_raw_operand_param(o))111.map(|o| format!("{}: {}", o.location, rust_param_raw(o)))112.chain(if inst.has_trap {113Some(format!("trap: &TrapCode"))114} else {115None116})117.collect::<Vec<_>>()118.join(", ");119f.add_block(120&format!("fn x64_{struct_name}_raw(&mut self, {rust_params}) -> AssemblerOutputs"),121|f| {122f.comment("Convert ISLE types to assembler types.");123for op in operands.iter() {124let loc = op.location;125let cvt = rust_convert_isle_to_assembler(op);126fmtln!(f, "let {loc} = {cvt};");127}128let mut args = operands129.iter()130.map(|o| format!("{}.clone()", o.location))131.collect::<Vec<_>>();132if inst.has_trap {133args.push(format!("{ASM}::TrapCode(trap.as_raw())"));134}135let args = args.join(", ");136f.empty_line();137138f.comment("Build the instruction.");139fmtln!(140f,141"let inst = {ASM}::inst::{struct_name}::new({args}).into();"142);143fmtln!(f, "let inst = MInst::External {{ inst }};");144f.empty_line();145146// When an instruction writes to an operand, Cranelift expects a147// returned value to use in other instructions: we return this148// information in the `AssemblerOutputs` struct defined in ISLE149// (below). The general rule here is that memory stores will create150// a `SideEffect` whereas for write or read-write registers we will151// return some form of `Ret*`.152f.comment("Return a type ISLE can work with.");153let access_reg = |op: &Operand| match op.mutability {154Mutability::Read => unreachable!(),155Mutability::Write => "to_reg()",156Mutability::ReadWrite => "write.to_reg()",157};158let ty_var_of_reg = |loc: Location| {159let ty = loc.reg_class().unwrap().to_string();160let var = ty.to_lowercase();161(ty, var)162};163match results.as_slice() {164[] => fmtln!(f, "AssemblerOutputs::SideEffect {{ inst }}"),165[op] => match op.location.kind() {166Imm(_) => unreachable!(),167Reg(r) | FixedReg(r) => {168let (ty, var) = ty_var_of_reg(r);169fmtln!(f, "let {var} = {r}.as_ref().{};", access_reg(op));170fmtln!(f, "AssemblerOutputs::Ret{ty} {{ inst, {var} }}");171}172Mem(_) => {173fmtln!(f, "AssemblerOutputs::SideEffect {{ inst }}")174}175RegMem(rm) => {176let (ty, var) = ty_var_of_reg(rm);177f.add_block(&format!("match {rm}"), |f| {178f.add_block(&format!("{ASM}::{ty}Mem::{ty}(reg) => "), |f| {179fmtln!(f, "let {var} = reg.{};", access_reg(op));180fmtln!(f, "AssemblerOutputs::Ret{ty} {{ inst, {var} }} ");181});182f.add_block(&format!("{ASM}::{ty}Mem::Mem(_) => "), |f| {183fmtln!(f, "AssemblerOutputs::SideEffect {{ inst }} ");184});185});186}187},188// For now, we assume that if there are two results, they are189// coming from a register-writing instruction like `mul`. The190// `match` below can be expanded as needed.191[op1, op2] => match (op1.location.kind(), op2.location.kind()) {192(FixedReg(loc1) | Reg(loc1), FixedReg(loc2) | Reg(loc2)) => {193fmtln!(f, "let one = {loc1}.as_ref().{}.to_reg();", access_reg(op1));194fmtln!(f, "let two = {loc2}.as_ref().{}.to_reg();", access_reg(op2));195fmtln!(f, "let regs = ValueRegs::two(one, two);");196fmtln!(f, "AssemblerOutputs::RetValueRegs {{ inst, regs }}");197}198(Reg(reg), Mem(_)) | (Mem(_) | RegMem(_), Reg(reg) | FixedReg(reg)) => {199let (ty, var) = ty_var_of_reg(reg);200fmtln!(f, "let {var} = {reg}.as_ref().{};", access_reg(op2));201fmtln!(f, "AssemblerOutputs::Ret{ty} {{ inst, {var} }}");202}203_ => unimplemented!("unhandled results: {results:?}"),204},205206[op1, op2, op3] => match (207op1.location.kind(),208op2.location.kind(),209op3.location.kind(),210) {211(FixedReg(loc1), FixedReg(loc2), Mem(_)) => {212fmtln!(f, "let one = {loc1}.as_ref().{}.to_reg();", access_reg(op1));213fmtln!(f, "let two = {loc2}.as_ref().{}.to_reg();", access_reg(op2));214fmtln!(f, "let regs = ValueRegs::two(one, two);");215fmtln!(f, "AssemblerOutputs::RetValueRegs {{ inst, regs }}");216}217_ => unimplemented!("unhandled results: {results:?}"),218},219220_ => panic!("instruction has more than one result"),221}222},223);224}225226/// Generate the `isle_assembler_methods!` macro.227pub fn generate_rust_macro(f: &mut Formatter, insts: &[Inst]) {228fmtln!(f, "#[doc(hidden)]");229fmtln!(f, "macro_rules! isle_assembler_methods {{");230f.indent(|f| {231fmtln!(f, "() => {{");232f.indent(|f| {233for inst in insts {234if include_inst(inst) {235generate_macro_inst_fn(f, inst);236}237}238});239fmtln!(f, "}};");240});241fmtln!(f, "}}");242}243244/// Returns the type of this operand in ISLE as a part of the ISLE "raw"245/// constructors.246fn isle_param_raw(op: &Operand) -> String {247match op.location.kind() {248OperandKind::Imm(loc) => {249let bits = loc.bits();250if op.extension.is_sign_extended() {251format!("i{bits}")252} else {253format!("u{bits}")254}255}256OperandKind::Reg(r) | OperandKind::FixedReg(r) => r.reg_class().unwrap().to_string(),257OperandKind::Mem(_) => {258if op.align {259unimplemented!("no way yet to mark an SyntheticAmode as aligned")260} else {261"SyntheticAmode".to_string()262}263}264OperandKind::RegMem(rm) => {265let reg = rm.reg_class().unwrap();266let aligned = if op.align { "Aligned" } else { "" };267format!("{reg}Mem{aligned}")268}269}270}271272/// Different kinds of ISLE constructors generated for a particular instruction.273///274/// One instruction may generate a single constructor or multiple constructors.275/// For example an instruction that writes its result to a register will276/// generate only a single constructor. An instruction where the destination277/// read/write operand is `GprMem` will generate two constructors though, one278/// for memory and one for in registers.279#[derive(Copy, Clone, Debug)]280enum IsleConstructor {281/// This constructor only produces a side effect, meaning that the282/// instruction does not produce results in registers. This may produce283/// a result in memory, however.284RetMemorySideEffect,285286/// This constructor produces a `Gpr` value, meaning that the instruction287/// will write its result to a single GPR register.288RetGpr,289290/// This is similar to `RetGpr`, but for XMM registers.291RetXmm,292293/// This "special" constructor captures multiple written-to registers (e.g.294/// `mul`).295RetValueRegs,296297/// This constructor does not return any results, but produces a side effect affecting EFLAGs.298NoReturnSideEffect,299300/// This constructor produces no results, but the flags register is written,301/// so a `ProducesFlags` value is returned with a side effect.302ProducesFlagsSideEffect,303304/// This instructions reads EFLAGS, and returns a single gpr, so this305/// creates `ConsumesFlags.ConsumesFlagsReturnsReg`.306ConsumesFlagsReturnsGpr,307}308309impl IsleConstructor {310/// Returns the result type, in ISLE, that this constructor generates.311fn result_ty(&self) -> &'static str {312match self {313IsleConstructor::RetGpr => "Gpr",314IsleConstructor::RetXmm => "Xmm",315IsleConstructor::RetValueRegs => "ValueRegs",316IsleConstructor::NoReturnSideEffect | IsleConstructor::RetMemorySideEffect => {317"SideEffectNoResult"318}319IsleConstructor::ProducesFlagsSideEffect => "ProducesFlags",320IsleConstructor::ConsumesFlagsReturnsGpr => "ConsumesFlags",321}322}323324/// Returns the constructor used to convert an `AssemblerOutput` into the325/// type returned by [`Self::result_ty`].326fn conversion_constructor(&self) -> &'static str {327match self {328IsleConstructor::NoReturnSideEffect | IsleConstructor::RetMemorySideEffect => {329"defer_side_effect"330}331IsleConstructor::RetGpr => "emit_ret_gpr",332IsleConstructor::RetXmm => "emit_ret_xmm",333IsleConstructor::RetValueRegs => "emit_ret_value_regs",334IsleConstructor::ProducesFlagsSideEffect => "asm_produce_flags_side_effect",335IsleConstructor::ConsumesFlagsReturnsGpr => "asm_consumes_flags_returns_gpr",336}337}338339/// Returns the suffix used in the ISLE constructor name.340fn suffix(&self) -> &'static str {341match self {342IsleConstructor::RetMemorySideEffect => "_mem",343IsleConstructor::RetGpr344| IsleConstructor::RetXmm345| IsleConstructor::RetValueRegs346| IsleConstructor::NoReturnSideEffect347| IsleConstructor::ProducesFlagsSideEffect348| IsleConstructor::ConsumesFlagsReturnsGpr => "",349}350}351352/// Returns whether this constructor will include a write-only `RegMem`353/// operand as an argument to the constructor.354///355/// Memory-based ctors take an `Amode`, but register-based ctors don't take356/// the result as an argument and instead manufacture it internally.357fn includes_write_only_reg_mem(&self) -> bool {358match self {359IsleConstructor::RetMemorySideEffect => true,360IsleConstructor::RetGpr361| IsleConstructor::RetXmm362| IsleConstructor::RetValueRegs363| IsleConstructor::NoReturnSideEffect364| IsleConstructor::ProducesFlagsSideEffect365| IsleConstructor::ConsumesFlagsReturnsGpr => false,366}367}368}369370/// Returns the parameter type used for the `IsleConstructor` variant371/// provided.372fn isle_param_for_ctor(op: &Operand, ctor: IsleConstructor) -> String {373match op.location.kind() {374// Writable `RegMem` operands are special here: in one constructor375// it's operating on memory so the argument is `Amode` and in the376// other constructor it's operating on registers so the argument is377// a `Gpr`.378OperandKind::RegMem(_) if op.mutability.is_write() => match ctor {379IsleConstructor::RetMemorySideEffect => "SyntheticAmode".to_string(),380IsleConstructor::NoReturnSideEffect => "".to_string(),381IsleConstructor::RetGpr | IsleConstructor::ConsumesFlagsReturnsGpr => "Gpr".to_string(),382IsleConstructor::RetXmm => "Xmm".to_string(),383IsleConstructor::RetValueRegs => "ValueRegs".to_string(),384IsleConstructor::ProducesFlagsSideEffect => todo!(),385},386387// everything else is the same as the "raw" variant388_ => isle_param_raw(op),389}390}391392/// Returns the ISLE constructors that are going to be used when generating393/// this instruction.394///395/// Note that one instruction might need multiple constructors, such as one396/// for operating on memory and one for operating on registers.397fn isle_constructors(format: &Format) -> Vec<IsleConstructor> {398use Mutability::*;399use OperandKind::*;400401let write_operands = format402.operands403.iter()404.filter(|o| o.mutability.is_write())405.collect::<Vec<_>>();406match &write_operands[..] {407[] => {408if format.eflags.is_write() {409vec![IsleConstructor::ProducesFlagsSideEffect]410} else {411vec![IsleConstructor::NoReturnSideEffect]412}413}414[one] => match one.mutability {415Read => unreachable!(),416ReadWrite | Write => match one.location.kind() {417Imm(_) => unreachable!(),418// One read/write register output? Output the instruction419// and that register.420Reg(r) | FixedReg(r) => match r.reg_class().unwrap() {421RegClass::Xmm => {422assert!(!format.eflags.is_read());423vec![IsleConstructor::RetXmm]424}425RegClass::Gpr => {426if format.eflags.is_read() {427vec![IsleConstructor::ConsumesFlagsReturnsGpr]428} else {429vec![IsleConstructor::RetGpr]430}431}432},433// One read/write memory operand? Output a side effect.434Mem(_) => {435assert!(!format.eflags.is_read());436vec![IsleConstructor::RetMemorySideEffect]437}438// One read/write reg-mem output? We need constructors for439// both variants.440RegMem(rm) => match rm.reg_class().unwrap() {441RegClass::Xmm => {442assert!(!format.eflags.is_read());443vec![444IsleConstructor::RetXmm,445IsleConstructor::RetMemorySideEffect,446]447}448RegClass::Gpr => {449if format.eflags.is_read() {450// FIXME: should expand this to include "consumes451// flags plus side effect" to model the452// memory-writing variant too. For example this453// means there's no memory-writing variant of454// `setcc` instructions generated.455vec![IsleConstructor::ConsumesFlagsReturnsGpr]456} else {457vec![458IsleConstructor::RetGpr,459IsleConstructor::RetMemorySideEffect,460]461}462}463},464},465},466[one, two] => {467assert!(!format.eflags.is_read());468match (one.location.kind(), two.location.kind()) {469(FixedReg(_) | Reg(_), FixedReg(_) | Reg(_)) => {470vec![IsleConstructor::RetValueRegs]471}472(Reg(r), Mem(_)) | (Mem(_) | RegMem(_), Reg(r) | FixedReg(r)) => {473assert!(matches!(r.reg_class().unwrap(), RegClass::Gpr));474vec![IsleConstructor::RetGpr]475}476other => panic!("unsupported number of write operands {other:?}"),477}478}479[one, two, three] => {480assert!(!format.eflags.is_read());481match (482one.location.kind(),483two.location.kind(),484three.location.kind(),485) {486(FixedReg(_), FixedReg(_), Mem(_)) => {487vec![IsleConstructor::RetValueRegs]488}489other => panic!("unsupported number of write operands {other:?}"),490}491}492493other => panic!("unsupported number of write operands {other:?}"),494}495}496497/// Generate a "raw" constructor that simply constructs, but does not emit498/// the assembly instruction:499///500/// ```text501/// (decl x64_<inst>_raw (<params>) AssemblerOutputs)502/// (extern constructor x64_<inst>_raw x64_<inst>_raw)503/// ```504///505/// Using the "raw" constructor, we also generate "emitter" constructors506/// (see [`IsleConstructor`]). E.g., instructions that write to a register507/// will return the register:508///509/// ```text510/// (decl x64_<inst> (<params>) Gpr)511/// (rule (x64_<inst> <params>) (emit_ret_gpr (x64_<inst>_raw <params>)))512/// ```513///514/// For instructions that write to memory, we also generate an "emitter"515/// constructor with the `_mem` suffix:516///517/// ```text518/// (decl x64_<inst>_mem (<params>) SideEffectNoResult)519/// (rule (x64_<inst>_mem <params>) (defer_side_effect (x64_<inst>_raw <params>)))520/// ```521///522/// # Panics523///524/// This function panics if the instruction has no operands.525fn generate_isle_inst_decls(f: &mut Formatter, inst: &Inst) {526let (trap_type, trap_name) = if inst.has_trap {527(Some("TrapCode".to_string()), Some("trap".to_string()))528} else {529(None, None)530};531532// First declare the "raw" constructor which is implemented in Rust533// with `generate_isle_macro` above. This is an "extern" constructor534// with relatively raw types. This is not intended to be used by535// general lowering rules in ISLE.536let struct_name = inst.name();537let raw_name = format!("x64_{struct_name}_raw");538let params = inst539.format540.operands541.iter()542.filter(|o| is_raw_operand_param(o))543.collect::<Vec<_>>();544let raw_param_tys = params545.iter()546.map(|o| isle_param_raw(o))547.chain(trap_type.clone())548.collect::<Vec<_>>()549.join(" ");550fmtln!(f, "(decl {raw_name} ({raw_param_tys}) AssemblerOutputs)");551fmtln!(f, "(extern constructor {raw_name} {raw_name})");552553// Next, for each "emitter" ISLE constructor being generated, synthesize554// a pure-ISLE constructor which delegates appropriately to the `*_raw`555// constructor above.556//557// The main purpose of these constructors is to have faithful type558// signatures for the SSA nature of VCode/ISLE, effectively translating559// x64's type system to ISLE/VCode's type system.560//561// Note that the `params` from above are partitioned into explicit/implicit562// parameters based on the `ctor` we're generating here. That means, for563// example, that a write-only `RegMem` will have one ctor which produces a564// register that takes no argument, but one ctors will take an `Amode` which565// is the address to write to.566for ctor in isle_constructors(&inst.format) {567let suffix = ctor.suffix();568let rule_name = format!("x64_{struct_name}{suffix}");569let result_ty = ctor.result_ty();570let mut explicit_params = Vec::new();571let mut implicit_params = Vec::new();572for param in params.iter() {573if param.mutability.is_read() || ctor.includes_write_only_reg_mem() {574explicit_params.push(param);575} else {576implicit_params.push(param);577}578}579assert!(implicit_params.len() <= 1);580let param_tys = explicit_params581.iter()582.map(|o| isle_param_for_ctor(o, ctor))583.chain(trap_type.clone())584.collect::<Vec<_>>()585.join(" ");586let param_names = explicit_params587.iter()588.map(|o| o.location.to_string())589.chain(trap_name.clone())590.collect::<Vec<_>>()591.join(" ");592let convert = ctor.conversion_constructor();593594// Generate implicit parameters to the `*_raw` constructor. Currently595// this is only destination gpr/xmm temps if the result of this entire596// constructor is a gpr/xmm register.597let implicit_params = implicit_params598.iter()599.map(|o| {600assert!(matches!(o.location.kind(), OperandKind::RegMem(_)));601match ctor {602IsleConstructor::RetMemorySideEffect | IsleConstructor::NoReturnSideEffect => {603unreachable!()604}605IsleConstructor::RetGpr | IsleConstructor::ConsumesFlagsReturnsGpr => {606"(temp_writable_gpr)"607}608IsleConstructor::RetXmm => "(temp_writable_xmm)",609IsleConstructor::RetValueRegs | IsleConstructor::ProducesFlagsSideEffect => {610todo!()611}612}613})614.collect::<Vec<_>>()615.join(" ");616617fmtln!(f, "(decl {rule_name} ({param_tys}) {result_ty})");618fmtln!(619f,620"(rule ({rule_name} {param_names}) ({convert} ({raw_name} {implicit_params} {param_names})))"621);622623if let Some(alternate) = &inst.alternate {624// We currently plan to use alternate instructions for SSE/AVX625// pairs, so we expect the one of the registers to be an XMM626// register. In the future we could relax this, but would need to627// handle more cases below.628assert!(629inst.format630.operands631.iter()632.any(|o| matches!(o.location.reg_class(), Some(RegClass::Xmm)))633);634let param_tys = if alternate.feature == Feature::avx {635param_tys.replace("Aligned", "")636} else {637param_tys638};639let alt_feature = alternate.feature.to_string();640let alt_name = &alternate.name;641let rule_name_or_feat = format!("{rule_name}_or_{alt_feature}");642fmtln!(f, "(decl {rule_name_or_feat} ({param_tys}) {result_ty})");643fmtln!(f, "(rule 1 ({rule_name_or_feat} {param_names})");644f.indent(|f| {645fmtln!(f, "(if-let true (use_{alt_feature}))");646fmtln!(f, "(x64_{alt_name}{suffix} {param_names}))");647});648fmtln!(649f,650"(rule 0 ({rule_name_or_feat} {param_names}) ({rule_name} {param_names}))"651);652}653}654}655656/// Generate the ISLE definitions that match the `isle_assembler_methods!` macro657/// above.658pub fn generate_isle(f: &mut Formatter, insts: &[Inst]) {659fmtln!(f, "(type AssemblerOutputs (enum");660fmtln!(f, " ;; Used for instructions that have ISLE");661fmtln!(f, " ;; `SideEffect`s (memory stores, traps,");662fmtln!(f, " ;; etc.) and do not return a `Value`.");663fmtln!(f, " (SideEffect (inst MInst))");664fmtln!(f, " ;; Used for instructions that return a");665fmtln!(f, " ;; GPR (including `GprMem` variants with");666fmtln!(f, " ;; a GPR as the first argument).");667fmtln!(f, " (RetGpr (inst MInst) (gpr Gpr))");668fmtln!(f, " ;; Used for instructions that return an");669fmtln!(f, " ;; XMM register.");670fmtln!(f, " (RetXmm (inst MInst) (xmm Xmm))");671fmtln!(f, " ;; Used for multi-return instructions.");672fmtln!(f, " (RetValueRegs (inst MInst) (regs ValueRegs))");673fmtln!(674f,675" ;; https://github.com/bytecodealliance/wasmtime/pull/10276"676);677fmtln!(f, "))");678f.empty_line();679680fmtln!(f, ";; Directly emit instructions that return a GPR.");681fmtln!(f, "(decl emit_ret_gpr (AssemblerOutputs) Gpr)");682fmtln!(f, "(rule (emit_ret_gpr (AssemblerOutputs.RetGpr inst gpr))");683fmtln!(f, " (let ((_ Unit (emit inst))) gpr))");684f.empty_line();685686fmtln!(f, ";; Directly emit instructions that return an");687fmtln!(f, ";; XMM register.");688fmtln!(f, "(decl emit_ret_xmm (AssemblerOutputs) Xmm)");689fmtln!(f, "(rule (emit_ret_xmm (AssemblerOutputs.RetXmm inst xmm))");690fmtln!(f, " (let ((_ Unit (emit inst))) xmm))");691f.empty_line();692693fmtln!(f, ";; Directly emit instructions that return multiple");694fmtln!(f, ";; registers (e.g. `mul`).");695fmtln!(f, "(decl emit_ret_value_regs (AssemblerOutputs) ValueRegs)");696fmtln!(697f,698"(rule (emit_ret_value_regs (AssemblerOutputs.RetValueRegs inst regs))"699);700fmtln!(f, " (let ((_ Unit (emit inst))) regs))");701f.empty_line();702703fmtln!(f, ";; Pass along the side-effecting instruction");704fmtln!(f, ";; for later emission.");705fmtln!(706f,707"(decl defer_side_effect (AssemblerOutputs) SideEffectNoResult)"708);709fmtln!(710f,711"(rule (defer_side_effect (AssemblerOutputs.SideEffect inst))"712);713fmtln!(f, " (SideEffectNoResult.Inst inst))");714f.empty_line();715716for inst in insts {717if include_inst(inst) {718generate_isle_inst_decls(f, inst);719f.empty_line();720}721}722}723724/// Returns whether `o` is included in the `*_raw` constructor generated in725/// ISLE/Rust.726///727/// This notably includes all operands that are read as those are the728/// data-dependencies of an instruction. This additionally includes, though,729/// write-only `RegMem` operands. In this situation the `RegMem` operand is730/// dynamically a `RegMem::Reg`, a temp register synthesized in ISLE, or a731/// `RegMem::Mem`, an operand from the constructor of the original entrypoint732/// itself.733fn is_raw_operand_param(o: &Operand) -> bool {734o.mutability.is_read()735|| matches!(736o.location.kind(),737OperandKind::RegMem(_) | OperandKind::Mem(_)738)739}740741742