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