Path: blob/main/winch/codegen/src/isa/aarch64/masm.rs
3076 views
use super::{1ABI, RegAlloc,2abi::Aarch64ABI,3address::Address,4asm::{Assembler, PatchableAddToReg},5regs::{self, scratch_fpr_bitset, scratch_gpr_bitset},6};7use crate::{8Result,9abi::{self, align_to, calculate_frame_adjustment, local::LocalSlot, vmctx},10bail,11codegen::{CodeGenContext, CodeGenError, Emission, FuncEnv, ptr_type_from_ptr_size},12format_err,13isa::{14CallingConvention,15aarch64::abi::SHADOW_STACK_POINTER_SLOT_SIZE,16reg::{Reg, WritableReg, writable},17},18masm::{19CalleeKind, DivKind, Extend, ExtendKind, ExtractLaneKind, FloatCmpKind, FloatScratch,20Imm as I, IntCmpKind, IntScratch, LoadKind, MacroAssembler as Masm, MulWideKind,21OperandSize, RegImm, RemKind, ReplaceLaneKind, RmwOp, RoundingMode, SPOffset, Scratch,22ScratchType, ShiftKind, SplatKind, StackSlot, StoreKind, TRUSTED_FLAGS, TrapCode,23TruncKind, UNTRUSTED_FLAGS, V128AbsKind, V128AddKind, V128ConvertKind, V128ExtAddKind,24V128ExtMulKind, V128ExtendKind, V128MaxKind, V128MinKind, V128MulKind, V128NarrowKind,25V128NegKind, V128SubKind, V128TruncKind, VectorCompareKind, VectorEqualityKind, Zero,26},27stack::TypedReg,28};29use cranelift_codegen::{30Final, MachBufferFinalized, MachLabel,31binemit::CodeOffset,32ir::{MemFlags, RelSourceLoc, SourceLoc},33isa::aarch64::inst::{self, Cond, Imm12, ImmLogic, ImmShift, VectorSize},34settings,35};36use regalloc2::RegClass;37use wasmtime_environ::{PtrSize, WasmValType};3839/// Aarch64 MacroAssembler.40pub(crate) struct MacroAssembler {41/// This value represents the maximum stack size seen while compiling the42/// function. While the function is still being compiled its value will not43/// be valid (the stack will grow and shrink as space is reserved and freed44/// during compilation), but once all instructions have been seen this value45/// will be the maximum stack usage seen.46sp_max: u32,4748/// Add-with-immediate patchable instruction sequence used to add the49/// constant stack max to a register.50stack_max_use_add: Option<PatchableAddToReg>,5152/// Low level assembler.53asm: Assembler,54/// Stack pointer offset.55sp_offset: u32,56/// The target pointer size.57ptr_size: OperandSize,58/// Scratch register scope.59scratch_scope: RegAlloc,60}6162impl MacroAssembler {63/// Create an Aarch64 MacroAssembler.64pub fn new(ptr_size: impl PtrSize, shared_flags: settings::Flags) -> Result<Self> {65Ok(Self {66sp_max: 0,67stack_max_use_add: None,68asm: Assembler::new(shared_flags),69sp_offset: 0u32,70ptr_size: ptr_type_from_ptr_size(ptr_size.size()).try_into()?,71scratch_scope: RegAlloc::from(scratch_gpr_bitset(), scratch_fpr_bitset()),72})73}7475/// Add the maximum stack used to a register, recording an obligation to update the76/// add-with-immediate instruction emitted to use the real stack max when the masm is being77/// finalized.78fn add_stack_max(&mut self, reg: WritableReg, tmp: WritableReg) {79assert!(self.stack_max_use_add.is_none());80let patch = PatchableAddToReg::new(reg, tmp, self.asm.buffer_mut());81self.stack_max_use_add.replace(patch);82}8384/// Ensures that the stack pointer remains 16-byte aligned for the duration85/// of the provided function. This alignment is necessary for AArch6486/// compliance, particularly for signal handlers that may be invoked87/// during execution. While the compiler doesn't directly use the stack88/// pointer for memory addressing, maintaining this alignment is crucial89/// to prevent issues when handling signals.90pub fn with_aligned_sp<F, T>(&mut self, f: F) -> Result<T>91where92F: FnOnce(&mut Self) -> Result<T>,93{94let mut aligned = false;95let alignment: u32 = <Aarch64ABI as ABI>::call_stack_align().into();96let addend: u32 = <Aarch64ABI as ABI>::initial_frame_size().into();97let delta = calculate_frame_adjustment(self.sp_offset()?.as_u32(), addend, alignment);98if delta != 0 {99self.sub(100writable!(regs::sp()),101// Since we don't need to synchronize the shadow stack pointer102// when freeing stack space [^1], the stack pointer may become103// out of sync with the primary shadow stack pointer. Therefore,104// we use the shadow stack pointer as the reference for105// calculating any alignment delta (self.sp_offset).106//107// [1]: This approach avoids an unnecessary move instruction and108// maintains the invariant of not accessing memory below the109// current stack pointer, preventing issues with signal handlers110// and interrupts.111regs::shadow_sp(),112RegImm::i32(delta as i32),113OperandSize::S64,114)?;115116aligned = true;117}118119let res = f(self)?;120121if aligned {122self.move_shadow_sp_to_sp();123}124125Ok(res)126}127}128129impl Masm for MacroAssembler {130type Address = Address;131type Ptr = u8;132type ABI = Aarch64ABI;133134fn frame_setup(&mut self) -> Result<()> {135let lr = regs::lr();136let fp = regs::fp();137let sp = regs::sp();138139let addr = Address::pre_indexed_from_sp(-16);140self.asm.stp(fp, lr, addr);141self.asm.mov_rr(sp, writable!(fp), OperandSize::S64);142143let addr = Address::pre_indexed_from_sp(-(SHADOW_STACK_POINTER_SLOT_SIZE as i64));144self.asm145.str(regs::shadow_sp(), addr, OperandSize::S64, TRUSTED_FLAGS);146147self.move_sp_to_shadow_sp();148Ok(())149}150151fn check_stack(&mut self, vmctx: Reg) -> Result<()> {152let ptr_size_u8: u8 = self.ptr_size.bytes().try_into().unwrap();153154// The PatchableAddToReg construct on aarch64 is not a single155// add-immediate instruction, but a 3-instruction sequence that loads an156// immediate using 2 mov-immediate instructions into _another_ scratch157// register before adding it into the target scratch register.158//159// In other words, to make this work we use _two_ scratch registers, one160// to hold the limit we're calculating and one helper that's just used161// to load the immediate.162//163// Luckily on aarch64 we have 2 available scratch registers, ip0 and164// ip1.165// NB that this in this case, we manually allocate the scratch registers166// as precision when it comes to its usage is167168let ptr_size = self.ptr_size;169self.with_aligned_sp(|masm| {170masm.with_scratch::<IntScratch, _>(|masm, scratch_stk_limit| {171masm.with_scratch::<IntScratch, _>(|masm, scratch_tmp| {172masm.load_ptr(173masm.address_at_reg(vmctx, ptr_size_u8.vmcontext_store_context().into())?,174scratch_stk_limit.writable(),175)?;176177masm.load_ptr(178Address::offset(179scratch_stk_limit.inner(),180ptr_size_u8.vmstore_context_stack_limit().into(),181),182scratch_stk_limit.writable(),183)?;184185masm.add_stack_max(scratch_stk_limit.writable(), scratch_tmp.writable());186187// Aarch can only do a cmp with sp in the first operand, which means we188// use a less-than comparison, not a greater-than (stack grows down).189masm.cmp(regs::sp(), scratch_stk_limit.inner().into(), ptr_size)?;190masm.asm191.trapif(IntCmpKind::LtU.into(), TrapCode::STACK_OVERFLOW);192193Ok(())194})195})196})197}198199fn frame_restore(&mut self) -> Result<()> {200debug_assert_eq!(self.sp_offset, 0);201202// Sync the real stack pointer with the value of the shadow stack203// pointer.204self.move_shadow_sp_to_sp();205206// Pop the shadow stack pointer. It's assumed that at this point207// `sp_offset` is 0 and therefore the real stack pointer should be208// 16-byte aligned.209let addr = Address::post_indexed_from_sp(SHADOW_STACK_POINTER_SLOT_SIZE as i64);210self.asm.uload(211addr,212writable!(regs::shadow_sp()),213OperandSize::S64,214TRUSTED_FLAGS,215);216217// Restore the link register and frame pointer.218let lr = regs::lr();219let fp = regs::fp();220let addr = Address::post_indexed_from_sp(16);221222self.asm.ldp(fp, lr, addr);223self.asm.ret();224Ok(())225}226227fn reserve_stack(&mut self, bytes: u32) -> Result<()> {228if bytes == 0 {229return Ok(());230}231232let ssp = regs::shadow_sp();233234match Imm12::maybe_from_u64(bytes as u64) {235Some(v) => self.asm.sub_ir(v, ssp, writable!(ssp), OperandSize::S64),236None => {237self.with_scratch::<IntScratch, _>(|masm, scratch| {238masm.asm239.mov_ir(scratch.writable(), I::I64(bytes as u64), OperandSize::S64);240masm.asm241.sub_rrr(scratch.inner(), ssp, writable!(ssp), OperandSize::S64);242});243}244}245246// Even though we're using the shadow stack pointer to reserve stack, we247// must ensure that the real stack pointer reflects the stack claimed so248// far; we can't use stack memory below the real stack pointer as it249// could be clobbered by interrupts or signal handlers.250self.move_shadow_sp_to_sp();251252self.increment_sp(bytes);253Ok(())254}255256fn free_stack(&mut self, bytes: u32) -> Result<()> {257if bytes == 0 {258return Ok(());259}260261let ssp = regs::shadow_sp();262match Imm12::maybe_from_u64(bytes as u64) {263Some(v) => self.asm.add_ir(v, ssp, writable!(ssp), OperandSize::S64),264None => {265self.with_scratch::<IntScratch, _>(|masm, scratch| {266masm.asm267.mov_ir(scratch.writable(), I::I64(bytes as u64), OperandSize::S64);268masm.asm269.add_rrr(ssp, scratch.inner(), writable!(ssp), OperandSize::S64);270});271}272}273274// We must ensure that the real stack pointer reflects the offset275// tracked by `self.sp_offset`, we use such value to calculate276// alignment, which is crucial for calls.277//278// As an optimization: this synchronization doesn't need to happen all279// the time, in theory we could ensure to sync the shadow stack pointer280// with the stack pointer when alignment is required, like at callsites.281// This is the simplest approach at the time of writing, which282// integrates well with the rest of the aarch64 infrastructure.283self.move_shadow_sp_to_sp();284285self.decrement_sp(bytes);286Ok(())287}288289fn reset_stack_pointer(&mut self, offset: SPOffset) -> Result<()> {290self.sp_offset = offset.as_u32();291Ok(())292}293294fn local_address(&mut self, local: &LocalSlot) -> Result<Address> {295let (reg, offset) = local296.addressed_from_sp()297.then(|| {298let offset = self.sp_offset.checked_sub(local.offset).expect(&format!(299"Invalid local offset = {}; sp offset = {}",300local.offset, self.sp_offset301));302(regs::shadow_sp(), offset)303})304.unwrap_or((regs::fp(), local.offset));305306Ok(Address::offset(reg, offset as i64))307}308309fn address_from_sp(&self, offset: SPOffset) -> Result<Self::Address> {310Ok(Address::from_shadow_sp(311(self.sp_offset - offset.as_u32()) as i64,312))313}314315fn address_at_sp(&self, offset: SPOffset) -> Result<Self::Address> {316Ok(Address::from_shadow_sp(offset.as_u32() as i64))317}318319fn address_at_vmctx(&self, offset: u32) -> Result<Self::Address> {320Ok(Address::offset(vmctx!(Self), offset as i64))321}322323fn store_ptr(&mut self, src: Reg, dst: Self::Address) -> Result<()> {324self.store(src.into(), dst, self.ptr_size)325}326327fn store(&mut self, src: RegImm, dst: Address, size: OperandSize) -> Result<()> {328match src {329RegImm::Imm(v) => {330match v {331I::I32(_) | I::I64(_) => {332self.with_scratch::<IntScratch, _>(|masm, scratch| {333masm.asm.mov_ir(scratch.writable(), v, v.size());334masm.asm.str(scratch.inner(), dst, size, TRUSTED_FLAGS);335});336}337imm @ (I::F32(_) | I::F64(_)) => {338self.with_scratch::<FloatScratch, _>(|masm, scratch| {339masm.asm.mov_ir(scratch.writable(), imm, imm.size());340masm.asm.str(scratch.inner(), dst, size, TRUSTED_FLAGS);341});342}343_ => bail!(CodeGenError::unsupported_wasm_type()),344};345Ok(())346}347RegImm::Reg(r) => {348self.asm.str(r, dst, size, TRUSTED_FLAGS);349Ok(())350}351}352}353354fn wasm_store(&mut self, src: Reg, dst: Self::Address, op_kind: StoreKind) -> Result<()> {355self.with_aligned_sp(|masm| match op_kind {356StoreKind::Operand(size) => {357masm.asm.str(src, dst, size, UNTRUSTED_FLAGS);358Ok(())359}360StoreKind::Atomic(_size) => {361Err(format_err!(CodeGenError::unimplemented_masm_instruction()))362}363StoreKind::VectorLane(_selector) => {364Err(format_err!(CodeGenError::unimplemented_masm_instruction()))365}366})367}368369fn with_scratch<T: ScratchType, R>(&mut self, f: impl FnOnce(&mut Self, Scratch) -> R) -> R {370let r = self371.scratch_scope372.reg_for_class(T::reg_class(), &mut |_| Ok(()))373.expect("Scratch register to be available");374375let ret = f(self, Scratch::new(r));376377self.scratch_scope.free(r);378ret379}380381fn call(382&mut self,383stack_args_size: u32,384mut load_callee: impl FnMut(&mut Self) -> Result<(CalleeKind, CallingConvention)>,385) -> Result<u32> {386let alignment: u32 = <Self::ABI as abi::ABI>::call_stack_align().into();387let addend: u32 = <Self::ABI as abi::ABI>::initial_frame_size().into();388let delta = calculate_frame_adjustment(self.sp_offset()?.as_u32(), addend, alignment);389let aligned_args_size = align_to(stack_args_size, alignment);390let total_stack = delta + aligned_args_size;391self.reserve_stack(total_stack)?;392let (callee, call_conv) = load_callee(self)?;393match callee {394CalleeKind::Indirect(reg) => self.asm.call_with_reg(reg, call_conv),395CalleeKind::Direct(idx) => self.asm.call_with_name(idx, call_conv),396}397398Ok(total_stack)399}400401fn load(&mut self, src: Address, dst: WritableReg, size: OperandSize) -> Result<()> {402self.asm.uload(src, dst, size, TRUSTED_FLAGS);403Ok(())404}405406fn load_ptr(&mut self, src: Self::Address, dst: WritableReg) -> Result<()> {407self.load(src, dst, self.ptr_size)408}409410fn wasm_load(&mut self, src: Self::Address, dst: WritableReg, kind: LoadKind) -> Result<()> {411let size = kind.derive_operand_size();412self.with_aligned_sp(|masm| match &kind {413LoadKind::Operand(_) => {414if size == OperandSize::S128 {415bail!(CodeGenError::UnimplementedWasmLoadKind)416} else {417Ok(masm.asm.uload(src, dst, size, UNTRUSTED_FLAGS))418}419}420LoadKind::Splat(_) => bail!(CodeGenError::UnimplementedWasmLoadKind),421LoadKind::ScalarExtend(extend_kind) => {422if extend_kind.signed() {423masm.asm.sload(src, dst, size, UNTRUSTED_FLAGS);424} else {425// unlike x64, unused bits are set to zero so we don't need to extend426masm.asm.uload(src, dst, size, UNTRUSTED_FLAGS);427}428429Ok(())430}431LoadKind::VectorExtend(_vector_extend_kind) => {432bail!(CodeGenError::UnimplementedWasmLoadKind)433}434LoadKind::VectorLane(_selector) => {435bail!(CodeGenError::unimplemented_masm_instruction())436}437LoadKind::Atomic(_, _) => bail!(CodeGenError::unimplemented_masm_instruction()),438LoadKind::VectorZero(_size) => {439bail!(CodeGenError::UnimplementedWasmLoadKind)440}441})442}443444fn compute_addr(445&mut self,446src: Self::Address,447dst: WritableReg,448size: OperandSize,449) -> Result<()> {450let (base, offset) = src.unwrap_offset();451self.add_ir(dst, base, I::i64(offset), size)452}453454fn pop(&mut self, dst: WritableReg, size: OperandSize) -> Result<()> {455let addr = self.address_from_sp(SPOffset::from_u32(self.sp_offset))?;456self.asm.uload(addr, dst, size, TRUSTED_FLAGS);457self.free_stack(size.bytes())458}459460fn sp_offset(&self) -> Result<SPOffset> {461Ok(SPOffset::from_u32(self.sp_offset))462}463464fn finalize(mut self, base: Option<SourceLoc>) -> Result<MachBufferFinalized<Final>> {465if let Some(patch) = self.stack_max_use_add {466patch.finalize(i32::try_from(self.sp_max).unwrap(), self.asm.buffer_mut());467}468469Ok(self.asm.finalize(base))470}471472fn mov(&mut self, dst: WritableReg, src: RegImm, size: OperandSize) -> Result<()> {473match (src, dst) {474(RegImm::Imm(v), _) => match v {475I::I32(_) | I::I64(_) => {476self.asm.mov_ir(dst, v, v.size());477Ok(())478}479imm @ (I::F32(_) | I::F64(_)) => {480self.asm.mov_ir(dst, imm, imm.size());481Ok(())482}483I::V128(_) => bail!(CodeGenError::unsupported_imm()),484},485(RegImm::Reg(rs), rd) => match (rs.class(), rd.to_reg().class()) {486(RegClass::Int, RegClass::Int) => Ok(self.asm.mov_rr(rs, rd, size)),487(RegClass::Float, RegClass::Float) => Ok(self.asm.fmov_rr(rs, rd, size)),488(RegClass::Int, RegClass::Float) => Ok(self.asm.mov_to_fpu(rs, rd, size)),489_ => bail!(CodeGenError::invalid_operand_combination()),490},491}492}493494fn cmov(495&mut self,496dst: WritableReg,497src: Reg,498cc: IntCmpKind,499size: OperandSize,500) -> Result<()> {501match (src.class(), dst.to_reg().class()) {502(RegClass::Int, RegClass::Int) => self.asm.csel(src, dst.to_reg(), dst, Cond::from(cc)),503(RegClass::Float, RegClass::Float) => {504self.asm505.fpu_csel(src, dst.to_reg(), dst, Cond::from(cc), size)506}507_ => return Err(format_err!(CodeGenError::invalid_operand_combination())),508}509510Ok(())511}512513fn add(&mut self, dst: WritableReg, lhs: Reg, rhs: RegImm, size: OperandSize) -> Result<()> {514match (rhs, lhs, dst) {515(RegImm::Imm(v), rn, rd) => self.add_ir(rd, rn, v, size),516517(RegImm::Reg(rm), rn, rd) => {518self.asm.add_rrr(rm, rn, rd, size);519Ok(())520}521}522}523524fn checked_uadd(525&mut self,526dst: WritableReg,527lhs: Reg,528rhs: RegImm,529size: OperandSize,530trap: TrapCode,531) -> Result<()> {532// Similar to all the other potentially-trapping operations, we need to533// ensure that the real SP is 16-byte aligned in case control flow is534// transferred to a signal handler.535self.with_aligned_sp(|masm| {536match (rhs, lhs, dst) {537// NB: we don't use `Self::add_ir` since we explicitly538// want to emit the add variant which sets overflow539// flags.540(RegImm::Imm(i), rn, rd) => {541let imm = i.unwrap_as_u64();542match Imm12::maybe_from_u64(imm) {543Some(imm12) => masm.asm.adds_ir(imm12, rn, rd, size),544None => {545masm.with_scratch::<IntScratch, _>(|masm, scratch| {546masm.asm.mov_ir(scratch.writable(), i, i.size());547masm.asm.adds_rrr(scratch.inner(), rn, rd, size);548});549}550}551}552553(RegImm::Reg(rm), rn, rd) => {554masm.asm.adds_rrr(rm, rn, rd, size);555}556}557masm.asm.trapif(Cond::Hs, trap);558Ok(())559})560}561562fn sub(&mut self, dst: WritableReg, lhs: Reg, rhs: RegImm, size: OperandSize) -> Result<()> {563match (rhs, lhs, dst) {564(RegImm::Imm(v), rn, rd) => {565let imm = v.unwrap_as_u64();566match Imm12::maybe_from_u64(imm) {567Some(imm12) => self.asm.sub_ir(imm12, rn, rd, size),568None => {569self.with_scratch::<IntScratch, _>(|masm, scratch| {570masm.asm.mov_ir(scratch.writable(), v, v.size());571masm.asm.sub_rrr(scratch.inner(), rn, rd, size);572});573}574};575576Ok(())577}578579(RegImm::Reg(rm), rn, rd) => {580self.asm.sub_rrr(rm, rn, rd, size);581Ok(())582}583}584}585586fn mul(&mut self, dst: WritableReg, lhs: Reg, rhs: RegImm, size: OperandSize) -> Result<()> {587match (rhs, lhs, dst) {588(RegImm::Imm(v), rn, rd) => self.with_scratch::<IntScratch, _>(|masm, scratch| {589masm.asm.mov_ir(scratch.writable(), v, v.size());590masm.asm.mul_rrr(scratch.inner(), rn, rd, size);591Ok(())592}),593594(RegImm::Reg(rm), rn, rd) => {595self.asm.mul_rrr(rm, rn, rd, size);596Ok(())597}598}599}600601fn float_add(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize) -> Result<()> {602self.asm.fadd_rrr(rhs, lhs, dst, size);603Ok(())604}605606fn float_sub(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize) -> Result<()> {607self.asm.fsub_rrr(rhs, lhs, dst, size);608Ok(())609}610611fn float_mul(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize) -> Result<()> {612self.asm.fmul_rrr(rhs, lhs, dst, size);613Ok(())614}615616fn float_div(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize) -> Result<()> {617self.asm.fdiv_rrr(rhs, lhs, dst, size);618Ok(())619}620621fn float_min(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize) -> Result<()> {622self.asm.fmin_rrr(rhs, lhs, dst, size);623Ok(())624}625626fn float_max(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize) -> Result<()> {627self.asm.fmax_rrr(rhs, lhs, dst, size);628Ok(())629}630631fn float_copysign(632&mut self,633dst: WritableReg,634lhs: Reg,635rhs: Reg,636size: OperandSize,637) -> Result<()> {638let max_shift = match size {639OperandSize::S32 => 0x1f,640OperandSize::S64 => 0x3f,641_ => bail!(CodeGenError::unexpected_operand_size()),642};643self.asm.fushr_rri(rhs, writable!(rhs), max_shift, size);644self.asm.fsli_rri_mod(lhs, rhs, dst, max_shift, size);645Ok(())646}647648fn float_neg(&mut self, dst: WritableReg, size: OperandSize) -> Result<()> {649self.asm.fneg_rr(dst.to_reg(), dst, size);650Ok(())651}652653fn float_abs(&mut self, dst: WritableReg, size: OperandSize) -> Result<()> {654self.asm.fabs_rr(dst.to_reg(), dst, size);655Ok(())656}657658fn float_round<659F: FnMut(&mut FuncEnv<Self::Ptr>, &mut CodeGenContext<Emission>, &mut Self) -> Result<()>,660>(661&mut self,662mode: RoundingMode,663_env: &mut FuncEnv<Self::Ptr>,664context: &mut CodeGenContext<Emission>,665size: OperandSize,666_fallback: F,667) -> Result<()> {668let src = context.pop_to_reg(self, None)?;669self.asm670.fround_rr(src.into(), writable!(src.into()), mode, size);671context.stack.push(src.into());672Ok(())673}674675fn float_sqrt(&mut self, dst: WritableReg, src: Reg, size: OperandSize) -> Result<()> {676self.asm.fsqrt_rr(src, dst, size);677Ok(())678}679680fn and(&mut self, dst: WritableReg, lhs: Reg, rhs: RegImm, size: OperandSize) -> Result<()> {681match (rhs, lhs, dst) {682(RegImm::Imm(v), rn, rd) => {683let imm = v.unwrap_as_u64();684let csize: inst::OperandSize = size.into();685686match ImmLogic::maybe_from_u64(imm, csize.to_ty()) {687Some(imml) => self.asm.and_ir(imml, rn, rd, size),688None => {689self.with_scratch::<IntScratch, _>(|masm, scratch| {690masm.asm.mov_ir(scratch.writable(), v, v.size());691masm.asm.and_rrr(scratch.inner(), rn, rd, size);692});693}694};695696Ok(())697}698699(RegImm::Reg(rm), rn, rd) => {700self.asm.and_rrr(rm, rn, rd, size);701Ok(())702}703}704}705706fn or(&mut self, dst: WritableReg, lhs: Reg, rhs: RegImm, size: OperandSize) -> Result<()> {707match (rhs, lhs, dst) {708(RegImm::Imm(v), rn, rd) => {709let imm = v.unwrap_as_u64();710let csize: inst::OperandSize = size.into();711712match ImmLogic::maybe_from_u64(imm, csize.to_ty()) {713Some(imml) => self.asm.or_ir(imml, rn, rd, size),714None => {715self.with_scratch::<IntScratch, _>(|masm, scratch| {716masm.asm.mov_ir(scratch.writable(), v, v.size());717masm.asm.or_rrr(scratch.inner(), rn, rd, size);718});719}720};721722Ok(())723}724725(RegImm::Reg(rm), rn, rd) => {726self.asm.or_rrr(rm, rn, rd, size);727Ok(())728}729}730}731732fn xor(&mut self, dst: WritableReg, lhs: Reg, rhs: RegImm, size: OperandSize) -> Result<()> {733match (rhs, lhs, dst) {734(RegImm::Imm(v), rn, rd) => {735let imm = v.unwrap_as_u64();736let csize: inst::OperandSize = size.into();737738match ImmLogic::maybe_from_u64(imm, csize.to_ty()) {739Some(imml) => self.asm.xor_ir(imml, rn, rd, size),740None => {741self.with_scratch::<IntScratch, _>(|masm, scratch| {742masm.asm.mov_ir(scratch.writable(), v, v.size());743masm.asm.xor_rrr(scratch.inner(), rn, rd, size);744});745}746};747Ok(())748}749750(RegImm::Reg(rm), rn, rd) => {751self.asm.xor_rrr(rm, rn, rd, size);752Ok(())753}754}755}756757fn shift_ir(758&mut self,759dst: WritableReg,760imm: I,761lhs: Reg,762kind: ShiftKind,763size: OperandSize,764) -> Result<()> {765match ImmShift::maybe_from_u64(imm.unwrap_as_u64()) {766// Immediate Ranges:767// 32-bit variant: 0-31768// 64-bit variant: 0-63769Some(imml) if imml.value() < size.num_bits() => {770self.asm.shift_ir(imml, lhs, dst, kind, size)771}772_ => {773self.with_scratch::<IntScratch, _>(|masm, scratch| {774masm.asm.mov_ir(scratch.writable(), imm, imm.size());775masm.asm.shift_rrr(scratch.inner(), lhs, dst, kind, size);776});777}778};779Ok(())780}781782fn shift(783&mut self,784context: &mut CodeGenContext<Emission>,785kind: ShiftKind,786size: OperandSize,787) -> Result<()> {788let src = context.pop_to_reg(self, None)?;789let dst = context.pop_to_reg(self, None)?;790791self.asm792.shift_rrr(src.into(), dst.into(), writable!(dst.into()), kind, size);793794context.free_reg(src);795context.stack.push(dst.into());796797Ok(())798}799800fn div(801&mut self,802context: &mut CodeGenContext<Emission>,803kind: DivKind,804size: OperandSize,805) -> Result<()> {806context.binop(self, size, |this, dividend, divisor, size| {807this.with_aligned_sp(|this| {808this.asm809.div_rrr(divisor, dividend, writable!(dividend), kind, size);810Ok(())811})?;812match size {813OperandSize::S32 => Ok(TypedReg::new(WasmValType::I32, dividend)),814OperandSize::S64 => Ok(TypedReg::new(WasmValType::I64, dividend)),815_ => Err(format_err!(CodeGenError::unexpected_operand_size())),816}817})818}819820fn rem(821&mut self,822context: &mut CodeGenContext<Emission>,823kind: RemKind,824size: OperandSize,825) -> Result<()> {826context.binop(self, size, |this, dividend, divisor, size| {827this.with_aligned_sp(|this| {828this.with_scratch::<IntScratch, _>(|masm, scratch| {829masm.asm.rem_rrr(830divisor,831dividend,832writable!(dividend),833scratch.writable(),834kind,835size,836);837});838Ok(())839})?;840match size {841OperandSize::S32 => Ok(TypedReg::new(WasmValType::I32, dividend)),842OperandSize::S64 => Ok(TypedReg::new(WasmValType::I64, dividend)),843_ => Err(format_err!(CodeGenError::unexpected_operand_size())),844}845})846}847848fn zero(&mut self, reg: WritableReg) -> Result<()> {849self.asm.mov_ir(reg, I::i64(0), OperandSize::S64);850Ok(())851}852853fn popcnt(&mut self, context: &mut CodeGenContext<Emission>, size: OperandSize) -> Result<()> {854let src = context.pop_to_reg(self, None)?;855self.with_scratch::<FloatScratch, _>(|masm, tmp| {856masm.asm.mov_to_fpu(src.into(), tmp.writable(), size);857masm.asm.cnt(tmp.writable());858masm.asm859.addv(tmp.inner(), tmp.writable(), VectorSize::Size8x8);860masm.asm861.mov_from_vec(tmp.inner(), writable!(src.into()), 0, OperandSize::S8);862});863context.stack.push(src.into());864Ok(())865}866867fn signed_truncate(868&mut self,869dst: WritableReg,870src: Reg,871src_size: OperandSize,872dst_size: OperandSize,873kind: TruncKind,874) -> Result<()> {875self.with_aligned_sp(|masm| {876masm.with_scratch::<FloatScratch, _>(|masm, scratch| {877masm.asm878.fpu_to_int(dst, src, scratch.writable(), src_size, dst_size, kind, true);879});880Ok(())881})882}883884fn unsigned_truncate(885&mut self,886ctx: &mut CodeGenContext<Emission>,887src_size: OperandSize,888dst_size: OperandSize,889kind: TruncKind,890) -> Result<()> {891let dst_ty = match dst_size {892OperandSize::S32 => WasmValType::I32,893OperandSize::S64 => WasmValType::I64,894_ => bail!(CodeGenError::unexpected_operand_size()),895};896897ctx.convert_op(self, dst_ty, |masm, dst, src, dst_size| {898masm.with_aligned_sp(|masm| {899masm.with_scratch::<FloatScratch, _>(|masm, scratch| {900masm.asm.fpu_to_int(901writable!(dst),902src,903scratch.writable(),904src_size,905dst_size,906kind,907false,908);909Ok(())910})911})912})913}914915fn signed_convert(916&mut self,917dst: WritableReg,918src: Reg,919src_size: OperandSize,920dst_size: OperandSize,921) -> Result<()> {922self.asm.cvt_sint_to_float(src, dst, src_size, dst_size);923Ok(())924}925926fn unsigned_convert(927&mut self,928dst: WritableReg,929src: Reg,930_tmp_gpr: Reg,931src_size: OperandSize,932dst_size: OperandSize,933) -> Result<()> {934self.asm.cvt_uint_to_float(src, dst, src_size, dst_size);935Ok(())936}937938fn reinterpret_float_as_int(939&mut self,940dst: WritableReg,941src: Reg,942size: OperandSize,943) -> Result<()> {944self.asm.mov_from_vec(src, dst, 0, size);945Ok(())946}947948fn reinterpret_int_as_float(949&mut self,950dst: WritableReg,951src: Reg,952size: OperandSize,953) -> Result<()> {954self.asm.mov_to_fpu(src, dst, size);955Ok(())956}957958fn demote(&mut self, dst: WritableReg, src: Reg) -> Result<()> {959self.asm960.cvt_float_to_float(src, dst, OperandSize::S64, OperandSize::S32);961Ok(())962}963964fn promote(&mut self, dst: WritableReg, src: Reg) -> Result<()> {965self.asm966.cvt_float_to_float(src, dst, OperandSize::S32, OperandSize::S64);967Ok(())968}969970fn push(&mut self, reg: Reg, size: OperandSize) -> Result<StackSlot> {971self.reserve_stack(size.bytes())?;972let address = self.address_from_sp(SPOffset::from_u32(self.sp_offset))?;973self.asm.str(reg, address, size, TRUSTED_FLAGS);974975Ok(StackSlot {976offset: SPOffset::from_u32(self.sp_offset),977size: size.bytes(),978})979}980981fn address_at_reg(&self, reg: Reg, offset: u32) -> Result<Self::Address> {982Ok(Address::offset(reg, offset as i64))983}984985fn cmp_with_set(986&mut self,987dst: WritableReg,988src: RegImm,989kind: IntCmpKind,990size: OperandSize,991) -> Result<()> {992self.cmp(dst.to_reg(), src, size)?;993self.asm.cset(dst, kind.into());994Ok(())995}996997fn cmp(&mut self, src1: Reg, src2: RegImm, size: OperandSize) -> Result<()> {998match src2 {999RegImm::Reg(src2) => {1000self.asm.subs_rrr(src2, src1, size);1001Ok(())1002}1003RegImm::Imm(v) => {1004let val = v.unwrap_as_u64();1005match Imm12::maybe_from_u64(val) {1006Some(imm12) => self.asm.subs_ir(imm12, src1, size),1007None => {1008self.with_scratch::<IntScratch, _>(|masm, scratch| {1009masm.asm.mov_ir(scratch.writable(), v, v.size());1010masm.asm.subs_rrr(scratch.inner(), src1, size);1011});1012}1013};1014Ok(())1015}1016}1017}10181019fn float_cmp_with_set(1020&mut self,1021dst: WritableReg,1022src1: Reg,1023src2: Reg,1024kind: FloatCmpKind,1025size: OperandSize,1026) -> Result<()> {1027self.asm.fcmp(src1, src2, size);1028self.asm.cset(dst, kind.into());1029Ok(())1030}10311032fn clz(&mut self, dst: WritableReg, src: Reg, size: OperandSize) -> Result<()> {1033self.asm.clz(src, dst, size);1034Ok(())1035}10361037fn ctz(&mut self, dst: WritableReg, src: Reg, size: OperandSize) -> Result<()> {1038self.with_scratch::<IntScratch, _>(|masm, scratch| {1039masm.asm.rbit(src, scratch.writable(), size);1040masm.asm.clz(scratch.inner(), dst, size);1041Ok(())1042})1043}10441045fn wrap(&mut self, dst: WritableReg, src: Reg) -> Result<()> {1046self.asm.mov_rr(src, dst, OperandSize::S32);1047Ok(())1048}10491050fn extend(&mut self, dst: WritableReg, src: Reg, kind: ExtendKind) -> Result<()> {1051self.asm.extend(src, dst, kind);1052Ok(())1053}10541055fn get_label(&mut self) -> Result<MachLabel> {1056Ok(self.asm.get_label())1057}10581059fn bind(&mut self, label: MachLabel) -> Result<()> {1060let buffer = self.asm.buffer_mut();1061buffer.bind_label(label, &mut Default::default());1062Ok(())1063}10641065fn branch(1066&mut self,1067kind: IntCmpKind,1068lhs: Reg,1069rhs: RegImm,1070taken: MachLabel,1071size: OperandSize,1072) -> Result<()> {1073use IntCmpKind::*;10741075match &(lhs, rhs) {1076(rlhs, RegImm::Reg(rrhs)) => {1077// If the comparison kind is zero or not zero and both operands1078// are the same register, emit an ands instruction. Else we emit1079// a normal comparison.1080if (kind == Eq || kind == Ne) && (rlhs == rrhs) {1081self.asm.ands_rr(*rlhs, *rrhs, size);1082} else {1083self.cmp(lhs, rhs, size)?;1084}1085}1086_ => self.cmp(lhs, rhs, size)?,1087}1088self.asm.jmp_if(kind.into(), taken);1089Ok(())1090}10911092fn jmp(&mut self, target: MachLabel) -> Result<()> {1093self.asm.jmp(target);1094Ok(())1095}10961097fn unreachable(&mut self) -> Result<()> {1098self.with_aligned_sp(|masm| {1099masm.asm.udf(wasmtime_cranelift::TRAP_UNREACHABLE);1100Ok(())1101})1102}11031104fn jmp_table(&mut self, targets: &[MachLabel], index: Reg, tmp: Reg) -> Result<()> {1105// At least one default target.1106debug_assert!(targets.len() >= 1);1107let default_index = targets.len() - 1;1108let max = default_index;1109self.asm.mov_ir(1110writable!(tmp),1111I::i32(i32::try_from(max).unwrap()),1112OperandSize::S32,1113);1114// NB: We only emit the comparison instruction, since1115// `Assembler::jmp_table` (and the underlying Cranelift1116// instruction) will emit spectre mitigation and bounds1117// checks.1118self.asm.subs_rrr(tmp, index, OperandSize::S32);1119let default = targets[default_index];1120let rest = &targets[0..default_index];1121self.with_scratch::<IntScratch, _>(|masm, scratch| {1122masm.asm1123.jmp_table(rest, default, index, scratch.inner(), tmp);1124Ok(())1125})1126}11271128fn trap(&mut self, code: TrapCode) -> Result<()> {1129self.with_aligned_sp(|masm| {1130masm.asm.udf(code);1131Ok(())1132})1133}11341135fn trapz(&mut self, src: Reg, code: TrapCode) -> Result<()> {1136self.with_aligned_sp(|masm| {1137masm.asm.trapz(src, code, OperandSize::S64);1138Ok(())1139})1140}11411142fn trapif(&mut self, cc: IntCmpKind, code: TrapCode) -> Result<()> {1143self.with_aligned_sp(|masm| {1144masm.asm.trapif(cc.into(), code);1145Ok(())1146})1147}11481149fn start_source_loc(&mut self, loc: RelSourceLoc) -> Result<(CodeOffset, RelSourceLoc)> {1150Ok(self.asm.buffer_mut().start_srcloc(loc))1151}11521153fn end_source_loc(&mut self) -> Result<()> {1154self.asm.buffer_mut().end_srcloc();1155Ok(())1156}11571158fn current_code_offset(&self) -> Result<CodeOffset> {1159Ok(self.asm.buffer().cur_offset())1160}11611162fn add128(1163&mut self,1164dst_lo: WritableReg,1165dst_hi: WritableReg,1166lhs_lo: Reg,1167lhs_hi: Reg,1168rhs_lo: Reg,1169rhs_hi: Reg,1170) -> Result<()> {1171let _ = (dst_lo, dst_hi, lhs_lo, lhs_hi, rhs_lo, rhs_hi);1172Err(format_err!(CodeGenError::unimplemented_masm_instruction()))1173}11741175fn sub128(1176&mut self,1177dst_lo: WritableReg,1178dst_hi: WritableReg,1179lhs_lo: Reg,1180lhs_hi: Reg,1181rhs_lo: Reg,1182rhs_hi: Reg,1183) -> Result<()> {1184let _ = (dst_lo, dst_hi, lhs_lo, lhs_hi, rhs_lo, rhs_hi);1185Err(format_err!(CodeGenError::unimplemented_masm_instruction()))1186}11871188fn mul_wide(1189&mut self,1190context: &mut CodeGenContext<Emission>,1191kind: MulWideKind,1192) -> Result<()> {1193let _ = (context, kind);1194Err(format_err!(CodeGenError::unimplemented_masm_instruction()))1195}11961197fn splat(&mut self, _context: &mut CodeGenContext<Emission>, _size: SplatKind) -> Result<()> {1198bail!(CodeGenError::unimplemented_masm_instruction())1199}12001201fn shuffle(&mut self, _dst: WritableReg, _lhs: Reg, _rhs: Reg, _lanes: [u8; 16]) -> Result<()> {1202bail!(CodeGenError::unimplemented_masm_instruction())1203}12041205fn swizzle(&mut self, _dst: WritableReg, _lhs: Reg, _rhs: Reg) -> Result<()> {1206bail!(CodeGenError::unimplemented_masm_instruction())1207}12081209fn atomic_rmw(1210&mut self,1211_context: &mut CodeGenContext<Emission>,1212_addr: Self::Address,1213_size: OperandSize,1214_op: RmwOp,1215_flags: MemFlags,1216_extend: Option<Extend<Zero>>,1217) -> Result<()> {1218Err(format_err!(CodeGenError::unimplemented_masm_instruction()))1219}12201221fn extract_lane(1222&mut self,1223_src: Reg,1224_dst: WritableReg,1225_lane: u8,1226_kind: ExtractLaneKind,1227) -> Result<()> {1228bail!(CodeGenError::unimplemented_masm_instruction())1229}12301231fn replace_lane(1232&mut self,1233_src: RegImm,1234_dst: WritableReg,1235_lane: u8,1236_kind: ReplaceLaneKind,1237) -> Result<()> {1238bail!(CodeGenError::unimplemented_masm_instruction())1239}12401241fn atomic_cas(1242&mut self,1243_context: &mut CodeGenContext<Emission>,1244_addr: Self::Address,1245_size: OperandSize,1246_flags: MemFlags,1247_extend: Option<Extend<Zero>>,1248) -> Result<()> {1249Err(format_err!(CodeGenError::unimplemented_masm_instruction()))1250}12511252fn v128_eq(1253&mut self,1254_dst: WritableReg,1255_lhs: Reg,1256_rhs: Reg,1257_kind: VectorEqualityKind,1258) -> Result<()> {1259bail!(CodeGenError::unimplemented_masm_instruction())1260}12611262fn v128_ne(1263&mut self,1264_dst: WritableReg,1265_lhs: Reg,1266_rhs: Reg,1267_kind: VectorEqualityKind,1268) -> Result<()> {1269bail!(CodeGenError::unimplemented_masm_instruction())1270}12711272fn v128_lt(1273&mut self,1274_dst: WritableReg,1275_lhs: Reg,1276_rhs: Reg,1277_kind: VectorCompareKind,1278) -> Result<()> {1279bail!(CodeGenError::unimplemented_masm_instruction())1280}12811282fn v128_le(1283&mut self,1284_dst: WritableReg,1285_lhs: Reg,1286_rhs: Reg,1287_kind: VectorCompareKind,1288) -> Result<()> {1289bail!(CodeGenError::unimplemented_masm_instruction())1290}12911292fn v128_gt(1293&mut self,1294_dst: WritableReg,1295_lhs: Reg,1296_rhs: Reg,1297_kind: VectorCompareKind,1298) -> Result<()> {1299bail!(CodeGenError::unimplemented_masm_instruction())1300}13011302fn v128_ge(1303&mut self,1304_dst: WritableReg,1305_lhs: Reg,1306_rhs: Reg,1307_kind: VectorCompareKind,1308) -> Result<()> {1309bail!(CodeGenError::unimplemented_masm_instruction())1310}13111312fn v128_not(&mut self, _dst: WritableReg) -> Result<()> {1313Err(format_err!(CodeGenError::unimplemented_masm_instruction()))1314}13151316fn fence(&mut self) -> Result<()> {1317Err(format_err!(CodeGenError::unimplemented_masm_instruction()))1318}13191320fn v128_and(&mut self, _src1: Reg, _src2: Reg, _dst: WritableReg) -> Result<()> {1321Err(format_err!(CodeGenError::unimplemented_masm_instruction()))1322}13231324fn v128_and_not(&mut self, _src1: Reg, _src2: Reg, _dst: WritableReg) -> Result<()> {1325Err(format_err!(CodeGenError::unimplemented_masm_instruction()))1326}13271328fn v128_or(&mut self, _src1: Reg, _src2: Reg, _dst: WritableReg) -> Result<()> {1329Err(format_err!(CodeGenError::unimplemented_masm_instruction()))1330}13311332fn v128_xor(&mut self, _src1: Reg, _src2: Reg, _dst: WritableReg) -> Result<()> {1333Err(format_err!(CodeGenError::unimplemented_masm_instruction()))1334}13351336fn v128_bitselect(1337&mut self,1338_src1: Reg,1339_src2: Reg,1340_mask: Reg,1341_dst: WritableReg,1342) -> Result<()> {1343Err(format_err!(CodeGenError::unimplemented_masm_instruction()))1344}13451346fn v128_any_true(&mut self, _src: Reg, _dst: WritableReg) -> Result<()> {1347Err(format_err!(CodeGenError::unimplemented_masm_instruction()))1348}13491350fn v128_convert(&mut self, _src: Reg, _dst: WritableReg, _kind: V128ConvertKind) -> Result<()> {1351bail!(CodeGenError::unimplemented_masm_instruction())1352}13531354fn v128_narrow(1355&mut self,1356_src1: Reg,1357_src2: Reg,1358_dst: WritableReg,1359_kind: V128NarrowKind,1360) -> Result<()> {1361bail!(CodeGenError::unimplemented_masm_instruction())1362}13631364fn v128_demote(&mut self, _src: Reg, _dst: WritableReg) -> Result<()> {1365bail!(CodeGenError::unimplemented_masm_instruction())1366}13671368fn v128_promote(&mut self, _src: Reg, _dst: WritableReg) -> Result<()> {1369bail!(CodeGenError::unimplemented_masm_instruction())1370}13711372fn v128_extend(&mut self, _src: Reg, _dst: WritableReg, _kind: V128ExtendKind) -> Result<()> {1373bail!(CodeGenError::unimplemented_masm_instruction())1374}13751376fn v128_add(1377&mut self,1378_lhs: Reg,1379_rhs: Reg,1380_dst: WritableReg,1381_kind: V128AddKind,1382) -> Result<()> {1383Err(format_err!(CodeGenError::unimplemented_masm_instruction()))1384}13851386fn v128_sub(1387&mut self,1388_lhs: Reg,1389_rhs: Reg,1390_dst: WritableReg,1391_kind: V128SubKind,1392) -> Result<()> {1393Err(format_err!(CodeGenError::unimplemented_masm_instruction()))1394}13951396fn v128_mul(1397&mut self,1398_context: &mut CodeGenContext<Emission>,1399_kind: V128MulKind,1400) -> Result<()> {1401Err(format_err!(CodeGenError::unimplemented_masm_instruction()))1402}14031404fn v128_abs(&mut self, _src: Reg, _dst: WritableReg, _kind: V128AbsKind) -> Result<()> {1405bail!(CodeGenError::unimplemented_masm_instruction())1406}14071408fn v128_neg(&mut self, _op: WritableReg, _kind: V128NegKind) -> Result<()> {1409Err(format_err!(CodeGenError::unimplemented_masm_instruction()))1410}14111412fn v128_shift(1413&mut self,1414_context: &mut CodeGenContext<Emission>,1415_lane_width: OperandSize,1416_shift_kind: ShiftKind,1417) -> Result<()> {1418Err(format_err!(CodeGenError::unimplemented_masm_instruction()))1419}14201421fn v128_q15mulr_sat_s(1422&mut self,1423_lhs: Reg,1424_rhs: Reg,1425_dst: WritableReg,1426_size: OperandSize,1427) -> Result<()> {1428bail!(CodeGenError::unimplemented_masm_instruction())1429}14301431fn v128_all_true(&mut self, _src: Reg, _dst: WritableReg, _size: OperandSize) -> Result<()> {1432bail!(CodeGenError::unimplemented_masm_instruction())1433}14341435fn v128_bitmask(&mut self, _src: Reg, _dst: WritableReg, _size: OperandSize) -> Result<()> {1436bail!(CodeGenError::unimplemented_masm_instruction())1437}14381439fn v128_trunc(1440&mut self,1441_context: &mut CodeGenContext<Emission>,1442_kind: V128TruncKind,1443) -> Result<()> {1444bail!(CodeGenError::unimplemented_masm_instruction())1445}14461447fn v128_min(1448&mut self,1449_src1: Reg,1450_src2: Reg,1451_dst: WritableReg,1452_kind: V128MinKind,1453) -> Result<()> {1454Err(format_err!(CodeGenError::unimplemented_masm_instruction()))1455}14561457fn v128_max(1458&mut self,1459_src1: Reg,1460_src2: Reg,1461_dst: WritableReg,1462_kind: V128MaxKind,1463) -> Result<()> {1464Err(format_err!(CodeGenError::unimplemented_masm_instruction()))1465}14661467fn v128_extmul(1468&mut self,1469_context: &mut CodeGenContext<Emission>,1470_kind: V128ExtMulKind,1471) -> Result<()> {1472Err(format_err!(CodeGenError::unimplemented_masm_instruction()))1473}14741475fn v128_extadd_pairwise(1476&mut self,1477_src: Reg,1478_dst: WritableReg,1479_kind: V128ExtAddKind,1480) -> Result<()> {1481Err(format_err!(CodeGenError::unimplemented_masm_instruction()))1482}14831484fn v128_dot(&mut self, _lhs: Reg, _rhs: Reg, _dst: WritableReg) -> Result<()> {1485bail!(CodeGenError::unimplemented_masm_instruction())1486}14871488fn v128_popcnt(&mut self, _context: &mut CodeGenContext<Emission>) -> Result<()> {1489bail!(CodeGenError::unimplemented_masm_instruction())1490}14911492fn v128_avgr(1493&mut self,1494_lhs: Reg,1495_rhs: Reg,1496_dst: WritableReg,1497_size: OperandSize,1498) -> Result<()> {1499bail!(CodeGenError::unimplemented_masm_instruction())1500}15011502fn v128_div(1503&mut self,1504_lhs: Reg,1505_rhs: Reg,1506_dst: WritableReg,1507_size: OperandSize,1508) -> Result<()> {1509bail!(CodeGenError::unimplemented_masm_instruction())1510}15111512fn v128_sqrt(&mut self, _src: Reg, _dst: WritableReg, _size: OperandSize) -> Result<()> {1513bail!(CodeGenError::unimplemented_masm_instruction())1514}15151516fn v128_ceil(&mut self, _src: Reg, _dst: WritableReg, _size: OperandSize) -> Result<()> {1517bail!(CodeGenError::unimplemented_masm_instruction())1518}15191520fn v128_floor(&mut self, _src: Reg, _dst: WritableReg, _size: OperandSize) -> Result<()> {1521bail!(CodeGenError::unimplemented_masm_instruction())1522}15231524fn v128_nearest(&mut self, _src: Reg, _dst: WritableReg, _size: OperandSize) -> Result<()> {1525bail!(CodeGenError::unimplemented_masm_instruction())1526}15271528fn v128_pmin(1529&mut self,1530_lhs: Reg,1531_rhs: Reg,1532_dst: WritableReg,1533_size: OperandSize,1534) -> Result<()> {1535bail!(CodeGenError::unimplemented_masm_instruction())1536}15371538fn v128_pmax(1539&mut self,1540_lhs: Reg,1541_rhs: Reg,1542_dst: WritableReg,1543_size: OperandSize,1544) -> Result<()> {1545bail!(CodeGenError::unimplemented_masm_instruction())1546}1547}15481549impl MacroAssembler {1550fn increment_sp(&mut self, bytes: u32) {1551self.sp_offset += bytes;15521553// NOTE: we use `max` here to track the largest stack allocation in `sp_max`. Once we have1554// seen the entire function, this value will represent the maximum size for the stack1555// frame.1556self.sp_max = self.sp_max.max(self.sp_offset);1557}15581559fn decrement_sp(&mut self, bytes: u32) {1560self.sp_offset -= bytes;1561}15621563// Copies the value of the stack pointer to the shadow stack1564// pointer: mov x28, sp15651566// This function is called at the epilogue.1567fn move_sp_to_shadow_sp(&mut self) {1568let sp = regs::sp();1569let shadow_sp = regs::shadow_sp();1570self.asm.mov_rr(sp, writable!(shadow_sp), OperandSize::S64);1571}15721573/// Heloper to add an immediate to a register.1574fn add_ir(&mut self, dst: WritableReg, lhs: Reg, rhs: I, size: OperandSize) -> Result<()> {1575let imm = rhs.unwrap_as_u64();1576match Imm12::maybe_from_u64(imm) {1577Some(imm12) => self.asm.add_ir(imm12, lhs, dst, size),1578None => {1579self.with_scratch::<IntScratch, _>(|masm, scratch| {1580masm.asm.mov_ir(scratch.writable(), rhs, rhs.size());1581masm.asm.add_rrr(scratch.inner(), lhs, dst, size);1582});1583}1584};1585Ok(())1586}15871588// Copies the value of the shadow stack pointer to the stack pointer: mov1589// sp, x28.1590//1591// This function is usually called when the space is claimed, e.g., via1592// a push, when stack space is reserved explicitly or after emitting code1593// that requires explicit stack pointer alignment (code that could result in1594// signal handling).1595//1596// This ensures the stack pointer always reflects the allocated stack space,1597// otherwise any space below the stack pointer could get clobbered with1598// interrupts and signal handlers.1599//1600// This function must also be called at the function epilogue, since the1601// stack pointer is used to restore the current function frame.1602fn move_shadow_sp_to_sp(&mut self) {1603let shadow_sp = regs::shadow_sp();1604let sp = writable!(regs::sp());1605let imm = Imm12::maybe_from_u64(0).unwrap();1606self.asm.add_ir(imm, shadow_sp, sp, OperandSize::S64);1607}1608}160916101611