diff --git a/cranelift/codegen/src/ir/immediates.rs b/cranelift/codegen/src/ir/immediates.rs index 4a8d5e1fbc..5c36f362ab 100644 --- a/cranelift/codegen/src/ir/immediates.rs +++ b/cranelift/codegen/src/ir/immediates.rs @@ -264,6 +264,74 @@ impl FromStr for Uimm32 { } } +/// A 128-bit unsigned integer immediate operand. +/// +/// This is used as an immediate value in SIMD instructions +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] +pub struct Uimm128(pub [u8; 16]); + +impl Display for Uimm128 { + // print a 128-bit vector in hexadecimal, e.g. 0x000102030405060708090a0b0c0d0e0f + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "0x")?; + let mut anything_written = false; + for &b in self.0.iter() { + if b == 0 && !anything_written { + continue; + } else { + anything_written = true; + write!(f, "{:02x}", b)?; + } + } + if !anything_written { + write!(f, "00")?; + } + Ok(()) + } +} + +impl From for Uimm128 { + fn from(x: u64) -> Self { + let mut buffer: [u8; 16] = [0; 16]; // zero-fill + (0..8).for_each(|byte| buffer[15 - byte] = (x >> (byte as u64 * 8) & 0xff) as u8); // insert each byte from the u64 into v after byte position 8 + Uimm128(buffer) + } +} + +impl From<&[u8]> for Uimm128 { + fn from(slice: &[u8]) -> Self { + assert_eq!(slice.len(), 16); + let mut buffer = [0; 16]; + buffer.copy_from_slice(slice); + Uimm128(buffer) + } +} + +impl FromStr for Uimm128 { + type Err = &'static str; + + // parse a 128-bit vector from a hexadecimal string, formatted as above + fn from_str(s: &str) -> Result { + if s.len() <= 2 || &s[0..2] != "0x" { + Err("Expected a hexadecimal string, e.g. 0x1234") + } else if s.len() % 2 != 0 { + Err("Hexadecimal string must have an even number of digits") + } else if s.len() > 32 { + Err("Hexadecimal string has too many digits to fit in a 128-bit vector") + } else { + let mut buffer = [0; 16]; // zero-fill + let start_at = 16 - s.len() / 2; + for i in (2..s.len()).step_by(2) { + let byte = u8::from_str_radix(&s[i..i + 2], 16) + .or_else(|_| Err("Unable to parse as hexadecimal"))?; + let position = start_at + (i / 2); + buffer[position] = byte; + } + Ok(Uimm128(buffer)) + } + } +} + /// 32-bit signed immediate offset. /// /// This is used to encode an immediate offset for load/store instructions. All supported ISAs have @@ -884,6 +952,38 @@ mod tests { parse_err::("0x0_0000_0000_0000_0000", "Too many hexadecimal digits"); } + #[test] + fn format_uimm128() { + assert_eq!(Uimm128::from(0).to_string(), "0x00"); + assert_eq!(Uimm128::from(42).to_string(), "0x2a"); + assert_eq!(Uimm128::from(3735928559).to_string(), "0xdeadbeef"); + assert_eq!( + Uimm128::from(0x0102030405060708).to_string(), + "0x0102030405060708" + ); + } + + #[test] + fn parse_uimm128() { + parse_ok::("0x00", "0x00"); + parse_ok::("0x00000042", "0x42"); + parse_ok::( + "0x0102030405060708090a0b0c0d0e0f", + "0x0102030405060708090a0b0c0d0e0f", + ); + + parse_err::("", "Expected a hexadecimal string, e.g. 0x1234"); + parse_err::("0x", "Expected a hexadecimal string, e.g. 0x1234"); + parse_err::( + "0x042", + "Hexadecimal string must have an even number of digits", + ); + parse_err::( + "0x00000000000000000000000000000000000000000000000000", + "Hexadecimal string has too many digits to fit in a 128-bit vector", + ); + } + #[test] fn format_offset32() { assert_eq!(Offset32(0).to_string(), "");