Skip to content

Commit 411d014

Browse files
add AbiMap
1 parent 72013e1 commit 411d014

File tree

4 files changed

+347
-0
lines changed

4 files changed

+347
-0
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3251,6 +3251,7 @@ dependencies = [
32513251
"rustc_macros",
32523252
"rustc_serialize",
32533253
"rustc_span",
3254+
"serde_json",
32543255
"tracing",
32553256
]
32563257

compiler/rustc_abi/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ rustc_index = { path = "../rustc_index", default-features = false }
1414
rustc_macros = { path = "../rustc_macros", optional = true }
1515
rustc_serialize = { path = "../rustc_serialize", optional = true }
1616
rustc_span = { path = "../rustc_span", optional = true }
17+
serde_json = "1"
1718
tracing = "0.1"
1819
# tidy-alphabetical-end
1920

compiler/rustc_abi/src/lib.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ mod callconv;
5858
mod canon_abi;
5959
mod extern_abi;
6060
mod layout;
61+
mod map;
6162
#[cfg(test)]
6263
mod tests;
6364

@@ -67,6 +68,7 @@ pub use extern_abi::{ExternAbi, all_names};
6768
#[cfg(feature = "nightly")]
6869
pub use layout::{FIRST_VARIANT, FieldIdx, Layout, TyAbiInterface, TyAndLayout, VariantIdx};
6970
pub use layout::{LayoutCalculator, LayoutCalculatorError};
71+
pub use map::{AbiMap, AbiNotNormal};
7072

7173
/// Requirements for a `StableHashingContext` to be used in this crate.
7274
/// This is a hack to allow using the `HashStable_Generic` derive macro
@@ -1904,3 +1906,10 @@ pub enum AbiFromStrErr {
19041906
/// no "-unwind" variant
19051907
NoUnwind,
19061908
}
1909+
1910+
#[derive(Clone, Debug)]
1911+
pub enum AbiFromJsonErr {
1912+
InvalidType,
1913+
UnusedKey,
1914+
Parse(AbiFromStrErr),
1915+
}

compiler/rustc_abi/src/map.rs

Lines changed: 336 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,336 @@
1+
use std::collections::{BTreeMap, BTreeSet};
2+
3+
use serde_json::Value as JsonValue;
4+
5+
use crate::{AbiFromJsonErr, ArmCall, CanonAbi, ExternAbi, InterruptKind};
6+
7+
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
8+
pub struct AbiMap {
9+
/// a target may support ABIs that are "proper names" for the C ABI, otherwise this is "C"
10+
pub c_proper: CanonAbi,
11+
12+
// required ABIs
13+
pub system: CanonAbi,
14+
pub system_varargs: CanonAbi,
15+
/// ABI used for `extern "rust-cold"`
16+
pub rust_cold: CanonAbi,
17+
18+
// optional abstract ABIs
19+
pub efiapi: Option<CanonAbi>,
20+
pub stdcall: Option<CanonAbi>,
21+
pub fastcall: Option<CanonAbi>,
22+
pub thiscall: Option<CanonAbi>,
23+
pub vectorcall: Option<CanonAbi>,
24+
pub win64: Option<CanonAbi>,
25+
pub sysv64: Option<CanonAbi>,
26+
27+
// optional concrete ABIs
28+
// arm
29+
pub aapcs: bool,
30+
pub cmse_nonsecure_entry: bool,
31+
// gpu
32+
pub gpu_kernel: bool,
33+
pub ptx_kernel: bool,
34+
// interrupt
35+
pub avr_interrupt: bool,
36+
pub msp430_interrupt: bool,
37+
pub riscv_interrupt: bool,
38+
pub x86_interrupt: bool,
39+
}
40+
41+
// usually the reasonable default, but pay attention to system
42+
impl Default for AbiMap {
43+
fn default() -> AbiMap {
44+
let c = CanonAbi::C;
45+
let rust_cold = CanonAbi::RustCold;
46+
AbiMap {
47+
c_proper: c,
48+
system: c,
49+
system_varargs: c,
50+
rust_cold,
51+
52+
// some mercifully either map or don't
53+
// arm
54+
aapcs: false,
55+
cmse_nonsecure_entry: false,
56+
// gpu
57+
gpu_kernel: false,
58+
ptx_kernel: false,
59+
// interrupt
60+
avr_interrupt: false,
61+
msp430_interrupt: false,
62+
riscv_interrupt: false,
63+
x86_interrupt: false,
64+
65+
// x86ish ones lack a mapping by default, but have many
66+
efiapi: None,
67+
fastcall: None,
68+
stdcall: None,
69+
sysv64: None,
70+
thiscall: None,
71+
vectorcall: None,
72+
win64: None,
73+
}
74+
}
75+
}
76+
77+
#[derive(Clone, Copy, Debug)]
78+
pub enum AbiNotNormal {
79+
CannotVarargs,
80+
NotSupported,
81+
}
82+
83+
impl AbiMap {
84+
/// transform an ExternAbi to a CanonAbi, depending on its vararg usage
85+
pub fn normalize_abi(
86+
&self,
87+
abi: ExternAbi,
88+
has_c_varargs: bool,
89+
) -> Result<CanonAbi, AbiNotNormal> {
90+
// because of feature(extern_system_varargs)
91+
if has_c_varargs && !abi.supports_varargs() && !matches!(abi, ExternAbi::System { .. }) {
92+
return Err(AbiNotNormal::CannotVarargs);
93+
};
94+
let option = match abi {
95+
// An ExternAbi is either
96+
// - "universal" or "optional" in their platform support
97+
// - "invariant" or "varying" in normalization to CanonAbi
98+
99+
// universal, invariant
100+
ExternAbi::C { unwind: _ } | ExternAbi::Cdecl { unwind: _ } => Some(CanonAbi::C),
101+
ExternAbi::Rust => Some(CanonAbi::Rust),
102+
ExternAbi::RustCall => Some(CanonAbi::Rust),
103+
ExternAbi::Unadjusted => Some(CanonAbi::C),
104+
105+
// universal, varying
106+
ExternAbi::RustCold => Some(self.rust_cold),
107+
ExternAbi::System { unwind: _ } => {
108+
Some(if has_c_varargs { self.system_varargs } else { self.system })
109+
}
110+
111+
// optional, varying
112+
ExternAbi::EfiApi => self.efiapi,
113+
ExternAbi::Stdcall { unwind: _ } => self.stdcall,
114+
ExternAbi::Fastcall { unwind: _ } => self.fastcall,
115+
ExternAbi::Vectorcall { unwind: _ } => self.vectorcall,
116+
ExternAbi::Thiscall { unwind: _ } => self.thiscall,
117+
ExternAbi::Win64 { unwind: _ } => self.win64,
118+
ExternAbi::SysV64 { unwind: _ } => self.sysv64,
119+
120+
// optional, invariant
121+
// arm (aarch32)
122+
ExternAbi::Aapcs { unwind: _ } => self.aapcs.then_some(CanonAbi::Arm(ArmCall::Aapcs)),
123+
ExternAbi::CCmseNonSecureCall => {
124+
self.cmse_nonsecure_entry.then_some(CanonAbi::Arm(ArmCall::CCmseNonSecureCall))
125+
}
126+
ExternAbi::CCmseNonSecureEntry => {
127+
self.cmse_nonsecure_entry.then_some(CanonAbi::Arm(ArmCall::CCmseNonSecureEntry))
128+
}
129+
130+
// gpu
131+
ExternAbi::PtxKernel => self.ptx_kernel.then_some(CanonAbi::GpuKernel),
132+
ExternAbi::GpuKernel => self.gpu_kernel.then_some(CanonAbi::GpuKernel),
133+
134+
// interrupt
135+
ExternAbi::AvrInterrupt => {
136+
self.avr_interrupt.then_some(CanonAbi::Interrupt(InterruptKind::Avr))
137+
}
138+
ExternAbi::AvrNonBlockingInterrupt => {
139+
self.avr_interrupt.then_some(CanonAbi::Interrupt(InterruptKind::AvrNonBlocking))
140+
}
141+
ExternAbi::Msp430Interrupt => {
142+
self.msp430_interrupt.then_some(CanonAbi::Interrupt(InterruptKind::Msp430))
143+
}
144+
ExternAbi::RiscvInterruptM => {
145+
self.riscv_interrupt.then_some(CanonAbi::Interrupt(InterruptKind::RiscvMachine))
146+
}
147+
ExternAbi::RiscvInterruptS => {
148+
self.riscv_interrupt.then_some(CanonAbi::Interrupt(InterruptKind::RiscvSupervisor))
149+
}
150+
ExternAbi::X86Interrupt => {
151+
self.x86_interrupt.then_some(CanonAbi::Interrupt(InterruptKind::X86))
152+
}
153+
};
154+
option.ok_or_else(|| AbiNotNormal::NotSupported)
155+
}
156+
}
157+
158+
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
159+
pub struct AbiKey {
160+
abi: ExternAbi,
161+
has_c_varargs: bool,
162+
}
163+
164+
impl AbiMap {
165+
/// map of ExternAbi + has_c_varargs to CanonAbi
166+
// "why not use this as the internal representation of AbiMap?"
167+
// see those ABIs that are only a bool to determine how they lower from ExternAbi?
168+
// yeah, let's not give up that property for no reason.
169+
// it is, however, admittedly easier to work with this
170+
fn to_btree_map(&self) -> BTreeMap<AbiKey, CanonAbi> {
171+
ExternAbi::ALL_VARIANTS
172+
.iter()
173+
.flat_map(|&abi| {
174+
[AbiKey { abi, has_c_varargs: false }, AbiKey { abi, has_c_varargs: true }]
175+
})
176+
.filter_map(|AbiKey { abi, has_c_varargs }| {
177+
let normalized_abi = self.normalize_abi(abi, has_c_varargs).ok()?;
178+
Some((AbiKey { abi, has_c_varargs }, normalized_abi))
179+
})
180+
.collect()
181+
}
182+
183+
/// map of CanonAbi to all codegen-equivalent ExternAbi
184+
pub fn to_denormalizing_map(&self) -> BTreeMap<CanonAbi, BTreeSet<AbiKey>> {
185+
let extern_to_canon = self.to_btree_map();
186+
let mut canon_to_extern = BTreeMap::new();
187+
for (extern_abi_with_varargs, canon_abi) in extern_to_canon {
188+
canon_to_extern
189+
.entry(canon_abi)
190+
.or_insert(BTreeSet::new())
191+
.insert(extern_abi_with_varargs);
192+
}
193+
let all_c: BTreeSet<_> = canon_to_extern[&self.c_proper]
194+
.union(&canon_to_extern[&CanonAbi::C])
195+
.cloned()
196+
.collect();
197+
if let Some(abi_set) = canon_to_extern.get_mut(&self.c_proper) {
198+
abi_set.extend(all_c.clone())
199+
};
200+
if let Some(abi_set) = canon_to_extern.get_mut(&CanonAbi::C) {
201+
abi_set.extend(all_c.clone())
202+
};
203+
canon_to_extern
204+
}
205+
}
206+
207+
// deserialization
208+
209+
type JsonObject = serde_json::Map<String, JsonValue>;
210+
211+
fn extract_abi_str(
212+
json: &mut JsonObject,
213+
s: &str,
214+
) -> Result<Option<CanonAbi>, (String, AbiFromJsonErr)> {
215+
json.remove_entry(s)
216+
.map(|(key_string, abi_str)| {
217+
if let JsonValue::String(abi_str) = abi_str {
218+
abi_str.parse().map_err(|err| (key_string, AbiFromJsonErr::Parse(err)))
219+
} else {
220+
Err((key_string, AbiFromJsonErr::InvalidType))
221+
}
222+
})
223+
.transpose()
224+
}
225+
226+
fn extract_bool_abi(
227+
json: &mut JsonObject,
228+
s: &str,
229+
) -> Result<Option<bool>, (String, AbiFromJsonErr)> {
230+
json.remove_entry(s)
231+
.map(|(key_string, boolean)| {
232+
if let JsonValue::Bool(boolean) = boolean {
233+
Ok(boolean)
234+
} else {
235+
Err((key_string, AbiFromJsonErr::InvalidType))
236+
}
237+
})
238+
.transpose()
239+
}
240+
241+
impl AbiMap {
242+
pub fn from_json_object(
243+
mut json: JsonObject,
244+
) -> Result<Self, BTreeMap<String, AbiFromJsonErr>> {
245+
// extract all keys we are interested in
246+
let required_c_abis =
247+
["C", "system", "system-varargs"].map(|abi_str| extract_abi_str(&mut json, abi_str));
248+
let rust_cold = extract_abi_str(&mut json, "rust-cold");
249+
let bool_abis = [
250+
// arm...
251+
"aapcs",
252+
"cmse-nonsecure-entry",
253+
// ...gpu...
254+
"gpu-kernel",
255+
"ptx-kernel",
256+
// ...interrupt
257+
"avr-interrupt",
258+
"msp430-interrupt",
259+
"riscv-interrupt",
260+
"x86-interrupt",
261+
]
262+
.map(|abi_str| extract_bool_abi(&mut json, abi_str));
263+
// x86ish
264+
let optional_abis =
265+
["efiapi", "stdcall", "fastcall", "thiscall", "vectorcall", "win64", "sysv64"]
266+
.map(|abi_str| extract_abi_str(&mut json, abi_str));
267+
268+
// accumulate errors
269+
// create an iterator of invalid types and bad parses
270+
let errs = required_c_abis
271+
.iter()
272+
.filter_map(|result| result.as_ref().err())
273+
.chain(rust_cold.as_ref().err())
274+
.chain(bool_abis.iter().filter_map(|result| result.as_ref().err()))
275+
.chain(optional_abis.iter().filter_map(|result| result.as_ref().err()));
276+
// consume the JSON object...
277+
let error_map = json
278+
.into_iter()
279+
// ...because any remaining keys are unused
280+
.map(|(key_string, _value)| (key_string, AbiFromJsonErr::UnusedKey))
281+
.chain(errs.cloned())
282+
.collect::<BTreeMap<_, _>>();
283+
284+
if error_map.len() > 0 {
285+
Err(error_map)
286+
} else {
287+
// oh? success? merry giftmas! time to unwrap your presents
288+
// these have default ABIs to select
289+
let [c_proper, system, system_varargs] =
290+
required_c_abis.map(|result| result.unwrap().unwrap_or(CanonAbi::C));
291+
let rust_cold = rust_cold.unwrap().unwrap_or(CanonAbi::RustCold);
292+
293+
// these stay options, but shell the Result
294+
let [efiapi, stdcall, fastcall, thiscall, vectorcall, win64, sysv64] =
295+
optional_abis.map(|result| result.unwrap());
296+
297+
// these simplify to booleans
298+
let bool_abis = bool_abis.map(|result| result.unwrap().unwrap_or(false));
299+
// repeat the mantra: arm...
300+
let [aapcs, cmse_nonsecure_entry, bool_abis @ ..] = bool_abis;
301+
// ...gpu...
302+
let [gpu_kernel, ptx_kernel, bool_abis @ ..] = bool_abis;
303+
// ...interrupt
304+
let [avr_interrupt, msp430_interrupt, riscv_interrupt, x86_interrupt] = bool_abis;
305+
Ok(AbiMap {
306+
c_proper,
307+
system,
308+
system_varargs,
309+
rust_cold,
310+
311+
// arm...
312+
aapcs,
313+
cmse_nonsecure_entry,
314+
315+
// ...gpu...
316+
gpu_kernel,
317+
ptx_kernel,
318+
319+
// ...interrupt
320+
avr_interrupt,
321+
msp430_interrupt,
322+
riscv_interrupt,
323+
x86_interrupt,
324+
325+
// x86-ish
326+
efiapi,
327+
stdcall,
328+
fastcall,
329+
thiscall,
330+
vectorcall,
331+
win64,
332+
sysv64,
333+
})
334+
}
335+
}
336+
}

0 commit comments

Comments
 (0)