1pub mod extension;
2pub use extension::{Extension, Extensions};
3
4#[derive(Debug, Copy, Clone, PartialEq, Eq)]
6pub enum Error<'a> {
7 InvalidTriple(&'a str),
8 InvalidArch(&'a str),
9 InvalidWidth(usize),
10 UnknownExtension(&'a str),
11 UnknownTargetFeature(&'a str),
12}
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16pub struct TargetTriple<'a> {
17 arch: &'a str,
18 vendor: &'a str,
19 os: &'a str,
20 bin: Option<&'a str>,
21}
22
23impl<'a> TryFrom<&'a str> for TargetTriple<'a> {
24 type Error = Error<'a>;
25
26 fn try_from(value: &'a str) -> Result<Self, Self::Error> {
27 let mut parts = value.split('-');
28
29 let arch = parts.next().ok_or(Error::InvalidTriple(value))?;
30 let vendor = parts.next().ok_or(Error::InvalidTriple(value))?;
31 let os = parts.next().ok_or(Error::InvalidTriple(value))?;
32 let bin = parts.next();
33
34 Ok(Self {
35 arch,
36 vendor,
37 os,
38 bin,
39 })
40 }
41}
42
43impl std::fmt::Display for TargetTriple<'_> {
44 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
45 write!(f, "{}-{}-{}", self.arch, self.vendor, self.os)?;
46 if let Some(bin) = self.bin {
47 write!(f, "-{bin}")?;
48 }
49 Ok(())
50 }
51}
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
55pub enum Width {
56 W32,
58 W64,
60 W128,
62}
63
64impl std::fmt::Display for Width {
65 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
66 match self {
67 Self::W32 => write!(f, "32"),
68 Self::W64 => write!(f, "64"),
69 Self::W128 => write!(f, "128"),
70 }
71 }
72}
73
74macro_rules! impl_try_from_width {
75 ($($t:ty),*) => {
76 $(
77 impl TryFrom<$t> for Width {
78 type Error = Error<'static>;
79 fn try_from(bits: $t) -> Result<Self, Self::Error> {
80 match bits {
81 32 => Ok(Self::W32),
82 64 => Ok(Self::W64),
83 128 => Ok(Self::W128),
84 _ => Err(Self::Error::InvalidWidth(bits as usize)),
85 }
86 }
87 }
88 impl From<Width> for $t {
89 fn from(width: Width) -> Self {
90 match width {
91 Width::W32 => 32,
92 Width::W64 => 64,
93 Width::W128 => 128,
94 }
95 }
96 }
97 )*
98 };
99}
100impl_try_from_width!(u8, u16, u32, u64, u128, usize, i16, i32, i64, i128, isize);
101
102#[derive(Debug, Clone, PartialEq, Eq)]
104pub struct RiscvTarget {
105 width: Width,
106 extensions: Extensions,
107}
108
109fn is_isa_extension(feature: &str) -> bool {
112 feature != "relax"
113}
114
115impl RiscvTarget {
116 pub fn build<'a>(target: &'a str, cargo_flags: &'a str) -> Result<Self, Error<'a>> {
132 let triple = TargetTriple::try_from(target)?;
133 let mut target = Self::try_from(triple)?;
134
135 for target_feature in cargo_flags
136 .split(0x1fu8 as char)
137 .filter(|arg| arg.starts_with("target-feature="))
138 .flat_map(|arg| {
139 let arg = arg.trim_start_matches("target-feature=");
140 arg.split(',')
141 })
142 {
143 if let Some(feature) = target_feature.strip_prefix('+') {
144 if is_isa_extension(feature) {
145 let extension = Extension::try_from(feature)?;
146 target.extensions.insert(extension);
147 }
148 } else if let Some(feature) = target_feature.strip_prefix('-') {
149 if is_isa_extension(feature) {
150 let extension = Extension::try_from(feature)?;
151 target.extensions.remove(&extension);
152 }
153 } else {
154 return Err(Error::UnknownTargetFeature(target_feature));
155 }
156 }
157 Ok(target)
158 }
159
160 pub fn rustc_flags(&self) -> Vec<String> {
175 self.extensions
176 .extensions()
177 .iter()
178 .map(|e| format!("riscv{e}"))
179 .collect::<Vec<_>>()
180 }
181
182 pub fn llvm_base_isa(&self) -> String {
184 match (self.width, self.extensions.base_extension()) {
185 (Width::W32, Some(Extension::I)) => String::from("rv32i"),
186 (Width::W32, Some(Extension::E)) => String::from("rv32e"),
187 (Width::W64, Some(Extension::I)) => String::from("rv64i"),
188 (Width::W64, Some(Extension::E)) => String::from("rv64e"),
189 (_, None) => panic!("RISC-V target must have a base extension"),
190 _ => panic!("LLVM does not support this base ISA"),
191 }
192 }
193
194 pub fn llvm_arch_patch(&self) -> String {
206 let mut patch = self.llvm_base_isa();
207 if self.extensions.contains(&Extension::M) {
208 patch.push('m');
209 }
210 if self.extensions.contains(&Extension::F) {
211 patch.push('f');
212 }
213 if self.extensions.contains(&Extension::D) {
214 patch.push('d');
215 }
216 patch
217 }
218
219 pub fn width(&self) -> Width {
221 self.width
222 }
223
224 pub fn base_extension(&self) -> Option<Extension> {
226 self.extensions.base_extension()
227 }
228}
229
230impl<'a> TryFrom<TargetTriple<'a>> for RiscvTarget {
231 type Error = Error<'a>;
232
233 fn try_from(triple: TargetTriple<'a>) -> Result<Self, Self::Error> {
234 match triple.arch.strip_prefix("riscv") {
235 Some(arch) => {
236 match arch
237 .find(|c: char| !c.is_ascii_digit())
238 .unwrap_or(arch.len())
239 {
240 0 => Err(Error::InvalidArch(arch)),
241 digit_end => {
242 let (width_str, extensions_str) = arch.split_at(digit_end);
243 let width = width_str.parse::<u32>().unwrap().try_into()?;
244 let extensions = extensions_str.try_into()?;
245 Ok(Self { width, extensions })
246 }
247 }
248 }
249 None => Err(Error::InvalidArch(triple.arch)),
250 }
251 }
252}
253
254#[cfg(test)]
255mod test {
256 #[test]
257 fn test_parse_target() {
258 let target = "riscv32imac-unknown-none-elf";
259 let cargo_flags = "target-feature=+m,-a,+f,+relax";
260 let target = super::RiscvTarget::build(target, cargo_flags).unwrap();
261 let rustc_flags = target.rustc_flags();
262 assert_eq!(rustc_flags, vec!["riscvi", "riscvm", "riscvf", "riscvc"]);
263 }
264}