Add well-known names for runtime library functions.

Add a LibCall type which represents runtime library functions that many
be synthesized by Cretonne from pure instructions.

Add a LibCall variant to ExternalName to represent one of these runtime
functions.
This commit is contained in:
Jakob Stoklund Olesen
2017-12-07 17:50:22 -08:00
parent 60c456c1ec
commit 362a4bdc4c
4 changed files with 159 additions and 18 deletions

View File

@@ -4,7 +4,10 @@
//! function. The name of an external declaration doesn't have any meaning to //! function. The name of an external declaration doesn't have any meaning to
//! Cretonne, which compiles functions independently. //! Cretonne, which compiles functions independently.
use ir::LibCall;
use std::cmp;
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use std::str::FromStr;
const TESTCASE_NAME_LENGTH: usize = 16; 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 /// A name in a user-defined symbol table. Cretonne does not interpret
/// these numbers in any way. /// these numbers in any way.
User { User {
/// Arbitrary /// Arbitrary.
namespace: u32, namespace: u32,
/// Arbitrary /// Arbitrary.
index: u32, index: u32,
}, },
/// A test case function name of up to 10 ascii characters. This is /// A test case function name of up to 10 ascii characters. This is
/// not intended to be used outside test cases. /// not intended to be used outside test cases.
TestCase { TestCase {
/// How many of the bytes in `ascii` are valid /// How many of the bytes in `ascii` are valid?
length: u8, length: u8,
/// Ascii bytes of the name /// Ascii bytes of the name.
ascii: [u8; TESTCASE_NAME_LENGTH], ascii: [u8; TESTCASE_NAME_LENGTH],
}, },
/// A well-known runtime library function.
LibCall(LibCall),
} }
impl ExternalName { impl ExternalName {
@@ -50,20 +55,12 @@ impl ExternalName {
/// let name = ExternalName::testcase("hello"); /// let name = ExternalName::testcase("hello");
/// assert_eq!(name.to_string(), "%hello"); /// assert_eq!(name.to_string(), "%hello");
/// ``` /// ```
pub fn testcase<T>(v: T) -> ExternalName pub fn testcase<T: AsRef<[u8]>>(v: T) -> ExternalName {
where let vec = v.as_ref();
T: Into<Vec<u8>>, let len = cmp::min(vec.len(), TESTCASE_NAME_LENGTH);
{
let vec = v.into();
let len = if vec.len() > TESTCASE_NAME_LENGTH {
TESTCASE_NAME_LENGTH
} else {
vec.len()
};
let mut bytes = [0u8; TESTCASE_NAME_LENGTH]; let mut bytes = [0u8; TESTCASE_NAME_LENGTH];
for (i, &byte) in vec.iter().take(len).enumerate() { bytes[0..len].copy_from_slice(&vec[0..len]);
bytes[i] = byte;
}
ExternalName::TestCase { ExternalName::TestCase {
length: len as u8, length: len as u8,
ascii: bytes, ascii: bytes,
@@ -104,6 +101,19 @@ impl fmt::Display for ExternalName {
} }
Ok(()) Ok(())
} }
ExternalName::LibCall(lc) => write!(f, "%{}", lc),
}
}
}
impl FromStr for ExternalName {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
// 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)] #[cfg(test)]
mod tests { mod tests {
use super::ExternalName; use super::ExternalName;
use ir::LibCall;
#[test] #[test]
fn display_testcase() { fn display_testcase() {
@@ -137,4 +148,16 @@ mod tests {
"u4294967295:4294967295" "u4294967295:4294967295"
); );
} }
#[test]
fn parsing() {
assert_eq!(
"FloorF32".parse(),
Ok(ExternalName::LibCall(LibCall::FloorF32))
);
assert_eq!(
ExternalName::LibCall(LibCall::FloorF32).to_string(),
"%FloorF32"
);
}
} }

View File

@@ -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<Self, Self::Err> {
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<LibCall> {
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));
}
}

View File

@@ -15,6 +15,7 @@ mod extfunc;
mod extname; mod extname;
mod globalvar; mod globalvar;
mod heap; mod heap;
mod libcall;
mod memflags; mod memflags;
mod progpoint; mod progpoint;
mod sourceloc; 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::instructions::{Opcode, InstructionData, VariableArgs, ValueList, ValueListPool};
pub use ir::jumptable::JumpTableData; pub use ir::jumptable::JumpTableData;
pub use ir::layout::Layout; pub use ir::layout::Layout;
pub use ir::libcall::LibCall;
pub use ir::memflags::MemFlags; pub use ir::memflags::MemFlags;
pub use ir::progpoint::{ProgramPoint, ProgramOrder, ExpandedProgramPoint}; pub use ir::progpoint::{ProgramPoint, ProgramOrder, ExpandedProgramPoint};
pub use ir::sourceloc::SourceLoc; pub use ir::sourceloc::SourceLoc;

View File

@@ -885,7 +885,9 @@ impl<'a> Parser<'a> {
match self.token() { match self.token() {
Some(Token::Name(s)) => { Some(Token::Name(s)) => {
self.consume(); self.consume();
Ok(ExternalName::testcase(s)) s.parse().map_err(
|_| self.error("invalid test case or libcall name"),
)
} }
Some(Token::UserRef(namespace)) => { Some(Token::UserRef(namespace)) => {
self.consume(); self.consume();