From 8dec09f3c5bf8e1f12c6ba6eb6040d710353ca63 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 15 Jan 2025 21:57:10 +0100 Subject: [PATCH 1/9] support wasm inline assembly in naked functions --- .../rustc_codegen_ssa/src/mir/naked_asm.rs | 140 ++++++++++++- tests/assembly/wasm32-naked-fn.rs | 198 ++++++++++++++++++ 2 files changed, 334 insertions(+), 4 deletions(-) create mode 100644 tests/assembly/wasm32-naked-fn.rs diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index 8df270abc8173..35e0ea545258f 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -1,10 +1,12 @@ +use rustc_abi::{BackendRepr, Float, Integer, Primitive, RegKind}; use rustc_attr_parsing::InstructionSetAttr; use rustc_middle::mir::mono::{Linkage, MonoItem, MonoItemData, Visibility}; use rustc_middle::mir::{Body, InlineAsmOperand}; -use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf}; -use rustc_middle::ty::{Instance, TyCtxt}; +use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, HasTypingEnv, LayoutOf}; +use rustc_middle::ty::{Instance, Ty, TyCtxt}; use rustc_middle::{bug, ty}; use rustc_span::sym; +use rustc_target::callconv::{ArgAbi, FnAbi, PassMode}; use crate::common; use crate::traits::{AsmCodegenMethods, BuilderMethods, GlobalAsmOperandRef, MiscCodegenMethods}; @@ -32,7 +34,8 @@ pub(crate) fn codegen_naked_asm<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let item_data = cx.codegen_unit().items().get(&MonoItem::Fn(instance)).unwrap(); let name = cx.mangled_name(instance); - let (begin, end) = prefix_and_suffix(cx.tcx(), instance, &name, item_data); + let fn_abi = cx.fn_abi_of_instance(instance, ty::List::empty()); + let (begin, end) = prefix_and_suffix(cx.tcx(), instance, &name, item_data, fn_abi); let mut template_vec = Vec::new(); template_vec.push(rustc_ast::ast::InlineAsmTemplatePiece::String(begin.into())); @@ -103,6 +106,7 @@ enum AsmBinaryFormat { Elf, Macho, Coff, + Wasm, } impl AsmBinaryFormat { @@ -111,6 +115,8 @@ impl AsmBinaryFormat { Self::Coff } else if target.is_like_osx { Self::Macho + } else if target.is_like_wasm { + Self::Wasm } else { Self::Elf } @@ -122,6 +128,7 @@ fn prefix_and_suffix<'tcx>( instance: Instance<'tcx>, asm_name: &str, item_data: &MonoItemData, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, ) -> (String, String) { use std::fmt::Write; @@ -169,7 +176,7 @@ fn prefix_and_suffix<'tcx>( } Linkage::LinkOnceAny | Linkage::LinkOnceODR | Linkage::WeakAny | Linkage::WeakODR => { match asm_binary_format { - AsmBinaryFormat::Elf | AsmBinaryFormat::Coff => { + AsmBinaryFormat::Elf | AsmBinaryFormat::Coff | AsmBinaryFormat::Wasm => { writeln!(w, ".weak {asm_name}")?; } AsmBinaryFormat::Macho => { @@ -264,7 +271,132 @@ fn prefix_and_suffix<'tcx>( writeln!(end, "{}", arch_suffix).unwrap(); } } + AsmBinaryFormat::Wasm => { + let section = link_section.unwrap_or(format!(".text.{asm_name}")); + + writeln!(begin, ".section {section},\"\",@").unwrap(); + // wasm functions cannot be aligned, so skip + write_linkage(&mut begin).unwrap(); + if let Visibility::Hidden = item_data.visibility { + writeln!(begin, ".hidden {asm_name}").unwrap(); + } + writeln!(begin, ".type {asm_name}, @function").unwrap(); + if !arch_prefix.is_empty() { + writeln!(begin, "{}", arch_prefix).unwrap(); + } + writeln!(begin, "{asm_name}:").unwrap(); + writeln!(begin, ".functype {asm_name} {}", wasm_functype(tcx, fn_abi)).unwrap(); + + writeln!(end).unwrap(); + // .size is ignored for function symbols, so we can skip it + writeln!(end, "end_function").unwrap(); + } } (begin, end) } + +/// The webassembly type signature for the given function. +/// +/// Used by the `.functype` directive on wasm targets. +fn wasm_functype<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> String { + let mut signature = String::with_capacity(64); + + let ptr_type = match tcx.data_layout.pointer_size.bits() { + 32 => "i32", + 64 => "i64", + other => bug!("wasm pointer size cannot be {other} bits"), + }; + + let hidden_return = + matches!(fn_abi.ret.mode, PassMode::Indirect { .. } | PassMode::Pair { .. }); + + signature.push('('); + + if hidden_return { + signature.push_str(ptr_type); + if !fn_abi.args.is_empty() { + signature.push_str(", "); + } + } + + let mut it = fn_abi.args.iter().peekable(); + while let Some(arg_abi) = it.next() { + wasm_type(&mut signature, arg_abi, ptr_type); + if it.peek().is_some() { + signature.push_str(", "); + } + } + + signature.push_str(") -> ("); + + if !hidden_return { + wasm_type(&mut signature, &fn_abi.ret, ptr_type); + } + + signature.push(')'); + + signature +} + +fn wasm_type<'tcx>(signature: &mut String, arg_abi: &ArgAbi<'_, Ty<'tcx>>, ptr_type: &'static str) { + match arg_abi.mode { + PassMode::Ignore => { /* do nothing */ } + PassMode::Direct(_) => { + let direct_type = match arg_abi.layout.backend_repr { + BackendRepr::Scalar(scalar) => wasm_primitive(scalar.primitive(), ptr_type), + BackendRepr::Vector { .. } => "v128", + other => unreachable!("unexpected BackendRepr: {:?}", other), + }; + + signature.push_str(direct_type); + } + PassMode::Pair(_, _) => match arg_abi.layout.backend_repr { + BackendRepr::ScalarPair(a, b) => { + signature.push_str(wasm_primitive(a.primitive(), ptr_type)); + signature.push_str(", "); + signature.push_str(wasm_primitive(b.primitive(), ptr_type)); + } + other => unreachable!("{other:?}"), + }, + PassMode::Cast { pad_i32, ref cast } => { + // For wasm, Cast is used for single-field primitive wrappers like `struct Wrapper(i64);` + assert!(!pad_i32, "not currently used by wasm calling convention"); + assert!(cast.prefix[0].is_none(), "no prefix"); + assert_eq!(cast.rest.total, arg_abi.layout.size, "single item"); + + let wrapped_wasm_type = match cast.rest.unit.kind { + RegKind::Integer => match cast.rest.unit.size.bytes() { + ..=4 => "i32", + ..=8 => "i64", + _ => ptr_type, + }, + RegKind::Float => match cast.rest.unit.size.bytes() { + ..=4 => "f32", + ..=8 => "f64", + _ => ptr_type, + }, + RegKind::Vector => "v128", + }; + + signature.push_str(wrapped_wasm_type); + } + PassMode::Indirect { .. } => signature.push_str(ptr_type), + } +} + +fn wasm_primitive(primitive: Primitive, ptr_type: &'static str) -> &'static str { + match primitive { + Primitive::Int(integer, _) => match integer { + Integer::I8 | Integer::I16 | Integer::I32 => "i32", + Integer::I64 => "i64", + Integer::I128 => "i64, i64", + }, + Primitive::Float(float) => match float { + Float::F16 | Float::F32 => "f32", + Float::F64 => "f64", + Float::F128 => "i64, i64", + }, + Primitive::Pointer(_) => ptr_type, + } +} diff --git a/tests/assembly/wasm32-naked-fn.rs b/tests/assembly/wasm32-naked-fn.rs new file mode 100644 index 0000000000000..8e98b0847298f --- /dev/null +++ b/tests/assembly/wasm32-naked-fn.rs @@ -0,0 +1,198 @@ +//@ revisions: wasm32-unknown wasm64-unknown wasm32-wasip1 +//@ add-core-stubs +//@ assembly-output: emit-asm +//@ [wasm32-unknown] compile-flags: --target wasm32-unknown-unknown +//@ [wasm64-unknown] compile-flags: --target wasm64-unknown-unknown +//@ [wasm32-wasip1] compile-flags: --target wasm32-wasip1 +//@ [wasm32-unknown] needs-llvm-components: webassembly +//@ [wasm64-unknown] needs-llvm-components: webassembly +//@ [wasm32-wasip1] needs-llvm-components: webassembly + +#![crate_type = "lib"] +#![feature(no_core, naked_functions, asm_experimental_arch, f128, linkage, fn_align)] +#![no_core] + +extern crate minicore; +use minicore::*; + +// CHECK: .section .text.nop,"",@ +// CHECK: .globl nop +// CHECK-LABEL: nop: +// CHECK: .functype nop () -> () +// CHECK-NOT: .size +// CHECK: end_function +#[no_mangle] +#[naked] +unsafe extern "C" fn nop() { + naked_asm!("nop") +} + +// CHECK: .section .text.weak_aligned_nop,"",@ +// CHECK: .weak weak_aligned_nop +// CHECK-LABEL: nop: +// CHECK: .functype weak_aligned_nop () -> () +// CHECK-NOT: .size +// CHECK: end_function +#[no_mangle] +#[naked] +#[linkage = "weak"] +// wasm functions cannot be aligned, so this has no effect +#[repr(align(32))] +unsafe extern "C" fn weak_aligned_nop() { + naked_asm!("nop") +} + +// CHECK-LABEL: fn_i8_i8: +// CHECK-NEXT: .functype fn_i8_i8 (i32) -> (i32) +// +// CHECK-NEXT: local.get 0 +// CHECK-NEXT: local.get 0 +// CHECK-NEXT: i32.mul +// +// CHECK-NEXT: end_function +#[no_mangle] +#[naked] +unsafe extern "C" fn fn_i8_i8(num: i8) -> i8 { + naked_asm!("local.get 0", "local.get 0", "i32.mul") +} + +// CHECK-LABEL: fn_i8_i8_i8: +// CHECK: .functype fn_i8_i8_i8 (i32, i32) -> (i32) +#[no_mangle] +#[naked] +unsafe extern "C" fn fn_i8_i8_i8(a: i8, b: i8) -> i8 { + naked_asm!("local.get 1", "local.get 0", "i32.mul") +} + +// CHECK-LABEL: fn_unit_i8: +// CHECK: .functype fn_unit_i8 () -> (i32) +#[no_mangle] +#[naked] +unsafe extern "C" fn fn_unit_i8() -> i8 { + naked_asm!("i32.const 42") +} + +// CHECK-LABEL: fn_i8_unit: +// CHECK: .functype fn_i8_unit (i32) -> () +#[no_mangle] +#[naked] +unsafe extern "C" fn fn_i8_unit(_: i8) { + naked_asm!("nop") +} + +// CHECK-LABEL: fn_i32_i32: +// CHECK: .functype fn_i32_i32 (i32) -> (i32) +#[no_mangle] +#[naked] +unsafe extern "C" fn fn_i32_i32(num: i32) -> i32 { + naked_asm!("local.get 0", "local.get 0", "i32.mul") +} + +// CHECK-LABEL: fn_i64_i64: +// CHECK: .functype fn_i64_i64 (i64) -> (i64) +#[no_mangle] +#[naked] +unsafe extern "C" fn fn_i64_i64(num: i64) -> i64 { + naked_asm!("local.get 0", "local.get 0", "i64.mul") +} + +// CHECK-LABEL: fn_i128_i128: +// wasm32-unknown,wasm32-wasip1: .functype fn_i128_i128 (i32, i64, i64) -> () +// wasm64-unknown: .functype fn_i128_i128 (i64, i64, i64) -> () +#[allow(improper_ctypes_definitions)] +#[no_mangle] +#[naked] +unsafe extern "C" fn fn_i128_i128(num: i128) -> i128 { + naked_asm!( + "local.get 0", + "local.get 2", + "i64.store 8", + "local.get 0", + "local.get 1", + "i64.store 0", + ) +} + +// CHECK-LABEL: fn_f128_f128: +// wasm32-unknown,wasm32-wasip1: .functype fn_f128_f128 (i32, i64, i64) -> () +// wasm64-unknown: .functype fn_f128_f128 (i64, i64, i64) -> () +#[no_mangle] +#[naked] +unsafe extern "C" fn fn_f128_f128(num: f128) -> f128 { + naked_asm!( + "local.get 0", + "local.get 2", + "i64.store 8", + "local.get 0", + "local.get 1", + "i64.store 0", + ) +} + +#[repr(C)] +struct Compound { + a: u16, + b: i64, +} + +// CHECK-LABEL: fn_compound_compound: +// wasm32-wasip1: .functype fn_compound_compound (i32, i32) -> () +// wasm32-unknown: .functype fn_compound_compound (i32, i32, i64) -> () +// wasm64-unknown: .functype fn_compound_compound (i64, i64) -> () +#[no_mangle] +#[naked] +unsafe extern "C" fn fn_compound_compound(_: Compound) -> Compound { + // this is the wasm32-unknown-unknown assembly + naked_asm!( + "local.get 0", + "local.get 2", + "i64.store 8", + "local.get 0", + "local.get 1", + "i32.store16 0", + ) +} + +#[repr(C)] +struct WrapperI32(i32); + +// CHECK-LABEL: fn_wrapperi32_wrapperi32: +// CHECK: .functype fn_wrapperi32_wrapperi32 (i32) -> (i32) +#[no_mangle] +#[naked] +unsafe extern "C" fn fn_wrapperi32_wrapperi32(_: WrapperI32) -> WrapperI32 { + naked_asm!("local.get 0") +} + +#[repr(C)] +struct WrapperI64(i64); + +// CHECK-LABEL: fn_wrapperi64_wrapperi64: +// CHECK: .functype fn_wrapperi64_wrapperi64 (i64) -> (i64) +#[no_mangle] +#[naked] +unsafe extern "C" fn fn_wrapperi64_wrapperi64(_: WrapperI64) -> WrapperI64 { + naked_asm!("local.get 0") +} + +#[repr(C)] +struct WrapperF32(f32); + +// CHECK-LABEL: fn_wrapperf32_wrapperf32: +// CHECK: .functype fn_wrapperf32_wrapperf32 (f32) -> (f32) +#[no_mangle] +#[naked] +unsafe extern "C" fn fn_wrapperf32_wrapperf32(_: WrapperF32) -> WrapperF32 { + naked_asm!("local.get 0") +} + +#[repr(C)] +struct WrapperF64(f64); + +// CHECK-LABEL: fn_wrapperf64_wrapperf64: +// CHECK: .functype fn_wrapperf64_wrapperf64 (f64) -> (f64) +#[no_mangle] +#[naked] +unsafe extern "C" fn fn_wrapperf64_wrapperf64(_: WrapperF64) -> WrapperF64 { + naked_asm!("local.get 0") +} From bcf478b7a6915a8ce14009934f2893ddcce8052c Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Fri, 17 Jan 2025 22:34:26 +0100 Subject: [PATCH 2/9] work around the `wasm32-unknown-unknown` ABI being broken --- .../rustc_codegen_ssa/src/mir/naked_asm.rs | 47 +++++++++++++++---- tests/assembly/wasm32-naked-fn.rs | 17 +++---- 2 files changed, 48 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index 35e0ea545258f..dc406809874d5 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -1,12 +1,14 @@ use rustc_abi::{BackendRepr, Float, Integer, Primitive, RegKind}; use rustc_attr_parsing::InstructionSetAttr; +use rustc_hir::def_id::DefId; use rustc_middle::mir::mono::{Linkage, MonoItem, MonoItemData, Visibility}; use rustc_middle::mir::{Body, InlineAsmOperand}; use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, HasTypingEnv, LayoutOf}; use rustc_middle::ty::{Instance, Ty, TyCtxt}; -use rustc_middle::{bug, ty}; +use rustc_middle::{bug, span_bug, ty}; use rustc_span::sym; use rustc_target::callconv::{ArgAbi, FnAbi, PassMode}; +use rustc_target::spec::WasmCAbi; use crate::common; use crate::traits::{AsmCodegenMethods, BuilderMethods, GlobalAsmOperandRef, MiscCodegenMethods}; @@ -285,7 +287,12 @@ fn prefix_and_suffix<'tcx>( writeln!(begin, "{}", arch_prefix).unwrap(); } writeln!(begin, "{asm_name}:").unwrap(); - writeln!(begin, ".functype {asm_name} {}", wasm_functype(tcx, fn_abi)).unwrap(); + writeln!( + begin, + ".functype {asm_name} {}", + wasm_functype(tcx, fn_abi, instance.def_id()) + ) + .unwrap(); writeln!(end).unwrap(); // .size is ignored for function symbols, so we can skip it @@ -299,7 +306,7 @@ fn prefix_and_suffix<'tcx>( /// The webassembly type signature for the given function. /// /// Used by the `.functype` directive on wasm targets. -fn wasm_functype<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> String { +fn wasm_functype<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, def_id: DefId) -> String { let mut signature = String::with_capacity(64); let ptr_type = match tcx.data_layout.pointer_size.bits() { @@ -308,8 +315,18 @@ fn wasm_functype<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> Str other => bug!("wasm pointer size cannot be {other} bits"), }; - let hidden_return = - matches!(fn_abi.ret.mode, PassMode::Indirect { .. } | PassMode::Pair { .. }); + // FIXME: remove this once the wasm32-unknown-unknown ABI is fixed + // please also add `wasm32-unknown-unknown` back in `tests/assembly/wasm32-naked-fn.rs` + // basically the commit introducing this comment should be reverted + if let PassMode::Pair { .. } = fn_abi.ret.mode { + let _ = WasmCAbi::Legacy; + span_bug!( + tcx.def_span(def_id), + "cannot return a pair (the wasm32-unknown-unknown ABI is broken, see https://github.com/rust-lang/rust/issues/115666" + ); + } + + let hidden_return = matches!(fn_abi.ret.mode, PassMode::Indirect { .. }); signature.push('('); @@ -322,7 +339,7 @@ fn wasm_functype<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> Str let mut it = fn_abi.args.iter().peekable(); while let Some(arg_abi) = it.next() { - wasm_type(&mut signature, arg_abi, ptr_type); + wasm_type(tcx, &mut signature, arg_abi, ptr_type, def_id); if it.peek().is_some() { signature.push_str(", "); } @@ -331,7 +348,7 @@ fn wasm_functype<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> Str signature.push_str(") -> ("); if !hidden_return { - wasm_type(&mut signature, &fn_abi.ret, ptr_type); + wasm_type(tcx, &mut signature, &fn_abi.ret, ptr_type, def_id); } signature.push(')'); @@ -339,13 +356,27 @@ fn wasm_functype<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> Str signature } -fn wasm_type<'tcx>(signature: &mut String, arg_abi: &ArgAbi<'_, Ty<'tcx>>, ptr_type: &'static str) { +fn wasm_type<'tcx>( + tcx: TyCtxt<'tcx>, + signature: &mut String, + arg_abi: &ArgAbi<'_, Ty<'tcx>>, + ptr_type: &'static str, + def_id: DefId, +) { match arg_abi.mode { PassMode::Ignore => { /* do nothing */ } PassMode::Direct(_) => { let direct_type = match arg_abi.layout.backend_repr { BackendRepr::Scalar(scalar) => wasm_primitive(scalar.primitive(), ptr_type), BackendRepr::Vector { .. } => "v128", + BackendRepr::Memory { .. } => { + // FIXME: remove this branch once the wasm32-unknown-unknown ABI is fixed + let _ = WasmCAbi::Legacy; + span_bug!( + tcx.def_span(def_id), + "cannot use memory args (the wasm32-unknown-unknown ABI is broken, see https://github.com/rust-lang/rust/issues/115666" + ); + } other => unreachable!("unexpected BackendRepr: {:?}", other), }; diff --git a/tests/assembly/wasm32-naked-fn.rs b/tests/assembly/wasm32-naked-fn.rs index 8e98b0847298f..4911a6bd08f68 100644 --- a/tests/assembly/wasm32-naked-fn.rs +++ b/tests/assembly/wasm32-naked-fn.rs @@ -1,10 +1,10 @@ -//@ revisions: wasm32-unknown wasm64-unknown wasm32-wasip1 +// FIXME: add wasm32-unknown when the wasm32-unknown-unknown ABI is fixed +// see https://github.com/rust-lang/rust/issues/115666 +//@ revisions: wasm64-unknown wasm32-wasip1 //@ add-core-stubs //@ assembly-output: emit-asm -//@ [wasm32-unknown] compile-flags: --target wasm32-unknown-unknown //@ [wasm64-unknown] compile-flags: --target wasm64-unknown-unknown //@ [wasm32-wasip1] compile-flags: --target wasm32-wasip1 -//@ [wasm32-unknown] needs-llvm-components: webassembly //@ [wasm64-unknown] needs-llvm-components: webassembly //@ [wasm32-wasip1] needs-llvm-components: webassembly @@ -97,7 +97,7 @@ unsafe extern "C" fn fn_i64_i64(num: i64) -> i64 { } // CHECK-LABEL: fn_i128_i128: -// wasm32-unknown,wasm32-wasip1: .functype fn_i128_i128 (i32, i64, i64) -> () +// wasm32-wasip1: .functype fn_i128_i128 (i32, i64, i64) -> () // wasm64-unknown: .functype fn_i128_i128 (i64, i64, i64) -> () #[allow(improper_ctypes_definitions)] #[no_mangle] @@ -114,7 +114,7 @@ unsafe extern "C" fn fn_i128_i128(num: i128) -> i128 { } // CHECK-LABEL: fn_f128_f128: -// wasm32-unknown,wasm32-wasip1: .functype fn_f128_f128 (i32, i64, i64) -> () +// wasm32-wasip1: .functype fn_f128_f128 (i32, i64, i64) -> () // wasm64-unknown: .functype fn_f128_f128 (i64, i64, i64) -> () #[no_mangle] #[naked] @@ -137,18 +137,19 @@ struct Compound { // CHECK-LABEL: fn_compound_compound: // wasm32-wasip1: .functype fn_compound_compound (i32, i32) -> () -// wasm32-unknown: .functype fn_compound_compound (i32, i32, i64) -> () // wasm64-unknown: .functype fn_compound_compound (i64, i64) -> () #[no_mangle] #[naked] unsafe extern "C" fn fn_compound_compound(_: Compound) -> Compound { - // this is the wasm32-unknown-unknown assembly + // this is the wasm32-wasip1 assembly naked_asm!( "local.get 0", - "local.get 2", + "local.get 1", + "i64.load 8", "i64.store 8", "local.get 0", "local.get 1", + "i32.load16_u 0", "i32.store16 0", ) } From bf5e634b68ab62d5e1d4dad0c65ce3899b04e9da Mon Sep 17 00:00:00 2001 From: Urgau Date: Thu, 9 Jan 2025 22:44:39 +0100 Subject: [PATCH 3/9] proc_macro: add `#![warn(unreachable_pub)]` --- library/proc_macro/src/bridge/closure.rs | 4 ++-- library/proc_macro/src/bridge/fxhash.rs | 4 ++-- library/proc_macro/src/bridge/rpc.rs | 4 ++-- library/proc_macro/src/bridge/selfless_reify.rs | 2 +- library/proc_macro/src/lib.rs | 1 + 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/library/proc_macro/src/bridge/closure.rs b/library/proc_macro/src/bridge/closure.rs index d371ae3cea098..524fdf53d6b7e 100644 --- a/library/proc_macro/src/bridge/closure.rs +++ b/library/proc_macro/src/bridge/closure.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; #[repr(C)] -pub struct Closure<'a, A, R> { +pub(super) struct Closure<'a, A, R> { call: unsafe extern "C" fn(*mut Env, A) -> R, env: *mut Env, // Prevent Send and Sync impls. `!Send`/`!Sync` is the usual way of doing @@ -26,7 +26,7 @@ impl<'a, A, R, F: FnMut(A) -> R> From<&'a mut F> for Closure<'a, A, R> { } impl<'a, A, R> Closure<'a, A, R> { - pub fn call(&mut self, arg: A) -> R { + pub(super) fn call(&mut self, arg: A) -> R { unsafe { (self.call)(self.env, arg) } } } diff --git a/library/proc_macro/src/bridge/fxhash.rs b/library/proc_macro/src/bridge/fxhash.rs index 3345e099a3724..5f6b3d1b929e4 100644 --- a/library/proc_macro/src/bridge/fxhash.rs +++ b/library/proc_macro/src/bridge/fxhash.rs @@ -9,7 +9,7 @@ use std::hash::{BuildHasherDefault, Hasher}; use std::ops::BitXor; /// Type alias for a hashmap using the `fx` hash algorithm. -pub type FxHashMap = HashMap>; +pub(super) type FxHashMap = HashMap>; /// A speedy hash algorithm for use within rustc. The hashmap in alloc by /// default uses SipHash which isn't quite as speedy as we want. In the compiler @@ -23,7 +23,7 @@ pub type FxHashMap = HashMap>; /// similar or slightly worse than FNV, but the speed of the hash function /// itself is much higher because it works on up to 8 bytes at a time. #[derive(Default)] -pub struct FxHasher { +pub(super) struct FxHasher { hash: usize, } diff --git a/library/proc_macro/src/bridge/rpc.rs b/library/proc_macro/src/bridge/rpc.rs index 202a8e04543b2..85fd7d138585c 100644 --- a/library/proc_macro/src/bridge/rpc.rs +++ b/library/proc_macro/src/bridge/rpc.rs @@ -67,7 +67,7 @@ macro_rules! rpc_encode_decode { mod tag { #[repr(u8)] enum Tag { $($variant),* } - $(pub const $variant: u8 = Tag::$variant as u8;)* + $(pub(crate) const $variant: u8 = Tag::$variant as u8;)* } match self { @@ -89,7 +89,7 @@ macro_rules! rpc_encode_decode { mod tag { #[repr(u8)] enum Tag { $($variant),* } - $(pub const $variant: u8 = Tag::$variant as u8;)* + $(pub(crate) const $variant: u8 = Tag::$variant as u8;)* } match u8::decode(r, s) { diff --git a/library/proc_macro/src/bridge/selfless_reify.rs b/library/proc_macro/src/bridge/selfless_reify.rs index 907ad256e4b43..312a79152e23b 100644 --- a/library/proc_macro/src/bridge/selfless_reify.rs +++ b/library/proc_macro/src/bridge/selfless_reify.rs @@ -44,7 +44,7 @@ macro_rules! define_reify_functions { fn $name:ident $(<$($param:ident),*>)? for $(extern $abi:tt)? fn($($arg:ident: $arg_ty:ty),*) -> $ret_ty:ty; )+) => { - $(pub const fn $name< + $(pub(super) const fn $name< $($($param,)*)? F: Fn($($arg_ty),*) -> $ret_ty + Copy >(f: F) -> $(extern $abi)? fn($($arg_ty),*) -> $ret_ty { diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index b19c9cee75a0b..6611ce30a1b01 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -32,6 +32,7 @@ #![allow(internal_features)] #![deny(ffi_unwind_calls)] #![warn(rustdoc::unescaped_backticks)] +#![warn(unreachable_pub)] #[unstable(feature = "proc_macro_internals", issue = "27812")] #[doc(hidden)] From 939b7047a0495e461efa57fed828fedd06b1cdb4 Mon Sep 17 00:00:00 2001 From: Urgau Date: Thu, 9 Jan 2025 22:57:57 +0100 Subject: [PATCH 4/9] test: add `#![warn(unreachable_pub)]` --- library/test/src/cli.rs | 2 +- library/test/src/console.rs | 18 +++++++-------- library/test/src/formatters/json.rs | 2 +- library/test/src/formatters/junit.rs | 4 ++-- library/test/src/formatters/pretty.rs | 26 ++++++++++----------- library/test/src/formatters/terse.rs | 20 ++++++++--------- library/test/src/helpers/concurrency.rs | 2 +- library/test/src/helpers/mod.rs | 6 ++--- library/test/src/helpers/shuffle.rs | 4 ++-- library/test/src/lib.rs | 1 + library/test/src/options.rs | 2 +- library/test/src/stats/tests.rs | 6 ++--- library/test/src/term.rs | 2 +- library/test/src/test_result.rs | 6 ++--- library/test/src/tests.rs | 28 +++++++++++------------ library/test/src/time.rs | 30 ++++++++++++------------- 16 files changed, 80 insertions(+), 79 deletions(-) diff --git a/library/test/src/cli.rs b/library/test/src/cli.rs index 4ccd825bf8dd3..52c8091762346 100644 --- a/library/test/src/cli.rs +++ b/library/test/src/cli.rs @@ -44,7 +44,7 @@ impl TestOpts { } /// Result of parsing the options. -pub type OptRes = Result; +pub(crate) type OptRes = Result; /// Result of parsing the option part. type OptPartRes = Result; diff --git a/library/test/src/console.rs b/library/test/src/console.rs index 4d4cdcf4d7b6c..024ef48fc5104 100644 --- a/library/test/src/console.rs +++ b/library/test/src/console.rs @@ -20,7 +20,7 @@ use super::types::{NamePadding, TestDesc, TestDescAndFn}; use super::{filter_tests, run_tests, term}; /// Generic wrapper over stdout. -pub enum OutputLocation { +pub(crate) enum OutputLocation { Pretty(Box), Raw(T), } @@ -41,7 +41,7 @@ impl Write for OutputLocation { } } -pub struct ConsoleTestDiscoveryState { +pub(crate) struct ConsoleTestDiscoveryState { pub log_out: Option, pub tests: usize, pub benchmarks: usize, @@ -49,7 +49,7 @@ pub struct ConsoleTestDiscoveryState { } impl ConsoleTestDiscoveryState { - pub fn new(opts: &TestOpts) -> io::Result { + pub(crate) fn new(opts: &TestOpts) -> io::Result { let log_out = match opts.logfile { Some(ref path) => Some(File::create(path)?), None => None, @@ -58,7 +58,7 @@ impl ConsoleTestDiscoveryState { Ok(ConsoleTestDiscoveryState { log_out, tests: 0, benchmarks: 0, ignored: 0 }) } - pub fn write_log(&mut self, msg: F) -> io::Result<()> + pub(crate) fn write_log(&mut self, msg: F) -> io::Result<()> where S: AsRef, F: FnOnce() -> S, @@ -74,7 +74,7 @@ impl ConsoleTestDiscoveryState { } } -pub struct ConsoleTestState { +pub(crate) struct ConsoleTestState { pub log_out: Option, pub total: usize, pub passed: usize, @@ -92,7 +92,7 @@ pub struct ConsoleTestState { } impl ConsoleTestState { - pub fn new(opts: &TestOpts) -> io::Result { + pub(crate) fn new(opts: &TestOpts) -> io::Result { let log_out = match opts.logfile { Some(ref path) => Some(File::create(path)?), None => None, @@ -116,7 +116,7 @@ impl ConsoleTestState { }) } - pub fn write_log(&mut self, msg: F) -> io::Result<()> + pub(crate) fn write_log(&mut self, msg: F) -> io::Result<()> where S: AsRef, F: FnOnce() -> S, @@ -131,7 +131,7 @@ impl ConsoleTestState { } } - pub fn write_log_result( + pub(crate) fn write_log_result( &mut self, test: &TestDesc, result: &TestResult, @@ -170,7 +170,7 @@ impl ConsoleTestState { } // List the tests to console, and optionally to logfile. Filters are honored. -pub fn list_tests_console(opts: &TestOpts, tests: Vec) -> io::Result<()> { +pub(crate) fn list_tests_console(opts: &TestOpts, tests: Vec) -> io::Result<()> { let output = match term::stdout() { None => OutputLocation::Raw(io::stdout().lock()), Some(t) => OutputLocation::Pretty(t), diff --git a/library/test/src/formatters/json.rs b/library/test/src/formatters/json.rs index aa1c50641cb54..92c1c0716f1f2 100644 --- a/library/test/src/formatters/json.rs +++ b/library/test/src/formatters/json.rs @@ -13,7 +13,7 @@ pub(crate) struct JsonFormatter { } impl JsonFormatter { - pub fn new(out: OutputLocation) -> Self { + pub(crate) fn new(out: OutputLocation) -> Self { Self { out } } diff --git a/library/test/src/formatters/junit.rs b/library/test/src/formatters/junit.rs index 96b432008404b..57b1b0feceefc 100644 --- a/library/test/src/formatters/junit.rs +++ b/library/test/src/formatters/junit.rs @@ -8,13 +8,13 @@ use crate::test_result::TestResult; use crate::time; use crate::types::{TestDesc, TestType}; -pub struct JunitFormatter { +pub(crate) struct JunitFormatter { out: OutputLocation, results: Vec<(TestDesc, TestResult, Duration, Vec)>, } impl JunitFormatter { - pub fn new(out: OutputLocation) -> Self { + pub(crate) fn new(out: OutputLocation) -> Self { Self { out, results: Vec::new() } } diff --git a/library/test/src/formatters/pretty.rs b/library/test/src/formatters/pretty.rs index 7089eae4330a0..bf3fc40db4117 100644 --- a/library/test/src/formatters/pretty.rs +++ b/library/test/src/formatters/pretty.rs @@ -20,7 +20,7 @@ pub(crate) struct PrettyFormatter { } impl PrettyFormatter { - pub fn new( + pub(crate) fn new( out: OutputLocation, use_color: bool, max_name_len: usize, @@ -31,19 +31,19 @@ impl PrettyFormatter { } #[cfg(test)] - pub fn output_location(&self) -> &OutputLocation { + pub(crate) fn output_location(&self) -> &OutputLocation { &self.out } - pub fn write_ok(&mut self) -> io::Result<()> { + pub(crate) fn write_ok(&mut self) -> io::Result<()> { self.write_short_result("ok", term::color::GREEN) } - pub fn write_failed(&mut self) -> io::Result<()> { + pub(crate) fn write_failed(&mut self) -> io::Result<()> { self.write_short_result("FAILED", term::color::RED) } - pub fn write_ignored(&mut self, message: Option<&'static str>) -> io::Result<()> { + pub(crate) fn write_ignored(&mut self, message: Option<&'static str>) -> io::Result<()> { if let Some(message) = message { self.write_short_result(&format!("ignored, {message}"), term::color::YELLOW) } else { @@ -51,15 +51,15 @@ impl PrettyFormatter { } } - pub fn write_time_failed(&mut self) -> io::Result<()> { + pub(crate) fn write_time_failed(&mut self) -> io::Result<()> { self.write_short_result("FAILED (time limit exceeded)", term::color::RED) } - pub fn write_bench(&mut self) -> io::Result<()> { + pub(crate) fn write_bench(&mut self) -> io::Result<()> { self.write_pretty("bench", term::color::CYAN) } - pub fn write_short_result( + pub(crate) fn write_short_result( &mut self, result: &str, color: term::color::Color, @@ -67,7 +67,7 @@ impl PrettyFormatter { self.write_pretty(result, color) } - pub fn write_pretty(&mut self, word: &str, color: term::color::Color) -> io::Result<()> { + pub(crate) fn write_pretty(&mut self, word: &str, color: term::color::Color) -> io::Result<()> { match self.out { OutputLocation::Pretty(ref mut term) => { if self.use_color { @@ -86,7 +86,7 @@ impl PrettyFormatter { } } - pub fn write_plain>(&mut self, s: S) -> io::Result<()> { + pub(crate) fn write_plain>(&mut self, s: S) -> io::Result<()> { let s = s.as_ref(); self.out.write_all(s.as_bytes())?; self.out.flush() @@ -154,15 +154,15 @@ impl PrettyFormatter { Ok(()) } - pub fn write_successes(&mut self, state: &ConsoleTestState) -> io::Result<()> { + pub(crate) fn write_successes(&mut self, state: &ConsoleTestState) -> io::Result<()> { self.write_results(&state.not_failures, "successes") } - pub fn write_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> { + pub(crate) fn write_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> { self.write_results(&state.failures, "failures") } - pub fn write_time_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> { + pub(crate) fn write_time_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> { self.write_results(&state.time_failures, "failures (time limit exceeded)") } diff --git a/library/test/src/formatters/terse.rs b/library/test/src/formatters/terse.rs index 534aa2f33110c..b28120ab56e69 100644 --- a/library/test/src/formatters/terse.rs +++ b/library/test/src/formatters/terse.rs @@ -25,7 +25,7 @@ pub(crate) struct TerseFormatter { } impl TerseFormatter { - pub fn new( + pub(crate) fn new( out: OutputLocation, use_color: bool, max_name_len: usize, @@ -42,11 +42,11 @@ impl TerseFormatter { } } - pub fn write_ok(&mut self) -> io::Result<()> { + pub(crate) fn write_ok(&mut self) -> io::Result<()> { self.write_short_result(".", term::color::GREEN) } - pub fn write_failed(&mut self, name: &str) -> io::Result<()> { + pub(crate) fn write_failed(&mut self, name: &str) -> io::Result<()> { // Put failed tests on their own line and include the test name, so that it's faster // to see which test failed without having to wait for them all to run. @@ -62,15 +62,15 @@ impl TerseFormatter { self.write_plain("\n") } - pub fn write_ignored(&mut self) -> io::Result<()> { + pub(crate) fn write_ignored(&mut self) -> io::Result<()> { self.write_short_result("i", term::color::YELLOW) } - pub fn write_bench(&mut self) -> io::Result<()> { + pub(crate) fn write_bench(&mut self) -> io::Result<()> { self.write_pretty("bench", term::color::CYAN) } - pub fn write_short_result( + pub(crate) fn write_short_result( &mut self, result: &str, color: term::color::Color, @@ -95,7 +95,7 @@ impl TerseFormatter { Ok(()) } - pub fn write_pretty(&mut self, word: &str, color: term::color::Color) -> io::Result<()> { + pub(crate) fn write_pretty(&mut self, word: &str, color: term::color::Color) -> io::Result<()> { match self.out { OutputLocation::Pretty(ref mut term) => { if self.use_color { @@ -114,13 +114,13 @@ impl TerseFormatter { } } - pub fn write_plain>(&mut self, s: S) -> io::Result<()> { + pub(crate) fn write_plain>(&mut self, s: S) -> io::Result<()> { let s = s.as_ref(); self.out.write_all(s.as_bytes())?; self.out.flush() } - pub fn write_outputs(&mut self, state: &ConsoleTestState) -> io::Result<()> { + pub(crate) fn write_outputs(&mut self, state: &ConsoleTestState) -> io::Result<()> { self.write_plain("\nsuccesses:\n")?; let mut successes = Vec::new(); let mut stdouts = String::new(); @@ -146,7 +146,7 @@ impl TerseFormatter { Ok(()) } - pub fn write_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> { + pub(crate) fn write_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> { self.write_plain("\nfailures:\n")?; let mut failures = Vec::new(); let mut fail_out = String::new(); diff --git a/library/test/src/helpers/concurrency.rs b/library/test/src/helpers/concurrency.rs index b1545cbec438a..6648b669125f7 100644 --- a/library/test/src/helpers/concurrency.rs +++ b/library/test/src/helpers/concurrency.rs @@ -4,7 +4,7 @@ use std::num::NonZero; use std::{env, thread}; -pub fn get_concurrency() -> usize { +pub(crate) fn get_concurrency() -> usize { if let Ok(value) = env::var("RUST_TEST_THREADS") { match value.parse::>().ok() { Some(n) => n.get(), diff --git a/library/test/src/helpers/mod.rs b/library/test/src/helpers/mod.rs index 3c79b90b16754..2fb29b4c7bee5 100644 --- a/library/test/src/helpers/mod.rs +++ b/library/test/src/helpers/mod.rs @@ -1,6 +1,6 @@ //! Module with common helpers not directly related to tests //! but used in `libtest`. -pub mod concurrency; -pub mod metrics; -pub mod shuffle; +pub(crate) mod concurrency; +pub(crate) mod metrics; +pub(crate) mod shuffle; diff --git a/library/test/src/helpers/shuffle.rs b/library/test/src/helpers/shuffle.rs index 14389eb0e37af..53d1d0e42d4e8 100644 --- a/library/test/src/helpers/shuffle.rs +++ b/library/test/src/helpers/shuffle.rs @@ -4,7 +4,7 @@ use std::time::{SystemTime, UNIX_EPOCH}; use crate::cli::TestOpts; use crate::types::{TestDescAndFn, TestId, TestName}; -pub fn get_shuffle_seed(opts: &TestOpts) -> Option { +pub(crate) fn get_shuffle_seed(opts: &TestOpts) -> Option { opts.shuffle_seed.or_else(|| { if opts.shuffle { Some( @@ -19,7 +19,7 @@ pub fn get_shuffle_seed(opts: &TestOpts) -> Option { }) } -pub fn shuffle_tests(shuffle_seed: u64, tests: &mut [(TestId, TestDescAndFn)]) { +pub(crate) fn shuffle_tests(shuffle_seed: u64, tests: &mut [(TestId, TestDescAndFn)]) { let test_names: Vec<&TestName> = tests.iter().map(|test| &test.1.desc.name).collect(); let test_names_hash = calculate_hash(&test_names); let mut rng = Rng::new(shuffle_seed, test_names_hash); diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index 47407df909bdf..54f7e4ae79f18 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -27,6 +27,7 @@ #![feature(thread_spawn_hook)] #![allow(internal_features)] #![warn(rustdoc::unescaped_backticks)] +#![warn(unreachable_pub)] pub use cli::TestOpts; diff --git a/library/test/src/options.rs b/library/test/src/options.rs index 3eaad59474a12..7a5c55f4e2411 100644 --- a/library/test/src/options.rs +++ b/library/test/src/options.rs @@ -2,7 +2,7 @@ /// Number of times to run a benchmarked function #[derive(Clone, PartialEq, Eq)] -pub enum BenchMode { +pub(crate) enum BenchMode { Auto, Single, } diff --git a/library/test/src/stats/tests.rs b/library/test/src/stats/tests.rs index 4b209dcf214da..7804ddc929132 100644 --- a/library/test/src/stats/tests.rs +++ b/library/test/src/stats/tests.rs @@ -573,13 +573,13 @@ fn test_sum_f64_between_ints_that_sum_to_0() { } #[bench] -pub fn sum_three_items(b: &mut Bencher) { +fn sum_three_items(b: &mut Bencher) { b.iter(|| { [1e20f64, 1.5f64, -1e20f64].sum(); }) } #[bench] -pub fn sum_many_f64(b: &mut Bencher) { +fn sum_many_f64(b: &mut Bencher) { let nums = [-1e30f64, 1e60, 1e30, 1.0, -1e60]; let v = (0..500).map(|i| nums[i % 5]).collect::>(); @@ -589,4 +589,4 @@ pub fn sum_many_f64(b: &mut Bencher) { } #[bench] -pub fn no_iter(_: &mut Bencher) {} +fn no_iter(_: &mut Bencher) {} diff --git a/library/test/src/term.rs b/library/test/src/term.rs index e736e85d46966..d9880a776406d 100644 --- a/library/test/src/term.rs +++ b/library/test/src/term.rs @@ -62,7 +62,7 @@ pub(crate) mod color { /// A terminal with similar capabilities to an ANSI Terminal /// (foreground/background colors etc). -pub trait Terminal: Write { +pub(crate) trait Terminal: Write { /// Sets the foreground color to the given color. /// /// If the color is a bright color, but the terminal only supports 8 colors, diff --git a/library/test/src/test_result.rs b/library/test/src/test_result.rs index 79fe07bc1ac5c..73dcc2e2a0cca 100644 --- a/library/test/src/test_result.rs +++ b/library/test/src/test_result.rs @@ -12,7 +12,7 @@ use super::types::TestDesc; // Return code for secondary process. // Start somewhere other than 0 so we know the return code means what we think // it means. -pub const TR_OK: i32 = 50; +pub(crate) const TR_OK: i32 = 50; // On Windows we use __fastfail to abort, which is documented to use this // exception code. @@ -39,7 +39,7 @@ pub enum TestResult { /// Creates a `TestResult` depending on the raw result of test execution /// and associated data. -pub fn calc_result<'a>( +pub(crate) fn calc_result<'a>( desc: &TestDesc, task_result: Result<(), &'a (dyn Any + 'static + Send)>, time_opts: Option<&time::TestTimeOptions>, @@ -93,7 +93,7 @@ pub fn calc_result<'a>( } /// Creates a `TestResult` depending on the exit code of test subprocess. -pub fn get_result_from_exit_code( +pub(crate) fn get_result_from_exit_code( desc: &TestDesc, status: ExitStatus, time_opts: Option<&time::TestTimeOptions>, diff --git a/library/test/src/tests.rs b/library/test/src/tests.rs index e85e61090a91b..abeb364216979 100644 --- a/library/test/src/tests.rs +++ b/library/test/src/tests.rs @@ -78,7 +78,7 @@ fn one_ignored_one_unignored_test() -> Vec { } #[test] -pub fn do_not_run_ignored_tests() { +fn do_not_run_ignored_tests() { fn f() -> Result<(), String> { panic!(); } @@ -106,7 +106,7 @@ pub fn do_not_run_ignored_tests() { } #[test] -pub fn ignored_tests_result_in_ignored() { +fn ignored_tests_result_in_ignored() { fn f() -> Result<(), String> { Ok(()) } @@ -479,7 +479,7 @@ fn parse_include_ignored_flag() { } #[test] -pub fn filter_for_ignored_option() { +fn filter_for_ignored_option() { // When we run ignored tests the test filter should filter out all the // unignored tests and flip the ignore flag on the rest to false @@ -496,7 +496,7 @@ pub fn filter_for_ignored_option() { } #[test] -pub fn run_include_ignored_option() { +fn run_include_ignored_option() { // When we "--include-ignored" tests, the ignore flag should be set to false on // all tests and no test filtered out @@ -513,7 +513,7 @@ pub fn run_include_ignored_option() { } #[test] -pub fn exclude_should_panic_option() { +fn exclude_should_panic_option() { let mut opts = TestOpts::new(); opts.run_tests = true; opts.exclude_should_panic = true; @@ -544,7 +544,7 @@ pub fn exclude_should_panic_option() { } #[test] -pub fn exact_filter_match() { +fn exact_filter_match() { fn tests() -> Vec { ["base", "base::test", "base::test1", "base::test2"] .into_iter() @@ -667,7 +667,7 @@ fn sample_tests() -> Vec { } #[test] -pub fn shuffle_tests() { +fn shuffle_tests() { let mut opts = TestOpts::new(); opts.shuffle = true; @@ -686,7 +686,7 @@ pub fn shuffle_tests() { } #[test] -pub fn shuffle_tests_with_seed() { +fn shuffle_tests_with_seed() { let mut opts = TestOpts::new(); opts.shuffle = true; @@ -704,7 +704,7 @@ pub fn shuffle_tests_with_seed() { } #[test] -pub fn order_depends_on_more_than_seed() { +fn order_depends_on_more_than_seed() { let mut opts = TestOpts::new(); opts.shuffle = true; @@ -732,7 +732,7 @@ pub fn order_depends_on_more_than_seed() { } #[test] -pub fn test_metricmap_compare() { +fn test_metricmap_compare() { let mut m1 = MetricMap::new(); let mut m2 = MetricMap::new(); m1.insert_metric("in-both-noise", 1000.0, 200.0); @@ -755,7 +755,7 @@ pub fn test_metricmap_compare() { } #[test] -pub fn test_bench_once_no_iter() { +fn test_bench_once_no_iter() { fn f(_: &mut Bencher) -> Result<(), String> { Ok(()) } @@ -763,7 +763,7 @@ pub fn test_bench_once_no_iter() { } #[test] -pub fn test_bench_once_iter() { +fn test_bench_once_iter() { fn f(b: &mut Bencher) -> Result<(), String> { b.iter(|| {}); Ok(()) @@ -772,7 +772,7 @@ pub fn test_bench_once_iter() { } #[test] -pub fn test_bench_no_iter() { +fn test_bench_no_iter() { fn f(_: &mut Bencher) -> Result<(), String> { Ok(()) } @@ -799,7 +799,7 @@ pub fn test_bench_no_iter() { } #[test] -pub fn test_bench_iter() { +fn test_bench_iter() { fn f(b: &mut Bencher) -> Result<(), String> { b.iter(|| {}); Ok(()) diff --git a/library/test/src/time.rs b/library/test/src/time.rs index 02ae050db55bd..f63b156b3dc5a 100644 --- a/library/test/src/time.rs +++ b/library/test/src/time.rs @@ -11,7 +11,7 @@ use std::{env, fmt}; use super::types::{TestDesc, TestType}; -pub const TEST_WARN_TIMEOUT_S: u64 = 60; +pub(crate) const TEST_WARN_TIMEOUT_S: u64 = 60; /// This small module contains constants used by `report-time` option. /// Those constants values will be used if corresponding environment variables are not set. @@ -22,42 +22,42 @@ pub const TEST_WARN_TIMEOUT_S: u64 = 60; /// /// Example of the expected format is `RUST_TEST_TIME_xxx=100,200`, where 100 means /// warn time, and 200 means critical time. -pub mod time_constants { +pub(crate) mod time_constants { use std::time::Duration; use super::TEST_WARN_TIMEOUT_S; /// Environment variable for overriding default threshold for unit-tests. - pub const UNIT_ENV_NAME: &str = "RUST_TEST_TIME_UNIT"; + pub(crate) const UNIT_ENV_NAME: &str = "RUST_TEST_TIME_UNIT"; // Unit tests are supposed to be really quick. - pub const UNIT_WARN: Duration = Duration::from_millis(50); - pub const UNIT_CRITICAL: Duration = Duration::from_millis(100); + pub(crate) const UNIT_WARN: Duration = Duration::from_millis(50); + pub(crate) const UNIT_CRITICAL: Duration = Duration::from_millis(100); /// Environment variable for overriding default threshold for unit-tests. - pub const INTEGRATION_ENV_NAME: &str = "RUST_TEST_TIME_INTEGRATION"; + pub(crate) const INTEGRATION_ENV_NAME: &str = "RUST_TEST_TIME_INTEGRATION"; // Integration tests may have a lot of work, so they can take longer to execute. - pub const INTEGRATION_WARN: Duration = Duration::from_millis(500); - pub const INTEGRATION_CRITICAL: Duration = Duration::from_millis(1000); + pub(crate) const INTEGRATION_WARN: Duration = Duration::from_millis(500); + pub(crate) const INTEGRATION_CRITICAL: Duration = Duration::from_millis(1000); /// Environment variable for overriding default threshold for unit-tests. - pub const DOCTEST_ENV_NAME: &str = "RUST_TEST_TIME_DOCTEST"; + pub(crate) const DOCTEST_ENV_NAME: &str = "RUST_TEST_TIME_DOCTEST"; // Doctests are similar to integration tests, because they can include a lot of // initialization code. - pub const DOCTEST_WARN: Duration = INTEGRATION_WARN; - pub const DOCTEST_CRITICAL: Duration = INTEGRATION_CRITICAL; + pub(crate) const DOCTEST_WARN: Duration = INTEGRATION_WARN; + pub(crate) const DOCTEST_CRITICAL: Duration = INTEGRATION_CRITICAL; // Do not suppose anything about unknown tests, base limits on the // `TEST_WARN_TIMEOUT_S` constant. - pub const UNKNOWN_WARN: Duration = Duration::from_secs(TEST_WARN_TIMEOUT_S); - pub const UNKNOWN_CRITICAL: Duration = Duration::from_secs(TEST_WARN_TIMEOUT_S * 2); + pub(crate) const UNKNOWN_WARN: Duration = Duration::from_secs(TEST_WARN_TIMEOUT_S); + pub(crate) const UNKNOWN_CRITICAL: Duration = Duration::from_secs(TEST_WARN_TIMEOUT_S * 2); } /// Returns an `Instance` object denoting when the test should be considered /// timed out. -pub fn get_default_test_timeout() -> Instant { +pub(crate) fn get_default_test_timeout() -> Instant { Instant::now() + Duration::from_secs(TEST_WARN_TIMEOUT_S) } @@ -73,7 +73,7 @@ impl fmt::Display for TestExecTime { /// The measured execution time of the whole test suite. #[derive(Debug, Clone, Default, PartialEq)] -pub struct TestSuiteExecTime(pub Duration); +pub(crate) struct TestSuiteExecTime(pub Duration); impl fmt::Display for TestSuiteExecTime { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { From 00381ead1af39db9e5ce24a8da4dda59b714dbf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 2 Jan 2025 18:02:57 +0100 Subject: [PATCH 5/9] Make it possible to build GCC on CI This is the first step to enable download of precompiled GCC --- src/bootstrap/src/core/build_steps/gcc.rs | 54 +++++++++++++++++-- .../host-x86_64/dist-x86_64-linux/Dockerfile | 1 + src/ci/docker/scripts/build-zstd.sh | 6 +++ 3 files changed, 57 insertions(+), 4 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/gcc.rs b/src/bootstrap/src/core/build_steps/gcc.rs index 563b715fa6400..98b8635132b0d 100644 --- a/src/bootstrap/src/core/build_steps/gcc.rs +++ b/src/bootstrap/src/core/build_steps/gcc.rs @@ -12,6 +12,8 @@ use std::fs; use std::path::PathBuf; use std::sync::OnceLock; +use build_helper::ci::CiEnv; + use crate::Kind; use crate::core::builder::{Builder, RunConfig, ShouldRun, Step}; use crate::core::config::TargetSelection; @@ -112,16 +114,60 @@ impl Step for Gcc { return true; } - command(root.join("contrib/download_prerequisites")).current_dir(&root).run(builder); - command(root.join("configure")) + // GCC creates files (e.g. symlinks to the downloaded dependencies) + // in the source directory, which does not work with our CI setup, where we mount + // source directories as read-only on Linux. + // Therefore, as a part of the build in CI, we first copy the whole source directory + // to the build directory, and perform the build from there. + let src_dir = if CiEnv::is_ci() { + let src_dir = builder.gcc_out(target).join("src"); + if src_dir.exists() { + builder.remove_dir(&src_dir); + } + builder.create_dir(&src_dir); + builder.cp_link_r(&root, &src_dir); + src_dir + } else { + root + }; + + command(src_dir.join("contrib/download_prerequisites")).current_dir(&src_dir).run(builder); + let mut configure_cmd = command(src_dir.join("configure")); + configure_cmd .current_dir(&out_dir) + // On CI, we compile GCC with Clang. + // The -Wno-everything flag is needed to make GCC compile with Clang 19. + // `-g -O2` are the default flags that are otherwise used by Make. + // FIXME(kobzol): change the flags once we have [gcc] configuration in config.toml. + .env("CXXFLAGS", "-Wno-everything -g -O2") + .env("CFLAGS", "-Wno-everything -g -O2") .arg("--enable-host-shared") .arg("--enable-languages=jit") .arg("--enable-checking=release") .arg("--disable-bootstrap") .arg("--disable-multilib") - .arg(format!("--prefix={}", install_dir.display())) - .run(builder); + .arg(format!("--prefix={}", install_dir.display())); + let cc = builder.build.cc(target).display().to_string(); + let cc = builder + .build + .config + .ccache + .as_ref() + .map_or_else(|| cc.clone(), |ccache| format!("{ccache} {cc}")); + configure_cmd.env("CC", cc); + + if let Ok(ref cxx) = builder.build.cxx(target) { + let cxx = cxx.display().to_string(); + let cxx = builder + .build + .config + .ccache + .as_ref() + .map_or_else(|| cxx.clone(), |ccache| format!("{ccache} {cxx}")); + configure_cmd.env("CXX", cxx); + } + configure_cmd.run(builder); + command("make").current_dir(&out_dir).arg(format!("-j{}", builder.jobs())).run(builder); command("make").current_dir(&out_dir).arg("install").run(builder); diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile index dde6fe7f6d0f3..3a39623058255 100644 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile @@ -34,6 +34,7 @@ RUN yum upgrade -y && \ python3 \ unzip \ wget \ + flex \ xz \ zlib-devel.i686 \ zlib-devel.x86_64 \ diff --git a/src/ci/docker/scripts/build-zstd.sh b/src/ci/docker/scripts/build-zstd.sh index a3d37ccc31120..cffa7151e3864 100755 --- a/src/ci/docker/scripts/build-zstd.sh +++ b/src/ci/docker/scripts/build-zstd.sh @@ -25,5 +25,11 @@ cd zstd-$ZSTD CFLAGS=-fPIC hide_output make -j$(nproc) VERBOSE=1 hide_output make install +# It doesn't seem to be possible to move destination directory +# of the `make install` above. We thus copy the built artifacts +# manually to our custom rustroot, so that it can be found through +# LD_LIBRARY_PATH. +cp /usr/local/lib/libzstd* /rustroot/lib64 + cd .. rm -rf zstd-$ZSTD From 9b70b8bbbf1457ce93813071727b5e719e690e4d Mon Sep 17 00:00:00 2001 From: MarcoIeni <11428655+MarcoIeni@users.noreply.github.com> Date: Tue, 21 Jan 2025 16:19:11 +0100 Subject: [PATCH 6/9] CI: free disk with in-tree script instead of GitHub Action Co-authored-by: whiteio --- .github/workflows/ci.yml | 2 +- src/ci/scripts/free-disk-space.sh | 142 ++++++++++++++++++++++++++++++ 2 files changed, 143 insertions(+), 1 deletion(-) create mode 100755 src/ci/scripts/free-disk-space.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c650df6a0ec2a..7a46d123c5d94 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -109,7 +109,7 @@ jobs: # intensive jobs to run on free runners, which however also have # less disk space. - name: free up disk space - uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be + run: src/ci/scripts/free-disk-space.sh if: matrix.free_disk # Rust Log Analyzer can't currently detect the PR number of a GitHub diff --git a/src/ci/scripts/free-disk-space.sh b/src/ci/scripts/free-disk-space.sh new file mode 100755 index 0000000000000..4a7dad0090b2e --- /dev/null +++ b/src/ci/scripts/free-disk-space.sh @@ -0,0 +1,142 @@ +#!/bin/bash + +# Free disk space on Linux GitHub action runners +# Script inspired by https://github.com/jlumbroso/free-disk-space + +# print a line of the specified character +printSeparationLine() { + for ((i = 0; i < 80; i++)); do + printf "%s" "$1" + done + printf "\n" +} + +# compute available space +# REF: https://unix.stackexchange.com/a/42049/60849 +# REF: https://stackoverflow.com/a/450821/408734 +getAvailableSpace() { echo $(df -a | awk 'NR > 1 {avail+=$4} END {print avail}'); } + +# make Kb human readable (assume the input is Kb) +# REF: https://unix.stackexchange.com/a/44087/60849 +formatByteCount() { echo $(numfmt --to=iec-i --suffix=B --padding=7 $1'000'); } + +# macro to output saved space +printSavedSpace() { + # Disk space before the operation + local before=${1} + local title=${2:-} + + local after + after=$(getAvailableSpace) + local saved=$((after - before)) + + echo "" + printSeparationLine "*" + if [ -n "${title}" ]; then + echo "=> ${title}: Saved $(formatByteCount "$saved")" + else + echo "=> Saved $(formatByteCount "$saved")" + fi + printSeparationLine "*" + echo "" +} + +# macro to print output of df with caption +printDF() { + local caption=${1} + + printSeparationLine "=" + echo "${caption}" + echo "" + echo "$ df -h" + echo "" + df -h + printSeparationLine "=" +} + +removeDir() { + dir=${1} + + local before + before=$(getAvailableSpace) + + sudo rm -rf "$dir" || true + + printSavedSpace "$before" "$dir" +} + +execAndMeasureSpaceChange() { + local operation=${1} # Function to execute + local title=${2} + + local before + before=$(getAvailableSpace) + $operation + + printSavedSpace "$before" "$title" +} + +# Remove large packages +# REF: https://github.com/apache/flink/blob/master/tools/azure-pipelines/free_disk_space.sh +cleanPackages() { + sudo apt-get -qq remove -y --fix-missing \ + '^aspnetcore-.*' \ + '^dotnet-.*' \ + '^llvm-.*' \ + 'php.*' \ + '^mongodb-.*' \ + '^mysql-.*' \ + 'azure-cli' \ + 'google-chrome-stable' \ + 'firefox' \ + 'powershell' \ + 'mono-devel' \ + 'libgl1-mesa-dri' \ + 'google-cloud-sdk' \ + 'google-cloud-cli' + + sudo apt-get autoremove -y || echo "::warning::The command [sudo apt-get autoremove -y] failed" + sudo apt-get clean || echo "::warning::The command [sudo apt-get clean] failed failed" +} + +# Remove Docker images +cleanDocker() { + echo "Removing the following docker images:" + sudo docker image ls + echo "Removing docker images..." + sudo docker image prune --all --force || true +} + +# Remove Swap storage +cleanSwap() { + sudo swapoff -a || true + sudo rm -rf /mnt/swapfile || true + free -h +} + +# Display initial disk space stats + +AVAILABLE_INITIAL=$(getAvailableSpace) + +printDF "BEFORE CLEAN-UP:" +echo "" + +removeDir /usr/local/lib/android +removeDir /usr/share/dotnet + +# Haskell runtime +removeDir /opt/ghc +removeDir /usr/local/.ghcup + +execAndMeasureSpaceChange cleanPackages "Large misc. packages" +execAndMeasureSpaceChange cleanDocker "Docker images" +execAndMeasureSpaceChange cleanSwap "Swap storage" + +# Output saved space statistic +echo "" +printDF "AFTER CLEAN-UP:" + +echo "" +echo "" + +printSavedSpace "$AVAILABLE_INITIAL" "Total saved" From aef640a6130ccb3edf9bc720881c3a9dde3c0ecd Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 21 Jan 2025 17:24:29 -0800 Subject: [PATCH 7/9] Only assert the `Parser` size on specific arches The size of this struct depends on the alignment of `u128`, for example powerpc64le and s390x have align-8 and end up with only 280 bytes. Our 64-bit tier-1 arches are the same though, so let's just assert on those. --- compiler/rustc_parse/src/parser/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 10756be6afb8e..25d8eb9b45392 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -189,8 +189,9 @@ pub struct Parser<'a> { } // This type is used a lot, e.g. it's cloned when matching many declarative macro rules with -// nonterminals. Make sure it doesn't unintentionally get bigger. -#[cfg(all(target_pointer_width = "64", not(target_arch = "s390x")))] +// nonterminals. Make sure it doesn't unintentionally get bigger. We only check a few arches +// though, because `TokenTypeSet(u128)` alignment varies on others, changing the total size. +#[cfg(all(target_pointer_width = "64", any(target_arch = "aarch64", target_arch = "x86_64")))] rustc_data_structures::static_assert_size!(Parser<'_>, 288); /// Stores span information about a closure. From eb3b3fe01c5331967ad8d24584c00d0fb9161121 Mon Sep 17 00:00:00 2001 From: MarcoIeni <11428655+MarcoIeni@users.noreply.github.com> Date: Wed, 22 Jan 2025 14:42:33 +0100 Subject: [PATCH 8/9] ci: use 8 core arm runner for dist-aarch64-linux --- src/ci/github-actions/jobs.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index c5b33a45db790..b86727c2bf607 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -50,6 +50,8 @@ runners: - &job-aarch64-linux os: ubuntu-22.04-arm + - &job-aarch64-linux-8c + os: ubuntu-22.04-arm64-8core-32gb envs: env-x86_64-apple-tests: &env-x86_64-apple-tests SCRIPT: ./x.py --stage 2 test --skip tests/ui --skip tests/rustdoc -- --exact @@ -142,7 +144,7 @@ auto: - name: dist-aarch64-linux env: CODEGEN_BACKENDS: llvm,cranelift - <<: *job-aarch64-linux + <<: *job-aarch64-linux-8c - name: dist-android <<: *job-linux-4c From 6b06aa619297c198e923e1d406a5bb0534260fef Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Wed, 22 Jan 2025 14:07:43 -0800 Subject: [PATCH 9/9] Enable kernel sanitizers for aarch64-unknown-none-softfloat We want kernels to be able to use this bare metal target, so let's enable the sanitizers that kernels want to use. --- .../src/spec/targets/aarch64_unknown_none_softfloat.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs index d6b77ffd091a9..3b719ebaf07e8 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs @@ -7,7 +7,8 @@ // For example, `-C target-cpu=cortex-a53`. use crate::spec::{ - Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, StackProbeType, Target, TargetOptions, + Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, SanitizerSet, StackProbeType, Target, + TargetOptions, }; pub(crate) fn target() -> Target { @@ -19,6 +20,7 @@ pub(crate) fn target() -> Target { relocation_model: RelocModel::Static, disable_redzone: true, max_atomic_width: Some(128), + supported_sanitizers: SanitizerSet::KCFI | SanitizerSet::KERNELADDRESS, stack_probes: StackProbeType::Inline, panic_strategy: PanicStrategy::Abort, ..Default::default()