From 4233e7aca75f0d9d3d5059d0c7a3b26cb1ed58db Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Mon, 10 May 2021 10:50:56 -0700 Subject: [PATCH 1/4] x64: move encodings to a separate module In order to benchmark the encoding code with criterion, the functions and structures must be public. Moving this code to its own module (instead of keeping as a submodule to `inst`), allows `inst` to remain private. This avoids having to expose and document (or ignore documenting) the numerous instruction variants in `inst` while allowing access to the encoding code. This commit changes no functionality. --- .../src/isa/x64/{inst => }/encoding/evex.rs | 21 ++++++++++++------- .../src/isa/x64/{inst => }/encoding/mod.rs | 3 +++ .../src/isa/x64/{inst => }/encoding/rex.rs | 4 +++- .../src/isa/x64/{inst => }/encoding/vex.rs | 0 cranelift/codegen/src/isa/x64/inst/emit.rs | 12 +++++------ cranelift/codegen/src/isa/x64/inst/mod.rs | 3 +-- cranelift/codegen/src/isa/x64/mod.rs | 1 + 7 files changed, 28 insertions(+), 16 deletions(-) rename cranelift/codegen/src/isa/x64/{inst => }/encoding/evex.rs (96%) rename cranelift/codegen/src/isa/x64/{inst => }/encoding/mod.rs (87%) rename cranelift/codegen/src/isa/x64/{inst => }/encoding/rex.rs (99%) rename cranelift/codegen/src/isa/x64/{inst => }/encoding/vex.rs (100%) diff --git a/cranelift/codegen/src/isa/x64/inst/encoding/evex.rs b/cranelift/codegen/src/isa/x64/encoding/evex.rs similarity index 96% rename from cranelift/codegen/src/isa/x64/inst/encoding/evex.rs rename to cranelift/codegen/src/isa/x64/encoding/evex.rs index 9029b9f43e08..80a3c86bda86 100644 --- a/cranelift/codegen/src/isa/x64/inst/encoding/evex.rs +++ b/cranelift/codegen/src/isa/x64/encoding/evex.rs @@ -208,6 +208,8 @@ impl EvexInstruction { } } +/// Describe the register index to use. This wrapper is a type-safe way to pass +/// around the registers defined in `inst/regs.rs`. #[derive(Copy, Clone, Default)] pub struct Register(u8); impl From for Register { @@ -216,13 +218,18 @@ impl From for Register { Self(reg) } } +impl Into for Register { + fn into(self) -> u8 { + self.0 + } +} /// Defines the EVEX context for the `L'`, `L`, and `b` bits (bits 6:4 of EVEX P2 byte). Table 2-36 in /// section 2.6.10 (Intel Software Development Manual, volume 2A) describes how these bits can be /// used together for certain classes of instructions; i.e., special care should be taken to ensure /// that instructions use an applicable correct `EvexContext`. Table 2-39 contains cases where /// opcodes can result in an #UD. -#[allow(dead_code)] // Rounding and broadcast modes are not yet used. +#[allow(dead_code, missing_docs)] // Rounding and broadcast modes are not yet used. pub enum EvexContext { RoundingRegToRegFP { rc: EvexRoundingControl, @@ -250,7 +257,7 @@ impl Default for EvexContext { impl EvexContext { /// Encode the `L'`, `L`, and `b` bits (bits 6:4 of EVEX P2 byte) for merging with the P2 byte. - fn bits(&self) -> u8 { + pub fn bits(&self) -> u8 { match self { Self::RoundingRegToRegFP { rc } => 0b001 | rc.bits() << 1, Self::NoRoundingFP { sae, length } => (*sae as u8) | length.bits() << 1, @@ -261,7 +268,7 @@ impl EvexContext { } /// The EVEX format allows choosing a vector length in the `L'` and `L` bits; see `EvexContext`. -#[allow(dead_code)] // Wider-length vectors are not yet used. +#[allow(dead_code, missing_docs)] // Wider-length vectors are not yet used. pub enum EvexVectorLength { V128, V256, @@ -287,7 +294,7 @@ impl Default for EvexVectorLength { } /// The EVEX format allows defining rounding control in the `L'` and `L` bits; see `EvexContext`. -#[allow(dead_code)] // Rounding controls are not yet used. +#[allow(dead_code, missing_docs)] // Rounding controls are not yet used. pub enum EvexRoundingControl { RNE, RD, @@ -309,7 +316,7 @@ impl EvexRoundingControl { /// Defines the EVEX masking behavior; masking support is described in section 2.6.4 of the Intel /// Software Development Manual, volume 2A. -#[allow(dead_code)] // Masking is not yet used. +#[allow(dead_code, missing_docs)] // Masking is not yet used. pub enum EvexMasking { None, Merging { k: u8 }, @@ -324,7 +331,7 @@ impl Default for EvexMasking { impl EvexMasking { /// Encode the `z` bit for merging with the P2 byte. - fn z_bit(&self) -> u8 { + pub fn z_bit(&self) -> u8 { match self { Self::None | Self::Merging { .. } => 0, Self::Zeroing { .. } => 1, @@ -332,7 +339,7 @@ impl EvexMasking { } /// Encode the `aaa` bits for merging with the P2 byte. - fn aaa_bits(&self) -> u8 { + pub fn aaa_bits(&self) -> u8 { match self { Self::None => 0b000, Self::Merging { k } | Self::Zeroing { k } => { diff --git a/cranelift/codegen/src/isa/x64/inst/encoding/mod.rs b/cranelift/codegen/src/isa/x64/encoding/mod.rs similarity index 87% rename from cranelift/codegen/src/isa/x64/inst/encoding/mod.rs rename to cranelift/codegen/src/isa/x64/encoding/mod.rs index a269e586094f..9dd269764989 100644 --- a/cranelift/codegen/src/isa/x64/inst/encoding/mod.rs +++ b/cranelift/codegen/src/isa/x64/encoding/mod.rs @@ -1,3 +1,4 @@ +//! Contains the encoding machinery for the various x64 instruction formats. use crate::{isa::x64, machinst::MachBuffer}; use std::vec::Vec; @@ -5,6 +6,8 @@ pub mod evex; pub mod rex; pub mod vex; +/// The encoding formats in this module all require a way of placing bytes into +/// a buffer. pub trait ByteSink { /// Add 1 byte to the code section. fn put1(&mut self, _: u8); diff --git a/cranelift/codegen/src/isa/x64/inst/encoding/rex.rs b/cranelift/codegen/src/isa/x64/encoding/rex.rs similarity index 99% rename from cranelift/codegen/src/isa/x64/inst/encoding/rex.rs rename to cranelift/codegen/src/isa/x64/encoding/rex.rs index 648941790e50..51016fa39e70 100644 --- a/cranelift/codegen/src/isa/x64/inst/encoding/rex.rs +++ b/cranelift/codegen/src/isa/x64/encoding/rex.rs @@ -28,8 +28,9 @@ pub(crate) fn low8_will_sign_extend_to_32(x: u32) -> bool { xs == ((xs << 24) >> 24) } +/// Encode the ModR/M byte. #[inline(always)] -pub(crate) fn encode_modrm(m0d: u8, enc_reg_g: u8, rm_e: u8) -> u8 { +pub fn encode_modrm(m0d: u8, enc_reg_g: u8, rm_e: u8) -> u8 { debug_assert!(m0d < 4); debug_assert!(enc_reg_g < 8); debug_assert!(rm_e < 8); @@ -155,6 +156,7 @@ impl From<(OperandSize, Reg)> for RexFlags { /// Allows using the same opcode byte in different "opcode maps" to allow for more instruction /// encodings. See appendix A in the Intel Software Developer's Manual, volume 2A, for more details. +#[allow(missing_docs)] pub enum OpcodeMap { None, _0F, diff --git a/cranelift/codegen/src/isa/x64/inst/encoding/vex.rs b/cranelift/codegen/src/isa/x64/encoding/vex.rs similarity index 100% rename from cranelift/codegen/src/isa/x64/inst/encoding/vex.rs rename to cranelift/codegen/src/isa/x64/encoding/vex.rs diff --git a/cranelift/codegen/src/isa/x64/inst/emit.rs b/cranelift/codegen/src/isa/x64/inst/emit.rs index a35bbe0a9994..0bd74ecd8ba4 100644 --- a/cranelift/codegen/src/isa/x64/inst/emit.rs +++ b/cranelift/codegen/src/isa/x64/inst/emit.rs @@ -2,16 +2,16 @@ use crate::binemit::{Addend, Reloc}; use crate::ir::immediates::{Ieee32, Ieee64}; use crate::ir::LibCall; use crate::ir::TrapCode; -use crate::isa::x64::inst::args::*; -use crate::isa::x64::inst::*; -use crate::machinst::{inst_common, MachBuffer, MachInstEmit, MachLabel}; -use core::convert::TryInto; -use encoding::evex::{EvexInstruction, EvexVectorLength}; -use encoding::rex::{ +use crate::isa::x64::encoding::evex::{EvexInstruction, EvexVectorLength}; +use crate::isa::x64::encoding::rex::{ emit_simm, emit_std_enc_enc, emit_std_enc_mem, emit_std_reg_mem, emit_std_reg_reg, int_reg_enc, low8_will_sign_extend_to_32, low8_will_sign_extend_to_64, reg_enc, LegacyPrefixes, OpcodeMap, RexFlags, }; +use crate::isa::x64::inst::args::*; +use crate::isa::x64::inst::*; +use crate::machinst::{inst_common, MachBuffer, MachInstEmit, MachLabel}; +use core::convert::TryInto; use log::debug; use regalloc::{Reg, Writable}; diff --git a/cranelift/codegen/src/isa/x64/inst/mod.rs b/cranelift/codegen/src/isa/x64/inst/mod.rs index cfb0351bf38c..fe89ac4c9009 100644 --- a/cranelift/codegen/src/isa/x64/inst/mod.rs +++ b/cranelift/codegen/src/isa/x64/inst/mod.rs @@ -22,7 +22,6 @@ pub mod args; mod emit; #[cfg(test)] mod emit_tests; -pub(crate) mod encoding; pub mod regs; pub mod unwind; @@ -2856,7 +2855,7 @@ impl EmitState { self.stack_map = None; } - fn cur_srcloc(&self) -> SourceLoc { + pub(crate) fn cur_srcloc(&self) -> SourceLoc { self.cur_srcloc } } diff --git a/cranelift/codegen/src/isa/x64/mod.rs b/cranelift/codegen/src/isa/x64/mod.rs index c150c01f238e..6ca4a7cec4f7 100644 --- a/cranelift/codegen/src/isa/x64/mod.rs +++ b/cranelift/codegen/src/isa/x64/mod.rs @@ -18,6 +18,7 @@ use target_lexicon::Triple; use crate::isa::unwind::systemv; mod abi; +pub mod encoding; mod inst; mod lower; mod settings; From 23c5d3979bd9e7da073e0936d0172f5af2f46a86 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Mon, 10 May 2021 10:55:35 -0700 Subject: [PATCH 2/4] x64: make the x64 module public In order to benchmark portions of the x64 module, this change makes it a public module of cranelift-codegen. --- cranelift/codegen/src/isa/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cranelift/codegen/src/isa/mod.rs b/cranelift/codegen/src/isa/mod.rs index f2252e256226..481c0c8de310 100644 --- a/cranelift/codegen/src/isa/mod.rs +++ b/cranelift/codegen/src/isa/mod.rs @@ -81,8 +81,10 @@ mod riscv; #[cfg(feature = "x86")] mod x86; +// This module is made public here for benchmarking purposes. No guarantees are +// made regarding API stability. #[cfg(feature = "x86")] -mod x64; +pub mod x64; #[cfg(feature = "arm32")] mod arm32; From 52c5d0b8f3dc0d92c21038a27697847a6c8b165b Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Mon, 10 May 2021 11:05:07 -0700 Subject: [PATCH 3/4] x64: add benchmarks for EVEX encoding This change adds a criterion-enabled benchmark, x64-evex-encoding, to compare the performance of the builder pattern used to encode EVEX instructions in the new x64 backend against the function pattern used to encode EVEX instructions in the legacy x86 backend. At face value, the results imply that the builder pattern is faster, but no efforts were made to analyze and optimize these approaches further. --- Cargo.lock | 268 +++++++++++++++++- cranelift/codegen/Cargo.toml | 7 + .../codegen/benches/x64-evex-encoding.rs | 138 +++++++++ 3 files changed, 402 insertions(+), 11 deletions(-) create mode 100644 cranelift/codegen/benches/x64-evex-encoding.rs diff --git a/Cargo.lock b/Cargo.lock index 6a2815313ac7..aad4a2bc11d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -250,6 +250,18 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bstr" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a40b47ad93e1a5404e6c18dec46b628214fee441c70f4ab5d6942142cc268a3d" +dependencies = [ + "lazy_static", + "memchr", + "regex-automata", + "serde", +] + [[package]] name = "bumpalo" version = "3.6.1" @@ -276,7 +288,7 @@ checksum = "ff3a1e32332db9ad29d6da34693ce9a7ac26a9edf96abb5c1788d193410031ab" dependencies = [ "cap-primitives", "cap-std", - "rustc_version", + "rustc_version 0.3.3", "unsafe-io", "winapi", ] @@ -294,7 +306,7 @@ dependencies = [ "maybe-owned", "once_cell", "posish", - "rustc_version", + "rustc_version 0.3.3", "unsafe-io", "winapi", "winapi-util", @@ -318,7 +330,7 @@ checksum = "7019d48ea53c5f378e0fdab0fe5f627fc00e76d65e75dffd6fb1cbc0c9b382ee" dependencies = [ "cap-primitives", "posish", - "rustc_version", + "rustc_version 0.3.3", "unsafe-io", ] @@ -365,6 +377,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cast" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc38c385bfd7e444464011bb24820f40dd1c76bcdfa1b78611cb7c2e5cafab75" +dependencies = [ + "rustc_version 0.2.3", +] + [[package]] name = "cc" version = "1.0.67" @@ -542,6 +563,7 @@ dependencies = [ "cranelift-codegen-meta", "cranelift-codegen-shared", "cranelift-entity", + "criterion", "gimli", "hashbrown", "log", @@ -748,7 +770,7 @@ dependencies = [ "cranelift-entity", "cranelift-frontend", "hashbrown", - "itertools", + "itertools 0.10.0", "log", "serde", "smallvec", @@ -767,6 +789,42 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "criterion" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab327ed7354547cc2ef43cbe20ef68b988e70b4b593cbd66a2a61733123a3d23" +dependencies = [ + "atty", + "cast", + "clap", + "criterion-plot", + "csv", + "itertools 0.10.0", + "lazy_static", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_cbor", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022feadec601fba1649cfa83586381a4ad31c6bf3a9ab7d408118b05dd9889d" +dependencies = [ + "cast", + "itertools 0.9.0", +] + [[package]] name = "crossbeam-channel" version = "0.5.0" @@ -822,6 +880,28 @@ dependencies = [ "subtle", ] +[[package]] +name = "csv" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" +dependencies = [ + "bstr", + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +dependencies = [ + "memchr", +] + [[package]] name = "ctr" version = "0.6.0" @@ -1252,6 +1332,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "half" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3" + [[package]] name = "hashbrown" version = "0.9.1" @@ -1369,6 +1455,15 @@ dependencies = [ "syn", ] +[[package]] +name = "itertools" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.10.0" @@ -1402,6 +1497,15 @@ dependencies = [ "libc", ] +[[package]] +name = "js-sys" +version = "0.3.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83bdfbace3a0e81a4253f73b49e960b053e396a11012cbd49b9b74d6a2b67062" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "k256" version = "0.7.2" @@ -1478,7 +1582,7 @@ dependencies = [ "dynasm", "dynasmrt", "iter-enum", - "itertools", + "itertools 0.10.0", "lazy_static", "memoffset", "more-asserts", @@ -1747,6 +1851,12 @@ version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + [[package]] name = "opaque-debug" version = "0.3.0" @@ -1986,6 +2096,34 @@ dependencies = [ "zeroize", ] +[[package]] +name = "plotters" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45ca0ae5f169d0917a7c7f5a9c1a3d3d9598f18f529dd2b8373ed988efea307a" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b07fffcddc1cb3a1de753caa4e4df03b79922ba43cf882acc1bdd7e8df9f4590" + +[[package]] +name = "plotters-svg" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b38a02e23bd9604b842a812063aec4ef702b57989c37b655254bb61c471ad211" +dependencies = [ + "plotters-backend", +] + [[package]] name = "poly1305" version = "0.6.2" @@ -2420,13 +2558,22 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver 0.9.0", +] + [[package]] name = "rustc_version" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" dependencies = [ - "semver", + "semver 0.11.0", ] [[package]] @@ -2482,15 +2629,30 @@ dependencies = [ "syn", ] +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser 0.7.0", +] + [[package]] name = "semver" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" dependencies = [ - "semver-parser", + "semver-parser 0.10.2", ] +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + [[package]] name = "semver-parser" version = "0.10.2" @@ -2509,6 +2671,16 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde_cbor" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e18acfa2f90e8b735b2836ab8d538de304cbb6729a7360729ea5a895d15a622" +dependencies = [ + "half", + "serde", +] + [[package]] name = "serde_derive" version = "1.0.123" @@ -2696,9 +2868,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.60" +version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" +checksum = "6498a9efc342871f91cc2d0d694c674368b4ceb40f62b65a7a08c3792935e702" dependencies = [ "proc-macro2", "quote", @@ -2728,7 +2900,7 @@ dependencies = [ "cap-fs-ext", "cap-std", "posish", - "rustc_version", + "rustc_version 0.3.3", "unsafe-io", "winapi", "winx", @@ -2840,6 +3012,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "tokio" version = "1.5.0" @@ -3022,7 +3204,7 @@ version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe39acfe60d3754452ea6881613c3240100b23ffd94a627c138863f8cd314b1b" dependencies = [ - "rustc_version", + "rustc_version 0.3.3", "winapi", ] @@ -3200,6 +3382,60 @@ dependencies = [ "winapi", ] +[[package]] +name = "wasm-bindgen" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54ee1d4ed486f78874278e63e4069fc1ab9f6a18ca492076ffb90c5eb2997fd" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b33f6a0694ccfea53d94db8b2ed1c3a8a4c86dd936b13b9f0a15ec4a451b900" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "088169ca61430fe1e58b8096c24975251700e7b1f6fd91cc9d59b04fb9b18bd4" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be2241542ff3d9f241f5e2cb6dd09b37efe786df8851c54957683a49f0987a97" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7cff876b8f18eed75a66cf49b65e7f967cb354a7aa16003fb55dbfd25b44b4f" + [[package]] name = "wasm-encoder" version = "0.4.1" @@ -3700,6 +3936,16 @@ dependencies = [ "wast 35.0.2", ] +[[package]] +name = "web-sys" +version = "0.3.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e828417b379f3df7111d3a2a9e5753706cae29c41f7c4029ee9fd77f3e09e582" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "wiggle" version = "0.26.0" diff --git a/cranelift/codegen/Cargo.toml b/cranelift/codegen/Cargo.toml index 5e21f11a19f2..2f35b5fe4b78 100644 --- a/cranelift/codegen/Cargo.toml +++ b/cranelift/codegen/Cargo.toml @@ -34,6 +34,9 @@ wast = { version = "35.0.0", optional = true } # machine code. Integration tests that need external dependencies can be # accomodated in `tests`. +[dev-dependencies] +criterion = "0.3" + [build-dependencies] cranelift-codegen-meta = { path = "meta", version = "0.73.0" } @@ -103,3 +106,7 @@ souper-harvest = ["souper-ir", "souper-ir/stringify"] [badges] maintenance = { status = "experimental" } + +[[bench]] +name = "x64-evex-encoding" +harness = false diff --git a/cranelift/codegen/benches/x64-evex-encoding.rs b/cranelift/codegen/benches/x64-evex-encoding.rs new file mode 100644 index 000000000000..e5c1434181cf --- /dev/null +++ b/cranelift/codegen/benches/x64-evex-encoding.rs @@ -0,0 +1,138 @@ +//! Measure instruction encoding latency using various approaches; the +//! benchmarking is feature-gated on `x86` since it only measures the encoding +//! mechanism of that backend. + +#[cfg(feature = "x86")] +mod x86 { + use cranelift_codegen::isa::x64::encoding::{ + evex::{EvexContext, EvexInstruction, EvexMasking, EvexVectorLength, Register}, + rex::OpcodeMap, + rex::{encode_modrm, LegacyPrefixes}, + ByteSink, + }; + use cranelift_codegen_shared::isa::x86::EncodingBits; + use criterion::{criterion_group, Criterion}; + + // Define the benchmarks. + fn x64_evex_encoding_benchmarks(c: &mut Criterion) { + let mut group = c.benchmark_group("x64 EVEX encoding"); + let rax = Register::from(0); + let rdx = Register::from(2); + + group.bench_function("EvexInstruction (builder pattern)", |b| { + let mut sink = vec![]; + b.iter(|| { + sink.clear(); + EvexInstruction::new() + .prefix(LegacyPrefixes::_66) + .map(OpcodeMap::_0F38) + .w(true) + .opcode(0x1F) + .reg(rax) + .rm(rdx) + .length(EvexVectorLength::V128) + .encode(&mut sink); + }); + }); + + group.bench_function("encode_evex (function pattern)", |b| { + let mut sink = vec![]; + let bits = EncodingBits::new(&[0x66, 0x0f, 0x38, 0x1f], 0, 1); + let vvvvv = Register::from(0); + b.iter(|| { + sink.clear(); + encode_evex( + bits, + rax, + vvvvv, + rdx, + EvexContext::Other { + length: EvexVectorLength::V128, + }, + EvexMasking::default(), + &mut sink, + ); + }) + }); + } + criterion_group!(benches, x64_evex_encoding_benchmarks); + + /// Using an inner module to feature-gate the benchmarks means that we must + /// manually specify how to run the benchmarks (see `criterion_main!`). + pub fn run_benchmarks() { + criterion::__warn_about_html_reports_feature(); + criterion::__warn_about_cargo_bench_support_feature(); + benches(); + Criterion::default().configure_from_args().final_summary(); + } + + /// From the legacy x86 backend: a mechanism for encoding an EVEX + /// instruction, including the prefixes, the instruction opcode, and the + /// ModRM byte. This EVEX encoding function only encodes the `reg` (operand + /// 1), `vvvv` (operand 2), `rm` (operand 3) form; other forms are possible + /// (see section 2.6.2, Intel Software Development Manual, volume 2A), + /// requiring refactoring of this function or separate functions for each + /// form (e.g. as for the REX prefix). + #[inline(always)] + pub fn encode_evex( + enc: EncodingBits, + reg: Register, + vvvvv: Register, + rm: Register, + context: EvexContext, + masking: EvexMasking, + sink: &mut CS, + ) { + let reg: u8 = reg.into(); + let rm: u8 = rm.into(); + let vvvvv: u8 = vvvvv.into(); + + // EVEX prefix. + sink.put1(0x62); + + debug_assert!(enc.mm() < 0b100); + let mut p0 = enc.mm() & 0b11; + p0 |= evex2(rm, reg) << 4; // bits 3:2 are always unset + sink.put1(p0); + + let mut p1 = enc.pp() | 0b100; // bit 2 is always set + p1 |= (!(vvvvv) & 0b1111) << 3; + p1 |= (enc.rex_w() & 0b1) << 7; + sink.put1(p1); + + let mut p2 = masking.aaa_bits(); + p2 |= (!(vvvvv >> 4) & 0b1) << 3; + p2 |= context.bits() << 4; + p2 |= masking.z_bit() << 7; + sink.put1(p2); + + // Opcode. + sink.put1(enc.opcode_byte()); + + // ModR/M byte. + sink.put1(encode_modrm(3, reg & 7, rm & 7)) + } + + /// From the legacy x86 backend: encode the RXBR' bits of the EVEX P0 byte. + /// For an explanation of these bits, see section 2.6.1 in the Intel + /// Software Development Manual, volume 2A. These bits can be used by + /// different addressing modes (see section 2.6.2), requiring different + /// `vex*` functions than this one. + fn evex2(rm: u8, reg: u8) -> u8 { + let b = !(rm >> 3) & 1; + let x = !(rm >> 4) & 1; + let r = !(reg >> 3) & 1; + let r_ = !(reg >> 4) & 1; + 0x00 | r_ | (b << 1) | (x << 2) | (r << 3) + } +} + +fn main() { + #[cfg(feature = "x86")] + x86::run_benchmarks(); + + #[cfg(not(feature = "x86"))] + println!( + "Unable to run the x64-evex-encoding benchmark; the `x86` feature must be enabled in Cargo.", + ); +} From eeccbc9c100060303ff798fff9dd7253e362dc41 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Tue, 11 May 2021 10:17:09 -0700 Subject: [PATCH 4/4] Temporarily ignore multiple versions of criterion's build dependencies Until https://github.com/japaric/cast.rs/pull/26 is resolved, the `cast` crate will pull in older versions of the `rustc_version`, `semver`, and `semver-parser` crates. `cast` is a build dependency of `criterion` which is used for benchmarking and is itself a dev dependency, not a normal dependency. --- deny.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/deny.toml b/deny.toml index 7f909d470ecd..a08d5812160e 100644 --- a/deny.toml +++ b/deny.toml @@ -45,4 +45,7 @@ skip = [ { name = "wast" }, # old one pulled in by witx { name = "itertools" }, # 0.9 pulled in by zstd-sys { name = "quick-error" }, # transitive dependencies + { name = "rustc_version" }, # transitive dependencies of criterion's build script (see https://github.com/japaric/cast.rs/pull/26) + { name = "semver" }, # transitive dependencies of criterion's build script (see https://github.com/japaric/cast.rs/pull/26) + { name = "semver-parser" }, # transitive dependencies of criterion's build script (see https://github.com/japaric/cast.rs/pull/26) ]