Path: blob/main/cranelift/codegen/meta/src/pulley.rs
1692 views
use cranelift_srcgen::error::Error;1use std::path::Path;23struct Inst<'a> {4snake_name: &'a str,5name: &'a str,6fields: &'a [(&'a str, &'a str)],7}89macro_rules! define {10(11$(12$( #[$attr:meta] )*13$snake_name:ident = $name:ident $( { $( $field:ident : $field_ty:ty ),* } )? ;14)*15) => {16&[$(Inst {17snake_name: stringify!($snake_name),18name: stringify!($name),19fields: &[$($( (stringify!($field), stringify!($field_ty)), )*)?],20}),*]21// helpers.push_str(concat!("(define pulley_", stringify!($snake_name), " ("));22};23}2425const OPS: &[Inst<'_>] = pulley_interpreter::for_each_op!(define);26const EXTENDED_OPS: &[Inst<'_>] = pulley_interpreter::for_each_extended_op!(define);2728enum Operand<'a> {29Normal {30name: &'a str,31ty: &'a str,32},33Writable {34name: &'a str,35ty: &'a str,36},37TrapCode {38name: &'a str,39ty: &'a str,40},41Binop {42dst: &'a str,43src1: &'a str,44src2: &'a str,45},46}4748impl Inst<'_> {49fn operands(&self) -> impl Iterator<Item = Operand<'_>> + use<'_> {50self.fields51.iter()52.map(|(name, ty)| match (*name, *ty) {53("operands", binop) => {54// Parse "BinaryOperands < A >"` as A/A/A55// Parse "BinaryOperands < A, B >"` as A/B/A56// Parse "BinaryOperands < A, B, C >"` as A/B/C57let mut parts = binop58.strip_prefix("BinaryOperands <")59.unwrap()60.strip_suffix(">")61.unwrap()62.trim()63.split(',')64.map(|x| x.trim());65let dst = parts.next().unwrap();66let src1 = parts.next().unwrap_or(dst);67let src2 = parts.next().unwrap_or(dst);68Operand::Binop { dst, src1, src2 }69}70(name, ty) if name.starts_with("dst") => Operand::Writable { name, ty },71(name, "UpperRegSet < XReg >") => Operand::Normal {72name,73ty: "UpperXRegSet",74},75(name, ty) => Operand::Normal { name, ty },76})77.chain(if self.name.contains("Trap") {78Some(Operand::TrapCode {79name: "code",80ty: "TrapCode",81})82} else {83None84})85}8687fn skip(&self) -> bool {88match self.name {89// Skip instructions related to control-flow as those require90// special handling with `MachBuffer`.91"Jump" => true,92n if n.starts_with("Call") => true,9394// Skip special instructions not used in Cranelift.95"XPush32Many" | "XPush64Many" | "XPop32Many" | "XPop64Many" => true,9697// Skip more branching-related instructions.98n => n.starts_with("Br"),99}100}101}102103pub fn generate_rust(filename: &str, out_dir: &Path) -> Result<(), Error> {104let mut rust = String::new();105106// Generate a pretty-printing method for debugging.107rust.push_str("pub fn print(inst: &RawInst) -> String {\n");108rust.push_str("match inst {\n");109for inst @ Inst { name, .. } in OPS.iter().chain(EXTENDED_OPS) {110if inst.skip() {111continue;112}113114let mut pat = String::new();115let mut locals = String::new();116let mut format_string = String::new();117format_string.push_str(inst.snake_name);118for (i, op) in inst.operands().enumerate() {119match op {120Operand::Normal { name, ty } | Operand::Writable { name, ty } => {121pat.push_str(name);122pat.push_str(",");123124if i > 0 {125format_string.push_str(",");126}127128if ty == "UpperXRegSet" {129format_string.push_str(" {");130format_string.push_str(name);131format_string.push_str(":?}");132continue;133}134135format_string.push_str(" {");136format_string.push_str(name);137format_string.push_str("}");138if ty.contains("Reg") {139if matches!(op, Operand::Writable { .. }) {140locals.push_str(&format!("let {name} = reg_name(*{name}.to_reg());\n"));141} else {142locals.push_str(&format!("let {name} = reg_name(**{name});\n"));143}144}145}146Operand::TrapCode { name, ty: _ } => {147pat.push_str(name);148pat.push_str(",");149format_string.push_str(&format!(" // trap={{{name}:?}}"));150}151Operand::Binop { src2, .. } => {152pat.push_str("dst, src1, src2,");153format_string.push_str(" {dst}, {src1}, {src2}");154locals.push_str(&format!("let dst = reg_name(*dst.to_reg());\n"));155locals.push_str(&format!("let src1 = reg_name(**src1);\n"));156if src2.contains("Reg") {157locals.push_str(&format!("let src2 = reg_name(**src2);\n"));158}159}160}161}162163rust.push_str(&format!(164"165RawInst::{name} {{ {pat} }} => {{166{locals}167format!(\"{format_string}\")168}}169"170));171}172rust.push_str("}\n");173rust.push_str("}\n");174175// Generate `get_operands` to feed information to regalloc176rust.push_str(177"pub fn get_operands(inst: &mut RawInst, collector: &mut impl OperandVisitor) {\n",178);179rust.push_str("match inst {\n");180for inst @ Inst { name, .. } in OPS.iter().chain(EXTENDED_OPS) {181if inst.skip() {182continue;183}184185let mut pat = String::new();186let mut uses = Vec::new();187let mut defs = Vec::new();188let mut addrs = Vec::new();189for op in inst.operands() {190match op {191// `{Push,Pop}Frame{Save,Restore}` doesn't participate in192// register allocation.193Operand::Normal {194name: _,195ty: "UpperXRegSet",196} if *name == "PushFrameSave" || *name == "PopFrameRestore" => {}197198Operand::Normal { name, ty } => {199if ty.contains("Reg") {200uses.push(name);201pat.push_str(name);202pat.push_str(",");203} else if ty.starts_with("Addr") {204addrs.push(name);205pat.push_str(name);206pat.push_str(",");207}208}209Operand::Writable { name, ty } => {210if ty.contains("Reg") {211defs.push(name);212pat.push_str(name);213pat.push_str(",");214}215}216Operand::TrapCode { .. } => {}217Operand::Binop { src2, .. } => {218pat.push_str("dst, src1,");219uses.push("src1");220defs.push("dst");221if src2.contains("Reg") {222pat.push_str("src2,");223uses.push("src2");224}225}226}227}228229let uses = uses230.iter()231.map(|u| format!("collector.reg_use({u});\n"))232.collect::<String>();233let defs = defs234.iter()235.map(|u| format!("collector.reg_def({u});\n"))236.collect::<String>();237let addrs = addrs238.iter()239.map(|u| format!("{u}.collect_operands(collector);\n"))240.collect::<String>();241242rust.push_str(&format!(243"244RawInst::{name} {{ {pat} .. }} => {{245{uses}246{defs}247{addrs}248}}249"250));251}252rust.push_str("}\n");253rust.push_str("}\n");254255// Generate an emission method256rust.push_str("pub fn emit<P>(inst: &RawInst, sink: &mut MachBuffer<InstAndKind<P>>)\n");257rust.push_str(" where P: PulleyTargetKind,\n");258rust.push_str("{\n");259rust.push_str("match *inst {\n");260for inst @ Inst {261name, snake_name, ..262} in OPS.iter().chain(EXTENDED_OPS)263{264if inst.skip() {265continue;266}267268let mut pat = String::new();269let mut args = String::new();270let mut trap = String::new();271for op in inst.operands() {272match op {273Operand::Normal { name, ty: _ } | Operand::Writable { name, ty: _ } => {274pat.push_str(name);275pat.push_str(",");276277args.push_str(name);278args.push_str(",");279}280Operand::TrapCode { name, ty: _ } => {281pat.push_str(name);282pat.push_str(",");283trap.push_str(&format!("sink.add_trap({name});\n"));284}285Operand::Binop { .. } => {286pat.push_str("dst, src1, src2,");287args.push_str(288"pulley_interpreter::regs::BinaryOperands::new(dst, src1, src2),",289);290}291}292}293294rust.push_str(&format!(295"296RawInst::{name} {{ {pat} }} => {{297{trap}298pulley_interpreter::encode::{snake_name}(sink, {args})299}}300"301));302}303rust.push_str("}\n");304rust.push_str("}\n");305306std::fs::write(out_dir.join(filename), rust)?;307Ok(())308}309310pub fn generate_isle(filename: &str, out_dir: &Path) -> Result<(), Error> {311let mut isle = String::new();312313// Generate the `RawInst` enum314isle.push_str("(type RawInst (enum\n");315for inst in OPS.iter().chain(EXTENDED_OPS) {316if inst.skip() {317continue;318}319isle.push_str(" (");320isle.push_str(inst.name);321for op in inst.operands() {322match op {323Operand::Normal { name, ty } | Operand::TrapCode { name, ty } => {324isle.push_str(&format!("\n ({name} {ty})"));325}326Operand::Writable { name, ty } => {327isle.push_str(&format!("\n ({name} Writable{ty})"));328}329Operand::Binop { dst, src1, src2 } => {330isle.push_str(&format!("\n (dst Writable{dst})"));331isle.push_str(&format!("\n (src1 {src1})"));332isle.push_str(&format!("\n (src2 {src2})"));333}334}335}336isle.push_str(")\n");337}338isle.push_str("))\n");339340// Generate the `pulley_*` constructors with a `decl` and a `rule`.341for inst @ Inst {342name, snake_name, ..343} in OPS.iter().chain(EXTENDED_OPS)344{345if inst.skip() {346continue;347}348// generate `decl` and `rule` at the same time, placing the `rule` in349// temporary storage on the side. Makes generation a bit easier to read350// as opposed to doing the decl first then the rule.351let mut rule = String::new();352isle.push_str(&format!("(decl pulley_{snake_name} ("));353rule.push_str(&format!("(rule (pulley_{snake_name} "));354let mut results = Vec::new();355let mut ops = Vec::new();356for op in inst.operands() {357match op {358Operand::Normal { name, ty } | Operand::TrapCode { name, ty } => {359isle.push_str(ty);360rule.push_str(name);361ops.push(name);362}363Operand::Writable { name: _, ty } => {364results.push(ty);365}366Operand::Binop { dst, src1, src2 } => {367isle.push_str(&format!("{src1} {src2}"));368rule.push_str("src1 src2");369ops.push("src1");370ops.push("src2");371results.push(dst);372}373}374isle.push_str(" ");375rule.push_str(" ");376}377isle.push_str(") ");378rule.push_str(")");379let ops = ops.join(" ");380match &results[..] {381[result] => {382isle.push_str(result);383rule.push_str(&format!(384"385(let (386(dst Writable{result} (temp_writable_{}))387(_ Unit (emit (RawInst.{name} dst {ops})))388)389dst))\390\n",391result.to_lowercase()392));393}394[a, b] => {395isle.push_str("ValueRegs");396rule.push_str(&format!(397"398(let (399(dst1 Writable{a} (temp_writable_{}))400(dst2 Writable{b} (temp_writable_{}))401(_ Unit (emit (RawInst.{name} dst1 dst2 {ops})))402)403(value_regs dst1 dst2)))\404\n",405a.to_lowercase(),406b.to_lowercase(),407));408}409[] => {410isle.push_str("SideEffectNoResult");411rule.push_str(&format!(412" (SideEffectNoResult.Inst (RawInst.{name} {ops})))\n",413));414}415other => panic!("cannot codegen results {other:?}"),416}417isle.push_str(")\n");418419isle.push_str(&rule);420}421422std::fs::write(out_dir.join(filename), isle)?;423Ok(())424}425426427