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