1 use crate::cdsl::formats::InstructionFormat; 2 use crate::cdsl::instructions::AllInstructions; 3 use crate::error; 4 use cranelift_srcgen::{Formatter, Language, fmtln}; 5 use std::{borrow::Cow, cmp::Ordering, rc::Rc}; 6 7 /// Which ISLE target are we generating code for? 8 #[derive(Clone, Copy, PartialEq, Eq)] 9 enum IsleTarget { 10 /// Generating code for instruction selection and lowering. 11 Lower, 12 /// Generating code for CLIF to CLIF optimizations. 13 Opt, 14 } 15 16 fn gen_common_isle( 17 formats: &[Rc<InstructionFormat>], 18 instructions: &AllInstructions, 19 fmt: &mut Formatter, 20 isle_target: IsleTarget, 21 ) { 22 use std::collections::{BTreeMap, BTreeSet}; 23 use std::fmt::Write; 24 25 use crate::cdsl::formats::FormatField; 26 27 fmt.multi_line( 28 r#" 29 ;; GENERATED BY `gen_isle`. DO NOT EDIT!!! 30 ;; 31 ;; This ISLE file defines all the external type declarations for Cranelift's 32 ;; data structures that ISLE will process, such as `InstructionData` and 33 ;; `Opcode`. 34 "#, 35 ); 36 fmt.empty_line(); 37 38 // Collect and deduplicate the immediate types from the instruction fields. 39 let rust_name = |f: &FormatField| f.kind.rust_type.rsplit("::").next().unwrap(); 40 let fields = |f: &FormatField| f.kind.fields.clone(); 41 let immediate_types: BTreeMap<_, _> = formats 42 .iter() 43 .flat_map(|f| { 44 f.imm_fields 45 .iter() 46 .map(|i| (rust_name(i), fields(i))) 47 .collect::<Vec<_>>() 48 }) 49 .collect(); 50 51 // Separate the `enum` immediates (e.g., `FloatCC`) from other kinds of 52 // immediates. 53 let (enums, others): (BTreeMap<_, _>, BTreeMap<_, _>) = immediate_types 54 .iter() 55 .partition(|(_, field)| field.enum_values().is_some()); 56 57 // Generate all the extern type declarations we need for the non-`enum` 58 // immediates. 59 fmt.line(";;;; Extern type declarations for immediates ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"); 60 fmt.empty_line(); 61 for ty in others.keys() { 62 fmtln!(fmt, "(type {} (primitive {}))", ty, ty); 63 } 64 fmt.empty_line(); 65 66 // Generate the `enum` immediates, expanding all of the available variants 67 // into ISLE. 68 for (name, field) in enums { 69 let field = field.enum_values().expect("only enums considered here"); 70 let variants = field.values().cloned().collect(); 71 gen_isle_enum(name, variants, fmt) 72 } 73 74 // Generate all of the value arrays we need for `InstructionData` as well as 75 // the constructors and extractors for them. 76 fmt.line(";;;; Value Arrays ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"); 77 fmt.empty_line(); 78 let value_array_arities: BTreeSet<_> = formats 79 .iter() 80 .filter(|f| f.typevar_operand.is_some() && !f.has_value_list && f.num_value_operands != 1) 81 .map(|f| f.num_value_operands) 82 .collect(); 83 for n in value_array_arities { 84 fmtln!(fmt, ";; ISLE representation of `[Value; {}]`.", n); 85 fmtln!(fmt, "(type ValueArray{} extern (enum))", n); 86 fmt.empty_line(); 87 88 fmtln!( 89 fmt, 90 "(decl value_array_{} ({}) ValueArray{})", 91 n, 92 (0..n).map(|_| "Value").collect::<Vec<_>>().join(" "), 93 n 94 ); 95 fmtln!( 96 fmt, 97 "(extern constructor value_array_{} pack_value_array_{})", 98 n, 99 n 100 ); 101 fmtln!( 102 fmt, 103 "(extern extractor infallible value_array_{} unpack_value_array_{})", 104 n, 105 n 106 ); 107 fmt.empty_line(); 108 } 109 110 // Generate all of the block arrays we need for `InstructionData` as well as 111 // the constructors and extractors for them. 112 fmt.line(";;;; Block Arrays ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"); 113 fmt.empty_line(); 114 let block_array_arities: BTreeSet<_> = formats 115 .iter() 116 .filter(|f| f.num_block_operands > 1) 117 .map(|f| f.num_block_operands) 118 .collect(); 119 for n in block_array_arities { 120 fmtln!(fmt, ";; ISLE representation of `[BlockCall; {}]`.", n); 121 fmtln!(fmt, "(type BlockArray{} extern (enum))", n); 122 fmt.empty_line(); 123 124 fmtln!( 125 fmt, 126 "(decl block_array_{0} ({1}) BlockArray{0})", 127 n, 128 (0..n).map(|_| "BlockCall").collect::<Vec<_>>().join(" ") 129 ); 130 131 fmtln!( 132 fmt, 133 "(extern constructor block_array_{0} pack_block_array_{0})", 134 n 135 ); 136 137 fmtln!( 138 fmt, 139 "(extern extractor infallible block_array_{0} unpack_block_array_{0})", 140 n 141 ); 142 fmt.empty_line(); 143 } 144 145 // Generate the extern type declaration for `Opcode`. 146 fmt.line(";;;; `Opcode` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"); 147 fmt.empty_line(); 148 fmt.line("(type Opcode extern"); 149 fmt.indent(|fmt| { 150 fmt.line("(enum"); 151 fmt.indent(|fmt| { 152 for inst in instructions { 153 fmtln!(fmt, "{}", inst.camel_name); 154 } 155 }); 156 fmt.line(")"); 157 }); 158 fmt.line(")"); 159 fmt.empty_line(); 160 161 // Generate the extern type declaration for `InstructionData`. 162 fmtln!( 163 fmt, 164 ";;;; `InstructionData` ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;", 165 ); 166 fmt.empty_line(); 167 fmtln!(fmt, "(type InstructionData extern"); 168 fmt.indent(|fmt| { 169 fmt.line("(enum"); 170 fmt.indent(|fmt| { 171 for format in formats { 172 let mut s = format!("({} (opcode Opcode)", format.name); 173 if format.has_value_list { 174 s.push_str(" (args ValueList)"); 175 } else if format.num_value_operands == 1 { 176 s.push_str(" (arg Value)"); 177 } else if format.num_value_operands > 1 { 178 write!(&mut s, " (args ValueArray{})", format.num_value_operands).unwrap(); 179 } 180 181 match format.num_block_operands { 182 0 => (), 183 1 => write!(&mut s, " (destination BlockCall)").unwrap(), 184 n => write!(&mut s, " (blocks BlockArray{n})").unwrap(), 185 } 186 187 for field in &format.imm_fields { 188 write!( 189 &mut s, 190 " ({} {})", 191 field.member, 192 field.kind.rust_type.rsplit("::").next().unwrap() 193 ) 194 .unwrap(); 195 } 196 s.push(')'); 197 fmt.line(&s); 198 } 199 }); 200 fmt.line(")"); 201 }); 202 fmt.line(")"); 203 fmt.empty_line(); 204 205 // Generate the helper extractors for each opcode's full instruction. 206 fmtln!( 207 fmt, 208 ";;;; Extracting Opcode, Operands, and Immediates from `InstructionData` ;;;;;;;;", 209 ); 210 fmt.empty_line(); 211 for inst in instructions { 212 let results_len = inst.value_results.len(); 213 let is_var_args = inst.format.has_value_list; 214 let has_side_effects = inst.can_trap || inst.other_side_effects; 215 216 let (ret_ty, ty_in_decl, make_inst_ctor, inst_data_etor) = 217 match (isle_target, is_var_args, results_len, has_side_effects) { 218 // The mid-end does not deal with instructions that have var-args right now. 219 (IsleTarget::Opt, true, _, _) => continue, 220 221 (IsleTarget::Opt, _, 1, false) => ("Value", true, "make_inst", "inst_data_value"), 222 (IsleTarget::Opt, _, _, _) => ("Inst", false, "make_skeleton_inst", "inst_data"), 223 (IsleTarget::Lower, _, _, _) => ("Inst", false, "make_inst", "inst_data_value"), 224 }; 225 226 fmtln!( 227 fmt, 228 "(decl {} ({}{}) {})", 229 inst.name, 230 if ty_in_decl { "Type " } else { "" }, 231 inst.operands_in 232 .iter() 233 .map(|o| { 234 let ty = o.kind.rust_type; 235 if ty == "&[Value]" { 236 "ValueSlice" 237 } else { 238 ty.rsplit("::").next().unwrap() 239 } 240 }) 241 .collect::<Vec<_>>() 242 .join(" "), 243 ret_ty 244 ); 245 fmtln!(fmt, "(extractor"); 246 fmt.indent(|fmt| { 247 fmtln!( 248 fmt, 249 "({} {}{})", 250 inst.name, 251 if ty_in_decl { "ty " } else { "" }, 252 inst.operands_in 253 .iter() 254 .map(|o| { o.name }) 255 .collect::<Vec<_>>() 256 .join(" ") 257 ); 258 259 let mut s = format!( 260 "({inst_data_etor} {}(InstructionData.{} (Opcode.{})", 261 if ty_in_decl { "ty " } else { "" }, 262 inst.format.name, 263 inst.camel_name 264 ); 265 266 // Value and varargs operands. 267 if inst.format.has_value_list { 268 // The instruction format uses a value list, but the 269 // instruction itself might have not only a `&[Value]` 270 // varargs operand, but also one or more `Value` operands as 271 // well. If this is the case, then we need to read them off 272 // the front of the `ValueList`. 273 let values: Vec<_> = inst 274 .operands_in 275 .iter() 276 .filter(|o| o.is_value()) 277 .map(|o| o.name) 278 .collect(); 279 let varargs = inst 280 .operands_in 281 .iter() 282 .find(|o| o.is_varargs()) 283 .unwrap() 284 .name; 285 if values.is_empty() { 286 write!(&mut s, " (value_list_slice {varargs})").unwrap(); 287 } else { 288 write!( 289 &mut s, 290 " (unwrap_head_value_list_{} {} {})", 291 values.len(), 292 values.join(" "), 293 varargs 294 ) 295 .unwrap(); 296 } 297 } else if inst.format.num_value_operands == 1 { 298 write!( 299 &mut s, 300 " {}", 301 inst.operands_in.iter().find(|o| o.is_value()).unwrap().name 302 ) 303 .unwrap(); 304 } else if inst.format.num_value_operands > 1 { 305 let values = inst 306 .operands_in 307 .iter() 308 .filter(|o| o.is_value()) 309 .map(|o| o.name) 310 .collect::<Vec<_>>(); 311 assert_eq!(values.len(), inst.format.num_value_operands); 312 let values = values.join(" "); 313 write!( 314 &mut s, 315 " (value_array_{} {})", 316 inst.format.num_value_operands, values, 317 ) 318 .unwrap(); 319 } 320 321 // Immediates. 322 let imm_operands: Vec<_> = inst 323 .operands_in 324 .iter() 325 .filter(|o| !o.is_value() && !o.is_varargs() && !o.kind.is_block()) 326 .collect(); 327 assert_eq!(imm_operands.len(), inst.format.imm_fields.len(),); 328 for op in imm_operands { 329 write!(&mut s, " {}", op.name).unwrap(); 330 } 331 332 // Blocks. 333 let block_operands: Vec<_> = inst 334 .operands_in 335 .iter() 336 .filter(|o| o.kind.is_block()) 337 .collect(); 338 assert_eq!(block_operands.len(), inst.format.num_block_operands); 339 assert!(block_operands.len() <= 2); 340 341 if !block_operands.is_empty() { 342 if block_operands.len() == 1 { 343 write!(&mut s, " {}", block_operands[0].name).unwrap(); 344 } else { 345 let blocks: Vec<_> = block_operands.iter().map(|o| o.name).collect(); 346 let blocks = blocks.join(" "); 347 write!( 348 &mut s, 349 " (block_array_{} {})", 350 inst.format.num_block_operands, blocks, 351 ) 352 .unwrap(); 353 } 354 } 355 356 s.push_str("))"); 357 fmt.line(&s); 358 }); 359 fmt.line(")"); 360 361 // Generate a constructor if this is the mid-end prelude. 362 if isle_target == IsleTarget::Opt { 363 fmtln!( 364 fmt, 365 "(rule ({}{} {})", 366 inst.name, 367 if ty_in_decl { " ty" } else { "" }, 368 inst.operands_in 369 .iter() 370 .map(|o| o.name) 371 .collect::<Vec<_>>() 372 .join(" ") 373 ); 374 fmt.indent(|fmt| { 375 let mut s = format!( 376 "({make_inst_ctor}{} (InstructionData.{} (Opcode.{})", 377 if ty_in_decl { " ty" } else { "" }, 378 inst.format.name, 379 inst.camel_name 380 ); 381 382 // Handle values. Note that we skip generating 383 // constructors for any instructions with variadic 384 // value lists. This is fine for the mid-end because 385 // in practice only calls and branches (for branch 386 // args) use this functionality, and neither can 387 // really be optimized or rewritten in the mid-end 388 // (currently). 389 // 390 // As a consequence, we only have to handle the 391 // one-`Value` case, in which the `Value` is directly 392 // in the `InstructionData`, and the multiple-`Value` 393 // case, in which the `Value`s are in a 394 // statically-sized array (e.g. `[Value; 2]` for a 395 // binary op). 396 assert!(!inst.format.has_value_list); 397 if inst.format.num_value_operands == 1 { 398 write!( 399 &mut s, 400 " {}", 401 inst.operands_in.iter().find(|o| o.is_value()).unwrap().name 402 ) 403 .unwrap(); 404 } else if inst.format.num_value_operands > 1 { 405 // As above, get all bindings together, and pass 406 // to a sub-term; here we use a constructor to 407 // build the value array. 408 let values = inst 409 .operands_in 410 .iter() 411 .filter(|o| o.is_value()) 412 .map(|o| o.name) 413 .collect::<Vec<_>>(); 414 assert_eq!(values.len(), inst.format.num_value_operands); 415 let values = values.join(" "); 416 write!( 417 &mut s, 418 " (value_array_{}_ctor {})", 419 inst.format.num_value_operands, values 420 ) 421 .unwrap(); 422 } 423 424 if inst.format.num_block_operands > 0 { 425 let blocks: Vec<_> = inst 426 .operands_in 427 .iter() 428 .filter(|o| o.kind.is_block()) 429 .map(|o| o.name) 430 .collect(); 431 if inst.format.num_block_operands == 1 { 432 write!(&mut s, " {}", blocks.first().unwrap(),).unwrap(); 433 } else { 434 write!( 435 &mut s, 436 " (block_array_{} {})", 437 inst.format.num_block_operands, 438 blocks.join(" ") 439 ) 440 .unwrap(); 441 } 442 } 443 444 // Immediates (non-value args). 445 for o in inst 446 .operands_in 447 .iter() 448 .filter(|o| !o.is_value() && !o.is_varargs() && !o.kind.is_block()) 449 { 450 write!(&mut s, " {}", o.name).unwrap(); 451 } 452 s.push_str("))"); 453 fmt.line(&s); 454 }); 455 fmt.line(")"); 456 } 457 458 fmt.empty_line(); 459 } 460 } 461 462 fn gen_opt_isle( 463 formats: &[Rc<InstructionFormat>], 464 instructions: &AllInstructions, 465 fmt: &mut Formatter, 466 ) { 467 gen_common_isle(formats, instructions, fmt, IsleTarget::Opt); 468 } 469 470 fn gen_lower_isle( 471 formats: &[Rc<InstructionFormat>], 472 instructions: &AllInstructions, 473 fmt: &mut Formatter, 474 ) { 475 gen_common_isle(formats, instructions, fmt, IsleTarget::Lower); 476 } 477 478 /// Generate an `enum` immediate in ISLE. 479 fn gen_isle_enum(name: &str, mut variants: Vec<&str>, fmt: &mut Formatter) { 480 variants.sort(); 481 let prefix = format!(";;;; Enumerated Immediate: {name} "); 482 fmtln!(fmt, "{:;<80}", prefix); 483 fmt.empty_line(); 484 fmtln!(fmt, "(type {} extern", name); 485 fmt.indent(|fmt| { 486 fmt.line("(enum"); 487 fmt.indent(|fmt| { 488 for variant in variants { 489 fmtln!(fmt, "{}", variant); 490 } 491 }); 492 fmt.line(")"); 493 }); 494 fmt.line(")"); 495 fmt.empty_line(); 496 } 497 498 #[derive(Clone, Copy, PartialEq, Eq)] 499 struct NumericType { 500 signed: bool, 501 byte_width: u8, 502 } 503 504 impl NumericType { 505 fn all() -> impl Iterator<Item = NumericType> { 506 [1, 2, 4, 8, 16].into_iter().flat_map(|byte_width| { 507 [true, false] 508 .into_iter() 509 .map(move |signed| NumericType { signed, byte_width }) 510 }) 511 } 512 513 fn name(&self) -> &'static str { 514 let idx = self.byte_width.ilog2(); 515 let idx = usize::try_from(idx).unwrap(); 516 if self.signed { 517 ["i8", "i16", "i32", "i64", "i128"][idx] 518 } else { 519 ["u8", "u16", "u32", "u64", "u128"][idx] 520 } 521 } 522 } 523 524 #[derive(Clone, Default, PartialEq, Eq)] 525 struct NumericOp<'a> { 526 /// The name of this operation. 527 name: &'a str, 528 /// The return type of this operation. 529 ret: &'a str, 530 /// Whether this operation is partial. 531 partial: bool, 532 /// (name, type) pairs of arguments. 533 args: Rc<[(&'a str, &'a str)]>, 534 /// The source text for the constructor's body. 535 body: &'a str, 536 /// Whether extractors should be generated for this op. 537 /// 538 /// Must have `arity == 1`, `ret == bool`, and `name.starts_with("is_")`. 539 etors: bool, 540 } 541 542 impl NumericOp<'_> { 543 fn ops_for_type(ty: &NumericType) -> impl Iterator<Item = NumericOp<'_>> { 544 let arity1 = NumericOp { 545 args: [("a", ty.name())].into(), 546 ..NumericOp::default() 547 }; 548 549 let arity2 = NumericOp { 550 args: [("a", ty.name()), ("b", ty.name())].into(), 551 ..NumericOp::default() 552 }; 553 554 let comparison = NumericOp { 555 ret: "bool", 556 ..arity2.clone() 557 }; 558 559 let predicate = NumericOp { 560 ret: "bool", 561 etors: true, 562 ..arity1.clone() 563 }; 564 565 let binop = NumericOp { 566 ret: ty.name(), 567 ..arity2.clone() 568 }; 569 570 let partial_binop = NumericOp { 571 ret: ty.name(), 572 partial: true, 573 ..binop.clone() 574 }; 575 576 let unop = NumericOp { 577 ret: ty.name(), 578 ..arity1.clone() 579 }; 580 581 let partial_unop = NumericOp { 582 ret: ty.name(), 583 partial: true, 584 ..unop.clone() 585 }; 586 587 let shift = NumericOp { 588 args: [("a", ty.name()), ("b", "u32")].into(), 589 ..binop.clone() 590 }; 591 592 let partial_shift = NumericOp { 593 args: [("a", ty.name()), ("b", "u32")].into(), 594 ..partial_binop.clone() 595 }; 596 597 // Operations that apply to both signed and unsigned numbers. 598 let ops = [ 599 // Comparisons. 600 NumericOp { 601 name: "eq", 602 body: "a == b", 603 ..comparison.clone() 604 }, 605 NumericOp { 606 name: "ne", 607 body: "a != b", 608 ..comparison.clone() 609 }, 610 NumericOp { 611 name: "lt", 612 body: "a < b", 613 ..comparison.clone() 614 }, 615 NumericOp { 616 name: "lt_eq", 617 body: "a <= b", 618 ..comparison.clone() 619 }, 620 NumericOp { 621 name: "gt", 622 body: "a > b", 623 ..comparison.clone() 624 }, 625 NumericOp { 626 name: "gt_eq", 627 body: "a >= b", 628 ..comparison.clone() 629 }, 630 // Arithmetic operations. 631 // 632 // For each operation (e.g. addition) we have three variants: 633 // 634 // * partial ctor `checked_add`: no return value on overflow 635 // * ctor `wrapping_add`: wraps on overflow 636 // * ctor `add`: non-partial but panics at runtime on overflow 637 NumericOp { 638 name: "checked_add", 639 body: "a.checked_add(b)", 640 ..partial_binop.clone() 641 }, 642 NumericOp { 643 name: "wrapping_add", 644 body: "a.wrapping_add(b)", 645 ..binop.clone() 646 }, 647 NumericOp { 648 name: "add", 649 body: r#"a.checked_add(b).unwrap_or_else(|| panic!("addition overflow: {a} + {b}"))"#, 650 ..binop.clone() 651 }, 652 NumericOp { 653 name: "checked_sub", 654 body: "a.checked_sub(b)", 655 ..partial_binop.clone() 656 }, 657 NumericOp { 658 name: "wrapping_sub", 659 body: "a.wrapping_sub(b)", 660 ..binop.clone() 661 }, 662 NumericOp { 663 name: "sub", 664 body: r#"a.checked_sub(b).unwrap_or_else(|| panic!("subtraction overflow: {a} - {b}"))"#, 665 ..binop.clone() 666 }, 667 NumericOp { 668 name: "checked_mul", 669 body: "a.checked_mul(b)", 670 ..partial_binop.clone() 671 }, 672 NumericOp { 673 name: "wrapping_mul", 674 body: "a.wrapping_mul(b)", 675 ..binop.clone() 676 }, 677 NumericOp { 678 name: "mul", 679 body: r#"a.checked_mul(b).unwrap_or_else(|| panic!("multiplication overflow: {a} * {b}"))"#, 680 ..binop.clone() 681 }, 682 NumericOp { 683 name: "checked_div", 684 body: "a.checked_div(b)", 685 ..partial_binop.clone() 686 }, 687 NumericOp { 688 name: "wrapping_div", 689 body: "a.wrapping_div(b)", 690 ..binop.clone() 691 }, 692 NumericOp { 693 name: "div", 694 body: r#"a.checked_div(b).unwrap_or_else(|| panic!("div failure: {a} / {b}"))"#, 695 ..binop.clone() 696 }, 697 NumericOp { 698 name: "checked_rem", 699 body: "a.checked_rem(b)", 700 ..partial_binop.clone() 701 }, 702 NumericOp { 703 name: "rem", 704 body: r#"a.checked_rem(b).unwrap_or_else(|| panic!("rem failure: {a} % {b}"))"#, 705 ..binop.clone() 706 }, 707 // Bitwise operations. 708 // 709 // When applicable (e.g. shifts) we have checked, wrapping, and 710 // unwrapping variants, similar to arithmetic operations. 711 NumericOp { 712 name: "and", 713 body: "a & b", 714 ..binop.clone() 715 }, 716 NumericOp { 717 name: "or", 718 body: "a | b", 719 ..binop.clone() 720 }, 721 NumericOp { 722 name: "xor", 723 body: "a ^ b", 724 ..binop.clone() 725 }, 726 NumericOp { 727 name: "not", 728 body: "!a", 729 ..unop.clone() 730 }, 731 NumericOp { 732 name: "checked_shl", 733 body: "a.checked_shl(b)", 734 ..partial_shift.clone() 735 }, 736 NumericOp { 737 name: "wrapping_shl", 738 body: "a.wrapping_shl(b)", 739 ..shift.clone() 740 }, 741 NumericOp { 742 name: "shl", 743 body: r#"a.checked_shl(b).unwrap_or_else(|| panic!("shl overflow: {a} << {b}"))"#, 744 ..shift.clone() 745 }, 746 NumericOp { 747 name: "checked_shr", 748 body: "a.checked_shr(b)", 749 ..partial_shift.clone() 750 }, 751 NumericOp { 752 name: "wrapping_shr", 753 body: "a.wrapping_shr(b)", 754 ..shift.clone() 755 }, 756 NumericOp { 757 name: "shr", 758 body: r#"a.checked_shr(b).unwrap_or_else(|| panic!("shr overflow: {a} >> {b}"))"#, 759 ..shift.clone() 760 }, 761 // Predicates. 762 // 763 // We generate both pure constructors and a variety of extractors 764 // for these. See the relevant comments in `gen_numerics_isle` about 765 // the extractors. 766 NumericOp { 767 name: "is_zero", 768 body: "a == 0", 769 ..predicate.clone() 770 }, 771 NumericOp { 772 name: "is_non_zero", 773 body: "a != 0", 774 ..predicate.clone() 775 }, 776 NumericOp { 777 name: "is_odd", 778 body: "a & 1 == 1", 779 ..predicate.clone() 780 }, 781 NumericOp { 782 name: "is_even", 783 body: "a & 1 == 0", 784 ..predicate.clone() 785 }, 786 // Miscellaneous unary operations. 787 NumericOp { 788 name: "checked_ilog2", 789 body: "a.checked_ilog2()", 790 ret: "u32", 791 ..partial_unop.clone() 792 }, 793 NumericOp { 794 name: "ilog2", 795 body: r#"a.checked_ilog2().unwrap_or_else(|| panic!("ilog2 overflow: {a}"))"#, 796 ret: "u32", 797 ..unop.clone() 798 }, 799 NumericOp { 800 name: "trailing_zeros", 801 body: "a.trailing_zeros()", 802 ret: "u32", 803 ..unop.clone() 804 }, 805 NumericOp { 806 name: "trailing_ones", 807 body: "a.trailing_ones()", 808 ret: "u32", 809 ..unop.clone() 810 }, 811 NumericOp { 812 name: "leading_zeros", 813 body: "a.leading_zeros()", 814 ret: "u32", 815 ..unop.clone() 816 }, 817 NumericOp { 818 name: "leading_ones", 819 body: "a.leading_ones()", 820 ret: "u32", 821 ..unop.clone() 822 }, 823 ]; 824 825 // Operations that apply only to signed numbers. 826 let signed_ops = [ 827 NumericOp { 828 name: "checked_neg", 829 body: "a.checked_neg()", 830 ..partial_unop.clone() 831 }, 832 NumericOp { 833 name: "wrapping_neg", 834 body: "a.wrapping_neg()", 835 ..unop.clone() 836 }, 837 NumericOp { 838 name: "neg", 839 body: r#"a.checked_neg().unwrap_or_else(|| panic!("negation overflow: {a}"))"#, 840 ..unop.clone() 841 }, 842 ]; 843 844 // Operations that apply only to unsigned numbers. 845 let unsigned_ops = [NumericOp { 846 name: "is_power_of_two", 847 body: "a.is_power_of_two()", 848 ..predicate.clone() 849 }]; 850 851 struct IterIf<I> { 852 condition: bool, 853 iter: I, 854 } 855 856 impl<I: Iterator> Iterator for IterIf<I> { 857 type Item = I::Item; 858 859 fn next(&mut self) -> Option<Self::Item> { 860 if self.condition { 861 self.iter.next() 862 } else { 863 None 864 } 865 } 866 } 867 868 ops.into_iter() 869 .chain(IterIf { 870 condition: ty.signed, 871 iter: signed_ops.into_iter(), 872 }) 873 .chain(IterIf { 874 condition: !ty.signed, 875 iter: unsigned_ops.into_iter(), 876 }) 877 } 878 } 879 880 fn gen_numerics_isle(isle: &mut Formatter, rust: &mut Formatter) { 881 fmtln!(rust, "#[macro_export]"); 882 fmtln!(rust, "#[doc(hidden)]"); 883 fmtln!(rust, "macro_rules! isle_numerics_methods {{"); 884 rust.indent_push(); 885 fmtln!(rust, "() => {{"); 886 rust.indent_push(); 887 888 for ty in NumericType::all() { 889 for op in NumericOp::ops_for_type(&ty) { 890 let ty = ty.name(); 891 let op_name = format!("{ty}_{}", op.name); 892 let partial = if op.partial { " partial" } else { "" }; 893 let ret = op.ret; 894 fmtln!(isle, "(decl pure{partial} {op_name} ("); 895 isle.indent(|isle| { 896 for (_arg_name, arg_ty) in op.args.iter() { 897 fmtln!(isle, "{arg_ty}"); 898 } 899 }); 900 fmtln!(isle, ") {ret})"); 901 fmtln!(isle, "(extern constructor {op_name} {op_name})"); 902 903 let ret = if op.partial { 904 Cow::from(format!("Option<{ret}>")) 905 } else { 906 Cow::from(ret) 907 }; 908 let body = op.body; 909 fmtln!(rust, "#[inline]"); 910 fmtln!(rust, "fn {op_name}("); 911 rust.indent(|rust| { 912 fmtln!(rust, "&mut self,"); 913 for (arg_name, arg_ty) in op.args.iter() { 914 fmtln!(rust, "{arg_name}: {arg_ty},"); 915 } 916 }); 917 fmtln!(rust, ") -> {ret} {{"); 918 rust.indent(|rust| { 919 fmtln!(rust, "{body}"); 920 }); 921 fmtln!(rust, "}}"); 922 923 // When generating extractors for a `{ty}_is_foo` predicate, 924 // we generate the following: 925 // 926 // * bool <- ty etor: `{ty}_matches_foo` 927 // * ty <- ty etor: `{ty}_extract_foo` 928 // * () <- ty etor: `{ty}_when_foo` 929 // * () <- ty etor: `{ty}_when_not_foo` 930 // 931 // The last three are defined as local extractors that are 932 // implemented in terms of the first. This gives the ISLE compiler 933 // visibility into the extractors' overlapping-ness. 934 if op.etors { 935 debug_assert_eq!(op.args.len(), 1); 936 debug_assert_eq!(op.args[0].1, ty); 937 debug_assert_eq!(op.ret, "bool"); 938 debug_assert!(op.name.starts_with("is_")); 939 940 // Cut of the `is_` prefix. 941 let base_name = &op.name[3..]; 942 debug_assert!(base_name.len() > 0); 943 944 fmtln!(isle, "(decl pure {ty}_matches_{base_name} (bool) {ty})"); 945 fmtln!( 946 isle, 947 "(extern extractor {ty}_matches_{base_name} {ty}_matches_{base_name})" 948 ); 949 fmtln!(rust, "#[inline]"); 950 fmtln!( 951 rust, 952 "fn {ty}_matches_{base_name}(&mut self, a: {ty}) -> Option<bool> {{" 953 ); 954 rust.indent(|rust| { 955 fmtln!(rust, "Some({body})"); 956 }); 957 fmtln!(rust, "}}"); 958 959 fmtln!(isle, "(decl pure {ty}_extract_{base_name} ({ty}) {ty})"); 960 fmtln!( 961 isle, 962 "(extractor ({ty}_extract_{base_name} x) (and ({ty}_matches_{base_name} true) x))" 963 ); 964 965 fmtln!(isle, "(decl pure {ty}_when_{base_name} () {ty})"); 966 fmtln!( 967 isle, 968 "(extractor ({ty}_when_{base_name}) ({ty}_matches_{base_name} true))" 969 ); 970 971 fmtln!(isle, "(decl pure {ty}_when_not_{base_name} () {ty})"); 972 fmtln!( 973 isle, 974 "(extractor ({ty}_when_not_{base_name}) ({ty}_matches_{base_name} false))" 975 ); 976 } 977 978 isle.empty_line(); 979 rust.empty_line(); 980 } 981 } 982 983 // Numeric type conversions. 984 // 985 // Naming and conventions: 986 // 987 // * Constructors: 988 // * "<from>_into_<to>" for lossless, infallible conversion 989 // * "<from>_try_into_<to>" for lossless, fallible conversions (exposed as 990 // partial constructors) 991 // * "<from>_unwrap_into_<to>" for lossless, fallible conversions that will 992 // panic at runtime if the conversion would be lossy 993 // * "<from>_truncate_into_<to>" for lossy, infallible conversions that 994 // ignore upper bits 995 // * "<from>_cast_[un]signed" for signed-to-unsigned (and vice versa) 996 // reinterpretation 997 // * Extractors: 998 // * "<to>_from_<from>" for both fallible and infallible extractors 999 // * No unwrapping extractors 1000 // * No truncating extractors 1001 // * No signed-to-unsigned reinterpreting extractors 1002 for from in NumericType::all() { 1003 for to in NumericType::all() { 1004 if from == to { 1005 continue; 1006 } 1007 1008 let from_name = from.name(); 1009 let to_name = to.name(); 1010 1011 let lossy = match (from.byte_width.cmp(&to.byte_width), from.signed, to.signed) { 1012 // Widening with the same signedness is lossless. 1013 (Ordering::Less, true, true) | (Ordering::Less, false, false) => false, 1014 // Widening from unsigned to signed is lossless. 1015 (Ordering::Less, false, true) => false, 1016 // Widening from signed to unsigned is lossy. 1017 (Ordering::Less, true, false) => true, 1018 // Same width means we must be changing sign, since we skip 1019 // `from == to`, and this is lossy. 1020 (Ordering::Equal, _, _) => { 1021 debug_assert_ne!(from.signed, to.signed); 1022 true 1023 } 1024 // Narrowing is always lossy. 1025 (Ordering::Greater, _, _) => true, 1026 }; 1027 1028 let (ctor, partial, rust_ret) = if lossy { 1029 ( 1030 "try_into", 1031 " partial", 1032 Cow::from(format!("Option<{to_name}>")), 1033 ) 1034 } else { 1035 ("into", "", Cow::from(to_name)) 1036 }; 1037 1038 // Constructor. 1039 fmtln!( 1040 isle, 1041 "(decl pure{partial} {from_name}_{ctor}_{to_name} ({from_name}) {to_name})" 1042 ); 1043 fmtln!( 1044 isle, 1045 "(extern constructor {from_name}_{ctor}_{to_name} {from_name}_{ctor}_{to_name})" 1046 ); 1047 if !lossy { 1048 fmtln!( 1049 isle, 1050 "(convert {from_name} {to_name} {from_name}_{ctor}_{to_name})" 1051 ); 1052 } 1053 fmtln!(rust, "#[inline]"); 1054 fmtln!( 1055 rust, 1056 "fn {from_name}_{ctor}_{to_name}(&mut self, x: {from_name}) -> {rust_ret} {{" 1057 ); 1058 rust.indent(|rust| { 1059 if lossy { 1060 fmtln!(rust, "{to_name}::try_from(x).ok()"); 1061 } else { 1062 fmtln!(rust, "{to_name}::from(x)"); 1063 } 1064 }); 1065 fmtln!(rust, "}}"); 1066 1067 // Unwrapping constructor. 1068 if lossy { 1069 fmtln!( 1070 isle, 1071 "(decl pure {from_name}_unwrap_into_{to_name} ({from_name}) {to_name})" 1072 ); 1073 fmtln!( 1074 isle, 1075 "(extern constructor {from_name}_unwrap_into_{to_name} {from_name}_unwrap_into_{to_name})" 1076 ); 1077 fmtln!(rust, "#[inline]"); 1078 fmtln!( 1079 rust, 1080 "fn {from_name}_unwrap_into_{to_name}(&mut self, x: {from_name}) -> {to_name} {{" 1081 ); 1082 rust.indent(|rust| { 1083 fmtln!(rust, "{to_name}::try_from(x).unwrap()"); 1084 }); 1085 fmtln!(rust, "}}"); 1086 } 1087 1088 // Truncating constructor. 1089 if lossy && from.signed == to.signed { 1090 fmtln!( 1091 isle, 1092 "(decl pure {from_name}_truncate_into_{to_name} ({from_name}) {to_name})" 1093 ); 1094 fmtln!( 1095 isle, 1096 "(extern constructor {from_name}_truncate_into_{to_name} {from_name}_truncate_into_{to_name})" 1097 ); 1098 fmtln!(rust, "#[inline]"); 1099 fmtln!( 1100 rust, 1101 "fn {from_name}_truncate_into_{to_name}(&mut self, x: {from_name}) -> {to_name} {{" 1102 ); 1103 rust.indent(|rust| { 1104 fmtln!(rust, "x as {to_name}"); 1105 }); 1106 fmtln!(rust, "}}"); 1107 } 1108 1109 // Signed-to-unsigned reinterpreting constructor. 1110 if from.byte_width == to.byte_width { 1111 debug_assert_ne!(from.signed, to.signed); 1112 let cast_name = if to.signed { 1113 "cast_signed" 1114 } else { 1115 "cast_unsigned" 1116 }; 1117 fmtln!( 1118 isle, 1119 "(decl pure {from_name}_{cast_name} ({from_name}) {to_name})" 1120 ); 1121 fmtln!( 1122 isle, 1123 "(extern constructor {from_name}_{cast_name} {from_name}_{cast_name})" 1124 ); 1125 fmtln!(rust, "#[inline]"); 1126 fmtln!( 1127 rust, 1128 "fn {from_name}_{cast_name}(&mut self, x: {from_name}) -> {to_name} {{" 1129 ); 1130 rust.indent(|rust| { 1131 // TODO: Once our MSRV is >= 1.87, we should use 1132 // `x.cast_[un]signed()` here. 1133 fmtln!(rust, "x as {to_name}"); 1134 }); 1135 fmtln!(rust, "}}"); 1136 } 1137 1138 // Extractor. 1139 fmtln!( 1140 isle, 1141 "(decl pure {to_name}_from_{from_name} ({to_name}) {from_name})" 1142 ); 1143 fmtln!( 1144 isle, 1145 "(extern extractor {to_name}_from_{from_name} {from_name}_from_{to_name})" 1146 ); 1147 fmtln!(rust, "#[inline]"); 1148 fmtln!( 1149 rust, 1150 "fn {from_name}_from_{to_name}(&mut self, x: {from_name}) -> Option<{to_name}> {{" 1151 ); 1152 rust.indent(|rust| { 1153 if lossy { 1154 fmtln!(rust, "x.try_into().ok()"); 1155 } else { 1156 fmtln!(rust, "Some(x.into())"); 1157 } 1158 }); 1159 fmtln!(rust, "}}"); 1160 1161 isle.empty_line(); 1162 rust.empty_line(); 1163 } 1164 } 1165 1166 rust.indent_pop(); 1167 fmtln!(rust, "}}"); 1168 rust.indent_pop(); 1169 fmtln!(rust, "}}"); 1170 } 1171 1172 pub(crate) fn generate( 1173 formats: &[Rc<InstructionFormat>], 1174 all_inst: &AllInstructions, 1175 isle_numerics_filename: &str, 1176 rust_numerics_filename: &str, 1177 isle_opt_filename: &str, 1178 isle_lower_filename: &str, 1179 isle_dir: &std::path::Path, 1180 ) -> Result<(), error::Error> { 1181 // Numerics 1182 let mut isle_fmt = Formatter::new(Language::Isle); 1183 let mut rust_fmt = Formatter::new(Language::Rust); 1184 gen_numerics_isle(&mut isle_fmt, &mut rust_fmt); 1185 isle_fmt.write(isle_numerics_filename, isle_dir)?; 1186 rust_fmt.write(rust_numerics_filename, isle_dir)?; 1187 1188 // ISLE DSL: mid-end ("opt") generated bindings. 1189 let mut fmt = Formatter::new(Language::Isle); 1190 gen_opt_isle(&formats, all_inst, &mut fmt); 1191 fmt.write(isle_opt_filename, isle_dir)?; 1192 1193 // ISLE DSL: lowering generated bindings. 1194 let mut fmt = Formatter::new(Language::Isle); 1195 gen_lower_isle(&formats, all_inst, &mut fmt); 1196 fmt.write(isle_lower_filename, isle_dir)?; 1197 1198 Ok(()) 1199 } 1200