Path: blob/main/cranelift/codegen/meta/src/gen_inst.rs
1692 views
//! Generate CLIF instruction data (including opcodes, formats, builders, etc.).12use crate::cdsl::camel_case;3use crate::cdsl::formats::InstructionFormat;4use crate::cdsl::instructions::{AllInstructions, Instruction};5use crate::cdsl::operands::{Operand, OperandKindFields};6use crate::cdsl::typevar::{TypeSet, TypeVar};7use crate::unique_table::{UniqueSeqTable, UniqueTable};8use cranelift_codegen_shared::constant_hash;9use cranelift_srcgen::{Formatter, Language, Match, error, fmtln};10use std::fmt;11use std::rc::Rc;1213// TypeSet indexes are encoded in 8 bits, with `0xff` reserved.14const TYPESET_LIMIT: usize = 0xff;1516/// Generate an instruction format enumeration.17fn gen_formats(formats: &[Rc<InstructionFormat>], fmt: &mut Formatter) {18fmt.doc_comment(19r#"20An instruction format2122Every opcode has a corresponding instruction format23which is represented by both the `InstructionFormat`24and the `InstructionData` enums.25"#,26);27fmt.line("#[derive(Copy, Clone, PartialEq, Eq, Debug)]");28fmt.add_block("pub enum InstructionFormat", |fmt| {29for format in formats {30fmt.doc_comment(format.to_string());31fmtln!(fmt, "{},", format.name);32}33});34fmt.empty_line();3536// Emit a From<InstructionData> which also serves to verify that37// InstructionFormat and InstructionData are in sync.38fmt.add_block(39"impl<'a> From<&'a InstructionData> for InstructionFormat",40|fmt| {41fmt.add_block("fn from(inst: &'a InstructionData) -> Self", |fmt| {42let mut m = Match::new("*inst");43for format in formats {44m.arm(45format!("InstructionData::{}", format.name),46vec![".."],47format!("Self::{}", format.name),48);49}50fmt.add_match(m);51});52},53);54fmt.empty_line();55}5657/// Generate the InstructionData enum.58///59/// Every variant must contain an `opcode` field. The size of `InstructionData` should be kept at60/// 16 bytes on 64-bit architectures. If more space is needed to represent an instruction, use a61/// `ValueList` to store the additional information out of line.62fn gen_instruction_data(formats: &[Rc<InstructionFormat>], fmt: &mut Formatter) {63fmt.line("#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]");64fmt.line(r#"#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]"#);65fmt.line("#[allow(missing_docs, reason = \"generated code\")]");66fmt.add_block("pub enum InstructionData", |fmt| {67for format in formats {68fmt.add_block(&format!("{}", format.name), |fmt| {69fmt.line("opcode: Opcode,");70if format.has_value_list {71fmt.line("args: ValueList,");72} else if format.num_value_operands == 1 {73fmt.line("arg: Value,");74} else if format.num_value_operands > 0 {75fmtln!(fmt, "args: [Value; {}],", format.num_value_operands);76}7778match format.num_block_operands {790 => (),801 => fmt.line("destination: ir::BlockCall,"),812 => fmtln!(82fmt,83"blocks: [ir::BlockCall; {}],",84format.num_block_operands85),86n => panic!("Too many block operands in instruction: {n}"),87}8889match format.num_raw_block_operands {900 => (),911 => fmt.line("block: ir::Block,"),92n => panic!("Too many block operands in instruction: {n}"),93}9495for field in &format.imm_fields {96fmtln!(fmt, "{}: {},", field.member, field.kind.rust_type);97}98});99fmtln!(fmt, ",");100}101});102}103104fn gen_arguments_method(formats: &[Rc<InstructionFormat>], fmt: &mut Formatter, is_mut: bool) {105let (method, mut_, rslice, as_slice) = if is_mut {106(107"arguments_mut",108"mut ",109"core::slice::from_mut",110"as_mut_slice",111)112} else {113("arguments", "", "core::slice::from_ref", "as_slice")114};115116fmt.add_block(&format!(117"pub fn {method}<'a>(&'a {mut_}self, pool: &'a {mut_}ir::ValueListPool) -> &'a {mut_}[Value]"),118119|fmt| {120let mut m = Match::new("*self");121for format in formats {122let name = format!("Self::{}", format.name);123124// Formats with a value list put all of their arguments in the list. We don't split125// them up, just return it all as variable arguments. (I expect the distinction to go126// away).127if format.has_value_list {128m.arm(129name,130vec![format!("ref {}args", mut_), "..".to_string()],131format!("args.{as_slice}(pool)"),132);133continue;134}135136// Fixed args.137let mut fields = Vec::new();138let arg = if format.num_value_operands == 0 {139format!("&{mut_}[]")140} else if format.num_value_operands == 1 {141fields.push(format!("ref {mut_}arg"));142format!("{rslice}(arg)")143} else {144let arg = format!("args_arity{}", format.num_value_operands);145fields.push(format!("args: ref {mut_}{arg}"));146arg147};148fields.push("..".into());149150m.arm(name, fields, arg);151}152fmt.add_match(m);153});154}155156/// Generate the boring parts of the InstructionData implementation.157///158/// These methods in `impl InstructionData` can be generated automatically from the instruction159/// formats:160///161/// - `pub fn opcode(&self) -> Opcode`162/// - `pub fn arguments(&self, &pool) -> &[Value]`163/// - `pub fn arguments_mut(&mut self, &pool) -> &mut [Value]`164/// - `pub fn eq(&self, &other: Self, &pool) -> bool`165/// - `pub fn hash<H: Hasher>(&self, state: &mut H, &pool)`166fn gen_instruction_data_impl(formats: &[Rc<InstructionFormat>], fmt: &mut Formatter) {167fmt.add_block("impl InstructionData", |fmt| {168fmt.doc_comment("Get the opcode of this instruction.");169fmt.add_block("pub fn opcode(&self) -> Opcode",|fmt| {170let mut m = Match::new("*self");171for format in formats {172m.arm(format!("Self::{}", format.name), vec!["opcode", ".."],173"opcode".to_string());174}175fmt.add_match(m);176});177fmt.empty_line();178179fmt.doc_comment("Get the controlling type variable operand.");180fmt.add_block("pub fn typevar_operand(&self, pool: &ir::ValueListPool) -> Option<Value>",|fmt| {181let mut m = Match::new("*self");182for format in formats {183let name = format!("Self::{}", format.name);184if format.typevar_operand.is_none() {185m.arm(name, vec![".."], "None".to_string());186} else if format.has_value_list {187// We keep all arguments in a value list.188m.arm(name, vec!["ref args", ".."], format!("args.get({}, pool)", format.typevar_operand.unwrap()));189} else if format.num_value_operands == 1 {190m.arm(name, vec!["arg", ".."], "Some(arg)".to_string());191} else {192// We have multiple value operands and an array `args`.193// Which `args` index to use?194let args = format!("args_arity{}", format.num_value_operands);195m.arm(name, vec![format!("args: ref {}", args), "..".to_string()],196format!("Some({}[{}])", args, format.typevar_operand.unwrap()));197}198}199fmt.add_match(m);200});201fmt.empty_line();202203fmt.doc_comment("Get the value arguments to this instruction.");204gen_arguments_method(formats, fmt, false);205fmt.empty_line();206207fmt.doc_comment(r#"Get mutable references to the value arguments to this208instruction."#);209gen_arguments_method(formats, fmt, true);210fmt.empty_line();211212fmt.doc_comment(r#"213Compare two `InstructionData` for equality.214215This operation requires a reference to a `ValueListPool` to216determine if the contents of any `ValueLists` are equal.217218This operation takes a closure that is allowed to map each219argument value to some other value before the instructions220are compared. This allows various forms of canonicalization.221"#);222fmt.add_block("pub fn eq(&self, other: &Self, pool: &ir::ValueListPool) -> bool", |fmt| {223fmt.add_block("if ::core::mem::discriminant(self) != ::core::mem::discriminant(other)", |fmt| {224fmt.line("return false;");225});226227fmt.add_block("match (self, other)",|fmt| {228for format in formats {229let name = format!("&Self::{}", format.name);230let mut members = vec!["opcode"];231232let args_eq = if format.has_value_list {233members.push("args");234Some("args1.as_slice(pool).iter().zip(args2.as_slice(pool).iter()).all(|(a, b)| a == b)")235} else if format.num_value_operands == 1 {236members.push("arg");237Some("arg1 == arg2")238} else if format.num_value_operands > 0 {239members.push("args");240Some("args1.iter().zip(args2.iter()).all(|(a, b)| a == b)")241} else {242None243};244245let blocks_eq = match format.num_block_operands {2460 => None,2471 => {248members.push("destination");249Some("destination1 == destination2")250},251_ => {252members.push("blocks");253Some("blocks1.iter().zip(blocks2.iter()).all(|(a, b)| a.block(pool) == b.block(pool))")254}255};256257let raw_blocks_eq = match format.num_raw_block_operands {2580 => None,2591 => {260members.push("block");261Some("block1 == block2")262}263_ => unreachable!("Not a valid format"),264};265266for field in &format.imm_fields {267members.push(field.member);268}269270let pat1 = members.iter().map(|x| format!("{x}: ref {x}1")).collect::<Vec<_>>().join(", ");271let pat2 = members.iter().map(|x| format!("{x}: ref {x}2")).collect::<Vec<_>>().join(", ");272fmt.add_block(&format!("({name} {{ {pat1} }}, {name} {{ {pat2} }}) => "), |fmt| {273fmt.line("opcode1 == opcode2");274for field in &format.imm_fields {275fmtln!(fmt, "&& {}1 == {}2", field.member, field.member);276}277if let Some(args_eq) = args_eq {278fmtln!(fmt, "&& {}", args_eq);279}280if let Some(blocks_eq) = blocks_eq {281fmtln!(fmt, "&& {}", blocks_eq);282}283if let Some(raw_blocks_eq) = raw_blocks_eq {284fmtln!(fmt, "&& {}", raw_blocks_eq);285}286});287}288fmt.line("_ => unreachable!()");289});290});291fmt.empty_line();292293fmt.doc_comment(r#"294Hash an `InstructionData`.295296This operation requires a reference to a `ValueListPool` to297hash the contents of any `ValueLists`.298299This operation takes a closure that is allowed to map each300argument value to some other value before it is hashed. This301allows various forms of canonicalization.302"#);303fmt.add_block("pub fn hash<H: ::core::hash::Hasher>(&self, state: &mut H, pool: &ir::ValueListPool)",|fmt| {304fmt.add_block("match *self",|fmt| {305for format in formats {306let name = format!("Self::{}", format.name);307let mut members = vec!["opcode"];308309let (args, len) = if format.has_value_list {310members.push("ref args");311(Some("args.as_slice(pool)"), "args.len(pool)")312} else if format.num_value_operands == 1 {313members.push("ref arg");314(Some("std::slice::from_ref(arg)"), "1")315} else if format.num_value_operands > 0 {316members.push("ref args");317(Some("args"), "args.len()")318} else {319(None, "0")320};321322let blocks = match format.num_block_operands {3230 => None,3241 => {325members.push("ref destination");326Some(("std::slice::from_ref(destination)", "1"))327}328_ => {329members.push("ref blocks");330Some(("blocks", "blocks.len()"))331}332};333334let raw_block = match format.num_raw_block_operands {3350 => None,3361 => {337members.push("block");338Some("block")339}340_ => panic!("Too many raw block operands"),341};342343for field in &format.imm_fields {344members.push(field.member);345}346let members = members.join(", ");347348fmt.add_block(&format!("{name}{{{members}}} => "), |fmt| {349fmt.line("::core::hash::Hash::hash( &::core::mem::discriminant(self), state);");350fmt.line("::core::hash::Hash::hash(&opcode, state);");351for field in &format.imm_fields {352fmtln!(fmt, "::core::hash::Hash::hash(&{}, state);", field.member);353}354fmtln!(fmt, "::core::hash::Hash::hash(&{}, state);", len);355if let Some(args) = args {356fmt.add_block(&format!("for &arg in {args}"), |fmt| {357fmtln!(fmt, "::core::hash::Hash::hash(&arg, state);");358});359}360361if let Some((blocks, len)) = blocks {362fmtln!(fmt, "::core::hash::Hash::hash(&{len}, state);");363fmt.add_block(&format!("for &block in {blocks}"), |fmt| {364fmtln!(fmt, "::core::hash::Hash::hash(&block.block(pool), state);");365fmt.add_block("for arg in block.args(pool)", |fmt| {366fmtln!(fmt, "::core::hash::Hash::hash(&arg, state);");367});368});369}370371if let Some(raw_block) = raw_block {372fmtln!(fmt, "::core::hash::Hash::hash(&{raw_block}, state);");373}374});375}376});377});378379fmt.empty_line();380381fmt.doc_comment(r#"382Deep-clone an `InstructionData`, including any referenced lists.383384This operation requires a reference to a `ValueListPool` to385clone the `ValueLists`.386"#);387fmt.add_block("pub fn deep_clone(&self, pool: &mut ir::ValueListPool) -> Self",|fmt| {388fmt.add_block("match *self",|fmt| {389for format in formats {390let name = format!("Self::{}", format.name);391let mut members = vec!["opcode"];392393if format.has_value_list {394members.push("ref args");395} else if format.num_value_operands == 1 {396members.push("arg");397} else if format.num_value_operands > 0 {398members.push("args");399}400401match format.num_block_operands {4020 => {}4031 => {404members.push("destination");405}406_ => {407members.push("blocks");408}409};410411match format.num_raw_block_operands {4120 => {}4131 => {414members.push("block");415}416_ => panic!("Too many raw-block operands to format"),417}418419for field in &format.imm_fields {420members.push(field.member);421}422let members = members.join(", ");423424fmt.add_block(&format!("{name}{{{members}}} => "),|fmt| {425fmt.add_block(&format!("Self::{}", format.name), |fmt| {426fmtln!(fmt, "opcode,");427428if format.has_value_list {429fmtln!(fmt, "args: args.deep_clone(pool),");430} else if format.num_value_operands == 1 {431fmtln!(fmt, "arg,");432} else if format.num_value_operands > 0 {433fmtln!(fmt, "args,");434}435436match format.num_block_operands {4370 => {}4381 => {439fmtln!(fmt, "destination: destination.deep_clone(pool),");440}4412 => {442fmtln!(fmt, "blocks: [blocks[0].deep_clone(pool), blocks[1].deep_clone(pool)],");443}444_ => panic!("Too many block targets in instruction"),445}446447match format.num_raw_block_operands {4480 => {}4491 => {450fmtln!(fmt, "block,");451}452_ => panic!("Too many raw-block operands in instruction"),453}454455for field in &format.imm_fields {456fmtln!(fmt, "{},", field.member);457}458});459});460}461});462});463fmt.doc_comment(r#"464Map some functions, described by the given `InstructionMapper`, over each of the465entities within this instruction, producing a new `InstructionData`.466"#);467fmt.add_block("pub fn map(&self, mut mapper: impl crate::ir::instructions::InstructionMapper) -> Self", |fmt| {468fmt.add_block("match *self",|fmt| {469for format in formats {470let name = format!("Self::{}", format.name);471let mut members = vec!["opcode"];472473if format.has_value_list {474members.push("args");475} else if format.num_value_operands == 1 {476members.push("arg");477} else if format.num_value_operands > 0 {478members.push("args");479}480481match format.num_block_operands {4820 => {}4831 => {484members.push("destination");485}486_ => {487members.push("blocks");488}489};490491match format.num_raw_block_operands {4920 => {}4931 => {494members.push("block");495}496_ => panic!("Too many raw-block operands"),497}498499for field in &format.imm_fields {500members.push(field.member);501}502let members = members.join(", ");503504fmt.add_block(&format!("{name}{{{members}}} => "), |fmt| {505fmt.add_block(&format!("Self::{}", format.name), |fmt| {506fmtln!(fmt, "opcode,");507508if format.has_value_list {509fmtln!(fmt, "args: mapper.map_value_list(args),");510} else if format.num_value_operands == 1 {511fmtln!(fmt, "arg: mapper.map_value(arg),");512} else if format.num_value_operands > 0 {513let maps = (0..format.num_value_operands)514.map(|i| format!("mapper.map_value(args[{i}])"))515.collect::<Box<[_]>>()516.join(", ");517fmtln!(fmt, "args: [{maps}],");518}519520match format.num_block_operands {5210 => {}5221 => {523fmtln!(fmt, "destination: mapper.map_block_call(destination),");524}5252 => {526fmtln!(fmt, "blocks: [mapper.map_block_call(blocks[0]), mapper.map_block_call(blocks[1])],");527}528_ => panic!("Too many block targets in instruction"),529}530531match format.num_raw_block_operands {5320 => {}5331 => {534fmtln!(fmt, "block: mapper.map_block(block),");535}536_ => panic!("Too many raw block arguments in instruction"),537}538539for field in &format.imm_fields {540let member = field.member;541match &field.kind.fields {542OperandKindFields::EntityRef => {543let mut kind = heck::ToSnakeCase::to_snake_case(544field545.kind546.rust_type547.split("::")548.last()549.unwrap_or(field.kind.rust_type),550);551if kind == "block" {552kind.push_str("_call");553}554fmtln!(fmt, "{member}: mapper.map_{kind}({member}),");555}556OperandKindFields::VariableArgs => {557fmtln!(fmt, "{member}: mapper.map_value_list({member}),");558}559OperandKindFields::ImmValue |560OperandKindFields::ImmEnum(_) |561OperandKindFields::TypeVar(_) => fmtln!(fmt, "{member},"),562}563}564});565});566}567});568});569});570}571572fn gen_bool_accessor<T: Fn(&Instruction) -> bool>(573all_inst: &AllInstructions,574get_attr: T,575name: &'static str,576doc: &'static str,577fmt: &mut Formatter,578) {579fmt.doc_comment(doc);580fmt.add_block(&format!("pub fn {name}(self) -> bool"), |fmt| {581let mut m = Match::new("self");582for inst in all_inst.iter() {583if get_attr(inst) {584m.arm_no_fields(format!("Self::{}", inst.camel_name), "true");585}586}587m.arm_no_fields("_", "false");588fmt.add_match(m);589});590fmt.empty_line();591}592593fn gen_opcodes(all_inst: &AllInstructions, fmt: &mut Formatter) {594fmt.doc_comment(595r#"596An instruction opcode.597598All instructions from all supported ISAs are present.599"#,600);601fmt.line("#[repr(u8)]");602fmt.line("#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]");603fmt.line(604r#"#[cfg_attr(605feature = "enable-serde",606derive(serde_derive::Serialize, serde_derive::Deserialize)607)]"#,608);609610// We explicitly set the discriminant of the first variant to 1, which allows us to take611// advantage of the NonZero optimization, meaning that wrapping enums can use the 0612// discriminant instead of increasing the size of the whole type, and so the size of613// Option<Opcode> is the same as Opcode's.614fmt.add_block("pub enum Opcode", |fmt| {615let mut is_first_opcode = true;616for inst in all_inst.iter() {617fmt.doc_comment(format!("`{}`. ({})", inst, inst.format.name));618619// Document polymorphism.620if let Some(poly) = &inst.polymorphic_info {621if poly.use_typevar_operand {622let op_num = inst.value_opnums[inst.format.typevar_operand.unwrap()];623fmt.doc_comment(format!(624"Type inferred from `{}`.",625inst.operands_in[op_num].name626));627}628}629630// Enum variant itself.631if is_first_opcode {632fmtln!(fmt, "{} = 1,", inst.camel_name);633is_first_opcode = false;634} else {635fmtln!(fmt, "{},", inst.camel_name)636}637}638});639fmt.empty_line();640641fmt.add_block("impl Opcode", |fmt| {642gen_bool_accessor(643all_inst,644|inst| inst.is_terminator,645"is_terminator",646"True for instructions that terminate the block",647fmt,648);649gen_bool_accessor(650all_inst,651|inst| inst.is_branch,652"is_branch",653"True for all branch or jump instructions.",654fmt,655);656gen_bool_accessor(657all_inst,658|inst| inst.is_call,659"is_call",660"Is this a call instruction?",661fmt,662);663gen_bool_accessor(664all_inst,665|inst| inst.is_return,666"is_return",667"Is this a return instruction?",668fmt,669);670gen_bool_accessor(671all_inst,672|inst| inst.can_load,673"can_load",674"Can this instruction read from memory?",675fmt,676);677gen_bool_accessor(678all_inst,679|inst| inst.can_store,680"can_store",681"Can this instruction write to memory?",682fmt,683);684gen_bool_accessor(685all_inst,686|inst| inst.can_trap,687"can_trap",688"Can this instruction cause a trap?",689fmt,690);691gen_bool_accessor(692all_inst,693|inst| inst.other_side_effects,694"other_side_effects",695"Does this instruction have other side effects besides can_* flags?",696fmt,697);698gen_bool_accessor(699all_inst,700|inst| inst.side_effects_idempotent,701"side_effects_idempotent",702"Despite having side effects, is this instruction okay to GVN?",703fmt,704);705706// Generate an opcode list, for iterating over all known opcodes.707fmt.doc_comment("All cranelift opcodes.");708fmt.add_block("pub fn all() -> &'static [Opcode]", |fmt| {709fmt.line("return &[");710for inst in all_inst {711fmt.indent(|fmt| {712fmtln!(fmt, "Opcode::{},", inst.camel_name);713});714}715fmt.line("];");716});717fmt.empty_line();718});719fmt.empty_line();720721// Generate a private opcode_format table.722fmtln!(723fmt,724"const OPCODE_FORMAT: [InstructionFormat; {}] = [",725all_inst.len()726);727fmt.indent(|fmt| {728for inst in all_inst.iter() {729fmtln!(730fmt,731"InstructionFormat::{}, // {}",732inst.format.name,733inst.name734);735}736});737fmtln!(fmt, "];");738fmt.empty_line();739740// Generate a private opcode_name function.741fmt.add_block("fn opcode_name(opc: Opcode) -> &\'static str", |fmt| {742let mut m = Match::new("opc");743for inst in all_inst.iter() {744m.arm_no_fields(745format!("Opcode::{}", inst.camel_name),746format!("\"{}\"", inst.name),747);748}749fmt.add_match(m);750});751fmt.empty_line();752753// Generate an opcode hash table for looking up opcodes by name.754let hash_table =755crate::constant_hash::generate_table(all_inst.iter(), all_inst.len(), |inst| {756constant_hash::simple_hash(&inst.name)757});758fmtln!(759fmt,760"const OPCODE_HASH_TABLE: [Option<Opcode>; {}] = [",761hash_table.len()762);763fmt.indent(|fmt| {764for i in hash_table {765match i {766Some(i) => fmtln!(fmt, "Some(Opcode::{}),", i.camel_name),767None => fmtln!(fmt, "None,"),768}769}770});771fmtln!(fmt, "];");772fmt.empty_line();773}774775/// Get the value type constraint for an SSA value operand, where776/// `ctrl_typevar` is the controlling type variable.777///778/// Each operand constraint is represented as a string, one of:779/// - `Concrete(vt)`, where `vt` is a value type name.780/// - `Free(idx)` where `idx` is an index into `type_sets`.781/// - `Same`, `Lane`, `AsTruthy` for controlling typevar-derived constraints.782fn get_constraint<'entries, 'table>(783operand: &'entries Operand,784ctrl_typevar: Option<&TypeVar>,785type_sets: &'table mut UniqueTable<'entries, TypeSet>,786) -> String {787assert!(operand.is_value());788let type_var = operand.type_var().unwrap();789790if let Some(typ) = type_var.singleton_type() {791return format!("Concrete({})", typ.rust_name());792}793794if let Some(free_typevar) = type_var.free_typevar() {795if ctrl_typevar.is_some() && free_typevar != *ctrl_typevar.unwrap() {796assert!(type_var.base.is_none());797return format!("Free({})", type_sets.add(type_var.get_raw_typeset()));798}799}800801if let Some(base) = &type_var.base {802assert!(base.type_var == *ctrl_typevar.unwrap());803return camel_case(base.derived_func.name());804}805806assert!(type_var == ctrl_typevar.unwrap());807"Same".into()808}809810fn gen_bitset<'a, T: IntoIterator<Item = &'a u16>>(811iterable: T,812name: &'static str,813field_size: u8,814fmt: &mut Formatter,815) {816let bits = iterable.into_iter().fold(0, |acc, x| {817assert!(x.is_power_of_two());818assert!(u32::from(*x) < (1 << u32::from(field_size)));819acc | x820});821fmtln!(fmt, "{}: ScalarBitSet::<u{}>({}),", name, field_size, bits);822}823824fn iterable_to_string<I: fmt::Display, T: IntoIterator<Item = I>>(iterable: T) -> String {825let elems = iterable826.into_iter()827.map(|x| x.to_string())828.collect::<Vec<_>>()829.join(", ");830format!("{{{elems}}}")831}832833fn typeset_to_string(ts: &TypeSet) -> String {834let mut result = format!("TypeSet(lanes={}", iterable_to_string(&ts.lanes));835if !ts.ints.is_empty() {836result += &format!(", ints={}", iterable_to_string(&ts.ints));837}838if !ts.floats.is_empty() {839result += &format!(", floats={}", iterable_to_string(&ts.floats));840}841result += ")";842result843}844845/// Generate the table of ValueTypeSets described by type_sets.846pub(crate) fn gen_typesets_table(type_sets: &UniqueTable<TypeSet>, fmt: &mut Formatter) {847if type_sets.len() == 0 {848return;849}850851fmt.comment("Table of value type sets.");852assert!(type_sets.len() <= TYPESET_LIMIT, "Too many type sets!");853fmtln!(854fmt,855"const TYPE_SETS: [ir::instructions::ValueTypeSet; {}] = [",856type_sets.len()857);858fmt.indent(|fmt| {859for ts in type_sets.iter() {860fmt.add_block("ir::instructions::ValueTypeSet", |fmt| {861fmt.comment(typeset_to_string(ts));862gen_bitset(&ts.lanes, "lanes", 16, fmt);863gen_bitset(&ts.dynamic_lanes, "dynamic_lanes", 16, fmt);864gen_bitset(&ts.ints, "ints", 8, fmt);865gen_bitset(&ts.floats, "floats", 8, fmt);866});867fmt.line(",");868}869});870fmtln!(fmt, "];");871}872873/// Generate value type constraints for all instructions.874/// - Emit a compact constant table of ValueTypeSet objects.875/// - Emit a compact constant table of OperandConstraint objects.876/// - Emit an opcode-indexed table of instruction constraints.877fn gen_type_constraints(all_inst: &AllInstructions, fmt: &mut Formatter) {878// Table of TypeSet instances.879let mut type_sets = UniqueTable::new();880881// Table of operand constraint sequences (as tuples). Each operand882// constraint is represented as a string, one of:883// - `Concrete(vt)`, where `vt` is a value type name.884// - `Free(idx)` where `idx` is an index into `type_sets`.885// - `Same`, `Lane`, `AsTruthy` for controlling typevar-derived constraints.886let mut operand_seqs = UniqueSeqTable::new();887888// Preload table with constraints for typical binops.889operand_seqs.add(&vec!["Same".to_string(); 3]);890891fmt.comment("Table of opcode constraints.");892fmtln!(893fmt,894"const OPCODE_CONSTRAINTS: [OpcodeConstraints; {}] = [",895all_inst.len()896);897fmt.indent(|fmt| {898for inst in all_inst.iter() {899let (ctrl_typevar, ctrl_typeset) = if let Some(poly) = &inst.polymorphic_info {900let index = type_sets.add(poly.ctrl_typevar.get_raw_typeset());901(Some(&poly.ctrl_typevar), index)902} else {903(None, TYPESET_LIMIT)904};905906// Collect constraints for the value results, not including `variable_args` results907// which are always special cased.908let mut constraints = Vec::new();909for &index in &inst.value_results {910constraints.push(get_constraint(&inst.operands_out[index], ctrl_typevar, &mut type_sets));911}912for &index in &inst.value_opnums {913constraints.push(get_constraint(&inst.operands_in[index], ctrl_typevar, &mut type_sets));914}915916let constraint_offset = operand_seqs.add(&constraints);917918let fixed_results = inst.value_results.len();919let fixed_values = inst.value_opnums.len();920921// Can the controlling type variable be inferred from the designated operand?922let use_typevar_operand = if let Some(poly) = &inst.polymorphic_info {923poly.use_typevar_operand924} else {925false926};927928// Can the controlling type variable be inferred from the result?929let use_result = fixed_results > 0 && inst.operands_out[inst.value_results[0]].type_var() == ctrl_typevar;930931// Are we required to use the designated operand instead of the result?932let requires_typevar_operand = use_typevar_operand && !use_result;933934fmt.comment(935format!("{}: fixed_results={}, use_typevar_operand={}, requires_typevar_operand={}, fixed_values={}",936inst.camel_name,937fixed_results,938use_typevar_operand,939requires_typevar_operand,940fixed_values)941);942fmt.comment(format!("Constraints=[{}]", constraints943.iter()944.map(|x| format!("'{x}'"))945.collect::<Vec<_>>()946.join(", ")));947if let Some(poly) = &inst.polymorphic_info {948fmt.comment(format!("Polymorphic over {}", typeset_to_string(poly.ctrl_typevar.get_raw_typeset())));949}950951// Compute the bit field encoding, c.f. instructions.rs.952assert!(fixed_results < 8 && fixed_values < 8, "Bit field encoding too tight");953let mut flags = fixed_results; // 3 bits954if use_typevar_operand {955flags |= 1<<3; // 4th bit956}957if requires_typevar_operand {958flags |= 1<<4; // 5th bit959}960flags |= fixed_values << 5; // 6th bit and more961962fmt.add_block("OpcodeConstraints",|fmt| {963fmtln!(fmt, "flags: {:#04x},", flags);964fmtln!(fmt, "typeset_offset: {},", ctrl_typeset);965fmtln!(fmt, "constraint_offset: {},", constraint_offset);966});967fmt.line(",");968}969});970fmtln!(fmt, "];");971fmt.empty_line();972973gen_typesets_table(&type_sets, fmt);974fmt.empty_line();975976fmt.comment("Table of operand constraint sequences.");977fmtln!(978fmt,979"const OPERAND_CONSTRAINTS: [OperandConstraint; {}] = [",980operand_seqs.len()981);982fmt.indent(|fmt| {983for constraint in operand_seqs.iter() {984fmtln!(fmt, "OperandConstraint::{},", constraint);985}986});987fmtln!(fmt, "];");988}989990/// Emit member initializers for an instruction format.991fn gen_member_inits(format: &InstructionFormat, fmt: &mut Formatter) {992// Immediate operands.993// We have local variables with the same names as the members.994for f in &format.imm_fields {995fmtln!(fmt, "{},", f.member);996}997998// Value operands.999if format.has_value_list {1000fmt.line("args,");1001} else if format.num_value_operands == 1 {1002fmt.line("arg: arg0,");1003} else if format.num_value_operands > 1 {1004let mut args = Vec::new();1005for i in 0..format.num_value_operands {1006args.push(format!("arg{i}"));1007}1008fmtln!(fmt, "args: [{}],", args.join(", "));1009}10101011// Block operands1012match format.num_block_operands {10130 => (),10141 => fmt.line("destination: block0"),1015n => {1016let mut blocks = Vec::new();1017for i in 0..n {1018blocks.push(format!("block{i}"));1019}1020fmtln!(fmt, "blocks: [{}],", blocks.join(", "));1021}1022}10231024// Raw block operands.1025match format.num_raw_block_operands {10260 => (),10271 => fmt.line("block: block0,"),1028_ => panic!("Too many raw block arguments"),1029}1030}10311032/// Emit a method for creating and inserting an instruction format.1033///1034/// All instruction formats take an `opcode` argument and a `ctrl_typevar` argument for deducing1035/// the result types.1036fn gen_format_constructor(format: &InstructionFormat, fmt: &mut Formatter) {1037// Construct method arguments.1038let mut args = vec![1039"self".to_string(),1040"opcode: Opcode".into(),1041"ctrl_typevar: Type".into(),1042];10431044// Raw block operands.1045args.extend((0..format.num_raw_block_operands).map(|i| format!("block{i}: ir::Block")));10461047// Normal operand arguments. Start with the immediate operands.1048for f in &format.imm_fields {1049args.push(format!("{}: {}", f.member, f.kind.rust_type));1050}10511052// Then the block operands.1053args.extend((0..format.num_block_operands).map(|i| format!("block{i}: ir::BlockCall")));10541055// Then the value operands.1056if format.has_value_list {1057// Take all value arguments as a finished value list. The value lists1058// are created by the individual instruction constructors.1059args.push("args: ir::ValueList".into());1060} else {1061// Take a fixed number of value operands.1062for i in 0..format.num_value_operands {1063args.push(format!("arg{i}: Value"));1064}1065}10661067let proto = format!(1068"{}({}) -> (Inst, &'f mut ir::DataFlowGraph)",1069format.name,1070args.join(", ")1071);10721073let imms_need_masking = format1074.imm_fields1075.iter()1076.any(|f| f.kind.rust_type == "ir::immediates::Imm64");10771078fmt.doc_comment(format.to_string());1079fmt.line("#[allow(non_snake_case, reason = \"generated code\")]");1080fmt.add_block(&format!("fn {proto}"), |fmt| {1081// Generate the instruction data.1082fmt.add_block(&format!(1083"let{} data = ir::InstructionData::{}",1084if imms_need_masking { " mut" } else { "" },1085format.name1086), |fmt| {1087fmt.line("opcode,");1088gen_member_inits(format, fmt);1089});1090fmtln!(fmt, ";");10911092if imms_need_masking {1093fmtln!(fmt, "data.mask_immediates(ctrl_typevar);");1094}10951096// Assert that this opcode belongs to this format1097fmtln!(fmt, "debug_assert_eq!(opcode.format(), InstructionFormat::from(&data), \"Wrong InstructionFormat for Opcode: {{opcode}}\");");10981099fmt.line("self.build(data, ctrl_typevar)");1100});1101}11021103/// Emit a method for generating the instruction `inst`.1104///1105/// The method will create and insert an instruction, then return the result values, or the1106/// instruction reference itself for instructions that don't have results.1107fn gen_inst_builder(inst: &Instruction, format: &InstructionFormat, fmt: &mut Formatter) {1108// Construct method arguments.1109let mut args = vec![String::new()];11101111let mut args_doc = Vec::new();1112let mut rets_doc = Vec::new();11131114// The controlling type variable will be inferred from the input values if1115// possible. Otherwise, it is the first method argument.1116if let Some(poly) = &inst.polymorphic_info {1117if !poly.use_typevar_operand {1118args.push(format!("{}: crate::ir::Type", poly.ctrl_typevar.name));1119args_doc.push(format!(1120"- {} (controlling type variable): {}",1121poly.ctrl_typevar.name, poly.ctrl_typevar.doc1122));1123}1124}11251126let mut tmpl_types = Vec::new();1127let mut into_args = Vec::new();1128let mut block_args = Vec::new();1129let mut lifetime_param = None;1130for op in &inst.operands_in {1131if op.kind.is_block() {1132args.push(format!("{}_label: {}", op.name, "ir::Block"));1133args_doc.push(format!(1134"- {}_label: {}",1135op.name, "Destination basic block"1136));11371138let lifetime = *lifetime_param.get_or_insert_with(|| {1139tmpl_types.insert(0, "'a".to_string());1140"'a"1141});1142args.push(format!(1143"{}_args: impl IntoIterator<Item = &{} BlockArg>",1144op.name, lifetime,1145));1146args_doc.push(format!("- {}_args: {}", op.name, "Block arguments"));11471148block_args.push(op);1149} else if op.kind.is_raw_block() {1150args.push("block: ir::Block".into());1151args_doc.push("- block: raw basic block".into());1152} else {1153let t = if op.is_immediate() {1154let t = format!("T{}", tmpl_types.len() + 1);1155tmpl_types.push(format!("{}: Into<{}>", t, op.kind.rust_type));1156into_args.push(op.name);1157t1158} else {1159op.kind.rust_type.to_string()1160};1161args.push(format!("{}: {}", op.name, t));1162args_doc.push(format!("- {}: {}", op.name, op.doc()));1163}1164}11651166// We need to mutate `self` if this instruction accepts a value list, or will construct1167// BlockCall values.1168if format.has_value_list || !block_args.is_empty() {1169args[0].push_str("mut self");1170} else {1171args[0].push_str("self");1172}11731174for op in &inst.operands_out {1175rets_doc.push(format!("- {}: {}", op.name, op.doc()));1176}11771178let rtype = match inst.value_results.len() {11790 => "Inst".into(),11801 => "Value".into(),1181_ => format!("({})", vec!["Value"; inst.value_results.len()].join(", ")),1182};11831184let tmpl = if !tmpl_types.is_empty() {1185format!("<{}>", tmpl_types.join(", "))1186} else {1187"".into()1188};11891190let proto = format!(1191"{}{}({}) -> {}",1192inst.snake_name(),1193tmpl,1194args.join(", "),1195rtype1196);11971198fmt.doc_comment(&inst.doc);1199if !args_doc.is_empty() {1200fmt.line("///");1201fmt.doc_comment("Inputs:");1202fmt.line("///");1203for doc_line in args_doc {1204fmt.doc_comment(doc_line);1205}1206}1207if !rets_doc.is_empty() {1208fmt.line("///");1209fmt.doc_comment("Outputs:");1210fmt.line("///");1211for doc_line in rets_doc {1212fmt.doc_comment(doc_line);1213}1214}12151216fmt.line("#[allow(non_snake_case, reason = \"generated code\")]");1217fmt.add_block(&format!("fn {proto}"), |fmt| {1218// Convert all of the `Into<>` arguments.1219for arg in into_args {1220fmtln!(fmt, "let {} = {}.into();", arg, arg);1221}12221223// Convert block references1224for op in block_args {1225fmtln!(1226fmt,1227"let {0} = self.data_flow_graph_mut().block_call({0}_label, {0}_args);",1228op.name1229);1230}12311232// Arguments for instruction constructor.1233let first_arg = format!("Opcode::{}", inst.camel_name);1234let mut args = vec![first_arg.as_str()];1235if let Some(poly) = &inst.polymorphic_info {1236if poly.use_typevar_operand {1237// Infer the controlling type variable from the input operands.1238let op_num = inst.value_opnums[format.typevar_operand.unwrap()];1239fmtln!(1240fmt,1241"let ctrl_typevar = self.data_flow_graph().value_type({});",1242inst.operands_in[op_num].name1243);12441245// The format constructor will resolve the result types from the type var.1246args.push("ctrl_typevar");1247} else {1248// This was an explicit method argument.1249args.push(&poly.ctrl_typevar.name);1250}1251} else {1252// No controlling type variable needed.1253args.push("types::INVALID");1254}12551256// Now add all of the immediate operands to the constructor arguments.1257for &op_num in &inst.imm_opnums {1258args.push(inst.operands_in[op_num].name);1259}12601261// Finally, the value operands.1262if format.has_value_list {1263// We need to build a value list with all the arguments.1264fmt.line("let mut vlist = ir::ValueList::default();");1265args.push("vlist");1266fmt.line("{");1267fmt.indent(|fmt| {1268fmt.line("let pool = &mut self.data_flow_graph_mut().value_lists;");1269for op in &inst.operands_in {1270if op.is_value() {1271fmtln!(fmt, "vlist.push({}, pool);", op.name);1272} else if op.is_varargs() {1273fmtln!(fmt, "vlist.extend({}.iter().cloned(), pool);", op.name);1274}1275}1276});1277fmt.line("}");1278} else {1279// With no value list, we're guaranteed to just have a set of fixed value operands.1280for &op_num in &inst.value_opnums {1281args.push(inst.operands_in[op_num].name);1282}1283}12841285// Call to the format constructor,1286let fcall = format!("self.{}({})", format.name, args.join(", "));12871288fmtln!(fmt, "let (inst, dfg) = {};", fcall);1289fmtln!(1290fmt,1291"crate::trace!(\"inserted {{inst:?}}: {{}}\", dfg.display_inst(inst));"1292);12931294if inst.value_results.is_empty() {1295fmtln!(fmt, "inst");1296return;1297}12981299if inst.value_results.len() == 1 {1300fmt.line("dfg.first_result(inst)");1301} else {1302fmtln!(1303fmt,1304"let results = &dfg.inst_results(inst)[0..{}];",1305inst.value_results.len()1306);1307fmtln!(1308fmt,1309"({})",1310inst.value_results1311.iter()1312.enumerate()1313.map(|(i, _)| format!("results[{i}]"))1314.collect::<Vec<_>>()1315.join(", ")1316);1317}1318});1319}13201321/// Generate a Builder trait with methods for all instructions.1322fn gen_builder(1323instructions: &AllInstructions,1324formats: &[Rc<InstructionFormat>],1325fmt: &mut Formatter,1326) {1327fmt.doc_comment(1328r#"1329Convenience methods for building instructions.13301331The `InstBuilder` trait has one method per instruction opcode for1332conveniently constructing the instruction with minimum arguments.1333Polymorphic instructions infer their result types from the input1334arguments when possible. In some cases, an explicit `ctrl_typevar`1335argument is required.13361337The opcode methods return the new instruction's result values, or1338the `Inst` itself for instructions that don't have any results.13391340There is also a method per instruction format. These methods all1341return an `Inst`.13421343When an address to a load or store is specified, its integer1344size is required to be equal to the platform's pointer width.1345"#,1346);1347fmt.add_block("pub trait InstBuilder<'f>: InstBuilderBase<'f>", |fmt| {1348for inst in instructions.iter() {1349gen_inst_builder(inst, &inst.format, fmt);1350fmt.empty_line();1351}1352for (i, format) in formats.iter().enumerate() {1353gen_format_constructor(format, fmt);1354if i + 1 != formats.len() {1355fmt.empty_line();1356}1357}1358});1359}13601361pub(crate) fn generate(1362formats: &[Rc<InstructionFormat>],1363all_inst: &AllInstructions,1364opcode_filename: &str,1365inst_builder_filename: &str,1366out_dir: &std::path::Path,1367) -> Result<(), error::Error> {1368// Opcodes.1369let mut fmt = Formatter::new(Language::Rust);1370gen_formats(&formats, &mut fmt);1371gen_instruction_data(&formats, &mut fmt);1372fmt.empty_line();1373gen_instruction_data_impl(&formats, &mut fmt);1374fmt.empty_line();1375gen_opcodes(all_inst, &mut fmt);1376fmt.empty_line();1377gen_type_constraints(all_inst, &mut fmt);1378fmt.write(opcode_filename, out_dir)?;13791380// Instruction builder.1381let mut fmt = Formatter::new(Language::Rust);1382gen_builder(all_inst, &formats, &mut fmt);1383fmt.write(inst_builder_filename, out_dir)?;13841385Ok(())1386}138713881389