pub mod extension;
pub use extension::{Extension, Extensions};
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Error<'a> {
InvalidTriple(&'a str),
InvalidArch(&'a str),
InvalidWidth(usize),
UnknownExtension(&'a str),
UnknownTargetFeature(&'a str),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TargetTriple<'a> {
arch: &'a str,
vendor: &'a str,
os: &'a str,
bin: Option<&'a str>,
}
impl<'a> TryFrom<&'a str> for TargetTriple<'a> {
type Error = Error<'a>;
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
let mut parts = value.split('-');
let arch = parts.next().ok_or(Error::InvalidTriple(value))?;
let vendor = parts.next().ok_or(Error::InvalidTriple(value))?;
let os = parts.next().ok_or(Error::InvalidTriple(value))?;
let bin = parts.next();
Ok(Self {
arch,
vendor,
os,
bin,
})
}
}
impl std::fmt::Display for TargetTriple<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}-{}-{}", self.arch, self.vendor, self.os)?;
if let Some(bin) = self.bin {
write!(f, "-{bin}")?;
}
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Width {
W32,
W64,
W128,
}
impl std::fmt::Display for Width {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::W32 => write!(f, "32"),
Self::W64 => write!(f, "64"),
Self::W128 => write!(f, "128"),
}
}
}
macro_rules! impl_try_from_width {
($($t:ty),*) => {
$(
impl TryFrom<$t> for Width {
type Error = Error<'static>;
fn try_from(bits: $t) -> Result<Self, Self::Error> {
match bits {
32 => Ok(Self::W32),
64 => Ok(Self::W64),
128 => Ok(Self::W128),
_ => Err(Self::Error::InvalidWidth(bits as usize)),
}
}
}
impl From<Width> for $t {
fn from(width: Width) -> Self {
match width {
Width::W32 => 32,
Width::W64 => 64,
Width::W128 => 128,
}
}
}
)*
};
}
impl_try_from_width!(u8, u16, u32, u64, u128, usize, i16, i32, i64, i128, isize);
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RiscvTarget {
width: Width,
extensions: Extensions,
}
fn is_isa_extension(feature: &str) -> bool {
feature != "relax"
}
impl RiscvTarget {
pub fn build<'a>(target: &'a str, cargo_flags: &'a str) -> Result<Self, Error<'a>> {
let triple = TargetTriple::try_from(target)?;
let mut target = Self::try_from(triple)?;
for target_feature in cargo_flags
.split(0x1fu8 as char)
.filter(|arg| arg.starts_with("target-feature="))
.flat_map(|arg| {
let arg = arg.trim_start_matches("target-feature=");
arg.split(',')
})
{
if let Some(feature) = target_feature.strip_prefix('+') {
if is_isa_extension(feature) {
let extension = Extension::try_from(feature)?;
target.extensions.insert(extension);
}
} else if let Some(feature) = target_feature.strip_prefix('-') {
if is_isa_extension(feature) {
let extension = Extension::try_from(feature)?;
target.extensions.remove(&extension);
}
} else {
return Err(Error::UnknownTargetFeature(target_feature));
}
}
Ok(target)
}
pub fn rustc_flags(&self) -> Vec<String> {
self.extensions
.extensions()
.iter()
.map(|e| format!("riscv{e}"))
.collect::<Vec<_>>()
}
pub fn llvm_base_isa(&self) -> String {
match (self.width, self.extensions.base_extension()) {
(Width::W32, Some(Extension::I)) => String::from("rv32i"),
(Width::W32, Some(Extension::E)) => String::from("rv32e"),
(Width::W64, Some(Extension::I)) => String::from("rv64i"),
(Width::W64, Some(Extension::E)) => String::from("rv64e"),
(_, None) => panic!("RISC-V target must have a base extension"),
_ => panic!("LLVM does not support this base ISA"),
}
}
pub fn llvm_arch_patch(&self) -> String {
let mut patch = self.llvm_base_isa();
if self.extensions.contains(&Extension::M) {
patch.push('m');
}
if self.extensions.contains(&Extension::F) {
patch.push('f');
}
if self.extensions.contains(&Extension::D) {
patch.push('d');
}
patch
}
pub fn width(&self) -> Width {
self.width
}
pub fn base_extension(&self) -> Option<Extension> {
self.extensions.base_extension()
}
}
impl<'a> TryFrom<TargetTriple<'a>> for RiscvTarget {
type Error = Error<'a>;
fn try_from(triple: TargetTriple<'a>) -> Result<Self, Self::Error> {
match triple.arch.strip_prefix("riscv") {
Some(arch) => {
match arch
.find(|c: char| !c.is_ascii_digit())
.unwrap_or(arch.len())
{
0 => Err(Error::InvalidArch(arch)),
digit_end => {
let (width_str, extensions_str) = arch.split_at(digit_end);
let width = width_str.parse::<u32>().unwrap().try_into()?;
let extensions = extensions_str.try_into()?;
Ok(Self { width, extensions })
}
}
}
None => Err(Error::InvalidArch(triple.arch)),
}
}
}
#[cfg(test)]
mod test {
#[test]
fn test_parse_target() {
let target = "riscv32imac-unknown-none-elf";
let cargo_flags = "target-feature=+m,-a,+f,+relax";
let target = super::RiscvTarget::build(target, cargo_flags).unwrap();
let rustc_flags = target.rustc_flags();
assert_eq!(rustc_flags, vec!["riscvi", "riscvm", "riscvf", "riscvc"]);
}
}