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