From 9f55e1a0ad2c07982829ff9e7beb0e47b60e430e Mon Sep 17 00:00:00 2001 From: Zachary Dremann Date: Fri, 23 Feb 2024 13:54:24 -0500 Subject: [PATCH 1/2] Use const generics to provide FastStrings Also, use some `const` functions to improve the `mk_const_fast_str` macro, so the caller only has to include the string they care about, not the length or padding. --- src/rust/iced-x86/src/data_reader.rs | 12 +- src/rust/iced-x86/src/formatter/fast.rs | 244 ++++++++++-------- .../iced-x86/src/formatter/fast/fmt_tbl.rs | 6 +- .../src/formatter/fast/mem_size_tbl.rs | 2 +- .../src/formatter/fast/pseudo_ops_fast.rs | 30 +-- src/rust/iced-x86/src/formatter/fast/regs.rs | 2 +- 6 files changed, 155 insertions(+), 141 deletions(-) diff --git a/src/rust/iced-x86/src/data_reader.rs b/src/rust/iced-x86/src/data_reader.rs index cccab5cbd..2fcbf074f 100644 --- a/src/rust/iced-x86/src/data_reader.rs +++ b/src/rust/iced-x86/src/data_reader.rs @@ -64,12 +64,14 @@ impl<'a> DataReader<'a> { s } + // Returns the whole slice starting at the current index, + // including the length byte, and advances the index by the current length + 1 #[cfg(feature = "fast_fmt")] - #[allow(trivial_casts)] - pub(crate) fn read_len_data(&mut self) -> *const u8 { - let len = &self.data[self.index]; - let len_data = len as *const u8; - self.index += 1 + *len as usize; + pub(crate) fn read_len_data(&mut self) -> &'a [u8] { + let len = usize::from(self.data[self.index]); + debug_assert!(self.index + 1 + len <= self.data.len()); + let len_data = &self.data[self.index..]; + self.index += 1 + len; len_data } } diff --git a/src/rust/iced-x86/src/formatter/fast.rs b/src/rust/iced-x86/src/formatter/fast.rs index 771d3b3f7..1cf48a19f 100644 --- a/src/rust/iced-x86/src/formatter/fast.rs +++ b/src/rust/iced-x86/src/formatter/fast.rs @@ -76,70 +76,89 @@ const _: () = assert!( // Make sure it doesn't grow too much without us knowing about it (eg. if more operands are added) const _: () = assert!(MAX_FMT_INSTR_LEN < 350); -// Creates a fast string type. It contains one ptr to the len (u8) + valid utf8 string. -// The utf8 string has enough bytes following it (eg. padding or the next fast str instance) -// so it's possible to read up to Self::SIZE bytes without crashing or causing a UB. -// Since the compiler knows that Self::SIZE is a constant, it can optimize the string copy, -// eg. if Self::SIZE == 8, it can read one unaligned u64 and write one unaligned u64. -macro_rules! mk_fast_str_ty { - ($ty_name:ident, $size:literal) => { - #[repr(transparent)] - #[derive(Copy, Clone)] - struct $ty_name { - // offset 0: u8, length in bytes of utf8 string - // offset 1: [u8; SIZE] SIZE bytes can be read but only the first len() bytes are part of the string - len_data: *const u8, +#[derive(Copy, Clone)] +#[repr(C)] +struct FastStringRepr { + len: u8, + // SIZE bytes can be read but only the first len bytes are part of the string + data: [u8; SIZE], +} + +impl FastStringRepr { + const fn from_str(s: &str) -> Self { + assert!(s.len() <= SIZE); + let s = s.as_bytes(); + let len = s.len() as u8; + let mut data = [0; SIZE]; + let mut i = 0; + while i < SIZE { + data[i] = if i < s.len() { s[i] } else { b' ' }; + i += 1; } - impl $ty_name { - const SIZE: usize = $size; + Self { len, data } + } +} - #[allow(dead_code)] - fn new(len_data: *const u8) -> Self { - debug_assert!(unsafe { *len_data as usize <= <$ty_name>::SIZE }); - Self { len_data } - } +#[repr(transparent)] +#[derive(Copy, Clone)] +struct FastString { + // offset 0: u8, length in bytes of utf8 string + len_data: &'static FastStringRepr, +} - fn len(self) -> usize { - unsafe { *self.len_data as usize } - } +impl FastString { + const SIZE: usize = SIZE; - fn utf8_data(self) -> *const u8 { - unsafe { self.len_data.add(1) } - } + #[allow(dead_code)] + const fn from_raw(len_data: &'static [u8]) -> Self { + let required_size = 1 + SIZE; + assert!(len_data.len() >= required_size); + assert!(len_data[0] as usize <= SIZE); + let len_data = unsafe { &*(len_data.as_ptr() as *const FastStringRepr) }; + Self::new(len_data) + } - #[allow(dead_code)] - fn get_slice(self) -> &'static [u8] { - unsafe { slice::from_raw_parts(self.utf8_data(), self.len()) } - } - } - // SAFETY: The ptr field points to a static immutable u8 array. - unsafe impl Send for $ty_name {} - unsafe impl Sync for $ty_name {} - }; + const fn new(len_data: &'static FastStringRepr) -> Self { + Self { len_data } + } + + const fn len(self) -> usize { + self.len_data.len as usize + } + + const fn utf8_data(self) -> *const u8 { + self.len_data.data.as_ptr() + } + + #[allow(dead_code)] + const fn get_slice(self) -> &'static [u8] { + unsafe { slice::from_raw_parts(self.utf8_data(), self.len()) } + } } + // FastString2 isn't used since the code needs a 66h prefix (if target CPU is x86) -mk_fast_str_ty! {FastString4, 4} // ld 4 -mk_fast_str_ty! {FastString8, 8} // ld 8 -mk_fast_str_ty! {FastString12, 12} // ld 8 + ld 4 -mk_fast_str_ty! {FastString16, 16} // ld 16 -mk_fast_str_ty! {FastString20, 20} // ld 16 + ld 4 +type FastString4 = FastString<4>; // ld 4 +type FastString8 = FastString<8>; // ld 8 +type FastString12 = FastString<12>; // ld 8 + ld 4 +type FastString16 = FastString<16>; // ld 16 +type FastString20 = FastString<20>; // ld 16 + ld 4 type FastStringMnemonic = FastString20; type FastStringMemorySize = FastString16; type FastStringRegister = FastString8; -// It doesn't seem to be possible to const-verify the arg (string literal) in a const fn so we create it with this macro macro_rules! mk_const_fast_str { - // $fast_ty = FastStringN where N is some integer - // $str = padded string. First byte is the string len and the rest is the utf8 data - // of $fast_ty::SIZE bytes padded with any bytes if needed - ($fast_ty:tt, $str:literal) => {{ - const STR: &str = $str; - const _: () = assert!(STR.len() == 1 + <$fast_ty>::SIZE); - const _: () = assert!(STR.as_bytes()[0] as usize <= <$fast_ty>::SIZE); - $fast_ty { len_data: STR.as_ptr() } + ($fast_ty:ty, $str:literal) => {{ + use $crate::formatter::fast::{FastString, FastStringRepr}; + + // Eventually, inline-const will let us do: + // const { FastString::new(&FastStringRepr::from_str($str)) } + // to avoid having to pass a fast string type, and let it be inferred. + const RES: $fast_ty = FastString::new(&FastStringRepr::from_str($str)); + RES }}; } +use mk_const_fast_str; macro_rules! verify_output_has_enough_bytes_left { ($dst:ident, $dst_next_p:ident, $num_bytes:expr) => { @@ -510,10 +529,10 @@ macro_rules! call_format_memory { } static SCALE_NUMBERS: [FastString4; 4] = [ - mk_const_fast_str!(FastString4, "\x02*1 "), - mk_const_fast_str!(FastString4, "\x02*2 "), - mk_const_fast_str!(FastString4, "\x02*4 "), - mk_const_fast_str!(FastString4, "\x02*8 "), + mk_const_fast_str!(FastString4, "*1"), + mk_const_fast_str!(FastString4, "*2"), + mk_const_fast_str!(FastString4, "*4"), + mk_const_fast_str!(FastString4, "*8"), ]; const _: () = assert!(RoundingControl::None as u32 == 0); const _: () = assert!(RoundingControl::RoundToNearest as u32 == 1); @@ -521,58 +540,58 @@ const _: () = assert!(RoundingControl::RoundDown as u32 == 2); const _: () = assert!(RoundingControl::RoundUp as u32 == 3); const _: () = assert!(RoundingControl::RoundTowardZero as u32 == 4); static RC_SAE_STRINGS: [FastString8; IcedConstants::ROUNDING_CONTROL_ENUM_COUNT] = [ - mk_const_fast_str!(FastString8, "\x00 "), - mk_const_fast_str!(FastString8, "\x08{rn-sae}"), - mk_const_fast_str!(FastString8, "\x08{rd-sae}"), - mk_const_fast_str!(FastString8, "\x08{ru-sae}"), - mk_const_fast_str!(FastString8, "\x08{rz-sae}"), + mk_const_fast_str!(FastString8, ""), + mk_const_fast_str!(FastString8, "{rn-sae}"), + mk_const_fast_str!(FastString8, "{rd-sae}"), + mk_const_fast_str!(FastString8, "{ru-sae}"), + mk_const_fast_str!(FastString8, "{rz-sae}"), ]; static RC_STRINGS: [FastString4; IcedConstants::ROUNDING_CONTROL_ENUM_COUNT] = [ - mk_const_fast_str!(FastString4, "\x00 "), - mk_const_fast_str!(FastString4, "\x04{rn}"), - mk_const_fast_str!(FastString4, "\x04{rd}"), - mk_const_fast_str!(FastString4, "\x04{ru}"), - mk_const_fast_str!(FastString4, "\x04{rz}"), + mk_const_fast_str!(FastString4, ""), + mk_const_fast_str!(FastString4, "{rn}"), + mk_const_fast_str!(FastString4, "{rd}"), + mk_const_fast_str!(FastString4, "{ru}"), + mk_const_fast_str!(FastString4, "{rz}"), ]; #[cfg(feature = "mvex")] static MVEX_REG_MEM_CONSTS_32: [FastString12; IcedConstants::MVEX_REG_MEM_CONV_ENUM_COUNT] = [ - mk_const_fast_str!(FastString12, "\x00 "), - mk_const_fast_str!(FastString12, "\x00 "), - mk_const_fast_str!(FastString12, "\x06{cdab} "), - mk_const_fast_str!(FastString12, "\x06{badc} "), - mk_const_fast_str!(FastString12, "\x06{dacb} "), - mk_const_fast_str!(FastString12, "\x06{aaaa} "), - mk_const_fast_str!(FastString12, "\x06{bbbb} "), - mk_const_fast_str!(FastString12, "\x06{cccc} "), - mk_const_fast_str!(FastString12, "\x06{dddd} "), - mk_const_fast_str!(FastString12, "\x00 "), - mk_const_fast_str!(FastString12, "\x07{1to16} "), - mk_const_fast_str!(FastString12, "\x07{4to16} "), - mk_const_fast_str!(FastString12, "\x09{float16} "), - mk_const_fast_str!(FastString12, "\x07{uint8} "), - mk_const_fast_str!(FastString12, "\x07{sint8} "), - mk_const_fast_str!(FastString12, "\x08{uint16} "), - mk_const_fast_str!(FastString12, "\x08{sint16} "), + mk_const_fast_str!(FastString12, ""), + mk_const_fast_str!(FastString12, ""), + mk_const_fast_str!(FastString12, "{cdab}"), + mk_const_fast_str!(FastString12, "{badc}"), + mk_const_fast_str!(FastString12, "{dacb}"), + mk_const_fast_str!(FastString12, "{aaaa}"), + mk_const_fast_str!(FastString12, "{bbbb}"), + mk_const_fast_str!(FastString12, "{cccc}"), + mk_const_fast_str!(FastString12, "{dddd}"), + mk_const_fast_str!(FastString12, ""), + mk_const_fast_str!(FastString12, "{1to16}"), + mk_const_fast_str!(FastString12, "{4to16}"), + mk_const_fast_str!(FastString12, "{float16}"), + mk_const_fast_str!(FastString12, "{uint8}"), + mk_const_fast_str!(FastString12, "{sint8}"), + mk_const_fast_str!(FastString12, "{uint16}"), + mk_const_fast_str!(FastString12, "{sint16}"), ]; #[cfg(feature = "mvex")] static MVEX_REG_MEM_CONSTS_64: [FastString12; IcedConstants::MVEX_REG_MEM_CONV_ENUM_COUNT] = [ - mk_const_fast_str!(FastString12, "\x00 "), - mk_const_fast_str!(FastString12, "\x00 "), - mk_const_fast_str!(FastString12, "\x06{cdab} "), - mk_const_fast_str!(FastString12, "\x06{badc} "), - mk_const_fast_str!(FastString12, "\x06{dacb} "), - mk_const_fast_str!(FastString12, "\x06{aaaa} "), - mk_const_fast_str!(FastString12, "\x06{bbbb} "), - mk_const_fast_str!(FastString12, "\x06{cccc} "), - mk_const_fast_str!(FastString12, "\x06{dddd} "), - mk_const_fast_str!(FastString12, "\x00 "), - mk_const_fast_str!(FastString12, "\x06{1to8} "), - mk_const_fast_str!(FastString12, "\x06{4to8} "), - mk_const_fast_str!(FastString12, "\x09{float16} "), - mk_const_fast_str!(FastString12, "\x07{uint8} "), - mk_const_fast_str!(FastString12, "\x07{sint8} "), - mk_const_fast_str!(FastString12, "\x08{uint16} "), - mk_const_fast_str!(FastString12, "\x08{sint16} "), + mk_const_fast_str!(FastString12, ""), + mk_const_fast_str!(FastString12, ""), + mk_const_fast_str!(FastString12, "{cdab}"), + mk_const_fast_str!(FastString12, "{badc}"), + mk_const_fast_str!(FastString12, "{dacb}"), + mk_const_fast_str!(FastString12, "{aaaa}"), + mk_const_fast_str!(FastString12, "{bbbb}"), + mk_const_fast_str!(FastString12, "{cccc}"), + mk_const_fast_str!(FastString12, "{dddd}"), + mk_const_fast_str!(FastString12, ""), + mk_const_fast_str!(FastString12, "{1to8}"), + mk_const_fast_str!(FastString12, "{4to8}"), + mk_const_fast_str!(FastString12, "{float16}"), + mk_const_fast_str!(FastString12, "{uint8}"), + mk_const_fast_str!(FastString12, "{sint8}"), + mk_const_fast_str!(FastString12, "{uint16}"), + mk_const_fast_str!(FastString12, "{sint16}"), ]; struct FmtTableData { @@ -878,21 +897,21 @@ impl SpecializedFormatter SpecializedFormatter::SHOW_USELESS_PREFIXES)) { if is_repe_or_repne_instruction(code) { - const FAST_STR: FastString8 = mk_const_fast_str!(FastString8, "\x05repe "); + const FAST_STR: FastString8 = mk_const_fast_str!(FastString8, "repe "); write_fast_str!(dst, dst_next_p, FastString8, FAST_STR); } else { - const FAST_STR: FastString4 = mk_const_fast_str!(FastString4, "\x04rep "); + const FAST_STR: FastString4 = mk_const_fast_str!(FastString4, "rep "); write_fast_str!(dst, dst_next_p, FastString4, FAST_STR); } } @@ -915,12 +934,12 @@ impl SpecializedFormatter::SHOW_USELESS_PREFIXES || show_repne_prefix_bool(code, SpecializedFormatter::::SHOW_USELESS_PREFIXES) { - const FAST_STR: FastString8 = mk_const_fast_str!(FastString8, "\x06repne "); + const FAST_STR: FastString8 = mk_const_fast_str!(FastString8, "repne "); write_fast_str!(dst, dst_next_p, FastString8, FAST_STR); } } @@ -1068,7 +1087,7 @@ impl SpecializedFormatter SpecializedFormatter SpecializedFormatter SpecializedFormatter SpecializedFormatter SpecializedFormatter {{ if $use_hex_prefix { - const FAST_STR: FastString4 = mk_const_fast_str!(FastString4, "\x020x "); + const FAST_STR: FastString4 = mk_const_fast_str!(FastString4, "0x"); write_fast_str!($dst, $dst_next_p, FastString4, FAST_STR); } @@ -1733,7 +1752,7 @@ impl SpecializedFormatter; #[allow(missing_copy_implementations)] #[allow(missing_debug_implementations)] pub struct DefaultSpecializedFormatterTraitOptions; + impl SpecializedFormatterTraitOptions for DefaultSpecializedFormatterTraitOptions {} diff --git a/src/rust/iced-x86/src/formatter/fast/fmt_tbl.rs b/src/rust/iced-x86/src/formatter/fast/fmt_tbl.rs index 9aab232d5..76647c581 100644 --- a/src/rust/iced-x86/src/formatter/fast/fmt_tbl.rs +++ b/src/rust/iced-x86/src/formatter/fast/fmt_tbl.rs @@ -30,7 +30,7 @@ fn get_strings_table() -> Vec { // It's safe to read FastStringMnemonic::SIZE bytes from the last string since the // table includes extra padding. See const-assert above and the table. let len_data = reader.read_len_data(); - strings.push(FastStringMnemonic::new(len_data)); + strings.push(FastStringMnemonic::from_raw(len_data)); } debug_assert_eq!(reader.len_left(), PADDING_SIZE); @@ -68,8 +68,8 @@ fn read() -> FmtTableData { new_vec.push(b'v'); new_vec.extend(old_str.get_slice().iter().copied().chain(core::iter::repeat(b' ')).take(FastStringMnemonic::SIZE - 1)); debug_assert_eq!(new_vec.len(), 1 + FastStringMnemonic::SIZE); - let len_data = new_vec.leak().as_ptr(); - FastStringMnemonic::new(len_data) + let len_data = new_vec.leak(); + FastStringMnemonic::from_raw(len_data) } else { strings[reader.read_compressed_u32() as usize] }; diff --git a/src/rust/iced-x86/src/formatter/fast/mem_size_tbl.rs b/src/rust/iced-x86/src/formatter/fast/mem_size_tbl.rs index 1eeb65374..58b0bfc36 100644 --- a/src/rust/iced-x86/src/formatter/fast/mem_size_tbl.rs +++ b/src/rust/iced-x86/src/formatter/fast/mem_size_tbl.rs @@ -210,7 +210,7 @@ lazy_static! { for mem_keywords in MEM_SIZE_TBL_DATA { let keywords = MEM_SIZE_TBL_STRINGS[mem_keywords as usize]; debug_assert!(keywords.len() == 1 + FastStringMemorySize::SIZE); - v.push(FastStringMemorySize::new(keywords.as_ptr())); + v.push(FastStringMemorySize::from_raw(keywords.as_bytes())); } #[allow(clippy::unwrap_used)] v.into_boxed_slice().try_into().ok().unwrap() diff --git a/src/rust/iced-x86/src/formatter/fast/pseudo_ops_fast.rs b/src/rust/iced-x86/src/formatter/fast/pseudo_ops_fast.rs index fba211d75..d1f15960f 100644 --- a/src/rust/iced-x86/src/formatter/fast/pseudo_ops_fast.rs +++ b/src/rust/iced-x86/src/formatter/fast/pseudo_ops_fast.rs @@ -8,15 +8,7 @@ use crate::formatter::fast::FastStringMnemonic; use alloc::vec::Vec; use lazy_static::lazy_static; -// Copied from fast.rs since it doesn't seem to be possible to use it from this module even with #[macro_use] -macro_rules! mk_const_fast_str { - ($fast_ty:tt, $str:literal) => {{ - const STR: &str = $str; - const _: () = assert!(STR.len() == 1 + <$fast_ty>::SIZE); - const _: () = assert!(STR.as_bytes()[0] as usize <= <$fast_ty>::SIZE); - $fast_ty { len_data: STR.as_ptr() } - }}; -} +use super::mk_const_fast_str; pub(super) fn get_pseudo_ops(kind: PseudoOpsKind) -> &'static Vec { let pseudo_ops = &*PSEUDO_OPS; @@ -177,17 +169,17 @@ lazy_static! { #[rustfmt::skip] let pclmulqdq = vec![ - mk_const_fast_str!(FastStringMnemonic, "\x0Cpclmullqlqdq "), - mk_const_fast_str!(FastStringMnemonic, "\x0Cpclmulhqlqdq "), - mk_const_fast_str!(FastStringMnemonic, "\x0Cpclmullqhqdq "), - mk_const_fast_str!(FastStringMnemonic, "\x0Cpclmulhqhqdq "), + mk_const_fast_str!(FastStringMnemonic, "pclmullqlqdq"), + mk_const_fast_str!(FastStringMnemonic, "pclmulhqlqdq"), + mk_const_fast_str!(FastStringMnemonic, "pclmullqhqdq"), + mk_const_fast_str!(FastStringMnemonic, "pclmulhqhqdq"), ]; #[rustfmt::skip] let vpclmulqdq = vec![ - mk_const_fast_str!(FastStringMnemonic, "\x0Dvpclmullqlqdq "), - mk_const_fast_str!(FastStringMnemonic, "\x0Dvpclmulhqlqdq "), - mk_const_fast_str!(FastStringMnemonic, "\x0Dvpclmullqhqdq "), - mk_const_fast_str!(FastStringMnemonic, "\x0Dvpclmulhqhqdq "), + mk_const_fast_str!(FastStringMnemonic, "vpclmullqlqdq"), + mk_const_fast_str!(FastStringMnemonic, "vpclmulhqlqdq"), + mk_const_fast_str!(FastStringMnemonic, "vpclmullqhqdq"), + mk_const_fast_str!(FastStringMnemonic, "vpclmulhqhqdq"), ]; #[rustfmt::skip] @@ -262,8 +254,8 @@ fn create(cc: &[&str], size: usize, prefix: &str, suffix: &str) -> Vec Box<[FastStringRegister; IcedConstants::REGISTER_ENUM_COUNT]> { // It's safe to read FastStringRegister::SIZE bytes from the last string since the // table includes extra padding. See const-assert above and the table. - result.push(FastStringRegister::new(data.as_ptr())); + result.push(FastStringRegister::from_raw(data)); data = &data[1 + len..]; } From 26c4b03de0ce8180ea84be731e6a78889c186517 Mon Sep 17 00:00:00 2001 From: Zachary Dremann Date: Sat, 24 Feb 2024 17:37:59 -0500 Subject: [PATCH 2/2] Fix for rust 1.57 Some options aren't available for const yet in rust 1.57 --- src/rust/iced-x86/src/formatter/fast.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/rust/iced-x86/src/formatter/fast.rs b/src/rust/iced-x86/src/formatter/fast.rs index 1cf48a19f..30be5e24f 100644 --- a/src/rust/iced-x86/src/formatter/fast.rs +++ b/src/rust/iced-x86/src/formatter/fast.rs @@ -110,11 +110,12 @@ impl FastString { const SIZE: usize = SIZE; #[allow(dead_code)] + #[allow(clippy::transmute_ptr_to_ref)] // Can't use pointer dereference in const fn yet const fn from_raw(len_data: &'static [u8]) -> Self { let required_size = 1 + SIZE; assert!(len_data.len() >= required_size); assert!(len_data[0] as usize <= SIZE); - let len_data = unsafe { &*(len_data.as_ptr() as *const FastStringRepr) }; + let len_data: &'static FastStringRepr = unsafe { mem::transmute(len_data.as_ptr()) }; Self::new(len_data) } @@ -131,7 +132,7 @@ impl FastString { } #[allow(dead_code)] - const fn get_slice(self) -> &'static [u8] { + fn get_slice(self) -> &'static [u8] { unsafe { slice::from_raw_parts(self.utf8_data(), self.len()) } } }