diff --git a/lib/cretonne/src/ir/extname.rs b/lib/cretonne/src/ir/extname.rs index 36cc18d5c3..f58f8d24ec 100644 --- a/lib/cretonne/src/ir/extname.rs +++ b/lib/cretonne/src/ir/extname.rs @@ -4,7 +4,10 @@ //! function. The name of an external declaration doesn't have any meaning to //! Cretonne, which compiles functions independently. +use ir::LibCall; +use std::cmp; use std::fmt::{self, Write}; +use std::str::FromStr; const TESTCASE_NAME_LENGTH: usize = 16; @@ -23,19 +26,21 @@ pub enum ExternalName { /// A name in a user-defined symbol table. Cretonne does not interpret /// these numbers in any way. User { - /// Arbitrary + /// Arbitrary. namespace: u32, - /// Arbitrary + /// Arbitrary. index: u32, }, /// A test case function name of up to 10 ascii characters. This is /// not intended to be used outside test cases. TestCase { - /// How many of the bytes in `ascii` are valid + /// How many of the bytes in `ascii` are valid? length: u8, - /// Ascii bytes of the name + /// Ascii bytes of the name. ascii: [u8; TESTCASE_NAME_LENGTH], }, + /// A well-known runtime library function. + LibCall(LibCall), } impl ExternalName { @@ -50,20 +55,12 @@ impl ExternalName { /// let name = ExternalName::testcase("hello"); /// assert_eq!(name.to_string(), "%hello"); /// ``` - pub fn testcase(v: T) -> ExternalName - where - T: Into>, - { - let vec = v.into(); - let len = if vec.len() > TESTCASE_NAME_LENGTH { - TESTCASE_NAME_LENGTH - } else { - vec.len() - }; + pub fn testcase>(v: T) -> ExternalName { + let vec = v.as_ref(); + let len = cmp::min(vec.len(), TESTCASE_NAME_LENGTH); let mut bytes = [0u8; TESTCASE_NAME_LENGTH]; - for (i, &byte) in vec.iter().take(len).enumerate() { - bytes[i] = byte; - } + bytes[0..len].copy_from_slice(&vec[0..len]); + ExternalName::TestCase { length: len as u8, ascii: bytes, @@ -104,6 +101,19 @@ impl fmt::Display for ExternalName { } Ok(()) } + ExternalName::LibCall(lc) => write!(f, "%{}", lc), + } + } +} + +impl FromStr for ExternalName { + type Err = (); + + fn from_str(s: &str) -> Result { + // Try to parse as a libcall name, otherwise it's a test case. + match s.parse() { + Ok(lc) => Ok(ExternalName::LibCall(lc)), + Err(_) => Ok(ExternalName::testcase(s.as_bytes())), } } } @@ -111,6 +121,7 @@ impl fmt::Display for ExternalName { #[cfg(test)] mod tests { use super::ExternalName; + use ir::LibCall; #[test] fn display_testcase() { @@ -137,4 +148,16 @@ mod tests { "u4294967295:4294967295" ); } + + #[test] + fn parsing() { + assert_eq!( + "FloorF32".parse(), + Ok(ExternalName::LibCall(LibCall::FloorF32)) + ); + assert_eq!( + ExternalName::LibCall(LibCall::FloorF32).to_string(), + "%FloorF32" + ); + } } diff --git a/lib/cretonne/src/ir/libcall.rs b/lib/cretonne/src/ir/libcall.rs new file mode 100644 index 0000000000..e8c848f71d --- /dev/null +++ b/lib/cretonne/src/ir/libcall.rs @@ -0,0 +1,114 @@ +//! Naming well-known routines in the runtime library. + +use ir::{types, Opcode, Type}; +use std::fmt; +use std::str::FromStr; + +/// The name of a runtime library routine. +/// +/// Runtime library calls are generated for Cretonne IL instructions that don't have an equivalent +/// ISA instruction or an easy macro expansion. A `LibCall` is used as a well-known name to refer to +/// the runtime library routine. This way, Cretonne doesn't have to know about the naming +/// convention in the embedding VM's runtime library. +/// +/// This list is likely to grow over time. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum LibCall { + /// ceil.f32 + CeilF32, + /// ceil.f64 + CeilF64, + /// floor.f32 + FloorF32, + /// floor.f64 + FloorF64, + /// trunc.f32 + TruncF32, + /// frunc.f64 + TruncF64, + /// nearest.f32 + NearestF32, + /// nearest.f64 + NearestF64, +} + +const NAME: [&str; 8] = [ + "CeilF32", + "CeilF64", + "FloorF32", + "FloorF64", + "TruncF32", + "TruncF64", + "NearestF32", + "NearestF64", +]; + +impl fmt::Display for LibCall { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(NAME[*self as usize]) + } +} + +impl FromStr for LibCall { + type Err = (); + + fn from_str(s: &str) -> Result { + match s { + "CeilF32" => Ok(LibCall::CeilF32), + "CeilF64" => Ok(LibCall::CeilF64), + "FloorF32" => Ok(LibCall::FloorF32), + "FloorF64" => Ok(LibCall::FloorF64), + "TruncF32" => Ok(LibCall::TruncF32), + "TruncF64" => Ok(LibCall::TruncF64), + "NearestF32" => Ok(LibCall::NearestF32), + "NearestF64" => Ok(LibCall::NearestF64), + _ => Err(()), + } + } +} + +impl LibCall { + /// Get the well-known library call name to use as a replacement for an instruction with the + /// given opcode and controlling type variable. + /// + /// Returns `None` if no well-known library routine name exists for that instruction. + pub fn for_inst(opcode: Opcode, ctrl_type: Type) -> Option { + Some(match ctrl_type { + types::F32 => { + match opcode { + Opcode::Ceil => LibCall::CeilF32, + Opcode::Floor => LibCall::FloorF32, + Opcode::Trunc => LibCall::TruncF32, + Opcode::Nearest => LibCall::NearestF32, + _ => return None, + } + } + types::F64 => { + match opcode { + Opcode::Ceil => LibCall::CeilF64, + Opcode::Floor => LibCall::FloorF64, + Opcode::Trunc => LibCall::TruncF64, + Opcode::Nearest => LibCall::NearestF64, + _ => return None, + } + } + _ => return None, + }) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn display() { + assert_eq!(LibCall::CeilF32.to_string(), "CeilF32"); + assert_eq!(LibCall::NearestF64.to_string(), "NearestF64"); + } + + #[test] + fn parsing() { + assert_eq!("FloorF32".parse(), Ok(LibCall::FloorF32)); + } +} diff --git a/lib/cretonne/src/ir/mod.rs b/lib/cretonne/src/ir/mod.rs index 3444a52dc6..2144ad6811 100644 --- a/lib/cretonne/src/ir/mod.rs +++ b/lib/cretonne/src/ir/mod.rs @@ -15,6 +15,7 @@ mod extfunc; mod extname; mod globalvar; mod heap; +mod libcall; mod memflags; mod progpoint; mod sourceloc; @@ -33,6 +34,7 @@ pub use ir::heap::{HeapData, HeapStyle, HeapBase}; pub use ir::instructions::{Opcode, InstructionData, VariableArgs, ValueList, ValueListPool}; pub use ir::jumptable::JumpTableData; pub use ir::layout::Layout; +pub use ir::libcall::LibCall; pub use ir::memflags::MemFlags; pub use ir::progpoint::{ProgramPoint, ProgramOrder, ExpandedProgramPoint}; pub use ir::sourceloc::SourceLoc; diff --git a/lib/reader/src/parser.rs b/lib/reader/src/parser.rs index c897d68a3f..17f14af9b1 100644 --- a/lib/reader/src/parser.rs +++ b/lib/reader/src/parser.rs @@ -885,7 +885,9 @@ impl<'a> Parser<'a> { match self.token() { Some(Token::Name(s)) => { self.consume(); - Ok(ExternalName::testcase(s)) + s.parse().map_err( + |_| self.error("invalid test case or libcall name"), + ) } Some(Token::UserRef(namespace)) => { self.consume();