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 IntoIterator<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.into_iter().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, .. } 495 | Self::IntAddTrap { code, .. } 496 | Self::Trap { code, .. } => Some(code), 497 _ => None, 498 } 499 } 500 501 /// If this is a control-flow instruction depending on an integer condition, gets its 502 /// condition. Otherwise, return `None`. 503 pub fn cond_code(&self) -> Option<IntCC> { 504 match self { 505 &InstructionData::IntCompare { cond, .. } 506 | &InstructionData::IntCompareImm { cond, .. } => Some(cond), 507 _ => None, 508 } 509 } 510 511 /// If this is a control-flow instruction depending on a floating-point condition, gets its 512 /// condition. Otherwise, return `None`. 513 pub fn fp_cond_code(&self) -> Option<FloatCC> { 514 match self { 515 &InstructionData::FloatCompare { cond, .. } => Some(cond), 516 _ => None, 517 } 518 } 519 520 /// If this is a trapping instruction, get an exclusive reference to its 521 /// trap code. Otherwise, return `None`. 522 pub fn trap_code_mut(&mut self) -> Option<&mut TrapCode> { 523 match self { 524 Self::CondTrap { code, .. } 525 | Self::IntAddTrap { code, .. } 526 | Self::Trap { code, .. } => Some(code), 527 _ => None, 528 } 529 } 530 531 /// If this is an atomic read/modify/write instruction, return its subopcode. 532 pub fn atomic_rmw_op(&self) -> Option<ir::AtomicRmwOp> { 533 match self { 534 &InstructionData::AtomicRmw { op, .. } => Some(op), 535 _ => None, 536 } 537 } 538 539 /// If this is a load/store instruction, returns its immediate offset. 540 pub fn load_store_offset(&self) -> Option<i32> { 541 match self { 542 &InstructionData::Load { offset, .. } 543 | &InstructionData::StackLoad { offset, .. } 544 | &InstructionData::Store { offset, .. } 545 | &InstructionData::StackStore { offset, .. } => Some(offset.into()), 546 _ => None, 547 } 548 } 549 550 /// If this is a load/store instruction, return its memory flags. 551 pub fn memflags(&self) -> Option<MemFlags> { 552 match self { 553 &InstructionData::Load { flags, .. } 554 | &InstructionData::LoadNoOffset { flags, .. } 555 | &InstructionData::Store { flags, .. } 556 | &InstructionData::StoreNoOffset { flags, .. } 557 | &InstructionData::AtomicCas { flags, .. } 558 | &InstructionData::AtomicRmw { flags, .. } => Some(flags), 559 _ => None, 560 } 561 } 562 563 /// If this instruction references a stack slot, return it 564 pub fn stack_slot(&self) -> Option<StackSlot> { 565 match self { 566 &InstructionData::StackStore { stack_slot, .. } 567 | &InstructionData::StackLoad { stack_slot, .. } => Some(stack_slot), 568 _ => None, 569 } 570 } 571 572 /// Return information about a call instruction. 573 /// 574 /// Any instruction that can call another function reveals its call signature here. 575 pub fn analyze_call<'a>( 576 &'a self, 577 pool: &'a ValueListPool, 578 exception_tables: &ExceptionTables, 579 ) -> CallInfo<'a> { 580 match *self { 581 Self::Call { 582 func_ref, ref args, .. 583 } => CallInfo::Direct(func_ref, args.as_slice(pool)), 584 Self::CallIndirect { 585 sig_ref, ref args, .. 586 } => CallInfo::Indirect(sig_ref, &args.as_slice(pool)[1..]), 587 Self::TryCall { 588 func_ref, 589 ref args, 590 exception, 591 .. 592 } => { 593 let exdata = &exception_tables[exception]; 594 CallInfo::DirectWithSig(func_ref, exdata.signature(), args.as_slice(pool)) 595 } 596 Self::TryCallIndirect { 597 exception, 598 ref args, 599 .. 600 } => { 601 let exdata = &exception_tables[exception]; 602 CallInfo::Indirect(exdata.signature(), &args.as_slice(pool)[1..]) 603 } 604 Self::Ternary { 605 opcode: Opcode::StackSwitch, 606 .. 607 } => { 608 // `StackSwitch` is not actually a call, but has the .call() side 609 // effect as it continues execution elsewhere. 610 CallInfo::NotACall 611 } 612 _ => { 613 debug_assert!(!self.opcode().is_call()); 614 CallInfo::NotACall 615 } 616 } 617 } 618 619 #[inline] 620 pub(crate) fn mask_immediates(&mut self, ctrl_typevar: Type) { 621 if ctrl_typevar.is_invalid() { 622 return; 623 } 624 625 let bit_width = ctrl_typevar.bits(); 626 627 match self { 628 Self::UnaryImm { opcode: _, imm } => { 629 *imm = imm.mask_to_width(bit_width); 630 } 631 Self::BinaryImm64 { 632 opcode, 633 arg: _, 634 imm, 635 } => { 636 if *opcode == Opcode::SdivImm || *opcode == Opcode::SremImm { 637 *imm = imm.mask_to_width(bit_width); 638 } 639 } 640 Self::IntCompareImm { 641 opcode, 642 arg: _, 643 cond, 644 imm, 645 } => { 646 debug_assert_eq!(*opcode, Opcode::IcmpImm); 647 if cond.unsigned() != *cond { 648 *imm = imm.mask_to_width(bit_width); 649 } 650 } 651 _ => {} 652 } 653 } 654 655 /// Get the exception table, if any, associated with this instruction. 656 pub fn exception_table(&self) -> Option<ExceptionTable> { 657 match self { 658 Self::TryCall { exception, .. } | Self::TryCallIndirect { exception, .. } => { 659 Some(*exception) 660 } 661 _ => None, 662 } 663 } 664 } 665 666 /// Information about call instructions. 667 pub enum CallInfo<'a> { 668 /// This is not a call instruction. 669 NotACall, 670 671 /// This is a direct call to an external function declared in the preamble. See 672 /// `DataFlowGraph.ext_funcs`. 673 Direct(FuncRef, &'a [Value]), 674 675 /// This is an indirect call with the specified signature. See `DataFlowGraph.signatures`. 676 Indirect(SigRef, &'a [Value]), 677 678 /// This is a direct call to an external function declared in the 679 /// preamble, but the signature is also known by other means: 680 /// e.g., from an exception table entry. 681 DirectWithSig(FuncRef, SigRef, &'a [Value]), 682 } 683 684 /// Value type constraints for a given opcode. 685 /// 686 /// The `InstructionFormat` determines the constraints on most operands, but `Value` operands and 687 /// results are not determined by the format. Every `Opcode` has an associated 688 /// `OpcodeConstraints` object that provides the missing details. 689 #[derive(Clone, Copy)] 690 pub struct OpcodeConstraints { 691 /// Flags for this opcode encoded as a bit field: 692 /// 693 /// Bits 0-2: 694 /// Number of fixed result values. This does not include `variable_args` results as are 695 /// produced by call instructions. 696 /// 697 /// Bit 3: 698 /// This opcode is polymorphic and the controlling type variable can be inferred from the 699 /// designated input operand. This is the `typevar_operand` index given to the 700 /// `InstructionFormat` meta language object. When this bit is not set, the controlling 701 /// type variable must be the first output value instead. 702 /// 703 /// Bit 4: 704 /// This opcode is polymorphic and the controlling type variable does *not* appear as the 705 /// first result type. 706 /// 707 /// Bits 5-7: 708 /// Number of fixed value arguments. The minimum required number of value operands. 709 flags: u8, 710 711 /// Permitted set of types for the controlling type variable as an index into `TYPE_SETS`. 712 typeset_offset: u8, 713 714 /// Offset into `OPERAND_CONSTRAINT` table of the descriptors for this opcode. The first 715 /// `num_fixed_results()` entries describe the result constraints, then follows constraints for 716 /// the fixed `Value` input operands. (`num_fixed_value_arguments()` of them). 717 constraint_offset: u16, 718 } 719 720 impl OpcodeConstraints { 721 /// Can the controlling type variable for this opcode be inferred from the designated value 722 /// input operand? 723 /// This also implies that this opcode is polymorphic. 724 pub fn use_typevar_operand(self) -> bool { 725 (self.flags & 0x8) != 0 726 } 727 728 /// Is it necessary to look at the designated value input operand in order to determine the 729 /// controlling type variable, or is it good enough to use the first return type? 730 /// 731 /// Most polymorphic instructions produce a single result with the type of the controlling type 732 /// variable. A few polymorphic instructions either don't produce any results, or produce 733 /// results with a fixed type. These instructions return `true`. 734 pub fn requires_typevar_operand(self) -> bool { 735 (self.flags & 0x10) != 0 736 } 737 738 /// Get the number of *fixed* result values produced by this opcode. 739 /// This does not include `variable_args` produced by calls. 740 pub fn num_fixed_results(self) -> usize { 741 (self.flags & 0x7) as usize 742 } 743 744 /// Get the number of *fixed* input values required by this opcode. 745 /// 746 /// This does not include `variable_args` arguments on call and branch instructions. 747 /// 748 /// The number of fixed input values is usually implied by the instruction format, but 749 /// instruction formats that use a `ValueList` put both fixed and variable arguments in the 750 /// list. This method returns the *minimum* number of values required in the value list. 751 pub fn num_fixed_value_arguments(self) -> usize { 752 ((self.flags >> 5) & 0x7) as usize 753 } 754 755 /// Get the offset into `TYPE_SETS` for the controlling type variable. 756 /// Returns `None` if the instruction is not polymorphic. 757 fn typeset_offset(self) -> Option<usize> { 758 let offset = usize::from(self.typeset_offset); 759 if offset < TYPE_SETS.len() { 760 Some(offset) 761 } else { 762 None 763 } 764 } 765 766 /// Get the offset into OPERAND_CONSTRAINTS where the descriptors for this opcode begin. 767 fn constraint_offset(self) -> usize { 768 self.constraint_offset as usize 769 } 770 771 /// Get the value type of result number `n`, having resolved the controlling type variable to 772 /// `ctrl_type`. 773 pub fn result_type(self, n: usize, ctrl_type: Type) -> Type { 774 debug_assert!(n < self.num_fixed_results(), "Invalid result index"); 775 match OPERAND_CONSTRAINTS[self.constraint_offset() + n].resolve(ctrl_type) { 776 ResolvedConstraint::Bound(t) => t, 777 ResolvedConstraint::Free(ts) => panic!("Result constraints can't be free: {ts:?}"), 778 } 779 } 780 781 /// Get the value type of input value number `n`, having resolved the controlling type variable 782 /// to `ctrl_type`. 783 /// 784 /// Unlike results, it is possible for some input values to vary freely within a specific 785 /// `ValueTypeSet`. This is represented with the `ArgumentConstraint::Free` variant. 786 pub fn value_argument_constraint(self, n: usize, ctrl_type: Type) -> ResolvedConstraint { 787 debug_assert!( 788 n < self.num_fixed_value_arguments(), 789 "Invalid value argument index" 790 ); 791 let offset = self.constraint_offset() + self.num_fixed_results(); 792 OPERAND_CONSTRAINTS[offset + n].resolve(ctrl_type) 793 } 794 795 /// Get the typeset of allowed types for the controlling type variable in a polymorphic 796 /// instruction. 797 pub fn ctrl_typeset(self) -> Option<ValueTypeSet> { 798 self.typeset_offset().map(|offset| TYPE_SETS[offset]) 799 } 800 801 /// Is this instruction polymorphic? 802 pub fn is_polymorphic(self) -> bool { 803 self.ctrl_typeset().is_some() 804 } 805 } 806 807 type BitSet8 = ScalarBitSet<u8>; 808 type BitSet16 = ScalarBitSet<u16>; 809 810 /// A value type set describes the permitted set of types for a type variable. 811 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] 812 pub struct ValueTypeSet { 813 /// Allowed lane sizes 814 pub lanes: BitSet16, 815 /// Allowed int widths 816 pub ints: BitSet8, 817 /// Allowed float widths 818 pub floats: BitSet8, 819 /// Allowed dynamic vectors minimum lane sizes 820 pub dynamic_lanes: BitSet16, 821 } 822 823 impl ValueTypeSet { 824 /// Is `scalar` part of the base type set? 825 /// 826 /// Note that the base type set does not have to be included in the type set proper. 827 fn is_base_type(self, scalar: Type) -> bool { 828 let l2b = u8::try_from(scalar.log2_lane_bits()).unwrap(); 829 if scalar.is_int() { 830 self.ints.contains(l2b) 831 } else if scalar.is_float() { 832 self.floats.contains(l2b) 833 } else { 834 false 835 } 836 } 837 838 /// Does `typ` belong to this set? 839 pub fn contains(self, typ: Type) -> bool { 840 if typ.is_dynamic_vector() { 841 let l2l = u8::try_from(typ.log2_min_lane_count()).unwrap(); 842 self.dynamic_lanes.contains(l2l) && self.is_base_type(typ.lane_type()) 843 } else { 844 let l2l = u8::try_from(typ.log2_lane_count()).unwrap(); 845 self.lanes.contains(l2l) && self.is_base_type(typ.lane_type()) 846 } 847 } 848 849 /// Get an example member of this type set. 850 /// 851 /// This is used for error messages to avoid suggesting invalid types. 852 pub fn example(self) -> Type { 853 let t = if self.ints.max().unwrap_or(0) > 5 { 854 types::I32 855 } else if self.floats.max().unwrap_or(0) > 5 { 856 types::F32 857 } else { 858 types::I8 859 }; 860 t.by(1 << self.lanes.min().unwrap()).unwrap() 861 } 862 } 863 864 /// Operand constraints. This describes the value type constraints on a single `Value` operand. 865 enum OperandConstraint { 866 /// This operand has a concrete value type. 867 Concrete(Type), 868 869 /// This operand can vary freely within the given type set. 870 /// The type set is identified by its index into the TYPE_SETS constant table. 871 Free(u8), 872 873 /// This operand is the same type as the controlling type variable. 874 Same, 875 876 /// This operand is `ctrlType.lane_of()`. 877 LaneOf, 878 879 /// This operand is `ctrlType.as_truthy()`. 880 AsTruthy, 881 882 /// This operand is `ctrlType.half_width()`. 883 HalfWidth, 884 885 /// This operand is `ctrlType.double_width()`. 886 DoubleWidth, 887 888 /// This operand is `ctrlType.split_lanes()`. 889 SplitLanes, 890 891 /// This operand is `ctrlType.merge_lanes()`. 892 MergeLanes, 893 894 /// This operands is `ctrlType.dynamic_to_vector()`. 895 DynamicToVector, 896 897 /// This operand is `ctrlType.narrower()`. 898 Narrower, 899 900 /// This operand is `ctrlType.wider()`. 901 Wider, 902 } 903 904 impl OperandConstraint { 905 /// Resolve this operand constraint into a concrete value type, given the value of the 906 /// controlling type variable. 907 pub fn resolve(&self, ctrl_type: Type) -> ResolvedConstraint { 908 use self::OperandConstraint::*; 909 use self::ResolvedConstraint::Bound; 910 match *self { 911 Concrete(t) => Bound(t), 912 Free(vts) => ResolvedConstraint::Free(TYPE_SETS[vts as usize]), 913 Same => Bound(ctrl_type), 914 LaneOf => Bound(ctrl_type.lane_of()), 915 AsTruthy => Bound(ctrl_type.as_truthy()), 916 HalfWidth => Bound(ctrl_type.half_width().expect("invalid type for half_width")), 917 DoubleWidth => Bound( 918 ctrl_type 919 .double_width() 920 .expect("invalid type for double_width"), 921 ), 922 SplitLanes => { 923 if ctrl_type.is_dynamic_vector() { 924 Bound( 925 ctrl_type 926 .dynamic_to_vector() 927 .expect("invalid type for dynamic_to_vector") 928 .split_lanes() 929 .expect("invalid type for split_lanes") 930 .vector_to_dynamic() 931 .expect("invalid dynamic type"), 932 ) 933 } else { 934 Bound( 935 ctrl_type 936 .split_lanes() 937 .expect("invalid type for split_lanes"), 938 ) 939 } 940 } 941 MergeLanes => { 942 if ctrl_type.is_dynamic_vector() { 943 Bound( 944 ctrl_type 945 .dynamic_to_vector() 946 .expect("invalid type for dynamic_to_vector") 947 .merge_lanes() 948 .expect("invalid type for merge_lanes") 949 .vector_to_dynamic() 950 .expect("invalid dynamic type"), 951 ) 952 } else { 953 Bound( 954 ctrl_type 955 .merge_lanes() 956 .expect("invalid type for merge_lanes"), 957 ) 958 } 959 } 960 DynamicToVector => Bound( 961 ctrl_type 962 .dynamic_to_vector() 963 .expect("invalid type for dynamic_to_vector"), 964 ), 965 Narrower => { 966 let ctrl_type_bits = ctrl_type.log2_lane_bits(); 967 let mut tys = ValueTypeSet::default(); 968 969 // We're testing scalar values, only. 970 tys.lanes = ScalarBitSet::from_range(0, 1); 971 972 if ctrl_type.is_int() { 973 // The upper bound in from_range is exclusive, and we want to exclude the 974 // control type to construct the interval of [I8, ctrl_type). 975 tys.ints = BitSet8::from_range(3, ctrl_type_bits as u8); 976 } else if ctrl_type.is_float() { 977 // The upper bound in from_range is exclusive, and we want to exclude the 978 // control type to construct the interval of [F16, ctrl_type). 979 tys.floats = BitSet8::from_range(4, ctrl_type_bits as u8); 980 } else { 981 panic!( 982 "The Narrower constraint only operates on floats or ints, got {ctrl_type:?}" 983 ); 984 } 985 ResolvedConstraint::Free(tys) 986 } 987 Wider => { 988 let ctrl_type_bits = ctrl_type.log2_lane_bits(); 989 let mut tys = ValueTypeSet::default(); 990 991 // We're testing scalar values, only. 992 tys.lanes = ScalarBitSet::from_range(0, 1); 993 994 if ctrl_type.is_int() { 995 let lower_bound = ctrl_type_bits as u8 + 1; 996 // The largest integer type we can represent in `BitSet8` is I128, which is 997 // represented by bit 7 in the bit set. Adding one to exclude I128 from the 998 // lower bound would overflow as 2^8 doesn't fit in a u8, but this would 999 // already describe the empty set so instead we leave `ints` in its default 1000 // empty state. 1001 if lower_bound < BitSet8::capacity() { 1002 // The interval should include all types wider than `ctrl_type`, so we use 1003 // `2^8` as the upper bound, and add one to the bits of `ctrl_type` to define 1004 // the interval `(ctrl_type, I128]`. 1005 tys.ints = BitSet8::from_range(lower_bound, 8); 1006 } 1007 } else if ctrl_type.is_float() { 1008 // Same as above but for `tys.floats`, as the largest float type is F128. 1009 let lower_bound = ctrl_type_bits as u8 + 1; 1010 if lower_bound < BitSet8::capacity() { 1011 tys.floats = BitSet8::from_range(lower_bound, 8); 1012 } 1013 } else { 1014 panic!( 1015 "The Wider constraint only operates on floats or ints, got {ctrl_type:?}" 1016 ); 1017 } 1018 1019 ResolvedConstraint::Free(tys) 1020 } 1021 } 1022 } 1023 } 1024 1025 /// The type constraint on a value argument once the controlling type variable is known. 1026 #[derive(Copy, Clone, Debug, PartialEq, Eq)] 1027 pub enum ResolvedConstraint { 1028 /// The operand is bound to a known type. 1029 Bound(Type), 1030 /// The operand type can vary freely within the given set. 1031 Free(ValueTypeSet), 1032 } 1033 1034 /// A trait to map some functions over each of the entities within an 1035 /// instruction, when paired with `InstructionData::map`. 1036 pub trait InstructionMapper { 1037 /// Map a function over a `Value`. 1038 fn map_value(&mut self, value: Value) -> Value; 1039 1040 /// Map a function over a `ValueList`. 1041 fn map_value_list(&mut self, value_list: ValueList) -> ValueList; 1042 1043 /// Map a function over a `GlobalValue`. 1044 fn map_global_value(&mut self, global_value: ir::GlobalValue) -> ir::GlobalValue; 1045 1046 /// Map a function over a `JumpTable`. 1047 fn map_jump_table(&mut self, jump_table: ir::JumpTable) -> ir::JumpTable; 1048 1049 /// Map a function over an `ExceptionTable`. 1050 fn map_exception_table(&mut self, exception_table: ExceptionTable) -> ExceptionTable; 1051 1052 /// Map a function over a `BlockCall`. 1053 fn map_block_call(&mut self, block_call: BlockCall) -> BlockCall; 1054 1055 /// Map a function over a `FuncRef`. 1056 fn map_func_ref(&mut self, func_ref: FuncRef) -> FuncRef; 1057 1058 /// Map a function over a `SigRef`. 1059 fn map_sig_ref(&mut self, sig_ref: SigRef) -> SigRef; 1060 1061 /// Map a function over a `StackSlot`. 1062 fn map_stack_slot(&mut self, stack_slot: StackSlot) -> StackSlot; 1063 1064 /// Map a function over a `DynamicStackSlot`. 1065 fn map_dynamic_stack_slot( 1066 &mut self, 1067 dynamic_stack_slot: ir::DynamicStackSlot, 1068 ) -> ir::DynamicStackSlot; 1069 1070 /// Map a function over a `Constant`. 1071 fn map_constant(&mut self, constant: ir::Constant) -> ir::Constant; 1072 1073 /// Map a function over an `Immediate`. 1074 fn map_immediate(&mut self, immediate: ir::Immediate) -> ir::Immediate; 1075 } 1076 1077 impl<'a, T> InstructionMapper for &'a mut T 1078 where 1079 T: InstructionMapper, 1080 { 1081 fn map_value(&mut self, value: Value) -> Value { 1082 (**self).map_value(value) 1083 } 1084 1085 fn map_value_list(&mut self, value_list: ValueList) -> ValueList { 1086 (**self).map_value_list(value_list) 1087 } 1088 1089 fn map_global_value(&mut self, global_value: ir::GlobalValue) -> ir::GlobalValue { 1090 (**self).map_global_value(global_value) 1091 } 1092 1093 fn map_jump_table(&mut self, jump_table: ir::JumpTable) -> ir::JumpTable { 1094 (**self).map_jump_table(jump_table) 1095 } 1096 1097 fn map_exception_table(&mut self, exception_table: ExceptionTable) -> ExceptionTable { 1098 (**self).map_exception_table(exception_table) 1099 } 1100 1101 fn map_block_call(&mut self, block_call: BlockCall) -> BlockCall { 1102 (**self).map_block_call(block_call) 1103 } 1104 1105 fn map_func_ref(&mut self, func_ref: FuncRef) -> FuncRef { 1106 (**self).map_func_ref(func_ref) 1107 } 1108 1109 fn map_sig_ref(&mut self, sig_ref: SigRef) -> SigRef { 1110 (**self).map_sig_ref(sig_ref) 1111 } 1112 1113 fn map_stack_slot(&mut self, stack_slot: StackSlot) -> StackSlot { 1114 (**self).map_stack_slot(stack_slot) 1115 } 1116 1117 fn map_dynamic_stack_slot( 1118 &mut self, 1119 dynamic_stack_slot: ir::DynamicStackSlot, 1120 ) -> ir::DynamicStackSlot { 1121 (**self).map_dynamic_stack_slot(dynamic_stack_slot) 1122 } 1123 1124 fn map_constant(&mut self, constant: ir::Constant) -> ir::Constant { 1125 (**self).map_constant(constant) 1126 } 1127 1128 fn map_immediate(&mut self, immediate: ir::Immediate) -> ir::Immediate { 1129 (**self).map_immediate(immediate) 1130 } 1131 } 1132 1133 #[cfg(test)] 1134 mod tests { 1135 use super::*; 1136 use alloc::string::ToString; 1137 use ir::{DynamicStackSlot, GlobalValue, JumpTable}; 1138 1139 #[test] 1140 fn inst_data_is_copy() { 1141 fn is_copy<T: Copy>() {} 1142 is_copy::<InstructionData>(); 1143 } 1144 1145 #[test] 1146 fn inst_data_size() { 1147 // The size of `InstructionData` is performance sensitive, so make sure 1148 // we don't regress it unintentionally. 1149 assert_eq!(std::mem::size_of::<InstructionData>(), 16); 1150 } 1151 1152 #[test] 1153 fn opcodes() { 1154 use core::mem; 1155 1156 let x = Opcode::Iadd; 1157 let mut y = Opcode::Isub; 1158 1159 assert!(x != y); 1160 y = Opcode::Iadd; 1161 assert_eq!(x, y); 1162 assert_eq!(x.format(), InstructionFormat::Binary); 1163 1164 assert_eq!(format!("{:?}", Opcode::IaddImm), "IaddImm"); 1165 assert_eq!(Opcode::IaddImm.to_string(), "iadd_imm"); 1166 1167 // Check the matcher. 1168 assert_eq!("iadd".parse::<Opcode>(), Ok(Opcode::Iadd)); 1169 assert_eq!("iadd_imm".parse::<Opcode>(), Ok(Opcode::IaddImm)); 1170 assert_eq!("iadd\0".parse::<Opcode>(), Err("Unknown opcode")); 1171 assert_eq!("".parse::<Opcode>(), Err("Unknown opcode")); 1172 assert_eq!("\0".parse::<Opcode>(), Err("Unknown opcode")); 1173 1174 // Opcode is a single byte, and because Option<Opcode> originally came to 2 bytes, early on 1175 // Opcode included a variant NotAnOpcode to avoid the unnecessary bloat. Since then the Rust 1176 // compiler has brought in NonZero optimization, meaning that an enum not using the 0 value 1177 // can be optional for no size cost. We want to ensure Option<Opcode> remains small. 1178 assert_eq!(mem::size_of::<Opcode>(), mem::size_of::<Option<Opcode>>()); 1179 } 1180 1181 #[test] 1182 fn instruction_data() { 1183 use core::mem; 1184 // The size of the `InstructionData` enum is important for performance. It should not 1185 // exceed 16 bytes. Use `Box<FooData>` out-of-line payloads for instruction formats that 1186 // require more space than that. It would be fine with a data structure smaller than 16 1187 // bytes, but what are the odds of that? 1188 assert_eq!(mem::size_of::<InstructionData>(), 16); 1189 } 1190 1191 #[test] 1192 fn constraints() { 1193 let a = Opcode::Iadd.constraints(); 1194 assert!(a.use_typevar_operand()); 1195 assert!(!a.requires_typevar_operand()); 1196 assert_eq!(a.num_fixed_results(), 1); 1197 assert_eq!(a.num_fixed_value_arguments(), 2); 1198 assert_eq!(a.result_type(0, types::I32), types::I32); 1199 assert_eq!(a.result_type(0, types::I8), types::I8); 1200 assert_eq!( 1201 a.value_argument_constraint(0, types::I32), 1202 ResolvedConstraint::Bound(types::I32) 1203 ); 1204 assert_eq!( 1205 a.value_argument_constraint(1, types::I32), 1206 ResolvedConstraint::Bound(types::I32) 1207 ); 1208 1209 let b = Opcode::Bitcast.constraints(); 1210 assert!(!b.use_typevar_operand()); 1211 assert!(!b.requires_typevar_operand()); 1212 assert_eq!(b.num_fixed_results(), 1); 1213 assert_eq!(b.num_fixed_value_arguments(), 1); 1214 assert_eq!(b.result_type(0, types::I32), types::I32); 1215 assert_eq!(b.result_type(0, types::I8), types::I8); 1216 match b.value_argument_constraint(0, types::I32) { 1217 ResolvedConstraint::Free(vts) => assert!(vts.contains(types::F32)), 1218 _ => panic!("Unexpected constraint from value_argument_constraint"), 1219 } 1220 1221 let c = Opcode::Call.constraints(); 1222 assert_eq!(c.num_fixed_results(), 0); 1223 assert_eq!(c.num_fixed_value_arguments(), 0); 1224 1225 let i = Opcode::CallIndirect.constraints(); 1226 assert_eq!(i.num_fixed_results(), 0); 1227 assert_eq!(i.num_fixed_value_arguments(), 1); 1228 1229 let cmp = Opcode::Icmp.constraints(); 1230 assert!(cmp.use_typevar_operand()); 1231 assert!(cmp.requires_typevar_operand()); 1232 assert_eq!(cmp.num_fixed_results(), 1); 1233 assert_eq!(cmp.num_fixed_value_arguments(), 2); 1234 assert_eq!(cmp.result_type(0, types::I64), types::I8); 1235 } 1236 1237 #[test] 1238 fn value_set() { 1239 use crate::ir::types::*; 1240 1241 let vts = ValueTypeSet { 1242 lanes: BitSet16::from_range(0, 8), 1243 ints: BitSet8::from_range(4, 7), 1244 floats: BitSet8::from_range(0, 0), 1245 dynamic_lanes: BitSet16::from_range(0, 4), 1246 }; 1247 assert!(!vts.contains(I8)); 1248 assert!(vts.contains(I32)); 1249 assert!(vts.contains(I64)); 1250 assert!(vts.contains(I32X4)); 1251 assert!(vts.contains(I32X4XN)); 1252 assert!(!vts.contains(F16)); 1253 assert!(!vts.contains(F32)); 1254 assert!(!vts.contains(F128)); 1255 assert_eq!(vts.example().to_string(), "i32"); 1256 1257 let vts = ValueTypeSet { 1258 lanes: BitSet16::from_range(0, 8), 1259 ints: BitSet8::from_range(0, 0), 1260 floats: BitSet8::from_range(5, 7), 1261 dynamic_lanes: BitSet16::from_range(0, 8), 1262 }; 1263 assert_eq!(vts.example().to_string(), "f32"); 1264 1265 let vts = ValueTypeSet { 1266 lanes: BitSet16::from_range(1, 8), 1267 ints: BitSet8::from_range(0, 0), 1268 floats: BitSet8::from_range(5, 7), 1269 dynamic_lanes: BitSet16::from_range(0, 8), 1270 }; 1271 assert_eq!(vts.example().to_string(), "f32x2"); 1272 1273 let vts = ValueTypeSet { 1274 lanes: BitSet16::from_range(2, 8), 1275 ints: BitSet8::from_range(3, 7), 1276 floats: BitSet8::from_range(0, 0), 1277 dynamic_lanes: BitSet16::from_range(0, 8), 1278 }; 1279 assert_eq!(vts.example().to_string(), "i32x4"); 1280 1281 let vts = ValueTypeSet { 1282 // TypeSet(lanes=(1, 256), ints=(8, 64)) 1283 lanes: BitSet16::from_range(0, 9), 1284 ints: BitSet8::from_range(3, 7), 1285 floats: BitSet8::from_range(0, 0), 1286 dynamic_lanes: BitSet16::from_range(0, 8), 1287 }; 1288 assert!(vts.contains(I32)); 1289 assert!(vts.contains(I32X4)); 1290 } 1291 1292 #[test] 1293 fn instruction_data_map() { 1294 struct TestMapper; 1295 1296 impl InstructionMapper for TestMapper { 1297 fn map_value(&mut self, value: Value) -> Value { 1298 Value::from_u32(value.as_u32() + 1) 1299 } 1300 1301 fn map_value_list(&mut self, _value_list: ValueList) -> ValueList { 1302 ValueList::new() 1303 } 1304 1305 fn map_global_value(&mut self, global_value: ir::GlobalValue) -> ir::GlobalValue { 1306 GlobalValue::from_u32(global_value.as_u32() + 1) 1307 } 1308 1309 fn map_jump_table(&mut self, jump_table: ir::JumpTable) -> ir::JumpTable { 1310 JumpTable::from_u32(jump_table.as_u32() + 1) 1311 } 1312 1313 fn map_exception_table(&mut self, exception_table: ExceptionTable) -> ExceptionTable { 1314 ExceptionTable::from_u32(exception_table.as_u32() + 1) 1315 } 1316 1317 fn map_block_call(&mut self, _block_call: BlockCall) -> BlockCall { 1318 let block = Block::from_u32(42); 1319 let mut pool = ValueListPool::new(); 1320 BlockCall::new(block, [], &mut pool) 1321 } 1322 1323 fn map_func_ref(&mut self, func_ref: FuncRef) -> FuncRef { 1324 FuncRef::from_u32(func_ref.as_u32() + 1) 1325 } 1326 1327 fn map_sig_ref(&mut self, sig_ref: SigRef) -> SigRef { 1328 SigRef::from_u32(sig_ref.as_u32() + 1) 1329 } 1330 1331 fn map_stack_slot(&mut self, stack_slot: StackSlot) -> StackSlot { 1332 StackSlot::from_u32(stack_slot.as_u32() + 1) 1333 } 1334 1335 fn map_dynamic_stack_slot( 1336 &mut self, 1337 dynamic_stack_slot: ir::DynamicStackSlot, 1338 ) -> ir::DynamicStackSlot { 1339 DynamicStackSlot::from_u32(dynamic_stack_slot.as_u32() + 1) 1340 } 1341 1342 fn map_constant(&mut self, constant: ir::Constant) -> ir::Constant { 1343 ir::Constant::from_u32(constant.as_u32() + 1) 1344 } 1345 1346 fn map_immediate(&mut self, immediate: ir::Immediate) -> ir::Immediate { 1347 ir::Immediate::from_u32(immediate.as_u32() + 1) 1348 } 1349 } 1350 1351 let mut pool = ValueListPool::new(); 1352 let map = |inst: InstructionData| inst.map(TestMapper); 1353 1354 // Mapping `Value`s. 1355 assert_eq!( 1356 map(InstructionData::Binary { 1357 opcode: Opcode::Iadd, 1358 args: [Value::from_u32(10), Value::from_u32(20)] 1359 }), 1360 InstructionData::Binary { 1361 opcode: Opcode::Iadd, 1362 args: [Value::from_u32(11), Value::from_u32(21)] 1363 } 1364 ); 1365 1366 // Mapping `ValueList`s and `FuncRef`s. 1367 let mut args = ValueList::new(); 1368 args.push(Value::from_u32(42), &mut pool); 1369 let func_ref = FuncRef::from_u32(99); 1370 let inst = map(InstructionData::Call { 1371 opcode: Opcode::Call, 1372 args, 1373 func_ref, 1374 }); 1375 let InstructionData::Call { 1376 opcode: Opcode::Call, 1377 args, 1378 func_ref, 1379 } = inst 1380 else { 1381 panic!() 1382 }; 1383 assert!(args.is_empty()); 1384 assert_eq!(func_ref, FuncRef::from_u32(100)); 1385 1386 // Mapping `GlobalValue`s. 1387 assert_eq!( 1388 map(InstructionData::UnaryGlobalValue { 1389 opcode: Opcode::GlobalValue, 1390 global_value: GlobalValue::from_u32(4), 1391 }), 1392 InstructionData::UnaryGlobalValue { 1393 opcode: Opcode::GlobalValue, 1394 global_value: GlobalValue::from_u32(5), 1395 } 1396 ); 1397 1398 // Mapping `JumpTable`s. 1399 assert_eq!( 1400 map(InstructionData::BranchTable { 1401 opcode: Opcode::BrTable, 1402 arg: Value::from_u32(0), 1403 table: JumpTable::from_u32(1), 1404 }), 1405 InstructionData::BranchTable { 1406 opcode: Opcode::BrTable, 1407 arg: Value::from_u32(1), 1408 table: JumpTable::from_u32(2), 1409 } 1410 ); 1411 1412 // Mapping `ExceptionTable`s. 1413 assert_eq!( 1414 map(InstructionData::TryCall { 1415 opcode: Opcode::TryCall, 1416 args, 1417 func_ref: FuncRef::from_u32(0), 1418 exception: ExceptionTable::from_u32(1), 1419 }), 1420 InstructionData::TryCall { 1421 opcode: Opcode::TryCall, 1422 args, 1423 func_ref: FuncRef::from_u32(1), 1424 exception: ExceptionTable::from_u32(2), 1425 } 1426 ); 1427 1428 // Mapping `BlockCall`s. 1429 assert_eq!( 1430 map(InstructionData::Jump { 1431 opcode: Opcode::Jump, 1432 destination: BlockCall::new(Block::from_u32(99), [], &mut pool), 1433 }), 1434 map(InstructionData::Jump { 1435 opcode: Opcode::Jump, 1436 destination: BlockCall::new(Block::from_u32(42), [], &mut pool), 1437 }) 1438 ); 1439 1440 // Mapping `SigRef`s. 1441 assert_eq!( 1442 map(InstructionData::CallIndirect { 1443 opcode: Opcode::CallIndirect, 1444 args, 1445 sig_ref: SigRef::from_u32(11) 1446 }), 1447 InstructionData::CallIndirect { 1448 opcode: Opcode::CallIndirect, 1449 args: ValueList::new(), 1450 sig_ref: SigRef::from_u32(12) 1451 } 1452 ); 1453 1454 // Mapping `StackSlot`s. 1455 assert_eq!( 1456 map(InstructionData::StackLoad { 1457 opcode: Opcode::StackLoad, 1458 stack_slot: StackSlot::from_u32(0), 1459 offset: 0.into() 1460 }), 1461 InstructionData::StackLoad { 1462 opcode: Opcode::StackLoad, 1463 stack_slot: StackSlot::from_u32(1), 1464 offset: 0.into() 1465 }, 1466 ); 1467 1468 // Mapping `DynamicStackSlot`s. 1469 assert_eq!( 1470 map(InstructionData::DynamicStackLoad { 1471 opcode: Opcode::DynamicStackLoad, 1472 dynamic_stack_slot: DynamicStackSlot::from_u32(0), 1473 }), 1474 InstructionData::DynamicStackLoad { 1475 opcode: Opcode::DynamicStackLoad, 1476 dynamic_stack_slot: DynamicStackSlot::from_u32(1), 1477 }, 1478 ); 1479 1480 // Mapping `Constant`s 1481 assert_eq!( 1482 map(InstructionData::UnaryConst { 1483 opcode: ir::Opcode::Vconst, 1484 constant_handle: ir::Constant::from_u32(2) 1485 }), 1486 InstructionData::UnaryConst { 1487 opcode: ir::Opcode::Vconst, 1488 constant_handle: ir::Constant::from_u32(3) 1489 }, 1490 ); 1491 1492 // Mapping `Immediate`s 1493 assert_eq!( 1494 map(InstructionData::Shuffle { 1495 opcode: ir::Opcode::Shuffle, 1496 args: [Value::from_u32(0), Value::from_u32(1)], 1497 imm: ir::Immediate::from_u32(41), 1498 }), 1499 InstructionData::Shuffle { 1500 opcode: ir::Opcode::Shuffle, 1501 args: [Value::from_u32(1), Value::from_u32(2)], 1502 imm: ir::Immediate::from_u32(42), 1503 }, 1504 ); 1505 } 1506 } 1507