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::LoadComplex { offset, .. } 308 | &InstructionData::Store { offset, .. } 309 | &InstructionData::StoreComplex { offset, .. } 310 | &InstructionData::StackLoad { offset, .. } 311 | &InstructionData::StackStore { offset, .. } 312 | &InstructionData::TableAddr { offset, .. } => Some(DataValue::from(offset)), 313 // 64-bit. 314 &InstructionData::UnaryImm { imm, .. } 315 | &InstructionData::BinaryImm64 { imm, .. } 316 | &InstructionData::IntCompareImm { imm, .. } => Some(DataValue::from(imm.bits())), 317 &InstructionData::UnaryIeee64 { imm, .. } => Some(DataValue::from(imm)), 318 // 128-bit; though these immediates are present logically in the IR they are not 319 // included in the `InstructionData` for memory-size reasons. This case, returning 320 // `None`, is left here to alert users of this method that they should retrieve the 321 // value using the `DataFlowGraph`. 322 &InstructionData::Shuffle { imm: _, .. } => None, 323 _ => None, 324 } 325 } 326 327 /// If this is a trapping instruction, get its trap code. Otherwise, return 328 /// `None`. 329 pub fn trap_code(&self) -> Option<TrapCode> { 330 match *self { 331 Self::CondTrap { code, .. } 332 | Self::FloatCondTrap { code, .. } 333 | Self::IntCondTrap { code, .. } 334 | Self::Trap { code, .. } => Some(code), 335 _ => None, 336 } 337 } 338 339 /// If this is a control-flow instruction depending on an integer condition, gets its 340 /// condition. Otherwise, return `None`. 341 pub fn cond_code(&self) -> Option<IntCC> { 342 match self { 343 &InstructionData::IntCond { cond, .. } 344 | &InstructionData::BranchIcmp { cond, .. } 345 | &InstructionData::IntCompare { cond, .. } 346 | &InstructionData::IntCondTrap { cond, .. } 347 | &InstructionData::BranchInt { cond, .. } 348 | &InstructionData::IntSelect { cond, .. } 349 | &InstructionData::IntCompareImm { cond, .. } => Some(cond), 350 _ => None, 351 } 352 } 353 354 /// If this is a control-flow instruction depending on a floating-point condition, gets its 355 /// condition. Otherwise, return `None`. 356 pub fn fp_cond_code(&self) -> Option<FloatCC> { 357 match self { 358 &InstructionData::BranchFloat { cond, .. } 359 | &InstructionData::FloatCompare { cond, .. } 360 | &InstructionData::FloatCond { cond, .. } 361 | &InstructionData::FloatCondTrap { cond, .. } => Some(cond), 362 _ => None, 363 } 364 } 365 366 /// If this is a trapping instruction, get an exclusive reference to its 367 /// trap code. Otherwise, return `None`. 368 pub fn trap_code_mut(&mut self) -> Option<&mut TrapCode> { 369 match self { 370 Self::CondTrap { code, .. } 371 | Self::FloatCondTrap { code, .. } 372 | Self::IntCondTrap { code, .. } 373 | Self::Trap { code, .. } => Some(code), 374 _ => None, 375 } 376 } 377 378 /// If this is an atomic read/modify/write instruction, return its subopcode. 379 pub fn atomic_rmw_op(&self) -> Option<ir::AtomicRmwOp> { 380 match self { 381 &InstructionData::AtomicRmw { op, .. } => Some(op), 382 _ => None, 383 } 384 } 385 386 /// If this is a load/store instruction, returns its immediate offset. 387 pub fn load_store_offset(&self) -> Option<i32> { 388 match self { 389 &InstructionData::Load { offset, .. } 390 | &InstructionData::StackLoad { offset, .. } 391 | &InstructionData::LoadComplex { offset, .. } 392 | &InstructionData::Store { offset, .. } 393 | &InstructionData::StackStore { offset, .. } 394 | &InstructionData::StoreComplex { offset, .. } => Some(offset.into()), 395 _ => None, 396 } 397 } 398 399 /// If this is a load/store instruction, return its memory flags. 400 pub fn memflags(&self) -> Option<MemFlags> { 401 match self { 402 &InstructionData::Load { flags, .. } 403 | &InstructionData::LoadComplex { flags, .. } 404 | &InstructionData::LoadNoOffset { flags, .. } 405 | &InstructionData::Store { flags, .. } 406 | &InstructionData::StoreComplex { flags, .. } 407 | &InstructionData::StoreNoOffset { flags, .. } => Some(flags), 408 _ => None, 409 } 410 } 411 412 /// If this instruction references a stack slot, return it 413 pub fn stack_slot(&self) -> Option<StackSlot> { 414 match self { 415 &InstructionData::StackStore { stack_slot, .. } 416 | &InstructionData::StackLoad { stack_slot, .. } => Some(stack_slot), 417 _ => None, 418 } 419 } 420 421 /// Return information about a call instruction. 422 /// 423 /// Any instruction that can call another function reveals its call signature here. 424 pub fn analyze_call<'a>(&'a self, pool: &'a ValueListPool) -> CallInfo<'a> { 425 match *self { 426 Self::Call { 427 func_ref, ref args, .. 428 } => CallInfo::Direct(func_ref, args.as_slice(pool)), 429 Self::CallIndirect { 430 sig_ref, ref args, .. 431 } => CallInfo::Indirect(sig_ref, &args.as_slice(pool)[1..]), 432 _ => { 433 debug_assert!(!self.opcode().is_call()); 434 CallInfo::NotACall 435 } 436 } 437 } 438 439 #[inline] 440 pub(crate) fn sign_extend_immediates(&mut self, ctrl_typevar: Type) { 441 if ctrl_typevar.is_invalid() { 442 return; 443 } 444 445 let bit_width = ctrl_typevar.bits(); 446 447 match self { 448 Self::BinaryImm64 { 449 opcode, 450 arg: _, 451 imm, 452 } => { 453 if *opcode == Opcode::SdivImm || *opcode == Opcode::SremImm { 454 imm.sign_extend_from_width(bit_width); 455 } 456 } 457 Self::IntCompareImm { 458 opcode, 459 arg: _, 460 cond, 461 imm, 462 } => { 463 debug_assert_eq!(*opcode, Opcode::IcmpImm); 464 if cond.unsigned() != *cond { 465 imm.sign_extend_from_width(bit_width); 466 } 467 } 468 _ => {} 469 } 470 } 471 } 472 473 /// Information about branch and jump instructions. 474 pub enum BranchInfo<'a> { 475 /// This is not a branch or jump instruction. 476 /// This instruction will not transfer control to another block in the function, but it may still 477 /// affect control flow by returning or trapping. 478 NotABranch, 479 480 /// This is a branch or jump to a single destination block, possibly taking value arguments. 481 SingleDest(Block, &'a [Value]), 482 483 /// This is a jump table branch which can have many destination blocks and maybe one default block. 484 Table(JumpTable, Option<Block>), 485 } 486 487 /// Information about call instructions. 488 pub enum CallInfo<'a> { 489 /// This is not a call instruction. 490 NotACall, 491 492 /// This is a direct call to an external function declared in the preamble. See 493 /// `DataFlowGraph.ext_funcs`. 494 Direct(FuncRef, &'a [Value]), 495 496 /// This is an indirect call with the specified signature. See `DataFlowGraph.signatures`. 497 Indirect(SigRef, &'a [Value]), 498 } 499 500 /// Value type constraints for a given opcode. 501 /// 502 /// The `InstructionFormat` determines the constraints on most operands, but `Value` operands and 503 /// results are not determined by the format. Every `Opcode` has an associated 504 /// `OpcodeConstraints` object that provides the missing details. 505 #[derive(Clone, Copy)] 506 pub struct OpcodeConstraints { 507 /// Flags for this opcode encoded as a bit field: 508 /// 509 /// Bits 0-2: 510 /// Number of fixed result values. This does not include `variable_args` results as are 511 /// produced by call instructions. 512 /// 513 /// Bit 3: 514 /// This opcode is polymorphic and the controlling type variable can be inferred from the 515 /// designated input operand. This is the `typevar_operand` index given to the 516 /// `InstructionFormat` meta language object. When this bit is not set, the controlling 517 /// type variable must be the first output value instead. 518 /// 519 /// Bit 4: 520 /// This opcode is polymorphic and the controlling type variable does *not* appear as the 521 /// first result type. 522 /// 523 /// Bits 5-7: 524 /// Number of fixed value arguments. The minimum required number of value operands. 525 flags: u8, 526 527 /// Permitted set of types for the controlling type variable as an index into `TYPE_SETS`. 528 typeset_offset: u8, 529 530 /// Offset into `OPERAND_CONSTRAINT` table of the descriptors for this opcode. The first 531 /// `num_fixed_results()` entries describe the result constraints, then follows constraints for 532 /// the fixed `Value` input operands. (`num_fixed_value_arguments()` of them). 533 constraint_offset: u16, 534 } 535 536 impl OpcodeConstraints { 537 /// Can the controlling type variable for this opcode be inferred from the designated value 538 /// input operand? 539 /// This also implies that this opcode is polymorphic. 540 pub fn use_typevar_operand(self) -> bool { 541 (self.flags & 0x8) != 0 542 } 543 544 /// Is it necessary to look at the designated value input operand in order to determine the 545 /// controlling type variable, or is it good enough to use the first return type? 546 /// 547 /// Most polymorphic instructions produce a single result with the type of the controlling type 548 /// variable. A few polymorphic instructions either don't produce any results, or produce 549 /// results with a fixed type. These instructions return `true`. 550 pub fn requires_typevar_operand(self) -> bool { 551 (self.flags & 0x10) != 0 552 } 553 554 /// Get the number of *fixed* result values produced by this opcode. 555 /// This does not include `variable_args` produced by calls. 556 pub fn num_fixed_results(self) -> usize { 557 (self.flags & 0x7) as usize 558 } 559 560 /// Get the number of *fixed* input values required by this opcode. 561 /// 562 /// This does not include `variable_args` arguments on call and branch instructions. 563 /// 564 /// The number of fixed input values is usually implied by the instruction format, but 565 /// instruction formats that use a `ValueList` put both fixed and variable arguments in the 566 /// list. This method returns the *minimum* number of values required in the value list. 567 pub fn num_fixed_value_arguments(self) -> usize { 568 ((self.flags >> 5) & 0x7) as usize 569 } 570 571 /// Get the offset into `TYPE_SETS` for the controlling type variable. 572 /// Returns `None` if the instruction is not polymorphic. 573 fn typeset_offset(self) -> Option<usize> { 574 let offset = usize::from(self.typeset_offset); 575 if offset < TYPE_SETS.len() { 576 Some(offset) 577 } else { 578 None 579 } 580 } 581 582 /// Get the offset into OPERAND_CONSTRAINTS where the descriptors for this opcode begin. 583 fn constraint_offset(self) -> usize { 584 self.constraint_offset as usize 585 } 586 587 /// Get the value type of result number `n`, having resolved the controlling type variable to 588 /// `ctrl_type`. 589 pub fn result_type(self, n: usize, ctrl_type: Type) -> Type { 590 debug_assert!(n < self.num_fixed_results(), "Invalid result index"); 591 if let ResolvedConstraint::Bound(t) = 592 OPERAND_CONSTRAINTS[self.constraint_offset() + n].resolve(ctrl_type) 593 { 594 t 595 } else { 596 panic!("Result constraints can't be free"); 597 } 598 } 599 600 /// Get the value type of input value number `n`, having resolved the controlling type variable 601 /// to `ctrl_type`. 602 /// 603 /// Unlike results, it is possible for some input values to vary freely within a specific 604 /// `ValueTypeSet`. This is represented with the `ArgumentConstraint::Free` variant. 605 pub fn value_argument_constraint(self, n: usize, ctrl_type: Type) -> ResolvedConstraint { 606 debug_assert!( 607 n < self.num_fixed_value_arguments(), 608 "Invalid value argument index" 609 ); 610 let offset = self.constraint_offset() + self.num_fixed_results(); 611 OPERAND_CONSTRAINTS[offset + n].resolve(ctrl_type) 612 } 613 614 /// Get the typeset of allowed types for the controlling type variable in a polymorphic 615 /// instruction. 616 pub fn ctrl_typeset(self) -> Option<ValueTypeSet> { 617 self.typeset_offset().map(|offset| TYPE_SETS[offset]) 618 } 619 620 /// Is this instruction polymorphic? 621 pub fn is_polymorphic(self) -> bool { 622 self.ctrl_typeset().is_some() 623 } 624 } 625 626 type BitSet8 = BitSet<u8>; 627 type BitSet16 = BitSet<u16>; 628 629 /// A value type set describes the permitted set of types for a type variable. 630 #[derive(Clone, Copy, Debug, PartialEq, Eq)] 631 pub struct ValueTypeSet { 632 /// Allowed lane sizes 633 pub lanes: BitSet16, 634 /// Allowed int widths 635 pub ints: BitSet8, 636 /// Allowed float widths 637 pub floats: BitSet8, 638 /// Allowed bool widths 639 pub bools: BitSet8, 640 /// Allowed ref widths 641 pub refs: BitSet8, 642 } 643 644 impl ValueTypeSet { 645 /// Is `scalar` part of the base type set? 646 /// 647 /// Note that the base type set does not have to be included in the type set proper. 648 fn is_base_type(self, scalar: Type) -> bool { 649 let l2b = scalar.log2_lane_bits(); 650 if scalar.is_int() { 651 self.ints.contains(l2b) 652 } else if scalar.is_float() { 653 self.floats.contains(l2b) 654 } else if scalar.is_bool() { 655 self.bools.contains(l2b) 656 } else if scalar.is_ref() { 657 self.refs.contains(l2b) 658 } else { 659 false 660 } 661 } 662 663 /// Does `typ` belong to this set? 664 pub fn contains(self, typ: Type) -> bool { 665 let l2l = typ.log2_lane_count(); 666 self.lanes.contains(l2l) && self.is_base_type(typ.lane_type()) 667 } 668 669 /// Get an example member of this type set. 670 /// 671 /// This is used for error messages to avoid suggesting invalid types. 672 pub fn example(self) -> Type { 673 let t = if self.ints.max().unwrap_or(0) > 5 { 674 types::I32 675 } else if self.floats.max().unwrap_or(0) > 5 { 676 types::F32 677 } else if self.bools.max().unwrap_or(0) > 5 { 678 types::B32 679 } else { 680 types::B1 681 }; 682 t.by(1 << self.lanes.min().unwrap()).unwrap() 683 } 684 } 685 686 /// Operand constraints. This describes the value type constraints on a single `Value` operand. 687 enum OperandConstraint { 688 /// This operand has a concrete value type. 689 Concrete(Type), 690 691 /// This operand can vary freely within the given type set. 692 /// The type set is identified by its index into the TYPE_SETS constant table. 693 Free(u8), 694 695 /// This operand is the same type as the controlling type variable. 696 Same, 697 698 /// This operand is `ctrlType.lane_of()`. 699 LaneOf, 700 701 /// This operand is `ctrlType.as_bool()`. 702 AsBool, 703 704 /// This operand is `ctrlType.half_width()`. 705 HalfWidth, 706 707 /// This operand is `ctrlType.double_width()`. 708 DoubleWidth, 709 710 /// This operand is `ctrlType.half_vector()`. 711 HalfVector, 712 713 /// This operand is `ctrlType.double_vector()`. 714 DoubleVector, 715 716 /// This operand is `ctrlType.split_lanes()`. 717 SplitLanes, 718 719 /// This operand is `ctrlType.merge_lanes()`. 720 MergeLanes, 721 } 722 723 impl OperandConstraint { 724 /// Resolve this operand constraint into a concrete value type, given the value of the 725 /// controlling type variable. 726 pub fn resolve(&self, ctrl_type: Type) -> ResolvedConstraint { 727 use self::OperandConstraint::*; 728 use self::ResolvedConstraint::Bound; 729 match *self { 730 Concrete(t) => Bound(t), 731 Free(vts) => ResolvedConstraint::Free(TYPE_SETS[vts as usize]), 732 Same => Bound(ctrl_type), 733 LaneOf => Bound(ctrl_type.lane_of()), 734 AsBool => Bound(ctrl_type.as_bool()), 735 HalfWidth => Bound(ctrl_type.half_width().expect("invalid type for half_width")), 736 DoubleWidth => Bound( 737 ctrl_type 738 .double_width() 739 .expect("invalid type for double_width"), 740 ), 741 HalfVector => Bound( 742 ctrl_type 743 .half_vector() 744 .expect("invalid type for half_vector"), 745 ), 746 DoubleVector => Bound(ctrl_type.by(2).expect("invalid type for double_vector")), 747 SplitLanes => Bound( 748 ctrl_type 749 .split_lanes() 750 .expect("invalid type for split_lanes"), 751 ), 752 MergeLanes => Bound( 753 ctrl_type 754 .merge_lanes() 755 .expect("invalid type for merge_lanes"), 756 ), 757 } 758 } 759 } 760 761 /// The type constraint on a value argument once the controlling type variable is known. 762 #[derive(Copy, Clone, Debug, PartialEq, Eq)] 763 pub enum ResolvedConstraint { 764 /// The operand is bound to a known type. 765 Bound(Type), 766 /// The operand type can vary freely within the given set. 767 Free(ValueTypeSet), 768 } 769 770 #[cfg(test)] 771 mod tests { 772 use super::*; 773 use alloc::string::ToString; 774 775 #[test] 776 fn opcodes() { 777 use core::mem; 778 779 let x = Opcode::Iadd; 780 let mut y = Opcode::Isub; 781 782 assert!(x != y); 783 y = Opcode::Iadd; 784 assert_eq!(x, y); 785 assert_eq!(x.format(), InstructionFormat::Binary); 786 787 assert_eq!(format!("{:?}", Opcode::IaddImm), "IaddImm"); 788 assert_eq!(Opcode::IaddImm.to_string(), "iadd_imm"); 789 790 // Check the matcher. 791 assert_eq!("iadd".parse::<Opcode>(), Ok(Opcode::Iadd)); 792 assert_eq!("iadd_imm".parse::<Opcode>(), Ok(Opcode::IaddImm)); 793 assert_eq!("iadd\0".parse::<Opcode>(), Err("Unknown opcode")); 794 assert_eq!("".parse::<Opcode>(), Err("Unknown opcode")); 795 assert_eq!("\0".parse::<Opcode>(), Err("Unknown opcode")); 796 797 // Opcode is a single byte, and because Option<Opcode> originally came to 2 bytes, early on 798 // Opcode included a variant NotAnOpcode to avoid the unnecessary bloat. Since then the Rust 799 // compiler has brought in NonZero optimization, meaning that an enum not using the 0 value 800 // can be optional for no size cost. We want to ensure Option<Opcode> remains small. 801 assert_eq!(mem::size_of::<Opcode>(), mem::size_of::<Option<Opcode>>()); 802 } 803 804 #[test] 805 fn instruction_data() { 806 use core::mem; 807 // The size of the `InstructionData` enum is important for performance. It should not 808 // exceed 16 bytes. Use `Box<FooData>` out-of-line payloads for instruction formats that 809 // require more space than that. It would be fine with a data structure smaller than 16 810 // bytes, but what are the odds of that? 811 assert_eq!(mem::size_of::<InstructionData>(), 16); 812 } 813 814 #[test] 815 fn constraints() { 816 let a = Opcode::Iadd.constraints(); 817 assert!(a.use_typevar_operand()); 818 assert!(!a.requires_typevar_operand()); 819 assert_eq!(a.num_fixed_results(), 1); 820 assert_eq!(a.num_fixed_value_arguments(), 2); 821 assert_eq!(a.result_type(0, types::I32), types::I32); 822 assert_eq!(a.result_type(0, types::I8), types::I8); 823 assert_eq!( 824 a.value_argument_constraint(0, types::I32), 825 ResolvedConstraint::Bound(types::I32) 826 ); 827 assert_eq!( 828 a.value_argument_constraint(1, types::I32), 829 ResolvedConstraint::Bound(types::I32) 830 ); 831 832 let b = Opcode::Bitcast.constraints(); 833 assert!(!b.use_typevar_operand()); 834 assert!(!b.requires_typevar_operand()); 835 assert_eq!(b.num_fixed_results(), 1); 836 assert_eq!(b.num_fixed_value_arguments(), 1); 837 assert_eq!(b.result_type(0, types::I32), types::I32); 838 assert_eq!(b.result_type(0, types::I8), types::I8); 839 match b.value_argument_constraint(0, types::I32) { 840 ResolvedConstraint::Free(vts) => assert!(vts.contains(types::F32)), 841 _ => panic!("Unexpected constraint from value_argument_constraint"), 842 } 843 844 let c = Opcode::Call.constraints(); 845 assert_eq!(c.num_fixed_results(), 0); 846 assert_eq!(c.num_fixed_value_arguments(), 0); 847 848 let i = Opcode::CallIndirect.constraints(); 849 assert_eq!(i.num_fixed_results(), 0); 850 assert_eq!(i.num_fixed_value_arguments(), 1); 851 852 let cmp = Opcode::Icmp.constraints(); 853 assert!(cmp.use_typevar_operand()); 854 assert!(cmp.requires_typevar_operand()); 855 assert_eq!(cmp.num_fixed_results(), 1); 856 assert_eq!(cmp.num_fixed_value_arguments(), 2); 857 } 858 859 #[test] 860 fn value_set() { 861 use crate::ir::types::*; 862 863 let vts = ValueTypeSet { 864 lanes: BitSet16::from_range(0, 8), 865 ints: BitSet8::from_range(4, 7), 866 floats: BitSet8::from_range(0, 0), 867 bools: BitSet8::from_range(3, 7), 868 refs: BitSet8::from_range(5, 7), 869 }; 870 assert!(!vts.contains(I8)); 871 assert!(vts.contains(I32)); 872 assert!(vts.contains(I64)); 873 assert!(vts.contains(I32X4)); 874 assert!(!vts.contains(F32)); 875 assert!(!vts.contains(B1)); 876 assert!(vts.contains(B8)); 877 assert!(vts.contains(B64)); 878 assert!(vts.contains(R32)); 879 assert!(vts.contains(R64)); 880 assert_eq!(vts.example().to_string(), "i32"); 881 882 let vts = ValueTypeSet { 883 lanes: BitSet16::from_range(0, 8), 884 ints: BitSet8::from_range(0, 0), 885 floats: BitSet8::from_range(5, 7), 886 bools: BitSet8::from_range(3, 7), 887 refs: BitSet8::from_range(0, 0), 888 }; 889 assert_eq!(vts.example().to_string(), "f32"); 890 891 let vts = ValueTypeSet { 892 lanes: BitSet16::from_range(1, 8), 893 ints: BitSet8::from_range(0, 0), 894 floats: BitSet8::from_range(5, 7), 895 bools: BitSet8::from_range(3, 7), 896 refs: BitSet8::from_range(0, 0), 897 }; 898 assert_eq!(vts.example().to_string(), "f32x2"); 899 900 let vts = ValueTypeSet { 901 lanes: BitSet16::from_range(2, 8), 902 ints: BitSet8::from_range(0, 0), 903 floats: BitSet8::from_range(0, 0), 904 bools: BitSet8::from_range(3, 7), 905 refs: BitSet8::from_range(0, 0), 906 }; 907 assert!(!vts.contains(B32X2)); 908 assert!(vts.contains(B32X4)); 909 assert_eq!(vts.example().to_string(), "b32x4"); 910 911 let vts = ValueTypeSet { 912 // TypeSet(lanes=(1, 256), ints=(8, 64)) 913 lanes: BitSet16::from_range(0, 9), 914 ints: BitSet8::from_range(3, 7), 915 floats: BitSet8::from_range(0, 0), 916 bools: BitSet8::from_range(0, 0), 917 refs: BitSet8::from_range(0, 0), 918 }; 919 assert!(vts.contains(I32)); 920 assert!(vts.contains(I32X4)); 921 assert!(!vts.contains(R32)); 922 assert!(!vts.contains(R64)); 923 } 924 } 925