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