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