Add an Offset32 immediate operand kind.
This will be used to represent an immediate 32-bit signed address offset for load/store instructions.
This commit is contained in:
@@ -249,6 +249,13 @@ indicate the different kinds of immediate operands on an instruction.
|
||||
In the textual format, :type:`imm64` immediates appear as decimal or
|
||||
hexadecimal literals using the same syntax as C.
|
||||
|
||||
.. type:: offset32
|
||||
|
||||
A signed 32-bit immediate address offset.
|
||||
|
||||
In the textual format, :type:`offset32` immediates always have an explicit
|
||||
sign, and a 0 offset may beomitted.
|
||||
|
||||
.. type:: ieee32
|
||||
|
||||
A 32-bit immediate floating point number in the IEEE 754-2008 binary32
|
||||
@@ -259,13 +266,6 @@ indicate the different kinds of immediate operands on an instruction.
|
||||
A 64-bit immediate floating point number in the IEEE 754-2008 binary64
|
||||
interchange format. All bit patterns are allowed.
|
||||
|
||||
.. type:: immvector
|
||||
|
||||
An immediate SIMD vector. This operand supplies all the bits of a SIMD
|
||||
type, so it can have different sizes depending on the type produced. The
|
||||
bits of the operand are interpreted as if the SIMD vector was loaded from
|
||||
memory containing the immediate.
|
||||
|
||||
.. type:: intcc
|
||||
|
||||
An integer condition code. See the :inst:`icmp` instruction for details.
|
||||
|
||||
@@ -17,6 +17,12 @@ imm64 = ImmediateKind('imm64', 'A 64-bit immediate integer.')
|
||||
#: immediate bit counts on shift instructions.
|
||||
uimm8 = ImmediateKind('uimm8', 'An 8-bit immediate unsigned integer.')
|
||||
|
||||
#: A 32-bit immediate signed offset.
|
||||
#:
|
||||
#: This is used to represent an immediate address offset in load/store
|
||||
#: instructions.
|
||||
offset32 = ImmediateKind('offset32', 'A 32-bit immediate signed offset.')
|
||||
|
||||
#: A 32-bit immediate floating point operand.
|
||||
#:
|
||||
#: IEEE 754-2008 binary32 interchange format.
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
//! module in the meta language.
|
||||
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::i32;
|
||||
use std::mem;
|
||||
use std::str::FromStr;
|
||||
|
||||
@@ -35,6 +36,22 @@ impl From<i64> for Imm64 {
|
||||
}
|
||||
}
|
||||
|
||||
// Hexadecimal with a multiple of 4 digits and group separators:
|
||||
//
|
||||
// 0xfff0
|
||||
// 0x0001_ffff
|
||||
// 0xffff_ffff_fff8_4400
|
||||
//
|
||||
fn write_hex(x: i64, f: &mut Formatter) -> fmt::Result {
|
||||
let mut pos = (64 - x.leading_zeros() - 1) & 0xf0;
|
||||
write!(f, "0x{:04x}", (x >> pos) & 0xffff)?;
|
||||
while pos > 0 {
|
||||
pos -= 16;
|
||||
write!(f, "_{:04x}", (x >> pos) & 0xffff)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl Display for Imm64 {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
let x = self.0;
|
||||
@@ -42,91 +59,88 @@ impl Display for Imm64 {
|
||||
// Use decimal for small numbers.
|
||||
write!(f, "{}", x)
|
||||
} else {
|
||||
// Hexadecimal with a multiple of 4 digits and group separators:
|
||||
//
|
||||
// 0xfff0
|
||||
// 0x0001_ffff
|
||||
// 0xffff_ffff_fff8_4400
|
||||
//
|
||||
let mut pos = (64 - x.leading_zeros() - 1) & 0xf0;
|
||||
write!(f, "0x{:04x}", (x >> pos) & 0xffff)?;
|
||||
while pos > 0 {
|
||||
pos -= 16;
|
||||
write!(f, "_{:04x}", (x >> pos) & 0xffff)?;
|
||||
}
|
||||
Ok(())
|
||||
write_hex(x, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a 64-bit number.
|
||||
fn parse_i64(s: &str) -> Result<i64, &'static str> {
|
||||
let mut value: u64 = 0;
|
||||
let mut digits = 0;
|
||||
let negative = s.starts_with('-');
|
||||
let s2 = if negative || s.starts_with('+') {
|
||||
&s[1..]
|
||||
} else {
|
||||
s
|
||||
};
|
||||
|
||||
if s2.starts_with("0x") {
|
||||
// Hexadecimal.
|
||||
for ch in s2[2..].chars() {
|
||||
match ch.to_digit(16) {
|
||||
Some(digit) => {
|
||||
digits += 1;
|
||||
if digits > 16 {
|
||||
return Err("Too many hexadecimal digits");
|
||||
}
|
||||
// This can't overflow given the digit limit.
|
||||
value = (value << 4) | digit as u64;
|
||||
}
|
||||
None => {
|
||||
// Allow embedded underscores, but fail on anything else.
|
||||
if ch != '_' {
|
||||
return Err("Invalid character in hexadecimal number");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Decimal number, possibly negative.
|
||||
for ch in s2.chars() {
|
||||
match ch.to_digit(16) {
|
||||
Some(digit) => {
|
||||
digits += 1;
|
||||
match value.checked_mul(10) {
|
||||
None => return Err("Too large decimal number"),
|
||||
Some(v) => value = v,
|
||||
}
|
||||
match value.checked_add(digit as u64) {
|
||||
None => return Err("Too large decimal number"),
|
||||
Some(v) => value = v,
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// Allow embedded underscores, but fail on anything else.
|
||||
if ch != '_' {
|
||||
return Err("Invalid character in decimal number");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if digits == 0 {
|
||||
return Err("No digits in number");
|
||||
}
|
||||
|
||||
// We support the range-and-a-half from -2^63 .. 2^64-1.
|
||||
if negative {
|
||||
value = value.wrapping_neg();
|
||||
// Don't allow large negative values to wrap around and become positive.
|
||||
if value as i64 > 0 {
|
||||
return Err("Negative number too small");
|
||||
}
|
||||
}
|
||||
Ok(value as i64)
|
||||
}
|
||||
|
||||
impl FromStr for Imm64 {
|
||||
type Err = &'static str;
|
||||
|
||||
// Parse a decimal or hexadecimal `Imm64`, formatted as above.
|
||||
fn from_str(s: &str) -> Result<Imm64, &'static str> {
|
||||
let mut value: u64 = 0;
|
||||
let mut digits = 0;
|
||||
let negative = s.starts_with('-');
|
||||
let s2 = if negative { &s[1..] } else { s };
|
||||
|
||||
if s2.starts_with("0x") {
|
||||
// Hexadecimal.
|
||||
for ch in s2[2..].chars() {
|
||||
match ch.to_digit(16) {
|
||||
Some(digit) => {
|
||||
digits += 1;
|
||||
if digits > 16 {
|
||||
return Err("Too many hexadecimal digits in Imm64");
|
||||
}
|
||||
// This can't overflow given the digit limit.
|
||||
value = (value << 4) | digit as u64;
|
||||
}
|
||||
None => {
|
||||
// Allow embedded underscores, but fail on anything else.
|
||||
if ch != '_' {
|
||||
return Err("Invalid character in hexadecimal Imm64");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Decimal number, possibly negative.
|
||||
for ch in s2.chars() {
|
||||
match ch.to_digit(16) {
|
||||
Some(digit) => {
|
||||
digits += 1;
|
||||
match value.checked_mul(10) {
|
||||
None => return Err("Too large decimal Imm64"),
|
||||
Some(v) => value = v,
|
||||
}
|
||||
match value.checked_add(digit as u64) {
|
||||
None => return Err("Too large decimal Imm64"),
|
||||
Some(v) => value = v,
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// Allow embedded underscores, but fail on anything else.
|
||||
if ch != '_' {
|
||||
return Err("Invalid character in decimal Imm64");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if digits == 0 {
|
||||
return Err("No digits in Imm64");
|
||||
}
|
||||
|
||||
// We support the range-and-a-half from -2^63 .. 2^64-1.
|
||||
if negative {
|
||||
value = value.wrapping_neg();
|
||||
// Don't allow large negative values to wrap around and become positive.
|
||||
if value as i64 > 0 {
|
||||
return Err("Negative number too small for Imm64");
|
||||
}
|
||||
}
|
||||
Ok(Imm64::new(value as i64))
|
||||
parse_i64(s).map(Imm64::new)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,6 +149,68 @@ impl FromStr for Imm64 {
|
||||
/// This is used to indicate lane indexes typically.
|
||||
pub type Uimm8 = u8;
|
||||
|
||||
/// 32-bit signed immediate offset.
|
||||
///
|
||||
/// This is used to encode an immediate offset for load/store instructions. All supported ISAs have
|
||||
/// a maximum load/store offset that fits in an `i32`.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Offset32(i32);
|
||||
|
||||
impl Offset32 {
|
||||
/// Create a new `Offset32` representing the signed number `x`.
|
||||
pub fn new(x: i32) -> Offset32 {
|
||||
Offset32(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<i32> for Offset32 {
|
||||
fn into(self) -> i32 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i32> for Offset32 {
|
||||
fn from(x: i32) -> Self {
|
||||
Offset32(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Offset32 {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
// 0 displays as an empty offset.
|
||||
if self.0 == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Always include a sign.
|
||||
write!(f, "{}", if self.0 < 0 { '-' } else { '+' })?;
|
||||
|
||||
let val = (self.0 as i64).abs();
|
||||
if val < 10_000 {
|
||||
write!(f, "{}", val)
|
||||
} else {
|
||||
write_hex(val, f)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Offset32 {
|
||||
type Err = &'static str;
|
||||
|
||||
// Parse a decimal or hexadecimal `Offset32`, formatted as above.
|
||||
fn from_str(s: &str) -> Result<Offset32, &'static str> {
|
||||
if !(s.starts_with('-') || s.starts_with('+')) {
|
||||
return Err("Offset must begin with sign");
|
||||
}
|
||||
parse_i64(s).and_then(|x| if i32::MIN as i64 <= x && x <= i32::MAX as i64 {
|
||||
Ok(Offset32::new(x as i32))
|
||||
} else {
|
||||
Err("Offset out of range")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// An IEEE binary32 immediate floating point value.
|
||||
///
|
||||
/// All bit patterns are allowed.
|
||||
@@ -486,15 +562,13 @@ mod tests {
|
||||
parse_ok::<Imm64>("0xffffffff_ffffffff", "-1");
|
||||
parse_ok::<Imm64>("0x80000000_00000000", "0x8000_0000_0000_0000");
|
||||
parse_ok::<Imm64>("-0x80000000_00000000", "0x8000_0000_0000_0000");
|
||||
parse_err::<Imm64>("-0x80000000_00000001",
|
||||
"Negative number too small for Imm64");
|
||||
parse_err::<Imm64>("-0x80000000_00000001", "Negative number too small");
|
||||
parse_ok::<Imm64>("18446744073709551615", "-1");
|
||||
parse_ok::<Imm64>("-9223372036854775808", "0x8000_0000_0000_0000");
|
||||
// Overflow both the `checked_add` and `checked_mul`.
|
||||
parse_err::<Imm64>("18446744073709551616", "Too large decimal Imm64");
|
||||
parse_err::<Imm64>("184467440737095516100", "Too large decimal Imm64");
|
||||
parse_err::<Imm64>("-9223372036854775809",
|
||||
"Negative number too small for Imm64");
|
||||
parse_err::<Imm64>("18446744073709551616", "Too large decimal number");
|
||||
parse_err::<Imm64>("184467440737095516100", "Too large decimal number");
|
||||
parse_err::<Imm64>("-9223372036854775809", "Negative number too small");
|
||||
|
||||
// Underscores are allowed where digits go.
|
||||
parse_ok::<Imm64>("0_0", "0");
|
||||
@@ -503,21 +577,47 @@ mod tests {
|
||||
parse_ok::<Imm64>("0x97_88_bb", "0x0097_88bb");
|
||||
parse_ok::<Imm64>("0x_97_", "151");
|
||||
|
||||
parse_err::<Imm64>("", "No digits in Imm64");
|
||||
parse_err::<Imm64>("-", "No digits in Imm64");
|
||||
parse_err::<Imm64>("_", "No digits in Imm64");
|
||||
parse_err::<Imm64>("0x", "No digits in Imm64");
|
||||
parse_err::<Imm64>("0x_", "No digits in Imm64");
|
||||
parse_err::<Imm64>("-0x", "No digits in Imm64");
|
||||
parse_err::<Imm64>(" ", "Invalid character in decimal Imm64");
|
||||
parse_err::<Imm64>("0 ", "Invalid character in decimal Imm64");
|
||||
parse_err::<Imm64>(" 0", "Invalid character in decimal Imm64");
|
||||
parse_err::<Imm64>("--", "Invalid character in decimal Imm64");
|
||||
parse_err::<Imm64>("-0x-", "Invalid character in hexadecimal Imm64");
|
||||
parse_err::<Imm64>("", "No digits in number");
|
||||
parse_err::<Imm64>("-", "No digits in number");
|
||||
parse_err::<Imm64>("_", "No digits in number");
|
||||
parse_err::<Imm64>("0x", "No digits in number");
|
||||
parse_err::<Imm64>("0x_", "No digits in number");
|
||||
parse_err::<Imm64>("-0x", "No digits in number");
|
||||
parse_err::<Imm64>(" ", "Invalid character in decimal number");
|
||||
parse_err::<Imm64>("0 ", "Invalid character in decimal number");
|
||||
parse_err::<Imm64>(" 0", "Invalid character in decimal number");
|
||||
parse_err::<Imm64>("--", "Invalid character in decimal number");
|
||||
parse_err::<Imm64>("-0x-", "Invalid character in hexadecimal number");
|
||||
|
||||
// Hex count overflow.
|
||||
parse_err::<Imm64>("0x0_0000_0000_0000_0000",
|
||||
"Too many hexadecimal digits in Imm64");
|
||||
parse_err::<Imm64>("0x0_0000_0000_0000_0000", "Too many hexadecimal digits");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_offset32() {
|
||||
assert_eq!(Offset32(0).to_string(), "");
|
||||
assert_eq!(Offset32(1).to_string(), "+1");
|
||||
assert_eq!(Offset32(-1).to_string(), "-1");
|
||||
assert_eq!(Offset32(9999).to_string(), "+9999");
|
||||
assert_eq!(Offset32(10000).to_string(), "+0x2710");
|
||||
assert_eq!(Offset32(-9999).to_string(), "-9999");
|
||||
assert_eq!(Offset32(-10000).to_string(), "-0x2710");
|
||||
assert_eq!(Offset32(0xffff).to_string(), "+0xffff");
|
||||
assert_eq!(Offset32(0x10000).to_string(), "+0x0001_0000");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_offset32() {
|
||||
parse_ok::<Offset32>("+0", "");
|
||||
parse_ok::<Offset32>("+1", "+1");
|
||||
parse_ok::<Offset32>("-0", "");
|
||||
parse_ok::<Offset32>("-1", "-1");
|
||||
parse_ok::<Offset32>("+0x0", "");
|
||||
parse_ok::<Offset32>("+0xf", "+15");
|
||||
parse_ok::<Offset32>("-0x9", "-9");
|
||||
parse_ok::<Offset32>("-0x8000_0000", "-0x8000_0000");
|
||||
|
||||
parse_err::<Offset32>("+0x8000_0000", "Offset out of range");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user