From c89f0dc01d08cbc79a7bcb8a308585fb1a6544f1 Mon Sep 17 00:00:00 2001 From: Caio Date: Sun, 22 Dec 2024 17:12:42 -0300 Subject: [PATCH] Adjust syntax --- compiler/rustc_data_structures/src/flock.rs | 25 +++ .../rustc_data_structures/src/profiling.rs | 63 ++++++ .../rustc_span/src/analyze_source_file.rs | 160 +++++++++++++++ library/core/src/macros/mod.rs | 52 +++++ library/core/tests/lib.rs | 3 + library/core/tests/macros.rs | 53 +++-- library/core/tests/macros_bootstrap.rs | 193 ++++++++++++++++++ 7 files changed, 528 insertions(+), 21 deletions(-) create mode 100644 library/core/tests/macros_bootstrap.rs diff --git a/compiler/rustc_data_structures/src/flock.rs b/compiler/rustc_data_structures/src/flock.rs index e03962a54ecab..292a33d56469a 100644 --- a/compiler/rustc_data_structures/src/flock.rs +++ b/compiler/rustc_data_structures/src/flock.rs @@ -4,6 +4,7 @@ //! green/native threading. This is just a bare-bones enough solution for //! librustdoc, it is not production quality at all. +#[cfg(bootstrap)] cfg_match! { cfg(target_os = "linux") => { mod linux; @@ -27,4 +28,28 @@ cfg_match! { } } +#[cfg(not(bootstrap))] +cfg_match! { + target_os = "linux" => { + mod linux; + use linux as imp; + } + target_os = "redox" => { + mod linux; + use linux as imp; + } + unix => { + mod unix; + use unix as imp; + } + windows => { + mod windows; + use self::windows as imp; + } + _ => { + mod unsupported; + use unsupported as imp; + } +} + pub use imp::Lock; diff --git a/compiler/rustc_data_structures/src/profiling.rs b/compiler/rustc_data_structures/src/profiling.rs index 19050746c2f18..18e98e6c39fa9 100644 --- a/compiler/rustc_data_structures/src/profiling.rs +++ b/compiler/rustc_data_structures/src/profiling.rs @@ -860,6 +860,7 @@ fn get_thread_id() -> u32 { } // Memory reporting +#[cfg(bootstrap)] cfg_match! { cfg(windows) => { pub fn get_resident_set_size() -> Option { @@ -921,5 +922,67 @@ cfg_match! { } } +#[cfg(not(bootstrap))] +cfg_match! { + windows => { + pub fn get_resident_set_size() -> Option { + use std::mem; + + use windows::{ + Win32::System::ProcessStatus::{K32GetProcessMemoryInfo, PROCESS_MEMORY_COUNTERS}, + Win32::System::Threading::GetCurrentProcess, + }; + + let mut pmc = PROCESS_MEMORY_COUNTERS::default(); + let pmc_size = mem::size_of_val(&pmc); + unsafe { + K32GetProcessMemoryInfo( + GetCurrentProcess(), + &mut pmc, + pmc_size as u32, + ) + } + .ok() + .ok()?; + + Some(pmc.WorkingSetSize) + } + } + target_os = "macos" => { + pub fn get_resident_set_size() -> Option { + use libc::{c_int, c_void, getpid, proc_pidinfo, proc_taskinfo, PROC_PIDTASKINFO}; + use std::mem; + const PROC_TASKINFO_SIZE: c_int = mem::size_of::() as c_int; + + unsafe { + let mut info: proc_taskinfo = mem::zeroed(); + let info_ptr = &mut info as *mut proc_taskinfo as *mut c_void; + let pid = getpid() as c_int; + let ret = proc_pidinfo(pid, PROC_PIDTASKINFO, 0, info_ptr, PROC_TASKINFO_SIZE); + if ret == PROC_TASKINFO_SIZE { + Some(info.pti_resident_size as usize) + } else { + None + } + } + } + } + unix => { + pub fn get_resident_set_size() -> Option { + let field = 1; + let contents = fs::read("/proc/self/statm").ok()?; + let contents = String::from_utf8(contents).ok()?; + let s = contents.split_whitespace().nth(field)?; + let npages = s.parse::().ok()?; + Some(npages * 4096) + } + } + _ => { + pub fn get_resident_set_size() -> Option { + None + } + } +} + #[cfg(test)] mod tests; diff --git a/compiler/rustc_span/src/analyze_source_file.rs b/compiler/rustc_span/src/analyze_source_file.rs index 28ce883daee9a..fba205665803c 100644 --- a/compiler/rustc_span/src/analyze_source_file.rs +++ b/compiler/rustc_span/src/analyze_source_file.rs @@ -29,6 +29,7 @@ pub(crate) fn analyze_source_file(src: &str) -> (Vec, Vec { fn analyze_source_file_dispatch( @@ -185,6 +186,165 @@ cfg_match! { } } } + +#[cfg(not(bootstrap))] +cfg_match! { + any(target_arch = "x86", target_arch = "x86_64") => { + fn analyze_source_file_dispatch( + src: &str, + lines: &mut Vec, + multi_byte_chars: &mut Vec, + ) { + if is_x86_feature_detected!("sse2") { + unsafe { + analyze_source_file_sse2(src, lines, multi_byte_chars); + } + } else { + analyze_source_file_generic( + src, + src.len(), + RelativeBytePos::from_u32(0), + lines, + multi_byte_chars, + ); + } + } + + /// Checks 16 byte chunks of text at a time. If the chunk contains + /// something other than printable ASCII characters and newlines, the + /// function falls back to the generic implementation. Otherwise it uses + /// SSE2 intrinsics to quickly find all newlines. + #[target_feature(enable = "sse2")] + unsafe fn analyze_source_file_sse2( + src: &str, + lines: &mut Vec, + multi_byte_chars: &mut Vec, + ) { + #[cfg(target_arch = "x86")] + use std::arch::x86::*; + #[cfg(target_arch = "x86_64")] + use std::arch::x86_64::*; + + const CHUNK_SIZE: usize = 16; + + let src_bytes = src.as_bytes(); + + let chunk_count = src.len() / CHUNK_SIZE; + + // This variable keeps track of where we should start decoding a + // chunk. If a multi-byte character spans across chunk boundaries, + // we need to skip that part in the next chunk because we already + // handled it. + let mut intra_chunk_offset = 0; + + for chunk_index in 0..chunk_count { + let ptr = src_bytes.as_ptr() as *const __m128i; + // We don't know if the pointer is aligned to 16 bytes, so we + // use `loadu`, which supports unaligned loading. + let chunk = unsafe { _mm_loadu_si128(ptr.add(chunk_index)) }; + + // For character in the chunk, see if its byte value is < 0, which + // indicates that it's part of a UTF-8 char. + let multibyte_test = unsafe { _mm_cmplt_epi8(chunk, _mm_set1_epi8(0)) }; + // Create a bit mask from the comparison results. + let multibyte_mask = unsafe { _mm_movemask_epi8(multibyte_test) }; + + // If the bit mask is all zero, we only have ASCII chars here: + if multibyte_mask == 0 { + assert!(intra_chunk_offset == 0); + + // Check if there are any control characters in the chunk. All + // control characters that we can encounter at this point have a + // byte value less than 32 or ... + let control_char_test0 = unsafe { _mm_cmplt_epi8(chunk, _mm_set1_epi8(32)) }; + let control_char_mask0 = unsafe { _mm_movemask_epi8(control_char_test0) }; + + // ... it's the ASCII 'DEL' character with a value of 127. + let control_char_test1 = unsafe { _mm_cmpeq_epi8(chunk, _mm_set1_epi8(127)) }; + let control_char_mask1 = unsafe { _mm_movemask_epi8(control_char_test1) }; + + let control_char_mask = control_char_mask0 | control_char_mask1; + + if control_char_mask != 0 { + // Check for newlines in the chunk + let newlines_test = unsafe { _mm_cmpeq_epi8(chunk, _mm_set1_epi8(b'\n' as i8)) }; + let newlines_mask = unsafe { _mm_movemask_epi8(newlines_test) }; + + if control_char_mask == newlines_mask { + // All control characters are newlines, record them + let mut newlines_mask = 0xFFFF0000 | newlines_mask as u32; + let output_offset = RelativeBytePos::from_usize(chunk_index * CHUNK_SIZE + 1); + + loop { + let index = newlines_mask.trailing_zeros(); + + if index >= CHUNK_SIZE as u32 { + // We have arrived at the end of the chunk. + break; + } + + lines.push(RelativeBytePos(index) + output_offset); + + // Clear the bit, so we can find the next one. + newlines_mask &= (!1) << index; + } + + // We are done for this chunk. All control characters were + // newlines and we took care of those. + continue; + } else { + // Some of the control characters are not newlines, + // fall through to the slow path below. + } + } else { + // No control characters, nothing to record for this chunk + continue; + } + } + + // The slow path. + // There are control chars in here, fallback to generic decoding. + let scan_start = chunk_index * CHUNK_SIZE + intra_chunk_offset; + intra_chunk_offset = analyze_source_file_generic( + &src[scan_start..], + CHUNK_SIZE - intra_chunk_offset, + RelativeBytePos::from_usize(scan_start), + lines, + multi_byte_chars, + ); + } + + // There might still be a tail left to analyze + let tail_start = chunk_count * CHUNK_SIZE + intra_chunk_offset; + if tail_start < src.len() { + analyze_source_file_generic( + &src[tail_start..], + src.len() - tail_start, + RelativeBytePos::from_usize(tail_start), + lines, + multi_byte_chars, + ); + } + } + } + _ => { + // The target (or compiler version) does not support SSE2 ... + fn analyze_source_file_dispatch( + src: &str, + lines: &mut Vec, + multi_byte_chars: &mut Vec, + ) { + analyze_source_file_generic( + src, + src.len(), + RelativeBytePos::from_u32(0), + lines, + multi_byte_chars, + ); + } + } +} + // `scan_len` determines the number of bytes in `src` to scan. Note that the // function can read past `scan_len` if a multi-byte character start within the // range but extends past it. The overflow is returned by the function. diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index bff7ad98df3fa..611647c427b76 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -224,6 +224,7 @@ pub macro assert_matches { /// } /// } /// ``` +#[cfg(bootstrap)] #[unstable(feature = "cfg_match", issue = "115585")] #[rustc_diagnostic_item = "cfg_match"] pub macro cfg_match { @@ -284,6 +285,57 @@ pub macro cfg_match { } } +/// A macro for defining `#[cfg]` match-like statements. +/// +/// It is similar to the `if/elif` C preprocessor macro by allowing definition of a cascade of +/// `#[cfg]` cases, emitting the implementation which matches first. +/// +/// This allows you to conveniently provide a long list `#[cfg]`'d blocks of code +/// without having to rewrite each clause multiple times. +/// +/// Trailing `_` wildcard match arms are **optional** and they indicate a fallback branch when +/// all previous declarations do not evaluate to true. +/// +/// # Example +/// +/// ``` +/// #![feature(cfg_match)] +/// +/// cfg_match! { +/// unix => { +/// fn foo() { /* unix specific functionality */ } +/// } +/// target_pointer_width = "32" => { +/// fn foo() { /* non-unix, 32-bit functionality */ } +/// } +/// _ => { +/// fn foo() { /* fallback implementation */ } +/// } +/// } +/// ``` +#[cfg(not(bootstrap))] +#[unstable(feature = "cfg_match", issue = "115585")] +#[rustc_diagnostic_item = "cfg_match"] +pub macro cfg_match { + ({ $($tt:tt)* }) => {{ + cfg_match! { $($tt)* } + }}, + (_ => { $($output:tt)* }) => { + $($output)* + }, + ( + $cfg:meta => $output:tt + $($( $rest:tt )+)? + ) => { + #[cfg($cfg)] + cfg_match! { _ => $output } + $( + #[cfg(not($cfg))] + cfg_match! { $($rest)+ } + )? + }, +} + /// Asserts that a boolean expression is `true` at runtime. /// /// This will invoke the [`panic!`] macro if the provided expression cannot be diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 89b65eefd027e..24c78b01c5c5d 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -149,7 +149,10 @@ mod intrinsics; mod io; mod iter; mod lazy; +#[cfg(not(bootstrap))] mod macros; +#[cfg(bootstrap)] +mod macros_bootstrap; mod manually_drop; mod mem; mod net; diff --git a/library/core/tests/macros.rs b/library/core/tests/macros.rs index fdb4ea2941285..b30a40b7df28e 100644 --- a/library/core/tests/macros.rs +++ b/library/core/tests/macros.rs @@ -10,7 +10,7 @@ struct Struct; impl Trait for Struct { cfg_match! { - cfg(feature = "blah") => { + feature = "blah" => { fn blah(&self) { unimplemented!(); } @@ -47,21 +47,21 @@ fn matches_leading_pipe() { #[test] fn cfg_match_basic() { cfg_match! { - cfg(target_pointer_width = "64") => { fn f0_() -> bool { true }} + target_pointer_width = "64" => { fn f0_() -> bool { true }} } cfg_match! { - cfg(unix) => { fn f1_() -> bool { true }} - cfg(any(target_os = "macos", target_os = "linux")) => { fn f1_() -> bool { false }} + unix => { fn f1_() -> bool { true } } + any(target_os = "macos", target_os = "linux") => { fn f1_() -> bool { false }} } cfg_match! { - cfg(target_pointer_width = "32") => { fn f2_() -> bool { false }} - cfg(target_pointer_width = "64") => { fn f2_() -> bool { true }} + target_pointer_width = "32" => { fn f2_() -> bool { false } } + target_pointer_width = "64" => { fn f2_() -> bool { true } } } cfg_match! { - cfg(target_pointer_width = "16") => { fn f3_() -> i32 { 1 }} + target_pointer_width = "16" => { fn f3_() -> i32 { 1 } } _ => { fn f3_() -> i32 { 2 }} } @@ -83,7 +83,7 @@ fn cfg_match_basic() { #[test] fn cfg_match_debug_assertions() { cfg_match! { - cfg(debug_assertions) => { + debug_assertions => { assert!(cfg!(debug_assertions)); assert_eq!(4, 2+2); } @@ -98,13 +98,13 @@ fn cfg_match_debug_assertions() { #[test] fn cfg_match_no_duplication_on_64() { cfg_match! { - cfg(windows) => { + windows => { fn foo() {} } - cfg(unix) => { + unix => { fn foo() {} } - cfg(target_pointer_width = "64") => { + target_pointer_width = "64" => { fn foo() {} } } @@ -114,7 +114,7 @@ fn cfg_match_no_duplication_on_64() { #[test] fn cfg_match_options() { cfg_match! { - cfg(test) => { + test => { use core::option::Option as Option2; fn works1() -> Option2 { Some(1) } } @@ -122,26 +122,26 @@ fn cfg_match_options() { } cfg_match! { - cfg(feature = "foo") => { fn works2() -> bool { false } } - cfg(test) => { fn works2() -> bool { true } } + feature = "foo" => { fn works2() -> bool { false } } + test => { fn works2() -> bool { true } } _ => { fn works2() -> bool { false } } } cfg_match! { - cfg(feature = "foo") => { fn works3() -> bool { false } } + feature = "foo" => { fn works3() -> bool { false } } _ => { fn works3() -> bool { true } } } cfg_match! { - cfg(test) => { + test => { use core::option::Option as Option3; fn works4() -> Option3 { Some(1) } } } cfg_match! { - cfg(feature = "foo") => { fn works5() -> bool { false } } - cfg(test) => { fn works5() -> bool { true } } + feature = "foo" => { fn works5() -> bool { false } } + test => { fn works5() -> bool { true } } } assert!(works1().is_some()); @@ -154,7 +154,7 @@ fn cfg_match_options() { #[test] fn cfg_match_two_functions() { cfg_match! { - cfg(target_pointer_width = "64") => { + target_pointer_width = "64" => { fn foo1() {} fn bar1() {} } @@ -178,7 +178,7 @@ fn cfg_match_two_functions() { fn _accepts_expressions() -> i32 { cfg_match! { - cfg(unix) => { 1 } + unix => { 1 } _ => { 2 } } } @@ -189,7 +189,18 @@ fn _allows_stmt_expr_attributes() { let one = 1; let two = 2; cfg_match! { - cfg(unix) => { one * two; } + unix => { one * two; } _ => { one + two; } } } + +fn _expression() { + let _ = cfg_match!({ + windows => { + " XP" + } + _ => { + "" + } + }); +} diff --git a/library/core/tests/macros_bootstrap.rs b/library/core/tests/macros_bootstrap.rs new file mode 100644 index 0000000000000..f10ef862c5dd9 --- /dev/null +++ b/library/core/tests/macros_bootstrap.rs @@ -0,0 +1,193 @@ +#![allow(unused_must_use)] + +#[allow(dead_code)] +trait Trait { + fn blah(&self); +} + +#[allow(dead_code)] +struct Struct; + +impl Trait for Struct { + cfg_match! { + cfg(feature = "blah") => { + fn blah(&self) { + unimplemented!(); + } + } + _ => { + fn blah(&self) { + unimplemented!(); + } + } + } +} + +#[test] +fn assert_eq_trailing_comma() { + assert_eq!(1, 1,); +} + +#[test] +fn assert_escape() { + assert!(r#"☃\backslash"#.contains("\\")); +} + +#[test] +fn assert_ne_trailing_comma() { + assert_ne!(1, 2,); +} + +#[rustfmt::skip] +#[test] +fn matches_leading_pipe() { + matches!(1, | 1 | 2 | 3); +} + +#[test] +fn cfg_match_basic() { + cfg_match! { + cfg(target_pointer_width = "64") => { fn f0_() -> bool { true }} + } + + cfg_match! { + cfg(unix) => { fn f1_() -> bool { true }} + cfg(any(target_os = "macos", target_os = "linux")) => { fn f1_() -> bool { false }} + } + + cfg_match! { + cfg(target_pointer_width = "32") => { fn f2_() -> bool { false }} + cfg(target_pointer_width = "64") => { fn f2_() -> bool { true }} + } + + cfg_match! { + cfg(target_pointer_width = "16") => { fn f3_() -> i32 { 1 }} + _ => { fn f3_() -> i32 { 2 }} + } + + #[cfg(target_pointer_width = "64")] + assert!(f0_()); + + #[cfg(unix)] + assert!(f1_()); + + #[cfg(target_pointer_width = "32")] + assert!(!f2_()); + #[cfg(target_pointer_width = "64")] + assert!(f2_()); + + #[cfg(not(target_pointer_width = "16"))] + assert_eq!(f3_(), 2); +} + +#[test] +fn cfg_match_debug_assertions() { + cfg_match! { + cfg(debug_assertions) => { + assert!(cfg!(debug_assertions)); + assert_eq!(4, 2+2); + } + _ => { + assert!(cfg!(not(debug_assertions))); + assert_eq!(10, 5+5); + } + } +} + +#[cfg(target_pointer_width = "64")] +#[test] +fn cfg_match_no_duplication_on_64() { + cfg_match! { + cfg(windows) => { + fn foo() {} + } + cfg(unix) => { + fn foo() {} + } + cfg(target_pointer_width = "64") => { + fn foo() {} + } + } + foo(); +} + +#[test] +fn cfg_match_options() { + cfg_match! { + cfg(test) => { + use core::option::Option as Option2; + fn works1() -> Option2 { Some(1) } + } + _ => { fn works1() -> Option { None } } + } + + cfg_match! { + cfg(feature = "foo") => { fn works2() -> bool { false } } + cfg(test) => { fn works2() -> bool { true } } + _ => { fn works2() -> bool { false } } + } + + cfg_match! { + cfg(feature = "foo") => { fn works3() -> bool { false } } + _ => { fn works3() -> bool { true } } + } + + cfg_match! { + cfg(test) => { + use core::option::Option as Option3; + fn works4() -> Option3 { Some(1) } + } + } + + cfg_match! { + cfg(feature = "foo") => { fn works5() -> bool { false } } + cfg(test) => { fn works5() -> bool { true } } + } + + assert!(works1().is_some()); + assert!(works2()); + assert!(works3()); + assert!(works4().is_some()); + assert!(works5()); +} + +#[test] +fn cfg_match_two_functions() { + cfg_match! { + cfg(target_pointer_width = "64") => { + fn foo1() {} + fn bar1() {} + } + _ => { + fn foo2() {} + fn bar2() {} + } + } + + #[cfg(target_pointer_width = "64")] + { + foo1(); + bar1(); + } + #[cfg(not(target_pointer_width = "64"))] + { + foo2(); + bar2(); + } +} + +fn _accepts_expressions() -> i32 { + cfg_match! { + cfg(unix) => { 1 } + _ => { 2 } + } +} + +fn _allows_stmt_expr_attributes() { + let one = 1; + let two = 2; + cfg_match! { + cfg(unix) => { one * two; } + _ => { one + two; } + } +}