1 //! Instruction formats and opcodes. 2 //! 3 //! The `instructions` module contains definitions for instruction formats, opcodes, and the 4 //! in-memory representation of IR instructions. 5 //! 6 //! A large part of this module is auto-generated from the instruction descriptions in the meta 7 //! directory. 8 9 use alloc::vec::Vec; 10 use core::fmt::{self, Display, Formatter}; 11 use core::ops::{Deref, DerefMut}; 12 use core::str::FromStr; 13 14 use crate::ir::{self, trapcode::TrapCode, types, Block, FuncRef, JumpTable, SigRef, Type, Value}; 15 use crate::isa; 16 17 use crate::bitset::BitSet; 18 use crate::entity; 19 20 /// Some instructions use an external list of argument values because there is not enough space in 21 /// the 16-byte `InstructionData` struct. These value lists are stored in a memory pool in 22 /// `dfg.value_lists`. 23 pub type ValueList = entity::EntityList<Value>; 24 25 /// Memory pool for holding value lists. See `ValueList`. 26 pub type ValueListPool = entity::ListPool<Value>; 27 28 // Include code generated by `cranelift-codegen/meta/src/gen_inst.rs`. This file contains: 29 // 30 // - The `pub enum InstructionFormat` enum with all the instruction formats. 31 // - The `pub enum InstructionData` enum with all the instruction data fields. 32 // - The `pub enum Opcode` definition with all known opcodes, 33 // - The `const OPCODE_FORMAT: [InstructionFormat; N]` table. 34 // - The private `fn opcode_name(Opcode) -> &'static str` function, and 35 // - The hash table `const OPCODE_HASH_TABLE: [Opcode; N]`. 36 // 37 // For value type constraints: 38 // 39 // - The `const OPCODE_CONSTRAINTS : [OpcodeConstraints; N]` table. 40 // - The `const TYPE_SETS : [ValueTypeSet; N]` table. 41 // - The `const OPERAND_CONSTRAINTS : [OperandConstraint; N]` table. 42 // 43 include!(concat!(env!("OUT_DIR"), "/opcodes.rs")); 44 45 impl Display for Opcode { fmt(&self, f: &mut Formatter) -> fmt::Result46 fn fmt(&self, f: &mut Formatter) -> fmt::Result { 47 write!(f, "{}", opcode_name(*self)) 48 } 49 } 50 51 impl Opcode { 52 /// Get the instruction format for this opcode. format(self) -> InstructionFormat53 pub fn format(self) -> InstructionFormat { 54 OPCODE_FORMAT[self as usize - 1] 55 } 56 57 /// Get the constraint descriptor for this opcode. 58 /// Panic if this is called on `NotAnOpcode`. constraints(self) -> OpcodeConstraints59 pub fn constraints(self) -> OpcodeConstraints { 60 OPCODE_CONSTRAINTS[self as usize - 1] 61 } 62 } 63 64 // This trait really belongs in cranelift-reader where it is used by the `.clif` file parser, but since 65 // it critically depends on the `opcode_name()` function which is needed here anyway, it lives in 66 // this module. This also saves us from running the build script twice to generate code for the two 67 // separate crates. 68 impl FromStr for Opcode { 69 type Err = &'static str; 70 71 /// Parse an Opcode name from a string. from_str(s: &str) -> Result<Self, &'static str>72 fn from_str(s: &str) -> Result<Self, &'static str> { 73 use crate::constant_hash::{probe, simple_hash, Table}; 74 75 impl<'a> Table<&'a str> for [Option<Opcode>] { 76 fn len(&self) -> usize { 77 self.len() 78 } 79 80 fn key(&self, idx: usize) -> Option<&'a str> { 81 self[idx].map(opcode_name) 82 } 83 } 84 85 match probe::<&str, [Option<Self>]>(&OPCODE_HASH_TABLE, s, simple_hash(s)) { 86 Err(_) => Err("Unknown opcode"), 87 // We unwrap here because probe() should have ensured that the entry 88 // at this index is not None. 89 Ok(i) => Ok(OPCODE_HASH_TABLE[i].unwrap()), 90 } 91 } 92 } 93 94 /// A variable list of `Value` operands used for function call arguments and passing arguments to 95 /// basic blocks. 96 #[derive(Clone, Debug)] 97 pub struct VariableArgs(Vec<Value>); 98 99 impl VariableArgs { 100 /// Create an empty argument list. new() -> Self101 pub fn new() -> Self { 102 Self(Vec::new()) 103 } 104 105 /// Add an argument to the end. push(&mut self, v: Value)106 pub fn push(&mut self, v: Value) { 107 self.0.push(v) 108 } 109 110 /// Check if the list is empty. is_empty(&self) -> bool111 pub fn is_empty(&self) -> bool { 112 self.0.is_empty() 113 } 114 115 /// Convert this to a value list in `pool` with `fixed` prepended. into_value_list(self, fixed: &[Value], pool: &mut ValueListPool) -> ValueList116 pub fn into_value_list(self, fixed: &[Value], pool: &mut ValueListPool) -> ValueList { 117 let mut vlist = ValueList::default(); 118 vlist.extend(fixed.iter().cloned(), pool); 119 vlist.extend(self.0, pool); 120 vlist 121 } 122 } 123 124 // Coerce `VariableArgs` into a `&[Value]` slice. 125 impl Deref for VariableArgs { 126 type Target = [Value]; 127 deref(&self) -> &[Value]128 fn deref(&self) -> &[Value] { 129 &self.0 130 } 131 } 132 133 impl DerefMut for VariableArgs { deref_mut(&mut self) -> &mut [Value]134 fn deref_mut(&mut self) -> &mut [Value] { 135 &mut self.0 136 } 137 } 138 139 impl Display for VariableArgs { fmt(&self, fmt: &mut Formatter) -> fmt::Result140 fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { 141 for (i, val) in self.0.iter().enumerate() { 142 if i == 0 { 143 write!(fmt, "{}", val)?; 144 } else { 145 write!(fmt, ", {}", val)?; 146 } 147 } 148 Ok(()) 149 } 150 } 151 152 impl Default for VariableArgs { default() -> Self153 fn default() -> Self { 154 Self::new() 155 } 156 } 157 158 /// Analyzing an instruction. 159 /// 160 /// Avoid large matches on instruction formats by using the methods defined here to examine 161 /// instructions. 162 impl InstructionData { 163 /// Return information about the destination of a branch or jump instruction. 164 /// 165 /// Any instruction that can transfer control to another block reveals its possible destinations 166 /// here. analyze_branch<'a>(&'a self, pool: &'a ValueListPool) -> BranchInfo<'a>167 pub fn analyze_branch<'a>(&'a self, pool: &'a ValueListPool) -> BranchInfo<'a> { 168 match *self { 169 Self::Jump { 170 destination, 171 ref args, 172 .. 173 } => BranchInfo::SingleDest(destination, args.as_slice(pool)), 174 Self::BranchInt { 175 destination, 176 ref args, 177 .. 178 } 179 | Self::BranchFloat { 180 destination, 181 ref args, 182 .. 183 } 184 | Self::Branch { 185 destination, 186 ref args, 187 .. 188 } => BranchInfo::SingleDest(destination, &args.as_slice(pool)[1..]), 189 Self::BranchIcmp { 190 destination, 191 ref args, 192 .. 193 } => BranchInfo::SingleDest(destination, &args.as_slice(pool)[2..]), 194 Self::BranchTable { 195 table, destination, .. 196 } => BranchInfo::Table(table, Some(destination)), 197 Self::IndirectJump { table, .. } => BranchInfo::Table(table, None), 198 _ => { 199 debug_assert!(!self.opcode().is_branch()); 200 BranchInfo::NotABranch 201 } 202 } 203 } 204 205 /// Get the single destination of this branch instruction, if it is a single destination 206 /// branch or jump. 207 /// 208 /// Multi-destination branches like `br_table` return `None`. branch_destination(&self) -> Option<Block>209 pub fn branch_destination(&self) -> Option<Block> { 210 match *self { 211 Self::Jump { destination, .. } 212 | Self::Branch { destination, .. } 213 | Self::BranchInt { destination, .. } 214 | Self::BranchFloat { destination, .. } 215 | Self::BranchIcmp { destination, .. } => Some(destination), 216 Self::BranchTable { .. } | Self::IndirectJump { .. } => None, 217 _ => { 218 debug_assert!(!self.opcode().is_branch()); 219 None 220 } 221 } 222 } 223 224 /// Get a mutable reference to the single destination of this branch instruction, if it is a 225 /// single destination branch or jump. 226 /// 227 /// Multi-destination branches like `br_table` return `None`. branch_destination_mut(&mut self) -> Option<&mut Block>228 pub fn branch_destination_mut(&mut self) -> Option<&mut Block> { 229 match *self { 230 Self::Jump { 231 ref mut destination, 232 .. 233 } 234 | Self::Branch { 235 ref mut destination, 236 .. 237 } 238 | Self::BranchInt { 239 ref mut destination, 240 .. 241 } 242 | Self::BranchFloat { 243 ref mut destination, 244 .. 245 } 246 | Self::BranchIcmp { 247 ref mut destination, 248 .. 249 } => Some(destination), 250 Self::BranchTable { .. } => None, 251 _ => { 252 debug_assert!(!self.opcode().is_branch()); 253 None 254 } 255 } 256 } 257 258 /// If this is a trapping instruction, get its trap code. Otherwise, return 259 /// `None`. trap_code(&self) -> Option<TrapCode>260 pub fn trap_code(&self) -> Option<TrapCode> { 261 match *self { 262 Self::CondTrap { code, .. } 263 | Self::FloatCondTrap { code, .. } 264 | Self::IntCondTrap { code, .. } 265 | Self::Trap { code, .. } => Some(code), 266 _ => None, 267 } 268 } 269 270 /// If this is a trapping instruction, get an exclusive reference to its 271 /// trap code. Otherwise, return `None`. trap_code_mut(&mut self) -> Option<&mut TrapCode>272 pub fn trap_code_mut(&mut self) -> Option<&mut TrapCode> { 273 match self { 274 Self::CondTrap { code, .. } 275 | Self::FloatCondTrap { code, .. } 276 | Self::IntCondTrap { code, .. } 277 | Self::Trap { code, .. } => Some(code), 278 _ => None, 279 } 280 } 281 282 /// Return information about a call instruction. 283 /// 284 /// Any instruction that can call another function reveals its call signature here. analyze_call<'a>(&'a self, pool: &'a ValueListPool) -> CallInfo<'a>285 pub fn analyze_call<'a>(&'a self, pool: &'a ValueListPool) -> CallInfo<'a> { 286 match *self { 287 Self::Call { 288 func_ref, ref args, .. 289 } => CallInfo::Direct(func_ref, args.as_slice(pool)), 290 Self::CallIndirect { 291 sig_ref, ref args, .. 292 } => CallInfo::Indirect(sig_ref, &args.as_slice(pool)[1..]), 293 _ => { 294 debug_assert!(!self.opcode().is_call()); 295 CallInfo::NotACall 296 } 297 } 298 } 299 300 #[inline] sign_extend_immediates(&mut self, ctrl_typevar: Type)301 pub(crate) fn sign_extend_immediates(&mut self, ctrl_typevar: Type) { 302 if ctrl_typevar.is_invalid() { 303 return; 304 } 305 306 let bit_width = ctrl_typevar.bits(); 307 308 match self { 309 Self::BinaryImm64 { 310 opcode, 311 arg: _, 312 imm, 313 } => { 314 if matches!(opcode, Opcode::SdivImm | Opcode::SremImm) { 315 imm.sign_extend_from_width(bit_width); 316 } 317 } 318 Self::IntCompareImm { 319 opcode, 320 arg: _, 321 cond, 322 imm, 323 } => { 324 debug_assert_eq!(*opcode, Opcode::IcmpImm); 325 if cond.unsigned() != *cond { 326 imm.sign_extend_from_width(bit_width); 327 } 328 } 329 _ => {} 330 } 331 } 332 } 333 334 /// Information about branch and jump instructions. 335 pub enum BranchInfo<'a> { 336 /// This is not a branch or jump instruction. 337 /// This instruction will not transfer control to another block in the function, but it may still 338 /// affect control flow by returning or trapping. 339 NotABranch, 340 341 /// This is a branch or jump to a single destination block, possibly taking value arguments. 342 SingleDest(Block, &'a [Value]), 343 344 /// This is a jump table branch which can have many destination blocks and maybe one default block. 345 Table(JumpTable, Option<Block>), 346 } 347 348 /// Information about call instructions. 349 pub enum CallInfo<'a> { 350 /// This is not a call instruction. 351 NotACall, 352 353 /// This is a direct call to an external function declared in the preamble. See 354 /// `DataFlowGraph.ext_funcs`. 355 Direct(FuncRef, &'a [Value]), 356 357 /// This is an indirect call with the specified signature. See `DataFlowGraph.signatures`. 358 Indirect(SigRef, &'a [Value]), 359 } 360 361 /// Value type constraints for a given opcode. 362 /// 363 /// The `InstructionFormat` determines the constraints on most operands, but `Value` operands and 364 /// results are not determined by the format. Every `Opcode` has an associated 365 /// `OpcodeConstraints` object that provides the missing details. 366 #[derive(Clone, Copy)] 367 pub struct OpcodeConstraints { 368 /// Flags for this opcode encoded as a bit field: 369 /// 370 /// Bits 0-2: 371 /// Number of fixed result values. This does not include `variable_args` results as are 372 /// produced by call instructions. 373 /// 374 /// Bit 3: 375 /// This opcode is polymorphic and the controlling type variable can be inferred from the 376 /// designated input operand. This is the `typevar_operand` index given to the 377 /// `InstructionFormat` meta language object. When this bit is not set, the controlling 378 /// type variable must be the first output value instead. 379 /// 380 /// Bit 4: 381 /// This opcode is polymorphic and the controlling type variable does *not* appear as the 382 /// first result type. 383 /// 384 /// Bits 5-7: 385 /// Number of fixed value arguments. The minimum required number of value operands. 386 flags: u8, 387 388 /// Permitted set of types for the controlling type variable as an index into `TYPE_SETS`. 389 typeset_offset: u8, 390 391 /// Offset into `OPERAND_CONSTRAINT` table of the descriptors for this opcode. The first 392 /// `num_fixed_results()` entries describe the result constraints, then follows constraints for 393 /// the fixed `Value` input operands. (`num_fixed_value_arguments()` of them). 394 constraint_offset: u16, 395 } 396 397 impl OpcodeConstraints { 398 /// Can the controlling type variable for this opcode be inferred from the designated value 399 /// input operand? 400 /// This also implies that this opcode is polymorphic. use_typevar_operand(self) -> bool401 pub fn use_typevar_operand(self) -> bool { 402 (self.flags & 0x8) != 0 403 } 404 405 /// Is it necessary to look at the designated value input operand in order to determine the 406 /// controlling type variable, or is it good enough to use the first return type? 407 /// 408 /// Most polymorphic instructions produce a single result with the type of the controlling type 409 /// variable. A few polymorphic instructions either don't produce any results, or produce 410 /// results with a fixed type. These instructions return `true`. requires_typevar_operand(self) -> bool411 pub fn requires_typevar_operand(self) -> bool { 412 (self.flags & 0x10) != 0 413 } 414 415 /// Get the number of *fixed* result values produced by this opcode. 416 /// This does not include `variable_args` produced by calls. num_fixed_results(self) -> usize417 pub fn num_fixed_results(self) -> usize { 418 (self.flags & 0x7) as usize 419 } 420 421 /// Get the number of *fixed* input values required by this opcode. 422 /// 423 /// This does not include `variable_args` arguments on call and branch instructions. 424 /// 425 /// The number of fixed input values is usually implied by the instruction format, but 426 /// instruction formats that use a `ValueList` put both fixed and variable arguments in the 427 /// list. This method returns the *minimum* number of values required in the value list. num_fixed_value_arguments(self) -> usize428 pub fn num_fixed_value_arguments(self) -> usize { 429 ((self.flags >> 5) & 0x7) as usize 430 } 431 432 /// Get the offset into `TYPE_SETS` for the controlling type variable. 433 /// Returns `None` if the instruction is not polymorphic. typeset_offset(self) -> Option<usize>434 fn typeset_offset(self) -> Option<usize> { 435 let offset = usize::from(self.typeset_offset); 436 if offset < TYPE_SETS.len() { 437 Some(offset) 438 } else { 439 None 440 } 441 } 442 443 /// Get the offset into OPERAND_CONSTRAINTS where the descriptors for this opcode begin. constraint_offset(self) -> usize444 fn constraint_offset(self) -> usize { 445 self.constraint_offset as usize 446 } 447 448 /// Get the value type of result number `n`, having resolved the controlling type variable to 449 /// `ctrl_type`. result_type(self, n: usize, ctrl_type: Type) -> Type450 pub fn result_type(self, n: usize, ctrl_type: Type) -> Type { 451 debug_assert!(n < self.num_fixed_results(), "Invalid result index"); 452 if let ResolvedConstraint::Bound(t) = 453 OPERAND_CONSTRAINTS[self.constraint_offset() + n].resolve(ctrl_type) 454 { 455 t 456 } else { 457 panic!("Result constraints can't be free"); 458 } 459 } 460 461 /// Get the value type of input value number `n`, having resolved the controlling type variable 462 /// to `ctrl_type`. 463 /// 464 /// Unlike results, it is possible for some input values to vary freely within a specific 465 /// `ValueTypeSet`. This is represented with the `ArgumentConstraint::Free` variant. value_argument_constraint(self, n: usize, ctrl_type: Type) -> ResolvedConstraint466 pub fn value_argument_constraint(self, n: usize, ctrl_type: Type) -> ResolvedConstraint { 467 debug_assert!( 468 n < self.num_fixed_value_arguments(), 469 "Invalid value argument index" 470 ); 471 let offset = self.constraint_offset() + self.num_fixed_results(); 472 OPERAND_CONSTRAINTS[offset + n].resolve(ctrl_type) 473 } 474 475 /// Get the typeset of allowed types for the controlling type variable in a polymorphic 476 /// instruction. ctrl_typeset(self) -> Option<ValueTypeSet>477 pub fn ctrl_typeset(self) -> Option<ValueTypeSet> { 478 self.typeset_offset().map(|offset| TYPE_SETS[offset]) 479 } 480 481 /// Is this instruction polymorphic? is_polymorphic(self) -> bool482 pub fn is_polymorphic(self) -> bool { 483 self.ctrl_typeset().is_some() 484 } 485 } 486 487 type BitSet8 = BitSet<u8>; 488 type BitSet16 = BitSet<u16>; 489 490 /// A value type set describes the permitted set of types for a type variable. 491 #[derive(Clone, Copy, Debug, PartialEq, Eq)] 492 pub struct ValueTypeSet { 493 /// Allowed lane sizes 494 pub lanes: BitSet16, 495 /// Allowed int widths 496 pub ints: BitSet8, 497 /// Allowed float widths 498 pub floats: BitSet8, 499 /// Allowed bool widths 500 pub bools: BitSet8, 501 /// Allowed ref widths 502 pub refs: BitSet8, 503 } 504 505 impl ValueTypeSet { 506 /// Is `scalar` part of the base type set? 507 /// 508 /// Note that the base type set does not have to be included in the type set proper. is_base_type(self, scalar: Type) -> bool509 fn is_base_type(self, scalar: Type) -> bool { 510 let l2b = scalar.log2_lane_bits(); 511 if scalar.is_int() { 512 self.ints.contains(l2b) 513 } else if scalar.is_float() { 514 self.floats.contains(l2b) 515 } else if scalar.is_bool() { 516 self.bools.contains(l2b) 517 } else if scalar.is_ref() { 518 self.refs.contains(l2b) 519 } else { 520 false 521 } 522 } 523 524 /// Does `typ` belong to this set? contains(self, typ: Type) -> bool525 pub fn contains(self, typ: Type) -> bool { 526 let l2l = typ.log2_lane_count(); 527 self.lanes.contains(l2l) && self.is_base_type(typ.lane_type()) 528 } 529 530 /// Get an example member of this type set. 531 /// 532 /// This is used for error messages to avoid suggesting invalid types. example(self) -> Type533 pub fn example(self) -> Type { 534 let t = if self.ints.max().unwrap_or(0) > 5 { 535 types::I32 536 } else if self.floats.max().unwrap_or(0) > 5 { 537 types::F32 538 } else if self.bools.max().unwrap_or(0) > 5 { 539 types::B32 540 } else { 541 types::B1 542 }; 543 t.by(1 << self.lanes.min().unwrap()).unwrap() 544 } 545 } 546 547 /// Operand constraints. This describes the value type constraints on a single `Value` operand. 548 enum OperandConstraint { 549 /// This operand has a concrete value type. 550 Concrete(Type), 551 552 /// This operand can vary freely within the given type set. 553 /// The type set is identified by its index into the TYPE_SETS constant table. 554 Free(u8), 555 556 /// This operand is the same type as the controlling type variable. 557 Same, 558 559 /// This operand is `ctrlType.lane_of()`. 560 LaneOf, 561 562 /// This operand is `ctrlType.as_bool()`. 563 AsBool, 564 565 /// This operand is `ctrlType.half_width()`. 566 HalfWidth, 567 568 /// This operand is `ctrlType.double_width()`. 569 DoubleWidth, 570 571 /// This operand is `ctrlType.half_vector()`. 572 HalfVector, 573 574 /// This operand is `ctrlType.double_vector()`. 575 DoubleVector, 576 577 /// This operand is `ctrlType.split_lanes()`. 578 SplitLanes, 579 } 580 581 impl OperandConstraint { 582 /// Resolve this operand constraint into a concrete value type, given the value of the 583 /// controlling type variable. resolve(&self, ctrl_type: Type) -> ResolvedConstraint584 pub fn resolve(&self, ctrl_type: Type) -> ResolvedConstraint { 585 use self::OperandConstraint::*; 586 use self::ResolvedConstraint::Bound; 587 match *self { 588 Concrete(t) => Bound(t), 589 Free(vts) => ResolvedConstraint::Free(TYPE_SETS[vts as usize]), 590 Same => Bound(ctrl_type), 591 LaneOf => Bound(ctrl_type.lane_of()), 592 AsBool => Bound(ctrl_type.as_bool()), 593 HalfWidth => Bound(ctrl_type.half_width().expect("invalid type for half_width")), 594 DoubleWidth => Bound( 595 ctrl_type 596 .double_width() 597 .expect("invalid type for double_width"), 598 ), 599 HalfVector => Bound( 600 ctrl_type 601 .half_vector() 602 .expect("invalid type for half_vector"), 603 ), 604 DoubleVector => Bound(ctrl_type.by(2).expect("invalid type for double_vector")), 605 SplitLanes => Bound( 606 ctrl_type 607 .split_lanes() 608 .expect("invalid type for split_lanes"), 609 ), 610 } 611 } 612 } 613 614 /// The type constraint on a value argument once the controlling type variable is known. 615 #[derive(Copy, Clone, Debug, PartialEq, Eq)] 616 pub enum ResolvedConstraint { 617 /// The operand is bound to a known type. 618 Bound(Type), 619 /// The operand type can vary freely within the given set. 620 Free(ValueTypeSet), 621 } 622 623 #[cfg(test)] 624 mod tests { 625 use super::*; 626 use alloc::string::ToString; 627 628 #[test] opcodes()629 fn opcodes() { 630 use core::mem; 631 632 let x = Opcode::Iadd; 633 let mut y = Opcode::Isub; 634 635 assert!(x != y); 636 y = Opcode::Iadd; 637 assert_eq!(x, y); 638 assert_eq!(x.format(), InstructionFormat::Binary); 639 640 assert_eq!(format!("{:?}", Opcode::IaddImm), "IaddImm"); 641 assert_eq!(Opcode::IaddImm.to_string(), "iadd_imm"); 642 643 // Check the matcher. 644 assert_eq!("iadd".parse::<Opcode>(), Ok(Opcode::Iadd)); 645 assert_eq!("iadd_imm".parse::<Opcode>(), Ok(Opcode::IaddImm)); 646 assert_eq!("iadd\0".parse::<Opcode>(), Err("Unknown opcode")); 647 assert_eq!("".parse::<Opcode>(), Err("Unknown opcode")); 648 assert_eq!("\0".parse::<Opcode>(), Err("Unknown opcode")); 649 650 // Opcode is a single byte, and because Option<Opcode> originally came to 2 bytes, early on 651 // Opcode included a variant NotAnOpcode to avoid the unnecessary bloat. Since then the Rust 652 // compiler has brought in NonZero optimization, meaning that an enum not using the 0 value 653 // can be optional for no size cost. We want to ensure Option<Opcode> remains small. 654 assert_eq!(mem::size_of::<Opcode>(), mem::size_of::<Option<Opcode>>()); 655 } 656 657 #[test] instruction_data()658 fn instruction_data() { 659 use core::mem; 660 // The size of the `InstructionData` enum is important for performance. It should not 661 // exceed 16 bytes. Use `Box<FooData>` out-of-line payloads for instruction formats that 662 // require more space than that. It would be fine with a data structure smaller than 16 663 // bytes, but what are the odds of that? 664 assert_eq!(mem::size_of::<InstructionData>(), 16); 665 } 666 667 #[test] constraints()668 fn constraints() { 669 let a = Opcode::Iadd.constraints(); 670 assert!(a.use_typevar_operand()); 671 assert!(!a.requires_typevar_operand()); 672 assert_eq!(a.num_fixed_results(), 1); 673 assert_eq!(a.num_fixed_value_arguments(), 2); 674 assert_eq!(a.result_type(0, types::I32), types::I32); 675 assert_eq!(a.result_type(0, types::I8), types::I8); 676 assert_eq!( 677 a.value_argument_constraint(0, types::I32), 678 ResolvedConstraint::Bound(types::I32) 679 ); 680 assert_eq!( 681 a.value_argument_constraint(1, types::I32), 682 ResolvedConstraint::Bound(types::I32) 683 ); 684 685 let b = Opcode::Bitcast.constraints(); 686 assert!(!b.use_typevar_operand()); 687 assert!(!b.requires_typevar_operand()); 688 assert_eq!(b.num_fixed_results(), 1); 689 assert_eq!(b.num_fixed_value_arguments(), 1); 690 assert_eq!(b.result_type(0, types::I32), types::I32); 691 assert_eq!(b.result_type(0, types::I8), types::I8); 692 match b.value_argument_constraint(0, types::I32) { 693 ResolvedConstraint::Free(vts) => assert!(vts.contains(types::F32)), 694 _ => panic!("Unexpected constraint from value_argument_constraint"), 695 } 696 697 let c = Opcode::Call.constraints(); 698 assert_eq!(c.num_fixed_results(), 0); 699 assert_eq!(c.num_fixed_value_arguments(), 0); 700 701 let i = Opcode::CallIndirect.constraints(); 702 assert_eq!(i.num_fixed_results(), 0); 703 assert_eq!(i.num_fixed_value_arguments(), 1); 704 705 let cmp = Opcode::Icmp.constraints(); 706 assert!(cmp.use_typevar_operand()); 707 assert!(cmp.requires_typevar_operand()); 708 assert_eq!(cmp.num_fixed_results(), 1); 709 assert_eq!(cmp.num_fixed_value_arguments(), 2); 710 } 711 712 #[test] value_set()713 fn value_set() { 714 use crate::ir::types::*; 715 716 let vts = ValueTypeSet { 717 lanes: BitSet16::from_range(0, 8), 718 ints: BitSet8::from_range(4, 7), 719 floats: BitSet8::from_range(0, 0), 720 bools: BitSet8::from_range(3, 7), 721 refs: BitSet8::from_range(5, 7), 722 }; 723 assert!(!vts.contains(I8)); 724 assert!(vts.contains(I32)); 725 assert!(vts.contains(I64)); 726 assert!(vts.contains(I32X4)); 727 assert!(!vts.contains(F32)); 728 assert!(!vts.contains(B1)); 729 assert!(vts.contains(B8)); 730 assert!(vts.contains(B64)); 731 assert!(vts.contains(R32)); 732 assert!(vts.contains(R64)); 733 assert_eq!(vts.example().to_string(), "i32"); 734 735 let vts = ValueTypeSet { 736 lanes: BitSet16::from_range(0, 8), 737 ints: BitSet8::from_range(0, 0), 738 floats: BitSet8::from_range(5, 7), 739 bools: BitSet8::from_range(3, 7), 740 refs: BitSet8::from_range(0, 0), 741 }; 742 assert_eq!(vts.example().to_string(), "f32"); 743 744 let vts = ValueTypeSet { 745 lanes: BitSet16::from_range(1, 8), 746 ints: BitSet8::from_range(0, 0), 747 floats: BitSet8::from_range(5, 7), 748 bools: BitSet8::from_range(3, 7), 749 refs: BitSet8::from_range(0, 0), 750 }; 751 assert_eq!(vts.example().to_string(), "f32x2"); 752 753 let vts = ValueTypeSet { 754 lanes: BitSet16::from_range(2, 8), 755 ints: BitSet8::from_range(0, 0), 756 floats: BitSet8::from_range(0, 0), 757 bools: BitSet8::from_range(3, 7), 758 refs: BitSet8::from_range(0, 0), 759 }; 760 assert!(!vts.contains(B32X2)); 761 assert!(vts.contains(B32X4)); 762 assert_eq!(vts.example().to_string(), "b32x4"); 763 764 let vts = ValueTypeSet { 765 // TypeSet(lanes=(1, 256), ints=(8, 64)) 766 lanes: BitSet16::from_range(0, 9), 767 ints: BitSet8::from_range(3, 7), 768 floats: BitSet8::from_range(0, 0), 769 bools: BitSet8::from_range(0, 0), 770 refs: BitSet8::from_range(0, 0), 771 }; 772 assert!(vts.contains(I32)); 773 assert!(vts.contains(I32X4)); 774 assert!(!vts.contains(R32)); 775 assert!(!vts.contains(R64)); 776 } 777 } 778