1 //! Tests for the emitter
2 //!
3 //! See comments at the top of `fn x64_emit` for advice on how to create reliable test cases.
4 //!
5 //! to see stdout: cargo test -- --nocapture
6 //!
7 //! for this specific case, as of 24 Aug 2020:
8 //!
9 //! cd to the top of your wasmtime tree, then:
10 //!
11 //! RUST_BACKTRACE=1 cargo test --features test-programs/test_programs \
12 //!   --all --exclude wasmtime-wasi-nn \
13 //!   -- isa::x64::inst::emit_tests::test_x64_emit
14 
15 use super::*;
16 use crate::ir::UserExternalNameRef;
17 use crate::isa::x64;
18 use crate::isa::x64::lower::isle::generated_code::{Atomic128RmwSeqOp, AtomicRmwSeqOp};
19 use alloc::vec::Vec;
20 use cranelift_entity::EntityRef as _;
21 
22 #[test]
test_x64_emit()23 fn test_x64_emit() {
24     let rax = regs::rax();
25     let rbx = regs::rbx();
26     let rcx = regs::rcx();
27     let rdx = regs::rdx();
28     let rsi = regs::rsi();
29     let _rdi = regs::rdi();
30     let rsp = regs::rsp();
31     let rbp = regs::rbp();
32     let r8 = regs::r8();
33     let r9 = regs::r9();
34     let r10 = regs::r10();
35     let r11 = regs::r11();
36     let r14 = regs::r14();
37     let r15 = regs::r15();
38 
39     let xmm0 = regs::xmm0();
40     let xmm1 = regs::xmm1();
41     let xmm2 = regs::xmm2();
42     let xmm3 = regs::xmm3();
43     let xmm4 = regs::xmm4();
44     let _xmm5 = regs::xmm5();
45     let xmm6 = regs::xmm6();
46     let xmm7 = regs::xmm7();
47     let xmm8 = regs::xmm8();
48     let xmm9 = regs::xmm9();
49     let xmm10 = regs::xmm10();
50     let xmm11 = regs::xmm11();
51     let xmm12 = regs::xmm12();
52     let _xmm13 = regs::xmm13();
53     let _xmm14 = regs::xmm14();
54     let _xmm15 = regs::xmm15();
55 
56     // And Writable<> versions of the same:
57     let w_rax = Writable::<Reg>::from_reg(rax);
58     let w_rbx = Writable::<Reg>::from_reg(rbx);
59     let w_rcx = Writable::<Reg>::from_reg(rcx);
60     let w_rdx = Writable::<Reg>::from_reg(rdx);
61     let _w_rsi = Writable::<Reg>::from_reg(rsi);
62     let _w_rsp = Writable::<Reg>::from_reg(rsp);
63     let _w_rbp = Writable::<Reg>::from_reg(rbp);
64     let _w_r8 = Writable::<Reg>::from_reg(r8);
65     let _w_r9 = Writable::<Reg>::from_reg(r9);
66     let w_r11 = Writable::<Reg>::from_reg(r11);
67     let _w_r14 = Writable::<Reg>::from_reg(r14);
68     let _w_r15 = Writable::<Reg>::from_reg(r15);
69 
70     let _w_xmm0 = Writable::<Reg>::from_reg(xmm0);
71     let _w_xmm1 = Writable::<Reg>::from_reg(xmm1);
72     let _w_xmm2 = Writable::<Reg>::from_reg(xmm2);
73     let _w_xmm3 = Writable::<Reg>::from_reg(xmm3);
74     let _w_xmm4 = Writable::<Reg>::from_reg(xmm4);
75     let _w_xmm6 = Writable::<Reg>::from_reg(xmm6);
76     let _w_xmm7 = Writable::<Reg>::from_reg(xmm7);
77     let _w_xmm8 = Writable::<Reg>::from_reg(xmm8);
78     let _w_xmm9 = Writable::<Reg>::from_reg(xmm9);
79     let _w_xmm10 = Writable::<Reg>::from_reg(xmm10);
80     let _w_xmm11 = Writable::<Reg>::from_reg(xmm11);
81     let _w_xmm12 = Writable::<Reg>::from_reg(xmm12);
82 
83     let mut insns = Vec::<(Inst, &str, &str)>::new();
84 
85     // End of test cases for Addr
86     // ========================================================
87 
88     // ========================================================
89     // General tests for each insn.  Don't forget to follow the
90     // guidelines commented just prior to `fn x64_emit`.
91     //
92 
93     // ========================================================
94     // CallKnown
95     insns.push((
96         Inst::call_known(Box::new(CallInfo::empty(
97             ExternalName::User(UserExternalNameRef::new(0)),
98             CallConv::SystemV,
99         ))),
100         "E800000000",
101         "call    User(userextname0)",
102     ));
103 
104     // ========================================================
105     // CallUnknown
106     fn call_unknown(rm: RegMem) -> Inst {
107         Inst::call_unknown(Box::new(CallInfo::empty(rm, CallConv::SystemV)))
108     }
109 
110     insns.push((call_unknown(RegMem::reg(rbp)), "FFD5", "call    *%rbp"));
111     insns.push((call_unknown(RegMem::reg(r11)), "41FFD3", "call    *%r11"));
112     insns.push((
113         call_unknown(RegMem::mem(Amode::imm_reg_reg_shift(
114             321,
115             Gpr::unwrap_new(rsi),
116             Gpr::unwrap_new(rcx),
117             3,
118         ))),
119         "FF94CE41010000",
120         "call    *321(%rsi,%rcx,8)",
121     ));
122     insns.push((
123         call_unknown(RegMem::mem(Amode::imm_reg_reg_shift(
124             321,
125             Gpr::unwrap_new(r10),
126             Gpr::unwrap_new(rdx),
127             2,
128         ))),
129         "41FF949241010000",
130         "call    *321(%r10,%rdx,4)",
131     ));
132 
133     // ========================================================
134     // LoadExtName
135     // N.B.: test harness below sets is_pic.
136     insns.push((
137         Inst::LoadExtName {
138             dst: Writable::from_reg(Gpr::R11),
139             name: Box::new(ExternalName::User(UserExternalNameRef::new(0))),
140             offset: 0,
141             distance: RelocDistance::Far,
142         },
143         "4C8B1D00000000",
144         "load_ext_name userextname0+0, %r11",
145     ));
146     insns.push((
147         Inst::LoadExtName {
148             dst: Writable::from_reg(Gpr::R11),
149             name: Box::new(ExternalName::User(UserExternalNameRef::new(0))),
150             offset: 0x12345678,
151             distance: RelocDistance::Far,
152         },
153         "4C8B1D000000004981C378563412",
154         "load_ext_name userextname0+305419896, %r11",
155     ));
156     insns.push((
157         Inst::LoadExtName {
158             dst: Writable::from_reg(Gpr::R11),
159             name: Box::new(ExternalName::User(UserExternalNameRef::new(0))),
160             offset: -0x12345678,
161             distance: RelocDistance::Far,
162         },
163         "4C8B1D000000004981C388A9CBED",
164         "load_ext_name userextname0+-305419896, %r11",
165     ));
166 
167     // ========================================================
168     // JmpKnown skipped for now
169 
170     // ========================================================
171     // JmpCondSymm isn't a real instruction
172 
173     // ========================================================
174     // JmpCond skipped for now
175 
176     // ========================================================
177     // JmpCondCompound isn't a real instruction
178 
179     // ========================================================
180     // Pertaining to atomics.
181     // Use `r9` with a 0 offset.
182     let am3: SyntheticAmode = Amode::imm_reg(0, r9).into();
183 
184     // AtomicRmwSeq
185     insns.push((
186         Inst::AtomicRmwSeq {
187             ty: types::I8,
188             op: AtomicRmwSeqOp::Or,
189             mem: am3.clone(),
190             operand: Gpr::unwrap_new(r10),
191             temp: w_r11.map(Gpr::unwrap_new),
192             dst_old: w_rax.map(Gpr::unwrap_new),
193         },
194         "490FB6014989C34D0BDAF0450FB0190F85EFFFFFFF",
195         "atomically { 8_bits_at_[%r9] Or= %r10; %rax = old_value_at_[%r9]; %r11, %rflags = trash }",
196     ));
197     insns.push((
198         Inst::AtomicRmwSeq {
199             ty: types::I16,
200             op: AtomicRmwSeqOp::And,
201             mem: am3.clone(),
202             operand: Gpr::unwrap_new(r10),
203             temp: w_r11.map(Gpr::unwrap_new),
204             dst_old: w_rax.map(Gpr::unwrap_new)
205         },
206         "490FB7014989C34D23DAF066450FB1190F85EEFFFFFF",
207         "atomically { 16_bits_at_[%r9] And= %r10; %rax = old_value_at_[%r9]; %r11, %rflags = trash }"
208     ));
209     insns.push((
210         Inst::AtomicRmwSeq {
211             ty: types::I32,
212             op: AtomicRmwSeqOp::Nand,
213             mem: am3.clone(),
214             operand: Gpr::unwrap_new(r10),
215             temp: w_r11.map(Gpr::unwrap_new),
216             dst_old: w_rax.map(Gpr::unwrap_new)
217         },
218         "418B014989C34D23DA49F7D3F0450FB1190F85ECFFFFFF",
219         "atomically { 32_bits_at_[%r9] Nand= %r10; %rax = old_value_at_[%r9]; %r11, %rflags = trash }"
220     ));
221     insns.push((
222         Inst::AtomicRmwSeq {
223             ty: types::I32,
224             op: AtomicRmwSeqOp::Umin,
225             mem: am3.clone(),
226             operand: Gpr::unwrap_new(r10),
227             temp: w_r11.map(Gpr::unwrap_new),
228             dst_old: w_rax.map(Gpr::unwrap_new)
229         },
230         "418B014989C34539DA4D0F46DAF0450FB1190F85EBFFFFFF",
231         "atomically { 32_bits_at_[%r9] Umin= %r10; %rax = old_value_at_[%r9]; %r11, %rflags = trash }"
232     ));
233     insns.push((
234         Inst::AtomicRmwSeq {
235             ty: types::I64,
236             op: AtomicRmwSeqOp::Smax,
237             mem: am3.clone(),
238             operand: Gpr::unwrap_new(r10),
239             temp: w_r11.map(Gpr::unwrap_new),
240             dst_old: w_rax.map(Gpr::unwrap_new)
241         },
242         "498B014989C34D39DA4D0F4DDAF04D0FB1190F85EBFFFFFF",
243         "atomically { 64_bits_at_[%r9] Smax= %r10; %rax = old_value_at_[%r9]; %r11, %rflags = trash }"
244     ));
245 
246     // Atomic128RmwSeq
247     let rmw_args = Atomic128RmwSeqArgs {
248         op: Atomic128RmwSeqOp::Or,
249         mem_low: am3.clone(),
250         mem_high: am3.offset(8).unwrap(),
251         operand_low: Gpr::unwrap_new(r10),
252         operand_high: Gpr::unwrap_new(r11),
253         temp_low: w_rbx.map(Gpr::unwrap_new),
254         temp_high: w_rcx.map(Gpr::unwrap_new),
255         dst_old_low: w_rax.map(Gpr::unwrap_new),
256         dst_old_high: w_rdx.map(Gpr::unwrap_new),
257     };
258     insns.push((
259         Inst::Atomic128RmwSeq {
260             args: Box::new(Atomic128RmwSeqArgs {
261                 op: Atomic128RmwSeqOp::Or,
262                 ..rmw_args.clone()
263             }),
264         },
265         "498B01498B51084889C34889D1490BDA490BCBF0490FC7090F85E9FFFFFF",
266         "atomically { %rdx:%rax = 0(%r9):8(%r9); %rcx:%rbx = %rdx:%rax Or %r11:%r10; 0(%r9):8(%r9) = %rcx:%rbx }"
267     ));
268     insns.push((
269         Inst::Atomic128RmwSeq {
270             args: Box::new(Atomic128RmwSeqArgs {
271                 op: Atomic128RmwSeqOp::And,
272                 ..rmw_args.clone()
273             }),
274         },
275         "498B01498B51084889C34889D14923DA4923CBF0490FC7090F85E9FFFFFF",
276         "atomically { %rdx:%rax = 0(%r9):8(%r9); %rcx:%rbx = %rdx:%rax And %r11:%r10; 0(%r9):8(%r9) = %rcx:%rbx }"
277     ));
278     insns.push((
279         Inst::Atomic128RmwSeq {
280             args: Box::new(Atomic128RmwSeqArgs {
281                 op: Atomic128RmwSeqOp::Umin,
282                 ..rmw_args.clone()
283             }),
284         },
285         "498B01498B51084889C34889D14C39D3491BCB4889D1490F43DA490F43CBF0490FC7090F85DEFFFFFF",
286         "atomically { %rdx:%rax = 0(%r9):8(%r9); %rcx:%rbx = %rdx:%rax Umin %r11:%r10; 0(%r9):8(%r9) = %rcx:%rbx }"
287     ));
288     insns.push((
289         Inst::Atomic128RmwSeq {
290             args: Box::new(Atomic128RmwSeqArgs {
291                 op: Atomic128RmwSeqOp::Add,
292                 ..rmw_args.clone()
293             }),
294         },
295         "498B01498B51084889C34889D14903DA4913CBF0490FC7090F85E9FFFFFF",
296         "atomically { %rdx:%rax = 0(%r9):8(%r9); %rcx:%rbx = %rdx:%rax Add %r11:%r10; 0(%r9):8(%r9) = %rcx:%rbx }",
297     ));
298     insns.push((
299         Inst::Atomic128XchgSeq {
300             args: Box::new(Atomic128XchgSeqArgs {
301                 mem_low: am3.clone(),
302                 mem_high: am3.offset(8).unwrap(),
303                 operand_low: Gpr::unwrap_new(rbx),
304                 operand_high: Gpr::unwrap_new(rcx),
305                 dst_old_low: w_rax.map(Gpr::unwrap_new),
306                 dst_old_high: w_rdx.map(Gpr::unwrap_new),
307             }),
308         },
309         "498B01498B5108F0490FC7090F85F5FFFFFF",
310         "atomically { %rdx:%rax = 0(%r9):8(%r9); 0(%r9):8(%r9) = %rcx:%rbx }",
311     ));
312 
313     // ========================================================
314     // Misc instructions.
315 
316     insns.push((
317         Inst::ElfTlsGetAddr {
318             symbol: ExternalName::User(UserExternalNameRef::new(0)),
319             dst: WritableGpr::from_writable_reg(w_rax).unwrap(),
320         },
321         "66488D3D00000000666648E800000000",
322         "%rax = elf_tls_get_addr User(userextname0)",
323     ));
324 
325     insns.push((
326         Inst::MachOTlsGetAddr {
327             symbol: ExternalName::User(UserExternalNameRef::new(0)),
328             dst: WritableGpr::from_writable_reg(w_rax).unwrap(),
329         },
330         "488B3D00000000FF17",
331         "%rax = macho_tls_get_addr User(userextname0)",
332     ));
333 
334     insns.push((
335         Inst::CoffTlsGetAddr {
336             symbol: ExternalName::User(UserExternalNameRef::new(0)),
337             dst: WritableGpr::from_writable_reg(w_rax).unwrap(),
338             tmp: WritableGpr::from_writable_reg(w_rcx).unwrap(),
339         },
340         "8B050000000065488B0C2558000000488B04C1488D8000000000",
341         "%rax = coff_tls_get_addr User(userextname0)",
342     ));
343 
344     // ========================================================
345     // Actually run the tests!
346     let ctrl_plane = &mut Default::default();
347     let constants = Default::default();
348     let mut flag_builder = settings::builder();
349     flag_builder.enable("is_pic").unwrap();
350     let flags = settings::Flags::new(flag_builder);
351 
352     use crate::settings::Configurable;
353     let mut isa_flag_builder = x64::settings::builder();
354     isa_flag_builder.enable("has_cmpxchg16b").unwrap();
355     isa_flag_builder.enable("has_ssse3").unwrap();
356     isa_flag_builder.enable("has_sse41").unwrap();
357     isa_flag_builder.enable("has_fma").unwrap();
358     isa_flag_builder.enable("has_avx").unwrap();
359     isa_flag_builder.enable("has_avx512bitalg").unwrap();
360     isa_flag_builder.enable("has_avx512dq").unwrap();
361     isa_flag_builder.enable("has_avx512f").unwrap();
362     isa_flag_builder.enable("has_avx512vbmi").unwrap();
363     isa_flag_builder.enable("has_avx512vl").unwrap();
364     let isa_flags = x64::settings::Flags::new(&flags, &isa_flag_builder);
365 
366     let emit_info = EmitInfo::new(flags, isa_flags);
367     for (insn, expected_encoding, expected_printing) in insns {
368         // Check the printed text is as expected.
369         let actual_printing = insn.pretty_print_inst(&mut Default::default());
370         assert_eq!(expected_printing, actual_printing.trim());
371         let mut buffer = MachBuffer::new();
372 
373         insn.emit(&mut buffer, &emit_info, &mut Default::default());
374 
375         // Allow one label just after the instruction (so the offset is 0).
376         let label = buffer.get_label();
377         buffer.bind_label(label, ctrl_plane);
378 
379         let buffer = buffer.finish(&constants, ctrl_plane);
380         let actual_encoding = &buffer.stringify_code_bytes();
381         assert_eq!(expected_encoding, actual_encoding, "{expected_printing}");
382     }
383 }
384