Browse Source

Add an Offset32 immediate operand kind.

This will be used to represent an immediate 32-bit signed address offset
for load/store instructions.
pull/3/head
Jakob Stoklund Olesen 8 years ago
parent
commit
ab1e51002d
  1. 14
      docs/langref.rst
  2. 6
      lib/cretonne/meta/base/immediates.py
  3. 274
      lib/cretonne/src/ir/immediates.rs

14
docs/langref.rst

@ -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 In the textual format, :type:`imm64` immediates appear as decimal or
hexadecimal literals using the same syntax as C. 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 .. type:: ieee32
A 32-bit immediate floating point number in the IEEE 754-2008 binary32 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 A 64-bit immediate floating point number in the IEEE 754-2008 binary64
interchange format. All bit patterns are allowed. 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 .. type:: intcc
An integer condition code. See the :inst:`icmp` instruction for details. An integer condition code. See the :inst:`icmp` instruction for details.

6
lib/cretonne/meta/base/immediates.py

@ -17,6 +17,12 @@ imm64 = ImmediateKind('imm64', 'A 64-bit immediate integer.')
#: immediate bit counts on shift instructions. #: immediate bit counts on shift instructions.
uimm8 = ImmediateKind('uimm8', 'An 8-bit immediate unsigned integer.') 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. #: A 32-bit immediate floating point operand.
#: #:
#: IEEE 754-2008 binary32 interchange format. #: IEEE 754-2008 binary32 interchange format.

274
lib/cretonne/src/ir/immediates.rs

@ -6,6 +6,7 @@
//! module in the meta language. //! module in the meta language.
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use std::i32;
use std::mem; use std::mem;
use std::str::FromStr; 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 { impl Display for Imm64 {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let x = self.0; let x = self.0;
@ -42,91 +59,88 @@ impl Display for Imm64 {
// Use decimal for small numbers. // Use decimal for small numbers.
write!(f, "{}", x) write!(f, "{}", x)
} else { } else {
// Hexadecimal with a multiple of 4 digits and group separators: write_hex(x, f)
//
// 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(())
} }
} }
} }
impl FromStr for Imm64 { /// Parse a 64-bit number.
type Err = &'static str; 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
};
// Parse a decimal or hexadecimal `Imm64`, formatted as above. if s2.starts_with("0x") {
fn from_str(s: &str) -> Result<Imm64, &'static str> { // Hexadecimal.
let mut value: u64 = 0; for ch in s2[2..].chars() {
let mut digits = 0; match ch.to_digit(16) {
let negative = s.starts_with('-'); Some(digit) => {
let s2 = if negative { &s[1..] } else { s }; digits += 1;
if digits > 16 {
if s2.starts_with("0x") { return Err("Too many hexadecimal digits");
// 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 => { // This can't overflow given the digit limit.
// Allow embedded underscores, but fail on anything else. value = (value << 4) | digit as u64;
if ch != '_' { }
return Err("Invalid character in hexadecimal Imm64"); None => {
} // Allow embedded underscores, but fail on anything else.
if ch != '_' {
return Err("Invalid character in hexadecimal number");
} }
} }
} }
} else { }
// Decimal number, possibly negative. } else {
for ch in s2.chars() { // Decimal number, possibly negative.
match ch.to_digit(16) { for ch in s2.chars() {
Some(digit) => { match ch.to_digit(16) {
digits += 1; Some(digit) => {
match value.checked_mul(10) { digits += 1;
None => return Err("Too large decimal Imm64"), match value.checked_mul(10) {
Some(v) => value = v, None => return Err("Too large decimal number"),
} Some(v) => value = v,
match value.checked_add(digit as u64) {
None => return Err("Too large decimal Imm64"),
Some(v) => value = v,
}
} }
None => { match value.checked_add(digit as u64) {
// Allow embedded underscores, but fail on anything else. None => return Err("Too large decimal number"),
if ch != '_' { Some(v) => value = v,
return Err("Invalid character in decimal Imm64"); }
} }
None => {
// Allow embedded underscores, but fail on anything else.
if ch != '_' {
return Err("Invalid character in decimal number");
} }
} }
} }
} }
}
if digits == 0 { if digits == 0 {
return Err("No digits in Imm64"); return Err("No digits in number");
} }
// We support the range-and-a-half from -2^63 .. 2^64-1. // We support the range-and-a-half from -2^63 .. 2^64-1.
if negative { if negative {
value = value.wrapping_neg(); value = value.wrapping_neg();
// Don't allow large negative values to wrap around and become positive. // Don't allow large negative values to wrap around and become positive.
if value as i64 > 0 { if value as i64 > 0 {
return Err("Negative number too small for Imm64"); return Err("Negative number too small");
}
} }
Ok(Imm64::new(value as i64)) }
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> {
parse_i64(s).map(Imm64::new)
} }
} }
@ -135,6 +149,68 @@ impl FromStr for Imm64 {
/// This is used to indicate lane indexes typically. /// This is used to indicate lane indexes typically.
pub type Uimm8 = u8; 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. /// An IEEE binary32 immediate floating point value.
/// ///
/// All bit patterns are allowed. /// All bit patterns are allowed.
@ -486,15 +562,13 @@ mod tests {
parse_ok::<Imm64>("0xffffffff_ffffffff", "-1"); 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_ok::<Imm64>("-0x80000000_00000000", "0x8000_0000_0000_0000"); parse_ok::<Imm64>("-0x80000000_00000000", "0x8000_0000_0000_0000");
parse_err::<Imm64>("-0x80000000_00000001", parse_err::<Imm64>("-0x80000000_00000001", "Negative number too small");
"Negative number too small for Imm64");
parse_ok::<Imm64>("18446744073709551615", "-1"); parse_ok::<Imm64>("18446744073709551615", "-1");
parse_ok::<Imm64>("-9223372036854775808", "0x8000_0000_0000_0000"); parse_ok::<Imm64>("-9223372036854775808", "0x8000_0000_0000_0000");
// Overflow both the `checked_add` and `checked_mul`. // Overflow both the `checked_add` and `checked_mul`.
parse_err::<Imm64>("18446744073709551616", "Too large decimal Imm64"); parse_err::<Imm64>("18446744073709551616", "Too large decimal number");
parse_err::<Imm64>("184467440737095516100", "Too large decimal Imm64"); parse_err::<Imm64>("184467440737095516100", "Too large decimal number");
parse_err::<Imm64>("-9223372036854775809", parse_err::<Imm64>("-9223372036854775809", "Negative number too small");
"Negative number too small for Imm64");
// Underscores are allowed where digits go. // Underscores are allowed where digits go.
parse_ok::<Imm64>("0_0", "0"); parse_ok::<Imm64>("0_0", "0");
@ -503,21 +577,47 @@ mod tests {
parse_ok::<Imm64>("0x97_88_bb", "0x0097_88bb"); parse_ok::<Imm64>("0x97_88_bb", "0x0097_88bb");
parse_ok::<Imm64>("0x_97_", "151"); parse_ok::<Imm64>("0x_97_", "151");
parse_err::<Imm64>("", "No digits in Imm64"); parse_err::<Imm64>("", "No digits in number");
parse_err::<Imm64>("-", "No digits in Imm64"); parse_err::<Imm64>("-", "No digits in number");
parse_err::<Imm64>("_", "No digits in Imm64"); parse_err::<Imm64>("_", "No digits in number");
parse_err::<Imm64>("0x", "No digits in Imm64"); parse_err::<Imm64>("0x", "No digits in number");
parse_err::<Imm64>("0x_", "No digits in Imm64"); parse_err::<Imm64>("0x_", "No digits in number");
parse_err::<Imm64>("-0x", "No digits in Imm64"); parse_err::<Imm64>("-0x", "No digits in number");
parse_err::<Imm64>(" ", "Invalid character in decimal Imm64"); parse_err::<Imm64>(" ", "Invalid character in decimal number");
parse_err::<Imm64>("0 ", "Invalid character in decimal Imm64"); parse_err::<Imm64>("0 ", "Invalid character in decimal number");
parse_err::<Imm64>(" 0", "Invalid character in decimal Imm64"); parse_err::<Imm64>(" 0", "Invalid character in decimal number");
parse_err::<Imm64>("--", "Invalid character in decimal Imm64"); parse_err::<Imm64>("--", "Invalid character in decimal number");
parse_err::<Imm64>("-0x-", "Invalid character in hexadecimal Imm64"); parse_err::<Imm64>("-0x-", "Invalid character in hexadecimal number");
// Hex count overflow. // Hex count overflow.
parse_err::<Imm64>("0x0_0000_0000_0000_0000", parse_err::<Imm64>("0x0_0000_0000_0000_0000", "Too many hexadecimal digits");
"Too many hexadecimal digits in Imm64"); }
#[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] #[test]

Loading…
Cancel
Save