Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 240d723

Browse files
committed
Auto merge of rust-lang#133944 - ricci009:master, r=<try>
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 aeba3c6 + 7edcddd commit 240d723

File tree

3 files changed

+518
-0
lines changed

3 files changed

+518
-0
lines changed

tests/auxiliary/minicore.rs

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,64 @@
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

24+
/// Vendored from the 'cfg_if' crate
25+
26+
macro_rules! cfg_if {
27+
// match if/else chains with a final `else`
28+
(
29+
$(
30+
if #[cfg( $i_meta:meta )] { $( $i_tokens:tt )* }
31+
) else+
32+
else { $( $e_tokens:tt )* }
33+
) => {
34+
cfg_if! {
35+
@__items () ;
36+
$(
37+
(( $i_meta ) ( $( $i_tokens )* )) ,
38+
)+
39+
(() ( $( $e_tokens )* )) ,
40+
}
41+
};
42+
43+
// Internal and recursive macro to emit all the items
44+
//
45+
// Collects all the previous cfgs in a list at the beginning, so they can be
46+
// negated. After the semicolon is all the remaining items.
47+
(@__items ( $( $_:meta , )* ) ; ) => {};
48+
(
49+
@__items ( $( $no:meta , )* ) ;
50+
(( $( $yes:meta )? ) ( $( $tokens:tt )* )) ,
51+
$( $rest:tt , )*
52+
) => {
53+
// Emit all items within one block, applying an appropriate #[cfg]. The
54+
// #[cfg] will require all `$yes` matchers specified and must also negate
55+
// all previous matchers.
56+
#[cfg(all(
57+
$( $yes , )?
58+
not(any( $( $no ),* ))
59+
))]
60+
cfg_if! { @__identity $( $tokens )* }
61+
62+
// Recurse to emit all other items in `$rest`, and when we do so add all
63+
// our `$yes` matchers to the list of `$no` matchers as future emissions
64+
// will have to negate everything we just matched as well.
65+
cfg_if! {
66+
@__items ( $( $no , )* $( $yes , )? ) ;
67+
$( $rest , )*
68+
}
69+
};
70+
71+
// Internal macro to make __apply work out right for different match types,
72+
// because of how macros match/expand stuff.
73+
(@__identity $( $tokens:tt )* ) => {
74+
$( $tokens )*
75+
};
76+
}
77+
2378
// `core` has some exotic `marker_impls!` macro for handling the with-generics cases, but for our
2479
// purposes, just use a simple macro_rules macro.
2580
macro_rules! impl_marker_trait {
@@ -101,10 +156,70 @@ macro_rules! concat {
101156
/* compiler built-in */
102157
};
103158
}
159+
104160
#[rustc_builtin_macro]
105161
#[macro_export]
106162
macro_rules! stringify {
107163
($($t:tt)*) => {
108164
/* compiler built-in */
109165
};
110166
}
167+
168+
#[macro_export]
169+
macro_rules! panic {
170+
($msg:literal) => {
171+
$crate::panic(&$msg)
172+
};
173+
}
174+
175+
#[rustc_intrinsic]
176+
#[rustc_intrinsic_const_stable_indirect]
177+
#[rustc_intrinsic_must_be_overridden]
178+
pub const fn size_of<T>() -> usize {
179+
loop {}
180+
}
181+
182+
#[rustc_intrinsic]
183+
#[rustc_intrinsic_must_be_overridden]
184+
pub const fn abort() -> ! {
185+
loop {}
186+
}
187+
188+
#[lang = "panic"]
189+
#[rustc_const_panic_str]
190+
const fn panic(_expr: &&'static str) -> ! {
191+
abort();
192+
}
193+
194+
#[lang = "eq"]
195+
pub trait PartialEq<Rhs: ?Sized = Self> {
196+
fn eq(&self, other: &Rhs) -> bool;
197+
fn ne(&self, other: &Rhs) -> bool {
198+
!self.eq(other)
199+
}
200+
}
201+
202+
impl PartialEq for usize {
203+
fn eq(&self, other: &usize) -> bool {
204+
(*self) == (*other)
205+
}
206+
}
207+
208+
impl PartialEq for bool {
209+
fn eq(&self, other: &bool) -> bool {
210+
(*self) == (*other)
211+
}
212+
}
213+
214+
#[lang = "not"]
215+
pub trait Not {
216+
type Output;
217+
fn not(self) -> Self::Output;
218+
}
219+
220+
impl Not for bool {
221+
type Output = bool;
222+
fn not(self) -> Self {
223+
!self
224+
}
225+
}
Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
//@ needs-force-clang-based-tests
2+
// This test checks that the clang defines for each target allign with the core ffi types defined in
3+
// mod.rs. Therefore each rust target is queried and the clang defines for each target are read and
4+
// compared to the core sizes to verify all types and sizes allign at buildtime.
5+
//
6+
// If this test fails because Rust adds a target that Clang does not support, this target should be
7+
// added to SKIPPED_TARGETS.
8+
9+
use run_make_support::{clang, llvm_components_contain, regex, rfs, rustc};
10+
11+
// It is not possible to run the Rust test-suite on these targets.
12+
const SKIPPED_TARGETS: &[&str] = &[
13+
"wasm32v1-none",
14+
"xtensa-esp32-espidf",
15+
"xtensa-esp32-none-elf",
16+
"xtensa-esp32s2-espidf",
17+
"xtensa-esp32s2-none-elf",
18+
"xtensa-esp32s3-espidf",
19+
"xtensa-esp32s3-none-elf",
20+
"csky-unknown-linux-gnuabiv2",
21+
"csky-unknown-linux-gnuabiv2hf",
22+
];
23+
24+
/// Map from a Rust target to the Clang target if they are not the same.
25+
const MAPPED_TARGETS: &[(&str, &str)] = &[
26+
("aarch64-apple-ios-sim", "aarch64-apple-ios"),
27+
("aarch64-apple-tvos-sim", "aarch64-apple-tvos"),
28+
("aarch64-apple-visionos-sim", "aarch64-apple-visionos"),
29+
("aarch64-apple-watchos-sim", "aarch64-apple-watchos"),
30+
("x86_64-apple-watchos-sim", "x86_64-apple-watchos"),
31+
("aarch64-pc-windows-gnullvm", "aarch64-pc-windows-gnu"),
32+
("aarch64-unknown-linux-gnu_ilp32", "aarch64-unknown-linux-gnu"),
33+
("aarch64-unknown-none-softfloat", "aarch64-unknown-none"),
34+
("aarch64-unknown-nto-qnx700", "aarch64-unknown-nto-700"),
35+
("aarch64-unknown-nto-qnx710", "aarch64-unknown-nto-710"),
36+
("aarch64-unknown-uefi", "aarch64-unknown"),
37+
("aarch64_be-unknown-linux-gnu_ilp32", "aarch64_be-unknown-linux-gnu"),
38+
("armv5te-unknown-linux-uclibceabi", "armv5te-unknown-linux"),
39+
("armv7-sony-vita-newlibeabihf", "armv7-sony-vita"),
40+
("armv7-unknown-linux-uclibceabi", "armv7-unknown-linux"),
41+
("armv7-unknown-linux-uclibceabihf", "armv7-unknown-linux"),
42+
("avr-unknown-gnu-atmega328", "avr-unknown-gnu"),
43+
("csky-unknown-linux-gnuabiv2", "csky-unknown-linux-gnu"),
44+
("i586-pc-nto-qnx700", "i586-pc-nto-700"),
45+
("i686-pc-windows-gnullvm", "i686-pc-windows-gnu"),
46+
("i686-unknown-uefi", "i686-unknown"),
47+
("loongarch64-unknown-none-softfloat", "loongarch64-unknown-none"),
48+
("mips-unknown-linux-uclibc", "mips-unknown-linux"),
49+
("mipsel-unknown-linux-uclibc", "mipsel-unknown-linux"),
50+
("powerpc-unknown-linux-gnuspe", "powerpc-unknown-linux-gnu"),
51+
("powerpc-unknown-linux-muslspe", "powerpc-unknown-linux-musl"),
52+
("powerpc-wrs-vxworks-spe", "powerpc-wrs-vxworks"),
53+
("x86_64-fortanix-unknown-sgx", "x86_64-fortanix-unknown"),
54+
("x86_64-pc-nto-qnx710", "x86_64-pc-nto-710"),
55+
("x86_64-pc-windows-gnullvm", "x86_64-pc-windows-gnu"),
56+
("x86_64-unknown-l4re-uclibc", "x86_64-unknown-l4re"),
57+
];
58+
59+
fn main() {
60+
let targets = get_target_list();
61+
62+
let minicore_path = run_make_support::source_root().join("tests/auxiliary/minicore.rs");
63+
64+
preprocess_core_ffi();
65+
66+
for target in targets.lines() {
67+
if SKIPPED_TARGETS.iter().any(|&to_skip_target| target == to_skip_target) {
68+
continue;
69+
}
70+
71+
// Map the Rust target string to a Clang target string if needed
72+
// Also replace riscv with necessary replacements to match clang
73+
// If neither just use target string
74+
let ctarget = MAPPED_TARGETS
75+
.iter()
76+
.find(|(rtarget, _)| *rtarget == target)
77+
.map(|(_, ctarget)| ctarget.to_string())
78+
.unwrap_or_else(|| {
79+
if target.starts_with("riscv") {
80+
target
81+
.replace("imac-", "-")
82+
.replace("gc-", "-")
83+
.replace("imafc-", "-")
84+
.replace("imc-", "-")
85+
.replace("ima-", "-")
86+
.replace("im-", "-")
87+
.replace("emc-", "-")
88+
.replace("em-", "-")
89+
.replace("e-", "-")
90+
.replace("i-", "-")
91+
} else {
92+
target.to_string()
93+
}
94+
});
95+
96+
// Run Clang's preprocessor for the relevant target, printing default macro definitions.
97+
let clang_output =
98+
clang().args(&["-E", "-dM", "-x", "c", "/dev/null", "-target", &ctarget]).run();
99+
100+
let defines = String::from_utf8(clang_output.stdout()).expect("Invalid UTF-8");
101+
102+
let minicore_content = rfs::read_to_string(&minicore_path);
103+
let mut rmake_content = format!(
104+
r#"
105+
#![no_std]
106+
#![no_core]
107+
#![feature(link_cfg)]
108+
#![allow(unused)]
109+
#![crate_type = "rlib"]
110+
111+
/* begin minicore content */
112+
{minicore_content}
113+
/* end minicore content */
114+
115+
#[path = "processed_mod.rs"]
116+
mod ffi;
117+
#[path = "tests.rs"]
118+
mod tests;
119+
"#
120+
);
121+
122+
rmake_content.push_str(&format!(
123+
"
124+
const CLANG_C_CHAR_SIZE: usize = {};
125+
const CLANG_C_CHAR_SIGNED: bool = {};
126+
const CLANG_C_SHORT_SIZE: usize = {};
127+
const CLANG_C_INT_SIZE: usize = {};
128+
const CLANG_C_LONG_SIZE: usize = {};
129+
const CLANG_C_LONGLONG_SIZE: usize = {};
130+
const CLANG_C_FLOAT_SIZE: usize = {};
131+
const CLANG_C_DOUBLE_SIZE: usize = {};
132+
const CLANG_C_SIZE_T_SIZE: usize = {};
133+
const CLANG_C_PTRDIFF_T_SIZE: usize = {};
134+
",
135+
parse_size(&defines, "CHAR"),
136+
char_is_signed(&defines),
137+
parse_size(&defines, "SHORT"),
138+
parse_size(&defines, "INT"),
139+
parse_size(&defines, "LONG"),
140+
parse_size(&defines, "LONG_LONG"),
141+
parse_size(&defines, "FLOAT"),
142+
parse_size(&defines, "DOUBLE"),
143+
parse_size(&defines, "SIZE_T"),
144+
parse_size(&defines, "PTRDIFF_T"),
145+
));
146+
147+
// Generate a target-specific rmake file.
148+
// If type misalignments occur,
149+
// generated rmake file name used to identify the failing target.
150+
let file_name = format!("{}_rmake.rs", target.replace("-", "_").replace(".", "_"));
151+
152+
// Attempt to build the test file for the relevant target.
153+
// Tests use constant evaluation, so running is not necessary.
154+
rfs::write(&file_name, rmake_content);
155+
let rustc_output = rustc()
156+
.arg("-Zunstable-options")
157+
.arg("--emit=metadata")
158+
.arg("--target")
159+
.arg(target)
160+
.arg("-o-")
161+
.arg(&file_name)
162+
.run();
163+
rfs::remove_file(&file_name);
164+
if !rustc_output.status().success() {
165+
panic!("Failed for target {}", target);
166+
}
167+
}
168+
169+
// Cleanup
170+
rfs::remove_file("processed_mod.rs");
171+
}
172+
173+
/// Get a list of available targets for 'rustc'.
174+
fn get_target_list() -> String {
175+
let completed_process = rustc().arg("--print").arg("target-list").run();
176+
String::from_utf8(completed_process.stdout()).expect("error not a string")
177+
}
178+
179+
// Helper to parse size from clang defines
180+
fn parse_size(defines: &str, type_name: &str) -> usize {
181+
let search_pattern = format!("__SIZEOF_{}__ ", type_name.to_uppercase());
182+
for line in defines.lines() {
183+
if line.contains(&search_pattern) {
184+
if let Some(size_str) = line.split_whitespace().last() {
185+
return size_str.parse().unwrap_or(0);
186+
}
187+
}
188+
}
189+
190+
// Only allow CHAR to default to 1
191+
if type_name.to_uppercase() == "CHAR" {
192+
return 1;
193+
}
194+
195+
panic!("Could not find size definition for type: {}", type_name);
196+
}
197+
198+
fn char_is_signed(defines: &str) -> bool {
199+
!defines.lines().any(|line| line.contains("__CHAR_UNSIGNED__"))
200+
}
201+
202+
/// Parse core/ffi/mod.rs to retrieve only necessary macros and type defines
203+
fn preprocess_core_ffi() {
204+
let mod_path = run_make_support::source_root().join("library/core/src/ffi/mod.rs");
205+
let mut content = rfs::read_to_string(&mod_path);
206+
207+
//remove stability features #![unstable]
208+
let mut re = regex::Regex::new(r"#!?\[(un)?stable[^]]*?\]").unwrap();
209+
content = re.replace_all(&content, "").to_string();
210+
211+
//remove doc features #[doc...]
212+
re = regex::Regex::new(r"#\[doc[^]]*?\]").unwrap();
213+
content = re.replace_all(&content, "").to_string();
214+
215+
//remove lang feature #[lang...]
216+
re = regex::Regex::new(r"#\[lang[^]]*?\]").unwrap();
217+
content = re.replace_all(&content, "").to_string();
218+
219+
//remove non inline modules
220+
re = regex::Regex::new(r".*mod.*;").unwrap();
221+
content = re.replace_all(&content, "").to_string();
222+
223+
//remove use
224+
re = regex::Regex::new(r".*use.*;").unwrap();
225+
content = re.replace_all(&content, "").to_string();
226+
227+
//remove fn fmt {...}
228+
re = regex::Regex::new(r"(?s)fn fmt.*?\{.*?\}").unwrap();
229+
content = re.replace_all(&content, "").to_string();
230+
231+
//rmv impl fmt {...}
232+
re = regex::Regex::new(r"(?s)impl fmt::Debug for.*?\{.*?\}").unwrap();
233+
content = re.replace_all(&content, "").to_string();
234+
235+
let file_name = "processed_mod.rs";
236+
237+
rfs::write(&file_name, content);
238+
}

0 commit comments

Comments
 (0)