Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/winch/codegen/src/isa/aarch64/masm.rs
1692 views
1
use super::{
2
ABI, RegAlloc,
3
abi::Aarch64ABI,
4
address::Address,
5
asm::{Assembler, PatchableAddToReg},
6
regs::{self, scratch_fpr_bitset, scratch_gpr_bitset},
7
};
8
use crate::{
9
abi::{self, align_to, calculate_frame_adjustment, local::LocalSlot, vmctx},
10
codegen::{CodeGenContext, CodeGenError, Emission, FuncEnv, ptr_type_from_ptr_size},
11
isa::{
12
CallingConvention,
13
aarch64::abi::SHADOW_STACK_POINTER_SLOT_SIZE,
14
reg::{Reg, WritableReg, writable},
15
},
16
masm::{
17
CalleeKind, DivKind, Extend, ExtendKind, ExtractLaneKind, FloatCmpKind, FloatScratch,
18
Imm as I, IntCmpKind, IntScratch, LoadKind, MacroAssembler as Masm, MulWideKind,
19
OperandSize, RegImm, RemKind, ReplaceLaneKind, RmwOp, RoundingMode, SPOffset, Scratch,
20
ScratchType, ShiftKind, SplatKind, StackSlot, StoreKind, TRUSTED_FLAGS, TrapCode,
21
TruncKind, UNTRUSTED_FLAGS, V128AbsKind, V128AddKind, V128ConvertKind, V128ExtAddKind,
22
V128ExtMulKind, V128ExtendKind, V128MaxKind, V128MinKind, V128MulKind, V128NarrowKind,
23
V128NegKind, V128SubKind, V128TruncKind, VectorCompareKind, VectorEqualityKind, Zero,
24
},
25
stack::TypedReg,
26
};
27
use anyhow::{Result, anyhow, bail};
28
use cranelift_codegen::{
29
Final, MachBufferFinalized, MachLabel,
30
binemit::CodeOffset,
31
ir::{MemFlags, RelSourceLoc, SourceLoc},
32
isa::aarch64::inst::{self, Cond, Imm12, ImmLogic, ImmShift, VectorSize},
33
settings,
34
};
35
use regalloc2::RegClass;
36
use wasmtime_environ::{PtrSize, WasmValType};
37
38
/// Aarch64 MacroAssembler.
39
pub(crate) struct MacroAssembler {
40
/// This value represents the maximum stack size seen while compiling the
41
/// function. While the function is still being compiled its value will not
42
/// be valid (the stack will grow and shrink as space is reserved and freed
43
/// during compilation), but once all instructions have been seen this value
44
/// will be the maximum stack usage seen.
45
sp_max: u32,
46
47
/// Add-with-immediate patchable instruction sequence used to add the
48
/// constant stack max to a register.
49
stack_max_use_add: Option<PatchableAddToReg>,
50
51
/// Low level assembler.
52
asm: Assembler,
53
/// Stack pointer offset.
54
sp_offset: u32,
55
/// The target pointer size.
56
ptr_size: OperandSize,
57
/// Scratch register scope.
58
scratch_scope: RegAlloc,
59
}
60
61
impl MacroAssembler {
62
/// Create an Aarch64 MacroAssembler.
63
pub fn new(ptr_size: impl PtrSize, shared_flags: settings::Flags) -> Result<Self> {
64
Ok(Self {
65
sp_max: 0,
66
stack_max_use_add: None,
67
asm: Assembler::new(shared_flags),
68
sp_offset: 0u32,
69
ptr_size: ptr_type_from_ptr_size(ptr_size.size()).try_into()?,
70
scratch_scope: RegAlloc::from(scratch_gpr_bitset(), scratch_fpr_bitset()),
71
})
72
}
73
74
/// Add the maximum stack used to a register, recording an obligation to update the
75
/// add-with-immediate instruction emitted to use the real stack max when the masm is being
76
/// finalized.
77
fn add_stack_max(&mut self, reg: WritableReg, tmp: WritableReg) {
78
assert!(self.stack_max_use_add.is_none());
79
let patch = PatchableAddToReg::new(reg, tmp, self.asm.buffer_mut());
80
self.stack_max_use_add.replace(patch);
81
}
82
83
/// Ensures that the stack pointer remains 16-byte aligned for the duration
84
/// of the provided function. This alignment is necessary for AArch64
85
/// compliance, particularly for signal handlers that may be invoked
86
/// during execution. While the compiler doesn't directly use the stack
87
/// pointer for memory addressing, maintaining this alignment is crucial
88
/// to prevent issues when handling signals.
89
pub fn with_aligned_sp<F, T>(&mut self, f: F) -> Result<T>
90
where
91
F: FnOnce(&mut Self) -> Result<T>,
92
{
93
let mut aligned = false;
94
let alignment: u32 = <Aarch64ABI as ABI>::call_stack_align().into();
95
let addend: u32 = <Aarch64ABI as ABI>::initial_frame_size().into();
96
let delta = calculate_frame_adjustment(self.sp_offset()?.as_u32(), addend, alignment);
97
if delta != 0 {
98
self.sub(
99
writable!(regs::sp()),
100
// Since we don't need to synchronize the shadow stack pointer
101
// when freeing stack space [^1], the stack pointer may become
102
// out of sync with the primary shadow stack pointer. Therefore,
103
// we use the shadow stack pointer as the reference for
104
// calculating any alignment delta (self.sp_offset).
105
//
106
// [1]: This approach avoids an unnecessary move instruction and
107
// maintains the invariant of not accessing memory below the
108
// current stack pointer, preventing issues with signal handlers
109
// and interrupts.
110
regs::shadow_sp(),
111
RegImm::i32(delta as i32),
112
OperandSize::S64,
113
)?;
114
115
aligned = true;
116
}
117
118
let res = f(self)?;
119
120
if aligned {
121
self.move_shadow_sp_to_sp();
122
}
123
124
Ok(res)
125
}
126
}
127
128
impl Masm for MacroAssembler {
129
type Address = Address;
130
type Ptr = u8;
131
type ABI = Aarch64ABI;
132
133
fn frame_setup(&mut self) -> Result<()> {
134
let lr = regs::lr();
135
let fp = regs::fp();
136
let sp = regs::sp();
137
138
let addr = Address::pre_indexed_from_sp(-16);
139
self.asm.stp(fp, lr, addr);
140
self.asm.mov_rr(sp, writable!(fp), OperandSize::S64);
141
142
let addr = Address::pre_indexed_from_sp(-(SHADOW_STACK_POINTER_SLOT_SIZE as i64));
143
self.asm
144
.str(regs::shadow_sp(), addr, OperandSize::S64, TRUSTED_FLAGS);
145
146
self.move_sp_to_shadow_sp();
147
Ok(())
148
}
149
150
fn check_stack(&mut self, vmctx: Reg) -> Result<()> {
151
let ptr_size_u8: u8 = self.ptr_size.bytes().try_into().unwrap();
152
153
// The PatchableAddToReg construct on aarch64 is not a single
154
// add-immediate instruction, but a 3-instruction sequence that loads an
155
// immediate using 2 mov-immediate instructions into _another_ scratch
156
// register before adding it into the target scratch register.
157
//
158
// In other words, to make this work we use _two_ scratch registers, one
159
// to hold the limit we're calculating and one helper that's just used
160
// to load the immediate.
161
//
162
// Luckily on aarch64 we have 2 available scratch registers, ip0 and
163
// ip1.
164
// NB that this in this case, we manually allocate the scratch registers
165
// as precision when it comes to its usage is
166
167
let ptr_size = self.ptr_size;
168
self.with_aligned_sp(|masm| {
169
masm.with_scratch::<IntScratch, _>(|masm, scratch_stk_limit| {
170
masm.with_scratch::<IntScratch, _>(|masm, scratch_tmp| {
171
masm.load_ptr(
172
masm.address_at_reg(vmctx, ptr_size_u8.vmcontext_store_context().into())?,
173
scratch_stk_limit.writable(),
174
)?;
175
176
masm.load_ptr(
177
Address::offset(
178
scratch_stk_limit.inner(),
179
ptr_size_u8.vmstore_context_stack_limit().into(),
180
),
181
scratch_stk_limit.writable(),
182
)?;
183
184
masm.add_stack_max(scratch_stk_limit.writable(), scratch_tmp.writable());
185
186
// Aarch can only do a cmp with sp in the first operand, which means we
187
// use a less-than comparison, not a greater-than (stack grows down).
188
masm.cmp(regs::sp(), scratch_stk_limit.inner().into(), ptr_size)?;
189
masm.asm
190
.trapif(IntCmpKind::LtU.into(), TrapCode::STACK_OVERFLOW);
191
192
Ok(())
193
})
194
})
195
})
196
}
197
198
fn frame_restore(&mut self) -> Result<()> {
199
debug_assert_eq!(self.sp_offset, 0);
200
201
// Sync the real stack pointer with the value of the shadow stack
202
// pointer.
203
self.move_shadow_sp_to_sp();
204
205
// Pop the shadow stack pointer. It's assumed that at this point
206
// `sp_offset` is 0 and therefore the real stack pointer should be
207
// 16-byte aligned.
208
let addr = Address::post_indexed_from_sp(SHADOW_STACK_POINTER_SLOT_SIZE as i64);
209
self.asm.uload(
210
addr,
211
writable!(regs::shadow_sp()),
212
OperandSize::S64,
213
TRUSTED_FLAGS,
214
);
215
216
// Restore the link register and frame pointer.
217
let lr = regs::lr();
218
let fp = regs::fp();
219
let addr = Address::post_indexed_from_sp(16);
220
221
self.asm.ldp(fp, lr, addr);
222
self.asm.ret();
223
Ok(())
224
}
225
226
fn reserve_stack(&mut self, bytes: u32) -> Result<()> {
227
if bytes == 0 {
228
return Ok(());
229
}
230
231
let ssp = regs::shadow_sp();
232
233
match Imm12::maybe_from_u64(bytes as u64) {
234
Some(v) => self.asm.sub_ir(v, ssp, writable!(ssp), OperandSize::S64),
235
None => {
236
self.with_scratch::<IntScratch, _>(|masm, scratch| {
237
masm.asm
238
.mov_ir(scratch.writable(), I::I64(bytes as u64), OperandSize::S64);
239
masm.asm
240
.sub_rrr(scratch.inner(), ssp, writable!(ssp), OperandSize::S64);
241
});
242
}
243
}
244
245
// Even though we're using the shadow stack pointer to reserve stack, we
246
// must ensure that the real stack pointer reflects the stack claimed so
247
// far; we can't use stack memory below the real stack pointer as it
248
// could be clobbered by interrupts or signal handlers.
249
self.move_shadow_sp_to_sp();
250
251
self.increment_sp(bytes);
252
Ok(())
253
}
254
255
fn free_stack(&mut self, bytes: u32) -> Result<()> {
256
if bytes == 0 {
257
return Ok(());
258
}
259
260
let ssp = regs::shadow_sp();
261
match Imm12::maybe_from_u64(bytes as u64) {
262
Some(v) => self.asm.add_ir(v, ssp, writable!(ssp), OperandSize::S64),
263
None => {
264
self.with_scratch::<IntScratch, _>(|masm, scratch| {
265
masm.asm
266
.mov_ir(scratch.writable(), I::I64(bytes as u64), OperandSize::S64);
267
masm.asm
268
.add_rrr(ssp, scratch.inner(), writable!(ssp), OperandSize::S64);
269
});
270
}
271
}
272
273
// We must ensure that the real stack pointer reflects the offset
274
// tracked by `self.sp_offset`, we use such value to calculate
275
// alignment, which is crucial for calls.
276
//
277
// As an optimization: this synchronization doesn't need to happen all
278
// the time, in theory we could ensure to sync the shadow stack pointer
279
// with the stack pointer when alignment is required, like at callsites.
280
// This is the simplest approach at the time of writing, which
281
// integrates well with the rest of the aarch64 infrastructure.
282
self.move_shadow_sp_to_sp();
283
284
self.decrement_sp(bytes);
285
Ok(())
286
}
287
288
fn reset_stack_pointer(&mut self, offset: SPOffset) -> Result<()> {
289
self.sp_offset = offset.as_u32();
290
Ok(())
291
}
292
293
fn local_address(&mut self, local: &LocalSlot) -> Result<Address> {
294
let (reg, offset) = local
295
.addressed_from_sp()
296
.then(|| {
297
let offset = self.sp_offset.checked_sub(local.offset).expect(&format!(
298
"Invalid local offset = {}; sp offset = {}",
299
local.offset, self.sp_offset
300
));
301
(regs::shadow_sp(), offset)
302
})
303
.unwrap_or((regs::fp(), local.offset));
304
305
Ok(Address::offset(reg, offset as i64))
306
}
307
308
fn address_from_sp(&self, offset: SPOffset) -> Result<Self::Address> {
309
Ok(Address::from_shadow_sp(
310
(self.sp_offset - offset.as_u32()) as i64,
311
))
312
}
313
314
fn address_at_sp(&self, offset: SPOffset) -> Result<Self::Address> {
315
Ok(Address::from_shadow_sp(offset.as_u32() as i64))
316
}
317
318
fn address_at_vmctx(&self, offset: u32) -> Result<Self::Address> {
319
Ok(Address::offset(vmctx!(Self), offset as i64))
320
}
321
322
fn store_ptr(&mut self, src: Reg, dst: Self::Address) -> Result<()> {
323
self.store(src.into(), dst, self.ptr_size)
324
}
325
326
fn store(&mut self, src: RegImm, dst: Address, size: OperandSize) -> Result<()> {
327
match src {
328
RegImm::Imm(v) => {
329
match v {
330
I::I32(_) | I::I64(_) => {
331
self.with_scratch::<IntScratch, _>(|masm, scratch| {
332
masm.asm.mov_ir(scratch.writable(), v, v.size());
333
masm.asm.str(scratch.inner(), dst, size, TRUSTED_FLAGS);
334
});
335
}
336
imm @ (I::F32(_) | I::F64(_)) => {
337
self.with_scratch::<FloatScratch, _>(|masm, scratch| {
338
masm.asm.mov_ir(scratch.writable(), imm, imm.size());
339
masm.asm.str(scratch.inner(), dst, size, TRUSTED_FLAGS);
340
});
341
}
342
_ => bail!(CodeGenError::unsupported_wasm_type()),
343
};
344
Ok(())
345
}
346
RegImm::Reg(r) => {
347
self.asm.str(r, dst, size, TRUSTED_FLAGS);
348
Ok(())
349
}
350
}
351
}
352
353
fn wasm_store(&mut self, src: Reg, dst: Self::Address, op_kind: StoreKind) -> Result<()> {
354
self.with_aligned_sp(|masm| match op_kind {
355
StoreKind::Operand(size) => {
356
masm.asm.str(src, dst, size, UNTRUSTED_FLAGS);
357
Ok(())
358
}
359
StoreKind::Atomic(_size) => {
360
Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))
361
}
362
StoreKind::VectorLane(_selector) => {
363
Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))
364
}
365
})
366
}
367
368
fn with_scratch<T: ScratchType, R>(&mut self, f: impl FnOnce(&mut Self, Scratch) -> R) -> R {
369
let r = self
370
.scratch_scope
371
.reg_for_class(T::reg_class(), &mut |_| Ok(()))
372
.expect("Scratch register to be available");
373
374
let ret = f(self, Scratch::new(r));
375
376
self.scratch_scope.free(r);
377
ret
378
}
379
380
fn call(
381
&mut self,
382
stack_args_size: u32,
383
mut load_callee: impl FnMut(&mut Self) -> Result<(CalleeKind, CallingConvention)>,
384
) -> Result<u32> {
385
let alignment: u32 = <Self::ABI as abi::ABI>::call_stack_align().into();
386
let addend: u32 = <Self::ABI as abi::ABI>::initial_frame_size().into();
387
let delta = calculate_frame_adjustment(self.sp_offset()?.as_u32(), addend, alignment);
388
let aligned_args_size = align_to(stack_args_size, alignment);
389
let total_stack = delta + aligned_args_size;
390
self.reserve_stack(total_stack)?;
391
let (callee, call_conv) = load_callee(self)?;
392
match callee {
393
CalleeKind::Indirect(reg) => self.asm.call_with_reg(reg, call_conv),
394
CalleeKind::Direct(idx) => self.asm.call_with_name(idx, call_conv),
395
}
396
397
Ok(total_stack)
398
}
399
400
fn load(&mut self, src: Address, dst: WritableReg, size: OperandSize) -> Result<()> {
401
self.asm.uload(src, dst, size, TRUSTED_FLAGS);
402
Ok(())
403
}
404
405
fn load_ptr(&mut self, src: Self::Address, dst: WritableReg) -> Result<()> {
406
self.load(src, dst, self.ptr_size)
407
}
408
409
fn wasm_load(&mut self, src: Self::Address, dst: WritableReg, kind: LoadKind) -> Result<()> {
410
let size = kind.derive_operand_size();
411
self.with_aligned_sp(|masm| match &kind {
412
LoadKind::Operand(_) => {
413
if size == OperandSize::S128 {
414
bail!(CodeGenError::UnimplementedWasmLoadKind)
415
} else {
416
Ok(masm.asm.uload(src, dst, size, UNTRUSTED_FLAGS))
417
}
418
}
419
LoadKind::Splat(_) => bail!(CodeGenError::UnimplementedWasmLoadKind),
420
LoadKind::ScalarExtend(extend_kind) => {
421
if extend_kind.signed() {
422
masm.asm.sload(src, dst, size, UNTRUSTED_FLAGS);
423
} else {
424
// unlike x64, unused bits are set to zero so we don't need to extend
425
masm.asm.uload(src, dst, size, UNTRUSTED_FLAGS);
426
}
427
428
Ok(())
429
}
430
LoadKind::VectorExtend(_vector_extend_kind) => {
431
bail!(CodeGenError::UnimplementedWasmLoadKind)
432
}
433
LoadKind::VectorLane(_selector) => {
434
bail!(CodeGenError::unimplemented_masm_instruction())
435
}
436
LoadKind::Atomic(_, _) => bail!(CodeGenError::unimplemented_masm_instruction()),
437
LoadKind::VectorZero(_size) => {
438
bail!(CodeGenError::UnimplementedWasmLoadKind)
439
}
440
})
441
}
442
443
fn compute_addr(
444
&mut self,
445
src: Self::Address,
446
dst: WritableReg,
447
size: OperandSize,
448
) -> Result<()> {
449
let (base, offset) = src.unwrap_offset();
450
self.add_ir(dst, base, I::i64(offset), size)
451
}
452
453
fn pop(&mut self, dst: WritableReg, size: OperandSize) -> Result<()> {
454
let addr = self.address_from_sp(SPOffset::from_u32(self.sp_offset))?;
455
self.asm.uload(addr, dst, size, TRUSTED_FLAGS);
456
self.free_stack(size.bytes())
457
}
458
459
fn sp_offset(&self) -> Result<SPOffset> {
460
Ok(SPOffset::from_u32(self.sp_offset))
461
}
462
463
fn finalize(mut self, base: Option<SourceLoc>) -> Result<MachBufferFinalized<Final>> {
464
if let Some(patch) = self.stack_max_use_add {
465
patch.finalize(i32::try_from(self.sp_max).unwrap(), self.asm.buffer_mut());
466
}
467
468
Ok(self.asm.finalize(base))
469
}
470
471
fn mov(&mut self, dst: WritableReg, src: RegImm, size: OperandSize) -> Result<()> {
472
match (src, dst) {
473
(RegImm::Imm(v), _) => match v {
474
I::I32(_) | I::I64(_) => {
475
self.asm.mov_ir(dst, v, v.size());
476
Ok(())
477
}
478
imm @ (I::F32(_) | I::F64(_)) => {
479
self.asm.mov_ir(dst, imm, imm.size());
480
Ok(())
481
}
482
I::V128(_) => bail!(CodeGenError::unsupported_imm()),
483
},
484
(RegImm::Reg(rs), rd) => match (rs.class(), rd.to_reg().class()) {
485
(RegClass::Int, RegClass::Int) => Ok(self.asm.mov_rr(rs, rd, size)),
486
(RegClass::Float, RegClass::Float) => Ok(self.asm.fmov_rr(rs, rd, size)),
487
(RegClass::Int, RegClass::Float) => Ok(self.asm.mov_to_fpu(rs, rd, size)),
488
_ => bail!(CodeGenError::invalid_operand_combination()),
489
},
490
}
491
}
492
493
fn cmov(
494
&mut self,
495
dst: WritableReg,
496
src: Reg,
497
cc: IntCmpKind,
498
size: OperandSize,
499
) -> Result<()> {
500
match (src.class(), dst.to_reg().class()) {
501
(RegClass::Int, RegClass::Int) => self.asm.csel(src, dst.to_reg(), dst, Cond::from(cc)),
502
(RegClass::Float, RegClass::Float) => {
503
self.asm
504
.fpu_csel(src, dst.to_reg(), dst, Cond::from(cc), size)
505
}
506
_ => return Err(anyhow!(CodeGenError::invalid_operand_combination())),
507
}
508
509
Ok(())
510
}
511
512
fn add(&mut self, dst: WritableReg, lhs: Reg, rhs: RegImm, size: OperandSize) -> Result<()> {
513
match (rhs, lhs, dst) {
514
(RegImm::Imm(v), rn, rd) => self.add_ir(rd, rn, v, size),
515
516
(RegImm::Reg(rm), rn, rd) => {
517
self.asm.add_rrr(rm, rn, rd, size);
518
Ok(())
519
}
520
}
521
}
522
523
fn checked_uadd(
524
&mut self,
525
dst: WritableReg,
526
lhs: Reg,
527
rhs: RegImm,
528
size: OperandSize,
529
trap: TrapCode,
530
) -> Result<()> {
531
// Similar to all the other potentially-trapping operations, we need to
532
// ensure that the real SP is 16-byte aligned in case control flow is
533
// transferred to a signal handler.
534
self.with_aligned_sp(|masm| {
535
match (rhs, lhs, dst) {
536
// NB: we don't use `Self::add_ir` since we explicitly
537
// want to emit the add variant which sets overflow
538
// flags.
539
(RegImm::Imm(i), rn, rd) => {
540
let imm = i.unwrap_as_u64();
541
match Imm12::maybe_from_u64(imm) {
542
Some(imm12) => masm.asm.adds_ir(imm12, rn, rd, size),
543
None => {
544
masm.with_scratch::<IntScratch, _>(|masm, scratch| {
545
masm.asm.mov_ir(scratch.writable(), i, i.size());
546
masm.asm.adds_rrr(scratch.inner(), rn, rd, size);
547
});
548
}
549
}
550
}
551
552
(RegImm::Reg(rm), rn, rd) => {
553
masm.asm.adds_rrr(rm, rn, rd, size);
554
}
555
}
556
masm.asm.trapif(Cond::Hs, trap);
557
Ok(())
558
})
559
}
560
561
fn sub(&mut self, dst: WritableReg, lhs: Reg, rhs: RegImm, size: OperandSize) -> Result<()> {
562
match (rhs, lhs, dst) {
563
(RegImm::Imm(v), rn, rd) => {
564
let imm = v.unwrap_as_u64();
565
match Imm12::maybe_from_u64(imm) {
566
Some(imm12) => self.asm.sub_ir(imm12, rn, rd, size),
567
None => {
568
self.with_scratch::<IntScratch, _>(|masm, scratch| {
569
masm.asm.mov_ir(scratch.writable(), v, v.size());
570
masm.asm.sub_rrr(scratch.inner(), rn, rd, size);
571
});
572
}
573
};
574
575
Ok(())
576
}
577
578
(RegImm::Reg(rm), rn, rd) => {
579
self.asm.sub_rrr(rm, rn, rd, size);
580
Ok(())
581
}
582
}
583
}
584
585
fn mul(&mut self, dst: WritableReg, lhs: Reg, rhs: RegImm, size: OperandSize) -> Result<()> {
586
match (rhs, lhs, dst) {
587
(RegImm::Imm(v), rn, rd) => self.with_scratch::<IntScratch, _>(|masm, scratch| {
588
masm.asm.mov_ir(scratch.writable(), v, v.size());
589
masm.asm.mul_rrr(scratch.inner(), rn, rd, size);
590
Ok(())
591
}),
592
593
(RegImm::Reg(rm), rn, rd) => {
594
self.asm.mul_rrr(rm, rn, rd, size);
595
Ok(())
596
}
597
}
598
}
599
600
fn float_add(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize) -> Result<()> {
601
self.asm.fadd_rrr(rhs, lhs, dst, size);
602
Ok(())
603
}
604
605
fn float_sub(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize) -> Result<()> {
606
self.asm.fsub_rrr(rhs, lhs, dst, size);
607
Ok(())
608
}
609
610
fn float_mul(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize) -> Result<()> {
611
self.asm.fmul_rrr(rhs, lhs, dst, size);
612
Ok(())
613
}
614
615
fn float_div(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize) -> Result<()> {
616
self.asm.fdiv_rrr(rhs, lhs, dst, size);
617
Ok(())
618
}
619
620
fn float_min(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize) -> Result<()> {
621
self.asm.fmin_rrr(rhs, lhs, dst, size);
622
Ok(())
623
}
624
625
fn float_max(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize) -> Result<()> {
626
self.asm.fmax_rrr(rhs, lhs, dst, size);
627
Ok(())
628
}
629
630
fn float_copysign(
631
&mut self,
632
dst: WritableReg,
633
lhs: Reg,
634
rhs: Reg,
635
size: OperandSize,
636
) -> Result<()> {
637
let max_shift = match size {
638
OperandSize::S32 => 0x1f,
639
OperandSize::S64 => 0x3f,
640
_ => bail!(CodeGenError::unexpected_operand_size()),
641
};
642
self.asm.fushr_rri(rhs, writable!(rhs), max_shift, size);
643
self.asm.fsli_rri_mod(lhs, rhs, dst, max_shift, size);
644
Ok(())
645
}
646
647
fn float_neg(&mut self, dst: WritableReg, size: OperandSize) -> Result<()> {
648
self.asm.fneg_rr(dst.to_reg(), dst, size);
649
Ok(())
650
}
651
652
fn float_abs(&mut self, dst: WritableReg, size: OperandSize) -> Result<()> {
653
self.asm.fabs_rr(dst.to_reg(), dst, size);
654
Ok(())
655
}
656
657
fn float_round<
658
F: FnMut(&mut FuncEnv<Self::Ptr>, &mut CodeGenContext<Emission>, &mut Self) -> Result<()>,
659
>(
660
&mut self,
661
mode: RoundingMode,
662
_env: &mut FuncEnv<Self::Ptr>,
663
context: &mut CodeGenContext<Emission>,
664
size: OperandSize,
665
_fallback: F,
666
) -> Result<()> {
667
let src = context.pop_to_reg(self, None)?;
668
self.asm
669
.fround_rr(src.into(), writable!(src.into()), mode, size);
670
context.stack.push(src.into());
671
Ok(())
672
}
673
674
fn float_sqrt(&mut self, dst: WritableReg, src: Reg, size: OperandSize) -> Result<()> {
675
self.asm.fsqrt_rr(src, dst, size);
676
Ok(())
677
}
678
679
fn and(&mut self, dst: WritableReg, lhs: Reg, rhs: RegImm, size: OperandSize) -> Result<()> {
680
match (rhs, lhs, dst) {
681
(RegImm::Imm(v), rn, rd) => {
682
let imm = v.unwrap_as_u64();
683
let csize: inst::OperandSize = size.into();
684
685
match ImmLogic::maybe_from_u64(imm, csize.to_ty()) {
686
Some(imml) => self.asm.and_ir(imml, rn, rd, size),
687
None => {
688
self.with_scratch::<IntScratch, _>(|masm, scratch| {
689
masm.asm.mov_ir(scratch.writable(), v, v.size());
690
masm.asm.and_rrr(scratch.inner(), rn, rd, size);
691
});
692
}
693
};
694
695
Ok(())
696
}
697
698
(RegImm::Reg(rm), rn, rd) => {
699
self.asm.and_rrr(rm, rn, rd, size);
700
Ok(())
701
}
702
}
703
}
704
705
fn or(&mut self, dst: WritableReg, lhs: Reg, rhs: RegImm, size: OperandSize) -> Result<()> {
706
match (rhs, lhs, dst) {
707
(RegImm::Imm(v), rn, rd) => {
708
let imm = v.unwrap_as_u64();
709
let csize: inst::OperandSize = size.into();
710
711
match ImmLogic::maybe_from_u64(imm, csize.to_ty()) {
712
Some(imml) => self.asm.or_ir(imml, rn, rd, size),
713
None => {
714
self.with_scratch::<IntScratch, _>(|masm, scratch| {
715
masm.asm.mov_ir(scratch.writable(), v, v.size());
716
masm.asm.or_rrr(scratch.inner(), rn, rd, size);
717
});
718
}
719
};
720
721
Ok(())
722
}
723
724
(RegImm::Reg(rm), rn, rd) => {
725
self.asm.or_rrr(rm, rn, rd, size);
726
Ok(())
727
}
728
}
729
}
730
731
fn xor(&mut self, dst: WritableReg, lhs: Reg, rhs: RegImm, size: OperandSize) -> Result<()> {
732
match (rhs, lhs, dst) {
733
(RegImm::Imm(v), rn, rd) => {
734
let imm = v.unwrap_as_u64();
735
let csize: inst::OperandSize = size.into();
736
737
match ImmLogic::maybe_from_u64(imm, csize.to_ty()) {
738
Some(imml) => self.asm.xor_ir(imml, rn, rd, size),
739
None => {
740
self.with_scratch::<IntScratch, _>(|masm, scratch| {
741
masm.asm.mov_ir(scratch.writable(), v, v.size());
742
masm.asm.xor_rrr(scratch.inner(), rn, rd, size);
743
});
744
}
745
};
746
Ok(())
747
}
748
749
(RegImm::Reg(rm), rn, rd) => {
750
self.asm.xor_rrr(rm, rn, rd, size);
751
Ok(())
752
}
753
}
754
}
755
756
fn shift_ir(
757
&mut self,
758
dst: WritableReg,
759
imm: I,
760
lhs: Reg,
761
kind: ShiftKind,
762
size: OperandSize,
763
) -> Result<()> {
764
match ImmShift::maybe_from_u64(imm.unwrap_as_u64()) {
765
Some(imml) => self.asm.shift_ir(imml, lhs, dst, kind, size),
766
None => {
767
self.with_scratch::<IntScratch, _>(|masm, scratch| {
768
masm.asm.mov_ir(scratch.writable(), imm, imm.size());
769
masm.asm.shift_rrr(scratch.inner(), lhs, dst, kind, size);
770
});
771
}
772
};
773
Ok(())
774
}
775
776
fn shift(
777
&mut self,
778
context: &mut CodeGenContext<Emission>,
779
kind: ShiftKind,
780
size: OperandSize,
781
) -> Result<()> {
782
let src = context.pop_to_reg(self, None)?;
783
let dst = context.pop_to_reg(self, None)?;
784
785
self.asm
786
.shift_rrr(src.into(), dst.into(), writable!(dst.into()), kind, size);
787
788
context.free_reg(src);
789
context.stack.push(dst.into());
790
791
Ok(())
792
}
793
794
fn div(
795
&mut self,
796
context: &mut CodeGenContext<Emission>,
797
kind: DivKind,
798
size: OperandSize,
799
) -> Result<()> {
800
context.binop(self, size, |this, dividend, divisor, size| {
801
this.with_aligned_sp(|this| {
802
this.asm
803
.div_rrr(divisor, dividend, writable!(dividend), kind, size);
804
Ok(())
805
})?;
806
match size {
807
OperandSize::S32 => Ok(TypedReg::new(WasmValType::I32, dividend)),
808
OperandSize::S64 => Ok(TypedReg::new(WasmValType::I64, dividend)),
809
_ => Err(anyhow!(CodeGenError::unexpected_operand_size())),
810
}
811
})
812
}
813
814
fn rem(
815
&mut self,
816
context: &mut CodeGenContext<Emission>,
817
kind: RemKind,
818
size: OperandSize,
819
) -> Result<()> {
820
context.binop(self, size, |this, dividend, divisor, size| {
821
this.with_aligned_sp(|this| {
822
this.with_scratch::<IntScratch, _>(|masm, scratch| {
823
masm.asm.rem_rrr(
824
divisor,
825
dividend,
826
writable!(dividend),
827
scratch.writable(),
828
kind,
829
size,
830
);
831
});
832
Ok(())
833
})?;
834
match size {
835
OperandSize::S32 => Ok(TypedReg::new(WasmValType::I32, dividend)),
836
OperandSize::S64 => Ok(TypedReg::new(WasmValType::I64, dividend)),
837
_ => Err(anyhow!(CodeGenError::unexpected_operand_size())),
838
}
839
})
840
}
841
842
fn zero(&mut self, reg: WritableReg) -> Result<()> {
843
self.asm.mov_ir(reg, I::i64(0), OperandSize::S64);
844
Ok(())
845
}
846
847
fn popcnt(&mut self, context: &mut CodeGenContext<Emission>, size: OperandSize) -> Result<()> {
848
let src = context.pop_to_reg(self, None)?;
849
self.with_scratch::<FloatScratch, _>(|masm, tmp| {
850
masm.asm.mov_to_fpu(src.into(), tmp.writable(), size);
851
masm.asm.cnt(tmp.writable());
852
masm.asm
853
.addv(tmp.inner(), tmp.writable(), VectorSize::Size8x8);
854
masm.asm
855
.mov_from_vec(tmp.inner(), writable!(src.into()), 0, OperandSize::S8);
856
});
857
context.stack.push(src.into());
858
Ok(())
859
}
860
861
fn signed_truncate(
862
&mut self,
863
dst: WritableReg,
864
src: Reg,
865
src_size: OperandSize,
866
dst_size: OperandSize,
867
kind: TruncKind,
868
) -> Result<()> {
869
self.with_aligned_sp(|masm| {
870
masm.with_scratch::<FloatScratch, _>(|masm, scratch| {
871
masm.asm
872
.fpu_to_int(dst, src, scratch.writable(), src_size, dst_size, kind, true);
873
});
874
Ok(())
875
})
876
}
877
878
fn unsigned_truncate(
879
&mut self,
880
ctx: &mut CodeGenContext<Emission>,
881
src_size: OperandSize,
882
dst_size: OperandSize,
883
kind: TruncKind,
884
) -> Result<()> {
885
let dst_ty = match dst_size {
886
OperandSize::S32 => WasmValType::I32,
887
OperandSize::S64 => WasmValType::I64,
888
_ => bail!(CodeGenError::unexpected_operand_size()),
889
};
890
891
ctx.convert_op(self, dst_ty, |masm, dst, src, dst_size| {
892
masm.with_aligned_sp(|masm| {
893
masm.with_scratch::<FloatScratch, _>(|masm, scratch| {
894
masm.asm.fpu_to_int(
895
writable!(dst),
896
src,
897
scratch.writable(),
898
src_size,
899
dst_size,
900
kind,
901
false,
902
);
903
Ok(())
904
})
905
})
906
})
907
}
908
909
fn signed_convert(
910
&mut self,
911
dst: WritableReg,
912
src: Reg,
913
src_size: OperandSize,
914
dst_size: OperandSize,
915
) -> Result<()> {
916
self.asm.cvt_sint_to_float(src, dst, src_size, dst_size);
917
Ok(())
918
}
919
920
fn unsigned_convert(
921
&mut self,
922
dst: WritableReg,
923
src: Reg,
924
_tmp_gpr: Reg,
925
src_size: OperandSize,
926
dst_size: OperandSize,
927
) -> Result<()> {
928
self.asm.cvt_uint_to_float(src, dst, src_size, dst_size);
929
Ok(())
930
}
931
932
fn reinterpret_float_as_int(
933
&mut self,
934
dst: WritableReg,
935
src: Reg,
936
size: OperandSize,
937
) -> Result<()> {
938
self.asm.mov_from_vec(src, dst, 0, size);
939
Ok(())
940
}
941
942
fn reinterpret_int_as_float(
943
&mut self,
944
dst: WritableReg,
945
src: Reg,
946
size: OperandSize,
947
) -> Result<()> {
948
self.asm.mov_to_fpu(src, dst, size);
949
Ok(())
950
}
951
952
fn demote(&mut self, dst: WritableReg, src: Reg) -> Result<()> {
953
self.asm
954
.cvt_float_to_float(src, dst, OperandSize::S64, OperandSize::S32);
955
Ok(())
956
}
957
958
fn promote(&mut self, dst: WritableReg, src: Reg) -> Result<()> {
959
self.asm
960
.cvt_float_to_float(src, dst, OperandSize::S32, OperandSize::S64);
961
Ok(())
962
}
963
964
fn push(&mut self, reg: Reg, size: OperandSize) -> Result<StackSlot> {
965
self.reserve_stack(size.bytes())?;
966
let address = self.address_from_sp(SPOffset::from_u32(self.sp_offset))?;
967
self.asm.str(reg, address, size, TRUSTED_FLAGS);
968
969
Ok(StackSlot {
970
offset: SPOffset::from_u32(self.sp_offset),
971
size: size.bytes(),
972
})
973
}
974
975
fn address_at_reg(&self, reg: Reg, offset: u32) -> Result<Self::Address> {
976
Ok(Address::offset(reg, offset as i64))
977
}
978
979
fn cmp_with_set(
980
&mut self,
981
dst: WritableReg,
982
src: RegImm,
983
kind: IntCmpKind,
984
size: OperandSize,
985
) -> Result<()> {
986
self.cmp(dst.to_reg(), src, size)?;
987
self.asm.cset(dst, kind.into());
988
Ok(())
989
}
990
991
fn cmp(&mut self, src1: Reg, src2: RegImm, size: OperandSize) -> Result<()> {
992
match src2 {
993
RegImm::Reg(src2) => {
994
self.asm.subs_rrr(src2, src1, size);
995
Ok(())
996
}
997
RegImm::Imm(v) => {
998
let val = v.unwrap_as_u64();
999
match Imm12::maybe_from_u64(val) {
1000
Some(imm12) => self.asm.subs_ir(imm12, src1, size),
1001
None => {
1002
self.with_scratch::<IntScratch, _>(|masm, scratch| {
1003
masm.asm.mov_ir(scratch.writable(), v, v.size());
1004
masm.asm.subs_rrr(scratch.inner(), src1, size);
1005
});
1006
}
1007
};
1008
Ok(())
1009
}
1010
}
1011
}
1012
1013
fn float_cmp_with_set(
1014
&mut self,
1015
dst: WritableReg,
1016
src1: Reg,
1017
src2: Reg,
1018
kind: FloatCmpKind,
1019
size: OperandSize,
1020
) -> Result<()> {
1021
self.asm.fcmp(src1, src2, size);
1022
self.asm.cset(dst, kind.into());
1023
Ok(())
1024
}
1025
1026
fn clz(&mut self, dst: WritableReg, src: Reg, size: OperandSize) -> Result<()> {
1027
self.asm.clz(src, dst, size);
1028
Ok(())
1029
}
1030
1031
fn ctz(&mut self, dst: WritableReg, src: Reg, size: OperandSize) -> Result<()> {
1032
self.with_scratch::<IntScratch, _>(|masm, scratch| {
1033
masm.asm.rbit(src, scratch.writable(), size);
1034
masm.asm.clz(scratch.inner(), dst, size);
1035
Ok(())
1036
})
1037
}
1038
1039
fn wrap(&mut self, dst: WritableReg, src: Reg) -> Result<()> {
1040
self.asm.mov_rr(src, dst, OperandSize::S32);
1041
Ok(())
1042
}
1043
1044
fn extend(&mut self, dst: WritableReg, src: Reg, kind: ExtendKind) -> Result<()> {
1045
self.asm.extend(src, dst, kind);
1046
Ok(())
1047
}
1048
1049
fn get_label(&mut self) -> Result<MachLabel> {
1050
Ok(self.asm.get_label())
1051
}
1052
1053
fn bind(&mut self, label: MachLabel) -> Result<()> {
1054
let buffer = self.asm.buffer_mut();
1055
buffer.bind_label(label, &mut Default::default());
1056
Ok(())
1057
}
1058
1059
fn branch(
1060
&mut self,
1061
kind: IntCmpKind,
1062
lhs: Reg,
1063
rhs: RegImm,
1064
taken: MachLabel,
1065
size: OperandSize,
1066
) -> Result<()> {
1067
use IntCmpKind::*;
1068
1069
match &(lhs, rhs) {
1070
(rlhs, RegImm::Reg(rrhs)) => {
1071
// If the comparison kind is zero or not zero and both operands
1072
// are the same register, emit an ands instruction. Else we emit
1073
// a normal comparison.
1074
if (kind == Eq || kind == Ne) && (rlhs == rrhs) {
1075
self.asm.ands_rr(*rlhs, *rrhs, size);
1076
} else {
1077
self.cmp(lhs, rhs, size)?;
1078
}
1079
}
1080
_ => self.cmp(lhs, rhs, size)?,
1081
}
1082
self.asm.jmp_if(kind.into(), taken);
1083
Ok(())
1084
}
1085
1086
fn jmp(&mut self, target: MachLabel) -> Result<()> {
1087
self.asm.jmp(target);
1088
Ok(())
1089
}
1090
1091
fn unreachable(&mut self) -> Result<()> {
1092
self.with_aligned_sp(|masm| {
1093
masm.asm.udf(wasmtime_cranelift::TRAP_UNREACHABLE);
1094
Ok(())
1095
})
1096
}
1097
1098
fn jmp_table(&mut self, targets: &[MachLabel], index: Reg, tmp: Reg) -> Result<()> {
1099
// At least one default target.
1100
debug_assert!(targets.len() >= 1);
1101
let default_index = targets.len() - 1;
1102
let max = default_index;
1103
self.asm.mov_ir(
1104
writable!(tmp),
1105
I::i32(i32::try_from(max).unwrap()),
1106
OperandSize::S32,
1107
);
1108
// NB: We only emit the comparison instruction, since
1109
// `Assembler::jmp_table` (and the underlying Cranelift
1110
// instruction) will emit spectre mitigation and bounds
1111
// checks.
1112
self.asm.subs_rrr(tmp, index, OperandSize::S32);
1113
let default = targets[default_index];
1114
let rest = &targets[0..default_index];
1115
self.with_scratch::<IntScratch, _>(|masm, scratch| {
1116
masm.asm
1117
.jmp_table(rest, default, index, scratch.inner(), tmp);
1118
Ok(())
1119
})
1120
}
1121
1122
fn trap(&mut self, code: TrapCode) -> Result<()> {
1123
self.with_aligned_sp(|masm| {
1124
masm.asm.udf(code);
1125
Ok(())
1126
})
1127
}
1128
1129
fn trapz(&mut self, src: Reg, code: TrapCode) -> Result<()> {
1130
self.with_aligned_sp(|masm| {
1131
masm.asm.trapz(src, code, OperandSize::S64);
1132
Ok(())
1133
})
1134
}
1135
1136
fn trapif(&mut self, cc: IntCmpKind, code: TrapCode) -> Result<()> {
1137
self.with_aligned_sp(|masm| {
1138
masm.asm.trapif(cc.into(), code);
1139
Ok(())
1140
})
1141
}
1142
1143
fn start_source_loc(&mut self, loc: RelSourceLoc) -> Result<(CodeOffset, RelSourceLoc)> {
1144
Ok(self.asm.buffer_mut().start_srcloc(loc))
1145
}
1146
1147
fn end_source_loc(&mut self) -> Result<()> {
1148
self.asm.buffer_mut().end_srcloc();
1149
Ok(())
1150
}
1151
1152
fn current_code_offset(&self) -> Result<CodeOffset> {
1153
Ok(self.asm.buffer().cur_offset())
1154
}
1155
1156
fn add128(
1157
&mut self,
1158
dst_lo: WritableReg,
1159
dst_hi: WritableReg,
1160
lhs_lo: Reg,
1161
lhs_hi: Reg,
1162
rhs_lo: Reg,
1163
rhs_hi: Reg,
1164
) -> Result<()> {
1165
let _ = (dst_lo, dst_hi, lhs_lo, lhs_hi, rhs_lo, rhs_hi);
1166
Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))
1167
}
1168
1169
fn sub128(
1170
&mut self,
1171
dst_lo: WritableReg,
1172
dst_hi: WritableReg,
1173
lhs_lo: Reg,
1174
lhs_hi: Reg,
1175
rhs_lo: Reg,
1176
rhs_hi: Reg,
1177
) -> Result<()> {
1178
let _ = (dst_lo, dst_hi, lhs_lo, lhs_hi, rhs_lo, rhs_hi);
1179
Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))
1180
}
1181
1182
fn mul_wide(
1183
&mut self,
1184
context: &mut CodeGenContext<Emission>,
1185
kind: MulWideKind,
1186
) -> Result<()> {
1187
let _ = (context, kind);
1188
Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))
1189
}
1190
1191
fn splat(&mut self, _context: &mut CodeGenContext<Emission>, _size: SplatKind) -> Result<()> {
1192
bail!(CodeGenError::unimplemented_masm_instruction())
1193
}
1194
1195
fn shuffle(&mut self, _dst: WritableReg, _lhs: Reg, _rhs: Reg, _lanes: [u8; 16]) -> Result<()> {
1196
bail!(CodeGenError::unimplemented_masm_instruction())
1197
}
1198
1199
fn swizzle(&mut self, _dst: WritableReg, _lhs: Reg, _rhs: Reg) -> Result<()> {
1200
bail!(CodeGenError::unimplemented_masm_instruction())
1201
}
1202
1203
fn atomic_rmw(
1204
&mut self,
1205
_context: &mut CodeGenContext<Emission>,
1206
_addr: Self::Address,
1207
_size: OperandSize,
1208
_op: RmwOp,
1209
_flags: MemFlags,
1210
_extend: Option<Extend<Zero>>,
1211
) -> Result<()> {
1212
Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))
1213
}
1214
1215
fn extract_lane(
1216
&mut self,
1217
_src: Reg,
1218
_dst: WritableReg,
1219
_lane: u8,
1220
_kind: ExtractLaneKind,
1221
) -> Result<()> {
1222
bail!(CodeGenError::unimplemented_masm_instruction())
1223
}
1224
1225
fn replace_lane(
1226
&mut self,
1227
_src: RegImm,
1228
_dst: WritableReg,
1229
_lane: u8,
1230
_kind: ReplaceLaneKind,
1231
) -> Result<()> {
1232
bail!(CodeGenError::unimplemented_masm_instruction())
1233
}
1234
1235
fn atomic_cas(
1236
&mut self,
1237
_context: &mut CodeGenContext<Emission>,
1238
_addr: Self::Address,
1239
_size: OperandSize,
1240
_flags: MemFlags,
1241
_extend: Option<Extend<Zero>>,
1242
) -> Result<()> {
1243
Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))
1244
}
1245
1246
fn v128_eq(
1247
&mut self,
1248
_dst: WritableReg,
1249
_lhs: Reg,
1250
_rhs: Reg,
1251
_kind: VectorEqualityKind,
1252
) -> Result<()> {
1253
bail!(CodeGenError::unimplemented_masm_instruction())
1254
}
1255
1256
fn v128_ne(
1257
&mut self,
1258
_dst: WritableReg,
1259
_lhs: Reg,
1260
_rhs: Reg,
1261
_kind: VectorEqualityKind,
1262
) -> Result<()> {
1263
bail!(CodeGenError::unimplemented_masm_instruction())
1264
}
1265
1266
fn v128_lt(
1267
&mut self,
1268
_dst: WritableReg,
1269
_lhs: Reg,
1270
_rhs: Reg,
1271
_kind: VectorCompareKind,
1272
) -> Result<()> {
1273
bail!(CodeGenError::unimplemented_masm_instruction())
1274
}
1275
1276
fn v128_le(
1277
&mut self,
1278
_dst: WritableReg,
1279
_lhs: Reg,
1280
_rhs: Reg,
1281
_kind: VectorCompareKind,
1282
) -> Result<()> {
1283
bail!(CodeGenError::unimplemented_masm_instruction())
1284
}
1285
1286
fn v128_gt(
1287
&mut self,
1288
_dst: WritableReg,
1289
_lhs: Reg,
1290
_rhs: Reg,
1291
_kind: VectorCompareKind,
1292
) -> Result<()> {
1293
bail!(CodeGenError::unimplemented_masm_instruction())
1294
}
1295
1296
fn v128_ge(
1297
&mut self,
1298
_dst: WritableReg,
1299
_lhs: Reg,
1300
_rhs: Reg,
1301
_kind: VectorCompareKind,
1302
) -> Result<()> {
1303
bail!(CodeGenError::unimplemented_masm_instruction())
1304
}
1305
1306
fn v128_not(&mut self, _dst: WritableReg) -> Result<()> {
1307
Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))
1308
}
1309
1310
fn fence(&mut self) -> Result<()> {
1311
Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))
1312
}
1313
1314
fn v128_and(&mut self, _src1: Reg, _src2: Reg, _dst: WritableReg) -> Result<()> {
1315
Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))
1316
}
1317
1318
fn v128_and_not(&mut self, _src1: Reg, _src2: Reg, _dst: WritableReg) -> Result<()> {
1319
Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))
1320
}
1321
1322
fn v128_or(&mut self, _src1: Reg, _src2: Reg, _dst: WritableReg) -> Result<()> {
1323
Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))
1324
}
1325
1326
fn v128_xor(&mut self, _src1: Reg, _src2: Reg, _dst: WritableReg) -> Result<()> {
1327
Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))
1328
}
1329
1330
fn v128_bitselect(
1331
&mut self,
1332
_src1: Reg,
1333
_src2: Reg,
1334
_mask: Reg,
1335
_dst: WritableReg,
1336
) -> Result<()> {
1337
Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))
1338
}
1339
1340
fn v128_any_true(&mut self, _src: Reg, _dst: WritableReg) -> Result<()> {
1341
Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))
1342
}
1343
1344
fn v128_convert(&mut self, _src: Reg, _dst: WritableReg, _kind: V128ConvertKind) -> Result<()> {
1345
bail!(CodeGenError::unimplemented_masm_instruction())
1346
}
1347
1348
fn v128_narrow(
1349
&mut self,
1350
_src1: Reg,
1351
_src2: Reg,
1352
_dst: WritableReg,
1353
_kind: V128NarrowKind,
1354
) -> Result<()> {
1355
bail!(CodeGenError::unimplemented_masm_instruction())
1356
}
1357
1358
fn v128_demote(&mut self, _src: Reg, _dst: WritableReg) -> Result<()> {
1359
bail!(CodeGenError::unimplemented_masm_instruction())
1360
}
1361
1362
fn v128_promote(&mut self, _src: Reg, _dst: WritableReg) -> Result<()> {
1363
bail!(CodeGenError::unimplemented_masm_instruction())
1364
}
1365
1366
fn v128_extend(&mut self, _src: Reg, _dst: WritableReg, _kind: V128ExtendKind) -> Result<()> {
1367
bail!(CodeGenError::unimplemented_masm_instruction())
1368
}
1369
1370
fn v128_add(
1371
&mut self,
1372
_lhs: Reg,
1373
_rhs: Reg,
1374
_dst: WritableReg,
1375
_kind: V128AddKind,
1376
) -> Result<()> {
1377
Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))
1378
}
1379
1380
fn v128_sub(
1381
&mut self,
1382
_lhs: Reg,
1383
_rhs: Reg,
1384
_dst: WritableReg,
1385
_kind: V128SubKind,
1386
) -> Result<()> {
1387
Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))
1388
}
1389
1390
fn v128_mul(
1391
&mut self,
1392
_context: &mut CodeGenContext<Emission>,
1393
_kind: V128MulKind,
1394
) -> Result<()> {
1395
Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))
1396
}
1397
1398
fn v128_abs(&mut self, _src: Reg, _dst: WritableReg, _kind: V128AbsKind) -> Result<()> {
1399
bail!(CodeGenError::unimplemented_masm_instruction())
1400
}
1401
1402
fn v128_neg(&mut self, _op: WritableReg, _kind: V128NegKind) -> Result<()> {
1403
Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))
1404
}
1405
1406
fn v128_shift(
1407
&mut self,
1408
_context: &mut CodeGenContext<Emission>,
1409
_lane_width: OperandSize,
1410
_shift_kind: ShiftKind,
1411
) -> Result<()> {
1412
Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))
1413
}
1414
1415
fn v128_q15mulr_sat_s(
1416
&mut self,
1417
_lhs: Reg,
1418
_rhs: Reg,
1419
_dst: WritableReg,
1420
_size: OperandSize,
1421
) -> Result<()> {
1422
bail!(CodeGenError::unimplemented_masm_instruction())
1423
}
1424
1425
fn v128_all_true(&mut self, _src: Reg, _dst: WritableReg, _size: OperandSize) -> Result<()> {
1426
bail!(CodeGenError::unimplemented_masm_instruction())
1427
}
1428
1429
fn v128_bitmask(&mut self, _src: Reg, _dst: WritableReg, _size: OperandSize) -> Result<()> {
1430
bail!(CodeGenError::unimplemented_masm_instruction())
1431
}
1432
1433
fn v128_trunc(
1434
&mut self,
1435
_context: &mut CodeGenContext<Emission>,
1436
_kind: V128TruncKind,
1437
) -> Result<()> {
1438
bail!(CodeGenError::unimplemented_masm_instruction())
1439
}
1440
1441
fn v128_min(
1442
&mut self,
1443
_src1: Reg,
1444
_src2: Reg,
1445
_dst: WritableReg,
1446
_kind: V128MinKind,
1447
) -> Result<()> {
1448
Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))
1449
}
1450
1451
fn v128_max(
1452
&mut self,
1453
_src1: Reg,
1454
_src2: Reg,
1455
_dst: WritableReg,
1456
_kind: V128MaxKind,
1457
) -> Result<()> {
1458
Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))
1459
}
1460
1461
fn v128_extmul(
1462
&mut self,
1463
_context: &mut CodeGenContext<Emission>,
1464
_kind: V128ExtMulKind,
1465
) -> Result<()> {
1466
Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))
1467
}
1468
1469
fn v128_extadd_pairwise(
1470
&mut self,
1471
_src: Reg,
1472
_dst: WritableReg,
1473
_kind: V128ExtAddKind,
1474
) -> Result<()> {
1475
Err(anyhow!(CodeGenError::unimplemented_masm_instruction()))
1476
}
1477
1478
fn v128_dot(&mut self, _lhs: Reg, _rhs: Reg, _dst: WritableReg) -> Result<()> {
1479
bail!(CodeGenError::unimplemented_masm_instruction())
1480
}
1481
1482
fn v128_popcnt(&mut self, _context: &mut CodeGenContext<Emission>) -> Result<()> {
1483
bail!(CodeGenError::unimplemented_masm_instruction())
1484
}
1485
1486
fn v128_avgr(
1487
&mut self,
1488
_lhs: Reg,
1489
_rhs: Reg,
1490
_dst: WritableReg,
1491
_size: OperandSize,
1492
) -> Result<()> {
1493
bail!(CodeGenError::unimplemented_masm_instruction())
1494
}
1495
1496
fn v128_div(
1497
&mut self,
1498
_lhs: Reg,
1499
_rhs: Reg,
1500
_dst: WritableReg,
1501
_size: OperandSize,
1502
) -> Result<()> {
1503
bail!(CodeGenError::unimplemented_masm_instruction())
1504
}
1505
1506
fn v128_sqrt(&mut self, _src: Reg, _dst: WritableReg, _size: OperandSize) -> Result<()> {
1507
bail!(CodeGenError::unimplemented_masm_instruction())
1508
}
1509
1510
fn v128_ceil(&mut self, _src: Reg, _dst: WritableReg, _size: OperandSize) -> Result<()> {
1511
bail!(CodeGenError::unimplemented_masm_instruction())
1512
}
1513
1514
fn v128_floor(&mut self, _src: Reg, _dst: WritableReg, _size: OperandSize) -> Result<()> {
1515
bail!(CodeGenError::unimplemented_masm_instruction())
1516
}
1517
1518
fn v128_nearest(&mut self, _src: Reg, _dst: WritableReg, _size: OperandSize) -> Result<()> {
1519
bail!(CodeGenError::unimplemented_masm_instruction())
1520
}
1521
1522
fn v128_pmin(
1523
&mut self,
1524
_lhs: Reg,
1525
_rhs: Reg,
1526
_dst: WritableReg,
1527
_size: OperandSize,
1528
) -> Result<()> {
1529
bail!(CodeGenError::unimplemented_masm_instruction())
1530
}
1531
1532
fn v128_pmax(
1533
&mut self,
1534
_lhs: Reg,
1535
_rhs: Reg,
1536
_dst: WritableReg,
1537
_size: OperandSize,
1538
) -> Result<()> {
1539
bail!(CodeGenError::unimplemented_masm_instruction())
1540
}
1541
}
1542
1543
impl MacroAssembler {
1544
fn increment_sp(&mut self, bytes: u32) {
1545
self.sp_offset += bytes;
1546
1547
// NOTE: we use `max` here to track the largest stack allocation in `sp_max`. Once we have
1548
// seen the entire function, this value will represent the maximum size for the stack
1549
// frame.
1550
self.sp_max = self.sp_max.max(self.sp_offset);
1551
}
1552
1553
fn decrement_sp(&mut self, bytes: u32) {
1554
self.sp_offset -= bytes;
1555
}
1556
1557
// Copies the value of the stack pointer to the shadow stack
1558
// pointer: mov x28, sp
1559
1560
// This function is called at the epilogue.
1561
fn move_sp_to_shadow_sp(&mut self) {
1562
let sp = regs::sp();
1563
let shadow_sp = regs::shadow_sp();
1564
self.asm.mov_rr(sp, writable!(shadow_sp), OperandSize::S64);
1565
}
1566
1567
/// Heloper to add an immediate to a register.
1568
fn add_ir(&mut self, dst: WritableReg, lhs: Reg, rhs: I, size: OperandSize) -> Result<()> {
1569
let imm = rhs.unwrap_as_u64();
1570
match Imm12::maybe_from_u64(imm) {
1571
Some(imm12) => self.asm.add_ir(imm12, lhs, dst, size),
1572
None => {
1573
self.with_scratch::<IntScratch, _>(|masm, scratch| {
1574
masm.asm.mov_ir(scratch.writable(), rhs, rhs.size());
1575
masm.asm.add_rrr(scratch.inner(), lhs, dst, size);
1576
});
1577
}
1578
};
1579
Ok(())
1580
}
1581
1582
// Copies the value of the shadow stack pointer to the stack pointer: mov
1583
// sp, x28.
1584
//
1585
// This function is usually called when the space is claimed, e.g., via
1586
// a push, when stack space is reserved explicitly or after emitting code
1587
// that requires explicit stack pointer alignment (code that could result in
1588
// signal handling).
1589
//
1590
// This ensures the stack pointer always reflects the allocated stack space,
1591
// otherwise any space below the stack pointer could get clobbered with
1592
// interrupts and signal handlers.
1593
//
1594
// This function must also be called at the function epilogue, since the
1595
// stack pointer is used to restore the current function frame.
1596
fn move_shadow_sp_to_sp(&mut self) {
1597
let shadow_sp = regs::shadow_sp();
1598
let sp = writable!(regs::sp());
1599
let imm = Imm12::maybe_from_u64(0).unwrap();
1600
self.asm.add_ir(imm, shadow_sp, sp, OperandSize::S64);
1601
}
1602
}
1603
1604