-
Notifications
You must be signed in to change notification settings - Fork 13.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Run-make test to check core::ffi::c_*
types against clang
#133944
base: master
Are you sure you want to change the base?
Changes from all commits
95f7416
4b8291f
98ae709
1a427d7
4e0c7cb
16cc9a0
ea35da9
1b49dbf
7edcddd
cedd098
8fbd055
46a68d1
a88bcbe
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -17,9 +17,64 @@ | |||||||||||||
#![feature(no_core, lang_items, rustc_attrs, decl_macro, naked_functions, f16, f128)] | ||||||||||||||
#![allow(unused, improper_ctypes_definitions, internal_features)] | ||||||||||||||
#![feature(asm_experimental_arch)] | ||||||||||||||
#![feature(intrinsics)] | ||||||||||||||
#![no_std] | ||||||||||||||
#![no_core] | ||||||||||||||
|
||||||||||||||
/// Vendored from the 'cfg_if' crate | ||||||||||||||
|
||||||||||||||
macro_rules! cfg_if { | ||||||||||||||
// match if/else chains with a final `else` | ||||||||||||||
( | ||||||||||||||
$( | ||||||||||||||
if #[cfg( $i_meta:meta )] { $( $i_tokens:tt )* } | ||||||||||||||
) else+ | ||||||||||||||
else { $( $e_tokens:tt )* } | ||||||||||||||
) => { | ||||||||||||||
cfg_if! { | ||||||||||||||
@__items () ; | ||||||||||||||
$( | ||||||||||||||
(( $i_meta ) ( $( $i_tokens )* )) , | ||||||||||||||
)+ | ||||||||||||||
(() ( $( $e_tokens )* )) , | ||||||||||||||
} | ||||||||||||||
}; | ||||||||||||||
|
||||||||||||||
// Internal and recursive macro to emit all the items | ||||||||||||||
// | ||||||||||||||
// Collects all the previous cfgs in a list at the beginning, so they can be | ||||||||||||||
// negated. After the semicolon is all the remaining items. | ||||||||||||||
(@__items ( $( $_:meta , )* ) ; ) => {}; | ||||||||||||||
( | ||||||||||||||
@__items ( $( $no:meta , )* ) ; | ||||||||||||||
(( $( $yes:meta )? ) ( $( $tokens:tt )* )) , | ||||||||||||||
$( $rest:tt , )* | ||||||||||||||
) => { | ||||||||||||||
// Emit all items within one block, applying an appropriate #[cfg]. The | ||||||||||||||
// #[cfg] will require all `$yes` matchers specified and must also negate | ||||||||||||||
// all previous matchers. | ||||||||||||||
#[cfg(all( | ||||||||||||||
$( $yes , )? | ||||||||||||||
not(any( $( $no ),* )) | ||||||||||||||
))] | ||||||||||||||
cfg_if! { @__identity $( $tokens )* } | ||||||||||||||
|
||||||||||||||
// Recurse to emit all other items in `$rest`, and when we do so add all | ||||||||||||||
// our `$yes` matchers to the list of `$no` matchers as future emissions | ||||||||||||||
// will have to negate everything we just matched as well. | ||||||||||||||
cfg_if! { | ||||||||||||||
@__items ( $( $no , )* $( $yes , )? ) ; | ||||||||||||||
$( $rest , )* | ||||||||||||||
} | ||||||||||||||
}; | ||||||||||||||
|
||||||||||||||
// Internal macro to make __apply work out right for different match types, | ||||||||||||||
// because of how macros match/expand stuff. | ||||||||||||||
(@__identity $( $tokens:tt )* ) => { | ||||||||||||||
$( $tokens )* | ||||||||||||||
}; | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
// `core` has some exotic `marker_impls!` macro for handling the with-generics cases, but for our | ||||||||||||||
// purposes, just use a simple macro_rules macro. | ||||||||||||||
macro_rules! impl_marker_trait { | ||||||||||||||
|
@@ -101,10 +156,70 @@ macro_rules! concat { | |||||||||||||
/* compiler built-in */ | ||||||||||||||
}; | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
#[rustc_builtin_macro] | ||||||||||||||
#[macro_export] | ||||||||||||||
macro_rules! stringify { | ||||||||||||||
($($t:tt)*) => { | ||||||||||||||
/* compiler built-in */ | ||||||||||||||
}; | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
#[macro_export] | ||||||||||||||
macro_rules! panic { | ||||||||||||||
($msg:literal) => { | ||||||||||||||
$crate::panic(&$msg) | ||||||||||||||
}; | ||||||||||||||
} | ||||||||||||||
Comment on lines
+168
to
+173
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Problem [PANIC 2/2]: ... which is why this has an extra |
||||||||||||||
|
||||||||||||||
#[rustc_intrinsic] | ||||||||||||||
#[rustc_intrinsic_const_stable_indirect] | ||||||||||||||
#[rustc_intrinsic_must_be_overridden] | ||||||||||||||
pub const fn size_of<T>() -> usize { | ||||||||||||||
loop {} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
#[rustc_intrinsic] | ||||||||||||||
#[rustc_intrinsic_must_be_overridden] | ||||||||||||||
pub const fn abort() -> ! { | ||||||||||||||
loop {} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
#[lang = "panic"] | ||||||||||||||
#[rustc_const_panic_str] | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Problem: I think we may need to apply There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am a bit confused on this to be honest. Would I just apply #[lang = "panic"]
#[rustc_const_panic_str]
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]
#[cfg_attr(feature = "panic_immediate_abort", inline)]
#[track_caller]
#[rustc_nounwind]
const fn panic(_expr: &'static str) -> ! {
abort()
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For the time being, I would make sure that the following attrs are included to minimize the difference versus the actual decl:
to line up the attributes. I wouldn't yet bother with In the long run, it would be very nice if we didn't need a |
||||||||||||||
const fn panic(_expr: &&'static str) -> ! { | ||||||||||||||
abort(); | ||||||||||||||
} | ||||||||||||||
Comment on lines
+188
to
+192
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Problem [PANIC 1/2]: AFAICT, this signature is wrong (this signature has one layer of reference too many), because: rust/library/core/src/panicking.rs Lines 128 to 133 in c37fbd8
|
||||||||||||||
|
||||||||||||||
#[lang = "eq"] | ||||||||||||||
pub trait PartialEq<Rhs: ?Sized = Self> { | ||||||||||||||
fn eq(&self, other: &Rhs) -> bool; | ||||||||||||||
fn ne(&self, other: &Rhs) -> bool { | ||||||||||||||
!self.eq(other) | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
impl PartialEq for usize { | ||||||||||||||
fn eq(&self, other: &usize) -> bool { | ||||||||||||||
(*self) == (*other) | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
Comment on lines
+202
to
+206
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: I think you could use a macro to construct these |
||||||||||||||
|
||||||||||||||
impl PartialEq for bool { | ||||||||||||||
fn eq(&self, other: &bool) -> bool { | ||||||||||||||
(*self) == (*other) | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
#[lang = "not"] | ||||||||||||||
pub trait Not { | ||||||||||||||
type Output; | ||||||||||||||
fn not(self) -> Self::Output; | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
impl Not for bool { | ||||||||||||||
type Output = bool; | ||||||||||||||
fn not(self) -> Self { | ||||||||||||||
!self | ||||||||||||||
} | ||||||||||||||
} |
tgross35 marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,194 @@ | ||||||||||
//@ needs-force-clang-based-tests | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit (style): can you move this directive below, and change the |
||||||||||
// This test checks that the clang defines for each target allign with the core ffi types defined in | ||||||||||
// mod.rs. Therefore each rust target is queried and the clang defines for each target are read and | ||||||||||
Comment on lines
+2
to
+3
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: (after you finish the |
||||||||||
// compared to the core sizes to verify all types and sizes allign at buildtime. | ||||||||||
// | ||||||||||
// If this test fails because Rust adds a target that Clang does not support, this target should be | ||||||||||
// added to SKIPPED_TARGETS. | ||||||||||
Comment on lines
+6
to
+7
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit (style):
Suggested change
|
||||||||||
|
||||||||||
use run_make_support::{clang, regex, rfs, rustc, serde_json}; | ||||||||||
use serde_json::Value; | ||||||||||
|
||||||||||
// It is not possible to run the Rust test-suite on these targets. | ||||||||||
ricci009 marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: could we elaborate why we can't run this test against these targets?
jieyouxu marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
const SKIPPED_TARGETS: &[&str] = &[ | ||||||||||
"xtensa-esp32-espidf", | ||||||||||
"xtensa-esp32-none-elf", | ||||||||||
"xtensa-esp32s2-espidf", | ||||||||||
"xtensa-esp32s2-none-elf", | ||||||||||
"xtensa-esp32s3-espidf", | ||||||||||
"xtensa-esp32s3-none-elf", | ||||||||||
"csky-unknown-linux-gnuabiv2", | ||||||||||
"csky-unknown-linux-gnuabiv2hf", | ||||||||||
]; | ||||||||||
|
||||||||||
const MAPPED_TARGETS: &[(&str, &str)] = &[ | ||||||||||
("armv5te-unknown-linux-uclibcgnueabi", "armv5te-unknown-linux"), | ||||||||||
("mips-unknown-linux-uclibc", "mips-unknown-linux"), | ||||||||||
("mipsel-unknown-linux-uclibc", "mips-unknown-linux"), | ||||||||||
("powerpc-unknown-linux-gnuspe", "powerpc-unknown-linux-gnu"), | ||||||||||
("powerpc-unknown-linux-muslspe", "powerpc-unknown-linux-musl"), | ||||||||||
("x86_64-unknown-l4re-uclibc", "x86_64-unknown-l4re"), | ||||||||||
]; | ||||||||||
Comment on lines
+24
to
+31
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are these targets where we have the wrong LLVM string and should fix that, or where LLVM is different from Clang? A note would be good. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The first val in the pair is a viable llvm target but it is not supported on clang. Hence I just map it to the "default" version that is supported on clang. For reference:
clang attempt to build:
I remove 'uclibc' from the target and proceed to compile with clang. A follow up on this is what should the non-working llvm target map to? Is it viable to assume I can map it to mips-unknown-linux? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is fine, the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: indeed, please document why this mapping is needed (and what purpose it serves) |
||||||||||
|
||||||||||
fn main() { | ||||||||||
let minicore_path = run_make_support::source_root().join("tests/auxiliary/minicore.rs"); | ||||||||||
|
||||||||||
preprocess_core_ffi(); | ||||||||||
|
||||||||||
let targets_json = | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
rustc().arg("--print").arg("all-target-specs-json").arg("-Z").arg("unstable-options").run(); | ||||||||||
let targets_json_str = | ||||||||||
String::from_utf8(targets_json.stdout().to_vec()).expect("error not a string"); | ||||||||||
|
||||||||||
let j: Value = serde_json::from_str(&targets_json_str).unwrap(); | ||||||||||
for (target, v) in j.as_object().unwrap() { | ||||||||||
let llvm_target = &v["llvm-target"].as_str().unwrap(); | ||||||||||
|
||||||||||
if SKIPPED_TARGETS.iter().any(|&to_skip_target| target == to_skip_target) { | ||||||||||
continue; | ||||||||||
} | ||||||||||
|
||||||||||
// Create a new variable to hold either the mapped target or original llvm_target | ||||||||||
let target_to_use = MAPPED_TARGETS | ||||||||||
.iter() | ||||||||||
.find(|&&(from, _)| from == *llvm_target) | ||||||||||
.map(|&(_, to)| to) | ||||||||||
.unwrap_or(llvm_target); | ||||||||||
|
||||||||||
// Run Clang's preprocessor for the relevant target, printing default macro definitions. | ||||||||||
let clang_output = | ||||||||||
clang().args(&["-E", "-dM", "-x", "c", "/dev/null", "-target", &target_to_use]).run(); | ||||||||||
|
||||||||||
let defines = String::from_utf8(clang_output.stdout()).expect("Invalid UTF-8"); | ||||||||||
|
||||||||||
let minicore_content = rfs::read_to_string(&minicore_path); | ||||||||||
let mut rmake_content = format!( | ||||||||||
r#" | ||||||||||
#![no_std] | ||||||||||
#![no_core] | ||||||||||
#![feature(link_cfg)] | ||||||||||
#![allow(unused)] | ||||||||||
#![crate_type = "rlib"] | ||||||||||
|
||||||||||
/* begin minicore content */ | ||||||||||
{minicore_content} | ||||||||||
/* end minicore content */ | ||||||||||
|
||||||||||
#[path = "processed_mod.rs"] | ||||||||||
mod ffi; | ||||||||||
#[path = "tests.rs"] | ||||||||||
mod tests; | ||||||||||
"# | ||||||||||
); | ||||||||||
|
||||||||||
rmake_content.push_str(&format!( | ||||||||||
" | ||||||||||
const CLANG_C_CHAR_SIZE: usize = {}; | ||||||||||
const CLANG_C_CHAR_SIGNED: bool = {}; | ||||||||||
const CLANG_C_SHORT_SIZE: usize = {}; | ||||||||||
const CLANG_C_INT_SIZE: usize = {}; | ||||||||||
const CLANG_C_LONG_SIZE: usize = {}; | ||||||||||
const CLANG_C_LONGLONG_SIZE: usize = {}; | ||||||||||
const CLANG_C_FLOAT_SIZE: usize = {}; | ||||||||||
const CLANG_C_DOUBLE_SIZE: usize = {}; | ||||||||||
const CLANG_C_SIZE_T_SIZE: usize = {}; | ||||||||||
const CLANG_C_PTRDIFF_T_SIZE: usize = {}; | ||||||||||
", | ||||||||||
parse_size(&defines, "CHAR"), | ||||||||||
char_is_signed(&defines), | ||||||||||
parse_size(&defines, "SHORT"), | ||||||||||
parse_size(&defines, "INT"), | ||||||||||
parse_size(&defines, "LONG"), | ||||||||||
parse_size(&defines, "LONG_LONG"), | ||||||||||
parse_size(&defines, "FLOAT"), | ||||||||||
parse_size(&defines, "DOUBLE"), | ||||||||||
parse_size(&defines, "SIZE_T"), | ||||||||||
parse_size(&defines, "PTRDIFF_T"), | ||||||||||
)); | ||||||||||
|
||||||||||
// Generate a target-specific rmake file. | ||||||||||
// If type misalignments occur, | ||||||||||
// generated rmake file name used to identify the failing target. | ||||||||||
let file_name = format!("{}_rmake.rs", target.replace("-", "_").replace(".", "_")); | ||||||||||
|
||||||||||
// Attempt to build the test file for the relevant target. | ||||||||||
// Tests use constant evaluation, so running is not necessary. | ||||||||||
rfs::write(&file_name, rmake_content); | ||||||||||
let rustc_output = rustc() | ||||||||||
.arg("-Zunstable-options") | ||||||||||
.arg("--emit=metadata") | ||||||||||
.arg("--target") | ||||||||||
.arg(target) | ||||||||||
.arg("-o-") | ||||||||||
.arg(&file_name) | ||||||||||
.run(); | ||||||||||
rfs::remove_file(&file_name); | ||||||||||
if !rustc_output.status().success() { | ||||||||||
panic!("Failed for target {}", target); | ||||||||||
} | ||||||||||
} | ||||||||||
|
||||||||||
// Cleanup | ||||||||||
rfs::remove_file("processed_mod.rs"); | ||||||||||
Comment on lines
+131
to
+132
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: not necessary, if a run-make test is successful and you only created temporary files under CWD, the whole directory will be cleaned up. |
||||||||||
} | ||||||||||
|
||||||||||
// Helper to parse size from clang defines | ||||||||||
fn parse_size(defines: &str, type_name: &str) -> usize { | ||||||||||
let search_pattern = format!("__SIZEOF_{}__ ", type_name.to_uppercase()); | ||||||||||
for line in defines.lines() { | ||||||||||
if line.contains(&search_pattern) { | ||||||||||
if let Some(size_str) = line.split_whitespace().last() { | ||||||||||
return size_str.parse().unwrap_or(0); | ||||||||||
} | ||||||||||
} | ||||||||||
} | ||||||||||
|
||||||||||
// Only allow CHAR to default to 1 | ||||||||||
if type_name.to_uppercase() == "CHAR" { | ||||||||||
return 1; | ||||||||||
} | ||||||||||
|
||||||||||
panic!("Could not find size definition for type: {}", type_name); | ||||||||||
} | ||||||||||
|
||||||||||
fn char_is_signed(defines: &str) -> bool { | ||||||||||
!defines.lines().any(|line| line.contains("__CHAR_UNSIGNED__")) | ||||||||||
} | ||||||||||
|
||||||||||
/// Parse core/ffi/mod.rs to retrieve only necessary macros and type defines | ||||||||||
fn preprocess_core_ffi() { | ||||||||||
let mod_path = run_make_support::source_root().join("library/core/src/ffi/mod.rs"); | ||||||||||
let mut content = rfs::read_to_string(&mod_path); | ||||||||||
|
||||||||||
//remove stability features #![unstable] | ||||||||||
let mut re = regex::Regex::new(r"#!?\[(un)?stable[^]]*?\]").unwrap(); | ||||||||||
content = re.replace_all(&content, "").to_string(); | ||||||||||
|
||||||||||
//remove doc features #[doc...] | ||||||||||
re = regex::Regex::new(r"#\[doc[^]]*?\]").unwrap(); | ||||||||||
content = re.replace_all(&content, "").to_string(); | ||||||||||
|
||||||||||
//remove lang feature #[lang...] | ||||||||||
re = regex::Regex::new(r"#\[lang[^]]*?\]").unwrap(); | ||||||||||
content = re.replace_all(&content, "").to_string(); | ||||||||||
|
||||||||||
//remove non inline modules | ||||||||||
re = regex::Regex::new(r".*mod.*;").unwrap(); | ||||||||||
content = re.replace_all(&content, "").to_string(); | ||||||||||
|
||||||||||
//remove use | ||||||||||
re = regex::Regex::new(r".*use.*;").unwrap(); | ||||||||||
content = re.replace_all(&content, "").to_string(); | ||||||||||
|
||||||||||
//remove fn fmt {...} | ||||||||||
re = regex::Regex::new(r"(?s)fn fmt.*?\{.*?\}").unwrap(); | ||||||||||
content = re.replace_all(&content, "").to_string(); | ||||||||||
|
||||||||||
//rmv impl fmt {...} | ||||||||||
re = regex::Regex::new(r"(?s)impl fmt::Debug for.*?\{.*?\}").unwrap(); | ||||||||||
content = re.replace_all(&content, "").to_string(); | ||||||||||
|
||||||||||
let file_name = "processed_mod.rs"; | ||||||||||
|
||||||||||
rfs::write(&file_name, content); | ||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Problem: this isn't actually a
core
macro. If you wantcfg_if
, can you actually just depend oncfg_if
-the-crate insrc/tools/run-make-support
, then re-exportcfg_if
viarun-make-support
? Arun-make
test will have access torun-make-support
. See howobject
orregex
are re-exported inrun-make-support
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I see, you actually want
cfg_if
for the test file. In that case, can you just putcfg_if
in the test file, and not inminicore.rs
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately cfg_if is needed for
processed_mod.rs
. What I could do to remove it from minicore.rs (although this is really ugly code) is put it into the rmake_content string.