1 //! Assembler library implementation for x64. 2 3 use crate::{ 4 constant_pool::ConstantPool, 5 isa::{CallingConvention, reg::Reg}, 6 masm::{ 7 DivKind, Extend, ExtendKind, ExtendType, IntCmpKind, MulWideKind, OperandSize, RemKind, 8 RoundingMode, ShiftKind, Signed, V128ExtendKind, V128LoadExtendKind, Zero, 9 }, 10 reg::writable, 11 }; 12 use cranelift_codegen::{ 13 CallInfo, Final, MachBuffer, MachBufferFinalized, MachInst, MachInstEmit, MachInstEmitState, 14 MachLabel, PatchRegion, Writable, 15 ir::{ExternalName, MemFlags, SourceLoc, TrapCode, Type, UserExternalNameRef, types}, 16 isa::{ 17 unwind::UnwindInst, 18 x64::{ 19 AtomicRmwSeqOp, EmitInfo, EmitState, Inst, 20 args::{ 21 self, Amode, Avx512Opcode, AvxOpcode, CC, CmpOpcode, ExtMode, FromWritableReg, Gpr, 22 GprMem, GprMemImm, RegMem, RegMemImm, SyntheticAmode, WritableGpr, WritableXmm, 23 Xmm, XmmMem, XmmMemImm, 24 }, 25 encoding::rex::{RexFlags, encode_modrm}, 26 external::{PairedGpr, PairedXmm}, 27 settings as x64_settings, 28 }, 29 }, 30 settings, 31 }; 32 33 use crate::reg::WritableReg; 34 use cranelift_assembler_x64 as asm; 35 use wasmtime_environ::Unsigned; 36 37 use super::address::Address; 38 use smallvec::SmallVec; 39 40 // Conversions between winch-codegen x64 types and cranelift-codegen x64 types. 41 42 impl From<Reg> for RegMemImm { 43 fn from(reg: Reg) -> Self { 44 RegMemImm::reg(reg.into()) 45 } 46 } 47 48 impl From<Reg> for RegMem { 49 fn from(value: Reg) -> Self { 50 RegMem::Reg { reg: value.into() } 51 } 52 } 53 54 impl From<Reg> for WritableGpr { 55 fn from(reg: Reg) -> Self { 56 let writable = Writable::from_reg(reg.into()); 57 WritableGpr::from_writable_reg(writable).expect("valid writable gpr") 58 } 59 } 60 61 impl From<Reg> for WritableXmm { 62 fn from(reg: Reg) -> Self { 63 let writable = Writable::from_reg(reg.into()); 64 WritableXmm::from_writable_reg(writable).expect("valid writable xmm") 65 } 66 } 67 68 /// Convert a writable GPR register to the read-write pair expected by 69 /// `cranelift-codegen`. 70 fn pair_gpr(reg: WritableReg) -> PairedGpr { 71 assert!(reg.to_reg().is_int()); 72 let read = Gpr::unwrap_new(reg.to_reg().into()); 73 let write = WritableGpr::from_reg(reg.to_reg().into()); 74 PairedGpr { read, write } 75 } 76 77 impl From<Reg> for asm::Gpr<Gpr> { 78 fn from(reg: Reg) -> Self { 79 asm::Gpr::new(reg.into()) 80 } 81 } 82 83 impl From<Reg> for asm::GprMem<Gpr, Gpr> { 84 fn from(reg: Reg) -> Self { 85 asm::GprMem::Gpr(reg.into()) 86 } 87 } 88 89 /// Convert a writable XMM register to the read-write pair expected by 90 /// `cranelift-codegen`. 91 fn pair_xmm(reg: WritableReg) -> PairedXmm { 92 assert!(reg.to_reg().is_float()); 93 let read = Xmm::unwrap_new(reg.to_reg().into()); 94 let write = WritableXmm::from_reg(reg.to_reg().into()); 95 PairedXmm { read, write } 96 } 97 98 impl From<Reg> for asm::Xmm<Xmm> { 99 fn from(reg: Reg) -> Self { 100 asm::Xmm::new(reg.into()) 101 } 102 } 103 104 impl From<Reg> for asm::XmmMem<Xmm, Gpr> { 105 fn from(reg: Reg) -> Self { 106 asm::XmmMem::Xmm(reg.into()) 107 } 108 } 109 110 impl From<Reg> for Gpr { 111 fn from(reg: Reg) -> Self { 112 Gpr::unwrap_new(reg.into()) 113 } 114 } 115 116 impl From<Reg> for GprMem { 117 fn from(value: Reg) -> Self { 118 GprMem::unwrap_new(value.into()) 119 } 120 } 121 122 impl From<Reg> for GprMemImm { 123 fn from(reg: Reg) -> Self { 124 GprMemImm::unwrap_new(reg.into()) 125 } 126 } 127 128 impl From<Reg> for Xmm { 129 fn from(reg: Reg) -> Self { 130 Xmm::unwrap_new(reg.into()) 131 } 132 } 133 134 impl From<Reg> for XmmMem { 135 fn from(value: Reg) -> Self { 136 XmmMem::unwrap_new(value.into()) 137 } 138 } 139 140 impl From<Reg> for XmmMemImm { 141 fn from(value: Reg) -> Self { 142 XmmMemImm::unwrap_new(value.into()) 143 } 144 } 145 146 impl From<OperandSize> for args::OperandSize { 147 fn from(size: OperandSize) -> Self { 148 match size { 149 OperandSize::S8 => Self::Size8, 150 OperandSize::S16 => Self::Size16, 151 OperandSize::S32 => Self::Size32, 152 OperandSize::S64 => Self::Size64, 153 s => panic!("Invalid operand size {s:?}"), 154 } 155 } 156 } 157 158 impl From<IntCmpKind> for CC { 159 fn from(value: IntCmpKind) -> Self { 160 match value { 161 IntCmpKind::Eq => CC::Z, 162 IntCmpKind::Ne => CC::NZ, 163 IntCmpKind::LtS => CC::L, 164 IntCmpKind::LtU => CC::B, 165 IntCmpKind::GtS => CC::NLE, 166 IntCmpKind::GtU => CC::NBE, 167 IntCmpKind::LeS => CC::LE, 168 IntCmpKind::LeU => CC::BE, 169 IntCmpKind::GeS => CC::NL, 170 IntCmpKind::GeU => CC::NB, 171 } 172 } 173 } 174 175 impl<T: ExtendType> From<Extend<T>> for ExtMode { 176 fn from(value: Extend<T>) -> Self { 177 match value { 178 Extend::I32Extend8 => ExtMode::BL, 179 Extend::I32Extend16 => ExtMode::WL, 180 Extend::I64Extend8 => ExtMode::BQ, 181 Extend::I64Extend16 => ExtMode::WQ, 182 Extend::I64Extend32 => ExtMode::LQ, 183 Extend::__Kind(_) => unreachable!(), 184 } 185 } 186 } 187 188 impl From<ExtendKind> for ExtMode { 189 fn from(value: ExtendKind) -> Self { 190 match value { 191 ExtendKind::Signed(s) => s.into(), 192 ExtendKind::Unsigned(u) => u.into(), 193 } 194 } 195 } 196 197 /// Kinds of extends supported by `vpmov`. 198 pub(super) enum VpmovKind { 199 /// Sign extends 8 lanes of 8-bit integers to 8 lanes of 16-bit integers. 200 E8x8S, 201 /// Zero extends 8 lanes of 8-bit integers to 8 lanes of 16-bit integers. 202 E8x8U, 203 /// Sign extends 4 lanes of 16-bit integers to 4 lanes of 32-bit integers. 204 E16x4S, 205 /// Zero extends 4 lanes of 16-bit integers to 4 lanes of 32-bit integers. 206 E16x4U, 207 /// Sign extends 2 lanes of 32-bit integers to 2 lanes of 64-bit integers. 208 E32x2S, 209 /// Zero extends 2 lanes of 32-bit integers to 2 lanes of 64-bit integers. 210 E32x2U, 211 } 212 213 impl From<VpmovKind> for AvxOpcode { 214 fn from(value: VpmovKind) -> Self { 215 match value { 216 VpmovKind::E8x8S => AvxOpcode::Vpmovsxbw, 217 VpmovKind::E8x8U => AvxOpcode::Vpmovzxbw, 218 VpmovKind::E16x4S => AvxOpcode::Vpmovsxwd, 219 VpmovKind::E16x4U => AvxOpcode::Vpmovzxwd, 220 VpmovKind::E32x2S => AvxOpcode::Vpmovsxdq, 221 VpmovKind::E32x2U => AvxOpcode::Vpmovzxdq, 222 } 223 } 224 } 225 226 impl From<V128LoadExtendKind> for VpmovKind { 227 fn from(value: V128LoadExtendKind) -> Self { 228 match value { 229 V128LoadExtendKind::E8x8S => Self::E8x8S, 230 V128LoadExtendKind::E8x8U => Self::E8x8U, 231 V128LoadExtendKind::E16x4S => Self::E16x4S, 232 V128LoadExtendKind::E16x4U => Self::E16x4U, 233 V128LoadExtendKind::E32x2S => Self::E32x2S, 234 V128LoadExtendKind::E32x2U => Self::E32x2U, 235 } 236 } 237 } 238 239 impl From<V128ExtendKind> for VpmovKind { 240 fn from(value: V128ExtendKind) -> Self { 241 match value { 242 V128ExtendKind::LowI8x16S | V128ExtendKind::HighI8x16S => Self::E8x8S, 243 V128ExtendKind::LowI8x16U => Self::E8x8U, 244 V128ExtendKind::LowI16x8S | V128ExtendKind::HighI16x8S => Self::E16x4S, 245 V128ExtendKind::LowI16x8U => Self::E16x4U, 246 V128ExtendKind::LowI32x4S | V128ExtendKind::HighI32x4S => Self::E32x2S, 247 V128ExtendKind::LowI32x4U => Self::E32x2U, 248 _ => unimplemented!(), 249 } 250 } 251 } 252 253 /// Kinds of comparisons supported by `vcmp`. 254 pub(super) enum VcmpKind { 255 /// Equal comparison. 256 Eq, 257 /// Not equal comparison. 258 Ne, 259 /// Less than comparison. 260 Lt, 261 /// Less than or equal comparison. 262 Le, 263 /// Unordered comparison. Sets result to all 1s if either source operand is 264 /// NaN. 265 Unord, 266 } 267 268 /// Kinds of conversions supported by `vcvt`. 269 pub(super) enum VcvtKind { 270 /// Converts 32-bit integers to 32-bit floats. 271 I32ToF32, 272 /// Converts doubleword integers to double precision floats. 273 I32ToF64, 274 /// Converts double precision floats to single precision floats. 275 F64ToF32, 276 // Converts double precision floats to 32-bit integers. 277 F64ToI32, 278 /// Converts single precision floats to double precision floats. 279 F32ToF64, 280 /// Converts single precision floats to 32-bit integers. 281 F32ToI32, 282 } 283 284 /// Modes supported by `vround`. 285 pub(crate) enum VroundMode { 286 /// Rounds toward nearest (ties to even). 287 TowardNearest, 288 /// Rounds toward negative infinity. 289 TowardNegativeInfinity, 290 /// Rounds toward positive infinity. 291 TowardPositiveInfinity, 292 /// Rounds toward zero. 293 TowardZero, 294 } 295 296 /// Low level assembler implementation for x64. 297 pub(crate) struct Assembler { 298 /// The machine instruction buffer. 299 buffer: MachBuffer<Inst>, 300 /// Constant emission information. 301 emit_info: EmitInfo, 302 /// Emission state. 303 emit_state: EmitState, 304 /// x64 flags. 305 isa_flags: x64_settings::Flags, 306 /// Constant pool. 307 pool: ConstantPool, 308 } 309 310 impl Assembler { 311 /// Create a new x64 assembler. 312 pub fn new(shared_flags: settings::Flags, isa_flags: x64_settings::Flags) -> Self { 313 Self { 314 buffer: MachBuffer::<Inst>::new(), 315 emit_state: Default::default(), 316 emit_info: EmitInfo::new(shared_flags, isa_flags.clone()), 317 pool: ConstantPool::new(), 318 isa_flags, 319 } 320 } 321 322 /// Get a mutable reference to underlying 323 /// machine buffer. 324 pub fn buffer_mut(&mut self) -> &mut MachBuffer<Inst> { 325 &mut self.buffer 326 } 327 328 /// Get a reference to the underlying machine buffer. 329 pub fn buffer(&self) -> &MachBuffer<Inst> { 330 &self.buffer 331 } 332 333 /// Adds a constant to the constant pool and returns its address. 334 pub fn add_constant(&mut self, constant: &[u8]) -> Address { 335 let handle = self.pool.register(constant, &mut self.buffer); 336 Address::constant(handle) 337 } 338 339 /// Load a floating point constant, using the constant pool. 340 pub fn load_fp_const(&mut self, dst: WritableReg, constant: &[u8], size: OperandSize) { 341 let addr = self.add_constant(constant); 342 self.xmm_mov_mr(&addr, dst, size, MemFlags::trusted()); 343 } 344 345 /// Return the emitted code. 346 pub fn finalize(mut self, loc: Option<SourceLoc>) -> MachBufferFinalized<Final> { 347 let stencil = self 348 .buffer 349 .finish(&self.pool.constants(), self.emit_state.ctrl_plane_mut()); 350 stencil.apply_base_srcloc(loc.unwrap_or_default()) 351 } 352 353 fn emit(&mut self, inst: Inst) { 354 inst.emit(&mut self.buffer, &self.emit_info, &mut self.emit_state); 355 } 356 357 fn to_synthetic_amode(addr: &Address, memflags: MemFlags) -> SyntheticAmode { 358 match *addr { 359 Address::Offset { base, offset } => { 360 let amode = Amode::imm_reg(offset as i32, base.into()).with_flags(memflags); 361 SyntheticAmode::real(amode) 362 } 363 Address::Const(c) => SyntheticAmode::ConstantOffset(c), 364 Address::ImmRegRegShift { 365 simm32, 366 base, 367 index, 368 shift, 369 } => SyntheticAmode::Real(Amode::ImmRegRegShift { 370 simm32, 371 base: base.into(), 372 index: index.into(), 373 shift, 374 flags: memflags, 375 }), 376 } 377 } 378 379 /// Emit an unwind instruction. 380 pub fn unwind_inst(&mut self, inst: UnwindInst) { 381 self.emit(Inst::Unwind { inst }) 382 } 383 384 /// Push register. 385 pub fn push_r(&mut self, reg: Reg) { 386 let inst = asm::inst::pushq_o::new(reg).into(); 387 self.emit(Inst::External { inst }); 388 } 389 390 /// Pop to register. 391 pub fn pop_r(&mut self, dst: WritableReg) { 392 let writable: WritableGpr = dst.map(Into::into); 393 let inst = asm::inst::popq_o::new(writable).into(); 394 self.emit(Inst::External { inst }); 395 } 396 397 /// Return instruction. 398 pub fn ret(&mut self) { 399 self.emit(Inst::Ret { 400 stack_bytes_to_pop: 0, 401 }); 402 } 403 404 /// Register-to-register move. 405 pub fn mov_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) { 406 let dst: WritableGpr = dst.map(|r| r.into()); 407 let inst = match size { 408 OperandSize::S8 => asm::inst::movb_mr::new(dst, src).into(), 409 OperandSize::S16 => asm::inst::movw_mr::new(dst, src).into(), 410 OperandSize::S32 => asm::inst::movl_mr::new(dst, src).into(), 411 OperandSize::S64 => asm::inst::movq_mr::new(dst, src).into(), 412 _ => unreachable!(), 413 }; 414 self.emit(Inst::External { inst }); 415 } 416 417 /// Register-to-memory move. 418 pub fn mov_rm(&mut self, src: Reg, addr: &Address, size: OperandSize, flags: MemFlags) { 419 assert!(addr.is_offset()); 420 let dst = Self::to_synthetic_amode(addr, flags); 421 let inst = match size { 422 OperandSize::S8 => asm::inst::movb_mr::new(dst, src).into(), 423 OperandSize::S16 => asm::inst::movw_mr::new(dst, src).into(), 424 OperandSize::S32 => asm::inst::movl_mr::new(dst, src).into(), 425 OperandSize::S64 => asm::inst::movq_mr::new(dst, src).into(), 426 _ => unreachable!(), 427 }; 428 self.emit(Inst::External { inst }); 429 } 430 431 /// Immediate-to-memory move. 432 pub fn mov_im(&mut self, src: i32, addr: &Address, size: OperandSize, flags: MemFlags) { 433 assert!(addr.is_offset()); 434 let dst = Self::to_synthetic_amode(addr, flags); 435 let inst = match size { 436 OperandSize::S8 => { 437 let src = i8::try_from(src).unwrap(); 438 asm::inst::movb_mi::new(dst, src.unsigned()).into() 439 } 440 OperandSize::S16 => { 441 let src = i16::try_from(src).unwrap(); 442 asm::inst::movw_mi::new(dst, src.unsigned()).into() 443 } 444 OperandSize::S32 => asm::inst::movl_mi::new(dst, src.unsigned()).into(), 445 OperandSize::S64 => asm::inst::movq_mi_sxl::new(dst, src).into(), 446 _ => unreachable!(), 447 }; 448 self.emit(Inst::External { inst }); 449 } 450 451 /// Immediate-to-register move. 452 pub fn mov_ir(&mut self, imm: u64, dst: WritableReg, size: OperandSize) { 453 self.emit(Inst::imm(size.into(), imm, dst.map(Into::into))); 454 } 455 456 /// Zero-extend memory-to-register load. 457 pub fn movzx_mr( 458 &mut self, 459 addr: &Address, 460 dst: WritableReg, 461 ext: Option<Extend<Zero>>, 462 memflags: MemFlags, 463 ) { 464 let src = Self::to_synthetic_amode(addr, memflags); 465 466 if let Some(ext) = ext { 467 let dst = WritableGpr::from_reg(dst.to_reg().into()); 468 let inst = match ext.into() { 469 ExtMode::BL => asm::inst::movzbl_rm::new(dst, src).into(), 470 ExtMode::BQ => asm::inst::movzbq_rm::new(dst, src).into(), 471 ExtMode::WL => asm::inst::movzwl_rm::new(dst, src).into(), 472 ExtMode::WQ => asm::inst::movzwq_rm::new(dst, src).into(), 473 ExtMode::LQ => { 474 // This instruction selection may seem strange but is 475 // correct in 64-bit mode: section 3.4.1.1 of the Intel 476 // manual says that "32-bit operands generate a 32-bit 477 // result, zero-extended to a 64-bit result in the 478 // destination general-purpose register." This is applicable 479 // beyond `mov` but we use this fact to zero-extend `src` 480 // into `dst`. 481 asm::inst::movl_rm::new(dst, src).into() 482 } 483 }; 484 self.emit(Inst::External { inst }); 485 } else { 486 let dst = WritableGpr::from_reg(dst.to_reg().into()); 487 let inst = asm::inst::movq_rm::new(dst, src).into(); 488 self.emit(Inst::External { inst }); 489 } 490 } 491 492 // Sign-extend memory-to-register load. 493 pub fn movsx_mr( 494 &mut self, 495 addr: &Address, 496 dst: WritableReg, 497 ext: Extend<Signed>, 498 memflags: MemFlags, 499 ) { 500 let src = Self::to_synthetic_amode(addr, memflags); 501 let dst = WritableGpr::from_reg(dst.to_reg().into()); 502 let inst = match ext.into() { 503 ExtMode::BL => asm::inst::movsbl_rm::new(dst, src).into(), 504 ExtMode::BQ => asm::inst::movsbq_rm::new(dst, src).into(), 505 ExtMode::WL => asm::inst::movswl_rm::new(dst, src).into(), 506 ExtMode::WQ => asm::inst::movswq_rm::new(dst, src).into(), 507 ExtMode::LQ => asm::inst::movslq_rm::new(dst, src).into(), 508 }; 509 self.emit(Inst::External { inst }); 510 } 511 512 /// Register-to-register move with zero extension. 513 pub fn movzx_rr(&mut self, src: Reg, dst: WritableReg, kind: Extend<Zero>) { 514 let dst = WritableGpr::from_reg(dst.to_reg().into()); 515 let inst = match kind.into() { 516 ExtMode::BL => asm::inst::movzbl_rm::new(dst, src).into(), 517 ExtMode::BQ => asm::inst::movzbq_rm::new(dst, src).into(), 518 ExtMode::WL => asm::inst::movzwl_rm::new(dst, src).into(), 519 ExtMode::WQ => asm::inst::movzwq_rm::new(dst, src).into(), 520 ExtMode::LQ => { 521 // This instruction selection may seem strange but is correct in 522 // 64-bit mode: section 3.4.1.1 of the Intel manual says that 523 // "32-bit operands generate a 32-bit result, zero-extended to a 524 // 64-bit result in the destination general-purpose register." 525 // This is applicable beyond `mov` but we use this fact to 526 // zero-extend `src` into `dst`. 527 asm::inst::movl_rm::new(dst, src).into() 528 } 529 }; 530 self.emit(Inst::External { inst }); 531 } 532 533 /// Register-to-register move with sign extension. 534 pub fn movsx_rr(&mut self, src: Reg, dst: WritableReg, kind: Extend<Signed>) { 535 let dst = WritableGpr::from_reg(dst.to_reg().into()); 536 let inst = match kind.into() { 537 ExtMode::BL => asm::inst::movsbl_rm::new(dst, src).into(), 538 ExtMode::BQ => asm::inst::movsbq_rm::new(dst, src).into(), 539 ExtMode::WL => asm::inst::movswl_rm::new(dst, src).into(), 540 ExtMode::WQ => asm::inst::movswq_rm::new(dst, src).into(), 541 ExtMode::LQ => asm::inst::movslq_rm::new(dst, src).into(), 542 }; 543 self.emit(Inst::External { inst }); 544 } 545 546 /// Integer register conditional move. 547 pub fn cmov(&mut self, src: Reg, dst: WritableReg, cc: IntCmpKind, size: OperandSize) { 548 let dst: WritableGpr = dst.map(Into::into); 549 self.emit(Inst::Cmove { 550 size: size.into(), 551 cc: cc.into(), 552 consequent: src.into(), 553 alternative: dst.to_reg(), 554 dst, 555 }) 556 } 557 558 /// Single and double precision floating point 559 /// register-to-register move. 560 pub fn xmm_mov_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) { 561 let ty = match size { 562 OperandSize::S32 => types::F32, 563 OperandSize::S64 => types::F64, 564 OperandSize::S128 => types::I32X4, 565 OperandSize::S8 | OperandSize::S16 => unreachable!(), 566 }; 567 self.emit(Inst::gen_move(dst.map(|r| r.into()), src.into(), ty)); 568 } 569 570 /// Single and double precision floating point load. 571 pub fn xmm_mov_mr( 572 &mut self, 573 src: &Address, 574 dst: WritableReg, 575 size: OperandSize, 576 flags: MemFlags, 577 ) { 578 use OperandSize::*; 579 580 assert!(dst.to_reg().is_float()); 581 582 let src = Self::to_synthetic_amode(src, flags); 583 let dst: WritableXmm = dst.map(|r| r.into()); 584 let inst = match size { 585 S32 => asm::inst::movss_a_m::new(dst, src).into(), 586 S64 => asm::inst::movsd_a_m::new(dst, src).into(), 587 S128 => asm::inst::movdqu_a::new(dst, src).into(), 588 S8 | S16 => unreachable!(), 589 }; 590 self.emit(Inst::External { inst }); 591 } 592 593 /// Vector load and extend. 594 pub fn xmm_vpmov_mr( 595 &mut self, 596 src: &Address, 597 dst: WritableReg, 598 kind: VpmovKind, 599 flags: MemFlags, 600 ) { 601 assert!(dst.to_reg().is_float()); 602 603 let src = Self::to_synthetic_amode(src, flags); 604 605 self.emit(Inst::XmmUnaryRmRVex { 606 op: kind.into(), 607 src: XmmMem::unwrap_new(RegMem::mem(src)), 608 dst: dst.to_reg().into(), 609 }); 610 } 611 612 /// Extends vector of integers in `src` and puts results in `dst`. 613 pub fn xmm_vpmov_rr(&mut self, src: Reg, dst: WritableReg, kind: VpmovKind) { 614 self.emit(Inst::XmmUnaryRmRVex { 615 op: kind.into(), 616 src: src.into(), 617 dst: dst.to_reg().into(), 618 }); 619 } 620 621 /// Vector load and broadcast. 622 pub fn xmm_vpbroadcast_mr( 623 &mut self, 624 src: &Address, 625 dst: WritableReg, 626 size: OperandSize, 627 flags: MemFlags, 628 ) { 629 assert!(dst.to_reg().is_float()); 630 631 let src = Self::to_synthetic_amode(src, flags); 632 633 let op = match size { 634 OperandSize::S8 => AvxOpcode::Vpbroadcastb, 635 OperandSize::S16 => AvxOpcode::Vpbroadcastw, 636 OperandSize::S32 => AvxOpcode::Vpbroadcastd, 637 _ => unimplemented!(), 638 }; 639 640 self.emit(Inst::XmmUnaryRmRVex { 641 op, 642 src: XmmMem::unwrap_new(RegMem::mem(src)), 643 dst: dst.to_reg().into(), 644 }); 645 } 646 647 /// Value in `src` is broadcast into lanes of `size` in `dst`. 648 pub fn xmm_vpbroadcast_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) { 649 assert!(src.is_float() && dst.to_reg().is_float()); 650 651 let op = match size { 652 OperandSize::S8 => AvxOpcode::Vpbroadcastb, 653 OperandSize::S16 => AvxOpcode::Vpbroadcastw, 654 OperandSize::S32 => AvxOpcode::Vpbroadcastd, 655 _ => unimplemented!(), 656 }; 657 658 self.emit(Inst::XmmUnaryRmRVex { 659 op, 660 src: XmmMem::unwrap_new(src.into()), 661 dst: dst.to_reg().into(), 662 }); 663 } 664 665 /// Memory to register shuffle of bytes in vector. 666 pub fn xmm_vpshuf_mr( 667 &mut self, 668 src: &Address, 669 dst: WritableReg, 670 mask: u8, 671 size: OperandSize, 672 flags: MemFlags, 673 ) { 674 let dst: WritableXmm = dst.map(|r| r.into()); 675 let src = Self::to_synthetic_amode(src, flags); 676 let inst = match size { 677 OperandSize::S32 => asm::inst::vpshufd_a::new(dst, src, mask).into(), 678 _ => unimplemented!(), 679 }; 680 self.emit(Inst::External { inst }); 681 } 682 683 /// Register to register shuffle of bytes in vector. 684 pub fn xmm_vpshuf_rr(&mut self, src: Reg, dst: WritableReg, mask: u8, size: OperandSize) { 685 let dst: WritableXmm = dst.map(|r| r.into()); 686 687 let inst = match size { 688 OperandSize::S16 => asm::inst::vpshuflw_a::new(dst, src, mask).into(), 689 OperandSize::S32 => asm::inst::vpshufd_a::new(dst, src, mask).into(), 690 _ => unimplemented!(), 691 }; 692 693 self.emit(Inst::External { inst }); 694 } 695 696 /// Single and double precision floating point store. 697 pub fn xmm_mov_rm(&mut self, src: Reg, dst: &Address, size: OperandSize, flags: MemFlags) { 698 use OperandSize::*; 699 700 assert!(src.is_float()); 701 702 let dst = Self::to_synthetic_amode(dst, flags); 703 let src: Xmm = src.into(); 704 let inst = match size { 705 S32 => asm::inst::movss_c_m::new(dst, src).into(), 706 S64 => asm::inst::movsd_c_m::new(dst, src).into(), 707 S128 => asm::inst::movdqu_b::new(dst, src).into(), 708 S16 | S8 => unreachable!(), 709 }; 710 self.emit(Inst::External { inst }) 711 } 712 713 /// Floating point register conditional move. 714 pub fn xmm_cmov(&mut self, src: Reg, dst: WritableReg, cc: IntCmpKind, size: OperandSize) { 715 let dst: WritableXmm = dst.map(Into::into); 716 let ty = match size { 717 OperandSize::S32 => types::F32, 718 OperandSize::S64 => types::F64, 719 // Move the entire 128 bits via movdqa. 720 OperandSize::S128 => types::I32X4, 721 OperandSize::S8 | OperandSize::S16 => unreachable!(), 722 }; 723 724 self.emit(Inst::XmmCmove { 725 ty, 726 cc: cc.into(), 727 consequent: Xmm::unwrap_new(src.into()), 728 alternative: dst.to_reg(), 729 dst, 730 }) 731 } 732 733 /// Subtract register and register 734 pub fn sub_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) { 735 let dst = pair_gpr(dst); 736 let inst = match size { 737 OperandSize::S8 => asm::inst::subb_rm::new(dst, src).into(), 738 OperandSize::S16 => asm::inst::subw_rm::new(dst, src).into(), 739 OperandSize::S32 => asm::inst::subl_rm::new(dst, src).into(), 740 OperandSize::S64 => asm::inst::subq_rm::new(dst, src).into(), 741 OperandSize::S128 => unimplemented!(), 742 }; 743 self.emit(Inst::External { inst }); 744 } 745 746 /// Subtract immediate register. 747 pub fn sub_ir(&mut self, imm: i32, dst: WritableReg, size: OperandSize) { 748 let dst = pair_gpr(dst); 749 let inst = match size { 750 OperandSize::S8 => asm::inst::subb_mi::new(dst, u8::try_from(imm).unwrap()).into(), 751 OperandSize::S16 => asm::inst::subw_mi::new(dst, u16::try_from(imm).unwrap()).into(), 752 OperandSize::S32 => asm::inst::subl_mi::new(dst, imm as u32).into(), 753 OperandSize::S64 => asm::inst::subq_mi_sxl::new(dst, imm).into(), 754 OperandSize::S128 => unimplemented!(), 755 }; 756 self.emit(Inst::External { inst }); 757 } 758 759 /// "and" two registers. 760 pub fn and_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) { 761 let dst = pair_gpr(dst); 762 let inst = match size { 763 OperandSize::S8 => asm::inst::andb_rm::new(dst, src).into(), 764 OperandSize::S16 => asm::inst::andw_rm::new(dst, src).into(), 765 OperandSize::S32 => asm::inst::andl_rm::new(dst, src).into(), 766 OperandSize::S64 => asm::inst::andq_rm::new(dst, src).into(), 767 OperandSize::S128 => unimplemented!(), 768 }; 769 self.emit(Inst::External { inst }); 770 } 771 772 pub fn and_ir(&mut self, imm: i32, dst: WritableReg, size: OperandSize) { 773 let dst = pair_gpr(dst); 774 let inst = match size { 775 OperandSize::S8 => asm::inst::andb_mi::new(dst, u8::try_from(imm).unwrap()).into(), 776 OperandSize::S16 => asm::inst::andw_mi::new(dst, u16::try_from(imm).unwrap()).into(), 777 OperandSize::S32 => asm::inst::andl_mi::new(dst, imm as u32).into(), 778 OperandSize::S64 => asm::inst::andq_mi_sxl::new(dst, imm).into(), 779 OperandSize::S128 => unimplemented!(), 780 }; 781 self.emit(Inst::External { inst }); 782 } 783 784 /// "and" two float registers. 785 pub fn xmm_and_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) { 786 let dst = pair_xmm(dst); 787 let inst = match size { 788 OperandSize::S32 => asm::inst::andps_a::new(dst, src).into(), 789 OperandSize::S64 => asm::inst::andpd_a::new(dst, src).into(), 790 OperandSize::S8 | OperandSize::S16 | OperandSize::S128 => unreachable!(), 791 }; 792 self.emit(Inst::External { inst }); 793 } 794 795 /// "and not" two float registers. 796 pub fn xmm_andn_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) { 797 let dst = pair_xmm(dst); 798 let inst = match size { 799 OperandSize::S32 => asm::inst::andnps_a::new(dst, src).into(), 800 OperandSize::S64 => asm::inst::andnpd_a::new(dst, src).into(), 801 OperandSize::S8 | OperandSize::S16 | OperandSize::S128 => unreachable!(), 802 }; 803 self.emit(Inst::External { inst }); 804 } 805 806 pub fn gpr_to_xmm(&mut self, src: Reg, dst: WritableReg, size: OperandSize) { 807 let dst: WritableXmm = dst.map(|r| r.into()); 808 let inst = match size { 809 OperandSize::S32 => asm::inst::movd_a::new(dst, src).into(), 810 OperandSize::S64 => asm::inst::movq_a::new(dst, src).into(), 811 OperandSize::S8 | OperandSize::S16 | OperandSize::S128 => unreachable!(), 812 }; 813 814 self.emit(Inst::External { inst }); 815 } 816 817 pub fn xmm_to_gpr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) { 818 let dst: WritableGpr = dst.map(Into::into); 819 let src: Xmm = src.into(); 820 let inst = match size { 821 OperandSize::S32 => asm::inst::movd_b::new(dst, src).into(), 822 OperandSize::S64 => asm::inst::movq_b::new(dst, src).into(), 823 OperandSize::S8 | OperandSize::S16 | OperandSize::S128 => unreachable!(), 824 }; 825 826 self.emit(Inst::External { inst }) 827 } 828 829 /// Convert float to signed int. 830 pub fn cvt_float_to_sint_seq( 831 &mut self, 832 src: Reg, 833 dst: WritableReg, 834 tmp_gpr: Reg, 835 tmp_xmm: Reg, 836 src_size: OperandSize, 837 dst_size: OperandSize, 838 saturating: bool, 839 ) { 840 self.emit(Inst::CvtFloatToSintSeq { 841 dst_size: dst_size.into(), 842 src_size: src_size.into(), 843 is_saturating: saturating, 844 src: src.into(), 845 dst: dst.map(Into::into), 846 tmp_gpr: tmp_gpr.into(), 847 tmp_xmm: tmp_xmm.into(), 848 }); 849 } 850 851 /// Convert float to unsigned int. 852 pub fn cvt_float_to_uint_seq( 853 &mut self, 854 src: Reg, 855 dst: WritableReg, 856 tmp_gpr: Reg, 857 tmp_xmm: Reg, 858 tmp_xmm2: Reg, 859 src_size: OperandSize, 860 dst_size: OperandSize, 861 saturating: bool, 862 ) { 863 self.emit(Inst::CvtFloatToUintSeq { 864 dst_size: dst_size.into(), 865 src_size: src_size.into(), 866 is_saturating: saturating, 867 src: src.into(), 868 dst: dst.map(Into::into), 869 tmp_gpr: tmp_gpr.into(), 870 tmp_xmm: tmp_xmm.into(), 871 tmp_xmm2: tmp_xmm2.into(), 872 }); 873 } 874 875 /// Convert signed int to float. 876 pub fn cvt_sint_to_float( 877 &mut self, 878 src: Reg, 879 dst: WritableReg, 880 src_size: OperandSize, 881 dst_size: OperandSize, 882 ) { 883 use OperandSize::*; 884 let dst = pair_xmm(dst); 885 let inst = match (src_size, dst_size) { 886 (S32, S32) => asm::inst::cvtsi2ssl_a::new(dst, src).into(), 887 (S32, S64) => asm::inst::cvtsi2sdl_a::new(dst, src).into(), 888 (S64, S32) => asm::inst::cvtsi2ssq_a::new(dst, src).into(), 889 (S64, S64) => asm::inst::cvtsi2sdq_a::new(dst, src).into(), 890 _ => unreachable!(), 891 }; 892 self.emit(Inst::External { inst }); 893 } 894 895 /// Convert unsigned 64-bit int to float. 896 pub fn cvt_uint64_to_float_seq( 897 &mut self, 898 src: Reg, 899 dst: WritableReg, 900 tmp_gpr1: Reg, 901 tmp_gpr2: Reg, 902 dst_size: OperandSize, 903 ) { 904 self.emit(Inst::CvtUint64ToFloatSeq { 905 dst_size: dst_size.into(), 906 src: src.into(), 907 dst: dst.map(Into::into), 908 tmp_gpr1: tmp_gpr1.into(), 909 tmp_gpr2: tmp_gpr2.into(), 910 }); 911 } 912 913 /// Change precision of float. 914 pub fn cvt_float_to_float( 915 &mut self, 916 src: Reg, 917 dst: WritableReg, 918 src_size: OperandSize, 919 dst_size: OperandSize, 920 ) { 921 use OperandSize::*; 922 let dst = pair_xmm(dst); 923 let inst = match (src_size, dst_size) { 924 (S32, S64) => asm::inst::cvtss2sd_a::new(dst, src).into(), 925 (S64, S32) => asm::inst::cvtsd2ss_a::new(dst, src).into(), 926 _ => unimplemented!(), 927 }; 928 self.emit(Inst::External { inst }); 929 } 930 931 pub fn or_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) { 932 let dst = pair_gpr(dst); 933 let inst = match size { 934 OperandSize::S8 => asm::inst::orb_rm::new(dst, src).into(), 935 OperandSize::S16 => asm::inst::orw_rm::new(dst, src).into(), 936 OperandSize::S32 => asm::inst::orl_rm::new(dst, src).into(), 937 OperandSize::S64 => asm::inst::orq_rm::new(dst, src).into(), 938 OperandSize::S128 => unimplemented!(), 939 }; 940 self.emit(Inst::External { inst }); 941 } 942 943 pub fn or_ir(&mut self, imm: i32, dst: WritableReg, size: OperandSize) { 944 let dst = pair_gpr(dst); 945 let inst = match size { 946 OperandSize::S8 => asm::inst::orb_mi::new(dst, u8::try_from(imm).unwrap()).into(), 947 OperandSize::S16 => asm::inst::orw_mi::new(dst, u16::try_from(imm).unwrap()).into(), 948 OperandSize::S32 => asm::inst::orl_mi::new(dst, imm as u32).into(), 949 OperandSize::S64 => asm::inst::orq_mi_sxl::new(dst, imm).into(), 950 OperandSize::S128 => unimplemented!(), 951 }; 952 self.emit(Inst::External { inst }); 953 } 954 955 pub fn xmm_or_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) { 956 let dst = pair_xmm(dst); 957 let inst = match size { 958 OperandSize::S32 => asm::inst::orps_a::new(dst, src).into(), 959 OperandSize::S64 => asm::inst::orpd_a::new(dst, src).into(), 960 OperandSize::S8 | OperandSize::S16 | OperandSize::S128 => unreachable!(), 961 }; 962 self.emit(Inst::External { inst }); 963 } 964 965 /// Logical exclusive or with registers. 966 pub fn xor_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) { 967 let dst = pair_gpr(dst); 968 let inst = match size { 969 OperandSize::S8 => asm::inst::xorb_rm::new(dst, src).into(), 970 OperandSize::S16 => asm::inst::xorw_rm::new(dst, src).into(), 971 OperandSize::S32 => asm::inst::xorl_rm::new(dst, src).into(), 972 OperandSize::S64 => asm::inst::xorq_rm::new(dst, src).into(), 973 OperandSize::S128 => unimplemented!(), 974 }; 975 self.emit(Inst::External { inst }); 976 } 977 978 pub fn xor_ir(&mut self, imm: i32, dst: WritableReg, size: OperandSize) { 979 let dst = pair_gpr(dst); 980 let inst = match size { 981 OperandSize::S8 => asm::inst::xorb_mi::new(dst, u8::try_from(imm).unwrap()).into(), 982 OperandSize::S16 => asm::inst::xorw_mi::new(dst, u16::try_from(imm).unwrap()).into(), 983 OperandSize::S32 => asm::inst::xorl_mi::new(dst, imm as u32).into(), 984 OperandSize::S64 => asm::inst::xorq_mi_sxl::new(dst, imm).into(), 985 OperandSize::S128 => unimplemented!(), 986 }; 987 self.emit(Inst::External { inst }); 988 } 989 990 /// Logical exclusive or with float registers. 991 pub fn xmm_xor_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) { 992 let dst = pair_xmm(dst); 993 let inst = match size { 994 OperandSize::S32 => asm::inst::xorps_a::new(dst, src).into(), 995 OperandSize::S64 => asm::inst::xorpd_a::new(dst, src).into(), 996 OperandSize::S8 | OperandSize::S16 | OperandSize::S128 => unreachable!(), 997 }; 998 self.emit(Inst::External { inst }); 999 } 1000 1001 /// Shift with register and register. 1002 pub fn shift_rr(&mut self, src: Reg, dst: WritableReg, kind: ShiftKind, size: OperandSize) { 1003 let dst = pair_gpr(dst); 1004 let src: Gpr = src.into(); 1005 let inst = match (kind, size) { 1006 (ShiftKind::Shl, OperandSize::S32) => asm::inst::shll_mc::new(dst, src).into(), 1007 (ShiftKind::Shl, OperandSize::S64) => asm::inst::shlq_mc::new(dst, src).into(), 1008 (ShiftKind::Shl, _) => todo!(), 1009 (ShiftKind::ShrS, OperandSize::S32) => asm::inst::sarl_mc::new(dst, src).into(), 1010 (ShiftKind::ShrS, OperandSize::S64) => asm::inst::sarq_mc::new(dst, src).into(), 1011 (ShiftKind::ShrS, _) => todo!(), 1012 (ShiftKind::ShrU, OperandSize::S32) => asm::inst::shrl_mc::new(dst, src).into(), 1013 (ShiftKind::ShrU, OperandSize::S64) => asm::inst::shrq_mc::new(dst, src).into(), 1014 (ShiftKind::ShrU, _) => todo!(), 1015 (ShiftKind::Rotl, OperandSize::S32) => asm::inst::roll_mc::new(dst, src).into(), 1016 (ShiftKind::Rotl, OperandSize::S64) => asm::inst::rolq_mc::new(dst, src).into(), 1017 (ShiftKind::Rotl, _) => todo!(), 1018 (ShiftKind::Rotr, OperandSize::S32) => asm::inst::rorl_mc::new(dst, src).into(), 1019 (ShiftKind::Rotr, OperandSize::S64) => asm::inst::rorq_mc::new(dst, src).into(), 1020 (ShiftKind::Rotr, _) => todo!(), 1021 }; 1022 self.emit(Inst::External { inst }); 1023 } 1024 1025 /// Shift with immediate and register. 1026 pub fn shift_ir(&mut self, imm: u8, dst: WritableReg, kind: ShiftKind, size: OperandSize) { 1027 let dst = pair_gpr(dst); 1028 let inst = match (kind, size) { 1029 (ShiftKind::Shl, OperandSize::S32) => asm::inst::shll_mi::new(dst, imm).into(), 1030 (ShiftKind::Shl, OperandSize::S64) => asm::inst::shlq_mi::new(dst, imm).into(), 1031 (ShiftKind::Shl, _) => todo!(), 1032 (ShiftKind::ShrS, OperandSize::S32) => asm::inst::sarl_mi::new(dst, imm).into(), 1033 (ShiftKind::ShrS, OperandSize::S64) => asm::inst::sarq_mi::new(dst, imm).into(), 1034 (ShiftKind::ShrS, _) => todo!(), 1035 (ShiftKind::ShrU, OperandSize::S32) => asm::inst::shrl_mi::new(dst, imm).into(), 1036 (ShiftKind::ShrU, OperandSize::S64) => asm::inst::shrq_mi::new(dst, imm).into(), 1037 (ShiftKind::ShrU, _) => todo!(), 1038 (ShiftKind::Rotl, OperandSize::S32) => asm::inst::roll_mi::new(dst, imm).into(), 1039 (ShiftKind::Rotl, OperandSize::S64) => asm::inst::rolq_mi::new(dst, imm).into(), 1040 (ShiftKind::Rotl, _) => todo!(), 1041 (ShiftKind::Rotr, OperandSize::S32) => asm::inst::rorl_mi::new(dst, imm).into(), 1042 (ShiftKind::Rotr, OperandSize::S64) => asm::inst::rorq_mi::new(dst, imm).into(), 1043 (ShiftKind::Rotr, _) => todo!(), 1044 }; 1045 self.emit(Inst::External { inst }); 1046 } 1047 1048 /// Signed/unsigned division. 1049 /// 1050 /// Emits a sequence of instructions to ensure the correctness of 1051 /// the division invariants. This function assumes that the 1052 /// caller has correctly allocated the dividend as `(rdx:rax)` and 1053 /// accounted for the quotient to be stored in `rax`. 1054 pub fn div(&mut self, divisor: Reg, dst: (Reg, Reg), kind: DivKind, size: OperandSize) { 1055 let trap = match kind { 1056 // Signed division has two trapping conditions, integer overflow and 1057 // divide-by-zero. Check for divide-by-zero explicitly and let the 1058 // hardware detect overflow. 1059 DivKind::Signed => { 1060 self.emit(Inst::CmpRmiR { 1061 size: size.into(), 1062 src1: divisor.into(), 1063 src2: GprMemImm::unwrap_new(RegMemImm::imm(0)), 1064 opcode: CmpOpcode::Cmp, 1065 }); 1066 self.emit(Inst::TrapIf { 1067 cc: CC::Z, 1068 trap_code: TrapCode::INTEGER_DIVISION_BY_ZERO, 1069 }); 1070 1071 // Sign-extend the dividend with tailor-made instructoins for 1072 // just this operation. 1073 let ext_dst: WritableGpr = dst.1.into(); 1074 let ext_src: Gpr = dst.0.into(); 1075 let inst = match size { 1076 OperandSize::S32 => asm::inst::cltd_zo::new(ext_dst, ext_src).into(), 1077 OperandSize::S64 => asm::inst::cqto_zo::new(ext_dst, ext_src).into(), 1078 _ => unimplemented!(), 1079 }; 1080 self.emit(Inst::External { inst }); 1081 TrapCode::INTEGER_OVERFLOW 1082 } 1083 1084 // Unsigned division only traps in one case, on divide-by-zero, so 1085 // defer that to the trap opcode. 1086 // 1087 // The divisor_hi reg is initialized with zero through an 1088 // xor-against-itself op. 1089 DivKind::Unsigned => { 1090 self.xor_rr(dst.1, writable!(dst.1), size); 1091 TrapCode::INTEGER_DIVISION_BY_ZERO 1092 } 1093 }; 1094 let dst0 = pair_gpr(writable!(dst.0)); 1095 let dst1 = pair_gpr(writable!(dst.1)); 1096 let inst = match (kind, size) { 1097 (DivKind::Signed, OperandSize::S32) => { 1098 asm::inst::idivl_m::new(dst0, dst1, divisor, trap).into() 1099 } 1100 (DivKind::Unsigned, OperandSize::S32) => { 1101 asm::inst::divl_m::new(dst0, dst1, divisor, trap).into() 1102 } 1103 (DivKind::Signed, OperandSize::S64) => { 1104 asm::inst::idivq_m::new(dst0, dst1, divisor, trap).into() 1105 } 1106 (DivKind::Unsigned, OperandSize::S64) => { 1107 asm::inst::divq_m::new(dst0, dst1, divisor, trap).into() 1108 } 1109 _ => todo!(), 1110 }; 1111 self.emit(Inst::External { inst }); 1112 } 1113 1114 /// Signed/unsigned remainder. 1115 /// 1116 /// Emits a sequence of instructions to ensure the correctness of the 1117 /// division invariants and ultimately calculate the remainder. 1118 /// This function assumes that the 1119 /// caller has correctly allocated the dividend as `(rdx:rax)` and 1120 /// accounted for the remainder to be stored in `rdx`. 1121 pub fn rem(&mut self, divisor: Reg, dst: (Reg, Reg), kind: RemKind, size: OperandSize) { 1122 match kind { 1123 // Signed remainder goes through a pseudo-instruction which has 1124 // some internal branching. The `dividend_hi`, or `rdx`, is 1125 // initialized here with a `SignExtendData` instruction. 1126 RemKind::Signed => { 1127 let ext_dst: WritableGpr = dst.1.into(); 1128 1129 // Initialize `dividend_hi`, or `rdx`, with a tailor-made 1130 // instruction for this operation. 1131 let ext_src: Gpr = dst.0.into(); 1132 let inst = match size { 1133 OperandSize::S32 => asm::inst::cltd_zo::new(ext_dst, ext_src).into(), 1134 OperandSize::S64 => asm::inst::cqto_zo::new(ext_dst, ext_src).into(), 1135 _ => unimplemented!(), 1136 }; 1137 self.emit(Inst::External { inst }); 1138 self.emit(Inst::CheckedSRemSeq { 1139 size: size.into(), 1140 divisor: divisor.into(), 1141 dividend_lo: dst.0.into(), 1142 dividend_hi: dst.1.into(), 1143 dst_quotient: dst.0.into(), 1144 dst_remainder: dst.1.into(), 1145 }); 1146 } 1147 1148 // Unsigned remainder initializes `dividend_hi` with zero and 1149 // then executes a normal `div` instruction. 1150 RemKind::Unsigned => { 1151 self.xor_rr(dst.1, writable!(dst.1), size); 1152 let dst0 = pair_gpr(writable!(dst.0)); 1153 let dst1 = pair_gpr(writable!(dst.1)); 1154 let trap = TrapCode::INTEGER_DIVISION_BY_ZERO; 1155 let inst = match size { 1156 OperandSize::S32 => asm::inst::divl_m::new(dst0, dst1, divisor, trap).into(), 1157 OperandSize::S64 => asm::inst::divq_m::new(dst0, dst1, divisor, trap).into(), 1158 _ => todo!(), 1159 }; 1160 self.emit(Inst::External { inst }); 1161 } 1162 } 1163 } 1164 1165 /// Multiply immediate and register. 1166 pub fn mul_ir(&mut self, imm: i32, dst: WritableReg, size: OperandSize) { 1167 use OperandSize::*; 1168 let src = dst.to_reg(); 1169 let dst: WritableGpr = dst.to_reg().into(); 1170 let inst = match size { 1171 S16 => asm::inst::imulw_rmi::new(dst, src, u16::try_from(imm).unwrap()).into(), 1172 S32 => asm::inst::imull_rmi::new(dst, src, imm as u32).into(), 1173 S64 => asm::inst::imulq_rmi_sxl::new(dst, src, imm).into(), 1174 S8 | S128 => unimplemented!(), 1175 }; 1176 self.emit(Inst::External { inst }); 1177 } 1178 1179 /// Multiply register and register. 1180 pub fn mul_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) { 1181 use OperandSize::*; 1182 let dst = pair_gpr(dst); 1183 let inst = match size { 1184 S16 => asm::inst::imulw_rm::new(dst, src).into(), 1185 S32 => asm::inst::imull_rm::new(dst, src).into(), 1186 S64 => asm::inst::imulq_rm::new(dst, src).into(), 1187 S8 | S128 => unimplemented!(), 1188 }; 1189 self.emit(Inst::External { inst }); 1190 } 1191 1192 /// Add immediate and register. 1193 pub fn add_ir(&mut self, imm: i32, dst: WritableReg, size: OperandSize) { 1194 let dst = pair_gpr(dst); 1195 let inst = match size { 1196 OperandSize::S8 => asm::inst::addb_mi::new(dst, u8::try_from(imm).unwrap()).into(), 1197 OperandSize::S16 => asm::inst::addw_mi::new(dst, u16::try_from(imm).unwrap()).into(), 1198 OperandSize::S32 => asm::inst::addl_mi::new(dst, imm as u32).into(), 1199 OperandSize::S64 => asm::inst::addq_mi_sxl::new(dst, imm).into(), 1200 OperandSize::S128 => unimplemented!(), 1201 }; 1202 self.emit(Inst::External { inst }); 1203 } 1204 1205 /// Add register and register. 1206 pub fn add_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) { 1207 let dst = pair_gpr(dst); 1208 let inst = match size { 1209 OperandSize::S8 => asm::inst::addb_rm::new(dst, src).into(), 1210 OperandSize::S16 => asm::inst::addw_rm::new(dst, src).into(), 1211 OperandSize::S32 => asm::inst::addl_rm::new(dst, src).into(), 1212 OperandSize::S64 => asm::inst::addq_rm::new(dst, src).into(), 1213 OperandSize::S128 => unimplemented!(), 1214 }; 1215 self.emit(Inst::External { inst }); 1216 } 1217 1218 pub fn lock_xadd( 1219 &mut self, 1220 addr: Address, 1221 dst: WritableReg, 1222 size: OperandSize, 1223 flags: MemFlags, 1224 ) { 1225 assert!(addr.is_offset()); 1226 let mem = Self::to_synthetic_amode(&addr, flags); 1227 let dst = pair_gpr(dst); 1228 let inst = match size { 1229 OperandSize::S8 => asm::inst::lock_xaddb_mr::new(mem, dst).into(), 1230 OperandSize::S16 => asm::inst::lock_xaddw_mr::new(mem, dst).into(), 1231 OperandSize::S32 => asm::inst::lock_xaddl_mr::new(mem, dst).into(), 1232 OperandSize::S64 => asm::inst::lock_xaddq_mr::new(mem, dst).into(), 1233 OperandSize::S128 => unimplemented!(), 1234 }; 1235 1236 self.emit(Inst::External { inst }); 1237 } 1238 1239 pub fn atomic_rmw_seq( 1240 &mut self, 1241 addr: Address, 1242 operand: Reg, 1243 dst: WritableReg, 1244 temp: WritableReg, 1245 size: OperandSize, 1246 flags: MemFlags, 1247 op: AtomicRmwSeqOp, 1248 ) { 1249 assert!(addr.is_offset()); 1250 let mem = Self::to_synthetic_amode(&addr, flags); 1251 self.emit(Inst::AtomicRmwSeq { 1252 ty: Type::int_with_byte_size(size.bytes() as _).unwrap(), 1253 mem, 1254 operand: operand.into(), 1255 temp: temp.map(Into::into), 1256 dst_old: dst.map(Into::into), 1257 op, 1258 }); 1259 } 1260 1261 pub fn xchg(&mut self, addr: Address, dst: WritableReg, size: OperandSize, flags: MemFlags) { 1262 assert!(addr.is_offset()); 1263 let mem = Self::to_synthetic_amode(&addr, flags); 1264 let dst = pair_gpr(dst); 1265 let inst = match size { 1266 OperandSize::S8 => asm::inst::xchgb_rm::new(dst, mem).into(), 1267 OperandSize::S16 => asm::inst::xchgw_rm::new(dst, mem).into(), 1268 OperandSize::S32 => asm::inst::xchgl_rm::new(dst, mem).into(), 1269 OperandSize::S64 => asm::inst::xchgq_rm::new(dst, mem).into(), 1270 OperandSize::S128 => unimplemented!(), 1271 }; 1272 1273 self.emit(Inst::External { inst }); 1274 } 1275 pub fn cmpxchg( 1276 &mut self, 1277 addr: Address, 1278 replacement: Reg, 1279 dst: WritableReg, 1280 size: OperandSize, 1281 flags: MemFlags, 1282 ) { 1283 assert!(addr.is_offset()); 1284 let mem = Self::to_synthetic_amode(&addr, flags); 1285 let dst = pair_gpr(dst); 1286 let inst = match size { 1287 OperandSize::S8 => asm::inst::lock_cmpxchgb_mr::new(mem, replacement, dst).into(), 1288 OperandSize::S16 => asm::inst::lock_cmpxchgw_mr::new(mem, replacement, dst).into(), 1289 OperandSize::S32 => asm::inst::lock_cmpxchgl_mr::new(mem, replacement, dst).into(), 1290 OperandSize::S64 => asm::inst::lock_cmpxchgq_mr::new(mem, replacement, dst).into(), 1291 OperandSize::S128 => unimplemented!(), 1292 }; 1293 1294 self.emit(Inst::External { inst }); 1295 } 1296 1297 pub fn cmp_ir(&mut self, src1: Reg, imm: i32, size: OperandSize) { 1298 let imm = RegMemImm::imm(imm as u32); 1299 1300 self.emit(Inst::CmpRmiR { 1301 size: size.into(), 1302 opcode: CmpOpcode::Cmp, 1303 src1: src1.into(), 1304 src2: GprMemImm::unwrap_new(imm), 1305 }); 1306 } 1307 1308 pub fn cmp_rr(&mut self, src1: Reg, src2: Reg, size: OperandSize) { 1309 self.emit(Inst::CmpRmiR { 1310 size: size.into(), 1311 opcode: CmpOpcode::Cmp, 1312 src1: src1.into(), 1313 src2: src2.into(), 1314 }); 1315 } 1316 1317 /// Compares values in src1 and src2 and sets ZF, PF, and CF flags in EFLAGS 1318 /// register. 1319 pub fn ucomis(&mut self, src1: Reg, src2: Reg, size: OperandSize) { 1320 let inst = match size { 1321 OperandSize::S32 => asm::inst::ucomiss_a::new(src1, src2).into(), 1322 OperandSize::S64 => asm::inst::ucomisd_a::new(src1, src2).into(), 1323 OperandSize::S8 | OperandSize::S16 | OperandSize::S128 => unreachable!(), 1324 }; 1325 self.emit(Inst::External { inst }); 1326 } 1327 1328 pub fn popcnt(&mut self, src: Reg, dst: WritableReg, size: OperandSize) { 1329 assert!( 1330 self.isa_flags.has_popcnt() && self.isa_flags.has_sse42(), 1331 "Requires has_popcnt and has_sse42 flags" 1332 ); 1333 let dst = WritableGpr::from_reg(dst.to_reg().into()); 1334 let inst = match size { 1335 OperandSize::S16 => asm::inst::popcntw_rm::new(dst, src).into(), 1336 OperandSize::S32 => asm::inst::popcntl_rm::new(dst, src).into(), 1337 OperandSize::S64 => asm::inst::popcntq_rm::new(dst, src).into(), 1338 OperandSize::S8 | OperandSize::S128 => unreachable!(), 1339 }; 1340 self.emit(Inst::External { inst }); 1341 } 1342 1343 /// Emit a test instruction with two register operands. 1344 pub fn test_rr(&mut self, src1: Reg, src2: Reg, size: OperandSize) { 1345 self.emit(Inst::CmpRmiR { 1346 size: size.into(), 1347 opcode: CmpOpcode::Test, 1348 src1: src1.into(), 1349 src2: src2.into(), 1350 }) 1351 } 1352 1353 /// Set value in dst to `0` or `1` based on flags in status register and 1354 /// [`CmpKind`]. 1355 pub fn setcc(&mut self, kind: IntCmpKind, dst: WritableReg) { 1356 self.setcc_impl(kind.into(), dst); 1357 } 1358 1359 /// Set value in dst to `1` if parity flag in status register is set, `0` 1360 /// otherwise. 1361 pub fn setp(&mut self, dst: WritableReg) { 1362 self.setcc_impl(CC::P, dst); 1363 } 1364 1365 /// Set value in dst to `1` if parity flag in status register is not set, 1366 /// `0` otherwise. 1367 pub fn setnp(&mut self, dst: WritableReg) { 1368 self.setcc_impl(CC::NP, dst); 1369 } 1370 1371 fn setcc_impl(&mut self, cc: CC, dst: WritableReg) { 1372 // Clear the dst register or bits 1 to 31 may be incorrectly set. 1373 // Don't use xor since it updates the status register. 1374 let dst: WritableGpr = dst.map(Into::into); 1375 let inst = asm::inst::movl_oi::new(dst, 0).into(); 1376 self.emit(Inst::External { inst }); 1377 // Copy correct bit from status register into dst register. 1378 self.emit(Inst::Setcc { cc, dst }); 1379 } 1380 1381 /// Store the count of leading zeroes in src in dst. 1382 /// Requires `has_lzcnt` flag. 1383 pub fn lzcnt(&mut self, src: Reg, dst: WritableReg, size: OperandSize) { 1384 assert!(self.isa_flags.has_lzcnt(), "Requires has_lzcnt flag"); 1385 let dst = WritableGpr::from_reg(dst.to_reg().into()); 1386 let inst = match size { 1387 OperandSize::S16 => asm::inst::lzcntw_rm::new(dst, src).into(), 1388 OperandSize::S32 => asm::inst::lzcntl_rm::new(dst, src).into(), 1389 OperandSize::S64 => asm::inst::lzcntq_rm::new(dst, src).into(), 1390 OperandSize::S8 | OperandSize::S128 => unreachable!(), 1391 }; 1392 self.emit(Inst::External { inst }); 1393 } 1394 1395 /// Store the count of trailing zeroes in src in dst. 1396 /// Requires `has_bmi1` flag. 1397 pub fn tzcnt(&mut self, src: Reg, dst: WritableReg, size: OperandSize) { 1398 assert!(self.isa_flags.has_bmi1(), "Requires has_bmi1 flag"); 1399 let dst = WritableGpr::from_reg(dst.to_reg().into()); 1400 let inst = match size { 1401 OperandSize::S16 => asm::inst::tzcntw_a::new(dst, src).into(), 1402 OperandSize::S32 => asm::inst::tzcntl_a::new(dst, src).into(), 1403 OperandSize::S64 => asm::inst::tzcntq_a::new(dst, src).into(), 1404 OperandSize::S8 | OperandSize::S128 => unreachable!(), 1405 }; 1406 self.emit(Inst::External { inst }); 1407 } 1408 1409 /// Stores position of the most significant bit set in src in dst. 1410 /// Zero flag is set if src is equal to 0. 1411 pub fn bsr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) { 1412 let dst: WritableGpr = WritableGpr::from_reg(dst.to_reg().into()); 1413 let inst = match size { 1414 OperandSize::S16 => asm::inst::bsrw_rm::new(dst, src).into(), 1415 OperandSize::S32 => asm::inst::bsrl_rm::new(dst, src).into(), 1416 OperandSize::S64 => asm::inst::bsrq_rm::new(dst, src).into(), 1417 OperandSize::S8 | OperandSize::S128 => unreachable!(), 1418 }; 1419 self.emit(Inst::External { inst }); 1420 } 1421 1422 /// Performs integer negation on `src` and places result in `dst`. 1423 pub fn neg(&mut self, read: Reg, write: WritableReg, size: OperandSize) { 1424 let gpr = PairedGpr { 1425 read: read.into(), 1426 write: WritableGpr::from_reg(write.to_reg().into()), 1427 }; 1428 let inst = match size { 1429 OperandSize::S8 => asm::inst::negb_m::new(gpr).into(), 1430 OperandSize::S16 => asm::inst::negw_m::new(gpr).into(), 1431 OperandSize::S32 => asm::inst::negl_m::new(gpr).into(), 1432 OperandSize::S64 => asm::inst::negq_m::new(gpr).into(), 1433 OperandSize::S128 => unreachable!(), 1434 }; 1435 self.emit(Inst::External { inst }); 1436 } 1437 1438 /// Stores position of the least significant bit set in src in dst. 1439 /// Zero flag is set if src is equal to 0. 1440 pub fn bsf(&mut self, src: Reg, dst: WritableReg, size: OperandSize) { 1441 let dst: WritableGpr = WritableGpr::from_reg(dst.to_reg().into()); 1442 let inst = match size { 1443 OperandSize::S16 => asm::inst::bsfw_rm::new(dst, src).into(), 1444 OperandSize::S32 => asm::inst::bsfl_rm::new(dst, src).into(), 1445 OperandSize::S64 => asm::inst::bsfq_rm::new(dst, src).into(), 1446 OperandSize::S8 | OperandSize::S128 => unreachable!(), 1447 }; 1448 self.emit(Inst::External { inst }); 1449 } 1450 1451 /// Performs float addition on src and dst and places result in dst. 1452 pub fn xmm_add_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) { 1453 let dst = pair_xmm(dst); 1454 let inst = match size { 1455 OperandSize::S32 => asm::inst::addss_a::new(dst, src).into(), 1456 OperandSize::S64 => asm::inst::addsd_a::new(dst, src).into(), 1457 OperandSize::S8 | OperandSize::S16 | OperandSize::S128 => unreachable!(), 1458 }; 1459 self.emit(Inst::External { inst }); 1460 } 1461 1462 /// Performs float subtraction on src and dst and places result in dst. 1463 pub fn xmm_sub_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) { 1464 let dst = pair_xmm(dst); 1465 let inst = match size { 1466 OperandSize::S32 => asm::inst::subss_a::new(dst, src).into(), 1467 OperandSize::S64 => asm::inst::subsd_a::new(dst, src).into(), 1468 OperandSize::S8 | OperandSize::S16 | OperandSize::S128 => unreachable!(), 1469 }; 1470 self.emit(Inst::External { inst }); 1471 } 1472 1473 /// Performs float multiplication on src and dst and places result in dst. 1474 pub fn xmm_mul_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) { 1475 use OperandSize::*; 1476 let dst = pair_xmm(dst); 1477 let inst = match size { 1478 S32 => asm::inst::mulss_a::new(dst, src).into(), 1479 S64 => asm::inst::mulsd_a::new(dst, src).into(), 1480 S8 | S16 | S128 => unreachable!(), 1481 }; 1482 self.emit(Inst::External { inst }); 1483 } 1484 1485 /// Performs float division on src and dst and places result in dst. 1486 pub fn xmm_div_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) { 1487 let dst = pair_xmm(dst); 1488 let inst = match size { 1489 OperandSize::S32 => asm::inst::divss_a::new(dst, src).into(), 1490 OperandSize::S64 => asm::inst::divsd_a::new(dst, src).into(), 1491 OperandSize::S8 | OperandSize::S16 | OperandSize::S128 => unreachable!(), 1492 }; 1493 self.emit(Inst::External { inst }); 1494 } 1495 1496 /// Minimum for src and dst XMM registers with results put in dst. 1497 pub fn xmm_min_seq(&mut self, src: Reg, dst: WritableReg, size: OperandSize) { 1498 self.emit(Inst::XmmMinMaxSeq { 1499 size: size.into(), 1500 is_min: true, 1501 lhs: src.into(), 1502 rhs: dst.to_reg().into(), 1503 dst: dst.map(Into::into), 1504 }); 1505 } 1506 1507 /// Maximum for src and dst XMM registers with results put in dst. 1508 pub fn xmm_max_seq(&mut self, src: Reg, dst: WritableReg, size: OperandSize) { 1509 self.emit(Inst::XmmMinMaxSeq { 1510 size: size.into(), 1511 is_min: false, 1512 lhs: src.into(), 1513 rhs: dst.to_reg().into(), 1514 dst: dst.map(Into::into), 1515 }); 1516 } 1517 1518 /// Perform rounding operation on float register src and place results in 1519 /// float register dst. 1520 pub fn xmm_rounds_rr( 1521 &mut self, 1522 src: Reg, 1523 dst: WritableReg, 1524 mode: RoundingMode, 1525 size: OperandSize, 1526 ) { 1527 let dst = dst.map(|r| r.into()); 1528 1529 let imm: u8 = match mode { 1530 RoundingMode::Nearest => 0x00, 1531 RoundingMode::Down => 0x01, 1532 RoundingMode::Up => 0x02, 1533 RoundingMode::Zero => 0x03, 1534 }; 1535 1536 let inst = match size { 1537 OperandSize::S32 => asm::inst::roundss_rmi::new(dst, src, imm).into(), 1538 OperandSize::S64 => asm::inst::roundsd_rmi::new(dst, src, imm).into(), 1539 OperandSize::S8 | OperandSize::S16 | OperandSize::S128 => unreachable!(), 1540 }; 1541 1542 self.emit(Inst::External { inst }); 1543 } 1544 1545 pub fn sqrt(&mut self, src: Reg, dst: WritableReg, size: OperandSize) { 1546 use OperandSize::*; 1547 let dst = pair_xmm(dst); 1548 let inst = match size { 1549 S32 => asm::inst::sqrtss_a::new(dst, src).into(), 1550 S64 => asm::inst::sqrtsd_a::new(dst, src).into(), 1551 S8 | S16 | S128 => unimplemented!(), 1552 }; 1553 self.emit(Inst::External { inst }); 1554 } 1555 1556 /// Emit a call to an unknown location through a register. 1557 pub fn call_with_reg(&mut self, cc: CallingConvention, callee: Reg) { 1558 self.emit(Inst::CallUnknown { 1559 info: Box::new(CallInfo::empty(RegMem::reg(callee.into()), cc.into())), 1560 }); 1561 } 1562 1563 /// Emit a call to a locally defined function through an index. 1564 pub fn call_with_name(&mut self, cc: CallingConvention, name: UserExternalNameRef) { 1565 self.emit(Inst::CallKnown { 1566 info: Box::new(CallInfo::empty(ExternalName::user(name), cc.into())), 1567 }); 1568 } 1569 1570 /// Emits a conditional jump to the given label. 1571 pub fn jmp_if(&mut self, cc: impl Into<CC>, taken: MachLabel) { 1572 self.emit(Inst::WinchJmpIf { 1573 cc: cc.into(), 1574 taken, 1575 }); 1576 } 1577 1578 /// Performs an unconditional jump to the given label. 1579 pub fn jmp(&mut self, target: MachLabel) { 1580 self.emit(Inst::JmpKnown { dst: target }); 1581 } 1582 1583 /// Emits a jump table sequence. 1584 pub fn jmp_table( 1585 &mut self, 1586 targets: SmallVec<[MachLabel; 4]>, 1587 default: MachLabel, 1588 index: Reg, 1589 tmp1: Reg, 1590 tmp2: Reg, 1591 ) { 1592 self.emit(Inst::JmpTableSeq { 1593 idx: index.into(), 1594 tmp1: Writable::from_reg(tmp1.into()), 1595 tmp2: Writable::from_reg(tmp2.into()), 1596 default_target: default, 1597 targets: Box::new(targets.to_vec()), 1598 }) 1599 } 1600 1601 /// Emit a trap instruction. 1602 pub fn trap(&mut self, code: TrapCode) { 1603 let inst = asm::inst::ud2_zo::new(code).into(); 1604 self.emit(Inst::External { inst }); 1605 } 1606 1607 /// Conditional trap. 1608 pub fn trapif(&mut self, cc: impl Into<CC>, trap_code: TrapCode) { 1609 self.emit(Inst::TrapIf { 1610 cc: cc.into(), 1611 trap_code, 1612 }); 1613 } 1614 1615 /// Load effective address. 1616 pub fn lea(&mut self, addr: &Address, dst: WritableReg, size: OperandSize) { 1617 let addr = Self::to_synthetic_amode(addr, MemFlags::trusted()); 1618 self.emit(Inst::LoadEffectiveAddress { 1619 addr, 1620 dst: dst.map(Into::into), 1621 size: size.into(), 1622 }); 1623 } 1624 1625 pub fn adc_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) { 1626 let dst = pair_gpr(dst); 1627 let inst = match size { 1628 OperandSize::S8 => asm::inst::adcb_rm::new(dst, src).into(), 1629 OperandSize::S16 => asm::inst::adcw_rm::new(dst, src).into(), 1630 OperandSize::S32 => asm::inst::adcl_rm::new(dst, src).into(), 1631 OperandSize::S64 => asm::inst::adcq_rm::new(dst, src).into(), 1632 OperandSize::S128 => unimplemented!(), 1633 }; 1634 self.emit(Inst::External { inst }); 1635 } 1636 1637 pub fn sbb_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) { 1638 let dst = pair_gpr(dst); 1639 let inst = match size { 1640 OperandSize::S8 => asm::inst::sbbb_rm::new(dst, src).into(), 1641 OperandSize::S16 => asm::inst::sbbw_rm::new(dst, src).into(), 1642 OperandSize::S32 => asm::inst::sbbl_rm::new(dst, src).into(), 1643 OperandSize::S64 => asm::inst::sbbq_rm::new(dst, src).into(), 1644 OperandSize::S128 => unimplemented!(), 1645 }; 1646 self.emit(Inst::External { inst }); 1647 } 1648 1649 pub fn mul_wide( 1650 &mut self, 1651 dst_lo: WritableReg, 1652 dst_hi: WritableReg, 1653 lhs: Reg, 1654 rhs: Reg, 1655 kind: MulWideKind, 1656 size: OperandSize, 1657 ) { 1658 use MulWideKind::*; 1659 use OperandSize::*; 1660 let rax = asm::Fixed(PairedGpr { 1661 read: lhs.into(), 1662 write: WritableGpr::from_reg(dst_lo.to_reg().into()), 1663 }); 1664 let rdx = asm::Fixed(dst_hi.to_reg().into()); 1665 if size == S8 { 1666 // For `mulb` and `imulb`, both the high and low bits are written to 1667 // RAX. 1668 assert_eq!(dst_lo, dst_hi); 1669 } 1670 let inst = match (size, kind) { 1671 (S8, Unsigned) => asm::inst::mulb_m::new(rax, rhs).into(), 1672 (S8, Signed) => asm::inst::imulb_m::new(rax, rhs).into(), 1673 (S16, Unsigned) => asm::inst::mulw_m::new(rax, rdx, rhs).into(), 1674 (S16, Signed) => asm::inst::imulw_m::new(rax, rdx, rhs).into(), 1675 (S32, Unsigned) => asm::inst::mull_m::new(rax, rdx, rhs).into(), 1676 (S32, Signed) => asm::inst::imull_m::new(rax, rdx, rhs).into(), 1677 (S64, Unsigned) => asm::inst::mulq_m::new(rax, rdx, rhs).into(), 1678 (S64, Signed) => asm::inst::imulq_m::new(rax, rdx, rhs).into(), 1679 (S128, _) => unimplemented!(), 1680 }; 1681 self.emit(Inst::External { inst }); 1682 } 1683 1684 /// Shuffles bytes in `src` according to contents of `mask` and puts 1685 /// result in `dst`. 1686 pub fn xmm_vpshufb_rrm(&mut self, dst: WritableReg, src: Reg, mask: &Address) { 1687 let mask = Self::to_synthetic_amode(mask, MemFlags::trusted()); 1688 1689 self.emit(Inst::XmmRmiRVex { 1690 op: args::AvxOpcode::Vpshufb, 1691 src1: src.into(), 1692 src2: XmmMemImm::unwrap_new(RegMemImm::Mem { addr: mask }), 1693 dst: dst.to_reg().into(), 1694 }); 1695 } 1696 1697 /// Shuffles bytes in `src` according to contents of `mask` and puts 1698 /// result in `dst`. 1699 pub fn xmm_vpshufb_rrr(&mut self, dst: WritableReg, src: Reg, mask: Reg) { 1700 self.emit(Inst::XmmRmiRVex { 1701 op: args::AvxOpcode::Vpshufb, 1702 src1: src.into(), 1703 src2: XmmMemImm::unwrap_new(RegMemImm::reg(mask.into())), 1704 dst: dst.to_reg().into(), 1705 }) 1706 } 1707 1708 /// Bitwise OR of `src1` and `src2`. 1709 pub fn vpor(&mut self, dst: WritableReg, src1: Reg, src2: Reg) { 1710 self.emit(Inst::XmmRmiRVex { 1711 op: args::AvxOpcode::Vpor, 1712 src1: src1.into(), 1713 src2: XmmMemImm::unwrap_new(src2.into()), 1714 dst: dst.to_reg().into(), 1715 }) 1716 } 1717 1718 /// Add unsigned integers with unsigned saturation. 1719 /// 1720 /// Adds the src operands but when an individual byte result is larger than 1721 /// an unsigned byte integer, 0xFF is written instead. 1722 pub fn xmm_vpaddusb_rrm(&mut self, dst: WritableReg, src1: Reg, src2: &Address) { 1723 let src2 = Self::to_synthetic_amode(src2, MemFlags::trusted()); 1724 1725 self.emit(Inst::XmmRmiRVex { 1726 op: args::AvxOpcode::Vpaddusb, 1727 src1: src1.into(), 1728 src2: XmmMemImm::unwrap_new(RegMemImm::mem(src2)), 1729 dst: dst.to_reg().into(), 1730 }) 1731 } 1732 1733 /// Converts an operand size to the appropriate opcode for `vpadd``. 1734 fn xmm_vpadd_opcode(size: OperandSize) -> AvxOpcode { 1735 match size { 1736 OperandSize::S8 => AvxOpcode::Vpaddb, 1737 OperandSize::S32 => AvxOpcode::Vpaddd, 1738 _ => unimplemented!(), 1739 } 1740 } 1741 1742 pub fn xmm_vpadd_rmr( 1743 &mut self, 1744 src1: Reg, 1745 src2: &Address, 1746 dst: WritableReg, 1747 size: OperandSize, 1748 ) { 1749 let address = Self::to_synthetic_amode(src2, MemFlags::trusted()); 1750 1751 self.emit(Inst::XmmRmiRVex { 1752 op: Self::xmm_vpadd_opcode(size), 1753 src1: src1.into(), 1754 src2: XmmMemImm::unwrap_new(RegMemImm::mem(address)), 1755 dst: dst.to_reg().into(), 1756 }); 1757 } 1758 1759 /// Adds vectors of integers in `src1` and `src2` and puts the results in 1760 /// `dst`. 1761 pub fn xmm_vpadd_rrr(&mut self, src1: Reg, src2: Reg, dst: WritableReg, size: OperandSize) { 1762 self.emit(Inst::XmmRmiRVex { 1763 op: Self::xmm_vpadd_opcode(size), 1764 src1: src1.into(), 1765 src2: src2.into(), 1766 dst: dst.to_reg().into(), 1767 }); 1768 } 1769 1770 pub fn mfence(&mut self) { 1771 self.emit(Inst::External { 1772 inst: asm::inst::mfence_zo::new().into(), 1773 }); 1774 } 1775 1776 /// Extract a value from `src` into `dst` (zero extended) determined by `lane`. 1777 pub fn xmm_vpextr_rr(&mut self, dst: WritableReg, src: Reg, lane: u8, size: OperandSize) { 1778 self.emit(Inst::XmmToGprImmVex { 1779 op: Self::vpextr_opcode(size), 1780 src: src.into(), 1781 dst: dst.to_reg().into(), 1782 imm: lane, 1783 }); 1784 } 1785 1786 /// Copy value from `src2`, merge into `src1`, and put result in `dst` at 1787 /// the location specified in `count`. 1788 pub fn xmm_vpinsr_rrm( 1789 &mut self, 1790 dst: WritableReg, 1791 src1: Reg, 1792 src2: &Address, 1793 count: u8, 1794 size: OperandSize, 1795 ) { 1796 let src2 = Self::to_synthetic_amode(src2, MemFlags::trusted()); 1797 let dst: WritableXmm = dst.map(|r| r.into()); 1798 1799 let inst = match size { 1800 OperandSize::S8 => asm::inst::vpinsrb_b::new(dst, src1, src2, count).into(), 1801 OperandSize::S16 => asm::inst::vpinsrw_b::new(dst, src1, src2, count).into(), 1802 OperandSize::S32 => asm::inst::vpinsrd_b::new(dst, src1, src2, count).into(), 1803 OperandSize::S64 => asm::inst::vpinsrq_b::new(dst, src1, src2, count).into(), 1804 OperandSize::S128 => unreachable!(), 1805 }; 1806 self.emit(Inst::External { inst }); 1807 } 1808 1809 /// Copy value from `src2`, merge into `src1`, and put result in `dst` at 1810 /// the location specified in `count`. 1811 pub fn xmm_vpinsr_rrr( 1812 &mut self, 1813 dst: WritableReg, 1814 src1: Reg, 1815 src2: Reg, 1816 count: u8, 1817 size: OperandSize, 1818 ) { 1819 let dst: WritableXmm = dst.map(|r| r.into()); 1820 let inst = match size { 1821 OperandSize::S8 => asm::inst::vpinsrb_b::new(dst, src1, src2, count).into(), 1822 OperandSize::S16 => asm::inst::vpinsrw_b::new(dst, src1, src2, count).into(), 1823 OperandSize::S32 => asm::inst::vpinsrd_b::new(dst, src1, src2, count).into(), 1824 OperandSize::S64 => asm::inst::vpinsrq_b::new(dst, src1, src2, count).into(), 1825 OperandSize::S128 => unreachable!(), 1826 }; 1827 self.emit(Inst::External { inst }); 1828 } 1829 1830 /// Copy a 32-bit float in `src2`, merge into `src1`, and put result in `dst`. 1831 pub fn xmm_vinsertps_rrm(&mut self, dst: WritableReg, src1: Reg, src2: &Address, imm: u8) { 1832 let src2 = Self::to_synthetic_amode(src2, MemFlags::trusted()); 1833 1834 self.emit(Inst::XmmRmRImmVex { 1835 op: AvxOpcode::Vinsertps, 1836 src1: src1.into(), 1837 src2: XmmMem::unwrap_new(RegMem::mem(src2)), 1838 dst: dst.to_reg().into(), 1839 imm, 1840 }); 1841 } 1842 1843 /// Copy a 32-bit float in `src2`, merge into `src1`, and put result in `dst`. 1844 pub fn xmm_vinsertps_rrr(&mut self, dst: WritableReg, src1: Reg, src2: Reg, imm: u8) { 1845 self.emit(Inst::XmmRmRImmVex { 1846 op: AvxOpcode::Vinsertps, 1847 src1: src1.into(), 1848 src2: XmmMem::unwrap_new(RegMem::reg(src2.into())), 1849 dst: dst.to_reg().into(), 1850 imm, 1851 }); 1852 } 1853 1854 /// Moves lower 64-bit float from `src2` into lower 64-bits of `dst` and the 1855 /// upper 64-bits in `src1` into the upper 64-bits of `dst`. 1856 pub fn xmm_vmovsd_rrr(&mut self, dst: WritableReg, src1: Reg, src2: Reg) { 1857 self.emit(Inst::XmmRmiRVex { 1858 op: AvxOpcode::Vmovsd, 1859 src1: src1.into(), 1860 src2: XmmMemImm::unwrap_new(src2.into()), 1861 dst: dst.to_reg().into(), 1862 }) 1863 } 1864 1865 /// Moves 64-bit float from `src` into lower 64-bits of `dst`. 1866 /// Zeroes out the upper 64 bits of `dst`. 1867 pub fn xmm_vmovsd_rm(&mut self, dst: WritableReg, src: &Address) { 1868 let src = Self::to_synthetic_amode(src, MemFlags::trusted()); 1869 1870 self.emit(Inst::XmmUnaryRmRVex { 1871 op: AvxOpcode::Vmovsd, 1872 src: XmmMem::unwrap_new(RegMem::mem(src)), 1873 dst: dst.to_reg().into(), 1874 }) 1875 } 1876 1877 /// Moves two 32-bit floats from `src2` to the upper 64-bits of `dst`. 1878 /// Copies two 32-bit floats from the lower 64-bits of `src1` to lower 1879 /// 64-bits of `dst`. 1880 pub fn xmm_vmovlhps_rrm(&mut self, dst: WritableReg, src1: Reg, src2: &Address) { 1881 let src2 = Self::to_synthetic_amode(src2, MemFlags::trusted()); 1882 let dst: WritableXmm = dst.map(|r| r.into()); 1883 let inst = asm::inst::vmovhps_b::new(dst, src1, src2).into(); 1884 self.emit(Inst::External { inst }); 1885 } 1886 1887 /// Moves two 32-bit floats from the lower 64-bits of `src2` to the upper 1888 /// 64-bits of `dst`. Copies two 32-bit floats from the lower 64-bits of 1889 /// `src1` to lower 64-bits of `dst`. 1890 pub fn xmm_vmovlhps_rrr(&mut self, dst: WritableReg, src1: Reg, src2: Reg) { 1891 let dst: WritableXmm = dst.map(|r| r.into()); 1892 let inst = asm::inst::vmovlhps_rvm::new(dst, src1, src2).into(); 1893 self.emit(Inst::External { inst }); 1894 } 1895 1896 /// Move unaligned packed integer values from address `src` to `dst`. 1897 pub fn xmm_vmovdqu_mr(&mut self, src: &Address, dst: WritableReg, flags: MemFlags) { 1898 let src = Self::to_synthetic_amode(src, flags); 1899 self.emit(Inst::XmmUnaryRmRVex { 1900 op: AvxOpcode::Vmovdqu, 1901 src: XmmMem::unwrap_new(RegMem::mem(src)), 1902 dst: dst.map(Into::into), 1903 }); 1904 } 1905 1906 /// Move integer from `src` to xmm register `dst` using an AVX instruction. 1907 pub fn avx_gpr_to_xmm(&mut self, src: Reg, dst: WritableReg, size: OperandSize) { 1908 let dst: WritableXmm = dst.map(|r| r.into()); 1909 let inst = match size { 1910 OperandSize::S32 => asm::inst::vmovd_a::new(dst, src).into(), 1911 OperandSize::S64 => asm::inst::vmovq_a::new(dst, src).into(), 1912 _ => unreachable!(), 1913 }; 1914 1915 self.emit(Inst::External { inst }); 1916 } 1917 1918 /// Perform an AVX opcode `op` involving registers `src1` and `src2`, writing the 1919 /// result to `dst`. 1920 pub fn xmm_vex_rr(&mut self, op: AvxOpcode, src1: Reg, src2: Reg, dst: WritableReg) { 1921 self.emit(Inst::XmmRmiRVex { 1922 op, 1923 src1: src1.into(), 1924 src2: src2.into(), 1925 dst: dst.map(Into::into), 1926 }) 1927 } 1928 1929 /// Perform an AVX opcode `op` involving register `src1` and an immediate `imm`, writing the 1930 /// result to `dst`. 1931 pub fn xmm_vex_ri(&mut self, op: AvxOpcode, src1: Reg, imm: u32, dst: WritableReg) { 1932 self.emit(Inst::XmmRmiRVex { 1933 op, 1934 src1: src1.into(), 1935 src2: XmmMemImm::unwrap_new(RegMemImm::imm(imm)), 1936 dst: dst.map(Into::into), 1937 }) 1938 } 1939 1940 pub fn xmm_vptest(&mut self, src1: Reg, src2: Reg) { 1941 self.emit(Inst::XmmCmpRmRVex { 1942 op: AvxOpcode::Vptest, 1943 src1: src1.into(), 1944 src2: src2.into(), 1945 }) 1946 } 1947 1948 /// The `vpextr` opcode to use. 1949 fn vpextr_opcode(size: OperandSize) -> AvxOpcode { 1950 match size { 1951 OperandSize::S8 => AvxOpcode::Vpextrb, 1952 OperandSize::S16 => AvxOpcode::Vpextrw, 1953 OperandSize::S32 => AvxOpcode::Vpextrd, 1954 OperandSize::S64 => AvxOpcode::Vpextrq, 1955 _ => unimplemented!(), 1956 } 1957 } 1958 1959 /// Extract a value from `src` into `addr` determined by `lane`. 1960 pub(crate) fn xmm_vpextr_rm( 1961 &mut self, 1962 addr: &Address, 1963 src: Reg, 1964 lane: u8, 1965 size: OperandSize, 1966 flags: MemFlags, 1967 ) -> anyhow::Result<()> { 1968 assert!(addr.is_offset()); 1969 let dst = Self::to_synthetic_amode(addr, flags); 1970 1971 self.emit(Inst::XmmMovRMImmVex { 1972 op: Self::vpextr_opcode(size), 1973 src: src.into(), 1974 dst, 1975 imm: lane, 1976 }); 1977 1978 Ok(()) 1979 } 1980 1981 /// Converts vector of integers into vector of floating values. 1982 pub fn xmm_vcvt_rr(&mut self, src: Reg, dst: WritableReg, kind: VcvtKind) { 1983 let dst: WritableXmm = dst.map(|x| x.into()); 1984 let inst = match kind { 1985 VcvtKind::I32ToF32 => asm::inst::vcvtdq2ps_a::new(dst, src).into(), 1986 VcvtKind::I32ToF64 => asm::inst::vcvtdq2pd_a::new(dst, src).into(), 1987 VcvtKind::F64ToF32 => asm::inst::vcvtpd2ps_a::new(dst, src).into(), 1988 VcvtKind::F64ToI32 => asm::inst::vcvttpd2dq_a::new(dst, src).into(), 1989 VcvtKind::F32ToF64 => asm::inst::vcvtps2pd_a::new(dst, src).into(), 1990 VcvtKind::F32ToI32 => asm::inst::vcvttps2dq_a::new(dst, src).into(), 1991 }; 1992 1993 self.emit(Inst::External { inst }); 1994 } 1995 1996 /// Shift vector data left by `imm`. 1997 pub fn xmm_vpsll_rr(&mut self, src: Reg, dst: WritableReg, imm: u32, size: OperandSize) { 1998 let op = match size { 1999 OperandSize::S32 => AvxOpcode::Vpslld, 2000 OperandSize::S64 => AvxOpcode::Vpsllq, 2001 _ => unimplemented!(), 2002 }; 2003 2004 self.emit(Inst::XmmRmiRVex { 2005 op, 2006 src1: src.into(), 2007 src2: XmmMemImm::unwrap_new(RegMemImm::imm(imm)), 2008 dst: dst.to_reg().into(), 2009 }); 2010 } 2011 2012 /// Shift vector data right by `imm`. 2013 pub fn xmm_vpsrl_rr(&mut self, src: Reg, dst: WritableReg, imm: u32, size: OperandSize) { 2014 let op = match size { 2015 OperandSize::S16 => AvxOpcode::Vpsrlw, 2016 OperandSize::S32 => AvxOpcode::Vpsrld, 2017 OperandSize::S64 => AvxOpcode::Vpsrlq, 2018 _ => unimplemented!(), 2019 }; 2020 2021 self.emit(Inst::XmmRmiRVex { 2022 op, 2023 src1: src.into(), 2024 src2: XmmMemImm::unwrap_new(RegMemImm::imm(imm)), 2025 dst: dst.to_reg().into(), 2026 }) 2027 } 2028 2029 /// Subtract integers in vector `src1` from integers in vector `src2`. 2030 pub fn xmm_vpsub_rrr(&mut self, src1: Reg, src2: Reg, dst: WritableReg, size: OperandSize) { 2031 let op = match size { 2032 OperandSize::S32 => AvxOpcode::Vpsubd, 2033 OperandSize::S64 => AvxOpcode::Vpsubq, 2034 _ => unimplemented!(), 2035 }; 2036 2037 self.emit(Inst::XmmRmiRVex { 2038 op, 2039 src1: src1.into(), 2040 src2: src2.into(), 2041 dst: dst.to_reg().into(), 2042 }) 2043 } 2044 2045 fn vaddp_opcode(size: OperandSize) -> AvxOpcode { 2046 match size { 2047 OperandSize::S32 => AvxOpcode::Vaddps, 2048 OperandSize::S64 => AvxOpcode::Vaddpd, 2049 _ => unimplemented!(), 2050 } 2051 } 2052 2053 /// Add floats in vector `src1` to floats in vector `src2`. 2054 pub fn xmm_vaddp_rrm( 2055 &mut self, 2056 src1: Reg, 2057 src2: &Address, 2058 dst: WritableReg, 2059 size: OperandSize, 2060 ) { 2061 let address = Self::to_synthetic_amode(src2, MemFlags::trusted()); 2062 2063 self.emit(Inst::XmmRmiRVex { 2064 op: Self::vaddp_opcode(size), 2065 src1: src1.into(), 2066 src2: XmmMemImm::unwrap_new(RegMemImm::mem(address)), 2067 dst: dst.to_reg().into(), 2068 }); 2069 } 2070 2071 /// Add floats in vector `src1` to floats in vector `src2`. 2072 pub fn xmm_vaddp_rrr(&mut self, src1: Reg, src2: Reg, dst: WritableReg, size: OperandSize) { 2073 self.emit(Inst::XmmRmiRVex { 2074 op: Self::vaddp_opcode(size), 2075 src1: src1.into(), 2076 src2: src2.into(), 2077 dst: dst.to_reg().into(), 2078 }) 2079 } 2080 2081 fn vpcmpeq_opcode(size: OperandSize) -> AvxOpcode { 2082 match size { 2083 OperandSize::S8 => AvxOpcode::Vpcmpeqb, 2084 OperandSize::S16 => AvxOpcode::Vpcmpeqw, 2085 OperandSize::S32 => AvxOpcode::Vpcmpeqd, 2086 OperandSize::S64 => AvxOpcode::Vpcmpeqq, 2087 _ => unimplemented!(), 2088 } 2089 } 2090 2091 /// Compare vector register `lhs` with a vector of integers in `rhs` for 2092 /// equality between packed integers and write the resulting vector into 2093 /// `dst`. 2094 pub fn xmm_vpcmpeq_rrm( 2095 &mut self, 2096 dst: WritableReg, 2097 lhs: Reg, 2098 address: &Address, 2099 size: OperandSize, 2100 ) { 2101 let address = Self::to_synthetic_amode(address, MemFlags::trusted()); 2102 2103 self.emit(Inst::XmmRmiRVex { 2104 op: Self::vpcmpeq_opcode(size), 2105 src1: lhs.into(), 2106 src2: XmmMemImm::unwrap_new(RegMemImm::mem(address)), 2107 dst: dst.to_reg().into(), 2108 }); 2109 } 2110 2111 /// Compare vector registers `lhs` and `rhs` for equality between packed 2112 /// integers and write the resulting vector into `dst`. 2113 pub fn xmm_vpcmpeq_rrr(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize) { 2114 self.emit(Inst::XmmRmiRVex { 2115 op: Self::vpcmpeq_opcode(size), 2116 src1: lhs.into(), 2117 src2: XmmMemImm::unwrap_new(rhs.into()), 2118 dst: dst.to_reg().into(), 2119 }) 2120 } 2121 2122 /// Performs a greater than comparison with vectors of signed integers in 2123 /// `lhs` and `rhs` and puts the results in `dst`. 2124 pub fn xmm_vpcmpgt_rrr(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize) { 2125 let op = match size { 2126 OperandSize::S8 => AvxOpcode::Vpcmpgtb, 2127 OperandSize::S16 => AvxOpcode::Vpcmpgtw, 2128 OperandSize::S32 => AvxOpcode::Vpcmpgtd, 2129 OperandSize::S64 => AvxOpcode::Vpcmpgtq, 2130 _ => unimplemented!(), 2131 }; 2132 2133 self.emit(Inst::XmmRmiRVex { 2134 op, 2135 src1: lhs.into(), 2136 src2: XmmMemImm::unwrap_new(rhs.into()), 2137 dst: dst.to_reg().into(), 2138 }) 2139 } 2140 2141 /// Performs a max operation with vectors of signed integers in `lhs` and 2142 /// `rhs` and puts the results in `dst`. 2143 pub fn xmm_vpmaxs_rrr(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize) { 2144 let op = match size { 2145 OperandSize::S8 => AvxOpcode::Vpmaxsb, 2146 OperandSize::S16 => AvxOpcode::Vpmaxsw, 2147 OperandSize::S32 => AvxOpcode::Vpmaxsd, 2148 _ => unimplemented!(), 2149 }; 2150 2151 self.emit(Inst::XmmRmiRVex { 2152 op, 2153 src1: lhs.into(), 2154 src2: XmmMemImm::unwrap_new(rhs.into()), 2155 dst: dst.to_reg().into(), 2156 }) 2157 } 2158 2159 /// Performs a max operation with vectors of unsigned integers in `lhs` and 2160 /// `rhs` and puts the results in `dst`. 2161 pub fn xmm_vpmaxu_rrr(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize) { 2162 let op = match size { 2163 OperandSize::S8 => AvxOpcode::Vpmaxub, 2164 OperandSize::S16 => AvxOpcode::Vpmaxuw, 2165 OperandSize::S32 => AvxOpcode::Vpmaxud, 2166 _ => unimplemented!(), 2167 }; 2168 2169 self.emit(Inst::XmmRmiRVex { 2170 op, 2171 src1: lhs.into(), 2172 src2: XmmMemImm::unwrap_new(rhs.into()), 2173 dst: dst.to_reg().into(), 2174 }) 2175 } 2176 2177 /// Performs a min operation with vectors of signed integers in `lhs` and 2178 /// `rhs` and puts the results in `dst`. 2179 pub fn xmm_vpmins_rrr(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize) { 2180 let op = match size { 2181 OperandSize::S8 => AvxOpcode::Vpminsb, 2182 OperandSize::S16 => AvxOpcode::Vpminsw, 2183 OperandSize::S32 => AvxOpcode::Vpminsd, 2184 _ => unimplemented!(), 2185 }; 2186 2187 self.emit(Inst::XmmRmiRVex { 2188 op, 2189 src1: lhs.into(), 2190 src2: XmmMemImm::unwrap_new(rhs.into()), 2191 dst: dst.to_reg().into(), 2192 }) 2193 } 2194 2195 /// Performs a min operation with vectors of unsigned integers in `lhs` and 2196 /// `rhs` and puts the results in `dst`. 2197 pub fn xmm_vpminu_rrr(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize) { 2198 let op = match size { 2199 OperandSize::S8 => AvxOpcode::Vpminub, 2200 OperandSize::S16 => AvxOpcode::Vpminuw, 2201 OperandSize::S32 => AvxOpcode::Vpminud, 2202 _ => unimplemented!(), 2203 }; 2204 2205 self.emit(Inst::XmmRmiRVex { 2206 op, 2207 src1: lhs.into(), 2208 src2: XmmMemImm::unwrap_new(rhs.into()), 2209 dst: dst.to_reg().into(), 2210 }) 2211 } 2212 2213 /// Performs a comparison operation between vectors of floats in `lhs` and 2214 /// `rhs` and puts the results in `dst`. 2215 pub fn xmm_vcmpp_rrr( 2216 &mut self, 2217 dst: WritableReg, 2218 lhs: Reg, 2219 rhs: Reg, 2220 size: OperandSize, 2221 kind: VcmpKind, 2222 ) { 2223 let op = match size { 2224 OperandSize::S32 => AvxOpcode::Vcmpps, 2225 OperandSize::S64 => AvxOpcode::Vcmppd, 2226 _ => unimplemented!(), 2227 }; 2228 2229 self.emit(Inst::XmmRmRImmVex { 2230 op, 2231 src1: lhs.into(), 2232 src2: XmmMem::unwrap_new(rhs.into()), 2233 dst: dst.to_reg().into(), 2234 imm: match kind { 2235 VcmpKind::Eq => 0, 2236 VcmpKind::Lt => 1, 2237 VcmpKind::Le => 2, 2238 VcmpKind::Unord => 3, 2239 VcmpKind::Ne => 4, 2240 }, 2241 }); 2242 } 2243 2244 /// Takes the lower lanes of vectors of floats in `src1` and `src2` and 2245 /// interleaves them in `dst`. 2246 pub fn xmm_vunpcklp_rrm( 2247 &mut self, 2248 src1: Reg, 2249 src2: &Address, 2250 dst: WritableReg, 2251 size: OperandSize, 2252 ) { 2253 let op = match size { 2254 OperandSize::S32 => AvxOpcode::Vunpcklps, 2255 _ => unimplemented!(), 2256 }; 2257 2258 let address = Self::to_synthetic_amode(src2, MemFlags::trusted()); 2259 2260 self.emit(Inst::XmmRmiRVex { 2261 op, 2262 src1: src1.into(), 2263 src2: XmmMemImm::unwrap_new(RegMemImm::mem(address)), 2264 dst: dst.to_reg().into(), 2265 }); 2266 } 2267 2268 /// Performs a subtraction on two vectors of floats and puts the results in 2269 /// `dst`. 2270 pub fn xmm_vsub_rrm(&mut self, src1: Reg, src2: &Address, dst: WritableReg, size: OperandSize) { 2271 let op = match size { 2272 OperandSize::S64 => AvxOpcode::Vsubpd, 2273 _ => unimplemented!(), 2274 }; 2275 2276 let address = Self::to_synthetic_amode(src2, MemFlags::trusted()); 2277 2278 self.emit(Inst::XmmRmiRVex { 2279 op, 2280 src1: src1.into(), 2281 src2: XmmMemImm::unwrap_new(RegMemImm::mem(address)), 2282 dst: dst.to_reg().into(), 2283 }); 2284 } 2285 2286 /// Performs a subtraction on two vectors of floats and puts the results in 2287 /// `dst`. 2288 pub fn xmm_vsub_rrr(&mut self, src1: Reg, src2: Reg, dst: WritableReg, size: OperandSize) { 2289 let op = match size { 2290 OperandSize::S32 => AvxOpcode::Vsubps, 2291 OperandSize::S64 => AvxOpcode::Vsubpd, 2292 _ => unimplemented!(), 2293 }; 2294 2295 self.emit(Inst::XmmRmiRVex { 2296 op, 2297 src1: src1.into(), 2298 src2: src2.into(), 2299 dst: dst.to_reg().into(), 2300 }); 2301 } 2302 2303 /// Converts a vector of signed integers into a vector of narrower integers 2304 /// using saturation to handle overflow. 2305 pub fn xmm_vpackss_rrr(&mut self, src1: Reg, src2: Reg, dst: WritableReg, size: OperandSize) { 2306 let op = match size { 2307 OperandSize::S8 => AvxOpcode::Vpacksswb, 2308 OperandSize::S16 => AvxOpcode::Vpackssdw, 2309 _ => unimplemented!(), 2310 }; 2311 2312 self.emit(Inst::XmmRmiRVex { 2313 op, 2314 src1: src1.into(), 2315 src2: src2.into(), 2316 dst: dst.to_reg().into(), 2317 }); 2318 } 2319 2320 /// Converts a vector of unsigned integers into a vector of narrower 2321 /// integers using saturation to handle overflow. 2322 pub fn xmm_vpackus_rrr(&mut self, src1: Reg, src2: Reg, dst: WritableReg, size: OperandSize) { 2323 let op = match size { 2324 OperandSize::S8 => AvxOpcode::Vpackuswb, 2325 OperandSize::S16 => AvxOpcode::Vpackusdw, 2326 _ => unimplemented!(), 2327 }; 2328 2329 self.emit(Inst::XmmRmiRVex { 2330 op, 2331 src1: src1.into(), 2332 src2: src2.into(), 2333 dst: dst.to_reg().into(), 2334 }); 2335 } 2336 2337 /// Concatenates `src1` and `src2` and shifts right by `imm` and puts 2338 /// result in `dst`. 2339 pub fn xmm_vpalignr_rrr(&mut self, src1: Reg, src2: Reg, dst: WritableReg, imm: u8) { 2340 self.emit(Inst::XmmRmRImmVex { 2341 op: AvxOpcode::Vpalignr, 2342 src1: src1.into(), 2343 src2: src2.into(), 2344 dst: dst.to_reg().into(), 2345 imm, 2346 }) 2347 } 2348 2349 /// Unpacks and interleaves the higher lanes of vectors of integers in 2350 /// `src1` and `src2` and puts the results in `dst`. 2351 pub fn xmm_vpunpckh_rrr(&mut self, src1: Reg, src2: Reg, dst: WritableReg, size: OperandSize) { 2352 let op = match size { 2353 OperandSize::S8 => AvxOpcode::Vpunpckhbw, 2354 OperandSize::S16 => AvxOpcode::Vpunpckhwd, 2355 _ => unimplemented!(), 2356 }; 2357 2358 self.emit(Inst::XmmRmiRVex { 2359 op, 2360 src1: src1.into(), 2361 src2: src2.into(), 2362 dst: dst.to_reg().into(), 2363 }); 2364 } 2365 2366 /// Bitwise logical xor of vectors of floats in `src1` and `src2` and puts 2367 /// the results in `dst`. 2368 pub fn xmm_vxorp_rrr(&mut self, src1: Reg, src2: Reg, dst: WritableReg, size: OperandSize) { 2369 let op = match size { 2370 OperandSize::S32 => AvxOpcode::Vxorps, 2371 OperandSize::S64 => AvxOpcode::Vxorpd, 2372 _ => unimplemented!(), 2373 }; 2374 2375 self.emit(Inst::XmmRmiRVex { 2376 op, 2377 src1: src1.into(), 2378 src2: src2.into(), 2379 dst: dst.to_reg().into(), 2380 }); 2381 } 2382 2383 /// Unpacks and interleaves high order data of floats in `src1` and `src2` 2384 /// and puts the results in `dst`. 2385 pub fn xmm_vunpckhp_rrr(&mut self, src1: Reg, src2: Reg, dst: WritableReg, size: OperandSize) { 2386 let op = match size { 2387 OperandSize::S32 => AvxOpcode::Vunpckhps, 2388 _ => unimplemented!(), 2389 }; 2390 2391 self.emit(Inst::XmmRmiRVex { 2392 op, 2393 src1: src1.into(), 2394 src2: src2.into(), 2395 dst: dst.to_reg().into(), 2396 }); 2397 } 2398 2399 pub(crate) fn xmm_rm_rvex3( 2400 &mut self, 2401 op: Avx512Opcode, 2402 src1: Reg, 2403 src2: Reg, 2404 dst: WritableReg, 2405 ) { 2406 self.emit(Inst::XmmRmREvex3 { 2407 op, 2408 // `src1` reuses `dst`, and is ignored in emission 2409 src1: dst.to_reg().into(), 2410 src2: src1.into(), 2411 src3: src2.into(), 2412 dst: dst.map(Into::into), 2413 }); 2414 } 2415 2416 /// Creates a mask made up of the most significant bit of each byte of 2417 /// `src` and stores the result in `dst`. 2418 pub fn xmm_vpmovmsk_rr( 2419 &mut self, 2420 src: Reg, 2421 dst: WritableReg, 2422 src_size: OperandSize, 2423 dst_size: OperandSize, 2424 ) { 2425 assert_eq!(dst_size, OperandSize::S32); 2426 let dst: WritableGpr = dst.map(|r| r.into()); 2427 let inst = match src_size { 2428 OperandSize::S8 => asm::inst::vpmovmskb_rm::new(dst, src).into(), 2429 _ => unimplemented!(), 2430 }; 2431 2432 self.emit(Inst::External { inst }); 2433 } 2434 2435 /// Creates a mask made up of the most significant bit of each byte of 2436 /// in `src` and stores the result in `dst`. 2437 pub fn xmm_vmovskp_rr( 2438 &mut self, 2439 src: Reg, 2440 dst: WritableReg, 2441 src_size: OperandSize, 2442 dst_size: OperandSize, 2443 ) { 2444 assert_eq!(dst_size, OperandSize::S32); 2445 let dst: WritableGpr = dst.map(|r| r.into()); 2446 let inst = match src_size { 2447 OperandSize::S32 => asm::inst::vmovmskps_rm::new(dst, src).into(), 2448 OperandSize::S64 => asm::inst::vmovmskpd_rm::new(dst, src).into(), 2449 _ => unimplemented!(), 2450 }; 2451 2452 self.emit(Inst::External { inst }); 2453 } 2454 2455 /// Compute the absolute value of elements in vector `src` and put the 2456 /// results in `dst`. 2457 pub fn xmm_vpabs_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) { 2458 let op = match size { 2459 OperandSize::S8 => AvxOpcode::Vpabsb, 2460 OperandSize::S16 => AvxOpcode::Vpabsw, 2461 OperandSize::S32 => AvxOpcode::Vpabsd, 2462 _ => unimplemented!(), 2463 }; 2464 2465 self.emit(Inst::XmmUnaryRmRVex { 2466 op, 2467 src: src.into(), 2468 dst: dst.to_reg().into(), 2469 }); 2470 } 2471 2472 /// Arithmetically (sign preserving) right shift on vector in `src` by 2473 /// `imm` with result written to `dst`. 2474 pub fn xmm_vpsra_rri(&mut self, src: Reg, dst: WritableReg, imm: u32, size: OperandSize) { 2475 let op = match size { 2476 OperandSize::S32 => AvxOpcode::Vpsrad, 2477 _ => unimplemented!(), 2478 }; 2479 2480 self.emit(Inst::XmmRmiRVex { 2481 op, 2482 src1: src.into(), 2483 src2: XmmMemImm::unwrap_new(RegMemImm::imm(imm)), 2484 dst: dst.to_reg().into(), 2485 }); 2486 } 2487 2488 fn vandp_opcode(size: OperandSize) -> AvxOpcode { 2489 match size { 2490 OperandSize::S32 => AvxOpcode::Vandps, 2491 OperandSize::S64 => AvxOpcode::Vandpd, 2492 _ => unimplemented!(), 2493 } 2494 } 2495 2496 /// Perform an `and` operation on vectors of floats in `src1` and `src2` 2497 /// and put the results in `dst`. 2498 pub fn xmm_vandp_rrm( 2499 &mut self, 2500 src1: Reg, 2501 src2: &Address, 2502 dst: WritableReg, 2503 size: OperandSize, 2504 ) { 2505 let address = Self::to_synthetic_amode(src2, MemFlags::trusted()); 2506 2507 self.emit(Inst::XmmRmiRVex { 2508 op: Self::vandp_opcode(size), 2509 src1: src1.into(), 2510 src2: XmmMemImm::unwrap_new(RegMemImm::mem(address)), 2511 dst: dst.to_reg().into(), 2512 }); 2513 } 2514 2515 /// Perform an `and` operation on vectors of floats in `src1` and `src2` 2516 /// and put the results in `dst`. 2517 pub fn xmm_vandp_rrr(&mut self, src1: Reg, src2: Reg, dst: WritableReg, size: OperandSize) { 2518 self.emit(Inst::XmmRmiRVex { 2519 op: Self::vandp_opcode(size), 2520 src1: src1.into(), 2521 src2: src2.into(), 2522 dst: dst.to_reg().into(), 2523 }); 2524 } 2525 2526 /// Perform an `and not` operation on vectors of floats in `src1` and 2527 /// `src2` and put the results in `dst`. 2528 pub fn xmm_vandnp_rrr(&mut self, src1: Reg, src2: Reg, dst: WritableReg, size: OperandSize) { 2529 let op = match size { 2530 OperandSize::S32 => AvxOpcode::Vandnps, 2531 OperandSize::S64 => AvxOpcode::Vandnpd, 2532 _ => unimplemented!(), 2533 }; 2534 2535 self.emit(Inst::XmmRmiRVex { 2536 op, 2537 src1: src1.into(), 2538 src2: src2.into(), 2539 dst: dst.to_reg().into(), 2540 }); 2541 } 2542 2543 /// Perform a max operation across two vectors of floats and put the 2544 /// results in `dst`. 2545 pub fn xmm_vmaxp_rrr(&mut self, src1: Reg, src2: Reg, dst: WritableReg, size: OperandSize) { 2546 let op = match size { 2547 OperandSize::S32 => AvxOpcode::Vmaxps, 2548 OperandSize::S64 => AvxOpcode::Vmaxpd, 2549 _ => unimplemented!(), 2550 }; 2551 2552 self.emit(Inst::XmmRmiRVex { 2553 op, 2554 src1: src1.into(), 2555 src2: src2.into(), 2556 dst: dst.to_reg().into(), 2557 }); 2558 } 2559 2560 fn vminp_opcode(size: OperandSize) -> AvxOpcode { 2561 match size { 2562 OperandSize::S32 => AvxOpcode::Vminps, 2563 OperandSize::S64 => AvxOpcode::Vminpd, 2564 _ => unimplemented!(), 2565 } 2566 } 2567 2568 // Perform a min operation across two vectors of floats and put the 2569 // results in `dst`. 2570 pub fn xmm_vminp_rrm( 2571 &mut self, 2572 src1: Reg, 2573 src2: &Address, 2574 dst: WritableReg, 2575 size: OperandSize, 2576 ) { 2577 let address = Self::to_synthetic_amode(src2, MemFlags::trusted()); 2578 2579 self.emit(Inst::XmmRmiRVex { 2580 op: Self::vminp_opcode(size), 2581 src1: src1.into(), 2582 src2: XmmMemImm::unwrap_new(RegMemImm::mem(address)), 2583 dst: dst.to_reg().into(), 2584 }); 2585 } 2586 2587 // Perform a min operation across two vectors of floats and put the 2588 // results in `dst`. 2589 pub fn xmm_vminp_rrr(&mut self, src1: Reg, src2: Reg, dst: WritableReg, size: OperandSize) { 2590 self.emit(Inst::XmmRmiRVex { 2591 op: Self::vminp_opcode(size), 2592 src1: src1.into(), 2593 src2: src2.into(), 2594 dst: dst.to_reg().into(), 2595 }); 2596 } 2597 2598 // Round a vector of floats. 2599 pub fn xmm_vroundp_rri( 2600 &mut self, 2601 src: Reg, 2602 dst: WritableReg, 2603 mode: VroundMode, 2604 size: OperandSize, 2605 ) { 2606 let dst: WritableXmm = dst.map(|r| r.into()); 2607 let imm = match mode { 2608 VroundMode::TowardNearest => 0, 2609 VroundMode::TowardNegativeInfinity => 1, 2610 VroundMode::TowardPositiveInfinity => 2, 2611 VroundMode::TowardZero => 3, 2612 }; 2613 2614 let inst = match size { 2615 OperandSize::S32 => asm::inst::vroundps_rmi::new(dst, src, imm).into(), 2616 OperandSize::S64 => asm::inst::vroundpd_rmi::new(dst, src, imm).into(), 2617 _ => unimplemented!(), 2618 }; 2619 2620 self.emit(Inst::External { inst }); 2621 } 2622 2623 /// Shuffle of vectors of floats. 2624 pub fn xmm_vshufp_rrri( 2625 &mut self, 2626 src1: Reg, 2627 src2: Reg, 2628 dst: WritableReg, 2629 imm: u8, 2630 size: OperandSize, 2631 ) { 2632 let op = match size { 2633 OperandSize::S32 => AvxOpcode::Vshufps, 2634 _ => unimplemented!(), 2635 }; 2636 2637 self.emit(Inst::XmmRmRImmVex { 2638 op, 2639 src1: src1.into(), 2640 src2: src2.into(), 2641 dst: dst.to_reg().into(), 2642 imm, 2643 }); 2644 } 2645 2646 /// Each lane in `src1` is multiplied by the corresponding lane in `src2` 2647 /// producing intermediate 32-bit operands. Each intermediate 32-bit 2648 /// operand is truncated to 18 most significant bits. Rounding is performed 2649 /// by adding 1 to the least significant bit of the 18-bit intermediate 2650 /// result. The 16 bits immediately to the right of the most significant 2651 /// bit of each 18-bit intermediate result is placed in each lane of `dst`. 2652 pub fn xmm_vpmulhrs_rrr(&mut self, src1: Reg, src2: Reg, dst: WritableReg, size: OperandSize) { 2653 let op = match size { 2654 OperandSize::S16 => AvxOpcode::Vpmulhrsw, 2655 _ => unimplemented!(), 2656 }; 2657 2658 self.emit(Inst::XmmRmiRVex { 2659 op, 2660 src1: src1.into(), 2661 src2: src2.into(), 2662 dst: dst.to_reg().into(), 2663 }); 2664 } 2665 2666 /// Performs a bitwise `and` operation on the vectors in `src1` and `src2` 2667 /// and stores the results in `dst`. 2668 pub fn xmm_vpand_rrm(&mut self, src1: Reg, src2: &Address, dst: WritableReg) { 2669 let address = Self::to_synthetic_amode(&src2, MemFlags::trusted()); 2670 2671 self.emit(Inst::XmmRmiRVex { 2672 op: AvxOpcode::Vpand, 2673 src1: src1.into(), 2674 src2: XmmMemImm::unwrap_new(RegMemImm::mem(address)), 2675 dst: dst.to_reg().into(), 2676 }); 2677 } 2678 2679 /// Perform an average operation for the vector of unsigned integers in 2680 /// `src1` and `src2` and put the results in `dst`. 2681 pub fn xmm_vpavg_rrr(&mut self, src1: Reg, src2: Reg, dst: WritableReg, size: OperandSize) { 2682 let op = match size { 2683 OperandSize::S8 => AvxOpcode::Vpavgb, 2684 OperandSize::S16 => AvxOpcode::Vpavgw, 2685 _ => unimplemented!(), 2686 }; 2687 2688 self.emit(Inst::XmmRmiRVex { 2689 op, 2690 src1: src1.into(), 2691 src2: src2.into(), 2692 dst: dst.to_reg().into(), 2693 }); 2694 } 2695 2696 /// Perform an or operation for the vectors of floats in `src1` and `src2` 2697 /// and put the results in `dst`. 2698 pub fn xmm_vorp_rrr(&mut self, src1: Reg, src2: Reg, dst: WritableReg, size: OperandSize) { 2699 let op = match size { 2700 OperandSize::S32 => AvxOpcode::Vorps, 2701 OperandSize::S64 => AvxOpcode::Vorpd, 2702 _ => unimplemented!(), 2703 }; 2704 2705 self.emit(Inst::XmmRmiRVex { 2706 op, 2707 src1: src1.into(), 2708 src2: src2.into(), 2709 dst: dst.to_reg().into(), 2710 }); 2711 } 2712 2713 /// Divide the vector of floats in `src1` by the vector of floats in `src2` 2714 /// and put the results in `dst`. 2715 pub fn xmm_vdivp_rrr(&mut self, src1: Reg, src2: Reg, dst: WritableReg, size: OperandSize) { 2716 let op = match size { 2717 OperandSize::S32 => AvxOpcode::Vdivps, 2718 OperandSize::S64 => AvxOpcode::Vdivpd, 2719 _ => unimplemented!(), 2720 }; 2721 2722 self.emit(Inst::XmmRmiRVex { 2723 op, 2724 src1: src1.into(), 2725 src2: src2.into(), 2726 dst: dst.to_reg().into(), 2727 }); 2728 } 2729 2730 /// Compute square roots of vector of floats in `src` and put the results 2731 /// in `dst`. 2732 pub fn xmm_vsqrtp_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) { 2733 let op = match size { 2734 OperandSize::S32 => AvxOpcode::Vsqrtps, 2735 OperandSize::S64 => AvxOpcode::Vsqrtpd, 2736 _ => unimplemented!(), 2737 }; 2738 2739 self.emit(Inst::XmmUnaryRmRVex { 2740 op, 2741 src: src.into(), 2742 dst: dst.to_reg().into(), 2743 }); 2744 } 2745 2746 /// Multiply and add packed signed and unsigned bytes. 2747 pub fn xmm_vpmaddubs_rmr( 2748 &mut self, 2749 src: Reg, 2750 address: &Address, 2751 dst: WritableReg, 2752 size: OperandSize, 2753 ) { 2754 let address = Self::to_synthetic_amode(address, MemFlags::trusted()); 2755 2756 let op = match size { 2757 OperandSize::S16 => AvxOpcode::Vpmaddubsw, 2758 _ => unimplemented!(), 2759 }; 2760 2761 self.emit(Inst::XmmRmiRVex { 2762 op, 2763 src1: src.into(), 2764 src2: XmmMemImm::unwrap_new(RegMemImm::mem(address)), 2765 dst: dst.to_reg().into(), 2766 }); 2767 } 2768 2769 /// Multiple and add packed integers. 2770 pub fn xmm_vpmaddwd_rmr(&mut self, src: Reg, address: &Address, dst: WritableReg) { 2771 let address = Self::to_synthetic_amode(address, MemFlags::trusted()); 2772 2773 self.emit(Inst::XmmRmiRVex { 2774 op: AvxOpcode::Vpmaddwd, 2775 src1: src.into(), 2776 src2: XmmMemImm::unwrap_new(RegMemImm::mem(address)), 2777 dst: dst.to_reg().into(), 2778 }) 2779 } 2780 2781 /// Perform a logical on vector in `src` and in `address` and put the 2782 /// results in `dst`. 2783 pub fn xmm_vpxor_rmr(&mut self, src: Reg, address: &Address, dst: WritableReg) { 2784 let address = Self::to_synthetic_amode(address, MemFlags::trusted()); 2785 2786 self.emit(Inst::XmmRmiRVex { 2787 op: AvxOpcode::Vpxor, 2788 src1: src.into(), 2789 src2: XmmMemImm::unwrap_new(RegMemImm::mem(address)), 2790 dst: dst.to_reg().into(), 2791 }) 2792 } 2793 } 2794 2795 /// Captures the region in a MachBuffer where an add-with-immediate instruction would be emitted, 2796 /// but the immediate is not yet known. Currently, this implementation expects a 32-bit immediate, 2797 /// so 8 and 16 bit operand sizes are not supported. 2798 pub(crate) struct PatchableAddToReg { 2799 /// The region to be patched in the [`MachBuffer`]. It must contain a valid add instruction 2800 /// sequence, accepting a 32-bit immediate. 2801 region: PatchRegion, 2802 2803 /// The offset into the patchable region where the patchable constant begins. 2804 constant_offset: usize, 2805 } 2806 2807 impl PatchableAddToReg { 2808 /// Create a new [`PatchableAddToReg`] by capturing a region in the output buffer where the 2809 /// add-with-immediate occurs. The [`MachBuffer`] will have and add-with-immediate instruction 2810 /// present in that region, though it will add `0` until the `::finalize` method is called. 2811 /// 2812 /// Currently this implementation expects to be able to patch a 32-bit immediate, which means 2813 /// that 8 and 16-bit addition cannot be supported. 2814 pub(crate) fn new(reg: Reg, size: OperandSize, buf: &mut MachBuffer<Inst>) -> Self { 2815 let open = buf.start_patchable(); 2816 2817 // Emit the opcode and register use for the add instruction. 2818 let start = buf.cur_offset(); 2819 Self::add_inst_bytes(reg, size, buf); 2820 let constant_offset = usize::try_from(buf.cur_offset() - start).unwrap(); 2821 2822 // Emit a placeholder for the 32-bit immediate. 2823 buf.put4(0); 2824 2825 let region = buf.end_patchable(open); 2826 2827 Self { 2828 region, 2829 constant_offset, 2830 } 2831 } 2832 2833 /// Generate the prefix of the add instruction (rex byte (depending on register use), opcode, 2834 /// and register reference). 2835 fn add_inst_bytes(reg: Reg, size: OperandSize, buf: &mut MachBuffer<Inst>) { 2836 match size { 2837 OperandSize::S32 | OperandSize::S64 => {} 2838 _ => { 2839 panic!( 2840 "{}-bit addition is not supported, please see the comment on PatchableAddToReg::new", 2841 size.num_bits(), 2842 ) 2843 } 2844 } 2845 2846 let enc_g = 0; 2847 2848 debug_assert!(reg.is_int()); 2849 let enc_e = u8::try_from(reg.hw_enc()).unwrap(); 2850 2851 RexFlags::from(args::OperandSize::from(size)).emit_two_op(buf, enc_g, enc_e); 2852 2853 // the opcode for an add 2854 buf.put1(0x81); 2855 2856 // the modrm byte 2857 buf.put1(encode_modrm(0b11, enc_g & 7, enc_e & 7)); 2858 } 2859 2860 /// Patch the [`MachBuffer`] with the known constant to be added to the register. The final 2861 /// value is passed in as an i32, but the instruction encoding is fixed when 2862 /// [`PatchableAddToReg::new`] is called. 2863 pub(crate) fn finalize(self, val: i32, buffer: &mut MachBuffer<Inst>) { 2864 let slice = self.region.patch(buffer); 2865 debug_assert_eq!(slice.len(), self.constant_offset + 4); 2866 slice[self.constant_offset..].copy_from_slice(val.to_le_bytes().as_slice()); 2867 } 2868 } 2869