1 use crate::abi::{self, align_to, LocalSlot}; 2 use crate::codegen::{ptr_type_from_ptr_size, CodeGenContext, TableData}; 3 use crate::isa::reg::Reg; 4 use cranelift_codegen::{ir::LibCall, Final, MachBufferFinalized, MachLabel}; 5 use std::{fmt::Debug, ops::Range}; 6 use wasmtime_environ::PtrSize; 7 8 pub(crate) use cranelift_codegen::ir::TrapCode; 9 10 #[derive(Eq, PartialEq)] 11 pub(crate) enum DivKind { 12 /// Signed division. 13 Signed, 14 /// Unsigned division. 15 Unsigned, 16 } 17 18 /// Remainder kind. 19 pub(crate) enum RemKind { 20 /// Signed remainder. 21 Signed, 22 /// Unsigned remainder. 23 Unsigned, 24 } 25 26 /// Representation of the stack pointer offset. 27 #[derive(Copy, Clone, Eq, PartialEq, Debug, PartialOrd, Ord)] 28 pub struct SPOffset(u32); 29 30 impl SPOffset { 31 pub fn from_u32(offs: u32) -> Self { 32 Self(offs) 33 } 34 35 pub fn as_u32(&self) -> u32 { 36 self.0 37 } 38 } 39 40 /// A stack slot. 41 #[derive(Debug, Clone, Copy, Eq, PartialEq)] 42 pub struct StackSlot { 43 /// The location of the slot, relative to the stack pointer. 44 pub offset: SPOffset, 45 /// The size of the slot, in bytes. 46 pub size: u32, 47 } 48 49 impl StackSlot { 50 pub fn new(offs: SPOffset, size: u32) -> Self { 51 Self { offset: offs, size } 52 } 53 } 54 55 /// Kinds of integer binary comparison in WebAssembly. The [`MacroAssembler`] 56 /// implementation for each ISA is responsible for emitting the correct 57 /// sequence of instructions when lowering to machine code. 58 #[derive(Debug, Clone, Copy, Eq, PartialEq)] 59 pub(crate) enum IntCmpKind { 60 /// Equal. 61 Eq, 62 /// Not equal. 63 Ne, 64 /// Signed less than. 65 LtS, 66 /// Unsigned less than. 67 LtU, 68 /// Signed greater than. 69 GtS, 70 /// Unsigned greater than. 71 GtU, 72 /// Signed less than or equal. 73 LeS, 74 /// Unsigned less than or equal. 75 LeU, 76 /// Signed greater than or equal. 77 GeS, 78 /// Unsigned greater than or equal. 79 GeU, 80 } 81 82 /// Kinds of float binary comparison in WebAssembly. The [`MacroAssembler`] 83 /// implementation for each ISA is responsible for emitting the correct 84 /// sequence of instructions when lowering code. 85 #[derive(Debug)] 86 pub(crate) enum FloatCmpKind { 87 /// Equal. 88 Eq, 89 /// Not equal. 90 Ne, 91 /// Less than. 92 Lt, 93 /// Greater than. 94 Gt, 95 /// Less than or equal. 96 Le, 97 /// Greater than or equal. 98 Ge, 99 } 100 101 /// Kinds of shifts in WebAssembly.The [`masm`] implementation for each ISA is 102 /// responsible for emitting the correct sequence of instructions when 103 /// lowering to machine code. 104 pub(crate) enum ShiftKind { 105 /// Left shift. 106 Shl, 107 /// Signed right shift. 108 ShrS, 109 /// Unsigned right shift. 110 ShrU, 111 /// Left rotate. 112 Rotl, 113 /// Right rotate. 114 Rotr, 115 } 116 117 /// Operand size, in bits. 118 #[derive(Copy, Debug, Clone, Eq, PartialEq)] 119 pub(crate) enum OperandSize { 120 /// 32 bits. 121 S32, 122 /// 64 bits. 123 S64, 124 /// 128 bits. 125 S128, 126 } 127 128 impl OperandSize { 129 /// The number of bits in the operand. 130 pub fn num_bits(&self) -> i32 { 131 match self { 132 OperandSize::S32 => 32, 133 OperandSize::S64 => 64, 134 OperandSize::S128 => 128, 135 } 136 } 137 138 /// The number of bytes in the operand. 139 pub fn bytes(&self) -> u32 { 140 match self { 141 Self::S32 => 4, 142 Self::S64 => 8, 143 Self::S128 => 16, 144 } 145 } 146 147 /// The binary logarithm of the number of bits in the operand. 148 pub fn log2(&self) -> u8 { 149 match self { 150 OperandSize::S32 => 5, 151 OperandSize::S64 => 6, 152 OperandSize::S128 => 7, 153 } 154 } 155 156 /// Create an [`OperandSize`] from the given number of bytes. 157 pub fn from_bytes(bytes: u8) -> Self { 158 use OperandSize::*; 159 match bytes { 160 4 => S32, 161 8 => S64, 162 16 => S128, 163 _ => panic!("Invalid bytes {} for OperandSize", bytes), 164 } 165 } 166 } 167 168 /// An abstraction over a register or immediate. 169 #[derive(Copy, Clone, Debug, PartialEq, Eq)] 170 pub(crate) enum RegImm { 171 /// A register. 172 Reg(Reg), 173 /// A tagged immediate argument. 174 Imm(Imm), 175 } 176 177 /// An tagged representation of an immediate. 178 #[derive(Copy, Clone, Debug, PartialEq, Eq)] 179 pub(crate) enum Imm { 180 /// I32 immediate. 181 I32(u32), 182 /// I64 immediate. 183 I64(u64), 184 /// F32 immediate. 185 F32(u32), 186 /// F64 immediate. 187 F64(u64), 188 } 189 190 impl Imm { 191 /// Create a new I64 immediate. 192 pub fn i64(val: i64) -> Self { 193 Self::I64(val as u64) 194 } 195 196 /// Create a new I32 immediate. 197 pub fn i32(val: i32) -> Self { 198 Self::I32(val as u32) 199 } 200 201 /// Create a new F32 immediate. 202 // Temporary until support for f32.const is added. 203 #[allow(dead_code)] 204 pub fn f32(bits: u32) -> Self { 205 Self::F32(bits) 206 } 207 208 /// Create a new F64 immediate. 209 // Temporary until support for f64.const is added. 210 #[allow(dead_code)] 211 pub fn f64(bits: u64) -> Self { 212 Self::F64(bits) 213 } 214 215 /// Convert the immediate to i32, if possible. 216 pub fn to_i32(&self) -> Option<i32> { 217 match self { 218 Self::I32(v) => Some(*v as i32), 219 Self::I64(v) => i32::try_from(*v as i64).ok(), 220 _ => None, 221 } 222 } 223 } 224 225 #[derive(Copy, Clone, Debug)] 226 pub(crate) enum CalleeKind { 227 /// A function call to a raw address. 228 Indirect(Reg), 229 /// A function call to a local function. 230 Direct(u32), 231 /// Call to a well known LibCall. 232 Known(LibCall), 233 } 234 235 impl CalleeKind { 236 /// Creates a callee kind from a register. 237 pub fn indirect(reg: Reg) -> Self { 238 Self::Indirect(reg) 239 } 240 241 /// Creates a direct callee kind from a function index. 242 pub fn direct(index: u32) -> Self { 243 Self::Direct(index) 244 } 245 246 /// Creates a known callee kind from a libcall. 247 pub fn known(call: LibCall) -> Self { 248 Self::Known(call) 249 } 250 } 251 252 impl RegImm { 253 /// Register constructor. 254 pub fn reg(r: Reg) -> Self { 255 RegImm::Reg(r) 256 } 257 258 /// I64 immediate constructor. 259 pub fn i64(val: i64) -> Self { 260 RegImm::Imm(Imm::i64(val)) 261 } 262 263 /// I32 immediate constructor. 264 pub fn i32(val: i32) -> Self { 265 RegImm::Imm(Imm::i32(val)) 266 } 267 268 /// F32 immediate, stored using its bits representation. 269 // Temporary until support for f32.const is added. 270 #[allow(dead_code)] 271 pub fn f32(bits: u32) -> Self { 272 RegImm::Imm(Imm::f32(bits)) 273 } 274 275 /// F64 immediate, stored using its bits representation. 276 // Temporary until support for f64.const is added. 277 #[allow(dead_code)] 278 pub fn f64(bits: u64) -> Self { 279 RegImm::Imm(Imm::f64(bits)) 280 } 281 } 282 283 impl From<Reg> for RegImm { 284 fn from(r: Reg) -> Self { 285 Self::Reg(r) 286 } 287 } 288 289 pub enum RoundingMode { 290 Nearest, 291 Up, 292 Down, 293 Zero, 294 } 295 296 /// Generic MacroAssembler interface used by the code generation. 297 /// 298 /// The MacroAssembler trait aims to expose an interface, high-level enough, 299 /// so that each ISA can provide its own lowering to machine code. For example, 300 /// for WebAssembly operators that don't have a direct mapping to a machine 301 /// a instruction, the interface defines a signature matching the WebAssembly 302 /// operator, allowing each implementation to lower such operator entirely. 303 /// This approach attributes more responsibility to the MacroAssembler, but frees 304 /// the caller from concerning about assembling the right sequence of 305 /// instructions at the operator callsite. 306 /// 307 /// The interface defaults to a three-argument form for binary operations; 308 /// this allows a natural mapping to instructions for RISC architectures, 309 /// that use three-argument form. 310 /// This approach allows for a more general interface that can be restricted 311 /// where needed, in the case of architectures that use a two-argument form. 312 313 pub(crate) trait MacroAssembler { 314 /// The addressing mode. 315 type Address: Copy + Debug; 316 317 /// The pointer representation of the target ISA, 318 /// used to access information from [`VMOffsets`]. 319 type Ptr: PtrSize; 320 321 /// The ABI details of the target. 322 type ABI: abi::ABI; 323 324 /// Emit the function prologue. 325 fn prologue(&mut self); 326 327 /// Emit the function epilogue. 328 fn epilogue(&mut self, locals_size: u32); 329 330 /// Reserve stack space. 331 fn reserve_stack(&mut self, bytes: u32); 332 333 /// Free stack space. 334 fn free_stack(&mut self, bytes: u32); 335 336 /// Reset the stack pointer to the given offset; 337 /// 338 /// Used to reset the stack pointer to a given offset 339 /// when dealing with unreachable code. 340 fn reset_stack_pointer(&mut self, offset: SPOffset); 341 342 /// Get the address of a local slot. 343 fn local_address(&mut self, local: &LocalSlot) -> Self::Address; 344 345 /// Loads the address of the table element at a given index. Returns the 346 /// address of the table element using the provided register as base. 347 fn table_elem_address( 348 &mut self, 349 index: Reg, 350 base: Reg, 351 table_data: &TableData, 352 context: &mut CodeGenContext, 353 ) -> Self::Address; 354 355 /// Retrieves the size of the table, pushing the result to the value stack. 356 fn table_size(&mut self, table_data: &TableData, context: &mut CodeGenContext); 357 358 /// Constructs an address with an offset that is relative to the 359 /// current position of the stack pointer (e.g. [sp + (sp_offset - 360 /// offset)]. 361 fn address_from_sp(&self, offset: SPOffset) -> Self::Address; 362 363 /// Constructs an address with an offset that is absolute to the 364 /// current position of the stack pointer (e.g. [sp + offset]. 365 fn address_at_sp(&self, offset: SPOffset) -> Self::Address; 366 367 /// Alias for [`Self::address_at_reg`] using the VMContext register as 368 /// a base. The VMContext register is derived from the ABI type that is 369 /// associated to the MacroAssembler. 370 fn address_at_vmctx(&self, offset: u32) -> Self::Address; 371 372 /// Construct an address that is absolute to the current position 373 /// of the given register. 374 fn address_at_reg(&self, reg: Reg, offset: u32) -> Self::Address; 375 376 /// Emit a function call to either a local or external function. 377 fn call(&mut self, stack_args_size: u32, f: impl FnMut(&mut Self) -> CalleeKind) -> u32; 378 379 /// Get stack pointer offset. 380 fn sp_offset(&self) -> SPOffset; 381 382 /// Perform a stack store. 383 fn store(&mut self, src: RegImm, dst: Self::Address, size: OperandSize); 384 385 /// Perform a stack load. 386 fn load(&mut self, src: Self::Address, dst: Reg, size: OperandSize); 387 388 /// Alias for `MacroAssembler::load` with the operand size corresponding 389 /// to the pointer size of the target. 390 fn load_ptr(&mut self, src: Self::Address, dst: Reg); 391 392 /// Loads the effective address into destination. 393 fn load_addr(&mut self, _src: Self::Address, _dst: Reg, _size: OperandSize); 394 395 /// Alias for `MacroAssembler::store` with the operand size corresponding 396 /// to the pointer size of the target. 397 fn store_ptr(&mut self, src: Reg, dst: Self::Address); 398 399 /// Pop a value from the machine stack into the given register. 400 fn pop(&mut self, dst: Reg, size: OperandSize); 401 402 /// Perform a move. 403 fn mov(&mut self, src: RegImm, dst: Reg, size: OperandSize); 404 405 /// Perform a conditional move. 406 fn cmov(&mut self, src: Reg, dst: Reg, cc: IntCmpKind, size: OperandSize); 407 408 /// Performs a memory move of bytes from src to dest. 409 /// Bytes are moved in blocks of 8 bytes, where possible. 410 fn memmove(&mut self, src: SPOffset, dst: SPOffset, bytes: u32) { 411 debug_assert!(dst.as_u32() < src.as_u32()); 412 // At least 4 byte aligned. 413 debug_assert!(bytes % 4 == 0); 414 let mut remaining = bytes; 415 let word_bytes = <Self::ABI as abi::ABI>::word_bytes(); 416 let scratch = <Self::ABI as abi::ABI>::scratch_reg(); 417 let ptr_size: OperandSize = ptr_type_from_ptr_size(word_bytes as u8).into(); 418 419 let mut dst_offs = dst.as_u32() - bytes; 420 let mut src_offs = src.as_u32() - bytes; 421 422 while remaining >= word_bytes { 423 remaining -= word_bytes; 424 dst_offs += word_bytes; 425 src_offs += word_bytes; 426 427 self.load( 428 self.address_from_sp(SPOffset::from_u32(src_offs)), 429 scratch, 430 ptr_size, 431 ); 432 self.store( 433 scratch.into(), 434 self.address_from_sp(SPOffset::from_u32(dst_offs)), 435 ptr_size, 436 ); 437 } 438 439 if remaining > 0 { 440 let half_word = word_bytes / 2; 441 let ptr_size = OperandSize::from_bytes(half_word as u8); 442 debug_assert!(remaining == half_word); 443 dst_offs += half_word; 444 src_offs += half_word; 445 446 self.load( 447 self.address_from_sp(SPOffset::from_u32(src_offs)), 448 scratch, 449 ptr_size, 450 ); 451 self.store( 452 scratch.into(), 453 self.address_from_sp(SPOffset::from_u32(dst_offs)), 454 ptr_size, 455 ); 456 } 457 } 458 459 /// Perform add operation. 460 fn add(&mut self, dst: Reg, lhs: Reg, rhs: RegImm, size: OperandSize); 461 462 /// Perform subtraction operation. 463 fn sub(&mut self, dst: Reg, lhs: Reg, rhs: RegImm, size: OperandSize); 464 465 /// Perform multiplication operation. 466 fn mul(&mut self, dst: Reg, lhs: Reg, rhs: RegImm, size: OperandSize); 467 468 /// Perform a floating point add operation. 469 fn float_add(&mut self, dst: Reg, lhs: Reg, rhs: Reg, size: OperandSize); 470 471 /// Perform a floating point subtraction operation. 472 fn float_sub(&mut self, dst: Reg, lhs: Reg, rhs: Reg, size: OperandSize); 473 474 /// Perform a floating point multiply operation. 475 fn float_mul(&mut self, dst: Reg, lhs: Reg, rhs: Reg, size: OperandSize); 476 477 /// Perform a floating point divide operation. 478 fn float_div(&mut self, dst: Reg, lhs: Reg, rhs: Reg, size: OperandSize); 479 480 /// Perform a floating point minimum operation. In x86, this will emit 481 /// multiple instructions. 482 fn float_min(&mut self, dst: Reg, lhs: Reg, rhs: Reg, size: OperandSize); 483 484 /// Perform a floating point maximum operation. In x86, this will emit 485 /// multiple instructions. 486 fn float_max(&mut self, dst: Reg, lhs: Reg, rhs: Reg, size: OperandSize); 487 488 /// Perform a floating point copysign operation. In x86, this will emit 489 /// multiple instructions. 490 fn float_copysign(&mut self, dst: Reg, lhs: Reg, rhs: Reg, size: OperandSize); 491 492 /// Perform a floating point abs operation. 493 fn float_abs(&mut self, dst: Reg, size: OperandSize); 494 495 /// Perform a floating point negation operation. 496 fn float_neg(&mut self, dst: Reg, size: OperandSize); 497 498 /// Perform a floating point floor operation. 499 fn float_round(&mut self, mode: RoundingMode, context: &mut CodeGenContext, size: OperandSize); 500 501 /// Perform a floating point square root operation. 502 fn float_sqrt(&mut self, dst: Reg, src: Reg, size: OperandSize); 503 504 /// Perform logical and operation. 505 fn and(&mut self, dst: Reg, lhs: Reg, rhs: RegImm, size: OperandSize); 506 507 /// Perform logical or operation. 508 fn or(&mut self, dst: Reg, lhs: Reg, rhs: RegImm, size: OperandSize); 509 510 /// Perform logical exclusive or operation. 511 fn xor(&mut self, dst: Reg, lhs: Reg, rhs: RegImm, size: OperandSize); 512 513 /// Perform a shift operation. 514 /// Shift is special in that some architectures have specific expectations 515 /// regarding the location of the instruction arguments. To free the 516 /// caller from having to deal with the architecture specific constraints 517 /// we give this function access to the code generation context, allowing 518 /// each implementation to decide the lowering path. 519 fn shift(&mut self, context: &mut CodeGenContext, kind: ShiftKind, size: OperandSize); 520 521 /// Perform division operation. 522 /// Division is special in that some architectures have specific 523 /// expectations regarding the location of the instruction 524 /// arguments and regarding the location of the quotient / 525 /// remainder. To free the caller from having to deal with the 526 /// architecure specific contraints we give this function access 527 /// to the code generation context, allowing each implementation 528 /// to decide the lowering path. For cases in which division is a 529 /// unconstrained binary operation, the caller can decide to use 530 /// the `CodeGenContext::i32_binop` or `CodeGenContext::i64_binop` 531 /// functions. 532 fn div(&mut self, context: &mut CodeGenContext, kind: DivKind, size: OperandSize); 533 534 /// Calculate remainder. 535 fn rem(&mut self, context: &mut CodeGenContext, kind: RemKind, size: OperandSize); 536 537 /// Compare src and dst and put the result in dst. 538 fn cmp(&mut self, src: RegImm, dest: Reg, size: OperandSize); 539 540 /// Compare src and dst and put the result in dst. 541 /// This function will potentially emit a series of instructions. 542 fn cmp_with_set(&mut self, src: RegImm, dst: Reg, kind: IntCmpKind, size: OperandSize); 543 544 /// Compare floats in src1 and src2 and put the result in dst. 545 /// In x86, this will emit multiple instructions. 546 fn float_cmp_with_set( 547 &mut self, 548 src1: Reg, 549 src2: Reg, 550 dst: Reg, 551 kind: FloatCmpKind, 552 size: OperandSize, 553 ); 554 555 /// Count the number of leading zeroes in src and put the result in dst. 556 /// In x64, this will emit multiple instructions if the `has_lzcnt` flag is 557 /// false. 558 fn clz(&mut self, src: Reg, dst: Reg, size: OperandSize); 559 560 /// Count the number of trailing zeroes in src and put the result in dst.masm 561 /// In x64, this will emit multiple instructions if the `has_tzcnt` flag is 562 /// false. 563 fn ctz(&mut self, src: Reg, dst: Reg, size: OperandSize); 564 565 /// Push the register to the stack, returning the stack slot metadata. 566 // NB 567 // The stack alignment should not be assumed after any call to `push`, 568 // unless explicitly aligned otherwise. Typically, stack alignment is 569 // maintained at call sites and during the execution of 570 // epilogues. 571 fn push(&mut self, src: Reg, size: OperandSize) -> StackSlot; 572 573 /// Finalize the assembly and return the result. 574 fn finalize(self) -> MachBufferFinalized<Final>; 575 576 /// Zero a particular register. 577 fn zero(&mut self, reg: Reg); 578 579 /// Count the number of 1 bits in src and put the result in dst. In x64, 580 /// this will emit multiple instructions if the `has_popcnt` flag is false. 581 fn popcnt(&mut self, context: &mut CodeGenContext, size: OperandSize); 582 583 /// Zero a given memory range. 584 /// 585 /// The default implementation divides the given memory range 586 /// into word-sized slots. Then it unrolls a series of store 587 /// instructions, effectively assigning zero to each slot. 588 fn zero_mem_range(&mut self, mem: &Range<u32>) { 589 let word_size = <Self::ABI as abi::ABI>::word_bytes(); 590 if mem.is_empty() { 591 return; 592 } 593 594 let start = if mem.start % word_size == 0 { 595 mem.start 596 } else { 597 // Ensure that the start of the range is at least 4-byte aligned. 598 assert!(mem.start % 4 == 0); 599 let start = align_to(mem.start, word_size); 600 let addr: Self::Address = self.local_address(&LocalSlot::i32(start)); 601 self.store(RegImm::i32(0), addr, OperandSize::S32); 602 // Ensure that the new start of the range, is word-size aligned. 603 assert!(start % word_size == 0); 604 start 605 }; 606 607 let end = align_to(mem.end, word_size); 608 let slots = (end - start) / word_size; 609 610 if slots == 1 { 611 let slot = LocalSlot::i64(start + word_size); 612 let addr: Self::Address = self.local_address(&slot); 613 self.store(RegImm::i64(0), addr, OperandSize::S64); 614 } else { 615 // TODO 616 // Add an upper bound to this generation; 617 // given a considerably large amount of slots 618 // this will be inefficient. 619 let zero = <Self::ABI as abi::ABI>::scratch_reg(); 620 self.zero(zero); 621 let zero = RegImm::reg(zero); 622 623 for step in (start..end).into_iter().step_by(word_size as usize) { 624 let slot = LocalSlot::i64(step + word_size); 625 let addr: Self::Address = self.local_address(&slot); 626 self.store(zero, addr, OperandSize::S64); 627 } 628 } 629 } 630 631 /// Generate a label. 632 fn get_label(&mut self) -> MachLabel; 633 634 /// Bind the given label at the current code offset. 635 fn bind(&mut self, label: MachLabel); 636 637 /// Conditional branch. 638 /// 639 /// Performs a comparison between the two operands, 640 /// and immediately after emits a jump to the given 641 /// label destination if the condition is met. 642 fn branch( 643 &mut self, 644 kind: IntCmpKind, 645 lhs: RegImm, 646 rhs: Reg, 647 taken: MachLabel, 648 size: OperandSize, 649 ); 650 651 /// Emits and unconditional jump to the given label. 652 fn jmp(&mut self, target: MachLabel); 653 654 /// Emits a jump table sequence. The default label is specified as 655 /// the last element of the targets slice. 656 fn jmp_table(&mut self, targets: &[MachLabel], index: Reg, tmp: Reg); 657 658 /// Emit an unreachable code trap. 659 fn unreachable(&mut self); 660 661 /// Traps if the condition code is met. 662 fn trapif(&mut self, cc: IntCmpKind, code: TrapCode); 663 664 /// Trap if the source register is zero. 665 fn trapz(&mut self, src: Reg, code: TrapCode); 666 667 /// Ensures that the stack pointer is correctly positioned before an unconditional 668 /// jump according to the requirements of the destination target. 669 fn ensure_sp_for_jump(&mut self, target: SPOffset) { 670 let bytes = self 671 .sp_offset() 672 .as_u32() 673 .checked_sub(target.as_u32()) 674 .unwrap_or(0); 675 if bytes > 0 { 676 self.free_stack(bytes); 677 } 678 } 679 } 680