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