diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/cranelift-codegen-shared/src | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/cranelift-codegen-shared/src')
7 files changed, 970 insertions, 0 deletions
diff --git a/third_party/rust/cranelift-codegen-shared/src/condcodes.rs b/third_party/rust/cranelift-codegen-shared/src/condcodes.rs new file mode 100644 index 0000000000..03ae865ce4 --- /dev/null +++ b/third_party/rust/cranelift-codegen-shared/src/condcodes.rs @@ -0,0 +1,405 @@ +//! Condition codes for the Cranelift code generator. +//! +//! A condition code here is an enumerated type that determined how to compare two numbers. There +//! are different rules for comparing integers and floating point numbers, so they use different +//! condition codes. + +use core::fmt::{self, Display, Formatter}; +use core::str::FromStr; + +/// Common traits of condition codes. +pub trait CondCode: Copy { + /// Get the inverse condition code of `self`. + /// + /// The inverse condition code produces the opposite result for all comparisons. + /// That is, `cmp CC, x, y` is true if and only if `cmp CC.inverse(), x, y` is false. + #[must_use] + fn inverse(self) -> Self; + + /// Get the reversed condition code for `self`. + /// + /// The reversed condition code produces the same result as swapping `x` and `y` in the + /// comparison. That is, `cmp CC, x, y` is the same as `cmp CC.reverse(), y, x`. + #[must_use] + fn reverse(self) -> Self; +} + +/// Condition code for comparing integers. +/// +/// This condition code is used by the `icmp` instruction to compare integer values. There are +/// separate codes for comparing the integers as signed or unsigned numbers where it makes a +/// difference. +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] +pub enum IntCC { + /// `==`. + Equal, + /// `!=`. + NotEqual, + /// Signed `<`. + SignedLessThan, + /// Signed `>=`. + SignedGreaterThanOrEqual, + /// Signed `>`. + SignedGreaterThan, + /// Signed `<=`. + SignedLessThanOrEqual, + /// Unsigned `<`. + UnsignedLessThan, + /// Unsigned `>=`. + UnsignedGreaterThanOrEqual, + /// Unsigned `>`. + UnsignedGreaterThan, + /// Unsigned `<=`. + UnsignedLessThanOrEqual, + /// Signed Overflow. + Overflow, + /// Signed No Overflow. + NotOverflow, +} + +impl CondCode for IntCC { + fn inverse(self) -> Self { + use self::IntCC::*; + match self { + Equal => NotEqual, + NotEqual => Equal, + SignedLessThan => SignedGreaterThanOrEqual, + SignedGreaterThanOrEqual => SignedLessThan, + SignedGreaterThan => SignedLessThanOrEqual, + SignedLessThanOrEqual => SignedGreaterThan, + UnsignedLessThan => UnsignedGreaterThanOrEqual, + UnsignedGreaterThanOrEqual => UnsignedLessThan, + UnsignedGreaterThan => UnsignedLessThanOrEqual, + UnsignedLessThanOrEqual => UnsignedGreaterThan, + Overflow => NotOverflow, + NotOverflow => Overflow, + } + } + + fn reverse(self) -> Self { + use self::IntCC::*; + match self { + Equal => Equal, + NotEqual => NotEqual, + SignedGreaterThan => SignedLessThan, + SignedGreaterThanOrEqual => SignedLessThanOrEqual, + SignedLessThan => SignedGreaterThan, + SignedLessThanOrEqual => SignedGreaterThanOrEqual, + UnsignedGreaterThan => UnsignedLessThan, + UnsignedGreaterThanOrEqual => UnsignedLessThanOrEqual, + UnsignedLessThan => UnsignedGreaterThan, + UnsignedLessThanOrEqual => UnsignedGreaterThanOrEqual, + Overflow => Overflow, + NotOverflow => NotOverflow, + } + } +} + +impl IntCC { + /// Get the corresponding IntCC with the equal component removed. + /// For conditions without a zero component, this is a no-op. + pub fn without_equal(self) -> Self { + use self::IntCC::*; + match self { + SignedGreaterThan | SignedGreaterThanOrEqual => SignedGreaterThan, + SignedLessThan | SignedLessThanOrEqual => SignedLessThan, + UnsignedGreaterThan | UnsignedGreaterThanOrEqual => UnsignedGreaterThan, + UnsignedLessThan | UnsignedLessThanOrEqual => UnsignedLessThan, + _ => self, + } + } + + /// Get the corresponding IntCC with the signed component removed. + /// For conditions without a signed component, this is a no-op. + pub fn unsigned(self) -> Self { + use self::IntCC::*; + match self { + SignedGreaterThan | UnsignedGreaterThan => UnsignedGreaterThan, + SignedGreaterThanOrEqual | UnsignedGreaterThanOrEqual => UnsignedGreaterThanOrEqual, + SignedLessThan | UnsignedLessThan => UnsignedLessThan, + SignedLessThanOrEqual | UnsignedLessThanOrEqual => UnsignedLessThanOrEqual, + _ => self, + } + } + + /// Get the corresponding string condition code for the IntCC object. + pub fn to_static_str(self) -> &'static str { + use self::IntCC::*; + match self { + Equal => "eq", + NotEqual => "ne", + SignedGreaterThan => "sgt", + SignedGreaterThanOrEqual => "sge", + SignedLessThan => "slt", + SignedLessThanOrEqual => "sle", + UnsignedGreaterThan => "ugt", + UnsignedGreaterThanOrEqual => "uge", + UnsignedLessThan => "ult", + UnsignedLessThanOrEqual => "ule", + Overflow => "of", + NotOverflow => "nof", + } + } +} + +impl Display for IntCC { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + f.write_str(self.to_static_str()) + } +} + +impl FromStr for IntCC { + type Err = (); + + fn from_str(s: &str) -> Result<Self, Self::Err> { + use self::IntCC::*; + match s { + "eq" => Ok(Equal), + "ne" => Ok(NotEqual), + "sge" => Ok(SignedGreaterThanOrEqual), + "sgt" => Ok(SignedGreaterThan), + "sle" => Ok(SignedLessThanOrEqual), + "slt" => Ok(SignedLessThan), + "uge" => Ok(UnsignedGreaterThanOrEqual), + "ugt" => Ok(UnsignedGreaterThan), + "ule" => Ok(UnsignedLessThanOrEqual), + "ult" => Ok(UnsignedLessThan), + "of" => Ok(Overflow), + "nof" => Ok(NotOverflow), + _ => Err(()), + } + } +} + +/// Condition code for comparing floating point numbers. +/// +/// This condition code is used by the `fcmp` instruction to compare floating point values. Two +/// IEEE floating point values relate in exactly one of four ways: +/// +/// 1. `UN` - unordered when either value is NaN. +/// 2. `EQ` - equal numerical value. +/// 3. `LT` - `x` is less than `y`. +/// 4. `GT` - `x` is greater than `y`. +/// +/// Note that `0.0` and `-0.0` relate as `EQ` because they both represent the number 0. +/// +/// The condition codes described here are used to produce a single boolean value from the +/// comparison. The 14 condition codes here cover every possible combination of the relation above +/// except the impossible `!UN & !EQ & !LT & !GT` and the always true `UN | EQ | LT | GT`. +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] +pub enum FloatCC { + /// EQ | LT | GT + Ordered, + /// UN + Unordered, + + /// EQ + Equal, + /// The C '!=' operator is the inverse of '==': `NotEqual`. + /// UN | LT | GT + NotEqual, + /// LT | GT + OrderedNotEqual, + /// UN | EQ + UnorderedOrEqual, + + /// LT + LessThan, + /// LT | EQ + LessThanOrEqual, + /// GT + GreaterThan, + /// GT | EQ + GreaterThanOrEqual, + + /// UN | LT + UnorderedOrLessThan, + /// UN | LT | EQ + UnorderedOrLessThanOrEqual, + /// UN | GT + UnorderedOrGreaterThan, + /// UN | GT | EQ + UnorderedOrGreaterThanOrEqual, +} + +impl CondCode for FloatCC { + fn inverse(self) -> Self { + use self::FloatCC::*; + match self { + Ordered => Unordered, + Unordered => Ordered, + Equal => NotEqual, + NotEqual => Equal, + OrderedNotEqual => UnorderedOrEqual, + UnorderedOrEqual => OrderedNotEqual, + LessThan => UnorderedOrGreaterThanOrEqual, + LessThanOrEqual => UnorderedOrGreaterThan, + GreaterThan => UnorderedOrLessThanOrEqual, + GreaterThanOrEqual => UnorderedOrLessThan, + UnorderedOrLessThan => GreaterThanOrEqual, + UnorderedOrLessThanOrEqual => GreaterThan, + UnorderedOrGreaterThan => LessThanOrEqual, + UnorderedOrGreaterThanOrEqual => LessThan, + } + } + fn reverse(self) -> Self { + use self::FloatCC::*; + match self { + Ordered => Ordered, + Unordered => Unordered, + Equal => Equal, + NotEqual => NotEqual, + OrderedNotEqual => OrderedNotEqual, + UnorderedOrEqual => UnorderedOrEqual, + LessThan => GreaterThan, + LessThanOrEqual => GreaterThanOrEqual, + GreaterThan => LessThan, + GreaterThanOrEqual => LessThanOrEqual, + UnorderedOrLessThan => UnorderedOrGreaterThan, + UnorderedOrLessThanOrEqual => UnorderedOrGreaterThanOrEqual, + UnorderedOrGreaterThan => UnorderedOrLessThan, + UnorderedOrGreaterThanOrEqual => UnorderedOrLessThanOrEqual, + } + } +} + +impl Display for FloatCC { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + use self::FloatCC::*; + f.write_str(match *self { + Ordered => "ord", + Unordered => "uno", + Equal => "eq", + NotEqual => "ne", + OrderedNotEqual => "one", + UnorderedOrEqual => "ueq", + LessThan => "lt", + LessThanOrEqual => "le", + GreaterThan => "gt", + GreaterThanOrEqual => "ge", + UnorderedOrLessThan => "ult", + UnorderedOrLessThanOrEqual => "ule", + UnorderedOrGreaterThan => "ugt", + UnorderedOrGreaterThanOrEqual => "uge", + }) + } +} + +impl FromStr for FloatCC { + type Err = (); + + fn from_str(s: &str) -> Result<Self, Self::Err> { + use self::FloatCC::*; + match s { + "ord" => Ok(Ordered), + "uno" => Ok(Unordered), + "eq" => Ok(Equal), + "ne" => Ok(NotEqual), + "one" => Ok(OrderedNotEqual), + "ueq" => Ok(UnorderedOrEqual), + "lt" => Ok(LessThan), + "le" => Ok(LessThanOrEqual), + "gt" => Ok(GreaterThan), + "ge" => Ok(GreaterThanOrEqual), + "ult" => Ok(UnorderedOrLessThan), + "ule" => Ok(UnorderedOrLessThanOrEqual), + "ugt" => Ok(UnorderedOrGreaterThan), + "uge" => Ok(UnorderedOrGreaterThanOrEqual), + _ => Err(()), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::string::ToString; + + static INT_ALL: [IntCC; 12] = [ + IntCC::Equal, + IntCC::NotEqual, + IntCC::SignedLessThan, + IntCC::SignedGreaterThanOrEqual, + IntCC::SignedGreaterThan, + IntCC::SignedLessThanOrEqual, + IntCC::UnsignedLessThan, + IntCC::UnsignedGreaterThanOrEqual, + IntCC::UnsignedGreaterThan, + IntCC::UnsignedLessThanOrEqual, + IntCC::Overflow, + IntCC::NotOverflow, + ]; + + #[test] + fn int_inverse() { + for r in &INT_ALL { + let cc = *r; + let inv = cc.inverse(); + assert!(cc != inv); + assert_eq!(inv.inverse(), cc); + } + } + + #[test] + fn int_reverse() { + for r in &INT_ALL { + let cc = *r; + let rev = cc.reverse(); + assert_eq!(rev.reverse(), cc); + } + } + + #[test] + fn int_display() { + for r in &INT_ALL { + let cc = *r; + assert_eq!(cc.to_string().parse(), Ok(cc)); + } + assert_eq!("bogus".parse::<IntCC>(), Err(())); + } + + static FLOAT_ALL: [FloatCC; 14] = [ + FloatCC::Ordered, + FloatCC::Unordered, + FloatCC::Equal, + FloatCC::NotEqual, + FloatCC::OrderedNotEqual, + FloatCC::UnorderedOrEqual, + FloatCC::LessThan, + FloatCC::LessThanOrEqual, + FloatCC::GreaterThan, + FloatCC::GreaterThanOrEqual, + FloatCC::UnorderedOrLessThan, + FloatCC::UnorderedOrLessThanOrEqual, + FloatCC::UnorderedOrGreaterThan, + FloatCC::UnorderedOrGreaterThanOrEqual, + ]; + + #[test] + fn float_inverse() { + for r in &FLOAT_ALL { + let cc = *r; + let inv = cc.inverse(); + assert!(cc != inv); + assert_eq!(inv.inverse(), cc); + } + } + + #[test] + fn float_reverse() { + for r in &FLOAT_ALL { + let cc = *r; + let rev = cc.reverse(); + assert_eq!(rev.reverse(), cc); + } + } + + #[test] + fn float_display() { + for r in &FLOAT_ALL { + let cc = *r; + assert_eq!(cc.to_string().parse(), Ok(cc)); + } + assert_eq!("bogus".parse::<FloatCC>(), Err(())); + } +} diff --git a/third_party/rust/cranelift-codegen-shared/src/constant_hash.rs b/third_party/rust/cranelift-codegen-shared/src/constant_hash.rs new file mode 100644 index 0000000000..ceac8e2722 --- /dev/null +++ b/third_party/rust/cranelift-codegen-shared/src/constant_hash.rs @@ -0,0 +1,81 @@ +//! Build support for precomputed constant hash tables. +//! +//! This module can generate constant hash tables using open addressing and quadratic probing. +//! +//! The hash tables are arrays that are guaranteed to: +//! +//! - Have a power-of-two size. +//! - Contain at least one empty slot. +//! +//! This module provides build meta support for lookups in these tables, as well as the shared hash +//! function used for probing. + +use std::iter; + +/// A primitive hash function for matching opcodes. +pub fn simple_hash(s: &str) -> usize { + let mut h: u32 = 5381; + for c in s.chars() { + h = (h ^ c as u32).wrapping_add(h.rotate_right(6)); + } + h as usize +} + +/// Compute an open addressed, quadratically probed hash table containing +/// `items`. The returned table is a list containing the elements of the +/// iterable `items` and `None` in unused slots. +#[allow(clippy::float_arithmetic)] +pub fn generate_table<'cont, T, I: iter::Iterator<Item = &'cont T>, H: Fn(&T) -> usize>( + items: I, + num_items: usize, + hash_function: H, +) -> Vec<Option<&'cont T>> { + let size = (1.20 * num_items as f64) as usize; + + // Probing code's stop condition relies on the table having one vacant entry at least. + let size = if size.is_power_of_two() { + size * 2 + } else { + size.next_power_of_two() + }; + + let mut table = vec![None; size]; + + for i in items { + let mut h = hash_function(&i) % size; + let mut s = 0; + while table[h].is_some() { + s += 1; + h = (h + s) % size; + } + table[h] = Some(i); + } + + table +} + +#[cfg(test)] +mod tests { + use super::{generate_table, simple_hash}; + + #[test] + fn basic() { + assert_eq!(simple_hash("Hello"), 0x2fa70c01); + assert_eq!(simple_hash("world"), 0x5b0c31d5); + } + + #[test] + fn test_generate_table() { + let v = vec!["Hello".to_string(), "world".to_string()]; + let table = generate_table(v.iter(), v.len(), |s| simple_hash(&s)); + assert_eq!( + table, + vec![ + None, + Some(&"Hello".to_string()), + Some(&"world".to_string()), + None + ] + ); + } +} diff --git a/third_party/rust/cranelift-codegen-shared/src/constants.rs b/third_party/rust/cranelift-codegen-shared/src/constants.rs new file mode 100644 index 0000000000..b3f1377856 --- /dev/null +++ b/third_party/rust/cranelift-codegen-shared/src/constants.rs @@ -0,0 +1,30 @@ +//! This module contains constants that are shared between the codegen and the meta crate, so they +//! are kept in sync. + +// Numbering scheme for value types: +// +// 0: Void +// 0x01-0x6f: Special types +// 0x70-0x7d: Lane types +// 0x7e-0x7f: Reference types +// 0x80-0xff: Vector types +// +// Vector types are encoded with the lane type in the low 4 bits and log2(lanes) +// in the high 4 bits, giving a range of 2-256 lanes. + +/// Start of the lane types. +pub const LANE_BASE: u8 = 0x70; + +/// Base for reference types. +pub const REFERENCE_BASE: u8 = 0x7E; + +/// Start of the 2-lane vector types. +pub const VECTOR_BASE: u8 = 0x80; + +// Some constants about register classes and types. + +/// Guaranteed maximum number of top-level register classes with pressure tracking in any ISA. +pub const MAX_TRACKED_TOP_RCS: usize = 4; + +/// Guaranteed maximum number of register classes in any ISA. +pub const MAX_NUM_REG_CLASSES: usize = 32; diff --git a/third_party/rust/cranelift-codegen-shared/src/isa/mod.rs b/third_party/rust/cranelift-codegen-shared/src/isa/mod.rs new file mode 100644 index 0000000000..4d8e485f6c --- /dev/null +++ b/third_party/rust/cranelift-codegen-shared/src/isa/mod.rs @@ -0,0 +1,3 @@ +//! Shared ISA-specific definitions. + +pub mod x86; diff --git a/third_party/rust/cranelift-codegen-shared/src/isa/x86/encoding_bits.rs b/third_party/rust/cranelift-codegen-shared/src/isa/x86/encoding_bits.rs new file mode 100644 index 0000000000..9edb2a6e6f --- /dev/null +++ b/third_party/rust/cranelift-codegen-shared/src/isa/x86/encoding_bits.rs @@ -0,0 +1,419 @@ +//! Provides a named interface to the `u16` Encoding bits. + +use std::ops::RangeInclusive; + +/// Named interface to the `u16` Encoding bits, representing an opcode. +/// +/// Cranelift requires each recipe to have a single encoding size in bytes. +/// X86 opcodes are variable length, so we use separate recipes for different +/// styles of opcodes and prefixes. The opcode format is indicated by the +/// recipe name prefix. +/// +/// VEX/XOP and EVEX prefixes are not yet supported. +/// Encodings using any of these prefixes are represented by separate recipes. +/// +/// The encoding bits are: +/// +/// 0-7: The opcode byte <op>. +/// 8-9: pp, mandatory prefix: +/// 00: none (Op*) +/// 01: 66 (Mp*) +/// 10: F3 (Mp*) +/// 11: F2 (Mp*) +/// 10-11: mm, opcode map: +/// 00: <op> (Op1/Mp1) +/// 01: 0F <op> (Op2/Mp2) +/// 10: 0F 38 <op> (Op3/Mp3) +/// 11: 0F 3A <op> (Op3/Mp3) +/// 12-14 rrr, opcode bits for the ModR/M byte for certain opcodes. +/// 15: REX.W bit (or VEX.W/E) +#[derive(Copy, Clone, PartialEq)] +pub struct EncodingBits(u16); +const OPCODE: RangeInclusive<u16> = 0..=7; +const OPCODE_PREFIX: RangeInclusive<u16> = 8..=11; // Includes pp and mm. +const RRR: RangeInclusive<u16> = 12..=14; +const REX_W: RangeInclusive<u16> = 15..=15; + +impl From<u16> for EncodingBits { + fn from(bits: u16) -> Self { + Self(bits) + } +} + +impl EncodingBits { + /// Constructs a new EncodingBits from parts. + pub fn new(op_bytes: &[u8], rrr: u16, rex_w: u16) -> Self { + assert!( + !op_bytes.is_empty(), + "op_bytes must include at least one opcode byte" + ); + let mut new = Self::from(0); + let last_byte = op_bytes[op_bytes.len() - 1]; + new.write(OPCODE, last_byte as u16); + let prefix: u8 = OpcodePrefix::from_opcode(op_bytes).into(); + new.write(OPCODE_PREFIX, prefix as u16); + new.write(RRR, rrr); + new.write(REX_W, rex_w); + new + } + + /// Returns a copy of the EncodingBits with the RRR bits set. + #[inline] + pub fn with_rrr(mut self, rrr: u8) -> Self { + debug_assert_eq!(self.rrr(), 0); + self.write(RRR, rrr.into()); + self + } + + /// Returns a copy of the EncodingBits with the REX.W bit set. + #[inline] + pub fn with_rex_w(mut self) -> Self { + debug_assert_eq!(self.rex_w(), 0); + self.write(REX_W, 1); + self + } + + /// Returns the raw bits. + #[inline] + pub fn bits(self) -> u16 { + self.0 + } + + /// Convenience method for writing bits to specific range. + #[inline] + fn write(&mut self, range: RangeInclusive<u16>, value: u16) { + assert!(ExactSizeIterator::len(&range) > 0); + let size = range.end() - range.start() + 1; // Calculate the number of bits in the range. + let mask = (1 << size) - 1; // Generate a bit mask. + debug_assert!( + value <= mask, + "The written value should have fewer than {} bits.", + size + ); + let mask_complement = !(mask << *range.start()); // Create the bitwise complement for the clear mask. + self.0 &= mask_complement; // Clear the bits in `range`. + let value = (value & mask) << *range.start(); // Place the value in the correct location. + self.0 |= value; // Modify the bits in `range`. + } + + /// Convenience method for reading bits from a specific range. + #[inline] + fn read(self, range: RangeInclusive<u16>) -> u8 { + assert!(ExactSizeIterator::len(&range) > 0); + let size = range.end() - range.start() + 1; // Calculate the number of bits in the range. + debug_assert!(size <= 8, "This structure expects ranges of at most 8 bits"); + let mask = (1 << size) - 1; // Generate a bit mask. + ((self.0 >> *range.start()) & mask) as u8 + } + + /// Instruction opcode byte, without the prefix. + #[inline] + pub fn opcode_byte(self) -> u8 { + self.read(OPCODE) + } + + /// Prefix kind for the instruction, as an enum. + #[inline] + pub fn prefix(self) -> OpcodePrefix { + OpcodePrefix::from(self.read(OPCODE_PREFIX)) + } + + /// Extracts the PP bits of the OpcodePrefix. + #[inline] + pub fn pp(self) -> u8 { + self.prefix().to_primitive() & 0x3 + } + + /// Extracts the MM bits of the OpcodePrefix. + #[inline] + pub fn mm(self) -> u8 { + (self.prefix().to_primitive() >> 2) & 0x3 + } + + /// Bits for the ModR/M byte for certain opcodes. + #[inline] + pub fn rrr(self) -> u8 { + self.read(RRR) + } + + /// REX.W bit (or VEX.W/E). + #[inline] + pub fn rex_w(self) -> u8 { + self.read(REX_W) + } +} + +/// Opcode prefix representation. +/// +/// The prefix type occupies four of the EncodingBits. +#[allow(non_camel_case_types)] +#[allow(missing_docs)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum OpcodePrefix { + Op1, + Mp1_66, + Mp1_f3, + Mp1_f2, + Op2_0f, + Mp2_66_0f, + Mp2_f3_0f, + Mp2_f2_0f, + Op3_0f_38, + Mp3_66_0f_38, + Mp3_f3_0f_38, + Mp3_f2_0f_38, + Op3_0f_3a, + Mp3_66_0f_3a, + Mp3_f3_0f_3a, + Mp3_f2_0f_3a, +} + +impl From<u8> for OpcodePrefix { + fn from(n: u8) -> Self { + use OpcodePrefix::*; + match n { + 0b0000 => Op1, + 0b0001 => Mp1_66, + 0b0010 => Mp1_f3, + 0b0011 => Mp1_f2, + 0b0100 => Op2_0f, + 0b0101 => Mp2_66_0f, + 0b0110 => Mp2_f3_0f, + 0b0111 => Mp2_f2_0f, + 0b1000 => Op3_0f_38, + 0b1001 => Mp3_66_0f_38, + 0b1010 => Mp3_f3_0f_38, + 0b1011 => Mp3_f2_0f_38, + 0b1100 => Op3_0f_3a, + 0b1101 => Mp3_66_0f_3a, + 0b1110 => Mp3_f3_0f_3a, + 0b1111 => Mp3_f2_0f_3a, + _ => panic!("invalid opcode prefix"), + } + } +} + +impl Into<u8> for OpcodePrefix { + fn into(self) -> u8 { + use OpcodePrefix::*; + match self { + Op1 => 0b0000, + Mp1_66 => 0b0001, + Mp1_f3 => 0b0010, + Mp1_f2 => 0b0011, + Op2_0f => 0b0100, + Mp2_66_0f => 0b0101, + Mp2_f3_0f => 0b0110, + Mp2_f2_0f => 0b0111, + Op3_0f_38 => 0b1000, + Mp3_66_0f_38 => 0b1001, + Mp3_f3_0f_38 => 0b1010, + Mp3_f2_0f_38 => 0b1011, + Op3_0f_3a => 0b1100, + Mp3_66_0f_3a => 0b1101, + Mp3_f3_0f_3a => 0b1110, + Mp3_f2_0f_3a => 0b1111, + } + } +} + +impl OpcodePrefix { + /// Convert an opcode prefix to a `u8`; this is a convenience proxy for `Into<u8>`. + fn to_primitive(self) -> u8 { + self.into() + } + + /// Extracts the OpcodePrefix from the opcode. + pub fn from_opcode(op_bytes: &[u8]) -> Self { + assert!(!op_bytes.is_empty(), "at least one opcode byte"); + + let prefix_bytes = &op_bytes[..op_bytes.len() - 1]; + match prefix_bytes { + [] => Self::Op1, + [0x66] => Self::Mp1_66, + [0xf3] => Self::Mp1_f3, + [0xf2] => Self::Mp1_f2, + [0x0f] => Self::Op2_0f, + [0x66, 0x0f] => Self::Mp2_66_0f, + [0xf3, 0x0f] => Self::Mp2_f3_0f, + [0xf2, 0x0f] => Self::Mp2_f2_0f, + [0x0f, 0x38] => Self::Op3_0f_38, + [0x66, 0x0f, 0x38] => Self::Mp3_66_0f_38, + [0xf3, 0x0f, 0x38] => Self::Mp3_f3_0f_38, + [0xf2, 0x0f, 0x38] => Self::Mp3_f2_0f_38, + [0x0f, 0x3a] => Self::Op3_0f_3a, + [0x66, 0x0f, 0x3a] => Self::Mp3_66_0f_3a, + [0xf3, 0x0f, 0x3a] => Self::Mp3_f3_0f_3a, + [0xf2, 0x0f, 0x3a] => Self::Mp3_f2_0f_3a, + _ => { + panic!("unexpected opcode sequence: {:?}", op_bytes); + } + } + } + + /// Returns the recipe name prefix. + /// + /// At the moment, each similar OpcodePrefix group is given its own Recipe. + /// In order to distinguish them, this string is prefixed. + pub fn recipe_name_prefix(self) -> &'static str { + use OpcodePrefix::*; + match self { + Op1 => "Op1", + Op2_0f => "Op2", + Op3_0f_38 | Op3_0f_3a => "Op3", + Mp1_66 | Mp1_f3 | Mp1_f2 => "Mp1", + Mp2_66_0f | Mp2_f3_0f | Mp2_f2_0f => "Mp2", + Mp3_66_0f_38 | Mp3_f3_0f_38 | Mp3_f2_0f_38 => "Mp3", + Mp3_66_0f_3a | Mp3_f3_0f_3a | Mp3_f2_0f_3a => "Mp3", + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + /// Helper function for prefix_roundtrip() to avoid long lines. + fn test_roundtrip(p: OpcodePrefix) { + assert_eq!(p, OpcodePrefix::from(p.to_primitive())); + } + + /// Tests that to/from each opcode matches. + #[test] + fn prefix_roundtrip() { + test_roundtrip(OpcodePrefix::Op1); + test_roundtrip(OpcodePrefix::Mp1_66); + test_roundtrip(OpcodePrefix::Mp1_f3); + test_roundtrip(OpcodePrefix::Mp1_f2); + test_roundtrip(OpcodePrefix::Op2_0f); + test_roundtrip(OpcodePrefix::Mp2_66_0f); + test_roundtrip(OpcodePrefix::Mp2_f3_0f); + test_roundtrip(OpcodePrefix::Mp2_f2_0f); + test_roundtrip(OpcodePrefix::Op3_0f_38); + test_roundtrip(OpcodePrefix::Mp3_66_0f_38); + test_roundtrip(OpcodePrefix::Mp3_f3_0f_38); + test_roundtrip(OpcodePrefix::Mp3_f2_0f_38); + test_roundtrip(OpcodePrefix::Op3_0f_3a); + test_roundtrip(OpcodePrefix::Mp3_66_0f_3a); + test_roundtrip(OpcodePrefix::Mp3_f3_0f_3a); + test_roundtrip(OpcodePrefix::Mp3_f2_0f_3a); + } + + #[test] + fn prefix_to_name() { + assert_eq!(OpcodePrefix::Op1.recipe_name_prefix(), "Op1"); + assert_eq!(OpcodePrefix::Op2_0f.recipe_name_prefix(), "Op2"); + assert_eq!(OpcodePrefix::Op3_0f_38.recipe_name_prefix(), "Op3"); + assert_eq!(OpcodePrefix::Mp1_66.recipe_name_prefix(), "Mp1"); + assert_eq!(OpcodePrefix::Mp2_66_0f.recipe_name_prefix(), "Mp2"); + assert_eq!(OpcodePrefix::Mp3_66_0f_3a.recipe_name_prefix(), "Mp3"); + } + + /// Tests that the opcode_byte is the lower of the EncodingBits. + #[test] + fn encodingbits_opcode_byte() { + let enc = EncodingBits::from(0x00ff); + assert_eq!(enc.opcode_byte(), 0xff); + assert_eq!(enc.prefix().to_primitive(), 0x0); + assert_eq!(enc.rrr(), 0x0); + assert_eq!(enc.rex_w(), 0x0); + + let enc = EncodingBits::from(0x00cd); + assert_eq!(enc.opcode_byte(), 0xcd); + } + + /// Tests that the OpcodePrefix is encoded correctly. + #[test] + fn encodingbits_prefix() { + let enc = EncodingBits::from(0x0c00); + assert_eq!(enc.opcode_byte(), 0x00); + assert_eq!(enc.prefix().to_primitive(), 0xc); + assert_eq!(enc.prefix(), OpcodePrefix::Op3_0f_3a); + assert_eq!(enc.rrr(), 0x0); + assert_eq!(enc.rex_w(), 0x0); + } + + /// Tests that the PP bits are encoded correctly. + #[test] + fn encodingbits_pp() { + let enc = EncodingBits::from(0x0300); + assert_eq!(enc.opcode_byte(), 0x0); + assert_eq!(enc.pp(), 0x3); + assert_eq!(enc.mm(), 0x0); + assert_eq!(enc.rrr(), 0x0); + assert_eq!(enc.rex_w(), 0x0); + } + + /// Tests that the MM bits are encoded correctly. + #[test] + fn encodingbits_mm() { + let enc = EncodingBits::from(0x0c00); + assert_eq!(enc.opcode_byte(), 0x0); + assert_eq!(enc.pp(), 0x00); + assert_eq!(enc.mm(), 0x3); + assert_eq!(enc.rrr(), 0x0); + assert_eq!(enc.rex_w(), 0x0); + } + + /// Tests that the ModR/M bits are encoded correctly. + #[test] + fn encodingbits_rrr() { + let enc = EncodingBits::from(0x5000); + assert_eq!(enc.opcode_byte(), 0x0); + assert_eq!(enc.prefix().to_primitive(), 0x0); + assert_eq!(enc.rrr(), 0x5); + assert_eq!(enc.rex_w(), 0x0); + } + + /// Tests that the REX.W bit is encoded correctly. + #[test] + fn encodingbits_rex_w() { + let enc = EncodingBits::from(0x8000); + assert_eq!(enc.opcode_byte(), 0x00); + assert_eq!(enc.prefix().to_primitive(), 0x0); + assert_eq!(enc.rrr(), 0x0); + assert_eq!(enc.rex_w(), 0x1); + } + + /// Tests setting and unsetting a bit using EncodingBits::write. + #[test] + fn encodingbits_flip() { + let mut bits = EncodingBits::from(0); + let range = 2..=2; + + bits.write(range.clone(), 1); + assert_eq!(bits.bits(), 0b100); + + bits.write(range, 0); + assert_eq!(bits.bits(), 0b000); + } + + /// Tests a round-trip of EncodingBits from/to a u16 (hardcoded endianness). + #[test] + fn encodingbits_roundtrip() { + let bits: u16 = 0x1234; + assert_eq!(EncodingBits::from(bits).bits(), bits); + } + + #[test] + // I purposely want to divide the bits using the ranges defined above. + #[allow(clippy::inconsistent_digit_grouping)] + fn encodingbits_construction() { + assert_eq!( + EncodingBits::new(&[0x66, 0x40], 5, 1).bits(), + 0b1_101_0001_01000000 // 1 = rex_w, 101 = rrr, 0001 = prefix, 01000000 = opcode + ); + } + + #[test] + #[should_panic] + fn encodingbits_panics_at_write_to_invalid_range() { + EncodingBits::from(0).write(1..=0, 42); + } + + #[test] + #[should_panic] + fn encodingbits_panics_at_read_to_invalid_range() { + EncodingBits::from(0).read(1..=0); + } +} diff --git a/third_party/rust/cranelift-codegen-shared/src/isa/x86/mod.rs b/third_party/rust/cranelift-codegen-shared/src/isa/x86/mod.rs new file mode 100644 index 0000000000..fb45ae56c3 --- /dev/null +++ b/third_party/rust/cranelift-codegen-shared/src/isa/x86/mod.rs @@ -0,0 +1,4 @@ +//! Shared x86-specific definitions. + +mod encoding_bits; +pub use encoding_bits::*; diff --git a/third_party/rust/cranelift-codegen-shared/src/lib.rs b/third_party/rust/cranelift-codegen-shared/src/lib.rs new file mode 100644 index 0000000000..9b4cb941ed --- /dev/null +++ b/third_party/rust/cranelift-codegen-shared/src/lib.rs @@ -0,0 +1,28 @@ +//! This library contains code that is common to both the `cranelift-codegen` and +//! `cranelift-codegen-meta` libraries. + +#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)] +#![warn(unused_import_braces)] +#![cfg_attr(feature = "std", deny(unstable_features))] +#![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))] +#![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))] +#![cfg_attr( + feature = "cargo-clippy", + warn( + clippy::float_arithmetic, + clippy::mut_mut, + clippy::nonminimal_bool, + clippy::map_unwrap_or, + clippy::print_stdout, + clippy::unicode_not_nfc, + clippy::use_self + ) +)] + +pub mod condcodes; +pub mod constant_hash; +pub mod constants; +pub mod isa; + +/// Version number of this crate. +pub const VERSION: &str = env!("CARGO_PKG_VERSION"); |