Skip to content

Commit 584322e

Browse files
committed
Auto merge of rust-lang#133944 - ricci009:master, r=<try>
[WIP] Prototype run-make test to check `core::ffi::c_*` types against clang Hello, I have been working on issue rust-lang#133058 for a bit of time now and would love some feedback as I seem to be stuck and not sure if I am approaching this correctly. I currently have the following setup: 1. Get the rust target list 2. Use rust target to query the llvm target 3. Get clang definitions through querying the clang command with llvm target. I only save the necessary defines. Here is an example of the saved info (code can easily be modified to store Width as well): Target: riscv64-unknown-linux-musl __CHAR_BIT__ = 8 __CHAR_UNSIGNED__ = 1 __SIZEOF_DOUBLE__ = 8 __SIZEOF_INT__ = 4 __SIZEOF_LONG__ = 8 __SIZEOF_PTRDIFF_T__ = 8 __SIZEOF_SIZE_T__ = 8 __SIZEOF_FLOAT__ = 4 __SIZEOF_LONG_LONG__ = 8 __SIZEOF_SHORT__ = 2 Target: riscv64-unknown-fuchsia __CHAR_UNSIGNED__ = 1 __SIZEOF_SHORT__ = 2 __CHAR_BIT__ = 8 __SIZEOF_INT__ = 4 __SIZEOF_SIZE_T__ = 8 __SIZEOF_FLOAT__ = 4 __SIZEOF_LONG__ = 8 __SIZEOF_DOUBLE__ = 8 __SIZEOF_PTRDIFF_T__ = 8 __SIZEOF_LONG_LONG__ = 8 - I then save this into a hash map with the following format: <LLVM TARGET, <DEFINE NAME, DEFINE VALUE>> - Ex: <x86_64-unknown-linux-gnu, <__SIZEOF_INT__, 4>> This is where it gets a bit shaky as I have been brainstorming ways to get the available c types in core::ffi to verify the size of the defined types but do not think I have the expertise to do this. For the current implementation I specifically focus on the c_char type (unsigned vs signed). The test is currently failing as there are type mismatches which are expected (issue rust-lang#129945 highlights this). I just do not know how to continue executing tests even with the type mismatches as it creates an error when running the run-make test. Or maybe I am doing something wrong in generating the test? Not too sure but would love your input. Thanks r? `@tgross35` `@jieyouxu` try-job: x86_64-gnu-debug
2 parents 633a3fe + 95f7416 commit 584322e

File tree

3 files changed

+382
-0
lines changed

3 files changed

+382
-0
lines changed

tests/auxiliary/minicore.rs

+125
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#![feature(no_core, lang_items, rustc_attrs, decl_macro, naked_functions, f16, f128)]
1818
#![allow(unused, improper_ctypes_definitions, internal_features)]
1919
#![feature(asm_experimental_arch)]
20+
#![feature(intrinsics)]
2021
#![no_std]
2122
#![no_core]
2223

@@ -108,3 +109,127 @@ macro_rules! stringify {
108109
/* compiler built-in */
109110
};
110111
}
112+
113+
macro_rules! cfg_if {
114+
// match if/else chains with a final `else`
115+
(
116+
$(
117+
if #[cfg( $i_meta:meta )] { $( $i_tokens:tt )* }
118+
) else+
119+
else { $( $e_tokens:tt )* }
120+
) => {
121+
cfg_if! {
122+
@__items () ;
123+
$(
124+
(( $i_meta ) ( $( $i_tokens )* )) ,
125+
)+
126+
(() ( $( $e_tokens )* )) ,
127+
}
128+
};
129+
130+
// Internal and recursive macro to emit all the items
131+
//
132+
// Collects all the previous cfgs in a list at the beginning, so they can be
133+
// negated. After the semicolon is all the remaining items.
134+
(@__items ( $( $_:meta , )* ) ; ) => {};
135+
(
136+
@__items ( $( $no:meta , )* ) ;
137+
(( $( $yes:meta )? ) ( $( $tokens:tt )* )) ,
138+
$( $rest:tt , )*
139+
) => {
140+
// Emit all items within one block, applying an appropriate #[cfg]. The
141+
// #[cfg] will require all `$yes` matchers specified and must also negate
142+
// all previous matchers.
143+
#[cfg(all(
144+
$( $yes , )?
145+
not(any( $( $no ),* ))
146+
))]
147+
cfg_if! { @__identity $( $tokens )* }
148+
149+
// Recurse to emit all other items in `$rest`, and when we do so add all
150+
// our `$yes` matchers to the list of `$no` matchers as future emissions
151+
// will have to negate everything we just matched as well.
152+
cfg_if! {
153+
@__items ( $( $no , )* $( $yes , )? ) ;
154+
$( $rest , )*
155+
}
156+
};
157+
158+
// Internal macro to make __apply work out right for different match types,
159+
// because of how macros match/expand stuff.
160+
(@__identity $( $tokens:tt )* ) => {
161+
$( $tokens )*
162+
};
163+
}
164+
165+
#[macro_export]
166+
macro_rules! panic {
167+
($msg:literal) => {
168+
$crate::panic(&$msg)
169+
};
170+
}
171+
172+
#[rustc_intrinsic]
173+
#[rustc_intrinsic_const_stable_indirect]
174+
#[rustc_intrinsic_must_be_overridden]
175+
pub const fn size_of<T>() -> usize {
176+
loop {}
177+
}
178+
179+
#[rustc_intrinsic]
180+
#[rustc_intrinsic_must_be_overridden]
181+
pub const fn abort() -> ! {
182+
loop {}
183+
}
184+
185+
#[lang = "panic"]
186+
#[rustc_const_panic_str]
187+
const fn panic(_expr: &&'static str) -> ! {
188+
abort();
189+
}
190+
191+
#[lang = "eq"]
192+
pub trait PartialEq<Rhs: ?Sized = Self> {
193+
fn eq(&self, other: &Rhs) -> bool;
194+
fn ne(&self, other: &Rhs) -> bool {
195+
!self.eq(other)
196+
}
197+
}
198+
199+
impl PartialEq for usize {
200+
fn eq(&self, other: &usize) -> bool {
201+
(*self) == (*other)
202+
}
203+
}
204+
205+
impl PartialEq for bool {
206+
fn eq(&self, other: &bool) -> bool {
207+
(*self) == (*other)
208+
}
209+
}
210+
211+
#[lang = "bitxor"]
212+
pub trait BitXor<Rhs = Self> {
213+
type Output;
214+
fn bitxor(self, rhs: Rhs) -> Self::Output;
215+
}
216+
217+
impl BitXor for bool {
218+
type Output = bool;
219+
fn bitxor(self, rhs: bool) -> bool {
220+
(self || rhs) && !(self && rhs)
221+
}
222+
}
223+
224+
#[lang = "not"]
225+
pub trait Not {
226+
type Output;
227+
fn not(self) -> Self::Output;
228+
}
229+
230+
impl Not for bool {
231+
type Output = bool;
232+
fn not(self) -> Self {
233+
!self
234+
}
235+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
//@ needs-force-clang-based-tests
2+
3+
use run_make_support::{clang, regex, rfs, rustc};
4+
5+
const SKIPPED_TARGETS: &[&str] = &[
6+
"riscv", //error: unknown target triple 'riscv32e-unknown-none-elf'
7+
"wasm", //error: unknown target triple 'wasm32v1-none'
8+
"xtensa", //error: unknown target triple 'xtensa-esp32-espidf'
9+
];
10+
11+
fn main() {
12+
let targets = get_target_list();
13+
14+
let minicore_path = run_make_support::source_root().join("tests/auxiliary/minicore.rs");
15+
16+
regex_mod();
17+
18+
for target in targets.lines() {
19+
if SKIPPED_TARGETS.iter().any(|prefix| target.starts_with(prefix)) {
20+
continue;
21+
}
22+
23+
let clang_output =
24+
clang().args(&["-E", "-dM", "-x", "c", "/dev/null", "-target", target]).run();
25+
26+
let defines = String::from_utf8(clang_output.stdout()).expect("Invalid UTF-8");
27+
28+
let minicore_content = rfs::read_to_string(&minicore_path);
29+
let mut rmake_content = format!(
30+
r#"
31+
#![no_std]
32+
#![no_core]
33+
#![feature(link_cfg)]
34+
#![allow(unused)]
35+
#![crate_type = "rlib"]
36+
{}
37+
#[path = "processed_mod.rs"]
38+
mod ffi;
39+
#[path = "tests.rs"]
40+
mod tests;
41+
"#,
42+
minicore_content
43+
);
44+
45+
rmake_content.push_str(&format!(
46+
"
47+
const CLANG_C_CHAR_SIZE: usize = {};
48+
const CLANG_C_CHAR_SIGNED: bool = {};
49+
const CLANG_C_SHORT_SIZE: usize = {};
50+
const CLANG_C_INT_SIZE: usize = {};
51+
const CLANG_C_LONG_SIZE: usize = {};
52+
const CLANG_C_LONGLONG_SIZE: usize = {};
53+
const CLANG_C_FLOAT_SIZE: usize = {};
54+
const CLANG_C_DOUBLE_SIZE: usize = {};
55+
",
56+
parse_size(&defines, "CHAR"),
57+
parse_signed(&defines, "CHAR"),
58+
parse_size(&defines, "SHORT"),
59+
parse_size(&defines, "INT"),
60+
parse_size(&defines, "LONG"),
61+
parse_size(&defines, "LONG_LONG"),
62+
parse_size(&defines, "FLOAT"),
63+
parse_size(&defines, "DOUBLE"),
64+
));
65+
66+
// Write to target-specific rmake file
67+
let mut file_name = format!("{}_rmake.rs", target.replace("-", "_"));
68+
69+
if target.starts_with("thumbv8m") {
70+
file_name = String::from("thumbv8m_rmake.rs");
71+
}
72+
73+
rfs::create_file(&file_name);
74+
rfs::write(&file_name, rmake_content);
75+
let rustc_output = rustc()
76+
.arg("-Zunstable-options")
77+
.arg("--emit=metadata")
78+
.arg("--target")
79+
.arg(target)
80+
.arg(&file_name)
81+
.run();
82+
rfs::remove_file(&file_name);
83+
if !rustc_output.status().success() {
84+
panic!("Failed for target {}", target);
85+
}
86+
}
87+
88+
// Cleanup
89+
rfs::remove_file("processed_mod.rs");
90+
}
91+
92+
fn get_target_list() -> String {
93+
let completed_process = rustc().arg("--print").arg("target-list").run();
94+
String::from_utf8(completed_process.stdout()).expect("error not a string")
95+
}
96+
97+
// Helper to parse size from clang defines
98+
fn parse_size(defines: &str, type_name: &str) -> usize {
99+
let search_pattern = format!("__SIZEOF_{}__ ", type_name.to_uppercase());
100+
for line in defines.lines() {
101+
if line.contains(&search_pattern) {
102+
if let Some(size_str) = line.split_whitespace().last() {
103+
return size_str.parse().unwrap_or(0);
104+
}
105+
}
106+
}
107+
108+
// Only allow CHAR to default to 1
109+
if type_name.to_uppercase() == "CHAR" {
110+
return 1;
111+
}
112+
113+
panic!("Could not find size definition for type: {}", type_name);
114+
}
115+
116+
// Helper to parse signedness from clang defines
117+
fn parse_signed(defines: &str, type_name: &str) -> bool {
118+
match type_name.to_uppercase().as_str() {
119+
"CHAR" => {
120+
// Check if char is explicitly unsigned
121+
!defines.lines().any(|line| line.contains("__CHAR_UNSIGNED__"))
122+
}
123+
_ => true,
124+
}
125+
}
126+
127+
// Parse core/ffi/mod.rs to retrieve only necessary macros and type defines
128+
fn regex_mod() {
129+
let mod_path = run_make_support::source_root().join("library/core/src/ffi/mod.rs");
130+
let mut content = rfs::read_to_string(&mod_path);
131+
132+
//remove stability features #![unstable]
133+
let mut re = regex::Regex::new(r"#!?\[(un)?stable[^]]*?\]").unwrap();
134+
content = re.replace_all(&content, "").to_string();
135+
136+
//remove doc features #[doc...]
137+
re = regex::Regex::new(r"#\[doc[^]]*?\]").unwrap();
138+
content = re.replace_all(&content, "").to_string();
139+
140+
//remove lang feature #[lang...]
141+
re = regex::Regex::new(r"#\[lang[^]]*?\]").unwrap();
142+
content = re.replace_all(&content, "").to_string();
143+
144+
//remove non inline modules
145+
re = regex::Regex::new(r".*mod.*;").unwrap();
146+
content = re.replace_all(&content, "").to_string();
147+
148+
//remove use
149+
re = regex::Regex::new(r".*use.*;").unwrap();
150+
content = re.replace_all(&content, "").to_string();
151+
152+
//remove fn fmt {...}
153+
re = regex::Regex::new(r"(?s)fn fmt.*?\{.*?\}").unwrap();
154+
content = re.replace_all(&content, "").to_string();
155+
156+
//rmv impl fmt {...}
157+
re = regex::Regex::new(r"(?s)impl fmt::Debug for.*?\{.*?\}").unwrap();
158+
content = re.replace_all(&content, "").to_string();
159+
160+
let file_name = format!("processed_mod.rs");
161+
162+
rfs::create_file(&file_name);
163+
rfs::write(&file_name, content);
164+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// tests.rs
2+
3+
use super::*; // `super` will include everything from `smallcore` once glued together
4+
5+
cfg_if! {
6+
if #[cfg(all(target_arch = "aarch64", target_abi = "ilp32"))] {
7+
// FIXME: long is not long enough on aarch64 ilp32, should be 8, defaulting to 4
8+
const XFAIL_C_LONG_SIZE: usize = 4;
9+
pub const TEST_C_LONG_SIZE: () = if size_of::<ffi::c_long>() != XFAIL_C_LONG_SIZE {
10+
panic!("wrong c_long size test ilp32");
11+
};
12+
}
13+
else {
14+
// Default test
15+
pub const TEST_C_LONG_SIZE: () = if size_of::<ffi::c_long>() != CLANG_C_LONG_SIZE {
16+
panic!("wrong c_long size");
17+
};
18+
}
19+
}
20+
21+
cfg_if! {
22+
if #[cfg(target_arch = "csky")] {
23+
// FIXME: c_char signedness misallignment on csky, should be signed on CLANG
24+
const XFAIL_C_CHAR_SIGNED: bool = false;
25+
pub const TEST_C_CHAR_UNSIGNED: () = if ffi::c_char::SIGNED ^ XFAIL_C_CHAR_SIGNED {
26+
panic!("mismatched c_char signed, target_arch: csky");
27+
};
28+
}
29+
else if #[cfg(target_arch = "msp430")] {
30+
// FIXME: c_char signedness misallignment on msp430, should be signed on CLANG
31+
const XFAIL_C_CHAR_SIGNED: bool = false; // Change to true for darwin
32+
pub const TEST_C_CHAR_UNSIGNED: () = if ffi::c_char::SIGNED ^ XFAIL_C_CHAR_SIGNED {
33+
panic!("mismatched c_char signed, target_arch: msp430");
34+
};
35+
}
36+
else {
37+
pub const TEST_C_CHAR_UNSIGNED: () = if ffi::c_char::SIGNED ^ CLANG_C_CHAR_SIGNED {
38+
panic!("mismatched c_char sign");
39+
};
40+
}
41+
}
42+
43+
cfg_if! {
44+
if #[cfg(target_arch = "avr")] {
45+
// FIXME: double is not short enough on avr-unknown-gnu-atmega328 (should be 4 bytes)
46+
const XFAIL_C_DOUBLE_SIZE: usize = 8;
47+
pub const TEST_C_DOUBLE_SIZE: () = if size_of::<ffi::c_double>() != XFAIL_C_DOUBLE_SIZE {
48+
panic!("wrong c_double size, target_arch: avr");
49+
};
50+
}
51+
else {
52+
pub const TEST_C_DOUBLE_SIZE: () = if size_of::<ffi::c_double>() != CLANG_C_DOUBLE_SIZE {
53+
panic!("wrong c_double size");
54+
};
55+
}
56+
}
57+
58+
trait Signed {
59+
const SIGNED: bool;
60+
}
61+
62+
impl Signed for i8 {
63+
const SIGNED: bool = true;
64+
}
65+
66+
impl Signed for u8 {
67+
const SIGNED: bool = false;
68+
}
69+
70+
//c_char size
71+
pub const TEST_C_CHAR_SIZE: () = if size_of::<ffi::c_char>() != CLANG_C_CHAR_SIZE {
72+
panic!("wrong c_char size");
73+
};
74+
75+
//c_int size
76+
pub const TEST_C_INT_SIZE: () = if size_of::<ffi::c_int>() != CLANG_C_INT_SIZE {
77+
panic!("mismatched c_int size");
78+
};
79+
80+
//c_short size
81+
pub const TEST_C_SHORT_SIZE: () = if size_of::<ffi::c_short>() != CLANG_C_SHORT_SIZE {
82+
panic!("wrong c_short size");
83+
};
84+
85+
//c_longlong size
86+
pub const TEST_C_LONGLONG_SIZE: () = if size_of::<ffi::c_longlong>() != CLANG_C_LONGLONG_SIZE {
87+
panic!("wrong c_longlong size");
88+
};
89+
90+
//c_float size
91+
pub const TEST_C_FLOAT_SIZE: () = if size_of::<ffi::c_float>() != CLANG_C_FLOAT_SIZE {
92+
panic!("wrong c_float size");
93+
};

0 commit comments

Comments
 (0)