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