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