From ca12a683ac6976c9db14435e61fecc7eeae03d1e Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Mon, 10 Apr 2017 14:32:06 -0700 Subject: [PATCH] Add a Uoffset32 immediate operand kind. WebAssembly memory instructions encode a 32-bit unsigned offset that is used to compute an effective address. --- lib/cretonne/meta/base/immediates.py | 9 +++ lib/cretonne/src/ir/immediates.rs | 95 +++++++++++++++++++++++++++- lib/reader/src/parser.rs | 18 +++++- 3 files changed, 120 insertions(+), 2 deletions(-) diff --git a/lib/cretonne/meta/base/immediates.py b/lib/cretonne/meta/base/immediates.py index ad1bda9989..7f1662f5c1 100644 --- a/lib/cretonne/meta/base/immediates.py +++ b/lib/cretonne/meta/base/immediates.py @@ -26,6 +26,15 @@ offset32 = ImmediateKind( 'A 32-bit immediate signed offset.', default_member='offset') +#: A 32-bit immediate unsigned offset. +#: +#: This is used to represent an immediate address offset in WebAssembly memory +#: instructions. +uoffset32 = ImmediateKind( + 'uoffset32', + 'A 32-bit immediate unsigned offset.', + default_member='offset') + #: A 32-bit immediate floating point operand. #: #: IEEE 754-2008 binary32 interchange format. diff --git a/lib/cretonne/src/ir/immediates.rs b/lib/cretonne/src/ir/immediates.rs index 8b779dde60..120409f3b8 100644 --- a/lib/cretonne/src/ir/immediates.rs +++ b/lib/cretonne/src/ir/immediates.rs @@ -6,7 +6,7 @@ //! module in the meta language. use std::fmt::{self, Display, Formatter}; -use std::i32; +use std::{i32, u32}; use std::mem; use std::str::FromStr; @@ -169,6 +169,12 @@ impl Into for Offset32 { } } +impl Into for Offset32 { + fn into(self) -> i64 { + self.0 as i64 + } +} + impl From for Offset32 { fn from(x: i32) -> Self { Offset32(x) @@ -211,6 +217,71 @@ impl FromStr for Offset32 { } } +/// 32-bit unsigned immediate offset. +/// +/// This is used to encode an immediate offset for WebAssembly heap_load/heap_store instructions. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct Uoffset32(u32); + +impl Uoffset32 { + /// Create a new `Uoffset32` representing the number `x`. + pub fn new(x: u32) -> Uoffset32 { + Uoffset32(x) + } +} + +impl Into for Uoffset32 { + fn into(self) -> u32 { + self.0 + } +} + +impl Into for Uoffset32 { + fn into(self) -> i64 { + self.0 as i64 + } +} + +impl From for Uoffset32 { + fn from(x: u32) -> Self { + Uoffset32(x) + } +} + +impl Display for Uoffset32 { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + // 0 displays as an empty offset. + if self.0 == 0 { + return Ok(()); + } + + // Always include a sign. + if self.0 < 10_000 { + write!(f, "+{}", self.0) + } else { + write!(f, "+")?; + write_hex(self.0 as i64, f) + } + + } +} + +impl FromStr for Uoffset32 { + type Err = &'static str; + + // Parse a decimal or hexadecimal `Uoffset32`, formatted as above. + fn from_str(s: &str) -> Result { + if !s.starts_with('+') { + return Err("Unsigned offset must begin with '+' sign"); + } + parse_i64(s).and_then(|x| if 0 <= x && x <= u32::MAX as i64 { + Ok(Uoffset32::new(x as u32)) + } else { + Err("Offset out of range") + }) + } +} + /// An IEEE binary32 immediate floating point value. /// /// All bit patterns are allowed. @@ -620,6 +691,28 @@ mod tests { parse_err::("+0x8000_0000", "Offset out of range"); } + #[test] + fn format_uoffset32() { + assert_eq!(Uoffset32(0).to_string(), ""); + assert_eq!(Uoffset32(1).to_string(), "+1"); + assert_eq!(Uoffset32(9999).to_string(), "+9999"); + assert_eq!(Uoffset32(10000).to_string(), "+0x2710"); + assert_eq!(Uoffset32(0xffff).to_string(), "+0xffff"); + assert_eq!(Uoffset32(0x10000).to_string(), "+0x0001_0000"); + } + + #[test] + fn parse_uoffset32() { + parse_ok::("+0", ""); + parse_ok::("+1", "+1"); + parse_ok::("+0x0", ""); + parse_ok::("+0xf", "+15"); + parse_ok::("+0x8000_0000", "+0x8000_0000"); + parse_ok::("+0xffff_ffff", "+0xffff_ffff"); + + parse_err::("+0x1_0000_0000", "Offset out of range"); + } + #[test] fn format_ieee32() { assert_eq!(Ieee32::new(0.0).to_string(), "0.0"); diff --git a/lib/reader/src/parser.rs b/lib/reader/src/parser.rs index 9a36ec2451..8775f1f0b4 100644 --- a/lib/reader/src/parser.rs +++ b/lib/reader/src/parser.rs @@ -13,7 +13,7 @@ use cretonne::ir::{Function, Ebb, Opcode, Value, Type, FunctionName, StackSlotDa JumpTableData, Signature, ArgumentType, ArgumentExtension, ExtFuncData, SigRef, FuncRef, StackSlot, ValueLoc, ArgumentLoc}; use cretonne::ir::types::VOID; -use cretonne::ir::immediates::{Imm64, Offset32, Ieee32, Ieee64}; +use cretonne::ir::immediates::{Imm64, Offset32, Uoffset32, Ieee32, Ieee64}; use cretonne::ir::entities::AnyEntity; use cretonne::ir::instructions::{InstructionFormat, InstructionData, VariableArgs}; use cretonne::isa::{self, TargetIsa, Encoding}; @@ -523,6 +523,22 @@ impl<'a> Parser<'a> { } } + // Match and consume an optional uoffset32 immediate. + // + // Note that that this will match an empty string as an empty offset, and that if an offset is + // present, it must contain a `+` sign. + fn optional_uoffset32(&mut self) -> Result { + if let Some(Token::Integer(text)) = self.token() { + self.consume(); + // Lexer just gives us raw text that looks like an integer. + // Parse it as a `Uoffset32` to check for overflow and other issues. + text.parse().map_err(|e| self.error(e)) + } else { + // A uoffset32 operand can be absent. + Ok(Uoffset32::new(0)) + } + } + // Match and consume an Ieee32 immediate. fn match_ieee32(&mut self, err_msg: &str) -> Result { if let Some(Token::Float(text)) = self.token() {