Path: blob/main/winch/codegen/src/isa/aarch64/masm.rs
1692 views
use super::{1ABI, RegAlloc,2abi::Aarch64ABI,3address::Address,4asm::{Assembler, PatchableAddToReg},5regs::{self, scratch_fpr_bitset, scratch_gpr_bitset},6};7use crate::{8abi::{self, align_to, calculate_frame_adjustment, local::LocalSlot, vmctx},9codegen::{CodeGenContext, CodeGenError, Emission, FuncEnv, ptr_type_from_ptr_size},10isa::{11CallingConvention,12aarch64::abi::SHADOW_STACK_POINTER_SLOT_SIZE,13reg::{Reg, WritableReg, writable},14},15masm::{16CalleeKind, DivKind, Extend, ExtendKind, ExtractLaneKind, FloatCmpKind, FloatScratch,17Imm as I, IntCmpKind, IntScratch, LoadKind, MacroAssembler as Masm, MulWideKind,18OperandSize, RegImm, RemKind, ReplaceLaneKind, RmwOp, RoundingMode, SPOffset, Scratch,19ScratchType, ShiftKind, SplatKind, StackSlot, StoreKind, TRUSTED_FLAGS, TrapCode,20TruncKind, UNTRUSTED_FLAGS, V128AbsKind, V128AddKind, V128ConvertKind, V128ExtAddKind,21V128ExtMulKind, V128ExtendKind, V128MaxKind, V128MinKind, V128MulKind, V128NarrowKind,22V128NegKind, V128SubKind, V128TruncKind, VectorCompareKind, VectorEqualityKind, Zero,23},24stack::TypedReg,25};26use anyhow::{Result, anyhow, bail};27use cranelift_codegen::{28Final, MachBufferFinalized, MachLabel,29binemit::CodeOffset,30ir::{MemFlags, RelSourceLoc, SourceLoc},31isa::aarch64::inst::{self, Cond, Imm12, ImmLogic, ImmShift, VectorSize},32settings,33};34use regalloc2::RegClass;35use wasmtime_environ::{PtrSize, WasmValType};3637/// Aarch64 MacroAssembler.38pub(crate) struct MacroAssembler {39/// This value represents the maximum stack size seen while compiling the40/// function. While the function is still being compiled its value will not41/// be valid (the stack will grow and shrink as space is reserved and freed42/// during compilation), but once all instructions have been seen this value43/// will be the maximum stack usage seen.44sp_max: u32,4546/// Add-with-immediate patchable instruction sequence used to add the47/// constant stack max to a register.48stack_max_use_add: Option<PatchableAddToReg>,4950/// Low level assembler.51asm: Assembler,52/// Stack pointer offset.53sp_offset: u32,54/// The target pointer size.55ptr_size: OperandSize,56/// Scratch register scope.57scratch_scope: RegAlloc,58}5960impl MacroAssembler {61/// Create an Aarch64 MacroAssembler.62pub fn new(ptr_size: impl PtrSize, shared_flags: settings::Flags) -> Result<Self> {63Ok(Self {64sp_max: 0,65stack_max_use_add: None,66asm: Assembler::new(shared_flags),67sp_offset: 0u32,68ptr_size: ptr_type_from_ptr_size(ptr_size.size()).try_into()?,69scratch_scope: RegAlloc::from(scratch_gpr_bitset(), scratch_fpr_bitset()),70})71}7273/// Add the maximum stack used to a register, recording an obligation to update the74/// add-with-immediate instruction emitted to use the real stack max when the masm is being75/// finalized.76fn add_stack_max(&mut self, reg: WritableReg, tmp: WritableReg) {77assert!(self.stack_max_use_add.is_none());78let patch = PatchableAddToReg::new(reg, tmp, self.asm.buffer_mut());79self.stack_max_use_add.replace(patch);80}8182/// Ensures that the stack pointer remains 16-byte aligned for the duration83/// of the provided function. This alignment is necessary for AArch6484/// compliance, particularly for signal handlers that may be invoked85/// during execution. While the compiler doesn't directly use the stack86/// pointer for memory addressing, maintaining this alignment is crucial87/// to prevent issues when handling signals.88pub fn with_aligned_sp<F, T>(&mut self, f: F) -> Result<T>89where90F: FnOnce(&mut Self) -> Result<T>,91{92let mut aligned = false;93let alignment: u32 = <Aarch64ABI as ABI>::call_stack_align().into();94let addend: u32 = <Aarch64ABI as ABI>::initial_frame_size().into();95let delta = calculate_frame_adjustment(self.sp_offset()?.as_u32(), addend, alignment);96if delta != 0 {97self.sub(98writable!(regs::sp()),99// Since we don't need to synchronize the shadow stack pointer100// when freeing stack space [^1], the stack pointer may become101// out of sync with the primary shadow stack pointer. Therefore,102// we use the shadow stack pointer as the reference for103// calculating any alignment delta (self.sp_offset).104//105// [1]: This approach avoids an unnecessary move instruction and106// maintains the invariant of not accessing memory below the107// current stack pointer, preventing issues with signal handlers108// and interrupts.109regs::shadow_sp(),110RegImm::i32(delta as i32),111OperandSize::S64,112)?;113114aligned = true;115}116117let res = f(self)?;118119if aligned {120self.move_shadow_sp_to_sp();121}122123Ok(res)124}125}126127impl Masm for MacroAssembler {128type Address = Address;129type Ptr = u8;130type ABI = Aarch64ABI;131132fn frame_setup(&mut self) -> Result<()> {133let lr = regs::lr();134let fp = regs::fp();135let sp = regs::sp();136137let addr = Address::pre_indexed_from_sp(-16);138self.asm.stp(fp, lr, addr);139self.asm.mov_rr(sp, writable!(fp), OperandSize::S64);140141let addr = Address::pre_indexed_from_sp(-(SHADOW_STACK_POINTER_SLOT_SIZE as i64));142self.asm143.str(regs::shadow_sp(), addr, OperandSize::S64, TRUSTED_FLAGS);144145self.move_sp_to_shadow_sp();146Ok(())147}148149fn check_stack(&mut self, vmctx: Reg) -> Result<()> {150let ptr_size_u8: u8 = self.ptr_size.bytes().try_into().unwrap();151152// The PatchableAddToReg construct on aarch64 is not a single153// add-immediate instruction, but a 3-instruction sequence that loads an154// immediate using 2 mov-immediate instructions into _another_ scratch155// register before adding it into the target scratch register.156//157// In other words, to make this work we use _two_ scratch registers, one158// to hold the limit we're calculating and one helper that's just used159// to load the immediate.160//161// Luckily on aarch64 we have 2 available scratch registers, ip0 and162// ip1.163// NB that this in this case, we manually allocate the scratch registers164// as precision when it comes to its usage is165166let ptr_size = self.ptr_size;167self.with_aligned_sp(|masm| {168masm.with_scratch::<IntScratch, _>(|masm, scratch_stk_limit| {169masm.with_scratch::<IntScratch, _>(|masm, scratch_tmp| {170masm.load_ptr(171masm.address_at_reg(vmctx, ptr_size_u8.vmcontext_store_context().into())?,172scratch_stk_limit.writable(),173)?;174175masm.load_ptr(176Address::offset(177scratch_stk_limit.inner(),178ptr_size_u8.vmstore_context_stack_limit().into(),179),180scratch_stk_limit.writable(),181)?;182183masm.add_stack_max(scratch_stk_limit.writable(), scratch_tmp.writable());184185// Aarch can only do a cmp with sp in the first operand, which means we186// use a less-than comparison, not a greater-than (stack grows down).187masm.cmp(regs::sp(), scratch_stk_limit.inner().into(), ptr_size)?;188masm.asm189.trapif(IntCmpKind::LtU.into(), TrapCode::STACK_OVERFLOW);190191Ok(())192})193})194})195}196197fn frame_restore(&mut self) -> Result<()> {198debug_assert_eq!(self.sp_offset, 0);199200// Sync the real stack pointer with the value of the shadow stack201// pointer.202self.move_shadow_sp_to_sp();203204// Pop the shadow stack pointer. It's assumed that at this point205// `sp_offset` is 0 and therefore the real stack pointer should be206// 16-byte aligned.207let addr = Address::post_indexed_from_sp(SHADOW_STACK_POINTER_SLOT_SIZE as i64);208self.asm.uload(209addr,210writable!(regs::shadow_sp()),211OperandSize::S64,212TRUSTED_FLAGS,213);214215// Restore the link register and frame pointer.216let lr = regs::lr();217let fp = regs::fp();218let addr = Address::post_indexed_from_sp(16);219220self.asm.ldp(fp, lr, addr);221self.asm.ret();222Ok(())223}224225fn reserve_stack(&mut self, bytes: u32) -> Result<()> {226if bytes == 0 {227return Ok(());228}229230let ssp = regs::shadow_sp();231232match Imm12::maybe_from_u64(bytes as u64) {233Some(v) => self.asm.sub_ir(v, ssp, writable!(ssp), OperandSize::S64),234None => {235self.with_scratch::<IntScratch, _>(|masm, scratch| {236masm.asm237.mov_ir(scratch.writable(), I::I64(bytes as u64), OperandSize::S64);238masm.asm239.sub_rrr(scratch.inner(), ssp, writable!(ssp), OperandSize::S64);240});241}242}243244// Even though we're using the shadow stack pointer to reserve stack, we245// must ensure that the real stack pointer reflects the stack claimed so246// far; we can't use stack memory below the real stack pointer as it247// could be clobbered by interrupts or signal handlers.248self.move_shadow_sp_to_sp();249250self.increment_sp(bytes);251Ok(())252}253254fn free_stack(&mut self, bytes: u32) -> Result<()> {255if bytes == 0 {256return Ok(());257}258259let ssp = regs::shadow_sp();260match Imm12::maybe_from_u64(bytes as u64) {261Some(v) => self.asm.add_ir(v, ssp, writable!(ssp), OperandSize::S64),262None => {263self.with_scratch::<IntScratch, _>(|masm, scratch| {264masm.asm265.mov_ir(scratch.writable(), I::I64(bytes as u64), OperandSize::S64);266masm.asm267.add_rrr(ssp, scratch.inner(), writable!(ssp), OperandSize::S64);268});269}270}271272// We must ensure that the real stack pointer reflects the offset273// tracked by `self.sp_offset`, we use such value to calculate274// alignment, which is crucial for calls.275//276// As an optimization: this synchronization doesn't need to happen all277// the time, in theory we could ensure to sync the shadow stack pointer278// with the stack pointer when alignment is required, like at callsites.279// This is the simplest approach at the time of writing, which280// integrates well with the rest of the aarch64 infrastructure.281self.move_shadow_sp_to_sp();282283self.decrement_sp(bytes);284Ok(())285}286287fn reset_stack_pointer(&mut self, offset: SPOffset) -> Result<()> {288self.sp_offset = offset.as_u32();289Ok(())290}291292fn local_address(&mut self, local: &LocalSlot) -> Result<Address> {293let (reg, offset) = local294.addressed_from_sp()295.then(|| {296let offset = self.sp_offset.checked_sub(local.offset).expect(&format!(297"Invalid local offset = {}; sp offset = {}",298local.offset, self.sp_offset299));300(regs::shadow_sp(), offset)301})302.unwrap_or((regs::fp(), local.offset));303304Ok(Address::offset(reg, offset as i64))305}306307fn address_from_sp(&self, offset: SPOffset) -> Result<Self::Address> {308Ok(Address::from_shadow_sp(309(self.sp_offset - offset.as_u32()) as i64,310))311}312313fn address_at_sp(&self, offset: SPOffset) -> Result<Self::Address> {314Ok(Address::from_shadow_sp(offset.as_u32() as i64))315}316317fn address_at_vmctx(&self, offset: u32) -> Result<Self::Address> {318Ok(Address::offset(vmctx!(Self), offset as i64))319}320321fn store_ptr(&mut self, src: Reg, dst: Self::Address) -> Result<()> {322self.store(src.into(), dst, self.ptr_size)323}324325fn store(&mut self, src: RegImm, dst: Address, size: OperandSize) -> Result<()> {326match src {327RegImm::Imm(v) => {328match v {329I::I32(_) | I::I64(_) => {330self.with_scratch::<IntScratch, _>(|masm, scratch| {331masm.asm.mov_ir(scratch.writable(), v, v.size());332masm.asm.str(scratch.inner(), dst, size, TRUSTED_FLAGS);333});334}335imm @ (I::F32(_) | I::F64(_)) => {336self.with_scratch::<FloatScratch, _>(|masm, scratch| {337masm.asm.mov_ir(scratch.writable(), imm, imm.size());338masm.asm.str(scratch.inner(), dst, size, TRUSTED_FLAGS);339});340}341_ => bail!(CodeGenError::unsupported_wasm_type()),342};343Ok(())344}345RegImm::Reg(r) => {346self.asm.str(r, dst, size, TRUSTED_FLAGS);347Ok(())348}349}350}351352fn wasm_store(&mut self, src: Reg, dst: Self::Address, op_kind: StoreKind) -> Result<()> {353self.with_aligned_sp(|masm| match op_kind {354StoreKind::Operand(size) => {355masm.asm.str(src, dst, size, UNTRUSTED_FLAGS);356Ok(())357}358StoreKind::Atomic(_size) => {359Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))360}361StoreKind::VectorLane(_selector) => {362Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))363}364})365}366367fn with_scratch<T: ScratchType, R>(&mut self, f: impl FnOnce(&mut Self, Scratch) -> R) -> R {368let r = self369.scratch_scope370.reg_for_class(T::reg_class(), &mut |_| Ok(()))371.expect("Scratch register to be available");372373let ret = f(self, Scratch::new(r));374375self.scratch_scope.free(r);376ret377}378379fn call(380&mut self,381stack_args_size: u32,382mut load_callee: impl FnMut(&mut Self) -> Result<(CalleeKind, CallingConvention)>,383) -> Result<u32> {384let alignment: u32 = <Self::ABI as abi::ABI>::call_stack_align().into();385let addend: u32 = <Self::ABI as abi::ABI>::initial_frame_size().into();386let delta = calculate_frame_adjustment(self.sp_offset()?.as_u32(), addend, alignment);387let aligned_args_size = align_to(stack_args_size, alignment);388let total_stack = delta + aligned_args_size;389self.reserve_stack(total_stack)?;390let (callee, call_conv) = load_callee(self)?;391match callee {392CalleeKind::Indirect(reg) => self.asm.call_with_reg(reg, call_conv),393CalleeKind::Direct(idx) => self.asm.call_with_name(idx, call_conv),394}395396Ok(total_stack)397}398399fn load(&mut self, src: Address, dst: WritableReg, size: OperandSize) -> Result<()> {400self.asm.uload(src, dst, size, TRUSTED_FLAGS);401Ok(())402}403404fn load_ptr(&mut self, src: Self::Address, dst: WritableReg) -> Result<()> {405self.load(src, dst, self.ptr_size)406}407408fn wasm_load(&mut self, src: Self::Address, dst: WritableReg, kind: LoadKind) -> Result<()> {409let size = kind.derive_operand_size();410self.with_aligned_sp(|masm| match &kind {411LoadKind::Operand(_) => {412if size == OperandSize::S128 {413bail!(CodeGenError::UnimplementedWasmLoadKind)414} else {415Ok(masm.asm.uload(src, dst, size, UNTRUSTED_FLAGS))416}417}418LoadKind::Splat(_) => bail!(CodeGenError::UnimplementedWasmLoadKind),419LoadKind::ScalarExtend(extend_kind) => {420if extend_kind.signed() {421masm.asm.sload(src, dst, size, UNTRUSTED_FLAGS);422} else {423// unlike x64, unused bits are set to zero so we don't need to extend424masm.asm.uload(src, dst, size, UNTRUSTED_FLAGS);425}426427Ok(())428}429LoadKind::VectorExtend(_vector_extend_kind) => {430bail!(CodeGenError::UnimplementedWasmLoadKind)431}432LoadKind::VectorLane(_selector) => {433bail!(CodeGenError::unimplemented_masm_instruction())434}435LoadKind::Atomic(_, _) => bail!(CodeGenError::unimplemented_masm_instruction()),436LoadKind::VectorZero(_size) => {437bail!(CodeGenError::UnimplementedWasmLoadKind)438}439})440}441442fn compute_addr(443&mut self,444src: Self::Address,445dst: WritableReg,446size: OperandSize,447) -> Result<()> {448let (base, offset) = src.unwrap_offset();449self.add_ir(dst, base, I::i64(offset), size)450}451452fn pop(&mut self, dst: WritableReg, size: OperandSize) -> Result<()> {453let addr = self.address_from_sp(SPOffset::from_u32(self.sp_offset))?;454self.asm.uload(addr, dst, size, TRUSTED_FLAGS);455self.free_stack(size.bytes())456}457458fn sp_offset(&self) -> Result<SPOffset> {459Ok(SPOffset::from_u32(self.sp_offset))460}461462fn finalize(mut self, base: Option<SourceLoc>) -> Result<MachBufferFinalized<Final>> {463if let Some(patch) = self.stack_max_use_add {464patch.finalize(i32::try_from(self.sp_max).unwrap(), self.asm.buffer_mut());465}466467Ok(self.asm.finalize(base))468}469470fn mov(&mut self, dst: WritableReg, src: RegImm, size: OperandSize) -> Result<()> {471match (src, dst) {472(RegImm::Imm(v), _) => match v {473I::I32(_) | I::I64(_) => {474self.asm.mov_ir(dst, v, v.size());475Ok(())476}477imm @ (I::F32(_) | I::F64(_)) => {478self.asm.mov_ir(dst, imm, imm.size());479Ok(())480}481I::V128(_) => bail!(CodeGenError::unsupported_imm()),482},483(RegImm::Reg(rs), rd) => match (rs.class(), rd.to_reg().class()) {484(RegClass::Int, RegClass::Int) => Ok(self.asm.mov_rr(rs, rd, size)),485(RegClass::Float, RegClass::Float) => Ok(self.asm.fmov_rr(rs, rd, size)),486(RegClass::Int, RegClass::Float) => Ok(self.asm.mov_to_fpu(rs, rd, size)),487_ => bail!(CodeGenError::invalid_operand_combination()),488},489}490}491492fn cmov(493&mut self,494dst: WritableReg,495src: Reg,496cc: IntCmpKind,497size: OperandSize,498) -> Result<()> {499match (src.class(), dst.to_reg().class()) {500(RegClass::Int, RegClass::Int) => self.asm.csel(src, dst.to_reg(), dst, Cond::from(cc)),501(RegClass::Float, RegClass::Float) => {502self.asm503.fpu_csel(src, dst.to_reg(), dst, Cond::from(cc), size)504}505_ => return Err(anyhow!(CodeGenError::invalid_operand_combination())),506}507508Ok(())509}510511fn add(&mut self, dst: WritableReg, lhs: Reg, rhs: RegImm, size: OperandSize) -> Result<()> {512match (rhs, lhs, dst) {513(RegImm::Imm(v), rn, rd) => self.add_ir(rd, rn, v, size),514515(RegImm::Reg(rm), rn, rd) => {516self.asm.add_rrr(rm, rn, rd, size);517Ok(())518}519}520}521522fn checked_uadd(523&mut self,524dst: WritableReg,525lhs: Reg,526rhs: RegImm,527size: OperandSize,528trap: TrapCode,529) -> Result<()> {530// Similar to all the other potentially-trapping operations, we need to531// ensure that the real SP is 16-byte aligned in case control flow is532// transferred to a signal handler.533self.with_aligned_sp(|masm| {534match (rhs, lhs, dst) {535// NB: we don't use `Self::add_ir` since we explicitly536// want to emit the add variant which sets overflow537// flags.538(RegImm::Imm(i), rn, rd) => {539let imm = i.unwrap_as_u64();540match Imm12::maybe_from_u64(imm) {541Some(imm12) => masm.asm.adds_ir(imm12, rn, rd, size),542None => {543masm.with_scratch::<IntScratch, _>(|masm, scratch| {544masm.asm.mov_ir(scratch.writable(), i, i.size());545masm.asm.adds_rrr(scratch.inner(), rn, rd, size);546});547}548}549}550551(RegImm::Reg(rm), rn, rd) => {552masm.asm.adds_rrr(rm, rn, rd, size);553}554}555masm.asm.trapif(Cond::Hs, trap);556Ok(())557})558}559560fn sub(&mut self, dst: WritableReg, lhs: Reg, rhs: RegImm, size: OperandSize) -> Result<()> {561match (rhs, lhs, dst) {562(RegImm::Imm(v), rn, rd) => {563let imm = v.unwrap_as_u64();564match Imm12::maybe_from_u64(imm) {565Some(imm12) => self.asm.sub_ir(imm12, rn, rd, size),566None => {567self.with_scratch::<IntScratch, _>(|masm, scratch| {568masm.asm.mov_ir(scratch.writable(), v, v.size());569masm.asm.sub_rrr(scratch.inner(), rn, rd, size);570});571}572};573574Ok(())575}576577(RegImm::Reg(rm), rn, rd) => {578self.asm.sub_rrr(rm, rn, rd, size);579Ok(())580}581}582}583584fn mul(&mut self, dst: WritableReg, lhs: Reg, rhs: RegImm, size: OperandSize) -> Result<()> {585match (rhs, lhs, dst) {586(RegImm::Imm(v), rn, rd) => self.with_scratch::<IntScratch, _>(|masm, scratch| {587masm.asm.mov_ir(scratch.writable(), v, v.size());588masm.asm.mul_rrr(scratch.inner(), rn, rd, size);589Ok(())590}),591592(RegImm::Reg(rm), rn, rd) => {593self.asm.mul_rrr(rm, rn, rd, size);594Ok(())595}596}597}598599fn float_add(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize) -> Result<()> {600self.asm.fadd_rrr(rhs, lhs, dst, size);601Ok(())602}603604fn float_sub(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize) -> Result<()> {605self.asm.fsub_rrr(rhs, lhs, dst, size);606Ok(())607}608609fn float_mul(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize) -> Result<()> {610self.asm.fmul_rrr(rhs, lhs, dst, size);611Ok(())612}613614fn float_div(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize) -> Result<()> {615self.asm.fdiv_rrr(rhs, lhs, dst, size);616Ok(())617}618619fn float_min(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize) -> Result<()> {620self.asm.fmin_rrr(rhs, lhs, dst, size);621Ok(())622}623624fn float_max(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize) -> Result<()> {625self.asm.fmax_rrr(rhs, lhs, dst, size);626Ok(())627}628629fn float_copysign(630&mut self,631dst: WritableReg,632lhs: Reg,633rhs: Reg,634size: OperandSize,635) -> Result<()> {636let max_shift = match size {637OperandSize::S32 => 0x1f,638OperandSize::S64 => 0x3f,639_ => bail!(CodeGenError::unexpected_operand_size()),640};641self.asm.fushr_rri(rhs, writable!(rhs), max_shift, size);642self.asm.fsli_rri_mod(lhs, rhs, dst, max_shift, size);643Ok(())644}645646fn float_neg(&mut self, dst: WritableReg, size: OperandSize) -> Result<()> {647self.asm.fneg_rr(dst.to_reg(), dst, size);648Ok(())649}650651fn float_abs(&mut self, dst: WritableReg, size: OperandSize) -> Result<()> {652self.asm.fabs_rr(dst.to_reg(), dst, size);653Ok(())654}655656fn float_round<657F: FnMut(&mut FuncEnv<Self::Ptr>, &mut CodeGenContext<Emission>, &mut Self) -> Result<()>,658>(659&mut self,660mode: RoundingMode,661_env: &mut FuncEnv<Self::Ptr>,662context: &mut CodeGenContext<Emission>,663size: OperandSize,664_fallback: F,665) -> Result<()> {666let src = context.pop_to_reg(self, None)?;667self.asm668.fround_rr(src.into(), writable!(src.into()), mode, size);669context.stack.push(src.into());670Ok(())671}672673fn float_sqrt(&mut self, dst: WritableReg, src: Reg, size: OperandSize) -> Result<()> {674self.asm.fsqrt_rr(src, dst, size);675Ok(())676}677678fn and(&mut self, dst: WritableReg, lhs: Reg, rhs: RegImm, size: OperandSize) -> Result<()> {679match (rhs, lhs, dst) {680(RegImm::Imm(v), rn, rd) => {681let imm = v.unwrap_as_u64();682let csize: inst::OperandSize = size.into();683684match ImmLogic::maybe_from_u64(imm, csize.to_ty()) {685Some(imml) => self.asm.and_ir(imml, rn, rd, size),686None => {687self.with_scratch::<IntScratch, _>(|masm, scratch| {688masm.asm.mov_ir(scratch.writable(), v, v.size());689masm.asm.and_rrr(scratch.inner(), rn, rd, size);690});691}692};693694Ok(())695}696697(RegImm::Reg(rm), rn, rd) => {698self.asm.and_rrr(rm, rn, rd, size);699Ok(())700}701}702}703704fn or(&mut self, dst: WritableReg, lhs: Reg, rhs: RegImm, size: OperandSize) -> Result<()> {705match (rhs, lhs, dst) {706(RegImm::Imm(v), rn, rd) => {707let imm = v.unwrap_as_u64();708let csize: inst::OperandSize = size.into();709710match ImmLogic::maybe_from_u64(imm, csize.to_ty()) {711Some(imml) => self.asm.or_ir(imml, rn, rd, size),712None => {713self.with_scratch::<IntScratch, _>(|masm, scratch| {714masm.asm.mov_ir(scratch.writable(), v, v.size());715masm.asm.or_rrr(scratch.inner(), rn, rd, size);716});717}718};719720Ok(())721}722723(RegImm::Reg(rm), rn, rd) => {724self.asm.or_rrr(rm, rn, rd, size);725Ok(())726}727}728}729730fn xor(&mut self, dst: WritableReg, lhs: Reg, rhs: RegImm, size: OperandSize) -> Result<()> {731match (rhs, lhs, dst) {732(RegImm::Imm(v), rn, rd) => {733let imm = v.unwrap_as_u64();734let csize: inst::OperandSize = size.into();735736match ImmLogic::maybe_from_u64(imm, csize.to_ty()) {737Some(imml) => self.asm.xor_ir(imml, rn, rd, size),738None => {739self.with_scratch::<IntScratch, _>(|masm, scratch| {740masm.asm.mov_ir(scratch.writable(), v, v.size());741masm.asm.xor_rrr(scratch.inner(), rn, rd, size);742});743}744};745Ok(())746}747748(RegImm::Reg(rm), rn, rd) => {749self.asm.xor_rrr(rm, rn, rd, size);750Ok(())751}752}753}754755fn shift_ir(756&mut self,757dst: WritableReg,758imm: I,759lhs: Reg,760kind: ShiftKind,761size: OperandSize,762) -> Result<()> {763match ImmShift::maybe_from_u64(imm.unwrap_as_u64()) {764Some(imml) => self.asm.shift_ir(imml, lhs, dst, kind, size),765None => {766self.with_scratch::<IntScratch, _>(|masm, scratch| {767masm.asm.mov_ir(scratch.writable(), imm, imm.size());768masm.asm.shift_rrr(scratch.inner(), lhs, dst, kind, size);769});770}771};772Ok(())773}774775fn shift(776&mut self,777context: &mut CodeGenContext<Emission>,778kind: ShiftKind,779size: OperandSize,780) -> Result<()> {781let src = context.pop_to_reg(self, None)?;782let dst = context.pop_to_reg(self, None)?;783784self.asm785.shift_rrr(src.into(), dst.into(), writable!(dst.into()), kind, size);786787context.free_reg(src);788context.stack.push(dst.into());789790Ok(())791}792793fn div(794&mut self,795context: &mut CodeGenContext<Emission>,796kind: DivKind,797size: OperandSize,798) -> Result<()> {799context.binop(self, size, |this, dividend, divisor, size| {800this.with_aligned_sp(|this| {801this.asm802.div_rrr(divisor, dividend, writable!(dividend), kind, size);803Ok(())804})?;805match size {806OperandSize::S32 => Ok(TypedReg::new(WasmValType::I32, dividend)),807OperandSize::S64 => Ok(TypedReg::new(WasmValType::I64, dividend)),808_ => Err(anyhow!(CodeGenError::unexpected_operand_size())),809}810})811}812813fn rem(814&mut self,815context: &mut CodeGenContext<Emission>,816kind: RemKind,817size: OperandSize,818) -> Result<()> {819context.binop(self, size, |this, dividend, divisor, size| {820this.with_aligned_sp(|this| {821this.with_scratch::<IntScratch, _>(|masm, scratch| {822masm.asm.rem_rrr(823divisor,824dividend,825writable!(dividend),826scratch.writable(),827kind,828size,829);830});831Ok(())832})?;833match size {834OperandSize::S32 => Ok(TypedReg::new(WasmValType::I32, dividend)),835OperandSize::S64 => Ok(TypedReg::new(WasmValType::I64, dividend)),836_ => Err(anyhow!(CodeGenError::unexpected_operand_size())),837}838})839}840841fn zero(&mut self, reg: WritableReg) -> Result<()> {842self.asm.mov_ir(reg, I::i64(0), OperandSize::S64);843Ok(())844}845846fn popcnt(&mut self, context: &mut CodeGenContext<Emission>, size: OperandSize) -> Result<()> {847let src = context.pop_to_reg(self, None)?;848self.with_scratch::<FloatScratch, _>(|masm, tmp| {849masm.asm.mov_to_fpu(src.into(), tmp.writable(), size);850masm.asm.cnt(tmp.writable());851masm.asm852.addv(tmp.inner(), tmp.writable(), VectorSize::Size8x8);853masm.asm854.mov_from_vec(tmp.inner(), writable!(src.into()), 0, OperandSize::S8);855});856context.stack.push(src.into());857Ok(())858}859860fn signed_truncate(861&mut self,862dst: WritableReg,863src: Reg,864src_size: OperandSize,865dst_size: OperandSize,866kind: TruncKind,867) -> Result<()> {868self.with_aligned_sp(|masm| {869masm.with_scratch::<FloatScratch, _>(|masm, scratch| {870masm.asm871.fpu_to_int(dst, src, scratch.writable(), src_size, dst_size, kind, true);872});873Ok(())874})875}876877fn unsigned_truncate(878&mut self,879ctx: &mut CodeGenContext<Emission>,880src_size: OperandSize,881dst_size: OperandSize,882kind: TruncKind,883) -> Result<()> {884let dst_ty = match dst_size {885OperandSize::S32 => WasmValType::I32,886OperandSize::S64 => WasmValType::I64,887_ => bail!(CodeGenError::unexpected_operand_size()),888};889890ctx.convert_op(self, dst_ty, |masm, dst, src, dst_size| {891masm.with_aligned_sp(|masm| {892masm.with_scratch::<FloatScratch, _>(|masm, scratch| {893masm.asm.fpu_to_int(894writable!(dst),895src,896scratch.writable(),897src_size,898dst_size,899kind,900false,901);902Ok(())903})904})905})906}907908fn signed_convert(909&mut self,910dst: WritableReg,911src: Reg,912src_size: OperandSize,913dst_size: OperandSize,914) -> Result<()> {915self.asm.cvt_sint_to_float(src, dst, src_size, dst_size);916Ok(())917}918919fn unsigned_convert(920&mut self,921dst: WritableReg,922src: Reg,923_tmp_gpr: Reg,924src_size: OperandSize,925dst_size: OperandSize,926) -> Result<()> {927self.asm.cvt_uint_to_float(src, dst, src_size, dst_size);928Ok(())929}930931fn reinterpret_float_as_int(932&mut self,933dst: WritableReg,934src: Reg,935size: OperandSize,936) -> Result<()> {937self.asm.mov_from_vec(src, dst, 0, size);938Ok(())939}940941fn reinterpret_int_as_float(942&mut self,943dst: WritableReg,944src: Reg,945size: OperandSize,946) -> Result<()> {947self.asm.mov_to_fpu(src, dst, size);948Ok(())949}950951fn demote(&mut self, dst: WritableReg, src: Reg) -> Result<()> {952self.asm953.cvt_float_to_float(src, dst, OperandSize::S64, OperandSize::S32);954Ok(())955}956957fn promote(&mut self, dst: WritableReg, src: Reg) -> Result<()> {958self.asm959.cvt_float_to_float(src, dst, OperandSize::S32, OperandSize::S64);960Ok(())961}962963fn push(&mut self, reg: Reg, size: OperandSize) -> Result<StackSlot> {964self.reserve_stack(size.bytes())?;965let address = self.address_from_sp(SPOffset::from_u32(self.sp_offset))?;966self.asm.str(reg, address, size, TRUSTED_FLAGS);967968Ok(StackSlot {969offset: SPOffset::from_u32(self.sp_offset),970size: size.bytes(),971})972}973974fn address_at_reg(&self, reg: Reg, offset: u32) -> Result<Self::Address> {975Ok(Address::offset(reg, offset as i64))976}977978fn cmp_with_set(979&mut self,980dst: WritableReg,981src: RegImm,982kind: IntCmpKind,983size: OperandSize,984) -> Result<()> {985self.cmp(dst.to_reg(), src, size)?;986self.asm.cset(dst, kind.into());987Ok(())988}989990fn cmp(&mut self, src1: Reg, src2: RegImm, size: OperandSize) -> Result<()> {991match src2 {992RegImm::Reg(src2) => {993self.asm.subs_rrr(src2, src1, size);994Ok(())995}996RegImm::Imm(v) => {997let val = v.unwrap_as_u64();998match Imm12::maybe_from_u64(val) {999Some(imm12) => self.asm.subs_ir(imm12, src1, size),1000None => {1001self.with_scratch::<IntScratch, _>(|masm, scratch| {1002masm.asm.mov_ir(scratch.writable(), v, v.size());1003masm.asm.subs_rrr(scratch.inner(), src1, size);1004});1005}1006};1007Ok(())1008}1009}1010}10111012fn float_cmp_with_set(1013&mut self,1014dst: WritableReg,1015src1: Reg,1016src2: Reg,1017kind: FloatCmpKind,1018size: OperandSize,1019) -> Result<()> {1020self.asm.fcmp(src1, src2, size);1021self.asm.cset(dst, kind.into());1022Ok(())1023}10241025fn clz(&mut self, dst: WritableReg, src: Reg, size: OperandSize) -> Result<()> {1026self.asm.clz(src, dst, size);1027Ok(())1028}10291030fn ctz(&mut self, dst: WritableReg, src: Reg, size: OperandSize) -> Result<()> {1031self.with_scratch::<IntScratch, _>(|masm, scratch| {1032masm.asm.rbit(src, scratch.writable(), size);1033masm.asm.clz(scratch.inner(), dst, size);1034Ok(())1035})1036}10371038fn wrap(&mut self, dst: WritableReg, src: Reg) -> Result<()> {1039self.asm.mov_rr(src, dst, OperandSize::S32);1040Ok(())1041}10421043fn extend(&mut self, dst: WritableReg, src: Reg, kind: ExtendKind) -> Result<()> {1044self.asm.extend(src, dst, kind);1045Ok(())1046}10471048fn get_label(&mut self) -> Result<MachLabel> {1049Ok(self.asm.get_label())1050}10511052fn bind(&mut self, label: MachLabel) -> Result<()> {1053let buffer = self.asm.buffer_mut();1054buffer.bind_label(label, &mut Default::default());1055Ok(())1056}10571058fn branch(1059&mut self,1060kind: IntCmpKind,1061lhs: Reg,1062rhs: RegImm,1063taken: MachLabel,1064size: OperandSize,1065) -> Result<()> {1066use IntCmpKind::*;10671068match &(lhs, rhs) {1069(rlhs, RegImm::Reg(rrhs)) => {1070// If the comparison kind is zero or not zero and both operands1071// are the same register, emit an ands instruction. Else we emit1072// a normal comparison.1073if (kind == Eq || kind == Ne) && (rlhs == rrhs) {1074self.asm.ands_rr(*rlhs, *rrhs, size);1075} else {1076self.cmp(lhs, rhs, size)?;1077}1078}1079_ => self.cmp(lhs, rhs, size)?,1080}1081self.asm.jmp_if(kind.into(), taken);1082Ok(())1083}10841085fn jmp(&mut self, target: MachLabel) -> Result<()> {1086self.asm.jmp(target);1087Ok(())1088}10891090fn unreachable(&mut self) -> Result<()> {1091self.with_aligned_sp(|masm| {1092masm.asm.udf(wasmtime_cranelift::TRAP_UNREACHABLE);1093Ok(())1094})1095}10961097fn jmp_table(&mut self, targets: &[MachLabel], index: Reg, tmp: Reg) -> Result<()> {1098// At least one default target.1099debug_assert!(targets.len() >= 1);1100let default_index = targets.len() - 1;1101let max = default_index;1102self.asm.mov_ir(1103writable!(tmp),1104I::i32(i32::try_from(max).unwrap()),1105OperandSize::S32,1106);1107// NB: We only emit the comparison instruction, since1108// `Assembler::jmp_table` (and the underlying Cranelift1109// instruction) will emit spectre mitigation and bounds1110// checks.1111self.asm.subs_rrr(tmp, index, OperandSize::S32);1112let default = targets[default_index];1113let rest = &targets[0..default_index];1114self.with_scratch::<IntScratch, _>(|masm, scratch| {1115masm.asm1116.jmp_table(rest, default, index, scratch.inner(), tmp);1117Ok(())1118})1119}11201121fn trap(&mut self, code: TrapCode) -> Result<()> {1122self.with_aligned_sp(|masm| {1123masm.asm.udf(code);1124Ok(())1125})1126}11271128fn trapz(&mut self, src: Reg, code: TrapCode) -> Result<()> {1129self.with_aligned_sp(|masm| {1130masm.asm.trapz(src, code, OperandSize::S64);1131Ok(())1132})1133}11341135fn trapif(&mut self, cc: IntCmpKind, code: TrapCode) -> Result<()> {1136self.with_aligned_sp(|masm| {1137masm.asm.trapif(cc.into(), code);1138Ok(())1139})1140}11411142fn start_source_loc(&mut self, loc: RelSourceLoc) -> Result<(CodeOffset, RelSourceLoc)> {1143Ok(self.asm.buffer_mut().start_srcloc(loc))1144}11451146fn end_source_loc(&mut self) -> Result<()> {1147self.asm.buffer_mut().end_srcloc();1148Ok(())1149}11501151fn current_code_offset(&self) -> Result<CodeOffset> {1152Ok(self.asm.buffer().cur_offset())1153}11541155fn add128(1156&mut self,1157dst_lo: WritableReg,1158dst_hi: WritableReg,1159lhs_lo: Reg,1160lhs_hi: Reg,1161rhs_lo: Reg,1162rhs_hi: Reg,1163) -> Result<()> {1164let _ = (dst_lo, dst_hi, lhs_lo, lhs_hi, rhs_lo, rhs_hi);1165Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))1166}11671168fn sub128(1169&mut self,1170dst_lo: WritableReg,1171dst_hi: WritableReg,1172lhs_lo: Reg,1173lhs_hi: Reg,1174rhs_lo: Reg,1175rhs_hi: Reg,1176) -> Result<()> {1177let _ = (dst_lo, dst_hi, lhs_lo, lhs_hi, rhs_lo, rhs_hi);1178Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))1179}11801181fn mul_wide(1182&mut self,1183context: &mut CodeGenContext<Emission>,1184kind: MulWideKind,1185) -> Result<()> {1186let _ = (context, kind);1187Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))1188}11891190fn splat(&mut self, _context: &mut CodeGenContext<Emission>, _size: SplatKind) -> Result<()> {1191bail!(CodeGenError::unimplemented_masm_instruction())1192}11931194fn shuffle(&mut self, _dst: WritableReg, _lhs: Reg, _rhs: Reg, _lanes: [u8; 16]) -> Result<()> {1195bail!(CodeGenError::unimplemented_masm_instruction())1196}11971198fn swizzle(&mut self, _dst: WritableReg, _lhs: Reg, _rhs: Reg) -> Result<()> {1199bail!(CodeGenError::unimplemented_masm_instruction())1200}12011202fn atomic_rmw(1203&mut self,1204_context: &mut CodeGenContext<Emission>,1205_addr: Self::Address,1206_size: OperandSize,1207_op: RmwOp,1208_flags: MemFlags,1209_extend: Option<Extend<Zero>>,1210) -> Result<()> {1211Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))1212}12131214fn extract_lane(1215&mut self,1216_src: Reg,1217_dst: WritableReg,1218_lane: u8,1219_kind: ExtractLaneKind,1220) -> Result<()> {1221bail!(CodeGenError::unimplemented_masm_instruction())1222}12231224fn replace_lane(1225&mut self,1226_src: RegImm,1227_dst: WritableReg,1228_lane: u8,1229_kind: ReplaceLaneKind,1230) -> Result<()> {1231bail!(CodeGenError::unimplemented_masm_instruction())1232}12331234fn atomic_cas(1235&mut self,1236_context: &mut CodeGenContext<Emission>,1237_addr: Self::Address,1238_size: OperandSize,1239_flags: MemFlags,1240_extend: Option<Extend<Zero>>,1241) -> Result<()> {1242Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))1243}12441245fn v128_eq(1246&mut self,1247_dst: WritableReg,1248_lhs: Reg,1249_rhs: Reg,1250_kind: VectorEqualityKind,1251) -> Result<()> {1252bail!(CodeGenError::unimplemented_masm_instruction())1253}12541255fn v128_ne(1256&mut self,1257_dst: WritableReg,1258_lhs: Reg,1259_rhs: Reg,1260_kind: VectorEqualityKind,1261) -> Result<()> {1262bail!(CodeGenError::unimplemented_masm_instruction())1263}12641265fn v128_lt(1266&mut self,1267_dst: WritableReg,1268_lhs: Reg,1269_rhs: Reg,1270_kind: VectorCompareKind,1271) -> Result<()> {1272bail!(CodeGenError::unimplemented_masm_instruction())1273}12741275fn v128_le(1276&mut self,1277_dst: WritableReg,1278_lhs: Reg,1279_rhs: Reg,1280_kind: VectorCompareKind,1281) -> Result<()> {1282bail!(CodeGenError::unimplemented_masm_instruction())1283}12841285fn v128_gt(1286&mut self,1287_dst: WritableReg,1288_lhs: Reg,1289_rhs: Reg,1290_kind: VectorCompareKind,1291) -> Result<()> {1292bail!(CodeGenError::unimplemented_masm_instruction())1293}12941295fn v128_ge(1296&mut self,1297_dst: WritableReg,1298_lhs: Reg,1299_rhs: Reg,1300_kind: VectorCompareKind,1301) -> Result<()> {1302bail!(CodeGenError::unimplemented_masm_instruction())1303}13041305fn v128_not(&mut self, _dst: WritableReg) -> Result<()> {1306Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))1307}13081309fn fence(&mut self) -> Result<()> {1310Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))1311}13121313fn v128_and(&mut self, _src1: Reg, _src2: Reg, _dst: WritableReg) -> Result<()> {1314Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))1315}13161317fn v128_and_not(&mut self, _src1: Reg, _src2: Reg, _dst: WritableReg) -> Result<()> {1318Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))1319}13201321fn v128_or(&mut self, _src1: Reg, _src2: Reg, _dst: WritableReg) -> Result<()> {1322Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))1323}13241325fn v128_xor(&mut self, _src1: Reg, _src2: Reg, _dst: WritableReg) -> Result<()> {1326Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))1327}13281329fn v128_bitselect(1330&mut self,1331_src1: Reg,1332_src2: Reg,1333_mask: Reg,1334_dst: WritableReg,1335) -> Result<()> {1336Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))1337}13381339fn v128_any_true(&mut self, _src: Reg, _dst: WritableReg) -> Result<()> {1340Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))1341}13421343fn v128_convert(&mut self, _src: Reg, _dst: WritableReg, _kind: V128ConvertKind) -> Result<()> {1344bail!(CodeGenError::unimplemented_masm_instruction())1345}13461347fn v128_narrow(1348&mut self,1349_src1: Reg,1350_src2: Reg,1351_dst: WritableReg,1352_kind: V128NarrowKind,1353) -> Result<()> {1354bail!(CodeGenError::unimplemented_masm_instruction())1355}13561357fn v128_demote(&mut self, _src: Reg, _dst: WritableReg) -> Result<()> {1358bail!(CodeGenError::unimplemented_masm_instruction())1359}13601361fn v128_promote(&mut self, _src: Reg, _dst: WritableReg) -> Result<()> {1362bail!(CodeGenError::unimplemented_masm_instruction())1363}13641365fn v128_extend(&mut self, _src: Reg, _dst: WritableReg, _kind: V128ExtendKind) -> Result<()> {1366bail!(CodeGenError::unimplemented_masm_instruction())1367}13681369fn v128_add(1370&mut self,1371_lhs: Reg,1372_rhs: Reg,1373_dst: WritableReg,1374_kind: V128AddKind,1375) -> Result<()> {1376Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))1377}13781379fn v128_sub(1380&mut self,1381_lhs: Reg,1382_rhs: Reg,1383_dst: WritableReg,1384_kind: V128SubKind,1385) -> Result<()> {1386Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))1387}13881389fn v128_mul(1390&mut self,1391_context: &mut CodeGenContext<Emission>,1392_kind: V128MulKind,1393) -> Result<()> {1394Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))1395}13961397fn v128_abs(&mut self, _src: Reg, _dst: WritableReg, _kind: V128AbsKind) -> Result<()> {1398bail!(CodeGenError::unimplemented_masm_instruction())1399}14001401fn v128_neg(&mut self, _op: WritableReg, _kind: V128NegKind) -> Result<()> {1402Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))1403}14041405fn v128_shift(1406&mut self,1407_context: &mut CodeGenContext<Emission>,1408_lane_width: OperandSize,1409_shift_kind: ShiftKind,1410) -> Result<()> {1411Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))1412}14131414fn v128_q15mulr_sat_s(1415&mut self,1416_lhs: Reg,1417_rhs: Reg,1418_dst: WritableReg,1419_size: OperandSize,1420) -> Result<()> {1421bail!(CodeGenError::unimplemented_masm_instruction())1422}14231424fn v128_all_true(&mut self, _src: Reg, _dst: WritableReg, _size: OperandSize) -> Result<()> {1425bail!(CodeGenError::unimplemented_masm_instruction())1426}14271428fn v128_bitmask(&mut self, _src: Reg, _dst: WritableReg, _size: OperandSize) -> Result<()> {1429bail!(CodeGenError::unimplemented_masm_instruction())1430}14311432fn v128_trunc(1433&mut self,1434_context: &mut CodeGenContext<Emission>,1435_kind: V128TruncKind,1436) -> Result<()> {1437bail!(CodeGenError::unimplemented_masm_instruction())1438}14391440fn v128_min(1441&mut self,1442_src1: Reg,1443_src2: Reg,1444_dst: WritableReg,1445_kind: V128MinKind,1446) -> Result<()> {1447Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))1448}14491450fn v128_max(1451&mut self,1452_src1: Reg,1453_src2: Reg,1454_dst: WritableReg,1455_kind: V128MaxKind,1456) -> Result<()> {1457Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))1458}14591460fn v128_extmul(1461&mut self,1462_context: &mut CodeGenContext<Emission>,1463_kind: V128ExtMulKind,1464) -> Result<()> {1465Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))1466}14671468fn v128_extadd_pairwise(1469&mut self,1470_src: Reg,1471_dst: WritableReg,1472_kind: V128ExtAddKind,1473) -> Result<()> {1474Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))1475}14761477fn v128_dot(&mut self, _lhs: Reg, _rhs: Reg, _dst: WritableReg) -> Result<()> {1478bail!(CodeGenError::unimplemented_masm_instruction())1479}14801481fn v128_popcnt(&mut self, _context: &mut CodeGenContext<Emission>) -> Result<()> {1482bail!(CodeGenError::unimplemented_masm_instruction())1483}14841485fn v128_avgr(1486&mut self,1487_lhs: Reg,1488_rhs: Reg,1489_dst: WritableReg,1490_size: OperandSize,1491) -> Result<()> {1492bail!(CodeGenError::unimplemented_masm_instruction())1493}14941495fn v128_div(1496&mut self,1497_lhs: Reg,1498_rhs: Reg,1499_dst: WritableReg,1500_size: OperandSize,1501) -> Result<()> {1502bail!(CodeGenError::unimplemented_masm_instruction())1503}15041505fn v128_sqrt(&mut self, _src: Reg, _dst: WritableReg, _size: OperandSize) -> Result<()> {1506bail!(CodeGenError::unimplemented_masm_instruction())1507}15081509fn v128_ceil(&mut self, _src: Reg, _dst: WritableReg, _size: OperandSize) -> Result<()> {1510bail!(CodeGenError::unimplemented_masm_instruction())1511}15121513fn v128_floor(&mut self, _src: Reg, _dst: WritableReg, _size: OperandSize) -> Result<()> {1514bail!(CodeGenError::unimplemented_masm_instruction())1515}15161517fn v128_nearest(&mut self, _src: Reg, _dst: WritableReg, _size: OperandSize) -> Result<()> {1518bail!(CodeGenError::unimplemented_masm_instruction())1519}15201521fn v128_pmin(1522&mut self,1523_lhs: Reg,1524_rhs: Reg,1525_dst: WritableReg,1526_size: OperandSize,1527) -> Result<()> {1528bail!(CodeGenError::unimplemented_masm_instruction())1529}15301531fn v128_pmax(1532&mut self,1533_lhs: Reg,1534_rhs: Reg,1535_dst: WritableReg,1536_size: OperandSize,1537) -> Result<()> {1538bail!(CodeGenError::unimplemented_masm_instruction())1539}1540}15411542impl MacroAssembler {1543fn increment_sp(&mut self, bytes: u32) {1544self.sp_offset += bytes;15451546// NOTE: we use `max` here to track the largest stack allocation in `sp_max`. Once we have1547// seen the entire function, this value will represent the maximum size for the stack1548// frame.1549self.sp_max = self.sp_max.max(self.sp_offset);1550}15511552fn decrement_sp(&mut self, bytes: u32) {1553self.sp_offset -= bytes;1554}15551556// Copies the value of the stack pointer to the shadow stack1557// pointer: mov x28, sp15581559// This function is called at the epilogue.1560fn move_sp_to_shadow_sp(&mut self) {1561let sp = regs::sp();1562let shadow_sp = regs::shadow_sp();1563self.asm.mov_rr(sp, writable!(shadow_sp), OperandSize::S64);1564}15651566/// Heloper to add an immediate to a register.1567fn add_ir(&mut self, dst: WritableReg, lhs: Reg, rhs: I, size: OperandSize) -> Result<()> {1568let imm = rhs.unwrap_as_u64();1569match Imm12::maybe_from_u64(imm) {1570Some(imm12) => self.asm.add_ir(imm12, lhs, dst, size),1571None => {1572self.with_scratch::<IntScratch, _>(|masm, scratch| {1573masm.asm.mov_ir(scratch.writable(), rhs, rhs.size());1574masm.asm.add_rrr(scratch.inner(), lhs, dst, size);1575});1576}1577};1578Ok(())1579}15801581// Copies the value of the shadow stack pointer to the stack pointer: mov1582// sp, x28.1583//1584// This function is usually called when the space is claimed, e.g., via1585// a push, when stack space is reserved explicitly or after emitting code1586// that requires explicit stack pointer alignment (code that could result in1587// signal handling).1588//1589// This ensures the stack pointer always reflects the allocated stack space,1590// otherwise any space below the stack pointer could get clobbered with1591// interrupts and signal handlers.1592//1593// This function must also be called at the function epilogue, since the1594// stack pointer is used to restore the current function frame.1595fn move_shadow_sp_to_sp(&mut self) {1596let shadow_sp = regs::shadow_sp();1597let sp = writable!(regs::sp());1598let imm = Imm12::maybe_from_u64(0).unwrap();1599self.asm.add_ir(imm, shadow_sp, sp, OperandSize::S64);1600}1601}160216031604