Auto merge of #139992 - matthiaskrgr:rollup-ak3uibu, r=matthiaskrgr

Rollup of 8 pull requests

Successful merges:

 - #139351 (Autodiff batching2)
 - #139483 (f*::NAN: guarantee that this is a quiet NaN)
 - #139498 (Ignore zero-sized types in wasm future-compat warning)
 - #139967 (Introduce and use specialized `//@ ignore-auxiliary` for test support files instead of using `//@ ignore-test`)
 - #139969 (update libc)
 - #139971 (Make C string merging test work on MIPS)
 - #139974 (Change `InterpCx::instantiate*` function visibility to pub)
 - #139977 (Fix drop handling in `hint::select_unpredictable`)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2025-04-17 21:30:51 +00:00
commit 1f76d219c9
69 changed files with 387 additions and 141 deletions

View file

@ -2022,9 +2022,9 @@ checksum = "9fa0e2a1fcbe2f6be6c42e342259976206b383122fc152e872795338b5a3f3a7"
[[package]]
name = "libc"
version = "0.2.171"
version = "0.2.172"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
[[package]]
name = "libdbus-sys"

View file

@ -50,8 +50,16 @@ pub enum DiffActivity {
/// with it.
Dual,
/// Forward Mode, Compute derivatives for this input/output and *overwrite* the shadow argument
/// with it. It expects the shadow argument to be `width` times larger than the original
/// input/output.
Dualv,
/// Forward Mode, Compute derivatives for this input/output and *overwrite* the shadow argument
/// with it. Drop the code which updates the original input/output for maximum performance.
DualOnly,
/// Forward Mode, Compute derivatives for this input/output and *overwrite* the shadow argument
/// with it. Drop the code which updates the original input/output for maximum performance.
/// It expects the shadow argument to be `width` times larger than the original input/output.
DualvOnly,
/// Reverse Mode, Compute derivatives for this &T or *T input and *add* it to the shadow argument.
Duplicated,
/// Reverse Mode, Compute derivatives for this &T or *T input and *add* it to the shadow argument.
@ -59,7 +67,15 @@ pub enum DiffActivity {
DuplicatedOnly,
/// All Integers must be Const, but these are used to mark the integer which represents the
/// length of a slice/vec. This is used for safety checks on slices.
FakeActivitySize,
/// The integer (if given) specifies the size of the slice element in bytes.
FakeActivitySize(Option<u32>),
}
impl DiffActivity {
pub fn is_dual_or_const(&self) -> bool {
use DiffActivity::*;
matches!(self, |Dual| DualOnly | Dualv | DualvOnly | Const)
}
}
/// We generate one of these structs for each `#[autodiff(...)]` attribute.
#[derive(Clone, Eq, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
@ -131,11 +147,7 @@ pub fn valid_ret_activity(mode: DiffMode, activity: DiffActivity) -> bool {
match mode {
DiffMode::Error => false,
DiffMode::Source => false,
DiffMode::Forward => {
activity == DiffActivity::Dual
|| activity == DiffActivity::DualOnly
|| activity == DiffActivity::Const
}
DiffMode::Forward => activity.is_dual_or_const(),
DiffMode::Reverse => {
activity == DiffActivity::Const
|| activity == DiffActivity::Active
@ -153,10 +165,8 @@ pub fn valid_ret_activity(mode: DiffMode, activity: DiffActivity) -> bool {
pub fn valid_ty_for_activity(ty: &P<Ty>, activity: DiffActivity) -> bool {
use DiffActivity::*;
// It's always allowed to mark something as Const, since we won't compute derivatives wrt. it.
if matches!(activity, Const) {
return true;
}
if matches!(activity, Dual | DualOnly) {
// Dual variants also support all types.
if activity.is_dual_or_const() {
return true;
}
// FIXME(ZuseZ4) We should make this more robust to also
@ -172,9 +182,7 @@ pub fn valid_input_activity(mode: DiffMode, activity: DiffActivity) -> bool {
return match mode {
DiffMode::Error => false,
DiffMode::Source => false,
DiffMode::Forward => {
matches!(activity, Dual | DualOnly | Const)
}
DiffMode::Forward => activity.is_dual_or_const(),
DiffMode::Reverse => {
matches!(activity, Active | ActiveOnly | Duplicated | DuplicatedOnly | Const)
}
@ -189,10 +197,12 @@ impl Display for DiffActivity {
DiffActivity::Active => write!(f, "Active"),
DiffActivity::ActiveOnly => write!(f, "ActiveOnly"),
DiffActivity::Dual => write!(f, "Dual"),
DiffActivity::Dualv => write!(f, "Dualv"),
DiffActivity::DualOnly => write!(f, "DualOnly"),
DiffActivity::DualvOnly => write!(f, "DualvOnly"),
DiffActivity::Duplicated => write!(f, "Duplicated"),
DiffActivity::DuplicatedOnly => write!(f, "DuplicatedOnly"),
DiffActivity::FakeActivitySize => write!(f, "FakeActivitySize"),
DiffActivity::FakeActivitySize(s) => write!(f, "FakeActivitySize({:?})", s),
}
}
}
@ -220,7 +230,9 @@ impl FromStr for DiffActivity {
"ActiveOnly" => Ok(DiffActivity::ActiveOnly),
"Const" => Ok(DiffActivity::Const),
"Dual" => Ok(DiffActivity::Dual),
"Dualv" => Ok(DiffActivity::Dualv),
"DualOnly" => Ok(DiffActivity::DualOnly),
"DualvOnly" => Ok(DiffActivity::DualvOnly),
"Duplicated" => Ok(DiffActivity::Duplicated),
"DuplicatedOnly" => Ok(DiffActivity::DuplicatedOnly),
_ => Err(()),

View file

@ -799,8 +799,19 @@ mod llvm_enzyme {
d_inputs.push(shadow_arg.clone());
}
}
DiffActivity::Dual | DiffActivity::DualOnly => {
for i in 0..x.width {
DiffActivity::Dual
| DiffActivity::DualOnly
| DiffActivity::Dualv
| DiffActivity::DualvOnly => {
// the *v variants get lowered to enzyme_dupv and enzyme_dupnoneedv, which cause
// Enzyme to not expect N arguments, but one argument (which is instead larger).
let iterations =
if matches!(activity, DiffActivity::Dualv | DiffActivity::DualvOnly) {
1
} else {
x.width
};
for i in 0..iterations {
let mut shadow_arg = arg.clone();
let old_name = if let PatKind::Ident(_, ident, _) = arg.pat.kind {
ident.name
@ -823,7 +834,7 @@ mod llvm_enzyme {
DiffActivity::Const => {
// Nothing to do here.
}
DiffActivity::None | DiffActivity::FakeActivitySize => {
DiffActivity::None | DiffActivity::FakeActivitySize(_) => {
panic!("Should not happen");
}
}
@ -887,8 +898,8 @@ mod llvm_enzyme {
}
};
if let DiffActivity::Dual = x.ret_activity {
let kind = if x.width == 1 {
if matches!(x.ret_activity, DiffActivity::Dual | DiffActivity::Dualv) {
let kind = if x.width == 1 || matches!(x.ret_activity, DiffActivity::Dualv) {
// Dual can only be used for f32/f64 ret.
// In that case we return now a tuple with two floats.
TyKind::Tup(thin_vec![ty.clone(), ty.clone()])
@ -903,7 +914,7 @@ mod llvm_enzyme {
let ty = P(rustc_ast::Ty { kind, id: ty.id, span: ty.span, tokens: None });
d_decl.output = FnRetTy::Ty(ty);
}
if let DiffActivity::DualOnly = x.ret_activity {
if matches!(x.ret_activity, DiffActivity::DualOnly | DiffActivity::DualvOnly) {
// No need to change the return type,
// we will just return the shadow in place of the primal return.
// However, if we have a width > 1, then we don't return -> T, but -> [T; width]

View file

@ -123,7 +123,7 @@ impl<'a, 'll, CX: Borrow<SCx<'ll>>> GenericBuilder<'a, 'll, CX> {
/// Empty string, to be used where LLVM expects an instruction name, indicating
/// that the instruction is to be left unnamed (i.e. numbered, in textual IR).
// FIXME(eddyb) pass `&CStr` directly to FFI once it's a thin pointer.
const UNNAMED: *const c_char = c"".as_ptr();
pub(crate) const UNNAMED: *const c_char = c"".as_ptr();
impl<'ll, CX: Borrow<SCx<'ll>>> BackendTypes for GenericBuilder<'_, 'll, CX> {
type Value = <GenericCx<'ll, CX> as BackendTypes>::Value;

View file

@ -10,7 +10,7 @@ use rustc_middle::bug;
use tracing::{debug, trace};
use crate::back::write::llvm_err;
use crate::builder::SBuilder;
use crate::builder::{SBuilder, UNNAMED};
use crate::context::SimpleCx;
use crate::declare::declare_simple_fn;
use crate::errors::{AutoDiffWithoutEnable, LlvmError};
@ -51,6 +51,7 @@ fn has_sret(fnc: &Value) -> bool {
// using iterators and peek()?
fn match_args_from_caller_to_enzyme<'ll>(
cx: &SimpleCx<'ll>,
builder: &SBuilder<'ll, 'll>,
width: u32,
args: &mut Vec<&'ll llvm::Value>,
inputs: &[DiffActivity],
@ -78,7 +79,9 @@ fn match_args_from_caller_to_enzyme<'ll>(
let enzyme_const = cx.create_metadata("enzyme_const".to_string()).unwrap();
let enzyme_out = cx.create_metadata("enzyme_out".to_string()).unwrap();
let enzyme_dup = cx.create_metadata("enzyme_dup".to_string()).unwrap();
let enzyme_dupv = cx.create_metadata("enzyme_dupv".to_string()).unwrap();
let enzyme_dupnoneed = cx.create_metadata("enzyme_dupnoneed".to_string()).unwrap();
let enzyme_dupnoneedv = cx.create_metadata("enzyme_dupnoneedv".to_string()).unwrap();
while activity_pos < inputs.len() {
let diff_activity = inputs[activity_pos as usize];
@ -90,13 +93,34 @@ fn match_args_from_caller_to_enzyme<'ll>(
DiffActivity::Active => (enzyme_out, false),
DiffActivity::ActiveOnly => (enzyme_out, false),
DiffActivity::Dual => (enzyme_dup, true),
DiffActivity::Dualv => (enzyme_dupv, true),
DiffActivity::DualOnly => (enzyme_dupnoneed, true),
DiffActivity::DualvOnly => (enzyme_dupnoneedv, true),
DiffActivity::Duplicated => (enzyme_dup, true),
DiffActivity::DuplicatedOnly => (enzyme_dupnoneed, true),
DiffActivity::FakeActivitySize => (enzyme_const, false),
DiffActivity::FakeActivitySize(_) => (enzyme_const, false),
};
let outer_arg = outer_args[outer_pos];
args.push(cx.get_metadata_value(activity));
if matches!(diff_activity, DiffActivity::Dualv) {
let next_outer_arg = outer_args[outer_pos + 1];
let elem_bytes_size: u64 = match inputs[activity_pos + 1] {
DiffActivity::FakeActivitySize(Some(s)) => s.into(),
_ => bug!("incorrect Dualv handling recognized."),
};
// stride: sizeof(T) * n_elems.
// n_elems is the next integer.
// Now we multiply `4 * next_outer_arg` to get the stride.
let mul = unsafe {
llvm::LLVMBuildMul(
builder.llbuilder,
cx.get_const_i64(elem_bytes_size),
next_outer_arg,
UNNAMED,
)
};
args.push(mul);
}
args.push(outer_arg);
if duplicated {
// We know that duplicated args by construction have a following argument,
@ -114,7 +138,7 @@ fn match_args_from_caller_to_enzyme<'ll>(
} else {
let next_activity = inputs[activity_pos + 1];
// We analyze the MIR types and add this dummy activity if we visit a slice.
next_activity == DiffActivity::FakeActivitySize
matches!(next_activity, DiffActivity::FakeActivitySize(_))
}
};
if slice {
@ -125,7 +149,10 @@ fn match_args_from_caller_to_enzyme<'ll>(
// int2 >= int1, which means the shadow vector is large enough to store the gradient.
assert_eq!(cx.type_kind(next_outer_ty), TypeKind::Integer);
for i in 0..(width as usize) {
let iterations =
if matches!(diff_activity, DiffActivity::Dualv) { 1 } else { width as usize };
for i in 0..iterations {
let next_outer_arg2 = outer_args[outer_pos + 2 * (i + 1)];
let next_outer_ty2 = cx.val_ty(next_outer_arg2);
assert_eq!(cx.type_kind(next_outer_ty2), TypeKind::Pointer);
@ -136,7 +163,7 @@ fn match_args_from_caller_to_enzyme<'ll>(
}
args.push(cx.get_metadata_value(enzyme_const));
args.push(next_outer_arg);
outer_pos += 2 + 2 * width as usize;
outer_pos += 2 + 2 * iterations;
activity_pos += 2;
} else {
// A duplicated pointer will have the following two outer_fn arguments:
@ -360,6 +387,7 @@ fn generate_enzyme_call<'ll>(
let outer_args: Vec<&llvm::Value> = get_params(outer_fn);
match_args_from_caller_to_enzyme(
&cx,
&builder,
attrs.width,
&mut args,
&attrs.input_activity,

View file

@ -268,7 +268,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
/// Call this on things you got out of the MIR (so it is as generic as the current
/// stack frame), to bring it into the proper environment for this interpreter.
pub(super) fn instantiate_from_current_frame_and_normalize_erasing_regions<
pub fn instantiate_from_current_frame_and_normalize_erasing_regions<
T: TypeFoldable<TyCtxt<'tcx>>,
>(
&self,
@ -279,9 +279,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
/// Call this on things you got out of the MIR (so it is as generic as the provided
/// stack frame), to bring it into the proper environment for this interpreter.
pub(super) fn instantiate_from_frame_and_normalize_erasing_regions<
T: TypeFoldable<TyCtxt<'tcx>>,
>(
pub fn instantiate_from_frame_and_normalize_erasing_regions<T: TypeFoldable<TyCtxt<'tcx>>>(
&self,
frame: &Frame<'tcx, M::Provenance, M::FrameExtra>,
value: T,

View file

@ -111,6 +111,11 @@ fn wasm_abi_safe<'tcx>(tcx: TyCtxt<'tcx>, arg: &ArgAbi<'tcx, Ty<'tcx>>) -> bool
}
}
// Zero-sized types are dropped in both ABIs, so they're safe
if arg.layout.is_zst() {
return true;
}
false
}

View file

@ -2,7 +2,7 @@ use rustc_ast::expand::autodiff_attrs::{AutoDiffItem, DiffActivity};
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_middle::bug;
use rustc_middle::mir::mono::MonoItem;
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
use rustc_middle::ty::{self, Instance, PseudoCanonicalInput, Ty, TyCtxt, TypingEnv};
use rustc_symbol_mangling::symbol_name_for_instance_in_crate;
use tracing::{debug, trace};
@ -22,23 +22,51 @@ fn adjust_activity_to_abi<'tcx>(tcx: TyCtxt<'tcx>, fn_ty: Ty<'tcx>, da: &mut Vec
for (i, ty) in sig.inputs().iter().enumerate() {
if let Some(inner_ty) = ty.builtin_deref(true) {
if inner_ty.is_slice() {
// Now we need to figure out the size of each slice element in memory to allow
// safety checks and usability improvements in the backend.
let sty = match inner_ty.builtin_index() {
Some(sty) => sty,
None => {
panic!("slice element type unknown");
}
};
let pci = PseudoCanonicalInput {
typing_env: TypingEnv::fully_monomorphized(),
value: sty,
};
let layout = tcx.layout_of(pci);
let elem_size = match layout {
Ok(layout) => layout.size,
Err(_) => {
bug!("autodiff failed to compute slice element size");
}
};
let elem_size: u32 = elem_size.bytes() as u32;
// We know that the length will be passed as extra arg.
if !da.is_empty() {
// We are looking at a slice. The length of that slice will become an
// extra integer on llvm level. Integers are always const.
// However, if the slice get's duplicated, we want to know to later check the
// size. So we mark the new size argument as FakeActivitySize.
// There is one FakeActivitySize per slice, so for convenience we store the
// slice element size in bytes in it. We will use the size in the backend.
let activity = match da[i] {
DiffActivity::DualOnly
| DiffActivity::Dual
| DiffActivity::Dualv
| DiffActivity::DuplicatedOnly
| DiffActivity::Duplicated => DiffActivity::FakeActivitySize,
| DiffActivity::Duplicated => {
DiffActivity::FakeActivitySize(Some(elem_size))
}
DiffActivity::Const => DiffActivity::Const,
_ => bug!("unexpected activity for ptr/ref"),
};
new_activities.push(activity);
new_positions.push(i + 1);
}
continue;
}
}

View file

@ -4,6 +4,7 @@
//!
//! Hints may be compile time or runtime.
use crate::mem::MaybeUninit;
use crate::{intrinsics, ub_checks};
/// Informs the compiler that the site which is calling this function is not
@ -735,9 +736,9 @@ pub const fn cold_path() {
crate::intrinsics::cold_path()
}
/// Returns either `true_val` or `false_val` depending on the value of `b`,
/// with a hint to the compiler that `b` is unlikely to be correctly
/// predicted by a CPUs branch predictor.
/// Returns either `true_val` or `false_val` depending on the value of
/// `condition`, with a hint to the compiler that `condition` is unlikely to be
/// correctly predicted by a CPUs branch predictor.
///
/// This method is functionally equivalent to
/// ```ignore (this is just for illustrative purposes)
@ -753,10 +754,10 @@ pub const fn cold_path() {
/// search.
///
/// Note however that this lowering is not guaranteed (on any platform) and
/// should not be relied upon when trying to write constant-time code. Also
/// be aware that this lowering might *decrease* performance if `condition`
/// is well-predictable. It is advisable to perform benchmarks to tell if
/// this function is useful.
/// should not be relied upon when trying to write cryptographic constant-time
/// code. Also be aware that this lowering might *decrease* performance if
/// `condition` is well-predictable. It is advisable to perform benchmarks to
/// tell if this function is useful.
///
/// # Examples
///
@ -780,6 +781,17 @@ pub const fn cold_path() {
/// ```
#[inline(always)]
#[unstable(feature = "select_unpredictable", issue = "133962")]
pub fn select_unpredictable<T>(b: bool, true_val: T, false_val: T) -> T {
crate::intrinsics::select_unpredictable(b, true_val, false_val)
pub fn select_unpredictable<T>(condition: bool, true_val: T, false_val: T) -> T {
// FIXME(https://github.com/rust-lang/unsafe-code-guidelines/issues/245):
// Change this to use ManuallyDrop instead.
let mut true_val = MaybeUninit::new(true_val);
let mut false_val = MaybeUninit::new(false_val);
// SAFETY: The value that is not selected is dropped, and the selected one
// is returned. This is necessary because the intrinsic doesn't drop the
// value that is not selected.
unsafe {
crate::intrinsics::select_unpredictable(!condition, &mut true_val, &mut false_val)
.assume_init_drop();
crate::intrinsics::select_unpredictable(condition, true_val, false_val).assume_init()
}
}

View file

@ -1327,6 +1327,8 @@ pub const fn unlikely(b: bool) -> bool {
/// any safety invariants.
///
/// The public form of this instrinsic is [`core::hint::select_unpredictable`].
/// However unlike the public form, the intrinsic will not drop the value that
/// is not selected.
#[unstable(feature = "core_intrinsics", issue = "none")]
#[rustc_intrinsic]
#[rustc_nounwind]

View file

@ -224,14 +224,16 @@ impl f128 {
/// Not a Number (NaN).
///
/// Note that IEEE 754 doesn't define just a single NaN value;
/// a plethora of bit patterns are considered to be NaN.
/// Furthermore, the standard makes a difference
/// between a "signaling" and a "quiet" NaN,
/// and allows inspecting its "payload" (the unspecified bits in the bit pattern).
/// This constant isn't guaranteed to equal to any specific NaN bitpattern,
/// and the stability of its representation over Rust versions
/// and target platforms isn't guaranteed.
/// Note that IEEE 754 doesn't define just a single NaN value; a plethora of bit patterns are
/// considered to be NaN. Furthermore, the standard makes a difference between a "signaling" and
/// a "quiet" NaN, and allows inspecting its "payload" (the unspecified bits in the bit pattern)
/// and its sign. See the [specification of NaN bit patterns](f32#nan-bit-patterns) for more
/// info.
///
/// This constant is guaranteed to be a quiet NaN (on targets that follow the Rust assumptions
/// that the quiet/signaling bit being set to 1 indicates a quiet NaN). Beyond that, nothing is
/// guaranteed about the specific bit pattern chosen here: both payload and sign are arbitrary.
/// The concrete bit pattern may change across Rust versions and target platforms.
#[allow(clippy::eq_op)]
#[rustc_diagnostic_item = "f128_nan"]
#[unstable(feature = "f128", issue = "116909")]

View file

@ -219,14 +219,16 @@ impl f16 {
/// Not a Number (NaN).
///
/// Note that IEEE 754 doesn't define just a single NaN value;
/// a plethora of bit patterns are considered to be NaN.
/// Furthermore, the standard makes a difference
/// between a "signaling" and a "quiet" NaN,
/// and allows inspecting its "payload" (the unspecified bits in the bit pattern).
/// This constant isn't guaranteed to equal to any specific NaN bitpattern,
/// and the stability of its representation over Rust versions
/// and target platforms isn't guaranteed.
/// Note that IEEE 754 doesn't define just a single NaN value; a plethora of bit patterns are
/// considered to be NaN. Furthermore, the standard makes a difference between a "signaling" and
/// a "quiet" NaN, and allows inspecting its "payload" (the unspecified bits in the bit pattern)
/// and its sign. See the [specification of NaN bit patterns](f32#nan-bit-patterns) for more
/// info.
///
/// This constant is guaranteed to be a quiet NaN (on targets that follow the Rust assumptions
/// that the quiet/signaling bit being set to 1 indicates a quiet NaN). Beyond that, nothing is
/// guaranteed about the specific bit pattern chosen here: both payload and sign are arbitrary.
/// The concrete bit pattern may change across Rust versions and target platforms.
#[allow(clippy::eq_op)]
#[rustc_diagnostic_item = "f16_nan"]
#[unstable(feature = "f16", issue = "116909")]

View file

@ -470,14 +470,16 @@ impl f32 {
/// Not a Number (NaN).
///
/// Note that IEEE 754 doesn't define just a single NaN value;
/// a plethora of bit patterns are considered to be NaN.
/// Furthermore, the standard makes a difference
/// between a "signaling" and a "quiet" NaN,
/// and allows inspecting its "payload" (the unspecified bits in the bit pattern).
/// This constant isn't guaranteed to equal to any specific NaN bitpattern,
/// and the stability of its representation over Rust versions
/// and target platforms isn't guaranteed.
/// Note that IEEE 754 doesn't define just a single NaN value; a plethora of bit patterns are
/// considered to be NaN. Furthermore, the standard makes a difference between a "signaling" and
/// a "quiet" NaN, and allows inspecting its "payload" (the unspecified bits in the bit pattern)
/// and its sign. See the [specification of NaN bit patterns](f32#nan-bit-patterns) for more
/// info.
///
/// This constant is guaranteed to be a quiet NaN (on targets that follow the Rust assumptions
/// that the quiet/signaling bit being set to 1 indicates a quiet NaN). Beyond that, nothing is
/// guaranteed about the specific bit pattern chosen here: both payload and sign are arbitrary.
/// The concrete bit pattern may change across Rust versions and target platforms.
#[stable(feature = "assoc_int_consts", since = "1.43.0")]
#[rustc_diagnostic_item = "f32_nan"]
#[allow(clippy::eq_op)]

View file

@ -469,14 +469,16 @@ impl f64 {
/// Not a Number (NaN).
///
/// Note that IEEE 754 doesn't define just a single NaN value;
/// a plethora of bit patterns are considered to be NaN.
/// Furthermore, the standard makes a difference
/// between a "signaling" and a "quiet" NaN,
/// and allows inspecting its "payload" (the unspecified bits in the bit pattern).
/// This constant isn't guaranteed to equal to any specific NaN bitpattern,
/// and the stability of its representation over Rust versions
/// and target platforms isn't guaranteed.
/// Note that IEEE 754 doesn't define just a single NaN value; a plethora of bit patterns are
/// considered to be NaN. Furthermore, the standard makes a difference between a "signaling" and
/// a "quiet" NaN, and allows inspecting its "payload" (the unspecified bits in the bit pattern)
/// and its sign. See the [specification of NaN bit patterns](f32#nan-bit-patterns) for more
/// info.
///
/// This constant is guaranteed to be a quiet NaN (on targets that follow the Rust assumptions
/// that the quiet/signaling bit being set to 1 indicates a quiet NaN). Beyond that, nothing is
/// guaranteed about the specific bit pattern chosen here: both payload and sign are arbitrary.
/// The concrete bit pattern may change across Rust versions and target platforms.
#[rustc_diagnostic_item = "f64_nan"]
#[stable(feature = "assoc_int_consts", since = "1.43.0")]
#[allow(clippy::eq_op)]

View file

@ -0,0 +1,23 @@
#[test]
fn select_unpredictable_drop() {
use core::cell::Cell;
struct X<'a>(&'a Cell<bool>);
impl Drop for X<'_> {
fn drop(&mut self) {
self.0.set(true);
}
}
let a_dropped = Cell::new(false);
let b_dropped = Cell::new(false);
let a = X(&a_dropped);
let b = X(&b_dropped);
assert!(!a_dropped.get());
assert!(!b_dropped.get());
let selected = core::hint::select_unpredictable(core::hint::black_box(true), a, b);
assert!(!a_dropped.get());
assert!(b_dropped.get());
drop(selected);
assert!(a_dropped.get());
assert!(b_dropped.get());
}

View file

@ -68,6 +68,7 @@
#![feature(pointer_is_aligned_to)]
#![feature(portable_simd)]
#![feature(ptr_metadata)]
#![feature(select_unpredictable)]
#![feature(slice_from_ptr_range)]
#![feature(slice_internals)]
#![feature(slice_partition_dedup)]
@ -147,6 +148,7 @@ mod ffi;
mod fmt;
mod future;
mod hash;
mod hint;
mod intrinsics;
mod io;
mod iter;

View file

@ -175,6 +175,8 @@ See [compiletest directives] for a listing of directives.
- For `ignore-*`/`needs-*`/`only-*` directives, unless extremely obvious,
provide a brief remark on why the directive is needed. E.g. `"//@ ignore-wasi
(wasi codegens the main symbol differently)"`.
- When using `//@ ignore-auxiliary`, specify the corresponding main test files,
e.g. ``//@ ignore-auxiliary (used by `./foo.rs`)``.
## FileCheck best practices

View file

@ -124,6 +124,9 @@ means the test won't be compiled or run.
* `ignore-X` where `X` is a target detail or other criteria on which to ignore the test (see below)
* `only-X` is like `ignore-X`, but will *only* run the test on that target or
stage
* `ignore-auxiliary` is intended for files that *participate* in one or more other
main test files but that `compiletest` should not try to build the file itself.
Please backlink to which main test is actually using the auxiliary file.
* `ignore-test` always ignores the test. This can be used to temporarily disable
a test if it is currently not working, but you want to keep it in tree to
re-enable it later.

View file

@ -44,6 +44,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
"ignore-arm-unknown-linux-gnueabihf",
"ignore-arm-unknown-linux-musleabi",
"ignore-arm-unknown-linux-musleabihf",
"ignore-auxiliary",
"ignore-avr",
"ignore-beta",
"ignore-cdb",

View file

@ -100,6 +100,10 @@ fn parse_cfg_name_directive<'a>(
name: "test",
message: "always"
}
condition! {
name: "auxiliary",
message: "used by another main test file"
}
condition! {
name: &config.target,
allowed_names: &target_cfgs.all_targets,

View file

@ -940,3 +940,9 @@ fn test_supported_crate_types() {
"//@ needs-crate-type: bin, cdylib, dylib, lib, proc-macro, rlib, staticlib"
));
}
#[test]
fn test_ignore_auxiliary() {
let config = cfg().build();
assert!(check_ignore(&config, "//@ ignore-auxiliary"));
}

View file

@ -13,7 +13,7 @@ pub fn main() {
TLS.set(Some(Box::leak(Box::new(123))));
// We can only ignore leaks on targets that use `#[thread_local]` statics to implement
// `thread_local!`. Ignore the test on targest that don't.
// `thread_local!`. Ignore the test on targets that don't.
if cfg!(target_thread_local) {
thread_local! {
static TLS_KEY: Cell<Option<&'static i32>> = Cell::new(None);

View file

@ -902,9 +902,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.169"
version = "0.2.172"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
[[package]]
name = "libloading"

View file

@ -1,3 +1,5 @@
// MIPS assembler uses the label prefix `$anon.` for local anonymous variables
// other architectures (including ARM and x86-64) use the prefix `.Lanon.`
//@ only-linux
//@ assembly-output: emit-asm
//@ compile-flags: --crate-type=lib -Copt-level=3
@ -6,13 +8,13 @@
use std::ffi::CStr;
// CHECK: .section .rodata.str1.{{[12]}},"aMS"
// CHECK: .Lanon.{{.+}}:
// CHECK: {{(\.L|\$)}}anon.{{.+}}:
// CHECK-NEXT: .asciz "foo"
#[unsafe(no_mangle)]
static CSTR: &[u8; 4] = b"foo\0";
// CHECK-NOT: .section
// CHECK: .Lanon.{{.+}}:
// CHECK: {{(\.L|\$)}}anon.{{.+}}:
// CHECK-NEXT: .asciz "bar"
#[unsafe(no_mangle)]
pub fn cstr() -> &'static CStr {
@ -20,7 +22,7 @@ pub fn cstr() -> &'static CStr {
}
// CHECK-NOT: .section
// CHECK: .Lanon.{{.+}}:
// CHECK: {{(\.L|\$)}}anon.{{.+}}:
// CHECK-NEXT: .asciz "baz"
#[unsafe(no_mangle)]
pub fn manual_cstr() -> &'static str {

113
tests/codegen/autodiffv2.rs Normal file
View file

@ -0,0 +1,113 @@
//@ compile-flags: -Zautodiff=Enable -C opt-level=3 -Clto=fat
//@ no-prefer-dynamic
//@ needs-enzyme
//
// In Enzyme, we test against a large range of LLVM versions (5+) and don't have overly many
// breakages. One benefit is that we match the IR generated by Enzyme only after running it
// through LLVM's O3 pipeline, which will remove most of the noise.
// However, our integration test could also be affected by changes in how rustc lowers MIR into
// LLVM-IR, which could cause additional noise and thus breakages. If that's the case, we should
// reduce this test to only match the first lines and the ret instructions.
//
// The function tested here has 4 inputs and 5 outputs, so we could either call forward-mode
// autodiff 4 times, or reverse mode 5 times. Since a forward-mode call is usually faster than
// reverse mode, we prefer it here. This file also tests a new optimization (batch mode), which
// allows us to call forward-mode autodiff only once, and get all 5 outputs in a single call.
//
// We support 2 different batch modes. `d_square2` has the same interface as scalar forward-mode,
// but each shadow argument is `width` times larger (thus 16 and 20 elements here).
// `d_square3` instead takes `width` (4) shadow arguments, which are all the same size as the
// original function arguments.
//
// FIXME(autodiff): We currently can't test `d_square1` and `d_square3` in the same file, since they
// generate the same dummy functions which get merged by LLVM, breaking pieces of our pipeline which
// try to rewrite the dummy functions later. We should consider to change to pure declarations both
// in our frontend and in the llvm backend to avoid these issues.
#![feature(autodiff)]
use std::autodiff::autodiff;
#[no_mangle]
//#[autodiff(d_square1, Forward, Dual, Dual)]
#[autodiff(d_square2, Forward, 4, Dualv, Dualv)]
#[autodiff(d_square3, Forward, 4, Dual, Dual)]
fn square(x: &[f32], y: &mut [f32]) {
assert!(x.len() >= 4);
assert!(y.len() >= 5);
y[0] = 4.3 * x[0] + 1.2 * x[1] + 3.4 * x[2] + 2.1 * x[3];
y[1] = 2.3 * x[0] + 4.5 * x[1] + 1.7 * x[2] + 6.4 * x[3];
y[2] = 1.1 * x[0] + 3.3 * x[1] + 2.5 * x[2] + 4.7 * x[3];
y[3] = 5.2 * x[0] + 1.4 * x[1] + 2.6 * x[2] + 3.8 * x[3];
y[4] = 1.0 * x[0] + 2.0 * x[1] + 3.0 * x[2] + 4.0 * x[3];
}
fn main() {
let x1 = std::hint::black_box(vec![0.0, 1.0, 2.0, 3.0]);
let dx1 = std::hint::black_box(vec![1.0; 12]);
let z1 = std::hint::black_box(vec![1.0, 0.0, 0.0, 0.0]);
let z2 = std::hint::black_box(vec![0.0, 1.0, 0.0, 0.0]);
let z3 = std::hint::black_box(vec![0.0, 0.0, 1.0, 0.0]);
let z4 = std::hint::black_box(vec![0.0, 0.0, 0.0, 1.0]);
let z5 = std::hint::black_box(vec![
1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0,
]);
let mut y1 = std::hint::black_box(vec![0.0; 5]);
let mut y2 = std::hint::black_box(vec![0.0; 5]);
let mut y3 = std::hint::black_box(vec![0.0; 5]);
let mut y4 = std::hint::black_box(vec![0.0; 5]);
let mut y5 = std::hint::black_box(vec![0.0; 5]);
let mut y6 = std::hint::black_box(vec![0.0; 5]);
let mut dy1_1 = std::hint::black_box(vec![0.0; 5]);
let mut dy1_2 = std::hint::black_box(vec![0.0; 5]);
let mut dy1_3 = std::hint::black_box(vec![0.0; 5]);
let mut dy1_4 = std::hint::black_box(vec![0.0; 5]);
let mut dy2 = std::hint::black_box(vec![0.0; 20]);
let mut dy3_1 = std::hint::black_box(vec![0.0; 5]);
let mut dy3_2 = std::hint::black_box(vec![0.0; 5]);
let mut dy3_3 = std::hint::black_box(vec![0.0; 5]);
let mut dy3_4 = std::hint::black_box(vec![0.0; 5]);
// scalar.
//d_square1(&x1, &z1, &mut y1, &mut dy1_1);
//d_square1(&x1, &z2, &mut y2, &mut dy1_2);
//d_square1(&x1, &z3, &mut y3, &mut dy1_3);
//d_square1(&x1, &z4, &mut y4, &mut dy1_4);
// assert y1 == y2 == y3 == y4
//for i in 0..5 {
// assert_eq!(y1[i], y2[i]);
// assert_eq!(y1[i], y3[i]);
// assert_eq!(y1[i], y4[i]);
//}
// batch mode A)
d_square2(&x1, &z5, &mut y5, &mut dy2);
// assert y1 == y2 == y3 == y4 == y5
//for i in 0..5 {
// assert_eq!(y1[i], y5[i]);
//}
// batch mode B)
d_square3(&x1, &z1, &z2, &z3, &z4, &mut y6, &mut dy3_1, &mut dy3_2, &mut dy3_3, &mut dy3_4);
for i in 0..5 {
assert_eq!(y5[i], y6[i]);
}
for i in 0..5 {
assert_eq!(dy2[0..5][i], dy3_1[i]);
assert_eq!(dy2[5..10][i], dy3_2[i]);
assert_eq!(dy2[10..15][i], dy3_3[i]);
assert_eq!(dy2[15..20][i], dy3_4[i]);
}
}

View file

@ -1,4 +1,4 @@
//@ ignore-test: this is not a test
//@ ignore-auxiliary (used by `./main.rs`)
#[inline]
pub fn some_aux_mod_function() -> i32 {

View file

@ -1,5 +1,7 @@
//@ ignore-android
//@ ignore-test: #128971
// FIXME: stepping with "next" in a debugger skips past end-of-scope drops
//@ ignore-test (broken, see #128971)
#![allow(unused)]

View file

@ -1,4 +1,4 @@
//@ ignore-test (auxiliary, used by other tests)
//@ ignore-auxiliary (used by `./move-error-snippets.rs`)
macro_rules! aaa {
($c:ident) => {{

View file

@ -1,4 +1,4 @@
//@ ignore-test (auxiliary, used by other tests)
//@ ignore-auxiliary (used by `./two_files.rs`)
trait Foo { }

View file

@ -1,3 +1,3 @@
//@ ignore-test (auxiliary, used by other tests)
//@ ignore-auxiliary (used by `./inner-cfg-non-inline-mod.rs`)
#![cfg_attr(all(), cfg(false))]

View file

@ -1,4 +1,4 @@
//@ ignore-test (auxiliary, used by other tests)
//@ ignore-auxiliary (used by `./main.rs`)
#![crate_type = "lib"]
macro_rules! underscore {

View file

@ -1 +1 @@
//@ ignore-test not a test, auxiliary
//@ ignore-auxiliary (used by `../../macro-expanded-mod.rs`)

View file

@ -1,3 +1,3 @@
//@ ignore-test not a test, auxiliary
//@ ignore-auxiliary (used by `../../macro-expanded-mod.rs`)
mod_decl!(bar);

View file

@ -1,6 +0,0 @@
//@ ignore-test this is not a test
macro_rules! m {
() => { mod mod_file_not_owning_aux2; }
}
m!();

View file

@ -1 +0,0 @@
//@ ignore-test this is not a test

View file

@ -1,3 +0,0 @@
//@ ignore-test this is not a test
mod mod_file_not_owning_aux2;

View file

@ -1,4 +1,4 @@
//@ ignore-test auxiliary file for expansion-time.rs
//@ ignore-auxiliary (used by `./expansion-time.rs`)
1
2

View file

@ -1,4 +1,4 @@
//@ ignore-test: not a test
//@ ignore-auxiliary (used by `./root.rs`)
#[allow(tool::lint)]
pub fn foo() {}

View file

@ -1,3 +1,3 @@
//@ ignore-test: not a test
//@ ignore-auxiliary (used by `./lint-pre-expansion-extern-module.rs`)
pub fn try() {}

View file

@ -1,6 +1,4 @@
//@ ignore-test (auxiliary)
// Companion to allow-in-other-module.rs
//@ ignore-auxiliary (used by `./allow-in-other-module.rs`)
// This should not warn.
#![allow(not_a_real_lint)]

View file

@ -39,3 +39,9 @@ pub fn call_other_fun(x: MyType) {
unsafe { other_fun(x) } //~ERROR: wasm ABI transition
//~^WARN: previously accepted
}
// Zero-sized types are safe in both ABIs
#[repr(C)]
pub struct MyZstType;
#[allow(improper_ctypes_definitions)]
pub extern "C" fn zst_safe(_x: (), _y: MyZstType) {}

View file

@ -1,3 +1 @@
// ignore-test: this is not a test
1

View file

@ -1,3 +1 @@
// ignore-test: this is not a test
fn foo() { bar() }

View file

@ -1,4 +1,4 @@
//@ ignore-test auxiliary file for include-single-expr.rs
//@ ignore-auxiliary (used by `./include-single-expr.rs`)
0

View file

@ -1,4 +1,4 @@
//@ ignore-test auxiliary file for include-single-expr.rs
//@ ignore-auxiliary (used by `./include-single-expr.rs`)
0
10

View file

@ -1,3 +1,3 @@
//@ ignore-test -- this is an auxiliary file as part of another test.
//@ ignore-auxiliary (used by `../issue-69838-mods-relative-to-included-path.rs`)
pub fn i_am_in_bar() {}

View file

@ -1,3 +1,3 @@
//@ ignore-test -- this is an auxiliary file as part of another test.
//@ ignore-auxiliary (used by `../issue-69838-mods-relative-to-included-path.rs`)
pub mod bar;

View file

@ -1,4 +1,4 @@
//@ ignore-test (auxiliary, used by other tests)
//@ ignore-auxiliary (used by `../test.rs`)
macro_rules! m {
() => { include!("file.txt"); }

View file

@ -1,4 +1,3 @@
//
//@ ignore-test this is just a helper for the real test in this dir
//@ ignore-auxiliary (used by `./missing_non_modrs_mod.rs`)
mod missing;

View file

@ -1,4 +1,4 @@
//@ ignore-test this is just a helper for the real test in this dir
//@ ignore-auxiliary (used by `./missing_non_modrs_mod_inline.rs`)
mod inline {
mod missing;

View file

@ -1,5 +1,5 @@
error[E0583]: file not found for module `missing`
--> $DIR/foo.rs:4:1
--> $DIR/foo.rs:3:1
|
LL | mod missing;
| ^^^^^^^^^^^^

View file

@ -1,4 +1,3 @@
//@ run-pass
//@ ignore-test Not a test. Used by other tests
//@ ignore-auxiliary (used by `./mod_file_with_path_attr.rs` and `mod_file.rs`)
pub fn foo() -> isize { 10 }

View file

@ -1,3 +1,3 @@
//@ ignore-test Not a test. Used by other tests
//@ ignore-auxiliary (used by `./mod_file_correct_spans.rs`)
pub fn foo() -> isize { 10 }

View file

@ -1 +1 @@
//@ ignore-test not a test. aux file
//@ ignore-auxiliary (used by `./mod_file_disambig.rs`)

View file

@ -1 +1 @@
//@ ignore-test not a test. aux file
//@ ignore-auxiliary (used by `../mod_file_disambig.rs`)

View file

@ -1,6 +1,4 @@
//@ run-pass
//
//@ ignore-test: not a test, used by non_modrs_mods.rs
//@ ignore-auxiliary (used by `./non_modrs_mods.rs`)
pub mod inner_modrs_mod;
pub mod inner_foors_mod;

View file

@ -1,4 +1,4 @@
//@ ignore-test: not a test
//@ ignore-auxiliary (used by `./non_modrs_mods_and_inline_mods.rs`)
pub mod y {
pub mod z;

View file

@ -1 +1 @@
//@ ignore-test: not a test
//@ ignore-auxiliary (used by `../../../non_modrs_mods_and_inline_mods.rs`)

View file

@ -1,4 +1,4 @@
//@ ignore-test (auxiliary, used by other tests)
//@ ignore-auxiliary (used by `./saturating-float-casts.rs` and `./saturating-float-casts-wasm.rs`)
// Tests saturating float->int casts. See u128-as-f32.rs for the opposite direction.
//

View file

@ -1,4 +1,4 @@
//@ ignore-test: this is an auxiliary file for circular-modules-main.rs
//@ ignore-auxiliary (used by `./circular-modules-main.rs`)
#[path = "circular_modules_main.rs"]
mod circular_modules_main;

View file

@ -1,4 +1,4 @@
//@ ignore-test: this is an auxiliary file for circular-module-with-doc-comment-issue-97589.rs
//@ ignore-auxiliary (used by `./circular-module-with-doc-comment-issue-97589.rs`)
//! this comment caused the circular dependency checker to break

View file

@ -1,5 +1,4 @@
//@ run-pass
//@ ignore-test Not a test. Used by issue-48508.rs
//@ ignore-auxiliary (used by `./issue-48508.rs`)
pub fn other() -> f64 {
let µ = 1.0;

View file

@ -1 +1 @@
//@ ignore-test (auxiliary, used by other tests)
//@ ignore-auxiliary (used by `./attributes-on-modules-fail.rs`)

View file

@ -1,4 +1,4 @@
//@ ignore-test (auxiliary, used by other tests)
//@ ignore-auxiliary (used by `../inner-attr-non-inline-mod.rs`)
#![rustfmt::skip]
#![print_attr]

View file

@ -1 +1 @@
//@ ignore-test (auxiliary, used by other tests)
//@ ignore-auxiliary (used by `../attributes-on-modules-fail.rs`)

View file

@ -1,4 +1,4 @@
//@ ignore-test (auxiliary, used by other tests)
//@ ignore-auxiliary (used by `../../../pretty-print-hack-show.rs`)
#[derive(Print)]
enum ProceduralMasqueradeDummyType {

View file

@ -1,4 +1,4 @@
//@ ignore-test (auxiliary, used by other tests)
//@ ignore-auxiliary (used by `../../../pretty-print-hack-show.rs`)
#[derive(Print)]
enum ProceduralMasqueradeDummyType {

View file

@ -1,4 +1,4 @@
//@ ignore-test (auxiliary, used by other tests)
//@ ignore-auxiliary (used by `../../../pretty-print-hack/hide.rs`)
#[derive(Print)]
enum ProceduralMasqueradeDummyType {

View file

@ -1,5 +1,4 @@
//@ run-pass
//@ ignore-test: not a test, used by backtrace-debuginfo.rs to test file!()
//@ ignore-auxiliary (used by `./backtrace-debuginfo.rs` to test `file!()`)
#[inline(never)]
pub fn callback<F>(f: F) where F: FnOnce((&'static str, u32)) {