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::ScalarBitSet; 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 /// Is this instruction a GC safepoint? 198 /// 199 /// Safepoints are all kinds of calls, except for tail calls. 200 #[inline] 201 pub fn is_safepoint(self) -> bool { 202 self.is_call() && !self.is_return() 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 mask_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::UnaryImm { opcode: _, imm } => { 470 *imm = imm.mask_to_width(bit_width); 471 } 472 Self::BinaryImm64 { 473 opcode, 474 arg: _, 475 imm, 476 } => { 477 if *opcode == Opcode::SdivImm || *opcode == Opcode::SremImm { 478 *imm = imm.mask_to_width(bit_width); 479 } 480 } 481 Self::IntCompareImm { 482 opcode, 483 arg: _, 484 cond, 485 imm, 486 } => { 487 debug_assert_eq!(*opcode, Opcode::IcmpImm); 488 if cond.unsigned() != *cond { 489 *imm = imm.mask_to_width(bit_width); 490 } 491 } 492 _ => {} 493 } 494 } 495 } 496 497 /// Information about call instructions. 498 pub enum CallInfo<'a> { 499 /// This is not a call instruction. 500 NotACall, 501 502 /// This is a direct call to an external function declared in the preamble. See 503 /// `DataFlowGraph.ext_funcs`. 504 Direct(FuncRef, &'a [Value]), 505 506 /// This is an indirect call with the specified signature. See `DataFlowGraph.signatures`. 507 Indirect(SigRef, &'a [Value]), 508 } 509 510 /// Value type constraints for a given opcode. 511 /// 512 /// The `InstructionFormat` determines the constraints on most operands, but `Value` operands and 513 /// results are not determined by the format. Every `Opcode` has an associated 514 /// `OpcodeConstraints` object that provides the missing details. 515 #[derive(Clone, Copy)] 516 pub struct OpcodeConstraints { 517 /// Flags for this opcode encoded as a bit field: 518 /// 519 /// Bits 0-2: 520 /// Number of fixed result values. This does not include `variable_args` results as are 521 /// produced by call instructions. 522 /// 523 /// Bit 3: 524 /// This opcode is polymorphic and the controlling type variable can be inferred from the 525 /// designated input operand. This is the `typevar_operand` index given to the 526 /// `InstructionFormat` meta language object. When this bit is not set, the controlling 527 /// type variable must be the first output value instead. 528 /// 529 /// Bit 4: 530 /// This opcode is polymorphic and the controlling type variable does *not* appear as the 531 /// first result type. 532 /// 533 /// Bits 5-7: 534 /// Number of fixed value arguments. The minimum required number of value operands. 535 flags: u8, 536 537 /// Permitted set of types for the controlling type variable as an index into `TYPE_SETS`. 538 typeset_offset: u8, 539 540 /// Offset into `OPERAND_CONSTRAINT` table of the descriptors for this opcode. The first 541 /// `num_fixed_results()` entries describe the result constraints, then follows constraints for 542 /// the fixed `Value` input operands. (`num_fixed_value_arguments()` of them). 543 constraint_offset: u16, 544 } 545 546 impl OpcodeConstraints { 547 /// Can the controlling type variable for this opcode be inferred from the designated value 548 /// input operand? 549 /// This also implies that this opcode is polymorphic. 550 pub fn use_typevar_operand(self) -> bool { 551 (self.flags & 0x8) != 0 552 } 553 554 /// Is it necessary to look at the designated value input operand in order to determine the 555 /// controlling type variable, or is it good enough to use the first return type? 556 /// 557 /// Most polymorphic instructions produce a single result with the type of the controlling type 558 /// variable. A few polymorphic instructions either don't produce any results, or produce 559 /// results with a fixed type. These instructions return `true`. 560 pub fn requires_typevar_operand(self) -> bool { 561 (self.flags & 0x10) != 0 562 } 563 564 /// Get the number of *fixed* result values produced by this opcode. 565 /// This does not include `variable_args` produced by calls. 566 pub fn num_fixed_results(self) -> usize { 567 (self.flags & 0x7) as usize 568 } 569 570 /// Get the number of *fixed* input values required by this opcode. 571 /// 572 /// This does not include `variable_args` arguments on call and branch instructions. 573 /// 574 /// The number of fixed input values is usually implied by the instruction format, but 575 /// instruction formats that use a `ValueList` put both fixed and variable arguments in the 576 /// list. This method returns the *minimum* number of values required in the value list. 577 pub fn num_fixed_value_arguments(self) -> usize { 578 ((self.flags >> 5) & 0x7) as usize 579 } 580 581 /// Get the offset into `TYPE_SETS` for the controlling type variable. 582 /// Returns `None` if the instruction is not polymorphic. 583 fn typeset_offset(self) -> Option<usize> { 584 let offset = usize::from(self.typeset_offset); 585 if offset < TYPE_SETS.len() { 586 Some(offset) 587 } else { 588 None 589 } 590 } 591 592 /// Get the offset into OPERAND_CONSTRAINTS where the descriptors for this opcode begin. 593 fn constraint_offset(self) -> usize { 594 self.constraint_offset as usize 595 } 596 597 /// Get the value type of result number `n`, having resolved the controlling type variable to 598 /// `ctrl_type`. 599 pub fn result_type(self, n: usize, ctrl_type: Type) -> Type { 600 debug_assert!(n < self.num_fixed_results(), "Invalid result index"); 601 match OPERAND_CONSTRAINTS[self.constraint_offset() + n].resolve(ctrl_type) { 602 ResolvedConstraint::Bound(t) => t, 603 ResolvedConstraint::Free(ts) => panic!("Result constraints can't be free: {ts:?}"), 604 } 605 } 606 607 /// Get the value type of input value number `n`, having resolved the controlling type variable 608 /// to `ctrl_type`. 609 /// 610 /// Unlike results, it is possible for some input values to vary freely within a specific 611 /// `ValueTypeSet`. This is represented with the `ArgumentConstraint::Free` variant. 612 pub fn value_argument_constraint(self, n: usize, ctrl_type: Type) -> ResolvedConstraint { 613 debug_assert!( 614 n < self.num_fixed_value_arguments(), 615 "Invalid value argument index" 616 ); 617 let offset = self.constraint_offset() + self.num_fixed_results(); 618 OPERAND_CONSTRAINTS[offset + n].resolve(ctrl_type) 619 } 620 621 /// Get the typeset of allowed types for the controlling type variable in a polymorphic 622 /// instruction. 623 pub fn ctrl_typeset(self) -> Option<ValueTypeSet> { 624 self.typeset_offset().map(|offset| TYPE_SETS[offset]) 625 } 626 627 /// Is this instruction polymorphic? 628 pub fn is_polymorphic(self) -> bool { 629 self.ctrl_typeset().is_some() 630 } 631 } 632 633 type BitSet8 = ScalarBitSet<u8>; 634 type BitSet16 = ScalarBitSet<u16>; 635 636 /// A value type set describes the permitted set of types for a type variable. 637 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] 638 pub struct ValueTypeSet { 639 /// Allowed lane sizes 640 pub lanes: BitSet16, 641 /// Allowed int widths 642 pub ints: BitSet8, 643 /// Allowed float widths 644 pub floats: BitSet8, 645 /// Allowed ref widths 646 pub refs: BitSet8, 647 /// Allowed dynamic vectors minimum lane sizes 648 pub dynamic_lanes: BitSet16, 649 } 650 651 impl ValueTypeSet { 652 /// Is `scalar` part of the base type set? 653 /// 654 /// Note that the base type set does not have to be included in the type set proper. 655 fn is_base_type(self, scalar: Type) -> bool { 656 let l2b = u8::try_from(scalar.log2_lane_bits()).unwrap(); 657 if scalar.is_int() { 658 self.ints.contains(l2b) 659 } else if scalar.is_float() { 660 self.floats.contains(l2b) 661 } else if scalar.is_ref() { 662 self.refs.contains(l2b) 663 } else { 664 false 665 } 666 } 667 668 /// Does `typ` belong to this set? 669 pub fn contains(self, typ: Type) -> bool { 670 if typ.is_dynamic_vector() { 671 let l2l = u8::try_from(typ.log2_min_lane_count()).unwrap(); 672 self.dynamic_lanes.contains(l2l) && self.is_base_type(typ.lane_type()) 673 } else { 674 let l2l = u8::try_from(typ.log2_lane_count()).unwrap(); 675 self.lanes.contains(l2l) && self.is_base_type(typ.lane_type()) 676 } 677 } 678 679 /// Get an example member of this type set. 680 /// 681 /// This is used for error messages to avoid suggesting invalid types. 682 pub fn example(self) -> Type { 683 let t = if self.ints.max().unwrap_or(0) > 5 { 684 types::I32 685 } else if self.floats.max().unwrap_or(0) > 5 { 686 types::F32 687 } else { 688 types::I8 689 }; 690 t.by(1 << self.lanes.min().unwrap()).unwrap() 691 } 692 } 693 694 /// Operand constraints. This describes the value type constraints on a single `Value` operand. 695 enum OperandConstraint { 696 /// This operand has a concrete value type. 697 Concrete(Type), 698 699 /// This operand can vary freely within the given type set. 700 /// The type set is identified by its index into the TYPE_SETS constant table. 701 Free(u8), 702 703 /// This operand is the same type as the controlling type variable. 704 Same, 705 706 /// This operand is `ctrlType.lane_of()`. 707 LaneOf, 708 709 /// This operand is `ctrlType.as_truthy()`. 710 AsTruthy, 711 712 /// This operand is `ctrlType.half_width()`. 713 HalfWidth, 714 715 /// This operand is `ctrlType.double_width()`. 716 DoubleWidth, 717 718 /// This operand is `ctrlType.split_lanes()`. 719 SplitLanes, 720 721 /// This operand is `ctrlType.merge_lanes()`. 722 MergeLanes, 723 724 /// This operands is `ctrlType.dynamic_to_vector()`. 725 DynamicToVector, 726 727 /// This operand is `ctrlType.narrower()`. 728 Narrower, 729 730 /// This operand is `ctrlType.wider()`. 731 Wider, 732 } 733 734 impl OperandConstraint { 735 /// Resolve this operand constraint into a concrete value type, given the value of the 736 /// controlling type variable. 737 pub fn resolve(&self, ctrl_type: Type) -> ResolvedConstraint { 738 use self::OperandConstraint::*; 739 use self::ResolvedConstraint::Bound; 740 match *self { 741 Concrete(t) => Bound(t), 742 Free(vts) => ResolvedConstraint::Free(TYPE_SETS[vts as usize]), 743 Same => Bound(ctrl_type), 744 LaneOf => Bound(ctrl_type.lane_of()), 745 AsTruthy => Bound(ctrl_type.as_truthy()), 746 HalfWidth => Bound(ctrl_type.half_width().expect("invalid type for half_width")), 747 DoubleWidth => Bound( 748 ctrl_type 749 .double_width() 750 .expect("invalid type for double_width"), 751 ), 752 SplitLanes => { 753 if ctrl_type.is_dynamic_vector() { 754 Bound( 755 ctrl_type 756 .dynamic_to_vector() 757 .expect("invalid type for dynamic_to_vector") 758 .split_lanes() 759 .expect("invalid type for split_lanes") 760 .vector_to_dynamic() 761 .expect("invalid dynamic type"), 762 ) 763 } else { 764 Bound( 765 ctrl_type 766 .split_lanes() 767 .expect("invalid type for split_lanes"), 768 ) 769 } 770 } 771 MergeLanes => { 772 if ctrl_type.is_dynamic_vector() { 773 Bound( 774 ctrl_type 775 .dynamic_to_vector() 776 .expect("invalid type for dynamic_to_vector") 777 .merge_lanes() 778 .expect("invalid type for merge_lanes") 779 .vector_to_dynamic() 780 .expect("invalid dynamic type"), 781 ) 782 } else { 783 Bound( 784 ctrl_type 785 .merge_lanes() 786 .expect("invalid type for merge_lanes"), 787 ) 788 } 789 } 790 DynamicToVector => Bound( 791 ctrl_type 792 .dynamic_to_vector() 793 .expect("invalid type for dynamic_to_vector"), 794 ), 795 Narrower => { 796 let ctrl_type_bits = ctrl_type.log2_lane_bits(); 797 let mut tys = ValueTypeSet::default(); 798 799 // We're testing scalar values, only. 800 tys.lanes = ScalarBitSet::from_range(0, 1); 801 802 if ctrl_type.is_int() { 803 // The upper bound in from_range is exclusive, and we want to exclude the 804 // control type to construct the interval of [I8, ctrl_type). 805 tys.ints = BitSet8::from_range(3, ctrl_type_bits as u8); 806 } else if ctrl_type.is_float() { 807 // The upper bound in from_range is exclusive, and we want to exclude the 808 // control type to construct the interval of [F16, ctrl_type). 809 tys.floats = BitSet8::from_range(4, ctrl_type_bits as u8); 810 } else { 811 panic!("The Narrower constraint only operates on floats or ints"); 812 } 813 ResolvedConstraint::Free(tys) 814 } 815 Wider => { 816 let ctrl_type_bits = ctrl_type.log2_lane_bits(); 817 let mut tys = ValueTypeSet::default(); 818 819 // We're testing scalar values, only. 820 tys.lanes = ScalarBitSet::from_range(0, 1); 821 822 if ctrl_type.is_int() { 823 let lower_bound = ctrl_type_bits as u8 + 1; 824 // The largest integer type we can represent in `BitSet8` is I128, which is 825 // represented by bit 7 in the bit set. Adding one to exclude I128 from the 826 // lower bound would overflow as 2^8 doesn't fit in a u8, but this would 827 // already describe the empty set so instead we leave `ints` in its default 828 // empty state. 829 if lower_bound < BitSet8::capacity() { 830 // The interval should include all types wider than `ctrl_type`, so we use 831 // `2^8` as the upper bound, and add one to the bits of `ctrl_type` to define 832 // the interval `(ctrl_type, I128]`. 833 tys.ints = BitSet8::from_range(lower_bound, 8); 834 } 835 } else if ctrl_type.is_float() { 836 // Same as above but for `tys.floats`, as the largest float type is F128. 837 let lower_bound = ctrl_type_bits as u8 + 1; 838 if lower_bound < BitSet8::capacity() { 839 tys.floats = BitSet8::from_range(lower_bound, 8); 840 } 841 } else { 842 panic!("The Wider constraint only operates on floats or ints"); 843 } 844 845 ResolvedConstraint::Free(tys) 846 } 847 } 848 } 849 } 850 851 /// The type constraint on a value argument once the controlling type variable is known. 852 #[derive(Copy, Clone, Debug, PartialEq, Eq)] 853 pub enum ResolvedConstraint { 854 /// The operand is bound to a known type. 855 Bound(Type), 856 /// The operand type can vary freely within the given set. 857 Free(ValueTypeSet), 858 } 859 860 #[cfg(test)] 861 mod tests { 862 use super::*; 863 use alloc::string::ToString; 864 865 #[test] 866 fn inst_data_is_copy() { 867 fn is_copy<T: Copy>() {} 868 is_copy::<InstructionData>(); 869 } 870 871 #[test] 872 fn inst_data_size() { 873 // The size of `InstructionData` is performance sensitive, so make sure 874 // we don't regress it unintentionally. 875 assert_eq!(std::mem::size_of::<InstructionData>(), 16); 876 } 877 878 #[test] 879 fn opcodes() { 880 use core::mem; 881 882 let x = Opcode::Iadd; 883 let mut y = Opcode::Isub; 884 885 assert!(x != y); 886 y = Opcode::Iadd; 887 assert_eq!(x, y); 888 assert_eq!(x.format(), InstructionFormat::Binary); 889 890 assert_eq!(format!("{:?}", Opcode::IaddImm), "IaddImm"); 891 assert_eq!(Opcode::IaddImm.to_string(), "iadd_imm"); 892 893 // Check the matcher. 894 assert_eq!("iadd".parse::<Opcode>(), Ok(Opcode::Iadd)); 895 assert_eq!("iadd_imm".parse::<Opcode>(), Ok(Opcode::IaddImm)); 896 assert_eq!("iadd\0".parse::<Opcode>(), Err("Unknown opcode")); 897 assert_eq!("".parse::<Opcode>(), Err("Unknown opcode")); 898 assert_eq!("\0".parse::<Opcode>(), Err("Unknown opcode")); 899 900 // Opcode is a single byte, and because Option<Opcode> originally came to 2 bytes, early on 901 // Opcode included a variant NotAnOpcode to avoid the unnecessary bloat. Since then the Rust 902 // compiler has brought in NonZero optimization, meaning that an enum not using the 0 value 903 // can be optional for no size cost. We want to ensure Option<Opcode> remains small. 904 assert_eq!(mem::size_of::<Opcode>(), mem::size_of::<Option<Opcode>>()); 905 } 906 907 #[test] 908 fn instruction_data() { 909 use core::mem; 910 // The size of the `InstructionData` enum is important for performance. It should not 911 // exceed 16 bytes. Use `Box<FooData>` out-of-line payloads for instruction formats that 912 // require more space than that. It would be fine with a data structure smaller than 16 913 // bytes, but what are the odds of that? 914 assert_eq!(mem::size_of::<InstructionData>(), 16); 915 } 916 917 #[test] 918 fn constraints() { 919 let a = Opcode::Iadd.constraints(); 920 assert!(a.use_typevar_operand()); 921 assert!(!a.requires_typevar_operand()); 922 assert_eq!(a.num_fixed_results(), 1); 923 assert_eq!(a.num_fixed_value_arguments(), 2); 924 assert_eq!(a.result_type(0, types::I32), types::I32); 925 assert_eq!(a.result_type(0, types::I8), types::I8); 926 assert_eq!( 927 a.value_argument_constraint(0, types::I32), 928 ResolvedConstraint::Bound(types::I32) 929 ); 930 assert_eq!( 931 a.value_argument_constraint(1, types::I32), 932 ResolvedConstraint::Bound(types::I32) 933 ); 934 935 let b = Opcode::Bitcast.constraints(); 936 assert!(!b.use_typevar_operand()); 937 assert!(!b.requires_typevar_operand()); 938 assert_eq!(b.num_fixed_results(), 1); 939 assert_eq!(b.num_fixed_value_arguments(), 1); 940 assert_eq!(b.result_type(0, types::I32), types::I32); 941 assert_eq!(b.result_type(0, types::I8), types::I8); 942 match b.value_argument_constraint(0, types::I32) { 943 ResolvedConstraint::Free(vts) => assert!(vts.contains(types::F32)), 944 _ => panic!("Unexpected constraint from value_argument_constraint"), 945 } 946 947 let c = Opcode::Call.constraints(); 948 assert_eq!(c.num_fixed_results(), 0); 949 assert_eq!(c.num_fixed_value_arguments(), 0); 950 951 let i = Opcode::CallIndirect.constraints(); 952 assert_eq!(i.num_fixed_results(), 0); 953 assert_eq!(i.num_fixed_value_arguments(), 1); 954 955 let cmp = Opcode::Icmp.constraints(); 956 assert!(cmp.use_typevar_operand()); 957 assert!(cmp.requires_typevar_operand()); 958 assert_eq!(cmp.num_fixed_results(), 1); 959 assert_eq!(cmp.num_fixed_value_arguments(), 2); 960 assert_eq!(cmp.result_type(0, types::I64), types::I8); 961 } 962 963 #[test] 964 fn value_set() { 965 use crate::ir::types::*; 966 967 let vts = ValueTypeSet { 968 lanes: BitSet16::from_range(0, 8), 969 ints: BitSet8::from_range(4, 7), 970 floats: BitSet8::from_range(0, 0), 971 refs: BitSet8::from_range(5, 7), 972 dynamic_lanes: BitSet16::from_range(0, 4), 973 }; 974 assert!(!vts.contains(I8)); 975 assert!(vts.contains(I32)); 976 assert!(vts.contains(I64)); 977 assert!(vts.contains(I32X4)); 978 assert!(vts.contains(I32X4XN)); 979 assert!(!vts.contains(F16)); 980 assert!(!vts.contains(F32)); 981 assert!(!vts.contains(F128)); 982 assert!(vts.contains(R32)); 983 assert!(vts.contains(R64)); 984 assert_eq!(vts.example().to_string(), "i32"); 985 986 let vts = ValueTypeSet { 987 lanes: BitSet16::from_range(0, 8), 988 ints: BitSet8::from_range(0, 0), 989 floats: BitSet8::from_range(5, 7), 990 refs: BitSet8::from_range(0, 0), 991 dynamic_lanes: BitSet16::from_range(0, 8), 992 }; 993 assert_eq!(vts.example().to_string(), "f32"); 994 995 let vts = ValueTypeSet { 996 lanes: BitSet16::from_range(1, 8), 997 ints: BitSet8::from_range(0, 0), 998 floats: BitSet8::from_range(5, 7), 999 refs: BitSet8::from_range(0, 0), 1000 dynamic_lanes: BitSet16::from_range(0, 8), 1001 }; 1002 assert_eq!(vts.example().to_string(), "f32x2"); 1003 1004 let vts = ValueTypeSet { 1005 lanes: BitSet16::from_range(2, 8), 1006 ints: BitSet8::from_range(3, 7), 1007 floats: BitSet8::from_range(0, 0), 1008 refs: BitSet8::from_range(0, 0), 1009 dynamic_lanes: BitSet16::from_range(0, 8), 1010 }; 1011 assert_eq!(vts.example().to_string(), "i32x4"); 1012 1013 let vts = ValueTypeSet { 1014 // TypeSet(lanes=(1, 256), ints=(8, 64)) 1015 lanes: BitSet16::from_range(0, 9), 1016 ints: BitSet8::from_range(3, 7), 1017 floats: BitSet8::from_range(0, 0), 1018 refs: BitSet8::from_range(0, 0), 1019 dynamic_lanes: BitSet16::from_range(0, 8), 1020 }; 1021 assert!(vts.contains(I32)); 1022 assert!(vts.contains(I32X4)); 1023 assert!(!vts.contains(R32)); 1024 assert!(!vts.contains(R64)); 1025 } 1026 } 1027