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
|
||||
//! 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<T>(v: T) -> ExternalName
|
||||
where
|
||||
T: Into<Vec<u8>>,
|
||||
{
|
||||
let vec = v.into();
|
||||
let len = if vec.len() > TESTCASE_NAME_LENGTH {
|
||||
TESTCASE_NAME_LENGTH
|
||||
} else {
|
||||
vec.len()
|
||||
};
|
||||
pub fn testcase<T: AsRef<[u8]>>(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<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)]
|
||||
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"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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 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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user