1 use crate::abi::{self, align_to, scratch, LocalSlot}; 2 use crate::codegen::{CodeGenContext, FuncEnv}; 3 use crate::isa::reg::{writable, Reg, WritableReg}; 4 use cranelift_codegen::{ 5 binemit::CodeOffset, 6 ir::{Endianness, LibCall, MemFlags, RelSourceLoc, SourceLoc, UserExternalNameRef}, 7 Final, MachBufferFinalized, MachLabel, 8 }; 9 use std::{fmt::Debug, ops::Range}; 10 use wasmtime_environ::PtrSize; 11 12 pub(crate) use cranelift_codegen::ir::TrapCode; 13 14 #[derive(Eq, PartialEq)] 15 pub(crate) enum DivKind { 16 /// Signed division. 17 Signed, 18 /// Unsigned division. 19 Unsigned, 20 } 21 22 /// Remainder kind. 23 pub(crate) enum RemKind { 24 /// Signed remainder. 25 Signed, 26 /// Unsigned remainder. 27 Unsigned, 28 } 29 30 #[derive(Eq, PartialEq)] 31 pub(crate) enum MulWideKind { 32 Signed, 33 Unsigned, 34 } 35 36 /// The direction to perform the memory move. 37 #[derive(Debug, Clone, Eq, PartialEq)] 38 pub(crate) enum MemMoveDirection { 39 /// From high memory addresses to low memory addresses. 40 /// Invariant: the source location is closer to the FP than the destination 41 /// location, which will be closer to the SP. 42 HighToLow, 43 /// From low memory addresses to high memory addresses. 44 /// Invariant: the source location is closer to the SP than the destination 45 /// location, which will be closer to the FP. 46 LowToHigh, 47 } 48 49 /// Classifies how to treat float-to-int conversions. 50 #[derive(Debug, Copy, Clone, Eq, PartialEq)] 51 pub(crate) enum TruncKind { 52 /// Saturating conversion. If the source value is greater than the maximum 53 /// value of the destination type, the result is clamped to the 54 /// destination maximum value. 55 Checked, 56 /// An exception is raised if the source value is greater than the maximum 57 /// value of the destination type. 58 Unchecked, 59 } 60 61 impl TruncKind { 62 /// Returns true if the truncation kind is checked. 63 pub(crate) fn is_checked(&self) -> bool { 64 *self == TruncKind::Checked 65 } 66 } 67 68 /// Representation of the stack pointer offset. 69 #[derive(Copy, Clone, Eq, PartialEq, Debug, PartialOrd, Ord, Default)] 70 pub struct SPOffset(u32); 71 72 impl SPOffset { 73 pub fn from_u32(offs: u32) -> Self { 74 Self(offs) 75 } 76 77 pub fn as_u32(&self) -> u32 { 78 self.0 79 } 80 } 81 82 /// A stack slot. 83 #[derive(Debug, Clone, Copy, Eq, PartialEq)] 84 pub struct StackSlot { 85 /// The location of the slot, relative to the stack pointer. 86 pub offset: SPOffset, 87 /// The size of the slot, in bytes. 88 pub size: u32, 89 } 90 91 impl StackSlot { 92 pub fn new(offs: SPOffset, size: u32) -> Self { 93 Self { offset: offs, size } 94 } 95 } 96 97 /// Kinds of integer binary comparison in WebAssembly. The [`MacroAssembler`] 98 /// implementation for each ISA is responsible for emitting the correct 99 /// sequence of instructions when lowering to machine code. 100 #[derive(Debug, Clone, Copy, Eq, PartialEq)] 101 pub(crate) enum IntCmpKind { 102 /// Equal. 103 Eq, 104 /// Not equal. 105 Ne, 106 /// Signed less than. 107 LtS, 108 /// Unsigned less than. 109 LtU, 110 /// Signed greater than. 111 GtS, 112 /// Unsigned greater than. 113 GtU, 114 /// Signed less than or equal. 115 LeS, 116 /// Unsigned less than or equal. 117 LeU, 118 /// Signed greater than or equal. 119 GeS, 120 /// Unsigned greater than or equal. 121 GeU, 122 } 123 124 /// Kinds of float binary comparison in WebAssembly. The [`MacroAssembler`] 125 /// implementation for each ISA is responsible for emitting the correct 126 /// sequence of instructions when lowering code. 127 #[derive(Debug)] 128 pub(crate) enum FloatCmpKind { 129 /// Equal. 130 Eq, 131 /// Not equal. 132 Ne, 133 /// Less than. 134 Lt, 135 /// Greater than. 136 Gt, 137 /// Less than or equal. 138 Le, 139 /// Greater than or equal. 140 Ge, 141 } 142 143 /// Kinds of shifts in WebAssembly.The [`masm`] implementation for each ISA is 144 /// responsible for emitting the correct sequence of instructions when 145 /// lowering to machine code. 146 #[derive(Debug, Clone, Copy, Eq, PartialEq)] 147 pub(crate) enum ShiftKind { 148 /// Left shift. 149 Shl, 150 /// Signed right shift. 151 ShrS, 152 /// Unsigned right shift. 153 ShrU, 154 /// Left rotate. 155 Rotl, 156 /// Right rotate. 157 Rotr, 158 } 159 160 /// Kinds of extends in WebAssembly. Each MacroAssembler implementation 161 /// is responsible for emitting the correct sequence of instructions when 162 /// lowering to machine code. 163 pub(crate) enum ExtendKind { 164 /// Sign extends i32 to i64. 165 I64ExtendI32S, 166 /// Zero extends i32 to i64. 167 I64ExtendI32U, 168 // Sign extends the 8 least significant bits to 32 bits. 169 I32Extend8S, 170 // Sign extends the 16 least significant bits to 32 bits. 171 I32Extend16S, 172 /// Sign extends the 8 least significant bits to 64 bits. 173 I64Extend8S, 174 /// Sign extends the 16 least significant bits to 64 bits. 175 I64Extend16S, 176 /// Sign extends the 32 least significant bits to 64 bits. 177 I64Extend32S, 178 } 179 180 impl ExtendKind { 181 pub fn signed(&self) -> bool { 182 if let Self::I64ExtendI32U = self { 183 false 184 } else { 185 true 186 } 187 } 188 189 pub fn from_bits(&self) -> u8 { 190 match self { 191 Self::I64ExtendI32S | Self::I64ExtendI32U | Self::I64Extend32S => 32, 192 Self::I32Extend8S | Self::I64Extend8S => 8, 193 Self::I32Extend16S | Self::I64Extend16S => 16, 194 } 195 } 196 197 pub fn to_bits(&self) -> u8 { 198 match self { 199 Self::I64ExtendI32S 200 | Self::I64ExtendI32U 201 | Self::I64Extend8S 202 | Self::I64Extend16S 203 | Self::I64Extend32S => 64, 204 Self::I32Extend8S | Self::I32Extend16S => 32, 205 } 206 } 207 } 208 209 /// Operand size, in bits. 210 #[derive(Copy, Debug, Clone, Eq, PartialEq)] 211 pub(crate) enum OperandSize { 212 /// 8 bits. 213 S8, 214 /// 16 bits. 215 S16, 216 /// 32 bits. 217 S32, 218 /// 64 bits. 219 S64, 220 /// 128 bits. 221 S128, 222 } 223 224 impl OperandSize { 225 /// The number of bits in the operand. 226 pub fn num_bits(&self) -> u8 { 227 match self { 228 OperandSize::S8 => 8, 229 OperandSize::S16 => 16, 230 OperandSize::S32 => 32, 231 OperandSize::S64 => 64, 232 OperandSize::S128 => 128, 233 } 234 } 235 236 /// The number of bytes in the operand. 237 pub fn bytes(&self) -> u32 { 238 match self { 239 Self::S8 => 1, 240 Self::S16 => 2, 241 Self::S32 => 4, 242 Self::S64 => 8, 243 Self::S128 => 16, 244 } 245 } 246 247 /// The binary logarithm of the number of bits in the operand. 248 pub fn log2(&self) -> u8 { 249 match self { 250 OperandSize::S8 => 3, 251 OperandSize::S16 => 4, 252 OperandSize::S32 => 5, 253 OperandSize::S64 => 6, 254 OperandSize::S128 => 7, 255 } 256 } 257 258 /// Create an [`OperandSize`] from the given number of bytes. 259 pub fn from_bytes(bytes: u8) -> Self { 260 use OperandSize::*; 261 match bytes { 262 4 => S32, 263 8 => S64, 264 16 => S128, 265 _ => panic!("Invalid bytes {bytes} for OperandSize"), 266 } 267 } 268 } 269 270 /// An abstraction over a register or immediate. 271 #[derive(Copy, Clone, Debug, PartialEq, Eq)] 272 pub(crate) enum RegImm { 273 /// A register. 274 Reg(Reg), 275 /// A tagged immediate argument. 276 Imm(Imm), 277 } 278 279 /// An tagged representation of an immediate. 280 #[derive(Copy, Clone, Debug, PartialEq, Eq)] 281 pub(crate) enum Imm { 282 /// I32 immediate. 283 I32(u32), 284 /// I64 immediate. 285 I64(u64), 286 /// F32 immediate. 287 F32(u32), 288 /// F64 immediate. 289 F64(u64), 290 /// V128 immediate. 291 V128(i128), 292 } 293 294 impl Imm { 295 /// Create a new I64 immediate. 296 pub fn i64(val: i64) -> Self { 297 Self::I64(val as u64) 298 } 299 300 /// Create a new I32 immediate. 301 pub fn i32(val: i32) -> Self { 302 Self::I32(val as u32) 303 } 304 305 /// Create a new F32 immediate. 306 pub fn f32(bits: u32) -> Self { 307 Self::F32(bits) 308 } 309 310 /// Create a new F64 immediate. 311 pub fn f64(bits: u64) -> Self { 312 Self::F64(bits) 313 } 314 315 /// Create a new V128 immediate. 316 pub fn v128(bits: i128) -> Self { 317 Self::V128(bits) 318 } 319 320 /// Convert the immediate to i32, if possible. 321 pub fn to_i32(&self) -> Option<i32> { 322 match self { 323 Self::I32(v) => Some(*v as i32), 324 Self::I64(v) => i32::try_from(*v as i64).ok(), 325 _ => None, 326 } 327 } 328 329 /// Returns true if the [`Imm`] is float. 330 pub fn is_float(&self) -> bool { 331 match self { 332 Self::F32(_) | Self::F64(_) => true, 333 _ => false, 334 } 335 } 336 337 /// Get the operand size of the immediate. 338 pub fn size(&self) -> OperandSize { 339 match self { 340 Self::I32(_) | Self::F32(_) => OperandSize::S32, 341 Self::I64(_) | Self::F64(_) => OperandSize::S64, 342 Self::V128(_) => OperandSize::S128, 343 } 344 } 345 } 346 347 /// The location of the [VMcontext] used for function calls. 348 #[derive(Copy, Clone, Debug, Eq, PartialEq)] 349 pub(crate) enum VMContextLoc { 350 /// Dynamic, stored in the given register. 351 Reg(Reg), 352 /// The pinned [VMContext] register. 353 Pinned, 354 } 355 356 /// The maximum number of context arguments currently used across the compiler. 357 pub(crate) const MAX_CONTEXT_ARGS: usize = 2; 358 359 /// Out-of-band special purpose arguments used for function call emission. 360 /// 361 /// We cannot rely on the value stack for these values given that inserting 362 /// register or memory values at arbitrary locations of the value stack has the 363 /// potential to break the stack ordering principle, which states that older 364 /// values must always precede newer values, effectively simulating the order of 365 /// values in the machine stack. 366 /// The [ContextArgs] are meant to be resolved at every callsite; in some cases 367 /// it might be possible to construct it early on, but given that it might 368 /// contain allocatable registers, it's preferred to construct it in 369 /// [FnCall::emit]. 370 #[derive(Clone, Debug)] 371 pub(crate) enum ContextArgs { 372 /// No context arguments required. This is used for libcalls that don't 373 /// require any special context arguments. For example builtin functions 374 /// that perform float calculations. 375 None, 376 /// A single context argument is required; the current pinned [VMcontext] 377 /// register must be passed as the first argument of the function call. 378 VMContext([VMContextLoc; 1]), 379 /// The callee and caller context arguments are required. In this case, the 380 /// callee context argument is usually stored into an allocatable register 381 /// and the caller is always the current pinned [VMContext] pointer. 382 CalleeAndCallerVMContext([VMContextLoc; MAX_CONTEXT_ARGS]), 383 } 384 385 impl ContextArgs { 386 /// Construct an empty [ContextArgs]. 387 pub fn none() -> Self { 388 Self::None 389 } 390 391 /// Construct a [ContextArgs] declaring the usage of the pinned [VMContext] 392 /// register as both the caller and callee context arguments. 393 pub fn pinned_callee_and_caller_vmctx() -> Self { 394 Self::CalleeAndCallerVMContext([VMContextLoc::Pinned, VMContextLoc::Pinned]) 395 } 396 397 /// Construct a [ContextArgs] that declares the usage of the pinned 398 /// [VMContext] register as the only context argument. 399 pub fn pinned_vmctx() -> Self { 400 Self::VMContext([VMContextLoc::Pinned]) 401 } 402 403 /// Construct a [ContextArgs] that declares a dynamic callee context and the 404 /// pinned [VMContext] register as the context arguments. 405 pub fn with_callee_and_pinned_caller(callee_vmctx: Reg) -> Self { 406 Self::CalleeAndCallerVMContext([VMContextLoc::Reg(callee_vmctx), VMContextLoc::Pinned]) 407 } 408 409 /// Get the length of the [ContextArgs]. 410 pub fn len(&self) -> usize { 411 self.as_slice().len() 412 } 413 414 /// Get a slice of the context arguments. 415 pub fn as_slice(&self) -> &[VMContextLoc] { 416 match self { 417 Self::None => &[], 418 Self::VMContext(a) => a.as_slice(), 419 Self::CalleeAndCallerVMContext(a) => a.as_slice(), 420 } 421 } 422 } 423 424 #[derive(Copy, Clone, Debug)] 425 pub(crate) enum CalleeKind { 426 /// A function call to a raw address. 427 Indirect(Reg), 428 /// A function call to a local function. 429 Direct(UserExternalNameRef), 430 /// Call to a well known LibCall. 431 LibCall(LibCall), 432 } 433 434 impl CalleeKind { 435 /// Creates a callee kind from a register. 436 pub fn indirect(reg: Reg) -> Self { 437 Self::Indirect(reg) 438 } 439 440 /// Creates a direct callee kind from a function name. 441 pub fn direct(name: UserExternalNameRef) -> Self { 442 Self::Direct(name) 443 } 444 445 /// Creates a known callee kind from a libcall. 446 pub fn libcall(call: LibCall) -> Self { 447 Self::LibCall(call) 448 } 449 } 450 451 impl RegImm { 452 /// Register constructor. 453 pub fn reg(r: Reg) -> Self { 454 RegImm::Reg(r) 455 } 456 457 /// I64 immediate constructor. 458 pub fn i64(val: i64) -> Self { 459 RegImm::Imm(Imm::i64(val)) 460 } 461 462 /// I32 immediate constructor. 463 pub fn i32(val: i32) -> Self { 464 RegImm::Imm(Imm::i32(val)) 465 } 466 467 /// F32 immediate, stored using its bits representation. 468 pub fn f32(bits: u32) -> Self { 469 RegImm::Imm(Imm::f32(bits)) 470 } 471 472 /// F64 immediate, stored using its bits representation. 473 pub fn f64(bits: u64) -> Self { 474 RegImm::Imm(Imm::f64(bits)) 475 } 476 477 /// V128 immediate. 478 pub fn v128(bits: i128) -> Self { 479 RegImm::Imm(Imm::v128(bits)) 480 } 481 } 482 483 impl From<Reg> for RegImm { 484 fn from(r: Reg) -> Self { 485 Self::Reg(r) 486 } 487 } 488 489 #[derive(Debug)] 490 pub enum RoundingMode { 491 Nearest, 492 Up, 493 Down, 494 Zero, 495 } 496 497 /// Memory flags for trusted loads/stores. 498 pub const TRUSTED_FLAGS: MemFlags = MemFlags::trusted(); 499 500 /// Flags used for WebAssembly loads / stores. 501 /// Untrusted by default so we don't set `no_trap`. 502 /// We also ensure that the endianness is the right one for WebAssembly. 503 pub const UNTRUSTED_FLAGS: MemFlags = MemFlags::new().with_endianness(Endianness::Little); 504 505 /// Generic MacroAssembler interface used by the code generation. 506 /// 507 /// The MacroAssembler trait aims to expose an interface, high-level enough, 508 /// so that each ISA can provide its own lowering to machine code. For example, 509 /// for WebAssembly operators that don't have a direct mapping to a machine 510 /// a instruction, the interface defines a signature matching the WebAssembly 511 /// operator, allowing each implementation to lower such operator entirely. 512 /// This approach attributes more responsibility to the MacroAssembler, but frees 513 /// the caller from concerning about assembling the right sequence of 514 /// instructions at the operator callsite. 515 /// 516 /// The interface defaults to a three-argument form for binary operations; 517 /// this allows a natural mapping to instructions for RISC architectures, 518 /// that use three-argument form. 519 /// This approach allows for a more general interface that can be restricted 520 /// where needed, in the case of architectures that use a two-argument form. 521 522 pub(crate) trait MacroAssembler { 523 /// The addressing mode. 524 type Address: Copy + Debug; 525 526 /// The pointer representation of the target ISA, 527 /// used to access information from [`VMOffsets`]. 528 type Ptr: PtrSize; 529 530 /// The ABI details of the target. 531 type ABI: abi::ABI; 532 533 /// Emit the function prologue. 534 fn prologue(&mut self, vmctx: Reg) { 535 self.frame_setup(); 536 self.check_stack(vmctx); 537 } 538 539 /// Generate the frame setup sequence. 540 fn frame_setup(&mut self); 541 542 /// Generate the frame restore sequence. 543 fn frame_restore(&mut self); 544 545 /// Emit a stack check. 546 fn check_stack(&mut self, vmctx: Reg); 547 548 /// Emit the function epilogue. 549 fn epilogue(&mut self) { 550 self.frame_restore(); 551 } 552 553 /// Reserve stack space. 554 fn reserve_stack(&mut self, bytes: u32); 555 556 /// Free stack space. 557 fn free_stack(&mut self, bytes: u32); 558 559 /// Reset the stack pointer to the given offset; 560 /// 561 /// Used to reset the stack pointer to a given offset 562 /// when dealing with unreachable code. 563 fn reset_stack_pointer(&mut self, offset: SPOffset); 564 565 /// Get the address of a local slot. 566 fn local_address(&mut self, local: &LocalSlot) -> Self::Address; 567 568 /// Constructs an address with an offset that is relative to the 569 /// current position of the stack pointer (e.g. [sp + (sp_offset - 570 /// offset)]. 571 fn address_from_sp(&self, offset: SPOffset) -> Self::Address; 572 573 /// Constructs an address with an offset that is absolute to the 574 /// current position of the stack pointer (e.g. [sp + offset]. 575 fn address_at_sp(&self, offset: SPOffset) -> Self::Address; 576 577 /// Alias for [`Self::address_at_reg`] using the VMContext register as 578 /// a base. The VMContext register is derived from the ABI type that is 579 /// associated to the MacroAssembler. 580 fn address_at_vmctx(&self, offset: u32) -> Self::Address; 581 582 /// Construct an address that is absolute to the current position 583 /// of the given register. 584 fn address_at_reg(&self, reg: Reg, offset: u32) -> Self::Address; 585 586 /// Emit a function call to either a local or external function. 587 fn call(&mut self, stack_args_size: u32, f: impl FnMut(&mut Self) -> CalleeKind) -> u32; 588 589 /// Get stack pointer offset. 590 fn sp_offset(&self) -> SPOffset; 591 592 /// Perform a stack store. 593 fn store(&mut self, src: RegImm, dst: Self::Address, size: OperandSize); 594 595 /// Alias for `MacroAssembler::store` with the operand size corresponding 596 /// to the pointer size of the target. 597 fn store_ptr(&mut self, src: Reg, dst: Self::Address); 598 599 /// Perform a WebAssembly store. 600 /// A WebAssembly store introduces several additional invariants compared to 601 /// [Self::store], more precisely, it can implicitly trap, in certain 602 /// circumstances, even if explicit bounds checks are elided, in that sense, 603 /// we consider this type of load as untrusted. It can also differ with 604 /// regards to the endianness depending on the target ISA. For this reason, 605 /// [Self::wasm_store], should be explicitly used when emitting WebAssembly 606 /// stores. 607 fn wasm_store(&mut self, src: Reg, dst: Self::Address, size: OperandSize); 608 609 /// Perform a zero-extended stack load. 610 fn load(&mut self, src: Self::Address, dst: WritableReg, size: OperandSize); 611 612 /// Perform a WebAssembly load. 613 /// A WebAssembly load introduces several additional invariants compared to 614 /// [Self::load], more precisely, it can implicitly trap, in certain 615 /// circumstances, even if explicit bounds checks are elided, in that sense, 616 /// we consider this type of load as untrusted. It can also differ with 617 /// regards to the endianness depending on the target ISA. For this reason, 618 /// [Self::wasm_load], should be explicitly used when emitting WebAssembly 619 /// loads. 620 fn wasm_load( 621 &mut self, 622 src: Self::Address, 623 dst: WritableReg, 624 size: OperandSize, 625 kind: Option<ExtendKind>, 626 ); 627 628 /// Alias for `MacroAssembler::load` with the operand size corresponding 629 /// to the pointer size of the target. 630 fn load_ptr(&mut self, src: Self::Address, dst: WritableReg); 631 632 /// Loads the effective address into destination. 633 fn load_addr(&mut self, _src: Self::Address, _dst: WritableReg, _size: OperandSize); 634 635 /// Pop a value from the machine stack into the given register. 636 fn pop(&mut self, dst: WritableReg, size: OperandSize); 637 638 /// Perform a move. 639 fn mov(&mut self, dst: WritableReg, src: RegImm, size: OperandSize); 640 641 /// Perform a conditional move. 642 fn cmov(&mut self, dst: WritableReg, src: Reg, cc: IntCmpKind, size: OperandSize); 643 644 /// Performs a memory move of bytes from src to dest. 645 /// Bytes are moved in blocks of 8 bytes, where possible. 646 fn memmove(&mut self, src: SPOffset, dst: SPOffset, bytes: u32, direction: MemMoveDirection) { 647 match direction { 648 MemMoveDirection::LowToHigh => debug_assert!(dst.as_u32() < src.as_u32()), 649 MemMoveDirection::HighToLow => debug_assert!(dst.as_u32() > src.as_u32()), 650 } 651 // At least 4 byte aligned. 652 debug_assert!(bytes % 4 == 0); 653 let mut remaining = bytes; 654 let word_bytes = <Self::ABI as abi::ABI>::word_bytes(); 655 let scratch = scratch!(Self); 656 657 let mut dst_offs = dst.as_u32() - bytes; 658 let mut src_offs = src.as_u32() - bytes; 659 660 let word_bytes = word_bytes as u32; 661 while remaining >= word_bytes { 662 remaining -= word_bytes; 663 dst_offs += word_bytes; 664 src_offs += word_bytes; 665 666 self.load_ptr( 667 self.address_from_sp(SPOffset::from_u32(src_offs)), 668 writable!(scratch), 669 ); 670 self.store_ptr( 671 scratch.into(), 672 self.address_from_sp(SPOffset::from_u32(dst_offs)), 673 ); 674 } 675 676 if remaining > 0 { 677 let half_word = word_bytes / 2; 678 let ptr_size = OperandSize::from_bytes(half_word as u8); 679 debug_assert!(remaining == half_word); 680 dst_offs += half_word; 681 src_offs += half_word; 682 683 self.load( 684 self.address_from_sp(SPOffset::from_u32(src_offs)), 685 writable!(scratch), 686 ptr_size, 687 ); 688 self.store( 689 scratch.into(), 690 self.address_from_sp(SPOffset::from_u32(dst_offs)), 691 ptr_size, 692 ); 693 } 694 } 695 696 /// Perform add operation. 697 fn add(&mut self, dst: WritableReg, lhs: Reg, rhs: RegImm, size: OperandSize); 698 699 /// Perform a checked unsigned integer addition, emitting the provided trap 700 /// if the addition overflows. 701 fn checked_uadd( 702 &mut self, 703 dst: WritableReg, 704 lhs: Reg, 705 rhs: RegImm, 706 size: OperandSize, 707 trap: TrapCode, 708 ); 709 710 /// Perform subtraction operation. 711 fn sub(&mut self, dst: WritableReg, lhs: Reg, rhs: RegImm, size: OperandSize); 712 713 /// Perform multiplication operation. 714 fn mul(&mut self, dst: WritableReg, lhs: Reg, rhs: RegImm, size: OperandSize); 715 716 /// Perform a floating point add operation. 717 fn float_add(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize); 718 719 /// Perform a floating point subtraction operation. 720 fn float_sub(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize); 721 722 /// Perform a floating point multiply operation. 723 fn float_mul(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize); 724 725 /// Perform a floating point divide operation. 726 fn float_div(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize); 727 728 /// Perform a floating point minimum operation. In x86, this will emit 729 /// multiple instructions. 730 fn float_min(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize); 731 732 /// Perform a floating point maximum operation. In x86, this will emit 733 /// multiple instructions. 734 fn float_max(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize); 735 736 /// Perform a floating point copysign operation. In x86, this will emit 737 /// multiple instructions. 738 fn float_copysign(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize); 739 740 /// Perform a floating point abs operation. 741 fn float_abs(&mut self, dst: WritableReg, size: OperandSize); 742 743 /// Perform a floating point negation operation. 744 fn float_neg(&mut self, dst: WritableReg, size: OperandSize); 745 746 /// Perform a floating point floor operation. 747 fn float_round<F: FnMut(&mut FuncEnv<Self::Ptr>, &mut CodeGenContext, &mut Self)>( 748 &mut self, 749 mode: RoundingMode, 750 env: &mut FuncEnv<Self::Ptr>, 751 context: &mut CodeGenContext, 752 size: OperandSize, 753 fallback: F, 754 ); 755 756 /// Perform a floating point square root operation. 757 fn float_sqrt(&mut self, dst: WritableReg, src: Reg, size: OperandSize); 758 759 /// Perform logical and operation. 760 fn and(&mut self, dst: WritableReg, lhs: Reg, rhs: RegImm, size: OperandSize); 761 762 /// Perform logical or operation. 763 fn or(&mut self, dst: WritableReg, lhs: Reg, rhs: RegImm, size: OperandSize); 764 765 /// Perform logical exclusive or operation. 766 fn xor(&mut self, dst: WritableReg, lhs: Reg, rhs: RegImm, size: OperandSize); 767 768 /// Perform a shift operation between a register and an immediate. 769 fn shift_ir( 770 &mut self, 771 dst: WritableReg, 772 imm: u64, 773 lhs: Reg, 774 kind: ShiftKind, 775 size: OperandSize, 776 ); 777 778 /// Perform a shift operation between two registers. 779 /// This case is special in that some architectures have specific expectations 780 /// regarding the location of the instruction arguments. To free the 781 /// caller from having to deal with the architecture specific constraints 782 /// we give this function access to the code generation context, allowing 783 /// each implementation to decide the lowering path. 784 fn shift(&mut self, context: &mut CodeGenContext, kind: ShiftKind, size: OperandSize); 785 786 /// Perform division operation. 787 /// Division is special in that some architectures have specific 788 /// expectations regarding the location of the instruction 789 /// arguments and regarding the location of the quotient / 790 /// remainder. To free the caller from having to deal with the 791 /// architecture specific constraints we give this function access 792 /// to the code generation context, allowing each implementation 793 /// to decide the lowering path. For cases in which division is a 794 /// unconstrained binary operation, the caller can decide to use 795 /// the `CodeGenContext::i32_binop` or `CodeGenContext::i64_binop` 796 /// functions. 797 fn div(&mut self, context: &mut CodeGenContext, kind: DivKind, size: OperandSize); 798 799 /// Calculate remainder. 800 fn rem(&mut self, context: &mut CodeGenContext, kind: RemKind, size: OperandSize); 801 802 /// Compares `src1` against `src2` for the side effect of setting processor 803 /// flags. 804 /// 805 /// Note that `src1` is the left-hand-side of the comparison and `src2` is 806 /// the right-hand-side, so if testing `a < b` then `src1 == a` and 807 /// `src2 == b` 808 fn cmp(&mut self, src1: Reg, src2: RegImm, size: OperandSize); 809 810 /// Compare src and dst and put the result in dst. 811 /// This function will potentially emit a series of instructions. 812 /// 813 /// The initial value in `dst` is the left-hand-side of the comparison and 814 /// the initial value in `src` is the right-hand-side of the comparison. 815 /// That means for `a < b` then `dst == a` and `src == b`. 816 fn cmp_with_set(&mut self, dst: WritableReg, src: RegImm, kind: IntCmpKind, size: OperandSize); 817 818 /// Compare floats in src1 and src2 and put the result in dst. 819 /// In x86, this will emit multiple instructions. 820 fn float_cmp_with_set( 821 &mut self, 822 dst: WritableReg, 823 src1: Reg, 824 src2: Reg, 825 kind: FloatCmpKind, 826 size: OperandSize, 827 ); 828 829 /// Count the number of leading zeroes in src and put the result in dst. 830 /// In x64, this will emit multiple instructions if the `has_lzcnt` flag is 831 /// false. 832 fn clz(&mut self, dst: WritableReg, src: Reg, size: OperandSize); 833 834 /// Count the number of trailing zeroes in src and put the result in dst.masm 835 /// In x64, this will emit multiple instructions if the `has_tzcnt` flag is 836 /// false. 837 fn ctz(&mut self, dst: WritableReg, src: Reg, size: OperandSize); 838 839 /// Push the register to the stack, returning the stack slot metadata. 840 // NB 841 // The stack alignment should not be assumed after any call to `push`, 842 // unless explicitly aligned otherwise. Typically, stack alignment is 843 // maintained at call sites and during the execution of 844 // epilogues. 845 fn push(&mut self, src: Reg, size: OperandSize) -> StackSlot; 846 847 /// Finalize the assembly and return the result. 848 fn finalize(self, base: Option<SourceLoc>) -> MachBufferFinalized<Final>; 849 850 /// Zero a particular register. 851 fn zero(&mut self, reg: WritableReg); 852 853 /// Count the number of 1 bits in src and put the result in dst. In x64, 854 /// this will emit multiple instructions if the `has_popcnt` flag is false. 855 fn popcnt(&mut self, context: &mut CodeGenContext, size: OperandSize); 856 857 /// Converts an i64 to an i32 by discarding the high 32 bits. 858 fn wrap(&mut self, dst: WritableReg, src: Reg); 859 860 /// Extends an integer of a given size to a larger size. 861 fn extend(&mut self, dst: WritableReg, src: Reg, kind: ExtendKind); 862 863 /// Emits one or more instructions to perform a signed truncation of a 864 /// float into an integer. 865 fn signed_truncate( 866 &mut self, 867 dst: WritableReg, 868 src: Reg, 869 src_size: OperandSize, 870 dst_size: OperandSize, 871 kind: TruncKind, 872 ); 873 874 /// Emits one or more instructions to perform an unsigned truncation of a 875 /// float into an integer. 876 fn unsigned_truncate( 877 &mut self, 878 dst: WritableReg, 879 src: Reg, 880 tmp_fpr: Reg, 881 src_size: OperandSize, 882 dst_size: OperandSize, 883 kind: TruncKind, 884 ); 885 886 /// Emits one or more instructions to perform a signed convert of an 887 /// integer into a float. 888 fn signed_convert( 889 &mut self, 890 dst: WritableReg, 891 src: Reg, 892 src_size: OperandSize, 893 dst_size: OperandSize, 894 ); 895 896 /// Emits one or more instructions to perform an unsigned convert of an 897 /// integer into a float. 898 fn unsigned_convert( 899 &mut self, 900 dst: WritableReg, 901 src: Reg, 902 tmp_gpr: Reg, 903 src_size: OperandSize, 904 dst_size: OperandSize, 905 ); 906 907 /// Reinterpret a float as an integer. 908 fn reinterpret_float_as_int(&mut self, dst: WritableReg, src: Reg, size: OperandSize); 909 910 /// Reinterpret an integer as a float. 911 fn reinterpret_int_as_float(&mut self, dst: WritableReg, src: Reg, size: OperandSize); 912 913 /// Demote an f64 to an f32. 914 fn demote(&mut self, dst: WritableReg, src: Reg); 915 916 /// Promote an f32 to an f64. 917 fn promote(&mut self, dst: WritableReg, src: Reg); 918 919 /// Zero a given memory range. 920 /// 921 /// The default implementation divides the given memory range 922 /// into word-sized slots. Then it unrolls a series of store 923 /// instructions, effectively assigning zero to each slot. 924 fn zero_mem_range(&mut self, mem: &Range<u32>) { 925 let word_size = <Self::ABI as abi::ABI>::word_bytes() as u32; 926 if mem.is_empty() { 927 return; 928 } 929 930 let start = if mem.start % word_size == 0 { 931 mem.start 932 } else { 933 // Ensure that the start of the range is at least 4-byte aligned. 934 assert!(mem.start % 4 == 0); 935 let start = align_to(mem.start, word_size); 936 let addr: Self::Address = self.local_address(&LocalSlot::i32(start)); 937 self.store(RegImm::i32(0), addr, OperandSize::S32); 938 // Ensure that the new start of the range, is word-size aligned. 939 assert!(start % word_size == 0); 940 start 941 }; 942 943 let end = align_to(mem.end, word_size); 944 let slots = (end - start) / word_size; 945 946 if slots == 1 { 947 let slot = LocalSlot::i64(start + word_size); 948 let addr: Self::Address = self.local_address(&slot); 949 self.store(RegImm::i64(0), addr, OperandSize::S64); 950 } else { 951 // TODO 952 // Add an upper bound to this generation; 953 // given a considerably large amount of slots 954 // this will be inefficient. 955 let zero = scratch!(Self); 956 self.zero(writable!(zero)); 957 let zero = RegImm::reg(zero); 958 959 for step in (start..end).into_iter().step_by(word_size as usize) { 960 let slot = LocalSlot::i64(step + word_size); 961 let addr: Self::Address = self.local_address(&slot); 962 self.store(zero, addr, OperandSize::S64); 963 } 964 } 965 } 966 967 /// Generate a label. 968 fn get_label(&mut self) -> MachLabel; 969 970 /// Bind the given label at the current code offset. 971 fn bind(&mut self, label: MachLabel); 972 973 /// Conditional branch. 974 /// 975 /// Performs a comparison between the two operands, 976 /// and immediately after emits a jump to the given 977 /// label destination if the condition is met. 978 fn branch( 979 &mut self, 980 kind: IntCmpKind, 981 lhs: Reg, 982 rhs: RegImm, 983 taken: MachLabel, 984 size: OperandSize, 985 ); 986 987 /// Emits and unconditional jump to the given label. 988 fn jmp(&mut self, target: MachLabel); 989 990 /// Emits a jump table sequence. The default label is specified as 991 /// the last element of the targets slice. 992 fn jmp_table(&mut self, targets: &[MachLabel], index: Reg, tmp: Reg); 993 994 /// Emit an unreachable code trap. 995 fn unreachable(&mut self); 996 997 /// Emit an unconditional trap. 998 fn trap(&mut self, code: TrapCode); 999 1000 /// Traps if the condition code is met. 1001 fn trapif(&mut self, cc: IntCmpKind, code: TrapCode); 1002 1003 /// Trap if the source register is zero. 1004 fn trapz(&mut self, src: Reg, code: TrapCode); 1005 1006 /// Ensures that the stack pointer is correctly positioned before an unconditional 1007 /// jump according to the requirements of the destination target. 1008 fn ensure_sp_for_jump(&mut self, target: SPOffset) { 1009 let bytes = self 1010 .sp_offset() 1011 .as_u32() 1012 .checked_sub(target.as_u32()) 1013 .unwrap_or(0); 1014 if bytes > 0 { 1015 self.free_stack(bytes); 1016 } 1017 } 1018 1019 /// Mark the start of a source location returning the machine code offset 1020 /// and the relative source code location. 1021 fn start_source_loc(&mut self, loc: RelSourceLoc) -> (CodeOffset, RelSourceLoc); 1022 1023 /// Mark the end of a source location. 1024 fn end_source_loc(&mut self); 1025 1026 /// The current offset, in bytes from the beginning of the function. 1027 fn current_code_offset(&self) -> CodeOffset; 1028 1029 /// Performs a 128-bit addition 1030 fn add128( 1031 &mut self, 1032 dst_lo: WritableReg, 1033 dst_hi: WritableReg, 1034 lhs_lo: Reg, 1035 lhs_hi: Reg, 1036 rhs_lo: Reg, 1037 rhs_hi: Reg, 1038 ); 1039 1040 /// Performs a 128-bit subtraction 1041 fn sub128( 1042 &mut self, 1043 dst_lo: WritableReg, 1044 dst_hi: WritableReg, 1045 lhs_lo: Reg, 1046 lhs_hi: Reg, 1047 rhs_lo: Reg, 1048 rhs_hi: Reg, 1049 ); 1050 1051 /// Performs a widening multiplication from two 64-bit operands into a 1052 /// 128-bit result. 1053 /// 1054 /// Note that some platforms require special handling of registers in this 1055 /// instruction (e.g. x64) so full access to `CodeGenContext` is provided. 1056 fn mul_wide(&mut self, context: &mut CodeGenContext, kind: MulWideKind); 1057 } 1058