1#![cfg(target_arch = "x86_64")]
17
18#[derive(Clone, Copy, Debug, PartialEq, Eq)]
20#[repr(u8)]
21#[allow(missing_docs)]
22pub enum Reg {
23 Rax = 0,
24 Rcx = 1,
25 Rdx = 2,
26 Rbx = 3,
27 Rsp = 4,
28 Rbp = 5,
29 Rsi = 6,
30 Rdi = 7,
31 R8 = 8,
32 R9 = 9,
33 R10 = 10,
34 R11 = 11,
35 R12 = 12,
36 R13 = 13,
37 R14 = 14,
38 R15 = 15,
39}
40
41impl Reg {
42 #[inline]
44 fn lo3(self) -> u8 {
45 (self as u8) & 0b111
46 }
47 #[inline]
49 fn hi(self) -> u8 {
50 ((self as u8) >> 3) & 1
51 }
52}
53
54#[derive(Clone, Copy, Debug, PartialEq, Eq)]
59#[repr(u8)]
60#[allow(missing_docs)]
61pub enum Xmm {
62 Xmm0 = 0,
63 Xmm1 = 1,
64 Xmm2 = 2,
65 Xmm3 = 3,
66 Xmm4 = 4,
67 Xmm5 = 5,
68 Xmm6 = 6,
69 Xmm7 = 7,
70 Xmm8 = 8,
71 Xmm9 = 9,
72 Xmm10 = 10,
73 Xmm11 = 11,
74 Xmm12 = 12,
75 Xmm13 = 13,
76 Xmm14 = 14,
77 Xmm15 = 15,
78}
79
80impl Xmm {
81 #[inline]
83 fn lo3(self) -> u8 {
84 (self as u8) & 0b111
85 }
86 #[inline]
88 fn hi(self) -> u8 {
89 ((self as u8) >> 3) & 1
90 }
91}
92
93#[derive(Clone, Copy, Debug, PartialEq, Eq)]
99pub enum Cond {
100 Lt,
102 Gt,
104 Le,
106 Ge,
108 Eq,
110 Ne,
112 AeU,
116 AU,
120 BeU,
124 BU,
127 ParityEven,
131 ParityOdd,
135 Overflow,
139}
140
141impl Cond {
142 fn cc(self) -> u8 {
145 match self {
146 Cond::Eq => 0x4,
147 Cond::Ne => 0x5,
148 Cond::Ge => 0xD, Cond::Lt => 0xC, Cond::Le => 0xE, Cond::Gt => 0xF, Cond::AeU => 0x3, Cond::AU => 0x7, Cond::BeU => 0x6, Cond::BU => 0x2, Cond::ParityEven => 0xA, Cond::ParityOdd => 0xB, Cond::Overflow => 0x0, }
160 }
161}
162
163#[derive(Clone, Copy, Debug, PartialEq, Eq)]
167pub struct LabelId(pub usize);
168
169enum Fixup {
170 Rel32 { site: usize, label: LabelId },
173}
174
175pub struct Asm {
177 buf: Vec<u8>,
178 label_pos: Vec<usize>,
180 fixups: Vec<Fixup>,
181}
182
183impl Default for Asm {
184 fn default() -> Self {
185 Asm::new()
186 }
187}
188
189impl Asm {
190 pub fn new() -> Self {
192 Asm { buf: Vec::with_capacity(256), label_pos: Vec::new(), fixups: Vec::new() }
193 }
194
195 pub fn pos(&self) -> usize {
197 self.buf.len()
198 }
199
200 pub fn new_label(&mut self) -> LabelId {
202 let id = LabelId(self.label_pos.len());
203 self.label_pos.push(usize::MAX);
204 id
205 }
206
207 pub fn bind(&mut self, l: LabelId) {
209 self.label_pos[l.0] = self.buf.len();
210 }
211
212 fn rex(&mut self, w: bool, r: u8, x: u8, b: u8) {
215 let byte = 0x40 | ((w as u8) << 3) | (r << 2) | (x << 1) | b;
216 self.buf.push(byte);
217 }
218
219 pub fn mov_ri(&mut self, dst: Reg, imm: i64) {
221 self.rex(true, 0, 0, dst.hi());
222 self.buf.push(0xB8 + dst.lo3());
223 self.buf.extend_from_slice(&imm.to_le_bytes());
224 }
225
226 pub fn mov_rr(&mut self, dst: Reg, src: Reg) {
228 if dst == src {
229 return;
230 }
231 self.rex(true, src.hi(), 0, dst.hi());
232 self.buf.push(0x89);
233 self.modrm_reg(src, dst);
234 }
235
236 fn modrm_reg(&mut self, r: Reg, m: Reg) {
238 self.buf.push(0b1100_0000 | (r.lo3() << 3) | m.lo3());
239 }
240
241 fn modrm_mem(&mut self, r: Reg, base: Reg, disp: i32) {
244 let rm = base.lo3();
246 self.buf.push(0b1000_0000 | (r.lo3() << 3) | rm);
247 if rm == 0b100 {
248 self.buf.push(0b0000_0000 | (0b100 << 3) | rm);
250 }
251 self.buf.extend_from_slice(&disp.to_le_bytes());
252 }
253
254 pub fn mov_rm(&mut self, dst: Reg, base: Reg, disp: i32) {
256 self.rex(true, dst.hi(), 0, base.hi());
257 self.buf.push(0x8B);
258 self.modrm_mem(dst, base, disp);
259 }
260
261 pub fn mov_mr(&mut self, base: Reg, disp: i32, src: Reg) {
263 self.rex(true, src.hi(), 0, base.hi());
264 self.buf.push(0x89);
265 self.modrm_mem(src, base, disp);
266 }
267
268 pub fn add_rr(&mut self, dst: Reg, src: Reg) {
270 self.rex(true, src.hi(), 0, dst.hi());
271 self.buf.push(0x01);
272 self.modrm_reg(src, dst);
273 }
274
275 pub fn sub_rr(&mut self, dst: Reg, src: Reg) {
277 self.rex(true, src.hi(), 0, dst.hi());
278 self.buf.push(0x29);
279 self.modrm_reg(src, dst);
280 }
281
282 pub fn imul_rr(&mut self, dst: Reg, src: Reg) {
284 self.rex(true, dst.hi(), 0, src.hi());
285 self.buf.push(0x0F);
286 self.buf.push(0xAF);
287 self.modrm_reg(dst, src);
288 }
289
290 pub fn sub_ri(&mut self, dst: Reg, imm: i32) {
292 self.rex(true, 0, 0, dst.hi());
293 self.buf.push(0x81);
294 self.buf.push(0b1100_0000 | (5 << 3) | dst.lo3());
295 self.buf.extend_from_slice(&imm.to_le_bytes());
296 }
297
298 pub fn add_ri(&mut self, dst: Reg, imm: i32) {
300 self.rex(true, 0, 0, dst.hi());
301 self.buf.push(0x81);
302 self.buf.push(0b1100_0000 | dst.lo3());
303 self.buf.extend_from_slice(&imm.to_le_bytes());
304 }
305
306 pub fn cmp_ri(&mut self, dst: Reg, imm: i32) {
308 self.rex(true, 0, 0, dst.hi());
309 self.buf.push(0x81);
310 self.buf.push(0b1100_0000 | (7 << 3) | dst.lo3());
311 self.buf.extend_from_slice(&imm.to_le_bytes());
312 }
313
314 pub fn call_r(&mut self, target: Reg) {
316 if target.hi() == 1 {
317 self.buf.push(0x41);
318 }
319 self.buf.push(0xFF);
320 self.buf.push(0b1100_0000 | (2 << 3) | target.lo3());
321 }
322
323 pub fn shl_ri(&mut self, dst: Reg, imm: u8) {
325 self.rex(true, 0, 0, dst.hi());
326 self.buf.push(0xC1);
327 self.buf.push(0b1100_0000 | (4 << 3) | dst.lo3());
328 self.buf.push(imm);
329 }
330
331 pub fn and_rr(&mut self, dst: Reg, src: Reg) {
333 self.rex(true, src.hi(), 0, dst.hi());
334 self.buf.push(0x21);
335 self.modrm_reg(src, dst);
336 }
337
338 pub fn or_rr(&mut self, dst: Reg, src: Reg) {
340 self.rex(true, src.hi(), 0, dst.hi());
341 self.buf.push(0x09);
342 self.modrm_reg(src, dst);
343 }
344
345 pub fn xor_rr(&mut self, dst: Reg, src: Reg) {
347 self.rex(true, src.hi(), 0, dst.hi());
348 self.buf.push(0x31);
349 self.modrm_reg(src, dst);
350 }
351
352 pub fn not_r(&mut self, dst: Reg) {
354 self.rex(true, 0, 0, dst.hi());
355 self.buf.push(0xF7);
356 self.buf.push(0b1100_0000 | (2 << 3) | dst.lo3());
357 }
358
359 pub fn neg_r(&mut self, dst: Reg) {
361 self.rex(true, 0, 0, dst.hi());
362 self.buf.push(0xF7);
363 self.buf.push(0b1100_0000 | (3 << 3) | dst.lo3());
364 }
365
366 pub fn shl_cl(&mut self, dst: Reg) {
368 self.rex(true, 0, 0, dst.hi());
369 self.buf.push(0xD3);
370 self.buf.push(0b1100_0000 | (4 << 3) | dst.lo3());
371 }
372
373 pub fn sar_cl(&mut self, dst: Reg) {
375 self.rex(true, 0, 0, dst.hi());
376 self.buf.push(0xD3);
377 self.buf.push(0b1100_0000 | (7 << 3) | dst.lo3());
378 }
379
380 pub fn cmp_rr(&mut self, a: Reg, b: Reg) {
382 self.rex(true, b.hi(), 0, a.hi());
383 self.buf.push(0x39);
384 self.modrm_reg(b, a);
385 }
386
387 pub fn cqo(&mut self) {
389 self.buf.push(0x48);
390 self.buf.push(0x99);
391 }
392
393 pub fn idiv_r(&mut self, src: Reg) {
395 self.rex(true, 0, 0, src.hi());
396 self.buf.push(0xF7);
397 self.buf.push(0b1100_0000 | (7 << 3) | src.lo3());
398 }
399
400 pub fn mul_r(&mut self, src: Reg) {
404 self.rex(true, 0, 0, src.hi());
405 self.buf.push(0xF7);
406 self.buf.push(0b1100_0000 | (4 << 3) | src.lo3());
407 }
408
409 pub fn shr_ri(&mut self, dst: Reg, imm: u8) {
414 self.rex(true, 0, 0, dst.hi());
415 self.buf.push(0xC1);
416 self.buf.push(0b1100_0000 | (5 << 3) | dst.lo3());
417 self.buf.push(imm);
418 }
419
420 pub fn test_rr(&mut self, a: Reg, b: Reg) {
422 self.rex(true, b.hi(), 0, a.hi());
423 self.buf.push(0x85);
424 self.modrm_reg(b, a);
425 }
426
427 fn sse_rex_rr(&mut self, r: Xmm, m: Xmm) {
443 if r.hi() != 0 || m.hi() != 0 {
444 self.buf.push(0x40 | (r.hi() << 2) | m.hi());
445 }
446 }
447
448 fn modrm_xmm_rr(&mut self, r: Xmm, m: Xmm) {
450 self.buf.push(0b1100_0000 | (r.lo3() << 3) | m.lo3());
451 }
452
453 fn sse_sd_rr(&mut self, prefix: u8, op: u8, dst: Xmm, src: Xmm) {
456 self.buf.push(prefix);
457 self.sse_rex_rr(dst, src);
458 self.buf.push(0x0F);
459 self.buf.push(op);
460 self.modrm_xmm_rr(dst, src);
461 }
462
463 fn sse_sd_rm(&mut self, prefix: u8, op: u8, dst: Xmm, base: Reg, disp: i32) {
466 self.buf.push(prefix);
467 if dst.hi() != 0 || base.hi() != 0 {
469 self.buf.push(0x40 | (dst.hi() << 2) | base.hi());
470 }
471 self.buf.push(0x0F);
472 self.buf.push(op);
473 let rm = base.lo3();
474 self.buf.push(0b1000_0000 | (dst.lo3() << 3) | rm);
475 if rm == 0b100 {
476 self.buf.push(0b0000_0000 | (0b100 << 3) | rm);
477 }
478 self.buf.extend_from_slice(&disp.to_le_bytes());
479 }
480
481 pub fn movsd_rm(&mut self, dst: Xmm, base: Reg, disp: i32) {
483 self.sse_sd_rm(0xF2, 0x10, dst, base, disp);
484 }
485 pub fn movsd_mr(&mut self, base: Reg, disp: i32, src: Xmm) {
487 self.sse_sd_rm(0xF2, 0x11, src, base, disp);
488 }
489 pub fn movsd_rr(&mut self, dst: Xmm, src: Xmm) {
491 if dst == src {
492 return;
493 }
494 self.sse_sd_rr(0xF2, 0x10, dst, src);
495 }
496 pub fn addsd_rr(&mut self, dst: Xmm, src: Xmm) {
498 self.sse_sd_rr(0xF2, 0x58, dst, src);
499 }
500 pub fn subsd_rr(&mut self, dst: Xmm, src: Xmm) {
502 self.sse_sd_rr(0xF2, 0x5C, dst, src);
503 }
504 pub fn mulsd_rr(&mut self, dst: Xmm, src: Xmm) {
506 self.sse_sd_rr(0xF2, 0x59, dst, src);
507 }
508 pub fn divsd_rr(&mut self, dst: Xmm, src: Xmm) {
510 self.sse_sd_rr(0xF2, 0x5E, dst, src);
511 }
512 pub fn sqrtsd_rr(&mut self, dst: Xmm, src: Xmm) {
514 self.sse_sd_rr(0xF2, 0x51, dst, src);
515 }
516
517 pub fn movupd_rm(&mut self, dst: Xmm, base: Reg, disp: i32) {
530 self.sse_sd_rm(0x66, 0x10, dst, base, disp);
531 }
532 pub fn movupd_mr(&mut self, base: Reg, disp: i32, src: Xmm) {
534 self.sse_sd_rm(0x66, 0x11, src, base, disp);
535 }
536 pub fn movupd_rr(&mut self, dst: Xmm, src: Xmm) {
538 if dst == src {
539 return;
540 }
541 self.sse_sd_rr(0x66, 0x10, dst, src);
542 }
543 pub fn addpd_rr(&mut self, dst: Xmm, src: Xmm) {
545 self.sse_sd_rr(0x66, 0x58, dst, src);
546 }
547 pub fn subpd_rr(&mut self, dst: Xmm, src: Xmm) {
549 self.sse_sd_rr(0x66, 0x5C, dst, src);
550 }
551 pub fn mulpd_rr(&mut self, dst: Xmm, src: Xmm) {
553 self.sse_sd_rr(0x66, 0x59, dst, src);
554 }
555 pub fn cmppd_rr(&mut self, dst: Xmm, src: Xmm, pred: u8) {
559 self.buf.push(0x66);
560 self.sse_rex_rr(dst, src);
561 self.buf.push(0x0F);
562 self.buf.push(0xC2);
563 self.modrm_xmm_rr(dst, src);
564 self.buf.push(pred);
565 }
566 pub fn movmskpd(&mut self, dst: Reg, src: Xmm) {
571 self.buf.push(0x66);
572 let rex = 0x40 | (dst.hi() << 2) | src.hi();
573 if rex != 0x40 {
574 self.buf.push(rex);
575 }
576 self.buf.push(0x0F);
577 self.buf.push(0x50);
578 self.buf.push(0b1100_0000 | (dst.lo3() << 3) | src.lo3());
579 }
580 pub fn andpd_rr(&mut self, dst: Xmm, src: Xmm) {
583 self.sse_sd_rr(0x66, 0x54, dst, src);
584 }
585 pub fn andnpd_rr(&mut self, dst: Xmm, src: Xmm) {
587 self.sse_sd_rr(0x66, 0x55, dst, src);
588 }
589 pub fn orpd_rr(&mut self, dst: Xmm, src: Xmm) {
591 self.sse_sd_rr(0x66, 0x56, dst, src);
592 }
593 pub fn xorpd_rr(&mut self, dst: Xmm, src: Xmm) {
595 self.sse_sd_rr(0x66, 0x57, dst, src);
596 }
597 pub fn divpd_rr(&mut self, dst: Xmm, src: Xmm) {
599 self.sse_sd_rr(0x66, 0x5E, dst, src);
600 }
601 pub fn sqrtpd_rr(&mut self, dst: Xmm, src: Xmm) {
603 self.sse_sd_rr(0x66, 0x51, dst, src);
604 }
605 pub fn ucomisd_rr(&mut self, a: Xmm, b: Xmm) {
610 self.buf.push(0x66);
611 self.sse_rex_rr(a, b);
612 self.buf.push(0x0F);
613 self.buf.push(0x2E);
614 self.modrm_xmm_rr(a, b);
615 }
616 pub fn cvtsi2sd(&mut self, dst: Xmm, src: Reg) {
619 self.buf.push(0xF2);
620 self.buf.push(0x48 | (dst.hi() << 2) | src.hi());
622 self.buf.push(0x0F);
623 self.buf.push(0x2A);
624 self.buf.push(0b1100_0000 | (dst.lo3() << 3) | src.lo3());
625 }
626 pub fn movq_xr(&mut self, dst: Xmm, src: Reg) {
629 self.buf.push(0x66);
630 self.buf.push(0x48 | (dst.hi() << 2) | src.hi());
631 self.buf.push(0x0F);
632 self.buf.push(0x6E);
633 self.buf.push(0b1100_0000 | (dst.lo3() << 3) | src.lo3());
634 }
635 pub fn movq_rx(&mut self, dst: Reg, src: Xmm) {
638 self.buf.push(0x66);
639 self.buf.push(0x48 | (src.hi() << 2) | dst.hi());
640 self.buf.push(0x0F);
641 self.buf.push(0x7E);
642 self.buf.push(0b1100_0000 | (src.lo3() << 3) | dst.lo3());
643 }
644
645 pub fn movzx_rm8(&mut self, dst: Reg, base: Reg, disp: i32) {
651 self.rex(true, dst.hi(), 0, base.hi());
652 self.buf.push(0x0F);
653 self.buf.push(0xB6);
654 self.modrm_mem(dst, base, disp);
655 }
656
657 pub fn mov_mr8(&mut self, base: Reg, disp: i32, src: Reg) {
665 let need_rex = src.hi() != 0 || base.hi() != 0 || ((src as u8) & 0b100) != 0;
666 if need_rex {
667 self.buf.push(0x40 | (src.hi() << 2) | base.hi());
668 }
669 self.buf.push(0x88);
670 self.modrm_mem(src, base, disp);
671 }
672
673 pub fn movsxd_rm(&mut self, dst: Reg, base: Reg, disp: i32) {
679 self.rex(true, dst.hi(), 0, base.hi());
680 self.buf.push(0x63);
681 self.modrm_mem(dst, base, disp);
682 }
683
684 pub fn mov_mr32(&mut self, base: Reg, disp: i32, src: Reg) {
690 if src.hi() != 0 || base.hi() != 0 {
691 self.buf.push(0x40 | (src.hi() << 2) | base.hi());
692 }
693 self.buf.push(0x89);
694 self.modrm_mem(src, base, disp);
695 }
696
697 pub fn setcc8(&mut self, cond: Cond, dst: Reg) {
703 self.buf.push(0x40 | dst.hi());
704 self.buf.push(0x0F);
705 self.buf.push(0x90 | cond.cc());
706 self.buf.push(0b1100_0000 | dst.lo3());
707 }
708
709 pub fn setcc_movzx(&mut self, cond: Cond, dst: Reg) {
713 self.buf.push(0x40 | dst.hi());
716 self.buf.push(0x0F);
717 self.buf.push(0x90 | cond.cc());
718 self.buf.push(0b1100_0000 | dst.lo3());
719 self.rex(true, dst.hi(), 0, dst.hi());
721 self.buf.push(0x0F);
722 self.buf.push(0xB6);
723 self.modrm_reg(dst, dst);
724 }
725
726 pub fn jmp(&mut self, label: LabelId) {
728 self.buf.push(0xE9);
729 let site = self.buf.len();
730 self.buf.extend_from_slice(&[0, 0, 0, 0]);
731 self.fixups.push(Fixup::Rel32 { site, label });
732 }
733
734 pub fn jcc(&mut self, cond: Cond, label: LabelId) {
736 self.buf.push(0x0F);
737 self.buf.push(0x80 | cond.cc());
738 let site = self.buf.len();
739 self.buf.extend_from_slice(&[0, 0, 0, 0]);
740 self.fixups.push(Fixup::Rel32 { site, label });
741 }
742
743 pub fn push(&mut self, r: Reg) {
745 if r.hi() == 1 {
746 self.buf.push(0x41);
747 }
748 self.buf.push(0x50 + r.lo3());
749 }
750
751 pub fn pop(&mut self, r: Reg) {
753 if r.hi() == 1 {
754 self.buf.push(0x41);
755 }
756 self.buf.push(0x58 + r.lo3());
757 }
758
759 pub fn ret(&mut self) {
761 self.buf.push(0xC3);
762 }
763
764 pub fn resolve(mut self) -> Vec<u8> {
768 for f in &self.fixups {
769 match *f {
770 Fixup::Rel32 { site, label } => {
771 let target = self.label_pos[label.0];
772 assert_ne!(target, usize::MAX, "unbound label {label:?}");
773 let rel = (target as i64) - (site as i64 + 4);
774 let rel32 = rel as i32;
775 self.buf[site..site + 4].copy_from_slice(&rel32.to_le_bytes());
776 }
777 }
778 }
779 self.buf
780 }
781}
782
783#[cfg(test)]
784mod tests {
785 use super::*;
786 use crate::JitPage;
787
788 fn run2(code: &[u8], a: i64, b: i64) -> i64 {
790 let page = JitPage::new(code).expect("map");
791 let f = unsafe { page.as_fn_i64_i64() };
792 f(a, b)
793 }
794
795 #[test]
796 fn mov_imm_and_ret_returns_constant() {
797 let mut a = Asm::new();
798 a.mov_ri(Reg::Rax, 0x1234_5678_9ABC_DEF0u64 as i64);
799 a.ret();
800 assert_eq!(run2(&a.resolve(), 0, 0), 0x1234_5678_9ABC_DEF0u64 as i64);
801 }
802
803 #[test]
804 fn add_two_args_via_sysv() {
805 let mut a = Asm::new();
807 a.mov_rr(Reg::Rax, Reg::Rdi);
808 a.add_rr(Reg::Rax, Reg::Rsi);
809 a.ret();
810 let code = a.resolve();
811 assert_eq!(run2(&code, 40, 2), 42);
812 assert_eq!(run2(&code, -5, 5), 0);
813 }
814
815 #[test]
816 fn imul_and_extended_registers() {
817 let mut a = Asm::new();
818 a.mov_rr(Reg::R10, Reg::Rdi);
819 a.imul_rr(Reg::R10, Reg::Rsi);
820 a.mov_rr(Reg::Rax, Reg::R10);
821 a.ret();
822 assert_eq!(run2(&a.resolve(), 6, 7), 42);
823 }
824
825 #[test]
826 fn setcc_materializes_zero_one() {
827 let mut a = Asm::new();
829 a.cmp_rr(Reg::Rdi, Reg::Rsi);
830 a.setcc_movzx(Cond::Lt, Reg::Rax);
831 a.ret();
832 let code = a.resolve();
833 assert_eq!(run2(&code, 1, 2), 1);
834 assert_eq!(run2(&code, 2, 1), 0);
835 assert_eq!(run2(&code, 2, 2), 0);
836 }
837
838 #[test]
839 fn conditional_jump_branches() {
840 let mut a = Asm::new();
842 let els = a.new_label();
843 a.cmp_rr(Reg::Rdi, Reg::Rsi);
844 a.jcc(Cond::Ge, els); a.mov_ri(Reg::Rax, 100);
846 a.ret();
847 a.bind(els);
848 a.mov_ri(Reg::Rax, 200);
849 a.ret();
850 let code = a.resolve();
851 assert_eq!(run2(&code, 1, 2), 100);
852 assert_eq!(run2(&code, 2, 1), 200);
853 }
854
855 #[test]
856 fn frame_load_store_roundtrip() {
857 let mut a = Asm::new();
860 a.mov_mr(Reg::Rdi, 8, Reg::Rsi);
861 a.mov_rm(Reg::Rax, Reg::Rdi, 8);
862 a.ret();
863 let mut frame = [0i64; 4];
864 let page = JitPage::new(&a.resolve()).unwrap();
865 let f: extern "C" fn(*mut i64, i64) -> i64 =
866 unsafe { std::mem::transmute(page.as_ptr()) };
867 let r = f(frame.as_mut_ptr(), 777);
868 assert_eq!(r, 777);
869 assert_eq!(frame[1], 777);
870 }
871
872 #[test]
873 fn idiv_signed_division() {
874 let mut a = Asm::new();
876 a.mov_rr(Reg::Rax, Reg::Rdi);
877 a.cqo();
878 a.idiv_r(Reg::Rsi);
879 a.ret();
880 let code = a.resolve();
881 assert_eq!(run2(&code, 100, 7), 14);
882 assert_eq!(run2(&code, -100, 7), -14);
883 }
884
885 #[test]
886 fn mul_r_high_product_unsigned() {
887 let mut a = Asm::new();
892 a.mov_rr(Reg::Rax, Reg::Rdi);
893 a.mul_r(Reg::Rsi); a.mov_rr(Reg::Rax, Reg::Rdx); a.ret();
896 let code = a.resolve();
897 assert_eq!(run2(&code, -1i64, 3) as u64, 2);
899 assert_eq!(run2(&code, 1i64 << 32, 1i64 << 32) as u64, 1);
901 assert_eq!(run2(&code, 123, 456) as u64, 0);
903 }
904
905 #[test]
906 fn shr_ri_is_logical_not_arithmetic() {
907 let mut a = Asm::new();
910 a.mov_rr(Reg::Rax, Reg::Rdi);
911 a.shr_ri(Reg::Rax, 1);
912 a.ret();
913 let code = a.resolve();
914 assert_eq!(run2(&code, -1i64, 0), i64::MAX);
915 assert_eq!(run2(&code, 1024, 0), 512);
916 let mut b = Asm::new();
918 b.mov_rr(Reg::Rax, Reg::Rdi);
919 b.shr_ri(Reg::Rax, 60);
920 b.ret();
921 assert_eq!(run2(&b.resolve(), -1i64, 0) as u64, 0xF);
922 }
923
924 #[test]
925 fn rsp_base_needs_sib() {
926 let mut a = Asm::new();
929 a.mov_rr(Reg::R12, Reg::Rdi);
930 a.mov_mr(Reg::R12, 16, Reg::Rsi);
931 a.mov_rm(Reg::Rax, Reg::R12, 16);
932 a.ret();
933 let mut frame = [0i64; 8];
934 let page = JitPage::new(&a.resolve()).unwrap();
935 let f: extern "C" fn(*mut i64, i64) -> i64 =
936 unsafe { std::mem::transmute(page.as_ptr()) };
937 let r = f(frame.as_mut_ptr(), 555);
938 assert_eq!(r, 555);
939 assert_eq!(frame[2], 555);
940 }
941
942 #[test]
943 fn shift_and_bitwise() {
944 let mut a = Asm::new();
946 a.mov_rr(Reg::Rax, Reg::Rdi);
947 a.mov_rr(Reg::Rcx, Reg::Rsi);
948 a.shl_cl(Reg::Rax);
949 a.ret();
950 let code = a.resolve();
951 assert_eq!(run2(&code, 1, 4), 16);
952 assert_eq!(run2(&code, 3, 2), 12);
953 }
954
955 #[test]
956 fn sub_immediate_and_shl_immediate() {
957 let mut a = Asm::new();
959 a.mov_rr(Reg::Rax, Reg::Rdi);
960 a.sub_ri(Reg::Rax, 1);
961 a.shl_ri(Reg::Rax, 3);
962 a.ret();
963 let code = a.resolve();
964 assert_eq!(run2(&code, 1, 0), 0); assert_eq!(run2(&code, 5, 0), 32); assert_eq!(run2(&code, 0, 0), -8); }
968
969 #[test]
970 fn unsigned_above_equal_branch() {
971 let mut a = Asm::new();
973 let oob = a.new_label();
974 a.cmp_rr(Reg::Rdi, Reg::Rsi);
975 a.jcc(Cond::AeU, oob);
976 a.mov_ri(Reg::Rax, 0);
977 a.ret();
978 a.bind(oob);
979 a.mov_ri(Reg::Rax, 1);
980 a.ret();
981 let code = a.resolve();
982 assert_eq!(run2(&code, 3, 4), 0); assert_eq!(run2(&code, 4, 4), 1); assert_eq!(run2(&code, -1, 4), 1); }
986
987 fn run_frame(code: &[u8], frame: &mut [i64]) -> i64 {
989 let page = JitPage::new(code).unwrap();
990 let f: extern "C" fn(*mut i64) -> i64 =
991 unsafe { std::mem::transmute(page.as_ptr()) };
992 f(frame.as_mut_ptr())
993 }
994
995 #[test]
996 fn sse_arithmetic_matches_ieee() {
997 for (a, b) in [(3.0f64, 4.0f64), (1.5, -2.25), (0.1, 0.2), (-7.0, 11.0)] {
999 let mut a_asm = Asm::new();
1000 a_asm.movsd_rm(Xmm::Xmm0, Reg::Rdi, 0); a_asm.movsd_rm(Xmm::Xmm1, Reg::Rdi, 8); a_asm.movsd_rr(Xmm::Xmm2, Xmm::Xmm0); a_asm.addsd_rr(Xmm::Xmm2, Xmm::Xmm1); a_asm.mulsd_rr(Xmm::Xmm2, Xmm::Xmm0); a_asm.subsd_rr(Xmm::Xmm2, Xmm::Xmm1); a_asm.divsd_rr(Xmm::Xmm2, Xmm::Xmm0); a_asm.movq_rx(Reg::Rax, Xmm::Xmm2);
1008 a_asm.ret();
1009 let mut frame = [a.to_bits() as i64, b.to_bits() as i64];
1010 let got = f64::from_bits(run_frame(&a_asm.resolve(), &mut frame) as u64);
1011 let want = ((a + b) * a - b) / a;
1012 assert_eq!(got.to_bits(), want.to_bits(), "a={a} b={b}");
1013 }
1014 }
1015
1016 #[test]
1017 fn sse_sqrt_and_cvtsi2sd() {
1018 for n in [0i64, 1, 2, 4, 9, 1_000_000, 123_456_789] {
1020 let mut a = Asm::new();
1021 a.mov_rm(Reg::Rax, Reg::Rdi, 0);
1022 a.cvtsi2sd(Xmm::Xmm0, Reg::Rax);
1023 a.sqrtsd_rr(Xmm::Xmm1, Xmm::Xmm0);
1024 a.movq_rx(Reg::Rax, Xmm::Xmm1);
1025 a.ret();
1026 let mut frame = [n];
1027 let got = f64::from_bits(run_frame(&a.resolve(), &mut frame) as u64);
1028 assert_eq!(got.to_bits(), (n as f64).sqrt().to_bits(), "n={n}");
1029 }
1030 }
1031
1032 #[test]
1033 fn sse_high_registers_route_rex() {
1034 for (a, b) in [(2.5f64, 0.5f64), (-1.0, 3.0)] {
1037 let mut asm = Asm::new();
1038 asm.movsd_rm(Xmm::Xmm8, Reg::Rdi, 0);
1039 asm.movsd_rm(Xmm::Xmm9, Reg::Rdi, 8);
1040 asm.addsd_rr(Xmm::Xmm8, Xmm::Xmm9);
1041 asm.movsd_mr(Reg::Rdi, 16, Xmm::Xmm8);
1042 asm.mov_rm(Reg::Rax, Reg::Rdi, 16);
1043 asm.ret();
1044 let mut frame = [a.to_bits() as i64, b.to_bits() as i64, 0];
1045 let got = f64::from_bits(run_frame(&asm.resolve(), &mut frame) as u64);
1046 assert_eq!(got.to_bits(), (a + b).to_bits(), "a={a} b={b}");
1047 }
1048 }
1049
1050 #[test]
1051 fn sse_packed_double_two_lanes_add_mul_sub() {
1052 let cases: [(f64, f64, f64, f64); 3] = [
1056 (2.5, 0.5, 1.0, 3.0),
1057 (-1.0, 7.25, 4.0, -2.5),
1058 (0.0, -0.0, 1.0, 1.0),
1059 ];
1060 for (a0, a1, b0, b1) in cases {
1061 let mut asm = Asm::new();
1062 asm.movupd_rm(Xmm::Xmm0, Reg::Rdi, 0); asm.movupd_rm(Xmm::Xmm1, Reg::Rdi, 16); asm.movupd_rr(Xmm::Xmm2, Xmm::Xmm0); asm.movupd_rr(Xmm::Xmm3, Xmm::Xmm0); asm.addpd_rr(Xmm::Xmm0, Xmm::Xmm1); asm.mulpd_rr(Xmm::Xmm2, Xmm::Xmm1); asm.subpd_rr(Xmm::Xmm3, Xmm::Xmm1); asm.movupd_mr(Reg::Rdi, 32, Xmm::Xmm0);
1070 asm.movupd_mr(Reg::Rdi, 48, Xmm::Xmm2);
1071 asm.movupd_mr(Reg::Rdi, 64, Xmm::Xmm3);
1072 asm.ret();
1073 let mut frame = [
1074 a0.to_bits() as i64,
1075 a1.to_bits() as i64,
1076 b0.to_bits() as i64,
1077 b1.to_bits() as i64,
1078 0, 0, 0, 0, 0, 0,
1079 ];
1080 run_frame(&asm.resolve(), &mut frame);
1081 let lane = |i: usize| f64::from_bits(frame[i] as u64);
1082 assert_eq!(lane(4).to_bits(), (a0 + b0).to_bits(), "add lane0");
1083 assert_eq!(lane(5).to_bits(), (a1 + b1).to_bits(), "add lane1");
1084 assert_eq!(lane(6).to_bits(), (a0 * b0).to_bits(), "mul lane0");
1085 assert_eq!(lane(7).to_bits(), (a1 * b1).to_bits(), "mul lane1");
1086 assert_eq!(lane(8).to_bits(), (a0 - b0).to_bits(), "sub lane0");
1087 assert_eq!(lane(9).to_bits(), (a1 - b1).to_bits(), "sub lane1");
1088 }
1089 }
1090
1091 #[test]
1092 fn sse_packed_high_registers_route_rex() {
1093 let cases: [(f64, f64, f64, f64); 2] = [(2.5, 0.5, 1.0, 3.0), (-1.0, 3.0, 4.0, -2.5)];
1095 for (a0, a1, b0, b1) in cases {
1096 let mut asm = Asm::new();
1097 asm.movupd_rm(Xmm::Xmm12, Reg::Rdi, 0);
1098 asm.movupd_rm(Xmm::Xmm13, Reg::Rdi, 16);
1099 asm.addpd_rr(Xmm::Xmm12, Xmm::Xmm13);
1100 asm.movupd_mr(Reg::Rdi, 32, Xmm::Xmm12);
1101 asm.ret();
1102 let mut frame = [
1103 a0.to_bits() as i64,
1104 a1.to_bits() as i64,
1105 b0.to_bits() as i64,
1106 b1.to_bits() as i64,
1107 0,
1108 0,
1109 ];
1110 run_frame(&asm.resolve(), &mut frame);
1111 assert_eq!(f64::from_bits(frame[4] as u64).to_bits(), (a0 + b0).to_bits());
1112 assert_eq!(f64::from_bits(frame[5] as u64).to_bits(), (a1 + b1).to_bits());
1113 }
1114 }
1115
1116 #[test]
1117 fn sse_packed_double_is_bit_identical_to_scalar() {
1118 let cases: [(f64, f64); 4] = [
1122 (0.1, 0.2),
1123 (1e308, 1e308),
1124 (f64::MIN_POSITIVE, f64::MIN_POSITIVE),
1125 (1.0 / 3.0, 7.0),
1126 ];
1127 for (a, b) in cases {
1128 let mut asm = Asm::new();
1129 asm.movupd_rm(Xmm::Xmm0, Reg::Rdi, 0);
1130 asm.movupd_rm(Xmm::Xmm1, Reg::Rdi, 16);
1131 asm.mulpd_rr(Xmm::Xmm0, Xmm::Xmm1);
1132 asm.movupd_mr(Reg::Rdi, 32, Xmm::Xmm0);
1133 asm.ret();
1134 let mut frame = [
1136 a.to_bits() as i64,
1137 a.to_bits() as i64,
1138 b.to_bits() as i64,
1139 b.to_bits() as i64,
1140 0,
1141 0,
1142 ];
1143 run_frame(&asm.resolve(), &mut frame);
1144 assert_eq!(f64::from_bits(frame[4] as u64).to_bits(), (a * b).to_bits());
1145 assert_eq!(f64::from_bits(frame[5] as u64).to_bits(), (a * b).to_bits());
1146 }
1147 }
1148
1149 #[test]
1150 fn sse_packed_div_and_sqrt_two_lanes() {
1151 let cases: [(f64, f64); 4] = [(9.0, 2.0), (1.0, 3.0), (-7.5, 0.25), (2.0, 8.0)];
1154 for (a, b) in cases {
1155 let mut asm = Asm::new();
1156 asm.movupd_rm(Xmm::Xmm0, Reg::Rdi, 0); asm.movupd_rm(Xmm::Xmm1, Reg::Rdi, 16); asm.divpd_rr(Xmm::Xmm0, Xmm::Xmm1); asm.sqrtpd_rr(Xmm::Xmm1, Xmm::Xmm1); asm.movupd_mr(Reg::Rdi, 32, Xmm::Xmm0);
1161 asm.movupd_mr(Reg::Rdi, 48, Xmm::Xmm1);
1162 asm.ret();
1163 let mut frame = [
1164 a.to_bits() as i64,
1165 a.to_bits() as i64,
1166 b.to_bits() as i64,
1167 b.to_bits() as i64,
1168 0, 0, 0, 0,
1169 ];
1170 run_frame(&asm.resolve(), &mut frame);
1171 assert_eq!(f64::from_bits(frame[4] as u64).to_bits(), (a / b).to_bits(), "div");
1172 assert_eq!(f64::from_bits(frame[6] as u64).to_bits(), b.sqrt().to_bits(), "sqrt");
1173 }
1174 }
1175
1176 #[test]
1177 fn sse_packed_cmple_and_movmskpd_lane_mask() {
1178 let cases: [(f64, f64, f64, f64, i64); 4] = [
1182 (1.0, 1.0, 4.0, 4.0, 0b11), (9.0, 1.0, 4.0, 4.0, 0b10), (1.0, 9.0, 4.0, 4.0, 0b01), (9.0, 9.0, 4.0, 4.0, 0b00), ];
1187 for (a0, a1, b0, b1, want) in cases {
1188 let mut asm = Asm::new();
1189 asm.movupd_rm(Xmm::Xmm0, Reg::Rdi, 0); asm.movupd_rm(Xmm::Xmm1, Reg::Rdi, 16); asm.cmppd_rr(Xmm::Xmm0, Xmm::Xmm1, 2); asm.movmskpd(Reg::Rax, Xmm::Xmm0);
1193 asm.ret();
1194 let mut frame = [
1195 a0.to_bits() as i64,
1196 a1.to_bits() as i64,
1197 b0.to_bits() as i64,
1198 b1.to_bits() as i64,
1199 ];
1200 assert_eq!(run_frame(&asm.resolve(), &mut frame), want, "a={a0},{a1}");
1201 }
1202 }
1203
1204 #[test]
1205 fn sse_packed_andpd_count_increment_trick() {
1206 let allones = u64::MAX as i64; let one = 1.0f64.to_bits() as i64;
1211 let cases: [(i64, i64, f64, f64); 3] = [
1212 (allones, allones, 1.0, 1.0),
1213 (allones, 0, 1.0, 0.0),
1214 (0, allones, 0.0, 1.0),
1215 ];
1216 for (m0, m1, want0, want1) in cases {
1217 let mut asm = Asm::new();
1218 asm.movupd_rm(Xmm::Xmm0, Reg::Rdi, 0); asm.movupd_rm(Xmm::Xmm1, Reg::Rdi, 16); asm.andpd_rr(Xmm::Xmm0, Xmm::Xmm1);
1221 asm.movupd_mr(Reg::Rdi, 32, Xmm::Xmm0);
1222 asm.ret();
1223 let mut frame = [m0, m1, one, one, 0, 0];
1224 run_frame(&asm.resolve(), &mut frame);
1225 assert_eq!(f64::from_bits(frame[4] as u64).to_bits(), want0.to_bits(), "lane0");
1226 assert_eq!(f64::from_bits(frame[5] as u64).to_bits(), want1.to_bits(), "lane1");
1227 }
1228 }
1229
1230 #[test]
1231 fn sse_packed_bitwise_or_andn_xor() {
1232 let a = 0xF0F0_F0F0_F0F0_F0F0u64 as i64;
1234 let b = 0x00FF_00FF_00FF_00FFu64 as i64;
1235 let mut asm = Asm::new();
1236 asm.movupd_rm(Xmm::Xmm0, Reg::Rdi, 0); asm.movupd_rm(Xmm::Xmm1, Reg::Rdi, 16); asm.movupd_rr(Xmm::Xmm2, Xmm::Xmm0);
1239 asm.movupd_rr(Xmm::Xmm3, Xmm::Xmm0);
1240 asm.orpd_rr(Xmm::Xmm0, Xmm::Xmm1); asm.andnpd_rr(Xmm::Xmm2, Xmm::Xmm1); asm.xorpd_rr(Xmm::Xmm3, Xmm::Xmm1); asm.movupd_mr(Reg::Rdi, 32, Xmm::Xmm0);
1244 asm.movupd_mr(Reg::Rdi, 48, Xmm::Xmm2);
1245 asm.movupd_mr(Reg::Rdi, 64, Xmm::Xmm3);
1246 asm.ret();
1247 let mut frame = [a, a, b, b, 0, 0, 0, 0, 0, 0];
1248 run_frame(&asm.resolve(), &mut frame);
1249 assert_eq!(frame[4] as u64, (a as u64) | (b as u64), "or");
1250 assert_eq!(frame[6] as u64, (!(a as u64)) & (b as u64), "andn");
1251 assert_eq!(frame[8] as u64, (a as u64) ^ (b as u64), "xor");
1252 }
1253
1254 #[test]
1255 fn simd_mandelbrot_pair_matches_scalar_membership_bit_exact() {
1256 fn inside_bench(cr: f64, ci: f64) -> bool {
1267 let mut zr = 0.0f64;
1268 let mut zi = 0.0f64;
1269 let mut is_inside = true;
1270 let mut iter = 0;
1271 while iter < 50 {
1272 let zr2 = zr * zr - zi * zi + cr;
1273 let zi2 = 2.0 * zr * zi + ci;
1274 zr = zr2;
1275 zi = zi2;
1276 if zr * zr + zi * zi > 4.0 {
1277 is_inside = false;
1278 iter = 50;
1279 }
1280 iter += 1;
1281 }
1282 is_inside
1283 }
1284
1285 let mut asm = Asm::new();
1288 asm.movupd_rm(Xmm::Xmm0, Reg::Rdi, 0); asm.movupd_rm(Xmm::Xmm1, Reg::Rdi, 16); asm.movupd_rm(Xmm::Xmm2, Reg::Rdi, 32); asm.movupd_rm(Xmm::Xmm3, Reg::Rdi, 48); asm.xorpd_rr(Xmm::Xmm4, Xmm::Xmm4); asm.xorpd_rr(Xmm::Xmm5, Xmm::Xmm5); asm.xorpd_rr(Xmm::Xmm6, Xmm::Xmm6);
1295 asm.cmppd_rr(Xmm::Xmm6, Xmm::Xmm6, 0); asm.mov_ri(Reg::Rcx, 0); let loop_top = asm.new_label();
1298 asm.bind(loop_top);
1299 asm.movupd_rr(Xmm::Xmm7, Xmm::Xmm4);
1301 asm.mulpd_rr(Xmm::Xmm7, Xmm::Xmm4); asm.movupd_rr(Xmm::Xmm8, Xmm::Xmm5);
1303 asm.mulpd_rr(Xmm::Xmm8, Xmm::Xmm5); asm.subpd_rr(Xmm::Xmm7, Xmm::Xmm8); asm.addpd_rr(Xmm::Xmm7, Xmm::Xmm0); asm.movupd_rr(Xmm::Xmm9, Xmm::Xmm4);
1308 asm.mulpd_rr(Xmm::Xmm9, Xmm::Xmm3); asm.mulpd_rr(Xmm::Xmm9, Xmm::Xmm5); asm.addpd_rr(Xmm::Xmm9, Xmm::Xmm1); asm.movupd_rr(Xmm::Xmm4, Xmm::Xmm7);
1313 asm.movupd_rr(Xmm::Xmm5, Xmm::Xmm9);
1314 asm.movupd_rr(Xmm::Xmm7, Xmm::Xmm4);
1316 asm.mulpd_rr(Xmm::Xmm7, Xmm::Xmm4);
1317 asm.movupd_rr(Xmm::Xmm8, Xmm::Xmm5);
1318 asm.mulpd_rr(Xmm::Xmm8, Xmm::Xmm5);
1319 asm.addpd_rr(Xmm::Xmm7, Xmm::Xmm8);
1320 asm.cmppd_rr(Xmm::Xmm7, Xmm::Xmm2, 2);
1322 asm.andpd_rr(Xmm::Xmm6, Xmm::Xmm7);
1323 let done = asm.new_label();
1328 asm.movmskpd(Reg::Rax, Xmm::Xmm6);
1329 asm.test_rr(Reg::Rax, Reg::Rax);
1330 asm.jcc(Cond::Eq, done); asm.add_ri(Reg::Rcx, 1);
1332 asm.cmp_ri(Reg::Rcx, 50);
1333 asm.jcc(Cond::Lt, loop_top);
1334 asm.bind(done);
1335 asm.ret(); let code = asm.resolve();
1337
1338 let n = 64i64;
1341 for y in 0..n {
1342 let ci = 2.0 * y as f64 / n as f64 - 1.0;
1343 for x in 0..(n - 1) {
1344 let cr0 = 2.0 * x as f64 / n as f64 - 1.5;
1345 let cr1 = 2.0 * (x + 1) as f64 / n as f64 - 1.5;
1346 let mut frame = [
1347 cr0.to_bits() as i64,
1348 cr1.to_bits() as i64,
1349 ci.to_bits() as i64,
1350 ci.to_bits() as i64,
1351 4.0f64.to_bits() as i64,
1352 4.0f64.to_bits() as i64,
1353 2.0f64.to_bits() as i64,
1354 2.0f64.to_bits() as i64,
1355 ];
1356 let got = run_frame(&code, &mut frame);
1357 let want = (inside_bench(cr0, ci) as i64)
1358 | ((inside_bench(cr1, ci) as i64) << 1);
1359 assert_eq!(got, want, "pixel pair x={x} y={y} cr0={cr0} cr1={cr1} ci={ci}");
1360 }
1361 }
1362 }
1363
1364 #[test]
1365 fn sse_ucomisd_ordering_and_nan() {
1366 let nan = f64::NAN;
1368 for (a, b, want) in [
1369 (2.0f64, 1.0, 1i64),
1370 (1.0, 2.0, 0),
1371 (3.0, 3.0, 0),
1372 (nan, 1.0, 0),
1373 (1.0, nan, 0),
1374 (nan, nan, 0),
1375 ] {
1376 let mut asm = Asm::new();
1377 asm.movsd_rm(Xmm::Xmm0, Reg::Rdi, 0);
1378 asm.movsd_rm(Xmm::Xmm1, Reg::Rdi, 8);
1379 asm.ucomisd_rr(Xmm::Xmm0, Xmm::Xmm1); asm.setcc_movzx(Cond::AU, Reg::Rax); asm.ret();
1382 let mut frame = [a.to_bits() as i64, b.to_bits() as i64];
1383 assert_eq!(run_frame(&asm.resolve(), &mut frame), want, "a={a} b={b}");
1384 }
1385 }
1386
1387 #[test]
1388 fn movq_bridges_gp_and_xmm_bit_exact() {
1389 for v in [0.0f64, -0.0, f64::NAN, f64::INFINITY, 1.5, -1e300] {
1391 let mut asm = Asm::new();
1392 asm.mov_rm(Reg::Rax, Reg::Rdi, 0);
1393 asm.movq_xr(Xmm::Xmm3, Reg::Rax);
1394 asm.movq_rx(Reg::Rax, Xmm::Xmm3);
1395 asm.ret();
1396 let mut frame = [v.to_bits() as i64];
1397 assert_eq!(run_frame(&asm.resolve(), &mut frame) as u64, v.to_bits(), "v={v:?}");
1398 }
1399 }
1400
1401 #[test]
1402 fn add_cmp_immediate() {
1403 let mut a = Asm::new();
1405 a.mov_rr(Reg::Rax, Reg::Rdi);
1406 a.add_ri(Reg::Rax, 7);
1407 a.ret();
1408 let code = a.resolve();
1409 assert_eq!(run2(&code, 35, 0), 42);
1410 assert_eq!(run2(&code, -7, 0), 0);
1411
1412 let mut b = Asm::new();
1414 b.cmp_ri(Reg::Rdi, 100);
1415 b.setcc_movzx(Cond::Ge, Reg::Rax);
1416 b.ret();
1417 let codeb = b.resolve();
1418 assert_eq!(run2(&codeb, 100, 0), 1);
1419 assert_eq!(run2(&codeb, 99, 0), 0);
1420 assert_eq!(run2(&codeb, 200, 0), 1);
1421 }
1422
1423 #[test]
1424 fn indirect_call_through_register() {
1425 let mut callee = Asm::new();
1429 callee.mov_rr(Reg::Rax, Reg::Rdi);
1430 callee.add_rr(Reg::Rax, Reg::Rdi);
1431 callee.add_rr(Reg::Rax, Reg::Rdi); callee.ret();
1433 let callee_page = JitPage::new(&callee.resolve()).expect("map callee");
1434 let callee_addr = callee_page.as_ptr() as i64;
1435
1436 let mut caller = Asm::new();
1437 caller.sub_ri(Reg::Rsp, 8);
1440 caller.mov_ri(Reg::R11, callee_addr);
1441 caller.call_r(Reg::R11); caller.add_ri(Reg::Rsp, 8);
1443 caller.ret();
1444 let code = caller.resolve();
1445 assert_eq!(run2(&code, 14, 0), 42);
1446 assert_eq!(run2(&code, -5, 0), -15);
1447 }
1448
1449 #[test]
1450 fn byte_load_zero_extends() {
1451 let mut a = Asm::new();
1454 a.mov_rr(Reg::R10, Reg::Rsi);
1455 a.sub_ri(Reg::R10, 1); a.add_rr(Reg::R10, Reg::Rdi); a.movzx_rm8(Reg::Rax, Reg::R10, 0);
1458 a.ret();
1459 let buf = [0u8, 1, 0xFF, 0x80, 7];
1460 let page = JitPage::new(&a.resolve()).unwrap();
1461 let f: extern "C" fn(*const u8, i64) -> i64 =
1462 unsafe { std::mem::transmute(page.as_ptr()) };
1463 assert_eq!(f(buf.as_ptr(), 1), 0);
1464 assert_eq!(f(buf.as_ptr(), 2), 1);
1465 assert_eq!(f(buf.as_ptr(), 3), 255); assert_eq!(f(buf.as_ptr(), 4), 128);
1467 assert_eq!(f(buf.as_ptr(), 5), 7);
1468 }
1469
1470 #[test]
1471 fn byte_store_low_byte_only() {
1472 let mut a = Asm::new();
1476 a.test_rr(Reg::Rsi, Reg::Rsi);
1477 a.setcc8(Cond::Ne, Reg::Rdx);
1478 a.mov_mr8(Reg::Rdi, 0, Reg::Rdx);
1479 a.mov_ri(Reg::Rax, 0);
1480 a.ret();
1481 let page = JitPage::new(&a.resolve()).unwrap();
1482 let f: extern "C" fn(*mut u8, i64) -> i64 =
1483 unsafe { std::mem::transmute(page.as_ptr()) };
1484 for (val, want) in [(0i64, 0u8), (1, 1), (256, 1), (-1, 1), (0xFF00_0000i64, 1)] {
1485 let mut cell = [0xAAu8];
1486 f(cell.as_mut_ptr(), val);
1487 assert_eq!(cell[0], want, "val={val}");
1488 }
1489 }
1490
1491 #[test]
1492 fn byte_store_through_rsp_class_base() {
1493 let mut a = Asm::new();
1496 a.mov_rr(Reg::R12, Reg::Rdi);
1497 a.mov_ri(Reg::R8, 1);
1498 a.mov_mr8(Reg::R12, 0, Reg::R8);
1499 a.mov_ri(Reg::Rax, 0);
1500 a.ret();
1501 let mut cell = [0u8, 9];
1502 let page = JitPage::new(&a.resolve()).unwrap();
1503 let f: extern "C" fn(*mut u8) -> i64 =
1504 unsafe { std::mem::transmute(page.as_ptr()) };
1505 f(cell.as_mut_ptr());
1506 assert_eq!(cell[0], 1);
1507 assert_eq!(cell[1], 9, "neighbor byte must be untouched (1-byte store)");
1508 }
1509
1510 #[test]
1511 fn byte_store_spl_class_register() {
1512 let mut a = Asm::new();
1515 a.mov_ri(Reg::Rsi, 0x77);
1516 a.mov_mr8(Reg::Rdi, 0, Reg::Rsi);
1517 a.mov_ri(Reg::Rax, 0);
1518 a.ret();
1519 let mut cell = [0u8];
1520 let page = JitPage::new(&a.resolve()).unwrap();
1521 let f: extern "C" fn(*mut u8) -> i64 =
1522 unsafe { std::mem::transmute(page.as_ptr()) };
1523 f(cell.as_mut_ptr());
1524 assert_eq!(cell[0], 0x77);
1525 }
1526
1527 #[test]
1528 fn computed_address_load_store() {
1529 let mut a = Asm::new();
1532 a.mov_rr(Reg::R10, Reg::Rsi); a.sub_ri(Reg::R10, 1); a.shl_ri(Reg::R10, 3); a.add_rr(Reg::R10, Reg::Rdi); a.mov_ri(Reg::R11, 0xABCD);
1537 a.mov_mr(Reg::R10, 0, Reg::R11); a.mov_rm(Reg::Rax, Reg::R10, 0); a.ret();
1540 let mut frame = [0i64; 8];
1541 let page = JitPage::new(&a.resolve()).unwrap();
1542 let f: extern "C" fn(*mut i64, i64) -> i64 =
1543 unsafe { std::mem::transmute(page.as_ptr()) };
1544 let r = f(frame.as_mut_ptr(), 3); assert_eq!(r, 0xABCD);
1546 assert_eq!(frame[2], 0xABCD);
1547 }
1548}