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