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