1 use crate::abi::{self, align_to, scratch, LocalSlot}; 2 use crate::codegen::{CodeGenContext, Emission, FuncEnv}; 3 use crate::isa::{ 4 reg::{writable, Reg, WritableReg}, 5 CallingConvention, 6 }; 7 use anyhow::Result; 8 use cranelift_codegen::{ 9 binemit::CodeOffset, 10 ir::{Endianness, LibCall, MemFlags, RelSourceLoc, SourceLoc, UserExternalNameRef}, 11 Final, MachBufferFinalized, MachLabel, 12 }; 13 use std::{fmt::Debug, ops::Range}; 14 use wasmtime_environ::PtrSize; 15 16 pub(crate) use cranelift_codegen::ir::TrapCode; 17 18 #[derive(Eq, PartialEq)] 19 pub(crate) enum DivKind { 20 /// Signed division. 21 Signed, 22 /// Unsigned division. 23 Unsigned, 24 } 25 26 /// Remainder kind. 27 #[derive(Copy, Clone)] 28 pub(crate) enum RemKind { 29 /// Signed remainder. 30 Signed, 31 /// Unsigned remainder. 32 Unsigned, 33 } 34 35 impl RemKind { 36 pub fn is_signed(&self) -> bool { 37 matches!(self, Self::Signed) 38 } 39 } 40 41 #[derive(Copy, Clone, PartialEq, Eq)] 42 pub(crate) enum MemOpKind { 43 /// An atomic memory operation with SeqCst memory ordering. 44 Atomic, 45 /// A memory operation with no memory ordering constraint. 46 Normal, 47 } 48 49 #[derive(Eq, PartialEq)] 50 pub(crate) enum MulWideKind { 51 Signed, 52 Unsigned, 53 } 54 55 /// Type of operation for a read-modify-write instruction. 56 pub(crate) enum RmwOp { 57 Add, 58 Sub, 59 Xchg, 60 And, 61 Or, 62 Xor, 63 } 64 65 /// The direction to perform the memory move. 66 #[derive(Debug, Clone, Eq, PartialEq)] 67 pub(crate) enum MemMoveDirection { 68 /// From high memory addresses to low memory addresses. 69 /// Invariant: the source location is closer to the FP than the destination 70 /// location, which will be closer to the SP. 71 HighToLow, 72 /// From low memory addresses to high memory addresses. 73 /// Invariant: the source location is closer to the SP than the destination 74 /// location, which will be closer to the FP. 75 LowToHigh, 76 } 77 78 /// Classifies how to treat float-to-int conversions. 79 #[derive(Debug, Copy, Clone, Eq, PartialEq)] 80 pub(crate) enum TruncKind { 81 /// Saturating conversion. If the source value is greater than the maximum 82 /// value of the destination type, the result is clamped to the 83 /// destination maximum value. 84 Checked, 85 /// An exception is raised if the source value is greater than the maximum 86 /// value of the destination type. 87 Unchecked, 88 } 89 90 impl TruncKind { 91 /// Returns true if the truncation kind is checked. 92 pub(crate) fn is_checked(&self) -> bool { 93 *self == TruncKind::Checked 94 } 95 96 /// Returns `true` if the trunc kind is [`Unchecked`]. 97 /// 98 /// [`Unchecked`]: TruncKind::Unchecked 99 #[must_use] 100 pub(crate) fn is_unchecked(&self) -> bool { 101 matches!(self, Self::Unchecked) 102 } 103 } 104 105 /// Representation of the stack pointer offset. 106 #[derive(Copy, Clone, Eq, PartialEq, Debug, PartialOrd, Ord, Default)] 107 pub struct SPOffset(u32); 108 109 impl SPOffset { 110 pub fn from_u32(offs: u32) -> Self { 111 Self(offs) 112 } 113 114 pub fn as_u32(&self) -> u32 { 115 self.0 116 } 117 } 118 119 /// A stack slot. 120 #[derive(Debug, Clone, Copy, Eq, PartialEq)] 121 pub struct StackSlot { 122 /// The location of the slot, relative to the stack pointer. 123 pub offset: SPOffset, 124 /// The size of the slot, in bytes. 125 pub size: u32, 126 } 127 128 impl StackSlot { 129 pub fn new(offs: SPOffset, size: u32) -> Self { 130 Self { offset: offs, size } 131 } 132 } 133 134 /// Kinds of integer binary comparison in WebAssembly. The [`MacroAssembler`] 135 /// implementation for each ISA is responsible for emitting the correct 136 /// sequence of instructions when lowering to machine code. 137 #[derive(Debug, Clone, Copy, Eq, PartialEq)] 138 pub(crate) enum IntCmpKind { 139 /// Equal. 140 Eq, 141 /// Not equal. 142 Ne, 143 /// Signed less than. 144 LtS, 145 /// Unsigned less than. 146 LtU, 147 /// Signed greater than. 148 GtS, 149 /// Unsigned greater than. 150 GtU, 151 /// Signed less than or equal. 152 LeS, 153 /// Unsigned less than or equal. 154 LeU, 155 /// Signed greater than or equal. 156 GeS, 157 /// Unsigned greater than or equal. 158 GeU, 159 } 160 161 /// Kinds of float binary comparison in WebAssembly. The [`MacroAssembler`] 162 /// implementation for each ISA is responsible for emitting the correct 163 /// sequence of instructions when lowering code. 164 #[derive(Debug)] 165 pub(crate) enum FloatCmpKind { 166 /// Equal. 167 Eq, 168 /// Not equal. 169 Ne, 170 /// Less than. 171 Lt, 172 /// Greater than. 173 Gt, 174 /// Less than or equal. 175 Le, 176 /// Greater than or equal. 177 Ge, 178 } 179 180 /// Kinds of shifts in WebAssembly.The [`masm`] implementation for each ISA is 181 /// responsible for emitting the correct sequence of instructions when 182 /// lowering to machine code. 183 #[derive(Debug, Clone, Copy, Eq, PartialEq)] 184 pub(crate) enum ShiftKind { 185 /// Left shift. 186 Shl, 187 /// Signed right shift. 188 ShrS, 189 /// Unsigned right shift. 190 ShrU, 191 /// Left rotate. 192 Rotl, 193 /// Right rotate. 194 Rotr, 195 } 196 197 /// Kinds of extends in WebAssembly. Each MacroAssembler implementation 198 /// is responsible for emitting the correct sequence of instructions when 199 /// lowering to machine code. 200 #[derive(Copy, Clone)] 201 pub(crate) enum ExtendKind { 202 Signed(Extend<Signed>), 203 Unsigned(Extend<Zero>), 204 } 205 206 #[derive(Copy, Clone)] 207 pub(crate) enum Signed {} 208 #[derive(Copy, Clone)] 209 pub(crate) enum Zero {} 210 211 pub(crate) trait ExtendType {} 212 213 impl ExtendType for Signed {} 214 impl ExtendType for Zero {} 215 216 #[derive(Copy, Clone)] 217 pub(crate) enum Extend<T: ExtendType> { 218 /// 8 to 32 bit extend. 219 I32Extend8, 220 /// 16 to 32 bit extend. 221 I32Extend16, 222 /// 8 to 64 bit extend. 223 I64Extend8, 224 /// 16 to 64 bit extend. 225 I64Extend16, 226 /// 32 to 64 bit extend. 227 I64Extend32, 228 229 /// Variant to hold the kind of extend marker. 230 /// 231 /// This is `Signed` or `Zero`, that are empty enums, which means that this variant cannot be 232 /// constructed. 233 __Kind(T), 234 } 235 236 impl From<Extend<Zero>> for ExtendKind { 237 fn from(value: Extend<Zero>) -> Self { 238 ExtendKind::Unsigned(value) 239 } 240 } 241 242 impl<T: ExtendType> Extend<T> { 243 pub fn from_size(&self) -> OperandSize { 244 match self { 245 Extend::I32Extend8 | Extend::I64Extend8 => OperandSize::S8, 246 Extend::I32Extend16 | Extend::I64Extend16 => OperandSize::S16, 247 Extend::I64Extend32 => OperandSize::S32, 248 Extend::__Kind(_) => unreachable!(), 249 } 250 } 251 252 pub fn to_size(&self) -> OperandSize { 253 match self { 254 Extend::I32Extend8 | Extend::I32Extend16 => OperandSize::S32, 255 Extend::I64Extend8 | Extend::I64Extend16 | Extend::I64Extend32 => OperandSize::S64, 256 Extend::__Kind(_) => unreachable!(), 257 } 258 } 259 260 pub fn from_bits(&self) -> u8 { 261 self.from_size().num_bits() 262 } 263 264 pub fn to_bits(&self) -> u8 { 265 self.to_size().num_bits() 266 } 267 } 268 269 impl From<Extend<Signed>> for ExtendKind { 270 fn from(value: Extend<Signed>) -> Self { 271 ExtendKind::Signed(value) 272 } 273 } 274 275 impl ExtendKind { 276 pub fn signed(&self) -> bool { 277 match self { 278 Self::Signed(_) => true, 279 _ => false, 280 } 281 } 282 283 pub fn from_bits(&self) -> u8 { 284 match self { 285 Self::Signed(s) => s.from_bits(), 286 Self::Unsigned(u) => u.from_bits(), 287 } 288 } 289 290 pub fn to_bits(&self) -> u8 { 291 match self { 292 Self::Signed(s) => s.to_bits(), 293 Self::Unsigned(u) => u.to_bits(), 294 } 295 } 296 } 297 298 /// Kinds of vector extends in WebAssembly. Each MacroAssembler implementation 299 /// is responsible for emitting the correct sequence of instructions when 300 /// lowering to machine code. 301 pub(crate) enum VectorExtendKind { 302 /// Sign extends eight 8 bit integers to eight 16 bit lanes. 303 V128Extend8x8S, 304 /// Zero extends eight 8 bit integers to eight 16 bit lanes. 305 V128Extend8x8U, 306 /// Sign extends four 16 bit integers to four 32 bit lanes. 307 V128Extend16x4S, 308 /// Zero extends four 16 bit integers to four 32 bit lanes. 309 V128Extend16x4U, 310 /// Sign extends two 32 bit integers to two 64 bit lanes. 311 V128Extend32x2S, 312 /// Zero extends two 32 bit integers to two 64 bit lanes. 313 V128Extend32x2U, 314 } 315 316 /// Kinds of splat loads supported by WebAssembly. 317 pub(crate) enum SplatLoadKind { 318 /// 8 bits. 319 S8, 320 /// 16 bits. 321 S16, 322 /// 32 bits. 323 S32, 324 /// 64 bits. 325 S64, 326 } 327 328 /// Kinds of splat supported by WebAssembly. 329 #[derive(Copy, Debug, Clone, Eq, PartialEq)] 330 pub(crate) enum SplatKind { 331 /// 8 bit integer. 332 I8x16, 333 /// 16 bit integer. 334 I16x8, 335 /// 32 bit integer. 336 I32x4, 337 /// 64 bit integer. 338 I64x2, 339 /// 32 bit float. 340 F32x4, 341 /// 64 bit float. 342 F64x2, 343 } 344 345 impl SplatKind { 346 /// The lane size to use for different kinds of splats. 347 pub(crate) fn lane_size(&self) -> OperandSize { 348 match self { 349 SplatKind::I8x16 => OperandSize::S8, 350 SplatKind::I16x8 => OperandSize::S16, 351 SplatKind::I32x4 | SplatKind::F32x4 => OperandSize::S32, 352 SplatKind::I64x2 | SplatKind::F64x2 => OperandSize::S64, 353 } 354 } 355 } 356 357 /// Kinds of extract lane supported by WebAssembly. 358 #[derive(Copy, Debug, Clone, Eq, PartialEq)] 359 pub(crate) enum ExtractLaneKind { 360 /// 16 lanes of 8-bit integers sign extended to 32-bits. 361 I8x16S, 362 /// 16 lanes of 8-bit integers zero extended to 32-bits. 363 I8x16U, 364 /// 8 lanes of 16-bit integers sign extended to 32-bits. 365 I16x8S, 366 /// 8 lanes of 16-bit integers zero extended to 32-bits. 367 I16x8U, 368 /// 4 lanes of 32-bit integers. 369 I32x4, 370 /// 2 lanes of 64-bit integers. 371 I64x2, 372 /// 4 lanes of 32-bit floats. 373 F32x4, 374 /// 2 lanes of 64-bit floats. 375 F64x2, 376 } 377 378 impl ExtractLaneKind { 379 /// The lane size to use for different kinds of extract lane kinds. 380 pub(crate) fn lane_size(&self) -> OperandSize { 381 match self { 382 ExtractLaneKind::I8x16S | ExtractLaneKind::I8x16U => OperandSize::S8, 383 ExtractLaneKind::I16x8S | ExtractLaneKind::I16x8U => OperandSize::S16, 384 ExtractLaneKind::I32x4 | ExtractLaneKind::F32x4 => OperandSize::S32, 385 ExtractLaneKind::I64x2 | ExtractLaneKind::F64x2 => OperandSize::S64, 386 } 387 } 388 } 389 390 impl From<ExtractLaneKind> for Extend<Signed> { 391 fn from(value: ExtractLaneKind) -> Self { 392 match value { 393 ExtractLaneKind::I8x16S => Extend::I32Extend8, 394 ExtractLaneKind::I16x8S => Extend::I32Extend16, 395 _ => unimplemented!(), 396 } 397 } 398 } 399 400 /// Kinds of behavior supported by Wasm loads. 401 pub(crate) enum LoadKind { 402 /// Load the entire bytes of the operand size without any modifications. 403 Operand(OperandSize), 404 /// Duplicate value into vector lanes. 405 Splat(SplatLoadKind), 406 /// Scalar (non-vector) extend. 407 ScalarExtend(ExtendKind), 408 /// Vector extend. 409 VectorExtend(VectorExtendKind), 410 } 411 412 impl LoadKind { 413 /// Returns the [`OperandSize`] used in the load operation. 414 pub(crate) fn derive_operand_size(&self) -> OperandSize { 415 match self { 416 Self::ScalarExtend(scalar) => Self::operand_size_for_scalar(scalar), 417 Self::VectorExtend(vector) => Self::operand_size_for_vector(vector), 418 Self::Splat(kind) => Self::operand_size_for_splat(kind), 419 Self::Operand(op) => *op, 420 } 421 } 422 423 fn operand_size_for_vector(vector: &VectorExtendKind) -> OperandSize { 424 match vector { 425 VectorExtendKind::V128Extend8x8S | VectorExtendKind::V128Extend8x8U => OperandSize::S8, 426 VectorExtendKind::V128Extend16x4S | VectorExtendKind::V128Extend16x4U => { 427 OperandSize::S16 428 } 429 VectorExtendKind::V128Extend32x2S | VectorExtendKind::V128Extend32x2U => { 430 OperandSize::S32 431 } 432 } 433 } 434 435 fn operand_size_for_scalar(extend_kind: &ExtendKind) -> OperandSize { 436 match extend_kind { 437 ExtendKind::Signed(s) => s.from_size(), 438 ExtendKind::Unsigned(u) => u.from_size(), 439 } 440 } 441 442 fn operand_size_for_splat(kind: &SplatLoadKind) -> OperandSize { 443 match kind { 444 SplatLoadKind::S8 => OperandSize::S8, 445 SplatLoadKind::S16 => OperandSize::S16, 446 SplatLoadKind::S32 => OperandSize::S32, 447 SplatLoadKind::S64 => OperandSize::S64, 448 } 449 } 450 } 451 452 /// Operand size, in bits. 453 #[derive(Copy, Debug, Clone, Eq, PartialEq)] 454 pub(crate) enum OperandSize { 455 /// 8 bits. 456 S8, 457 /// 16 bits. 458 S16, 459 /// 32 bits. 460 S32, 461 /// 64 bits. 462 S64, 463 /// 128 bits. 464 S128, 465 } 466 467 impl OperandSize { 468 /// The number of bits in the operand. 469 pub fn num_bits(&self) -> u8 { 470 match self { 471 OperandSize::S8 => 8, 472 OperandSize::S16 => 16, 473 OperandSize::S32 => 32, 474 OperandSize::S64 => 64, 475 OperandSize::S128 => 128, 476 } 477 } 478 479 /// The number of bytes in the operand. 480 pub fn bytes(&self) -> u32 { 481 match self { 482 Self::S8 => 1, 483 Self::S16 => 2, 484 Self::S32 => 4, 485 Self::S64 => 8, 486 Self::S128 => 16, 487 } 488 } 489 490 /// The binary logarithm of the number of bits in the operand. 491 pub fn log2(&self) -> u8 { 492 match self { 493 OperandSize::S8 => 3, 494 OperandSize::S16 => 4, 495 OperandSize::S32 => 5, 496 OperandSize::S64 => 6, 497 OperandSize::S128 => 7, 498 } 499 } 500 501 /// Create an [`OperandSize`] from the given number of bytes. 502 pub fn from_bytes(bytes: u8) -> Self { 503 use OperandSize::*; 504 match bytes { 505 4 => S32, 506 8 => S64, 507 16 => S128, 508 _ => panic!("Invalid bytes {bytes} for OperandSize"), 509 } 510 } 511 512 pub fn extend_to<T: ExtendType>(&self, to: Self) -> Option<Extend<T>> { 513 match to { 514 OperandSize::S32 => match self { 515 OperandSize::S8 => Some(Extend::I32Extend8), 516 OperandSize::S16 => Some(Extend::I32Extend16), 517 _ => None, 518 }, 519 OperandSize::S64 => match self { 520 OperandSize::S8 => Some(Extend::I64Extend8), 521 OperandSize::S16 => Some(Extend::I64Extend16), 522 OperandSize::S32 => Some(Extend::I64Extend32), 523 _ => None, 524 }, 525 _ => None, 526 } 527 } 528 } 529 530 /// An abstraction over a register or immediate. 531 #[derive(Copy, Clone, Debug, PartialEq, Eq)] 532 pub(crate) enum RegImm { 533 /// A register. 534 Reg(Reg), 535 /// A tagged immediate argument. 536 Imm(Imm), 537 } 538 539 /// An tagged representation of an immediate. 540 #[derive(Copy, Clone, Debug, PartialEq, Eq)] 541 pub(crate) enum Imm { 542 /// I32 immediate. 543 I32(u32), 544 /// I64 immediate. 545 I64(u64), 546 /// F32 immediate. 547 F32(u32), 548 /// F64 immediate. 549 F64(u64), 550 /// V128 immediate. 551 V128(i128), 552 } 553 554 impl Imm { 555 /// Create a new I64 immediate. 556 pub fn i64(val: i64) -> Self { 557 Self::I64(val as u64) 558 } 559 560 /// Create a new I32 immediate. 561 pub fn i32(val: i32) -> Self { 562 Self::I32(val as u32) 563 } 564 565 /// Create a new F32 immediate. 566 pub fn f32(bits: u32) -> Self { 567 Self::F32(bits) 568 } 569 570 /// Create a new F64 immediate. 571 pub fn f64(bits: u64) -> Self { 572 Self::F64(bits) 573 } 574 575 /// Create a new V128 immediate. 576 pub fn v128(bits: i128) -> Self { 577 Self::V128(bits) 578 } 579 580 /// Convert the immediate to i32, if possible. 581 pub fn to_i32(&self) -> Option<i32> { 582 match self { 583 Self::I32(v) => Some(*v as i32), 584 Self::I64(v) => i32::try_from(*v as i64).ok(), 585 _ => None, 586 } 587 } 588 589 /// Returns true if the [`Imm`] is float. 590 pub fn is_float(&self) -> bool { 591 match self { 592 Self::F32(_) | Self::F64(_) => true, 593 _ => false, 594 } 595 } 596 597 /// Get the operand size of the immediate. 598 pub fn size(&self) -> OperandSize { 599 match self { 600 Self::I32(_) | Self::F32(_) => OperandSize::S32, 601 Self::I64(_) | Self::F64(_) => OperandSize::S64, 602 Self::V128(_) => OperandSize::S128, 603 } 604 } 605 606 /// Get a little endian representation of the immediate. 607 /// 608 /// This method heap allocates and is intended to be used when adding 609 /// values to the constant pool. 610 pub fn to_bytes(&self) -> Vec<u8> { 611 match self { 612 Imm::I32(n) => n.to_le_bytes().to_vec(), 613 Imm::I64(n) => n.to_le_bytes().to_vec(), 614 Imm::F32(n) => n.to_le_bytes().to_vec(), 615 Imm::F64(n) => n.to_le_bytes().to_vec(), 616 Imm::V128(n) => n.to_le_bytes().to_vec(), 617 } 618 } 619 } 620 621 /// The location of the [VMcontext] used for function calls. 622 #[derive(Copy, Clone, Debug, Eq, PartialEq)] 623 pub(crate) enum VMContextLoc { 624 /// Dynamic, stored in the given register. 625 Reg(Reg), 626 /// The pinned [VMContext] register. 627 Pinned, 628 } 629 630 /// The maximum number of context arguments currently used across the compiler. 631 pub(crate) const MAX_CONTEXT_ARGS: usize = 2; 632 633 /// Out-of-band special purpose arguments used for function call emission. 634 /// 635 /// We cannot rely on the value stack for these values given that inserting 636 /// register or memory values at arbitrary locations of the value stack has the 637 /// potential to break the stack ordering principle, which states that older 638 /// values must always precede newer values, effectively simulating the order of 639 /// values in the machine stack. 640 /// The [ContextArgs] are meant to be resolved at every callsite; in some cases 641 /// it might be possible to construct it early on, but given that it might 642 /// contain allocatable registers, it's preferred to construct it in 643 /// [FnCall::emit]. 644 #[derive(Clone, Debug)] 645 pub(crate) enum ContextArgs { 646 /// No context arguments required. This is used for libcalls that don't 647 /// require any special context arguments. For example builtin functions 648 /// that perform float calculations. 649 None, 650 /// A single context argument is required; the current pinned [VMcontext] 651 /// register must be passed as the first argument of the function call. 652 VMContext([VMContextLoc; 1]), 653 /// The callee and caller context arguments are required. In this case, the 654 /// callee context argument is usually stored into an allocatable register 655 /// and the caller is always the current pinned [VMContext] pointer. 656 CalleeAndCallerVMContext([VMContextLoc; MAX_CONTEXT_ARGS]), 657 } 658 659 impl ContextArgs { 660 /// Construct an empty [ContextArgs]. 661 pub fn none() -> Self { 662 Self::None 663 } 664 665 /// Construct a [ContextArgs] declaring the usage of the pinned [VMContext] 666 /// register as both the caller and callee context arguments. 667 pub fn pinned_callee_and_caller_vmctx() -> Self { 668 Self::CalleeAndCallerVMContext([VMContextLoc::Pinned, VMContextLoc::Pinned]) 669 } 670 671 /// Construct a [ContextArgs] that declares the usage of the pinned 672 /// [VMContext] register as the only context argument. 673 pub fn pinned_vmctx() -> Self { 674 Self::VMContext([VMContextLoc::Pinned]) 675 } 676 677 /// Construct a [ContextArgs] that declares a dynamic callee context and the 678 /// pinned [VMContext] register as the context arguments. 679 pub fn with_callee_and_pinned_caller(callee_vmctx: Reg) -> Self { 680 Self::CalleeAndCallerVMContext([VMContextLoc::Reg(callee_vmctx), VMContextLoc::Pinned]) 681 } 682 683 /// Get the length of the [ContextArgs]. 684 pub fn len(&self) -> usize { 685 self.as_slice().len() 686 } 687 688 /// Get a slice of the context arguments. 689 pub fn as_slice(&self) -> &[VMContextLoc] { 690 match self { 691 Self::None => &[], 692 Self::VMContext(a) => a.as_slice(), 693 Self::CalleeAndCallerVMContext(a) => a.as_slice(), 694 } 695 } 696 } 697 698 #[derive(Copy, Clone, Debug)] 699 pub(crate) enum CalleeKind { 700 /// A function call to a raw address. 701 Indirect(Reg), 702 /// A function call to a local function. 703 Direct(UserExternalNameRef), 704 /// Call to a well known LibCall. 705 LibCall(LibCall), 706 } 707 708 impl CalleeKind { 709 /// Creates a callee kind from a register. 710 pub fn indirect(reg: Reg) -> Self { 711 Self::Indirect(reg) 712 } 713 714 /// Creates a direct callee kind from a function name. 715 pub fn direct(name: UserExternalNameRef) -> Self { 716 Self::Direct(name) 717 } 718 719 /// Creates a known callee kind from a libcall. 720 pub fn libcall(call: LibCall) -> Self { 721 Self::LibCall(call) 722 } 723 } 724 725 impl RegImm { 726 /// Register constructor. 727 pub fn reg(r: Reg) -> Self { 728 RegImm::Reg(r) 729 } 730 731 /// I64 immediate constructor. 732 pub fn i64(val: i64) -> Self { 733 RegImm::Imm(Imm::i64(val)) 734 } 735 736 /// I32 immediate constructor. 737 pub fn i32(val: i32) -> Self { 738 RegImm::Imm(Imm::i32(val)) 739 } 740 741 /// F32 immediate, stored using its bits representation. 742 pub fn f32(bits: u32) -> Self { 743 RegImm::Imm(Imm::f32(bits)) 744 } 745 746 /// F64 immediate, stored using its bits representation. 747 pub fn f64(bits: u64) -> Self { 748 RegImm::Imm(Imm::f64(bits)) 749 } 750 751 /// V128 immediate. 752 pub fn v128(bits: i128) -> Self { 753 RegImm::Imm(Imm::v128(bits)) 754 } 755 } 756 757 impl From<Reg> for RegImm { 758 fn from(r: Reg) -> Self { 759 Self::Reg(r) 760 } 761 } 762 763 #[derive(Debug)] 764 pub enum RoundingMode { 765 Nearest, 766 Up, 767 Down, 768 Zero, 769 } 770 771 /// Memory flags for trusted loads/stores. 772 pub const TRUSTED_FLAGS: MemFlags = MemFlags::trusted(); 773 774 /// Flags used for WebAssembly loads / stores. 775 /// Untrusted by default so we don't set `no_trap`. 776 /// We also ensure that the endianness is the right one for WebAssembly. 777 pub const UNTRUSTED_FLAGS: MemFlags = MemFlags::new().with_endianness(Endianness::Little); 778 779 /// Generic MacroAssembler interface used by the code generation. 780 /// 781 /// The MacroAssembler trait aims to expose an interface, high-level enough, 782 /// so that each ISA can provide its own lowering to machine code. For example, 783 /// for WebAssembly operators that don't have a direct mapping to a machine 784 /// a instruction, the interface defines a signature matching the WebAssembly 785 /// operator, allowing each implementation to lower such operator entirely. 786 /// This approach attributes more responsibility to the MacroAssembler, but frees 787 /// the caller from concerning about assembling the right sequence of 788 /// instructions at the operator callsite. 789 /// 790 /// The interface defaults to a three-argument form for binary operations; 791 /// this allows a natural mapping to instructions for RISC architectures, 792 /// that use three-argument form. 793 /// This approach allows for a more general interface that can be restricted 794 /// where needed, in the case of architectures that use a two-argument form. 795 796 pub(crate) trait MacroAssembler { 797 /// The addressing mode. 798 type Address: Copy + Debug; 799 800 /// The pointer representation of the target ISA, 801 /// used to access information from [`VMOffsets`]. 802 type Ptr: PtrSize; 803 804 /// The ABI details of the target. 805 type ABI: abi::ABI; 806 807 /// Emit the function prologue. 808 fn prologue(&mut self, vmctx: Reg) -> Result<()> { 809 self.frame_setup()?; 810 self.check_stack(vmctx) 811 } 812 813 /// Generate the frame setup sequence. 814 fn frame_setup(&mut self) -> Result<()>; 815 816 /// Generate the frame restore sequence. 817 fn frame_restore(&mut self) -> Result<()>; 818 819 /// Emit a stack check. 820 fn check_stack(&mut self, vmctx: Reg) -> Result<()>; 821 822 /// Emit the function epilogue. 823 fn epilogue(&mut self) -> Result<()> { 824 self.frame_restore() 825 } 826 827 /// Reserve stack space. 828 fn reserve_stack(&mut self, bytes: u32) -> Result<()>; 829 830 /// Free stack space. 831 fn free_stack(&mut self, bytes: u32) -> Result<()>; 832 833 /// Reset the stack pointer to the given offset; 834 /// 835 /// Used to reset the stack pointer to a given offset 836 /// when dealing with unreachable code. 837 fn reset_stack_pointer(&mut self, offset: SPOffset) -> Result<()>; 838 839 /// Get the address of a local slot. 840 fn local_address(&mut self, local: &LocalSlot) -> Result<Self::Address>; 841 842 /// Constructs an address with an offset that is relative to the 843 /// current position of the stack pointer (e.g. [sp + (sp_offset - 844 /// offset)]. 845 fn address_from_sp(&self, offset: SPOffset) -> Result<Self::Address>; 846 847 /// Constructs an address with an offset that is absolute to the 848 /// current position of the stack pointer (e.g. [sp + offset]. 849 fn address_at_sp(&self, offset: SPOffset) -> Result<Self::Address>; 850 851 /// Alias for [`Self::address_at_reg`] using the VMContext register as 852 /// a base. The VMContext register is derived from the ABI type that is 853 /// associated to the MacroAssembler. 854 fn address_at_vmctx(&self, offset: u32) -> Result<Self::Address>; 855 856 /// Construct an address that is absolute to the current position 857 /// of the given register. 858 fn address_at_reg(&self, reg: Reg, offset: u32) -> Result<Self::Address>; 859 860 /// Emit a function call to either a local or external function. 861 fn call( 862 &mut self, 863 stack_args_size: u32, 864 f: impl FnMut(&mut Self) -> Result<(CalleeKind, CallingConvention)>, 865 ) -> Result<u32>; 866 867 /// Get stack pointer offset. 868 fn sp_offset(&self) -> Result<SPOffset>; 869 870 /// Perform a stack store. 871 fn store(&mut self, src: RegImm, dst: Self::Address, size: OperandSize) -> Result<()>; 872 873 /// Alias for `MacroAssembler::store` with the operand size corresponding 874 /// to the pointer size of the target. 875 fn store_ptr(&mut self, src: Reg, dst: Self::Address) -> Result<()>; 876 877 /// Perform a WebAssembly store. 878 /// A WebAssembly store introduces several additional invariants compared to 879 /// [Self::store], more precisely, it can implicitly trap, in certain 880 /// circumstances, even if explicit bounds checks are elided, in that sense, 881 /// we consider this type of load as untrusted. It can also differ with 882 /// regards to the endianness depending on the target ISA. For this reason, 883 /// [Self::wasm_store], should be explicitly used when emitting WebAssembly 884 /// stores. 885 fn wasm_store( 886 &mut self, 887 src: Reg, 888 dst: Self::Address, 889 size: OperandSize, 890 op_kind: MemOpKind, 891 ) -> Result<()>; 892 893 /// Perform a zero-extended stack load. 894 fn load(&mut self, src: Self::Address, dst: WritableReg, size: OperandSize) -> Result<()>; 895 896 /// Perform a WebAssembly load. 897 /// A WebAssembly load introduces several additional invariants compared to 898 /// [Self::load], more precisely, it can implicitly trap, in certain 899 /// circumstances, even if explicit bounds checks are elided, in that sense, 900 /// we consider this type of load as untrusted. It can also differ with 901 /// regards to the endianness depending on the target ISA. For this reason, 902 /// [Self::wasm_load], should be explicitly used when emitting WebAssembly 903 /// loads. 904 fn wasm_load( 905 &mut self, 906 src: Self::Address, 907 dst: WritableReg, 908 kind: LoadKind, 909 op_kind: MemOpKind, 910 ) -> Result<()>; 911 912 /// Alias for `MacroAssembler::load` with the operand size corresponding 913 /// to the pointer size of the target. 914 fn load_ptr(&mut self, src: Self::Address, dst: WritableReg) -> Result<()>; 915 916 /// Loads the effective address into destination. 917 fn load_addr( 918 &mut self, 919 _src: Self::Address, 920 _dst: WritableReg, 921 _size: OperandSize, 922 ) -> Result<()>; 923 924 /// Pop a value from the machine stack into the given register. 925 fn pop(&mut self, dst: WritableReg, size: OperandSize) -> Result<()>; 926 927 /// Perform a move. 928 fn mov(&mut self, dst: WritableReg, src: RegImm, size: OperandSize) -> Result<()>; 929 930 /// Perform a conditional move. 931 fn cmov(&mut self, dst: WritableReg, src: Reg, cc: IntCmpKind, size: OperandSize) 932 -> Result<()>; 933 934 /// Performs a memory move of bytes from src to dest. 935 /// Bytes are moved in blocks of 8 bytes, where possible. 936 fn memmove( 937 &mut self, 938 src: SPOffset, 939 dst: SPOffset, 940 bytes: u32, 941 direction: MemMoveDirection, 942 ) -> Result<()> { 943 match direction { 944 MemMoveDirection::LowToHigh => debug_assert!(dst.as_u32() < src.as_u32()), 945 MemMoveDirection::HighToLow => debug_assert!(dst.as_u32() > src.as_u32()), 946 } 947 // At least 4 byte aligned. 948 debug_assert!(bytes % 4 == 0); 949 let mut remaining = bytes; 950 let word_bytes = <Self::ABI as abi::ABI>::word_bytes(); 951 let scratch = scratch!(Self); 952 953 let mut dst_offs = dst.as_u32() - bytes; 954 let mut src_offs = src.as_u32() - bytes; 955 956 let word_bytes = word_bytes as u32; 957 while remaining >= word_bytes { 958 remaining -= word_bytes; 959 dst_offs += word_bytes; 960 src_offs += word_bytes; 961 962 self.load_ptr( 963 self.address_from_sp(SPOffset::from_u32(src_offs))?, 964 writable!(scratch), 965 )?; 966 self.store_ptr( 967 scratch.into(), 968 self.address_from_sp(SPOffset::from_u32(dst_offs))?, 969 )?; 970 } 971 972 if remaining > 0 { 973 let half_word = word_bytes / 2; 974 let ptr_size = OperandSize::from_bytes(half_word as u8); 975 debug_assert!(remaining == half_word); 976 dst_offs += half_word; 977 src_offs += half_word; 978 979 self.load( 980 self.address_from_sp(SPOffset::from_u32(src_offs))?, 981 writable!(scratch), 982 ptr_size, 983 )?; 984 self.store( 985 scratch.into(), 986 self.address_from_sp(SPOffset::from_u32(dst_offs))?, 987 ptr_size, 988 )?; 989 } 990 Ok(()) 991 } 992 993 /// Perform add operation. 994 fn add(&mut self, dst: WritableReg, lhs: Reg, rhs: RegImm, size: OperandSize) -> Result<()>; 995 996 /// Perform a checked unsigned integer addition, emitting the provided trap 997 /// if the addition overflows. 998 fn checked_uadd( 999 &mut self, 1000 dst: WritableReg, 1001 lhs: Reg, 1002 rhs: RegImm, 1003 size: OperandSize, 1004 trap: TrapCode, 1005 ) -> Result<()>; 1006 1007 /// Perform subtraction operation. 1008 fn sub(&mut self, dst: WritableReg, lhs: Reg, rhs: RegImm, size: OperandSize) -> Result<()>; 1009 1010 /// Perform multiplication operation. 1011 fn mul(&mut self, dst: WritableReg, lhs: Reg, rhs: RegImm, size: OperandSize) -> Result<()>; 1012 1013 /// Perform a floating point add operation. 1014 fn float_add(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize) -> Result<()>; 1015 1016 /// Perform a floating point subtraction operation. 1017 fn float_sub(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize) -> Result<()>; 1018 1019 /// Perform a floating point multiply operation. 1020 fn float_mul(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize) -> Result<()>; 1021 1022 /// Perform a floating point divide operation. 1023 fn float_div(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize) -> Result<()>; 1024 1025 /// Perform a floating point minimum operation. In x86, this will emit 1026 /// multiple instructions. 1027 fn float_min(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize) -> Result<()>; 1028 1029 /// Perform a floating point maximum operation. In x86, this will emit 1030 /// multiple instructions. 1031 fn float_max(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize) -> Result<()>; 1032 1033 /// Perform a floating point copysign operation. In x86, this will emit 1034 /// multiple instructions. 1035 fn float_copysign( 1036 &mut self, 1037 dst: WritableReg, 1038 lhs: Reg, 1039 rhs: Reg, 1040 size: OperandSize, 1041 ) -> Result<()>; 1042 1043 /// Perform a floating point abs operation. 1044 fn float_abs(&mut self, dst: WritableReg, size: OperandSize) -> Result<()>; 1045 1046 /// Perform a floating point negation operation. 1047 fn float_neg(&mut self, dst: WritableReg, size: OperandSize) -> Result<()>; 1048 1049 /// Perform a floating point floor operation. 1050 fn float_round< 1051 F: FnMut(&mut FuncEnv<Self::Ptr>, &mut CodeGenContext<Emission>, &mut Self) -> Result<()>, 1052 >( 1053 &mut self, 1054 mode: RoundingMode, 1055 env: &mut FuncEnv<Self::Ptr>, 1056 context: &mut CodeGenContext<Emission>, 1057 size: OperandSize, 1058 fallback: F, 1059 ) -> Result<()>; 1060 1061 /// Perform a floating point square root operation. 1062 fn float_sqrt(&mut self, dst: WritableReg, src: Reg, size: OperandSize) -> Result<()>; 1063 1064 /// Perform logical and operation. 1065 fn and(&mut self, dst: WritableReg, lhs: Reg, rhs: RegImm, size: OperandSize) -> Result<()>; 1066 1067 /// Perform logical or operation. 1068 fn or(&mut self, dst: WritableReg, lhs: Reg, rhs: RegImm, size: OperandSize) -> Result<()>; 1069 1070 /// Perform logical exclusive or operation. 1071 fn xor(&mut self, dst: WritableReg, lhs: Reg, rhs: RegImm, size: OperandSize) -> Result<()>; 1072 1073 /// Perform a shift operation between a register and an immediate. 1074 fn shift_ir( 1075 &mut self, 1076 dst: WritableReg, 1077 imm: u64, 1078 lhs: Reg, 1079 kind: ShiftKind, 1080 size: OperandSize, 1081 ) -> Result<()>; 1082 1083 /// Perform a shift operation between two registers. 1084 /// This case is special in that some architectures have specific expectations 1085 /// regarding the location of the instruction arguments. To free the 1086 /// caller from having to deal with the architecture specific constraints 1087 /// we give this function access to the code generation context, allowing 1088 /// each implementation to decide the lowering path. 1089 fn shift( 1090 &mut self, 1091 context: &mut CodeGenContext<Emission>, 1092 kind: ShiftKind, 1093 size: OperandSize, 1094 ) -> Result<()>; 1095 1096 /// Perform division operation. 1097 /// Division is special in that some architectures have specific 1098 /// expectations regarding the location of the instruction 1099 /// arguments and regarding the location of the quotient / 1100 /// remainder. To free the caller from having to deal with the 1101 /// architecture specific constraints we give this function access 1102 /// to the code generation context, allowing each implementation 1103 /// to decide the lowering path. For cases in which division is a 1104 /// unconstrained binary operation, the caller can decide to use 1105 /// the `CodeGenContext::i32_binop` or `CodeGenContext::i64_binop` 1106 /// functions. 1107 fn div( 1108 &mut self, 1109 context: &mut CodeGenContext<Emission>, 1110 kind: DivKind, 1111 size: OperandSize, 1112 ) -> Result<()>; 1113 1114 /// Calculate remainder. 1115 fn rem( 1116 &mut self, 1117 context: &mut CodeGenContext<Emission>, 1118 kind: RemKind, 1119 size: OperandSize, 1120 ) -> Result<()>; 1121 1122 /// Compares `src1` against `src2` for the side effect of setting processor 1123 /// flags. 1124 /// 1125 /// Note that `src1` is the left-hand-side of the comparison and `src2` is 1126 /// the right-hand-side, so if testing `a < b` then `src1 == a` and 1127 /// `src2 == b` 1128 fn cmp(&mut self, src1: Reg, src2: RegImm, size: OperandSize) -> Result<()>; 1129 1130 /// Compare src and dst and put the result in dst. 1131 /// This function will potentially emit a series of instructions. 1132 /// 1133 /// The initial value in `dst` is the left-hand-side of the comparison and 1134 /// the initial value in `src` is the right-hand-side of the comparison. 1135 /// That means for `a < b` then `dst == a` and `src == b`. 1136 fn cmp_with_set( 1137 &mut self, 1138 dst: WritableReg, 1139 src: RegImm, 1140 kind: IntCmpKind, 1141 size: OperandSize, 1142 ) -> Result<()>; 1143 1144 /// Compare floats in src1 and src2 and put the result in dst. 1145 /// In x86, this will emit multiple instructions. 1146 fn float_cmp_with_set( 1147 &mut self, 1148 dst: WritableReg, 1149 src1: Reg, 1150 src2: Reg, 1151 kind: FloatCmpKind, 1152 size: OperandSize, 1153 ) -> Result<()>; 1154 1155 /// Count the number of leading zeroes in src and put the result in dst. 1156 /// In x64, this will emit multiple instructions if the `has_lzcnt` flag is 1157 /// false. 1158 fn clz(&mut self, dst: WritableReg, src: Reg, size: OperandSize) -> Result<()>; 1159 1160 /// Count the number of trailing zeroes in src and put the result in dst.masm 1161 /// In x64, this will emit multiple instructions if the `has_tzcnt` flag is 1162 /// false. 1163 fn ctz(&mut self, dst: WritableReg, src: Reg, size: OperandSize) -> Result<()>; 1164 1165 /// Push the register to the stack, returning the stack slot metadata. 1166 // NB 1167 // The stack alignment should not be assumed after any call to `push`, 1168 // unless explicitly aligned otherwise. Typically, stack alignment is 1169 // maintained at call sites and during the execution of 1170 // epilogues. 1171 fn push(&mut self, src: Reg, size: OperandSize) -> Result<StackSlot>; 1172 1173 /// Finalize the assembly and return the result. 1174 fn finalize(self, base: Option<SourceLoc>) -> Result<MachBufferFinalized<Final>>; 1175 1176 /// Zero a particular register. 1177 fn zero(&mut self, reg: WritableReg) -> Result<()>; 1178 1179 /// Count the number of 1 bits in src and put the result in dst. In x64, 1180 /// this will emit multiple instructions if the `has_popcnt` flag is false. 1181 fn popcnt(&mut self, context: &mut CodeGenContext<Emission>, size: OperandSize) -> Result<()>; 1182 1183 /// Converts an i64 to an i32 by discarding the high 32 bits. 1184 fn wrap(&mut self, dst: WritableReg, src: Reg) -> Result<()>; 1185 1186 /// Extends an integer of a given size to a larger size. 1187 fn extend(&mut self, dst: WritableReg, src: Reg, kind: ExtendKind) -> Result<()>; 1188 1189 /// Emits one or more instructions to perform a signed truncation of a 1190 /// float into an integer. 1191 fn signed_truncate( 1192 &mut self, 1193 dst: WritableReg, 1194 src: Reg, 1195 src_size: OperandSize, 1196 dst_size: OperandSize, 1197 kind: TruncKind, 1198 ) -> Result<()>; 1199 1200 /// Emits one or more instructions to perform an unsigned truncation of a 1201 /// float into an integer. 1202 fn unsigned_truncate( 1203 &mut self, 1204 context: &mut CodeGenContext<Emission>, 1205 src_size: OperandSize, 1206 dst_size: OperandSize, 1207 kind: TruncKind, 1208 ) -> Result<()>; 1209 1210 /// Emits one or more instructions to perform a signed convert of an 1211 /// integer into a float. 1212 fn signed_convert( 1213 &mut self, 1214 dst: WritableReg, 1215 src: Reg, 1216 src_size: OperandSize, 1217 dst_size: OperandSize, 1218 ) -> Result<()>; 1219 1220 /// Emits one or more instructions to perform an unsigned convert of an 1221 /// integer into a float. 1222 fn unsigned_convert( 1223 &mut self, 1224 dst: WritableReg, 1225 src: Reg, 1226 tmp_gpr: Reg, 1227 src_size: OperandSize, 1228 dst_size: OperandSize, 1229 ) -> Result<()>; 1230 1231 /// Reinterpret a float as an integer. 1232 fn reinterpret_float_as_int( 1233 &mut self, 1234 dst: WritableReg, 1235 src: Reg, 1236 size: OperandSize, 1237 ) -> Result<()>; 1238 1239 /// Reinterpret an integer as a float. 1240 fn reinterpret_int_as_float( 1241 &mut self, 1242 dst: WritableReg, 1243 src: Reg, 1244 size: OperandSize, 1245 ) -> Result<()>; 1246 1247 /// Demote an f64 to an f32. 1248 fn demote(&mut self, dst: WritableReg, src: Reg) -> Result<()>; 1249 1250 /// Promote an f32 to an f64. 1251 fn promote(&mut self, dst: WritableReg, src: Reg) -> Result<()>; 1252 1253 /// Zero a given memory range. 1254 /// 1255 /// The default implementation divides the given memory range 1256 /// into word-sized slots. Then it unrolls a series of store 1257 /// instructions, effectively assigning zero to each slot. 1258 fn zero_mem_range(&mut self, mem: &Range<u32>) -> Result<()> { 1259 let word_size = <Self::ABI as abi::ABI>::word_bytes() as u32; 1260 if mem.is_empty() { 1261 return Ok(()); 1262 } 1263 1264 let start = if mem.start % word_size == 0 { 1265 mem.start 1266 } else { 1267 // Ensure that the start of the range is at least 4-byte aligned. 1268 assert!(mem.start % 4 == 0); 1269 let start = align_to(mem.start, word_size); 1270 let addr: Self::Address = self.local_address(&LocalSlot::i32(start))?; 1271 self.store(RegImm::i32(0), addr, OperandSize::S32)?; 1272 // Ensure that the new start of the range, is word-size aligned. 1273 assert!(start % word_size == 0); 1274 start 1275 }; 1276 1277 let end = align_to(mem.end, word_size); 1278 let slots = (end - start) / word_size; 1279 1280 if slots == 1 { 1281 let slot = LocalSlot::i64(start + word_size); 1282 let addr: Self::Address = self.local_address(&slot)?; 1283 self.store(RegImm::i64(0), addr, OperandSize::S64)?; 1284 } else { 1285 // TODO 1286 // Add an upper bound to this generation; 1287 // given a considerably large amount of slots 1288 // this will be inefficient. 1289 let zero = scratch!(Self); 1290 self.zero(writable!(zero))?; 1291 let zero = RegImm::reg(zero); 1292 1293 for step in (start..end).into_iter().step_by(word_size as usize) { 1294 let slot = LocalSlot::i64(step + word_size); 1295 let addr: Self::Address = self.local_address(&slot)?; 1296 self.store(zero, addr, OperandSize::S64)?; 1297 } 1298 } 1299 1300 Ok(()) 1301 } 1302 1303 /// Generate a label. 1304 fn get_label(&mut self) -> Result<MachLabel>; 1305 1306 /// Bind the given label at the current code offset. 1307 fn bind(&mut self, label: MachLabel) -> Result<()>; 1308 1309 /// Conditional branch. 1310 /// 1311 /// Performs a comparison between the two operands, 1312 /// and immediately after emits a jump to the given 1313 /// label destination if the condition is met. 1314 fn branch( 1315 &mut self, 1316 kind: IntCmpKind, 1317 lhs: Reg, 1318 rhs: RegImm, 1319 taken: MachLabel, 1320 size: OperandSize, 1321 ) -> Result<()>; 1322 1323 /// Emits and unconditional jump to the given label. 1324 fn jmp(&mut self, target: MachLabel) -> Result<()>; 1325 1326 /// Emits a jump table sequence. The default label is specified as 1327 /// the last element of the targets slice. 1328 fn jmp_table(&mut self, targets: &[MachLabel], index: Reg, tmp: Reg) -> Result<()>; 1329 1330 /// Emit an unreachable code trap. 1331 fn unreachable(&mut self) -> Result<()>; 1332 1333 /// Emit an unconditional trap. 1334 fn trap(&mut self, code: TrapCode) -> Result<()>; 1335 1336 /// Traps if the condition code is met. 1337 fn trapif(&mut self, cc: IntCmpKind, code: TrapCode) -> Result<()>; 1338 1339 /// Trap if the source register is zero. 1340 fn trapz(&mut self, src: Reg, code: TrapCode) -> Result<()>; 1341 1342 /// Ensures that the stack pointer is correctly positioned before an unconditional 1343 /// jump according to the requirements of the destination target. 1344 fn ensure_sp_for_jump(&mut self, target: SPOffset) -> Result<()> { 1345 let bytes = self 1346 .sp_offset()? 1347 .as_u32() 1348 .checked_sub(target.as_u32()) 1349 .unwrap_or(0); 1350 1351 if bytes > 0 { 1352 self.free_stack(bytes)?; 1353 } 1354 1355 Ok(()) 1356 } 1357 1358 /// Mark the start of a source location returning the machine code offset 1359 /// and the relative source code location. 1360 fn start_source_loc(&mut self, loc: RelSourceLoc) -> Result<(CodeOffset, RelSourceLoc)>; 1361 1362 /// Mark the end of a source location. 1363 fn end_source_loc(&mut self) -> Result<()>; 1364 1365 /// The current offset, in bytes from the beginning of the function. 1366 fn current_code_offset(&self) -> Result<CodeOffset>; 1367 1368 /// Performs a 128-bit addition 1369 fn add128( 1370 &mut self, 1371 dst_lo: WritableReg, 1372 dst_hi: WritableReg, 1373 lhs_lo: Reg, 1374 lhs_hi: Reg, 1375 rhs_lo: Reg, 1376 rhs_hi: Reg, 1377 ) -> Result<()>; 1378 1379 /// Performs a 128-bit subtraction 1380 fn sub128( 1381 &mut self, 1382 dst_lo: WritableReg, 1383 dst_hi: WritableReg, 1384 lhs_lo: Reg, 1385 lhs_hi: Reg, 1386 rhs_lo: Reg, 1387 rhs_hi: Reg, 1388 ) -> Result<()>; 1389 1390 /// Performs a widening multiplication from two 64-bit operands into a 1391 /// 128-bit result. 1392 /// 1393 /// Note that some platforms require special handling of registers in this 1394 /// instruction (e.g. x64) so full access to `CodeGenContext` is provided. 1395 fn mul_wide(&mut self, context: &mut CodeGenContext<Emission>, kind: MulWideKind) 1396 -> Result<()>; 1397 1398 /// Takes the value in a src operand and replicates it across lanes of 1399 /// `size` in a destination result. 1400 fn splat(&mut self, context: &mut CodeGenContext<Emission>, size: SplatKind) -> Result<()>; 1401 1402 /// Performs a shuffle between two 128-bit vectors into a 128-bit result 1403 /// using lanes as a mask to select which indexes to copy. 1404 fn shuffle(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, lanes: [u8; 16]) -> Result<()>; 1405 1406 /// Performs a swizzle between two 128-bit vectors into a 128-bit result. 1407 fn swizzle(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg) -> Result<()>; 1408 1409 /// Performs the RMW `op` operation on the passed `addr`. 1410 /// 1411 /// The value *before* the operation was performed is written back to the `operand` register. 1412 fn atomic_rmw( 1413 &mut self, 1414 context: &mut CodeGenContext<Emission>, 1415 addr: Self::Address, 1416 size: OperandSize, 1417 op: RmwOp, 1418 flags: MemFlags, 1419 extend: Option<Extend<Zero>>, 1420 ) -> Result<()>; 1421 1422 /// Extracts the scalar value from `src` in `lane` to `dst`. 1423 fn extract_lane( 1424 &mut self, 1425 src: Reg, 1426 dst: WritableReg, 1427 lane: u8, 1428 kind: ExtractLaneKind, 1429 ) -> Result<()>; 1430 1431 /// Perform an atomic CAS (compare-and-swap) operation with the value at `addr`, and `expected` 1432 /// and `replacement` (at the top of the context's stack). 1433 /// 1434 /// This method takes the `CodeGenContext` as an arguments to accommodate architectures that 1435 /// expect parameters in specific registers. The context stack contains the `replacement`, 1436 /// and `expected` values in that order. The implementer is expected to push the value at 1437 /// `addr` before the update to the context's stack before returning. 1438 fn atomic_cas( 1439 &mut self, 1440 context: &mut CodeGenContext<Emission>, 1441 addr: Self::Address, 1442 size: OperandSize, 1443 flags: MemFlags, 1444 extend: Option<Extend<Zero>>, 1445 ) -> Result<()>; 1446 } 1447