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