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:
@@ -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"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
114
lib/cretonne/src/ir/libcall.rs
Normal file
114
lib/cretonne/src/ir/libcall.rs
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user