diff --git a/compiler/rustc_ast/src/expand/autodiff_attrs.rs b/compiler/rustc_ast/src/expand/autodiff_attrs.rs index c8ec185ee5e29..f01c781f46c65 100644 --- a/compiler/rustc_ast/src/expand/autodiff_attrs.rs +++ b/compiler/rustc_ast/src/expand/autodiff_attrs.rs @@ -77,6 +77,17 @@ pub struct AutoDiffAttrs { /// e.g. in the [JAX /// Documentation](https://jax.readthedocs.io/en/latest/_tutorials/advanced-autodiff.html#how-it-s-made-two-foundational-autodiff-functions). pub mode: DiffMode, + /// A user-provided, batching width. If not given, we will default to 1 (no batching). + /// Calling a differentiated, non-batched function through a loop 100 times is equivalent to: + /// - Calling the function 50 times with a batch size of 2 + /// - Calling the function 25 times with a batch size of 4, + /// etc. A batched function takes more (or longer) arguments, and might be able to benefit from + /// cache locality, better re-usal of primal values, and other optimizations. + /// We will (before LLVM's vectorizer runs) just generate most LLVM-IR instructions `width` + /// times, so this massively increases code size. As such, values like 1024 are unlikely to + /// work. We should consider limiting this to u8 or u16, but will leave it at u32 for + /// experiments for now and focus on documenting the implications of a large width. + pub width: u32, pub ret_activity: DiffActivity, pub input_activity: Vec, } @@ -222,6 +233,7 @@ impl AutoDiffAttrs { pub const fn error() -> Self { AutoDiffAttrs { mode: DiffMode::Error, + width: 0, ret_activity: DiffActivity::None, input_activity: Vec::new(), } @@ -229,6 +241,7 @@ impl AutoDiffAttrs { pub fn source() -> Self { AutoDiffAttrs { mode: DiffMode::Source, + width: 0, ret_activity: DiffActivity::None, input_activity: Vec::new(), } diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index 3f03834f8d781..603dc90bafca2 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -79,6 +79,7 @@ builtin_macros_autodiff_ret_activity = invalid return activity {$act} in {$mode} builtin_macros_autodiff_ty_activity = {$act} can not be used for this type builtin_macros_autodiff_unknown_activity = did not recognize Activity: `{$act}` +builtin_macros_autodiff_width = autodiff width must fit u32, but is {$width} builtin_macros_bad_derive_target = `derive` may only be applied to `struct`s, `enum`s and `union`s .label = not applicable here .label2 = not a `struct`, `enum` or `union` diff --git a/compiler/rustc_builtin_macros/src/autodiff.rs b/compiler/rustc_builtin_macros/src/autodiff.rs index 8937d35d53aed..7f99f75b2b9df 100644 --- a/compiler/rustc_builtin_macros/src/autodiff.rs +++ b/compiler/rustc_builtin_macros/src/autodiff.rs @@ -12,12 +12,12 @@ mod llvm_enzyme { valid_ty_for_activity, }; use rustc_ast::ptr::P; - use rustc_ast::token::{Token, TokenKind}; + use rustc_ast::token::{Lit, LitKind, Token, TokenKind}; use rustc_ast::tokenstream::*; use rustc_ast::visit::AssocCtxt::*; use rustc_ast::{ - self as ast, AssocItemKind, BindingMode, FnRetTy, FnSig, Generics, ItemKind, MetaItemInner, - PatKind, TyKind, + self as ast, AssocItemKind, BindingMode, ExprKind, FnRetTy, FnSig, Generics, ItemKind, + MetaItemInner, PatKind, QSelf, TyKind, }; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::{Ident, Span, Symbol, kw, sym}; @@ -45,6 +45,16 @@ mod llvm_enzyme { } } fn first_ident(x: &MetaItemInner) -> rustc_span::Ident { + if let Some(l) = x.lit() { + match l.kind { + ast::LitKind::Int(val, _) => { + // get an Ident from a lit + return rustc_span::Ident::from_str(val.get().to_string().as_str()); + } + _ => {} + } + } + let segments = &x.meta_item().unwrap().path.segments; assert!(segments.len() == 1); segments[0].ident @@ -54,6 +64,14 @@ mod llvm_enzyme { first_ident(x).name.to_string() } + fn width(x: &MetaItemInner) -> Option { + let lit = x.lit()?; + match lit.kind { + ast::LitKind::Int(x, _) => Some(x.get()), + _ => return None, + } + } + pub(crate) fn from_ast( ecx: &mut ExtCtxt<'_>, meta_item: &ThinVec, @@ -65,9 +83,32 @@ mod llvm_enzyme { dcx.emit_err(errors::AutoDiffInvalidMode { span: meta_item[1].span(), mode }); return AutoDiffAttrs::error(); }; + + // Now we check, whether the user wants autodiff in batch/vector mode, or scalar mode. + // If he doesn't specify an integer (=width), we default to scalar mode, thus width=1. + let mut first_activity = 2; + + let width = if let [_, _, x, ..] = &meta_item[..] + && let Some(x) = width(x) + { + first_activity = 3; + match x.try_into() { + Ok(x) => x, + Err(_) => { + dcx.emit_err(errors::AutoDiffInvalidWidth { + span: meta_item[2].span(), + width: x, + }); + return AutoDiffAttrs::error(); + } + } + } else { + 1 + }; + let mut activities: Vec = vec![]; let mut errors = false; - for x in &meta_item[2..] { + for x in &meta_item[first_activity..] { let activity_str = name(&x); let res = DiffActivity::from_str(&activity_str); match res { @@ -98,7 +139,20 @@ mod llvm_enzyme { (&DiffActivity::None, activities.as_slice()) }; - AutoDiffAttrs { mode, ret_activity: *ret_activity, input_activity: input_activity.to_vec() } + AutoDiffAttrs { + mode, + width, + ret_activity: *ret_activity, + input_activity: input_activity.to_vec(), + } + } + + fn meta_item_inner_to_ts(t: &MetaItemInner, ts: &mut Vec) { + let comma: Token = Token::new(TokenKind::Comma, Span::default()); + let val = first_ident(t); + let t = Token::from_ast_ident(val); + ts.push(TokenTree::Token(t, Spacing::Joint)); + ts.push(TokenTree::Token(comma.clone(), Spacing::Alone)); } /// We expand the autodiff macro to generate a new placeholder function which passes @@ -195,27 +249,49 @@ mod llvm_enzyme { // create TokenStream from vec elemtents: // meta_item doesn't have a .tokens field - let comma: Token = Token::new(TokenKind::Comma, Span::default()); let mut ts: Vec = vec![]; if meta_item_vec.len() < 2 { // At the bare minimum, we need a fnc name and a mode, even for a dummy function with no // input and output args. dcx.emit_err(errors::AutoDiffMissingConfig { span: item.span() }); return vec![item]; + } + + meta_item_inner_to_ts(&meta_item_vec[1], &mut ts); + + // Now, if the user gave a width (vector aka batch-mode ad), then we copy it. + // If it is not given, we default to 1 (scalar mode). + let start_position; + let kind: LitKind = LitKind::Integer; + let symbol; + if meta_item_vec.len() >= 3 + && let Some(width) = width(&meta_item_vec[2]) + { + start_position = 3; + symbol = Symbol::intern(&width.to_string()); } else { - for t in meta_item_vec.clone()[1..].iter() { - let val = first_ident(t); - let t = Token::from_ast_ident(val); - ts.push(TokenTree::Token(t, Spacing::Joint)); - ts.push(TokenTree::Token(comma.clone(), Spacing::Alone)); - } + start_position = 2; + symbol = sym::integer(1); } + let l: Lit = Lit { kind, symbol, suffix: None }; + let t = Token::new(TokenKind::Literal(l), Span::default()); + let comma = Token::new(TokenKind::Comma, Span::default()); + ts.push(TokenTree::Token(t, Spacing::Joint)); + ts.push(TokenTree::Token(comma.clone(), Spacing::Alone)); + + for t in meta_item_vec.clone()[start_position..].iter() { + meta_item_inner_to_ts(t, &mut ts); + } + if !has_ret { // We don't want users to provide a return activity if the function doesn't return anything. // For simplicity, we just add a dummy token to the end of the list. let t = Token::new(TokenKind::Ident(sym::None, false.into()), Span::default()); ts.push(TokenTree::Token(t, Spacing::Joint)); + ts.push(TokenTree::Token(comma, Spacing::Alone)); } + // We remove the last, trailing comma. + ts.pop(); let ts: TokenStream = TokenStream::from_iter(ts); let x: AutoDiffAttrs = from_ast(ecx, &meta_item_vec, has_ret); @@ -470,6 +546,8 @@ mod llvm_enzyme { return body; } + // Everything from here onwards just tries to fullfil the return type. Fun! + // having an active-only return means we'll drop the original return type. // So that can be treated identical to not having one in the first place. let primal_ret = has_ret(&sig.decl.output) && !x.has_active_only_ret(); @@ -497,86 +575,65 @@ mod llvm_enzyme { return body; } - let mut exprs = ThinVec::>::new(); - if primal_ret { - // We have both primal ret and active floats. - // primal ret is first, by construction. - exprs.push(primal_call); - } - - // Now construct default placeholder for each active float. - // Is there something nicer than f32::default() and f64::default()? + let mut exprs: P = primal_call.clone(); let d_ret_ty = match d_sig.decl.output { FnRetTy::Ty(ref ty) => ty.clone(), FnRetTy::Default(span) => { panic!("Did not expect Default ret ty: {:?}", span); } }; - let mut d_ret_ty = match d_ret_ty.kind.clone() { - TyKind::Tup(ref tys) => tys.clone(), - TyKind::Path(_, rustc_ast::Path { segments, .. }) => { - if let [segment] = &segments[..] - && segment.args.is_none() - { - let id = vec![segments[0].ident]; - let kind = TyKind::Path(None, ecx.path(span, id)); - let ty = P(rustc_ast::Ty { kind, id: ast::DUMMY_NODE_ID, span, tokens: None }); - thin_vec![ty] - } else { - panic!("Expected tuple or simple path return type"); - } - } - _ => { - // We messed up construction of d_sig - panic!("Did not expect non-tuple ret ty: {:?}", d_ret_ty); - } - }; - - if x.mode.is_fwd() && x.ret_activity == DiffActivity::Dual { - assert!(d_ret_ty.len() == 2); - // both should be identical, by construction - let arg = d_ret_ty[0].kind.is_simple_path().unwrap(); - let arg2 = d_ret_ty[1].kind.is_simple_path().unwrap(); - assert!(arg == arg2); - let sl: Vec = vec![arg, kw::Default]; - let tmp = ecx.def_site_path(&sl); - let default_call_expr = ecx.expr_path(ecx.path(span, tmp)); - let default_call_expr = ecx.expr_call(new_decl_span, default_call_expr, thin_vec![]); - exprs.push(default_call_expr); - } else if x.mode.is_rev() { - if primal_ret { - // We have extra handling above for the primal ret - d_ret_ty = d_ret_ty[1..].to_vec().into(); - } - for arg in d_ret_ty.iter() { - let arg = arg.kind.is_simple_path().unwrap(); - let sl: Vec = vec![arg, kw::Default]; - let tmp = ecx.def_site_path(&sl); - let default_call_expr = ecx.expr_path(ecx.path(span, tmp)); + if x.mode.is_fwd() { + // Fwd mode is easy. If the return activity is Const, we support arbitrary types. + // Otherwise, we only support a scalar, a pair of scalars, or an array of scalars. + // We checked that (on a best-effort base) in the preceding gen_enzyme_decl function. + // In all three cases, we can return `std::hint::black_box(::default())`. + if x.ret_activity == DiffActivity::Const { + // Here we call the primal function, since our dummy function has the same return + // type due to the Const return activity. + exprs = ecx.expr_call(new_decl_span, bb_call_expr, thin_vec![exprs]); + } else { + let q = QSelf { ty: d_ret_ty.clone(), path_span: span, position: 0 }; + let y = + ExprKind::Path(Some(P(q)), ecx.path_ident(span, Ident::from_str("default"))); + let default_call_expr = ecx.expr(span, y); let default_call_expr = ecx.expr_call(new_decl_span, default_call_expr, thin_vec![]); - exprs.push(default_call_expr); - } - } - - let ret: P; - match &exprs[..] { - [] => { - assert!(!has_ret(&d_sig.decl.output)); - // We don't have to match the return type. - return body; - } - [arg] => { - ret = ecx.expr_call(new_decl_span, bb_call_expr, thin_vec![arg.clone()]); + exprs = ecx.expr_call(new_decl_span, bb_call_expr, thin_vec![default_call_expr]); } - args => { - let ret_tuple: P = ecx.expr_tuple(span, args.into()); - ret = ecx.expr_call(new_decl_span, bb_call_expr, thin_vec![ret_tuple]); + } else if x.mode.is_rev() { + if x.width == 1 { + // We either have `-> ArbitraryType` or `-> (ArbitraryType, repeated_float_scalars)`. + match d_ret_ty.kind { + TyKind::Tup(ref args) => { + // We have a tuple return type. We need to create a tuple of the same size + // and fill it with default values. + let mut exprs2 = thin_vec![exprs]; + for arg in args.iter().skip(1) { + let arg = arg.kind.is_simple_path().unwrap(); + let sl: Vec = vec![arg, kw::Default]; + let tmp = ecx.def_site_path(&sl); + let default_call_expr = ecx.expr_path(ecx.path(span, tmp)); + let default_call_expr = + ecx.expr_call(new_decl_span, default_call_expr, thin_vec![]); + exprs2.push(default_call_expr); + } + exprs = ecx.expr_tuple(new_decl_span, exprs2); + } + _ => { + // Interestingly, even the `-> ArbitraryType` case + // ends up getting matched and handled correctly above, + // so we don't have to handle any other case for now. + panic!("Unsupported return type: {:?}", d_ret_ty); + } + } } + exprs = ecx.expr_call(new_decl_span, bb_call_expr, thin_vec![exprs]); + } else { + unreachable!("Unsupported mode: {:?}", x.mode); } - assert!(has_ret(&d_sig.decl.output)); - body.stmts.push(ecx.stmt_expr(ret)); + + body.stmts.push(ecx.stmt_expr(exprs)); body } @@ -684,50 +741,55 @@ mod llvm_enzyme { match activity { DiffActivity::Active => { act_ret.push(arg.ty.clone()); + // if width =/= 1, then push [arg.ty; width] to act_ret } DiffActivity::ActiveOnly => { // We will add the active scalar to the return type. // This is handled later. } DiffActivity::Duplicated | DiffActivity::DuplicatedOnly => { - let mut shadow_arg = arg.clone(); - // We += into the shadow in reverse mode. - shadow_arg.ty = P(assure_mut_ref(&arg.ty)); - let old_name = if let PatKind::Ident(_, ident, _) = arg.pat.kind { - ident.name - } else { - debug!("{:#?}", &shadow_arg.pat); - panic!("not an ident?"); - }; - let name: String = format!("d{}", old_name); - new_inputs.push(name.clone()); - let ident = Ident::from_str_and_span(&name, shadow_arg.pat.span); - shadow_arg.pat = P(ast::Pat { - id: ast::DUMMY_NODE_ID, - kind: PatKind::Ident(BindingMode::NONE, ident, None), - span: shadow_arg.pat.span, - tokens: shadow_arg.pat.tokens.clone(), - }); - d_inputs.push(shadow_arg); + for i in 0..x.width { + let mut shadow_arg = arg.clone(); + // We += into the shadow in reverse mode. + shadow_arg.ty = P(assure_mut_ref(&arg.ty)); + let old_name = if let PatKind::Ident(_, ident, _) = arg.pat.kind { + ident.name + } else { + debug!("{:#?}", &shadow_arg.pat); + panic!("not an ident?"); + }; + let name: String = format!("d{}_{}", old_name, i); + new_inputs.push(name.clone()); + let ident = Ident::from_str_and_span(&name, shadow_arg.pat.span); + shadow_arg.pat = P(ast::Pat { + id: ast::DUMMY_NODE_ID, + kind: PatKind::Ident(BindingMode::NONE, ident, None), + span: shadow_arg.pat.span, + tokens: shadow_arg.pat.tokens.clone(), + }); + d_inputs.push(shadow_arg.clone()); + } } DiffActivity::Dual | DiffActivity::DualOnly => { - let mut shadow_arg = arg.clone(); - let old_name = if let PatKind::Ident(_, ident, _) = arg.pat.kind { - ident.name - } else { - debug!("{:#?}", &shadow_arg.pat); - panic!("not an ident?"); - }; - let name: String = format!("b{}", old_name); - new_inputs.push(name.clone()); - let ident = Ident::from_str_and_span(&name, shadow_arg.pat.span); - shadow_arg.pat = P(ast::Pat { - id: ast::DUMMY_NODE_ID, - kind: PatKind::Ident(BindingMode::NONE, ident, None), - span: shadow_arg.pat.span, - tokens: shadow_arg.pat.tokens.clone(), - }); - d_inputs.push(shadow_arg); + for i in 0..x.width { + let mut shadow_arg = arg.clone(); + let old_name = if let PatKind::Ident(_, ident, _) = arg.pat.kind { + ident.name + } else { + debug!("{:#?}", &shadow_arg.pat); + panic!("not an ident?"); + }; + let name: String = format!("b{}_{}", old_name, i); + new_inputs.push(name.clone()); + let ident = Ident::from_str_and_span(&name, shadow_arg.pat.span); + shadow_arg.pat = P(ast::Pat { + id: ast::DUMMY_NODE_ID, + kind: PatKind::Ident(BindingMode::NONE, ident, None), + span: shadow_arg.pat.span, + tokens: shadow_arg.pat.tokens.clone(), + }); + d_inputs.push(shadow_arg.clone()); + } } DiffActivity::Const => { // Nothing to do here. @@ -783,23 +845,48 @@ mod llvm_enzyme { d_decl.inputs = d_inputs.into(); if x.mode.is_fwd() { + let ty = match d_decl.output { + FnRetTy::Ty(ref ty) => ty.clone(), + FnRetTy::Default(span) => { + // We want to return std::hint::black_box(()). + let kind = TyKind::Tup(ThinVec::new()); + let ty = P(rustc_ast::Ty { kind, id: ast::DUMMY_NODE_ID, span, tokens: None }); + d_decl.output = FnRetTy::Ty(ty.clone()); + assert!(matches!(x.ret_activity, DiffActivity::None)); + // this won't be used below, so any type would be fine. + ty + } + }; + if let DiffActivity::Dual = x.ret_activity { - let ty = match d_decl.output { - FnRetTy::Ty(ref ty) => ty.clone(), - FnRetTy::Default(span) => { - panic!("Did not expect Default ret ty: {:?}", span); - } + let kind = if x.width == 1 { + // 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()]) + } else { + // We have to return [T; width+1], +1 for the primal return. + let anon_const = rustc_ast::AnonConst { + id: ast::DUMMY_NODE_ID, + value: ecx.expr_usize(span, 1 + x.width as usize), + }; + TyKind::Array(ty.clone(), anon_const) }; - // Dual can only be used for f32/f64 ret. - // In that case we return now a tuple with two floats. - let kind = TyKind::Tup(thin_vec![ty.clone(), ty.clone()]); 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 { // No need to change the return type, - // we will just return the shadow in place - // of the primal return. + // 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] + if x.width > 1 { + let anon_const = rustc_ast::AnonConst { + id: ast::DUMMY_NODE_ID, + value: ecx.expr_usize(span, x.width as usize), + }; + let kind = TyKind::Array(ty.clone(), anon_const); + let ty = P(rustc_ast::Ty { kind, id: ty.id, span: ty.span, tokens: None }); + d_decl.output = FnRetTy::Ty(ty); + } } } diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index 30597944124cb..4bbe212f4296c 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -202,6 +202,14 @@ mod autodiff { pub(crate) mode: String, } + #[derive(Diagnostic)] + #[diag(builtin_macros_autodiff_width)] + pub(crate) struct AutoDiffInvalidWidth { + #[primary_span] + pub(crate) span: Span, + pub(crate) width: u128, + } + #[derive(Diagnostic)] #[diag(builtin_macros_autodiff)] pub(crate) struct AutoDiffInvalidApplication { diff --git a/compiler/rustc_codegen_cranelift/build_system/tests.rs b/compiler/rustc_codegen_cranelift/build_system/tests.rs index 122b541fa35f8..eec89c026b26a 100644 --- a/compiler/rustc_codegen_cranelift/build_system/tests.rs +++ b/compiler/rustc_codegen_cranelift/build_system/tests.rs @@ -99,6 +99,34 @@ const BASE_SYSROOT_SUITE: &[TestCase] = &[ runner.run_out_command("gen_block_iterate", &[]); }), TestCase::build_bin_and_run("aot.raw-dylib", "example/raw-dylib.rs", &[]), + TestCase::custom("test.sysroot", &|runner| { + apply_patches( + &runner.dirs, + "sysroot_tests", + &runner.stdlib_source.join("library"), + &SYSROOT_TESTS_SRC.to_path(&runner.dirs), + ); + + SYSROOT_TESTS.clean(&runner.dirs); + + let mut target_compiler = runner.target_compiler.clone(); + // coretests and alloctests produce a bunch of warnings. When running + // in rust's CI warnings are denied, so we have to override that here. + target_compiler.rustflags.push("--cap-lints=allow".to_owned()); + // The standard library may have been compiled with -Zrandomize-layout. + target_compiler.rustflags.extend(["--cfg".to_owned(), "randomized_layouts".to_owned()]); + + if runner.is_native { + let mut test_cmd = SYSROOT_TESTS.test(&target_compiler, &runner.dirs); + test_cmd.args(["-p", "coretests", "-p", "alloctests", "--tests", "--", "-q"]); + spawn_and_wait(test_cmd); + } else { + eprintln!("Cross-Compiling: Not running tests"); + let mut build_cmd = SYSROOT_TESTS.build(&target_compiler, &runner.dirs); + build_cmd.args(["-p", "coretests", "-p", "alloctests", "--tests"]); + spawn_and_wait(build_cmd); + } + }), ]; pub(crate) static RAND_REPO: GitRepo = GitRepo::github( @@ -146,27 +174,6 @@ const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[ spawn_and_wait(build_cmd); } }), - TestCase::custom("test.sysroot", &|runner| { - apply_patches( - &runner.dirs, - "sysroot_tests", - &runner.stdlib_source.join("library"), - &SYSROOT_TESTS_SRC.to_path(&runner.dirs), - ); - - SYSROOT_TESTS.clean(&runner.dirs); - - if runner.is_native { - let mut test_cmd = SYSROOT_TESTS.test(&runner.target_compiler, &runner.dirs); - test_cmd.args(["-p", "coretests", "-p", "alloctests", "--", "-q"]); - spawn_and_wait(test_cmd); - } else { - eprintln!("Cross-Compiling: Not running tests"); - let mut build_cmd = SYSROOT_TESTS.build(&runner.target_compiler, &runner.dirs); - build_cmd.args(["-p", "coretests", "-p", "alloctests", "--tests"]); - spawn_and_wait(build_cmd); - } - }), TestCase::custom("test.regex", &|runner| { REGEX_REPO.patch(&runner.dirs); diff --git a/compiler/rustc_codegen_cranelift/build_system/utils.rs b/compiler/rustc_codegen_cranelift/build_system/utils.rs index c2114caf8692e..f239976845964 100644 --- a/compiler/rustc_codegen_cranelift/build_system/utils.rs +++ b/compiler/rustc_codegen_cranelift/build_system/utils.rs @@ -105,7 +105,11 @@ impl CargoProject { .arg(self.manifest_path(dirs)) .arg("--target-dir") .arg(self.target_dir(dirs)) - .arg("--locked"); + .arg("--locked") + // bootstrap sets both RUSTC and RUSTC_WRAPPER to the same wrapper. RUSTC is already + // respected by the rustc-clif wrapper, but RUSTC_WRAPPER will misinterpret rustc-clif + // as filename, so we need to unset it. + .env_remove("RUSTC_WRAPPER"); if dirs.frozen { cmd.arg("--frozen"); diff --git a/compiler/rustc_codegen_cranelift/config.txt b/compiler/rustc_codegen_cranelift/config.txt index 714414fe8d685..6ae4767adfdf5 100644 --- a/compiler/rustc_codegen_cranelift/config.txt +++ b/compiler/rustc_codegen_cranelift/config.txt @@ -32,9 +32,9 @@ aot.issue-59326 aot.neon aot.gen_block_iterate aot.raw-dylib +test.sysroot testsuite.extended_sysroot test.rust-random/rand -test.sysroot test.regex test.portable-simd diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index f083cfbd7d306..a8b49e9552c30 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -610,6 +610,8 @@ fn enable_autodiff_settings(ad: &[config::AutoDiff], module: &mut ModuleCodegen< } // We handle this below config::AutoDiff::PrintModAfter => {} + // We handle this below + config::AutoDiff::PrintModFinal => {} // This is required and already checked config::AutoDiff::Enable => {} } @@ -657,14 +659,20 @@ pub(crate) fn run_pass_manager( } if cfg!(llvm_enzyme) && enable_ad { + // This is the post-autodiff IR, mainly used for testing and educational purposes. + if config.autodiff.contains(&config::AutoDiff::PrintModAfter) { + unsafe { llvm::LLVMDumpModule(module.module_llvm.llmod()) }; + } + let opt_stage = llvm::OptStage::FatLTO; let stage = write::AutodiffStage::PostAD; unsafe { write::llvm_optimize(cgcx, dcx, module, None, config, opt_level, opt_stage, stage)?; } - // This is the final IR, so people should be able to inspect the optimized autodiff output. - if config.autodiff.contains(&config::AutoDiff::PrintModAfter) { + // This is the final IR, so people should be able to inspect the optimized autodiff output, + // for manual inspection. + if config.autodiff.contains(&config::AutoDiff::PrintModFinal) { unsafe { llvm::LLVMDumpModule(module.module_llvm.llmod()) }; } } diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs index 7cd4ee539d875..c6092fba39671 100644 --- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs +++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs @@ -3,8 +3,10 @@ use std::ptr; use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, AutoDiffItem, DiffActivity, DiffMode}; use rustc_codegen_ssa::ModuleCodegen; use rustc_codegen_ssa::back::write::ModuleConfig; -use rustc_codegen_ssa::traits::BaseTypeCodegenMethods as _; +use rustc_codegen_ssa::common::TypeKind; +use rustc_codegen_ssa::traits::BaseTypeCodegenMethods; use rustc_errors::FatalError; +use rustc_middle::bug; use tracing::{debug, trace}; use crate::back::write::llvm_err; @@ -18,21 +20,42 @@ use crate::value::Value; use crate::{CodegenContext, LlvmCodegenBackend, ModuleLlvm, attributes, llvm}; fn get_params(fnc: &Value) -> Vec<&Value> { + let param_num = llvm::LLVMCountParams(fnc) as usize; + let mut fnc_args: Vec<&Value> = vec![]; + fnc_args.reserve(param_num); unsafe { - let param_num = llvm::LLVMCountParams(fnc) as usize; - let mut fnc_args: Vec<&Value> = vec![]; - fnc_args.reserve(param_num); llvm::LLVMGetParams(fnc, fnc_args.as_mut_ptr()); fnc_args.set_len(param_num); - fnc_args } + fnc_args } +fn has_sret(fnc: &Value) -> bool { + let num_args = llvm::LLVMCountParams(fnc) as usize; + if num_args == 0 { + false + } else { + unsafe { llvm::LLVMRustHasAttributeAtIndex(fnc, 0, llvm::AttributeKind::StructRet) } + } +} + +// When we call the `__enzyme_autodiff` or `__enzyme_fwddiff` function, we need to pass all the +// original inputs, as well as metadata and the additional shadow arguments. +// This function matches the arguments from the outer function to the inner enzyme call. +// +// This function also considers that Rust level arguments not always match the llvm-ir level +// arguments. A slice, `&[f32]`, for example, is represented as a pointer and a length on +// llvm-ir level. The number of activities matches the number of Rust level arguments, so we +// need to match those. +// FIXME(ZuseZ4): This logic is a bit more complicated than it should be, can we simplify it +// using iterators and peek()? fn match_args_from_caller_to_enzyme<'ll>( cx: &SimpleCx<'ll>, + width: u32, args: &mut Vec<&'ll llvm::Value>, inputs: &[DiffActivity], outer_args: &[&'ll llvm::Value], + has_sret: bool, ) { debug!("matching autodiff arguments"); // We now handle the issue that Rust level arguments not always match the llvm-ir level @@ -44,6 +67,14 @@ fn match_args_from_caller_to_enzyme<'ll>( let mut outer_pos: usize = 0; let mut activity_pos = 0; + if has_sret { + // Then the first outer arg is the sret pointer. Enzyme doesn't know about sret, so the + // inner function will still return something. We increase our outer_pos by one, + // and once we're done with all other args we will take the return of the inner call and + // update the sret pointer with it + outer_pos = 1; + } + 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(); @@ -92,23 +123,20 @@ fn match_args_from_caller_to_enzyme<'ll>( // (..., metadata! enzyme_dup, ptr, ptr, int1, ...). // FIXME(ZuseZ4): We will upstream a safety check later which asserts that // int2 >= int1, which means the shadow vector is large enough to store the gradient. - assert!(unsafe { - llvm::LLVMRustGetTypeKind(next_outer_ty) == llvm::TypeKind::Integer - }); - let next_outer_arg2 = outer_args[outer_pos + 2]; - let next_outer_ty2 = cx.val_ty(next_outer_arg2); - assert!(unsafe { - llvm::LLVMRustGetTypeKind(next_outer_ty2) == llvm::TypeKind::Pointer - }); - let next_outer_arg3 = outer_args[outer_pos + 3]; - let next_outer_ty3 = cx.val_ty(next_outer_arg3); - assert!(unsafe { - llvm::LLVMRustGetTypeKind(next_outer_ty3) == llvm::TypeKind::Integer - }); - args.push(next_outer_arg2); + assert_eq!(cx.type_kind(next_outer_ty), TypeKind::Integer); + + for _ in 0..width { + let next_outer_arg2 = outer_args[outer_pos + 2]; + let next_outer_ty2 = cx.val_ty(next_outer_arg2); + assert_eq!(cx.type_kind(next_outer_ty2), TypeKind::Pointer); + let next_outer_arg3 = outer_args[outer_pos + 3]; + let next_outer_ty3 = cx.val_ty(next_outer_arg3); + assert_eq!(cx.type_kind(next_outer_ty3), TypeKind::Integer); + args.push(next_outer_arg2); + } args.push(cx.get_metadata_value(enzyme_const)); args.push(next_outer_arg); - outer_pos += 4; + outer_pos += 2 + 2 * width as usize; activity_pos += 2; } else { // A duplicated pointer will have the following two outer_fn arguments: @@ -116,15 +144,19 @@ fn match_args_from_caller_to_enzyme<'ll>( // (..., metadata! enzyme_dup, ptr, ptr, ...). if matches!(diff_activity, DiffActivity::Duplicated | DiffActivity::DuplicatedOnly) { - assert!( - unsafe { llvm::LLVMRustGetTypeKind(next_outer_ty) } - == llvm::TypeKind::Pointer - ); + assert_eq!(cx.type_kind(next_outer_ty), TypeKind::Pointer); } // In the case of Dual we don't have assumptions, e.g. f32 would be valid. args.push(next_outer_arg); outer_pos += 2; activity_pos += 1; + + // Now, if width > 1, we need to account for that + for _ in 1..width { + let next_outer_arg = outer_args[outer_pos]; + args.push(next_outer_arg); + outer_pos += 1; + } } } else { // We do not differentiate with resprect to this argument. @@ -135,6 +167,76 @@ fn match_args_from_caller_to_enzyme<'ll>( } } +// On LLVM-IR, we can luckily declare __enzyme_ functions without specifying the input +// arguments. We do however need to declare them with their correct return type. +// We already figured the correct return type out in our frontend, when generating the outer_fn, +// so we can now just go ahead and use that. This is not always trivial, e.g. because sret. +// Beyond sret, this article describes our challenges nicely: +// +// I.e. (i32, f32) will get merged into i64, but we don't handle that yet. +fn compute_enzyme_fn_ty<'ll>( + cx: &SimpleCx<'ll>, + attrs: &AutoDiffAttrs, + fn_to_diff: &'ll Value, + outer_fn: &'ll Value, +) -> &'ll llvm::Type { + let fn_ty = cx.get_type_of_global(outer_fn); + let mut ret_ty = cx.get_return_type(fn_ty); + + let has_sret = has_sret(outer_fn); + + if has_sret { + // Now we don't just forward the return type, so we have to figure it out based on the + // primal return type, in combination with the autodiff settings. + let fn_ty = cx.get_type_of_global(fn_to_diff); + let inner_ret_ty = cx.get_return_type(fn_ty); + + let void_ty = unsafe { llvm::LLVMVoidTypeInContext(cx.llcx) }; + if inner_ret_ty == void_ty { + // This indicates that even the inner function has an sret. + // Right now I only look for an sret in the outer function. + // This *probably* needs some extra handling, but I never ran + // into such a case. So I'll wait for user reports to have a test case. + bug!("sret in inner function"); + } + + if attrs.width == 1 { + todo!("Handle sret for scalar ad"); + } else { + // First we check if we also have to deal with the primal return. + match attrs.mode { + DiffMode::Forward => match attrs.ret_activity { + DiffActivity::Dual => { + let arr_ty = + unsafe { llvm::LLVMArrayType2(inner_ret_ty, attrs.width as u64 + 1) }; + ret_ty = arr_ty; + } + DiffActivity::DualOnly => { + let arr_ty = + unsafe { llvm::LLVMArrayType2(inner_ret_ty, attrs.width as u64) }; + ret_ty = arr_ty; + } + DiffActivity::Const => { + todo!("Not sure, do we need to do something here?"); + } + _ => { + bug!("unreachable"); + } + }, + DiffMode::Reverse => { + todo!("Handle sret for reverse mode"); + } + _ => { + bug!("unreachable"); + } + } + } + } + + // LLVM can figure out the input types on it's own, so we take a shortcut here. + unsafe { llvm::LLVMFunctionType(ret_ty, ptr::null(), 0, True) } +} + /// When differentiating `fn_to_diff`, take a `outer_fn` and generate another /// function with expected naming and calling conventions[^1] which will be /// discovered by the enzyme LLVM pass and its body populated with the differentiated @@ -197,17 +299,9 @@ fn generate_enzyme_call<'ll>( // } // ``` unsafe { - // On LLVM-IR, we can luckily declare __enzyme_ functions without specifying the input - // arguments. We do however need to declare them with their correct return type. - // We already figured the correct return type out in our frontend, when generating the outer_fn, - // so we can now just go ahead and use that. FIXME(ZuseZ4): This doesn't handle sret yet. - let fn_ty = llvm::LLVMGlobalGetValueType(outer_fn); - let ret_ty = llvm::LLVMGetReturnType(fn_ty); - - // LLVM can figure out the input types on it's own, so we take a shortcut here. - let enzyme_ty = llvm::LLVMFunctionType(ret_ty, ptr::null(), 0, True); + let enzyme_ty = compute_enzyme_fn_ty(cx, &attrs, fn_to_diff, outer_fn); - //FIXME(ZuseZ4): the CC/Addr/Vis values are best effort guesses, we should look at tests and + // FIXME(ZuseZ4): the CC/Addr/Vis values are best effort guesses, we should look at tests and // think a bit more about what should go here. let cc = llvm::LLVMGetFunctionCallConv(outer_fn); let ad_fn = declare_simple_fn( @@ -240,14 +334,27 @@ fn generate_enzyme_call<'ll>( if matches!(attrs.ret_activity, DiffActivity::Dual | DiffActivity::Active) { args.push(cx.get_metadata_value(enzyme_primal_ret)); } + if attrs.width > 1 { + let enzyme_width = cx.create_metadata("enzyme_width".to_string()).unwrap(); + args.push(cx.get_metadata_value(enzyme_width)); + args.push(cx.get_const_i64(attrs.width as u64)); + } + let has_sret = has_sret(outer_fn); let outer_args: Vec<&llvm::Value> = get_params(outer_fn); - match_args_from_caller_to_enzyme(&cx, &mut args, &attrs.input_activity, &outer_args); + match_args_from_caller_to_enzyme( + &cx, + attrs.width, + &mut args, + &attrs.input_activity, + &outer_args, + has_sret, + ); let call = builder.call(enzyme_ty, ad_fn, &args, None); // This part is a bit iffy. LLVM requires that a call to an inlineable function has some - // metadata attachted to it, but we just created this code oota. Given that the + // metadata attached to it, but we just created this code oota. Given that the // differentiated function already has partly confusing metadata, and given that this // affects nothing but the auttodiff IR, we take a shortcut and just steal metadata from the // dummy code which we inserted at a higher level. @@ -268,7 +375,22 @@ fn generate_enzyme_call<'ll>( // Now that we copied the metadata, get rid of dummy code. llvm::LLVMRustEraseInstUntilInclusive(entry, last_inst); - if cx.val_ty(call) == cx.type_void() { + if cx.val_ty(call) == cx.type_void() || has_sret { + if has_sret { + // This is what we already have in our outer_fn (shortened): + // define void @_foo(ptr <..> sret([32 x i8]) initializes((0, 32)) %0, <...>) { + // %7 = call [4 x double] (...) @__enzyme_fwddiff_foo(ptr @square, metadata !"enzyme_width", i64 4, <...>) + // + // store [4 x double] %7, ptr %0, align 8 + // ret void + // } + + // now store the result of the enzyme call into the sret pointer. + let sret_ptr = outer_args[0]; + let call_ty = cx.val_ty(call); + assert_eq!(cx.type_kind(call_ty), TypeKind::Array); + llvm::LLVMBuildStore(&builder.llbuilder, call, sret_ptr); + } builder.ret_void(); } else { builder.ret(call); @@ -300,8 +422,7 @@ pub(crate) fn differentiate<'ll>( if !diff_items.is_empty() && !cgcx.opts.unstable_opts.autodiff.contains(&rustc_session::config::AutoDiff::Enable) { - let dcx = cgcx.create_dcx(); - return Err(dcx.handle().emit_almost_fatal(AutoDiffWithoutEnable)); + return Err(diag_handler.handle().emit_almost_fatal(AutoDiffWithoutEnable)); } // Before dumping the module, we want all the TypeTrees to become part of the module. diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 7675e75338a3e..bf81eb648f8ae 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -430,7 +430,7 @@ impl<'ll> CodegenCx<'ll, '_> { let val_llty = self.val_ty(v); let g = self.get_static_inner(def_id, val_llty); - let llty = llvm::LLVMGlobalGetValueType(g); + let llty = self.get_type_of_global(g); let g = if val_llty == llty { g diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index f7b096ff976a6..3be8cd5f6ac21 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -8,6 +8,7 @@ use std::str; use rustc_abi::{HasDataLayout, Size, TargetDataLayout, VariantIdx}; use rustc_codegen_ssa::back::versioned_llvm_target; use rustc_codegen_ssa::base::{wants_msvc_seh, wants_wasm_eh}; +use rustc_codegen_ssa::common::TypeKind; use rustc_codegen_ssa::errors as ssa_errors; use rustc_codegen_ssa::traits::*; use rustc_data_structures::base_n::{ALPHANUMERIC_ONLY, ToBaseN}; @@ -38,7 +39,7 @@ use crate::debuginfo::metadata::apply_vcall_visibility_metadata; use crate::llvm::Metadata; use crate::type_::Type; use crate::value::Value; -use crate::{attributes, coverageinfo, debuginfo, llvm, llvm_util}; +use crate::{attributes, common, coverageinfo, debuginfo, llvm, llvm_util}; /// `TyCtxt` (and related cache datastructures) can't be move between threads. /// However, there are various cx related functions which we want to be available to the builder and @@ -643,7 +644,18 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { llvm::set_section(g, c"llvm.metadata"); } } - +impl<'ll> SimpleCx<'ll> { + pub(crate) fn get_return_type(&self, ty: &'ll Type) -> &'ll Type { + assert_eq!(self.type_kind(ty), TypeKind::Function); + unsafe { llvm::LLVMGetReturnType(ty) } + } + pub(crate) fn get_type_of_global(&self, val: &'ll Value) -> &'ll Type { + unsafe { llvm::LLVMGlobalGetValueType(val) } + } + pub(crate) fn val_ty(&self, v: &'ll Value) -> &'ll Type { + common::val_ty(v) + } +} impl<'ll> SimpleCx<'ll> { pub(crate) fn new( llmod: &'ll llvm::Module, @@ -660,6 +672,13 @@ impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { llvm::LLVMMetadataAsValue(self.llcx(), metadata) } + // FIXME(autodiff): We should split `ConstCodegenMethods` to pull the reusable parts + // onto a trait that is also implemented for GenericCx. + pub(crate) fn get_const_i64(&self, n: u64) -> &'ll Value { + let ty = unsafe { llvm::LLVMInt64TypeInContext(self.llcx()) }; + unsafe { llvm::LLVMConstInt(ty, n, llvm::False) } + } + pub(crate) fn get_function(&self, name: &str) -> Option<&'ll Value> { let name = SmallCStr::new(name); unsafe { llvm::LLVMGetNamedFunction((**self).borrow().llmod, name.as_ptr()) } diff --git a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs index 79e4cc8aa7744..a9b3bdf7344be 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs @@ -4,7 +4,7 @@ use libc::{c_char, c_uint}; use super::MetadataKindId; -use super::ffi::{BasicBlock, Metadata, Module, Type, Value}; +use super::ffi::{AttributeKind, BasicBlock, Metadata, Module, Type, Value}; use crate::llvm::Bool; #[link(name = "llvm-wrapper", kind = "static")] @@ -17,6 +17,8 @@ unsafe extern "C" { pub(crate) fn LLVMRustEraseInstFromParent(V: &Value); pub(crate) fn LLVMRustGetTerminator<'a>(B: &BasicBlock) -> &'a Value; pub(crate) fn LLVMRustVerifyFunction(V: &Value, action: LLVMRustVerifierFailureAction) -> Bool; + pub(crate) fn LLVMRustHasAttributeAtIndex(V: &Value, i: c_uint, Kind: AttributeKind) -> bool; + pub(crate) fn LLVMRustGetArrayNumElements(Ty: &Type) -> u64; } unsafe extern "C" { diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 3ce3761944b3a..9ff04f729030c 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1180,7 +1180,7 @@ unsafe extern "C" { // Operations on parameters pub(crate) fn LLVMIsAArgument(Val: &Value) -> Option<&Value>; - pub(crate) fn LLVMCountParams(Fn: &Value) -> c_uint; + pub(crate) safe fn LLVMCountParams(Fn: &Value) -> c_uint; pub(crate) fn LLVMGetParam(Fn: &Value, Index: c_uint) -> &Value; // Operations on basic blocks diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 954a601480935..1dabf01ffd654 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -4,12 +4,6 @@ codegen_ssa_add_native_library = failed to add native library {$library_path}: { codegen_ssa_aix_strip_not_used = using host's `strip` binary to cross-compile to AIX which is not guaranteed to work -codegen_ssa_apple_deployment_target_invalid = - failed to parse deployment target specified in {$env_var}: {$error} - -codegen_ssa_apple_deployment_target_too_low = - deployment target in {$env_var} was set to {$version}, but the minimum supported by `rustc` is {$os_min} - codegen_ssa_archive_build_failure = failed to build archive at `{$path}`: {$error} codegen_ssa_atomic_compare_exchange = Atomic compare-exchange intrinsic missing failure memory ordering diff --git a/compiler/rustc_codegen_ssa/src/back/apple.rs b/compiler/rustc_codegen_ssa/src/back/apple.rs index 2c8b0ec418dd8..d242efaf4fd42 100644 --- a/compiler/rustc_codegen_ssa/src/back/apple.rs +++ b/compiler/rustc_codegen_ssa/src/back/apple.rs @@ -1,7 +1,4 @@ -use std::env; use std::ffi::OsString; -use std::fmt::{Display, from_fn}; -use std::num::ParseIntError; use std::path::PathBuf; use std::process::Command; @@ -9,9 +6,10 @@ use itertools::Itertools; use rustc_middle::middle::exported_symbols::SymbolExportKind; use rustc_session::Session; use rustc_target::spec::Target; +pub(super) use rustc_target::spec::apple::OSVersion; use tracing::debug; -use crate::errors::{AppleDeploymentTarget, XcrunError, XcrunSdkPathWarning}; +use crate::errors::{XcrunError, XcrunSdkPathWarning}; use crate::fluent_generated as fluent; #[cfg(test)] @@ -134,124 +132,6 @@ pub(super) fn add_data_and_relocation( Ok(()) } -/// Deployment target or SDK version. -/// -/// The size of the numbers in here are limited by Mach-O's `LC_BUILD_VERSION`. -type OSVersion = (u16, u8, u8); - -/// Parse an OS version triple (SDK version or deployment target). -fn parse_version(version: &str) -> Result { - if let Some((major, minor)) = version.split_once('.') { - let major = major.parse()?; - if let Some((minor, patch)) = minor.split_once('.') { - Ok((major, minor.parse()?, patch.parse()?)) - } else { - Ok((major, minor.parse()?, 0)) - } - } else { - Ok((version.parse()?, 0, 0)) - } -} - -pub fn pretty_version(version: OSVersion) -> impl Display { - let (major, minor, patch) = version; - from_fn(move |f| { - write!(f, "{major}.{minor}")?; - if patch != 0 { - write!(f, ".{patch}")?; - } - Ok(()) - }) -} - -/// Minimum operating system versions currently supported by `rustc`. -fn os_minimum_deployment_target(os: &str) -> OSVersion { - // When bumping a version in here, remember to update the platform-support docs too. - // - // NOTE: The defaults may change in future `rustc` versions, so if you are looking for the - // default deployment target, prefer: - // ``` - // $ rustc --print deployment-target - // ``` - match os { - "macos" => (10, 12, 0), - "ios" => (10, 0, 0), - "tvos" => (10, 0, 0), - "watchos" => (5, 0, 0), - "visionos" => (1, 0, 0), - _ => unreachable!("tried to get deployment target for non-Apple platform"), - } -} - -/// The deployment target for the given target. -/// -/// This is similar to `os_minimum_deployment_target`, except that on certain targets it makes sense -/// to raise the minimum OS version. -/// -/// This matches what LLVM does, see in part: -/// -fn minimum_deployment_target(target: &Target) -> OSVersion { - match (&*target.os, &*target.arch, &*target.abi) { - ("macos", "aarch64", _) => (11, 0, 0), - ("ios", "aarch64", "macabi") => (14, 0, 0), - ("ios", "aarch64", "sim") => (14, 0, 0), - ("ios", _, _) if target.llvm_target.starts_with("arm64e") => (14, 0, 0), - // Mac Catalyst defaults to 13.1 in Clang. - ("ios", _, "macabi") => (13, 1, 0), - ("tvos", "aarch64", "sim") => (14, 0, 0), - ("watchos", "aarch64", "sim") => (7, 0, 0), - (os, _, _) => os_minimum_deployment_target(os), - } -} - -/// Name of the environment variable used to fetch the deployment target on the given OS. -pub fn deployment_target_env_var(os: &str) -> &'static str { - match os { - "macos" => "MACOSX_DEPLOYMENT_TARGET", - "ios" => "IPHONEOS_DEPLOYMENT_TARGET", - "watchos" => "WATCHOS_DEPLOYMENT_TARGET", - "tvos" => "TVOS_DEPLOYMENT_TARGET", - "visionos" => "XROS_DEPLOYMENT_TARGET", - _ => unreachable!("tried to get deployment target env var for non-Apple platform"), - } -} - -/// Get the deployment target based on the standard environment variables, or fall back to the -/// minimum version supported by `rustc`. -pub fn deployment_target(sess: &Session) -> OSVersion { - let min = minimum_deployment_target(&sess.target); - let env_var = deployment_target_env_var(&sess.target.os); - - if let Ok(deployment_target) = env::var(env_var) { - match parse_version(&deployment_target) { - Ok(version) => { - let os_min = os_minimum_deployment_target(&sess.target.os); - // It is common that the deployment target is set a bit too low, for example on - // macOS Aarch64 to also target older x86_64. So we only want to warn when variable - // is lower than the minimum OS supported by rustc, not when the variable is lower - // than the minimum for a specific target. - if version < os_min { - sess.dcx().emit_warn(AppleDeploymentTarget::TooLow { - env_var, - version: pretty_version(version).to_string(), - os_min: pretty_version(os_min).to_string(), - }); - } - - // Raise the deployment target to the minimum supported. - version.max(min) - } - Err(error) => { - sess.dcx().emit_err(AppleDeploymentTarget::Invalid { env_var, error }); - min - } - } - } else { - // If no deployment target variable is set, default to the minimum found above. - min - } -} - pub(super) fn add_version_to_llvm_target( llvm_target: &str, deployment_target: OSVersion, @@ -263,18 +143,17 @@ pub(super) fn add_version_to_llvm_target( let environment = components.next(); assert_eq!(components.next(), None, "too many LLVM triple components"); - let (major, minor, patch) = deployment_target; - assert!( !os.contains(|c: char| c.is_ascii_digit()), "LLVM target must not already be versioned" ); + let version = deployment_target.fmt_full(); if let Some(env) = environment { // Insert version into OS, before environment - format!("{arch}-{vendor}-{os}{major}.{minor}.{patch}-{env}") + format!("{arch}-{vendor}-{os}{version}-{env}") } else { - format!("{arch}-{vendor}-{os}{major}.{minor}.{patch}") + format!("{arch}-{vendor}-{os}{version}") } } diff --git a/compiler/rustc_codegen_ssa/src/back/apple/tests.rs b/compiler/rustc_codegen_ssa/src/back/apple/tests.rs index 8df740a4bcf7d..5afe79b71954a 100644 --- a/compiler/rustc_codegen_ssa/src/back/apple/tests.rs +++ b/compiler/rustc_codegen_ssa/src/back/apple/tests.rs @@ -3,23 +3,15 @@ use super::*; #[test] fn test_add_version_to_llvm_target() { assert_eq!( - add_version_to_llvm_target("aarch64-apple-macosx", (10, 14, 1)), + add_version_to_llvm_target("aarch64-apple-macosx", OSVersion::new(10, 14, 1)), "aarch64-apple-macosx10.14.1" ); assert_eq!( - add_version_to_llvm_target("aarch64-apple-ios-simulator", (16, 1, 0)), + add_version_to_llvm_target("aarch64-apple-ios-simulator", OSVersion::new(16, 1, 0)), "aarch64-apple-ios16.1.0-simulator" ); } -#[test] -fn test_parse_version() { - assert_eq!(parse_version("10"), Ok((10, 0, 0))); - assert_eq!(parse_version("10.12"), Ok((10, 12, 0))); - assert_eq!(parse_version("10.12.6"), Ok((10, 12, 6))); - assert_eq!(parse_version("9999.99.99"), Ok((9999, 99, 99))); -} - #[test] #[cfg_attr(not(target_os = "macos"), ignore = "xcode-select is only available on macOS")] fn lookup_developer_dir() { diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 2e91a1d921dd8..f0f62118ab705 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -3115,8 +3115,7 @@ fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavo _ => bug!("invalid OS/ABI combination for Apple target: {target_os}, {target_abi}"), }; - let (major, minor, patch) = apple::deployment_target(sess); - let min_version = format!("{major}.{minor}.{patch}"); + let min_version = sess.apple_deployment_target().fmt_full().to_string(); // The SDK version is used at runtime when compiling with a newer SDK / version of Xcode: // - By dyld to give extra warnings and errors, see e.g.: @@ -3185,10 +3184,10 @@ fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavo // The presence of `-mmacosx-version-min` makes CC default to // macOS, and it sets the deployment target. - let (major, minor, patch) = apple::deployment_target(sess); + let version = sess.apple_deployment_target().fmt_full(); // Intentionally pass this as a single argument, Clang doesn't // seem to like it otherwise. - cmd.cc_arg(&format!("-mmacosx-version-min={major}.{minor}.{patch}")); + cmd.cc_arg(&format!("-mmacosx-version-min={version}")); // macOS has no environment, so with these two, we've told CC the // four desired parameters. diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index ac9ac9bbb31f6..f048734cb396e 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -388,13 +388,13 @@ pub(super) fn elf_e_flags(architecture: Architecture, sess: &Session) -> u32 { fn macho_object_build_version_for_target(sess: &Session) -> object::write::MachOBuildVersion { /// The `object` crate demands "X.Y.Z encoded in nibbles as xxxx.yy.zz" /// e.g. minOS 14.0 = 0x000E0000, or SDK 16.2 = 0x00100200 - fn pack_version((major, minor, patch): (u16, u8, u8)) -> u32 { + fn pack_version(apple::OSVersion { major, minor, patch }: apple::OSVersion) -> u32 { let (major, minor, patch) = (major as u32, minor as u32, patch as u32); (major << 16) | (minor << 8) | patch } let platform = apple::macho_platform(&sess.target); - let min_os = apple::deployment_target(sess); + let min_os = sess.apple_deployment_target(); let mut build_version = object::write::MachOBuildVersion::default(); build_version.platform = platform; diff --git a/compiler/rustc_codegen_ssa/src/back/mod.rs b/compiler/rustc_codegen_ssa/src/back/mod.rs index 64b5d4569ecce..76971096a5820 100644 --- a/compiler/rustc_codegen_ssa/src/back/mod.rs +++ b/compiler/rustc_codegen_ssa/src/back/mod.rs @@ -20,7 +20,7 @@ pub mod write; /// Certain optimizations also depend on the deployment target. pub fn versioned_llvm_target(sess: &Session) -> Cow<'_, str> { if sess.target.is_like_osx { - apple::add_version_to_llvm_target(&sess.target.llvm_target, apple::deployment_target(sess)) + apple::add_version_to_llvm_target(&sess.target.llvm_target, sess.apple_deployment_target()) .into() } else { // FIXME(madsmtm): Certain other targets also include a version, diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index a85d032f36eee..7f448d8aee9d3 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -2,7 +2,7 @@ use std::str::FromStr; use rustc_abi::ExternAbi; use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode}; -use rustc_ast::{MetaItem, MetaItemInner, attr}; +use rustc_ast::{LitKind, MetaItem, MetaItemInner, attr}; use rustc_attr_parsing::ReprAttr::ReprAlign; use rustc_attr_parsing::{AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr}; use rustc_data_structures::fx::FxHashMap; @@ -805,8 +805,8 @@ fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option { return Some(AutoDiffAttrs::source()); } - let [mode, input_activities @ .., ret_activity] = &list[..] else { - span_bug!(attr.span(), "rustc_autodiff attribute must contain mode and activities"); + let [mode, width_meta, input_activities @ .., ret_activity] = &list[..] else { + span_bug!(attr.span(), "rustc_autodiff attribute must contain mode, width and activities"); }; let mode = if let MetaItemInner::MetaItem(MetaItem { path: p1, .. }) = mode { p1.segments.first().unwrap().ident @@ -823,6 +823,30 @@ fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option { } }; + let width: u32 = match width_meta { + MetaItemInner::MetaItem(MetaItem { path: p1, .. }) => { + let w = p1.segments.first().unwrap().ident; + match w.as_str().parse() { + Ok(val) => val, + Err(_) => { + span_bug!(w.span, "rustc_autodiff width should fit u32"); + } + } + } + MetaItemInner::Lit(lit) => { + if let LitKind::Int(val, _) = lit.kind { + match val.get().try_into() { + Ok(val) => val, + Err(_) => { + span_bug!(lit.span, "rustc_autodiff width should fit u32"); + } + } + } else { + span_bug!(lit.span, "rustc_autodiff width should be an integer"); + } + } + }; + // First read the ret symbol from the attribute let ret_symbol = if let MetaItemInner::MetaItem(MetaItem { path: p1, .. }) = ret_activity { p1.segments.first().unwrap().ident @@ -860,7 +884,7 @@ fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option { } } - Some(AutoDiffAttrs { mode, ret_activity, input_activity: arg_activities }) + Some(AutoDiffAttrs { mode, width, ret_activity, input_activity: arg_activities }) } pub(crate) fn provide(providers: &mut Providers) { diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index f52d29baf9dc0..b33de8802d83a 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -3,7 +3,6 @@ use std::borrow::Cow; use std::ffi::OsString; use std::io::Error; -use std::num::ParseIntError; use std::path::{Path, PathBuf}; use std::process::ExitStatus; @@ -738,14 +737,6 @@ pub enum ExtractBundledLibsError<'a> { ExtractSection { rlib: &'a Path, error: Box }, } -#[derive(Diagnostic)] -pub(crate) enum AppleDeploymentTarget { - #[diag(codegen_ssa_apple_deployment_target_invalid)] - Invalid { env_var: &'static str, error: ParseIntError }, - #[diag(codegen_ssa_apple_deployment_target_too_low)] - TooLow { env_var: &'static str, version: String, os_min: String }, -} - #[derive(Diagnostic)] #[diag(codegen_ssa_read_file)] pub(crate) struct ReadFileError { diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index f36e42cc6b95f..67217927a8729 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -7,7 +7,6 @@ #![doc(rust_logo)] #![feature(assert_matches)] #![feature(box_patterns)] -#![feature(debug_closure_helpers)] #![feature(file_buffered)] #![feature(if_let_guard)] #![feature(let_chains)] diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index a64ffe5da8ee1..57c3822186182 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -34,7 +34,6 @@ use std::time::{Instant, SystemTime}; use std::{env, str}; use rustc_ast as ast; -use rustc_codegen_ssa::back::apple; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_codegen_ssa::{CodegenErrors, CodegenResults}; use rustc_data_structures::profiling::{ @@ -64,6 +63,7 @@ use rustc_session::lint::{Lint, LintId}; use rustc_session::output::{CRATE_TYPES, collect_crate_types, invalid_output_for_target}; use rustc_session::{EarlyDiagCtxt, Session, config, filesearch}; use rustc_span::FileName; +use rustc_span::def_id::LOCAL_CRATE; use rustc_target::json::ToJson; use rustc_target::spec::{Target, TargetTuple}; use time::OffsetDateTime; @@ -392,14 +392,10 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) } fn dump_feature_usage_metrics(tcxt: TyCtxt<'_>, metrics_dir: &Path) { - let output_filenames = tcxt.output_filenames(()); - let mut metrics_file_name = std::ffi::OsString::from("unstable_feature_usage_metrics-"); - let mut metrics_path = output_filenames.with_directory_and_extension(metrics_dir, "json"); - let metrics_file_stem = - metrics_path.file_name().expect("there should be a valid default output filename"); - metrics_file_name.push(metrics_file_stem); - metrics_path.pop(); - metrics_path.push(metrics_file_name); + let hash = tcxt.crate_hash(LOCAL_CRATE); + let crate_name = tcxt.crate_name(LOCAL_CRATE); + let metrics_file_name = format!("unstable_feature_usage_metrics-{crate_name}-{hash}.json"); + let metrics_path = metrics_dir.join(metrics_file_name); if let Err(error) = tcxt.features().dump_feature_usage_metrics(metrics_path) { // FIXME(yaahc): once metrics can be enabled by default we will want "failure to emit // default metrics" to only produce a warning when metrics are enabled by default and emit @@ -810,8 +806,8 @@ fn print_crate_info( if sess.target.is_like_osx { println_info!( "{}={}", - apple::deployment_target_env_var(&sess.target.os), - apple::pretty_version(apple::deployment_target(sess)), + rustc_target::spec::apple::deployment_target_env_var(&sess.target.os), + sess.apple_deployment_target().fmt_pretty(), ) } else { #[allow(rustc::diagnostic_outside_of_impl)] diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 53df59930f4fd..32e6da446d72e 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -384,6 +384,12 @@ static inline void AddAttributes(T *t, unsigned Index, LLVMAttributeRef *Attrs, t->setAttributes(PALNew); } +extern "C" bool LLVMRustHasAttributeAtIndex(LLVMValueRef Fn, unsigned Index, + LLVMRustAttributeKind RustAttr) { + Function *F = unwrap(Fn); + return F->hasParamAttribute(Index, fromRust(RustAttr)); +} + extern "C" void LLVMRustAddFunctionAttributes(LLVMValueRef Fn, unsigned Index, LLVMAttributeRef *Attrs, size_t AttrsLen) { @@ -636,6 +642,10 @@ static InlineAsm::AsmDialect fromRust(LLVMRustAsmDialect Dialect) { } } +extern "C" uint64_t LLVMRustGetArrayNumElements(LLVMTypeRef Ty) { + return unwrap(Ty)->getArrayNumElements(); +} + extern "C" LLVMValueRef LLVMRustInlineAsm(LLVMTypeRef Ty, char *AsmString, size_t AsmStringLen, char *Constraints, size_t ConstraintsLen, diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 13217ae58e900..037f3cd43b65c 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1804,10 +1804,15 @@ impl<'tcx> TyCtxt<'tcx> { // - needs_metadata: for putting into crate metadata. // - instrument_coverage: for putting into coverage data (see // `hash_mir_source`). + // - metrics_dir: metrics use the strict version hash in the filenames + // for dumped metrics files to prevent overwriting distinct metrics + // for similar source builds (may change in the future, this is part + // of the proof of concept impl for the metrics initiative project goal) cfg!(debug_assertions) || self.sess.opts.incremental.is_some() || self.needs_metadata() || self.sess.instrument_coverage() + || self.sess.opts.unstable_opts.metrics_dir.is_some() } #[inline] diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl index 74b8087e07769..2f1a918d3a67d 100644 --- a/compiler/rustc_session/messages.ftl +++ b/compiler/rustc_session/messages.ftl @@ -1,3 +1,9 @@ +session_apple_deployment_target_invalid = + failed to parse deployment target specified in {$env_var}: {$error} + +session_apple_deployment_target_too_low = + deployment target in {$env_var} was set to {$version}, but the minimum supported by `rustc` is {$os_min} + session_binary_float_literal_not_supported = binary float literal is not supported session_branch_protection_requires_aarch64 = `-Zbranch-protection` is only supported on aarch64 diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 1f18950feac35..b5f24c7795368 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -237,10 +237,12 @@ pub enum AutoDiff { PrintPerf, /// Print intermediate IR generation steps PrintSteps, - /// Print the whole module, before running opts. + /// Print the module, before running autodiff. PrintModBefore, - /// Print the module after Enzyme differentiated everything. + /// Print the module after running autodiff. PrintModAfter, + /// Print the module after running autodiff and optimizations. + PrintModFinal, /// Enzyme's loose type debug helper (can cause incorrect gradients!!) /// Usable in cases where Enzyme errors with `can not deduce type of X`. diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index 71d8dbe44fed0..c4e726613a07a 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -1,4 +1,4 @@ -use std::num::NonZero; +use std::num::{NonZero, ParseIntError}; use rustc_ast::token; use rustc_ast::util::literal::LitError; @@ -14,6 +14,14 @@ use rustc_target::spec::{SplitDebuginfo, StackProtector, TargetTuple}; use crate::config::CrateType; use crate::parse::ParseSess; +#[derive(Diagnostic)] +pub(crate) enum AppleDeploymentTarget { + #[diag(session_apple_deployment_target_invalid)] + Invalid { env_var: &'static str, error: ParseIntError }, + #[diag(session_apple_deployment_target_too_low)] + TooLow { env_var: &'static str, version: String, os_min: String }, +} + pub(crate) struct FeatureGateError { pub(crate) span: MultiSpan, pub(crate) explain: DiagMessage, diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index eb513e00936ca..b3d71d1563996 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -711,7 +711,7 @@ mod desc { pub(crate) const parse_list: &str = "a space-separated list of strings"; pub(crate) const parse_list_with_polarity: &str = "a comma-separated list of strings, with elements beginning with + or -"; - pub(crate) const parse_autodiff: &str = "a comma separated list of settings: `Enable`, `PrintSteps`, `PrintTA`, `PrintAA`, `PrintPerf`, `PrintModBefore`, `PrintModAfter`, `LooseTypes`, `Inline`"; + pub(crate) const parse_autodiff: &str = "a comma separated list of settings: `Enable`, `PrintSteps`, `PrintTA`, `PrintAA`, `PrintPerf`, `PrintModBefore`, `PrintModAfter`, `PrintModFinal`, `LooseTypes`, `Inline`"; pub(crate) const parse_comma_list: &str = "a comma-separated list of strings"; pub(crate) const parse_opt_comma_list: &str = parse_comma_list; pub(crate) const parse_number: &str = "a number"; @@ -1359,6 +1359,7 @@ pub mod parse { "PrintSteps" => AutoDiff::PrintSteps, "PrintModBefore" => AutoDiff::PrintModBefore, "PrintModAfter" => AutoDiff::PrintModAfter, + "PrintModFinal" => AutoDiff::PrintModFinal, "LooseTypes" => AutoDiff::LooseTypes, "Inline" => AutoDiff::Inline, _ => { @@ -2093,6 +2094,7 @@ options! { `=PrintSteps` `=PrintModBefore` `=PrintModAfter` + `=PrintModFinal` `=LooseTypes` `=Inline` Multiple options can be combined with commas."), diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index a87b1961a9951..c8fbecc888603 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -29,7 +29,7 @@ use rustc_target::asm::InlineAsmArch; use rustc_target::spec::{ CodeModel, DebuginfoKind, PanicStrategy, RelocModel, RelroLevel, SanitizerSet, SmallDataThresholdSupport, SplitDebuginfo, StackProtector, SymbolVisibility, Target, - TargetTuple, TlsModel, + TargetTuple, TlsModel, apple, }; use crate::code_stats::CodeStats; @@ -891,6 +891,45 @@ impl Session { FileNameDisplayPreference::Local } } + + /// Get the deployment target on Apple platforms based on the standard environment variables, + /// or fall back to the minimum version supported by `rustc`. + /// + /// This should be guarded behind `if sess.target.is_like_osx`. + pub fn apple_deployment_target(&self) -> apple::OSVersion { + let min = apple::OSVersion::minimum_deployment_target(&self.target); + let env_var = apple::deployment_target_env_var(&self.target.os); + + // FIXME(madsmtm): Track changes to this. + if let Ok(deployment_target) = env::var(env_var) { + match apple::OSVersion::from_str(&deployment_target) { + Ok(version) => { + let os_min = apple::OSVersion::os_minimum_deployment_target(&self.target.os); + // It is common that the deployment target is set a bit too low, for example on + // macOS Aarch64 to also target older x86_64. So we only want to warn when variable + // is lower than the minimum OS supported by rustc, not when the variable is lower + // than the minimum for a specific target. + if version < os_min { + self.dcx().emit_warn(errors::AppleDeploymentTarget::TooLow { + env_var, + version: version.fmt_pretty().to_string(), + os_min: os_min.fmt_pretty().to_string(), + }); + } + + // Raise the deployment target to the minimum supported. + version.max(min) + } + Err(error) => { + self.dcx().emit_err(errors::AppleDeploymentTarget::Invalid { env_var, error }); + min + } + } + } else { + // If no deployment target variable is set, default to the minimum found above. + min + } + } } // JUSTIFICATION: part of session construction diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs index a8d7da5692de4..df99280f5712e 100644 --- a/compiler/rustc_target/src/lib.rs +++ b/compiler/rustc_target/src/lib.rs @@ -12,6 +12,7 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(assert_matches)] +#![feature(debug_closure_helpers)] #![feature(iter_intersperse)] #![feature(let_chains)] #![feature(rustc_attrs)] diff --git a/compiler/rustc_target/src/spec/base/apple/mod.rs b/compiler/rustc_target/src/spec/base/apple/mod.rs index 66c85146c2944..4165fa5b89cc7 100644 --- a/compiler/rustc_target/src/spec/base/apple/mod.rs +++ b/compiler/rustc_target/src/spec/base/apple/mod.rs @@ -1,9 +1,12 @@ use std::borrow::Cow; use std::env; +use std::fmt::{Display, from_fn}; +use std::num::ParseIntError; +use std::str::FromStr; use crate::spec::{ BinaryFormat, Cc, DebuginfoKind, FloatAbi, FramePointer, LinkerFlavor, Lld, RustcAbi, - SplitDebuginfo, StackProbeType, StaticCow, TargetOptions, cvs, + SplitDebuginfo, StackProbeType, StaticCow, Target, TargetOptions, cvs, }; #[cfg(test)] @@ -222,3 +225,107 @@ fn link_env_remove(os: &'static str) -> StaticCow<[StaticCow]> { cvs!["MACOSX_DEPLOYMENT_TARGET"] } } + +/// Deployment target or SDK version. +/// +/// The size of the numbers in here are limited by Mach-O's `LC_BUILD_VERSION`. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct OSVersion { + pub major: u16, + pub minor: u8, + pub patch: u8, +} + +impl FromStr for OSVersion { + type Err = ParseIntError; + + /// Parse an OS version triple (SDK version or deployment target). + fn from_str(version: &str) -> Result { + if let Some((major, minor)) = version.split_once('.') { + let major = major.parse()?; + if let Some((minor, patch)) = minor.split_once('.') { + Ok(Self { major, minor: minor.parse()?, patch: patch.parse()? }) + } else { + Ok(Self { major, minor: minor.parse()?, patch: 0 }) + } + } else { + Ok(Self { major: version.parse()?, minor: 0, patch: 0 }) + } + } +} + +impl OSVersion { + pub fn new(major: u16, minor: u8, patch: u8) -> Self { + Self { major, minor, patch } + } + + pub fn fmt_pretty(self) -> impl Display { + let Self { major, minor, patch } = self; + from_fn(move |f| { + write!(f, "{major}.{minor}")?; + if patch != 0 { + write!(f, ".{patch}")?; + } + Ok(()) + }) + } + + pub fn fmt_full(self) -> impl Display { + let Self { major, minor, patch } = self; + from_fn(move |f| write!(f, "{major}.{minor}.{patch}")) + } + + /// Minimum operating system versions currently supported by `rustc`. + pub fn os_minimum_deployment_target(os: &str) -> Self { + // When bumping a version in here, remember to update the platform-support docs too. + // + // NOTE: The defaults may change in future `rustc` versions, so if you are looking for the + // default deployment target, prefer: + // ``` + // $ rustc --print deployment-target + // ``` + let (major, minor, patch) = match os { + "macos" => (10, 12, 0), + "ios" => (10, 0, 0), + "tvos" => (10, 0, 0), + "watchos" => (5, 0, 0), + "visionos" => (1, 0, 0), + _ => unreachable!("tried to get deployment target for non-Apple platform"), + }; + Self { major, minor, patch } + } + + /// The deployment target for the given target. + /// + /// This is similar to `os_minimum_deployment_target`, except that on certain targets it makes sense + /// to raise the minimum OS version. + /// + /// This matches what LLVM does, see in part: + /// + pub fn minimum_deployment_target(target: &Target) -> Self { + let (major, minor, patch) = match (&*target.os, &*target.arch, &*target.abi) { + ("macos", "aarch64", _) => (11, 0, 0), + ("ios", "aarch64", "macabi") => (14, 0, 0), + ("ios", "aarch64", "sim") => (14, 0, 0), + ("ios", _, _) if target.llvm_target.starts_with("arm64e") => (14, 0, 0), + // Mac Catalyst defaults to 13.1 in Clang. + ("ios", _, "macabi") => (13, 1, 0), + ("tvos", "aarch64", "sim") => (14, 0, 0), + ("watchos", "aarch64", "sim") => (7, 0, 0), + (os, _, _) => return Self::os_minimum_deployment_target(os), + }; + Self { major, minor, patch } + } +} + +/// Name of the environment variable used to fetch the deployment target on the given OS. +pub fn deployment_target_env_var(os: &str) -> &'static str { + match os { + "macos" => "MACOSX_DEPLOYMENT_TARGET", + "ios" => "IPHONEOS_DEPLOYMENT_TARGET", + "watchos" => "WATCHOS_DEPLOYMENT_TARGET", + "tvos" => "TVOS_DEPLOYMENT_TARGET", + "visionos" => "XROS_DEPLOYMENT_TARGET", + _ => unreachable!("tried to get deployment target env var for non-Apple platform"), + } +} diff --git a/compiler/rustc_target/src/spec/base/apple/tests.rs b/compiler/rustc_target/src/spec/base/apple/tests.rs index 7a985ad4dc056..391f347010436 100644 --- a/compiler/rustc_target/src/spec/base/apple/tests.rs +++ b/compiler/rustc_target/src/spec/base/apple/tests.rs @@ -1,3 +1,4 @@ +use super::OSVersion; use crate::spec::targets::{ aarch64_apple_darwin, aarch64_apple_ios_sim, aarch64_apple_visionos_sim, aarch64_apple_watchos_sim, i686_apple_darwin, x86_64_apple_darwin, x86_64_apple_ios, @@ -42,3 +43,11 @@ fn macos_link_environment_unmodified() { ); } } + +#[test] +fn test_parse_version() { + assert_eq!("10".parse(), Ok(OSVersion::new(10, 0, 0))); + assert_eq!("10.12".parse(), Ok(OSVersion::new(10, 12, 0))); + assert_eq!("10.12.6".parse(), Ok(OSVersion::new(10, 12, 6))); + assert_eq!("9999.99.99".parse(), Ok(OSVersion::new(9999, 99, 99))); +} diff --git a/compiler/rustc_target/src/spec/base/mod.rs b/compiler/rustc_target/src/spec/base/mod.rs index e8fdc87178539..71b6528c2dd11 100644 --- a/compiler/rustc_target/src/spec/base/mod.rs +++ b/compiler/rustc_target/src/spec/base/mod.rs @@ -1,6 +1,6 @@ pub(crate) mod aix; pub(crate) mod android; -pub(crate) mod apple; +pub mod apple; pub(crate) mod avr; pub(crate) mod bpf; pub(crate) mod cygwin; diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 7234d1dc63ee1..bd1830ce27117 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -60,6 +60,7 @@ pub mod crt_objects; mod base; mod json; +pub use base::apple; pub use base::avr::ef_avr_arch; /// Linker is called through a C/C++ compiler. diff --git a/compiler/rustc_target/src/spec/targets/i686_pc_windows_gnu.rs b/compiler/rustc_target/src/spec/targets/i686_pc_windows_gnu.rs index 2a26323e5147b..e775c8fc524c4 100644 --- a/compiler/rustc_target/src/spec/targets/i686_pc_windows_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/i686_pc_windows_gnu.rs @@ -20,7 +20,7 @@ pub(crate) fn target() -> Target { llvm_target: "i686-pc-windows-gnu".into(), metadata: TargetMetadata { description: Some("32-bit MinGW (Windows 10+)".into()), - tier: Some(1), + tier: Some(2), host_tools: Some(true), std: Some(true), }, diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml index b729d5e116d2c..1d2dd1e60819f 100644 --- a/library/alloc/Cargo.toml +++ b/library/alloc/Cargo.toml @@ -36,5 +36,4 @@ check-cfg = [ 'cfg(no_global_oom_handling)', 'cfg(no_rc)', 'cfg(no_sync)', - 'cfg(randomized_layouts)', ] diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs index 46c11ea150bf8..f1f4cc6f93bbd 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -63,6 +63,7 @@ mod fmt; mod heap; mod linked_list; mod misc_tests; +mod num; mod rc; mod slice; mod sort; diff --git a/library/alloctests/tests/num.rs b/library/alloctests/tests/num.rs new file mode 100644 index 0000000000000..3c76e68c60640 --- /dev/null +++ b/library/alloctests/tests/num.rs @@ -0,0 +1,69 @@ +use std::fmt::{Debug, Display}; +use std::str::FromStr; + +fn assert_nb(value: Int) { + let s = value.to_string(); + let s2 = format!("s: {}.", value); + + assert_eq!(format!("s: {s}."), s2); + let Ok(ret) = Int::from_str(&s) else { + panic!("failed to convert into to string"); + }; + assert_eq!(ret, value); +} + +macro_rules! uint_to_s { + ($($fn_name:ident, $int:ident,)+) => { + $( + #[test] + fn $fn_name() { + assert_nb::<$int>($int::MIN); + assert_nb::<$int>($int::MAX); + assert_nb::<$int>(1); + assert_nb::<$int>($int::MIN / 2); + assert_nb::<$int>($int::MAX / 2); + } + )+ + } +} +macro_rules! int_to_s { + ($($fn_name:ident, $int:ident,)+) => { + $( + #[test] + fn $fn_name() { + assert_nb::<$int>($int::MIN); + assert_nb::<$int>($int::MAX); + assert_nb::<$int>(1); + assert_nb::<$int>(0); + assert_nb::<$int>(-1); + assert_nb::<$int>($int::MIN / 2); + assert_nb::<$int>($int::MAX / 2); + } + )+ + } +} + +int_to_s!( + test_i8_to_string, + i8, + test_i16_to_string, + i16, + test_i32_to_string, + i32, + test_i64_to_string, + i64, + test_i128_to_string, + i128, +); +uint_to_s!( + test_u8_to_string, + u8, + test_u16_to_string, + u16, + test_u32_to_string, + u32, + test_u64_to_string, + u64, + test_u128_to_string, + u128, +); diff --git a/library/coretests/Cargo.toml b/library/coretests/Cargo.toml index e44f01d347b3d..7656388d24bee 100644 --- a/library/coretests/Cargo.toml +++ b/library/coretests/Cargo.toml @@ -12,6 +12,7 @@ edition = "2024" path = "lib.rs" test = false bench = false +doc = false [[test]] name = "coretests" diff --git a/src/bootstrap/mk/Makefile.in b/src/bootstrap/mk/Makefile.in index 6cb0b19d7632a..605f54d1d7fc7 100644 --- a/src/bootstrap/mk/Makefile.in +++ b/src/bootstrap/mk/Makefile.in @@ -120,10 +120,6 @@ ci-msvc: ci-msvc-py ci-msvc-ps1 # Set of tests that should represent half of the time of the test suite. # Used to split tests across multiple CI runners. # Test both x and bootstrap entrypoints. -ci-mingw-x-1: - $(Q)$(CFG_SRC_DIR)/x test --stage 2 $(SKIP_COMPILER) $(TEST_SET2) -ci-mingw-x-2: - $(Q)$(CFG_SRC_DIR)/x test --stage 2 $(SKIP_SRC) $(TEST_SET2) ci-mingw-x: $(Q)$(CFG_SRC_DIR)/x test --stage 2 $(TEST_SET1) ci-mingw-bootstrap: diff --git a/src/ci/docker/host-x86_64/mingw-check/Dockerfile b/src/ci/docker/host-x86_64/mingw-check/Dockerfile index b32fa6c8e4eef..8bb7116c3ec58 100644 --- a/src/ci/docker/host-x86_64/mingw-check/Dockerfile +++ b/src/ci/docker/host-x86_64/mingw-check/Dockerfile @@ -47,7 +47,7 @@ COPY host-x86_64/mingw-check/validate-error-codes.sh /scripts/ ENV SCRIPT \ python3 ../x.py check --stage 0 --set build.optimized-compiler-builtins=false core alloc std --target=aarch64-unknown-linux-gnu,i686-pc-windows-msvc,i686-unknown-linux-gnu,x86_64-apple-darwin,x86_64-pc-windows-gnu,x86_64-pc-windows-msvc && \ /scripts/check-default-config-profiles.sh && \ - python3 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu && \ + python3 ../x.py check --target=x86_64-pc-windows-gnu --host=x86_64-pc-windows-gnu && \ python3 ../x.py clippy ci && \ python3 ../x.py build --stage 0 src/tools/build-manifest && \ python3 ../x.py test --stage 0 src/tools/compiletest && \ diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh index 8324d1ec58624..28c035daa5d50 100755 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh @@ -62,7 +62,6 @@ case $HOST_TARGET in # See # For now, these tests are moved to `x86_64-msvc-ext2` in `src/ci/github-actions/jobs.yml`. #python3 "$X_PY" test --stage 2 src/tools/miri --target aarch64-apple-darwin --test-args pass - #python3 "$X_PY" test --stage 2 src/tools/miri --target i686-pc-windows-gnu --test-args pass ;; *) echo "FATAL: unexpected host $HOST_TARGET" diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index f62ed23d038c7..26ff17e423b5d 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -501,7 +501,7 @@ auto: env: SCRIPT: > python x.py test --stage 2 src/tools/miri --target aarch64-apple-darwin --test-args pass && - python x.py test --stage 2 src/tools/miri --target i686-pc-windows-gnu --test-args pass && + python x.py test --stage 2 src/tools/miri --target x86_64-pc-windows-gnu --test-args pass && python x.py miri --stage 2 library/core --test-args notest && python x.py miri --stage 2 library/alloc --test-args notest && python x.py miri --stage 2 library/std --test-args notest @@ -533,31 +533,6 @@ auto: # came from the mingw-w64 SourceForge download site. Unfortunately # SourceForge is notoriously flaky, so we mirror it on our own infrastructure. - # i686-mingw is split into three jobs to run tests in parallel. - - name: i686-mingw-1 - env: - RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu - SCRIPT: make ci-mingw-x-1 - # There is no dist-i686-mingw-alt, so there is no prebuilt LLVM with assertions - NO_DOWNLOAD_CI_LLVM: 1 - <<: *job-windows-25 - - - name: i686-mingw-2 - env: - RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu - SCRIPT: make ci-mingw-x-2 - # There is no dist-i686-mingw-alt, so there is no prebuilt LLVM with assertions - NO_DOWNLOAD_CI_LLVM: 1 - <<: *job-windows-25 - - - name: i686-mingw-3 - env: - RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu - SCRIPT: make ci-mingw-bootstrap - # There is no dist-i686-mingw-alt, so there is no prebuilt LLVM with assertions - NO_DOWNLOAD_CI_LLVM: 1 - <<: *job-windows-25 - # x86_64-mingw is split into two jobs to run tests in parallel. - name: x86_64-mingw-1 env: diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index e1ba27c07daef..9bb64adfa7869 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -103,7 +103,6 @@ - [s390x-unknown-linux-musl](platform-support/s390x-unknown-linux-musl.md) - [sparc-unknown-none-elf](./platform-support/sparc-unknown-none-elf.md) - [solaris](platform-support/solaris.md) - - [\*-pc-windows-gnullvm](platform-support/pc-windows-gnullvm.md) - [\*-nto-qnx-\*](platform-support/nto-qnx.md) - [\*-unikraft-linux-musl](platform-support/unikraft-linux-musl.md) - [\*-unknown-hermit](platform-support/hermit.md) @@ -122,6 +121,8 @@ - [wasm32-unknown-unknown](platform-support/wasm32-unknown-unknown.md) - [wasm32v1-none](platform-support/wasm32v1-none.md) - [wasm64-unknown-unknown](platform-support/wasm64-unknown-unknown.md) + - [windows-gnu](platform-support/windows-gnu.md) + - [windows-gnullvm](platform-support/windows-gnullvm.md) - [\*-win7-windows-gnu](platform-support/win7-windows-gnu.md) - [\*-win7-windows-msvc](platform-support/win7-windows-msvc.md) - [x86_64-fortanix-unknown-sgx](platform-support/x86_64-fortanix-unknown-sgx.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index bc97568f85c97..4149b4cb92020 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -34,11 +34,10 @@ target | notes -------|------- [`aarch64-apple-darwin`](platform-support/apple-darwin.md) | ARM64 macOS (11.0+, Big Sur+) `aarch64-unknown-linux-gnu` | ARM64 Linux (kernel 4.1, glibc 2.17+) -`i686-pc-windows-gnu` | 32-bit MinGW (Windows 10+, Windows Server 2016+, Pentium 4) [^x86_32-floats-return-ABI] `i686-pc-windows-msvc` | 32-bit MSVC (Windows 10+, Windows Server 2016+, Pentium 4) [^x86_32-floats-return-ABI] `i686-unknown-linux-gnu` | 32-bit Linux (kernel 3.2+, glibc 2.17+, Pentium 4) [^x86_32-floats-return-ABI] [`x86_64-apple-darwin`](platform-support/apple-darwin.md) | 64-bit macOS (10.12+, Sierra+) -`x86_64-pc-windows-gnu` | 64-bit MinGW (Windows 10+, Windows Server 2016+) +[`x86_64-pc-windows-gnu`](platform-support/windows-gnu.md) | 64-bit MinGW (Windows 10+, Windows Server 2016+) `x86_64-pc-windows-msvc` | 64-bit MSVC (Windows 10+, Windows Server 2016+) `x86_64-unknown-linux-gnu` | 64-bit Linux (kernel 3.2+, glibc 2.17+) @@ -96,6 +95,7 @@ target | notes [`armv7-unknown-linux-ohos`](platform-support/openharmony.md) | Armv7-A OpenHarmony [`loongarch64-unknown-linux-gnu`](platform-support/loongarch-linux.md) | LoongArch64 Linux, LP64D ABI (kernel 5.19, glibc 2.36) [`loongarch64-unknown-linux-musl`](platform-support/loongarch-linux.md) | LoongArch64 Linux, LP64D ABI (kernel 5.19, musl 1.2.5) +[`i686-pc-windows-gnu`](platform-support/windows-gnu.md) | 32-bit MinGW (Windows 10+, Windows Server 2016+, Pentium 4) [^x86_32-floats-return-ABI] `powerpc-unknown-linux-gnu` | PowerPC Linux (kernel 3.2, glibc 2.17) `powerpc64-unknown-linux-gnu` | PPC64 Linux (kernel 3.2, glibc 2.17) [`powerpc64le-unknown-linux-gnu`](platform-support/powerpc64le-unknown-linux-gnu.md) | PPC64LE Linux (kernel 3.10, glibc 2.17) @@ -143,7 +143,7 @@ target | std | notes [`aarch64-apple-ios-macabi`](platform-support/apple-ios-macabi.md) | ✓ | Mac Catalyst on ARM64 [`aarch64-apple-ios-sim`](platform-support/apple-ios.md) | ✓ | Apple iOS Simulator on ARM64 [`aarch64-linux-android`](platform-support/android.md) | ✓ | ARM64 Android -[`aarch64-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | ARM64 MinGW (Windows 10+), LLVM ABI +[`aarch64-pc-windows-gnullvm`](platform-support/windows-gnullvm.md) | ✓ | ARM64 MinGW (Windows 10+), LLVM ABI [`aarch64-unknown-fuchsia`](platform-support/fuchsia.md) | ✓ | ARM64 Fuchsia `aarch64-unknown-none` | * | Bare ARM64, hardfloat `aarch64-unknown-none-softfloat` | * | Bare ARM64, softfloat @@ -166,7 +166,7 @@ target | std | notes `i586-unknown-linux-gnu` | ✓ | 32-bit Linux (kernel 3.2, glibc 2.17, original Pentium) [^x86_32-floats-x87] `i586-unknown-linux-musl` | ✓ | 32-bit Linux (musl 1.2.3, original Pentium) [^x86_32-floats-x87] [`i686-linux-android`](platform-support/android.md) | ✓ | 32-bit x86 Android ([Pentium 4 plus various extensions](https://developer.android.com/ndk/guides/abis.html#x86)) [^x86_32-floats-return-ABI] -[`i686-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | 32-bit x86 MinGW (Windows 10+, Pentium 4), LLVM ABI [^x86_32-floats-return-ABI] +[`i686-pc-windows-gnullvm`](platform-support/windows-gnullvm.md) | ✓ | 32-bit x86 MinGW (Windows 10+, Pentium 4), LLVM ABI [^x86_32-floats-return-ABI] [`i686-unknown-freebsd`](platform-support/freebsd.md) | ✓ | 32-bit x86 FreeBSD (Pentium 4) [^x86_32-floats-return-ABI] `i686-unknown-linux-musl` | ✓ | 32-bit Linux with musl 1.2.3 (Pentium 4) [^x86_32-floats-return-ABI] [`i686-unknown-uefi`](platform-support/unknown-uefi.md) | ? | 32-bit UEFI (Pentium 4, softfloat) @@ -202,7 +202,7 @@ target | std | notes [`x86_64-fortanix-unknown-sgx`](platform-support/x86_64-fortanix-unknown-sgx.md) | ✓ | [Fortanix ABI] for 64-bit Intel SGX [`x86_64-linux-android`](platform-support/android.md) | ✓ | 64-bit x86 Android [`x86_64-pc-solaris`](platform-support/solaris.md) | ✓ | 64-bit x86 Solaris 11.4 -[`x86_64-pc-windows-gnullvm`](platform-support/pc-windows-gnullvm.md) | ✓ | 64-bit x86 MinGW (Windows 10+), LLVM ABI +[`x86_64-pc-windows-gnullvm`](platform-support/windows-gnullvm.md) | ✓ | 64-bit x86 MinGW (Windows 10+), LLVM ABI [`x86_64-unknown-fuchsia`](platform-support/fuchsia.md) | ✓ | 64-bit x86 Fuchsia `x86_64-unknown-linux-gnux32` | ✓ | 64-bit Linux (x32 ABI) (kernel 4.15, glibc 2.27) [`x86_64-unknown-none`](platform-support/x86_64-unknown-none.md) | * | Freestanding/bare-metal x86_64, softfloat diff --git a/src/doc/rustc/src/platform-support/windows-gnu.md b/src/doc/rustc/src/platform-support/windows-gnu.md new file mode 100644 index 0000000000000..a867ebcfe2539 --- /dev/null +++ b/src/doc/rustc/src/platform-support/windows-gnu.md @@ -0,0 +1,32 @@ +# \*-windows-gnu + +**⚠️ This documentation page is a stub, you can help improving it by sending a PR. ⚠️** + +**Tier: 1/2 (with host tools)** + +Target triples available: +- `i686-pc-windows-gnu`: Tier 2 +- `x86_64-pc-windows-gnu`: Tier 1 + +## Target maintainers + +**⚠️ These targets do not have any maintainers and are not properly maintained. ⚠️** + +If you are using this target, consider signing up to become a target maintainer. +See the target tier policy for details. +Without maintainers, these targets may be demoted in the future. + +## Requirements + +These targets support std and host tools. + +Unlike their MSVC counterparts, windows-gnu targets support cross-compilation and are free of all MSVC licensing implications. + +They follow Windows calling convention for `extern "C"`. + +Like with any other Windows target, created binaries are in PE format. + +## Building Rust programs + +Rust does ship a pre-compiled std library for those targets. +That means one can easily compile and cross-compile for those targets from other hosts if C proper toolchain is installed. diff --git a/src/doc/rustc/src/platform-support/pc-windows-gnullvm.md b/src/doc/rustc/src/platform-support/windows-gnullvm.md similarity index 88% rename from src/doc/rustc/src/platform-support/pc-windows-gnullvm.md rename to src/doc/rustc/src/platform-support/windows-gnullvm.md index f14fe7df42286..1ff559fe9600c 100644 --- a/src/doc/rustc/src/platform-support/pc-windows-gnullvm.md +++ b/src/doc/rustc/src/platform-support/windows-gnullvm.md @@ -1,8 +1,8 @@ -# \*-pc-windows-gnullvm +# \*-windows-gnullvm **Tier: 2 (without host tools)** -Windows targets similar to `*-pc-windows-gnu` but using UCRT as the runtime and various LLVM tools/libraries instead of GCC/Binutils. +Windows targets similar to `*-windows-gnu` but using UCRT as the runtime and various LLVM tools/libraries instead of GCC/Binutils. Target triples available so far: - `aarch64-pc-windows-gnullvm` @@ -17,9 +17,9 @@ Target triples available so far: ## Requirements The easiest way to obtain these targets is cross-compilation, but native build from `x86_64-pc-windows-gnu` is possible with few hacks which I don't recommend. -Std support is expected to be on par with `*-pc-windows-gnu`. +Std support is expected to be on par with `*-windows-gnu`. -Binaries for this target should be at least on par with `*-pc-windows-gnu` in terms of requirements and functionality. +Binaries for this target should be at least on par with `*-windows-gnu` in terms of requirements and functionality. Those targets follow Windows calling convention for `extern "C"`. diff --git a/src/librustdoc/doctest/runner.rs b/src/librustdoc/doctest/runner.rs index e34b8a53b073d..784d628805d97 100644 --- a/src/librustdoc/doctest/runner.rs +++ b/src/librustdoc/doctest/runner.rs @@ -113,6 +113,7 @@ impl DocTestRunner { mod __doctest_mod {{ use std::sync::OnceLock; use std::path::PathBuf; + use std::process::ExitCode; pub static BINARY_PATH: OnceLock = OnceLock::new(); pub const RUN_OPTION: &str = \"RUSTDOC_DOCTEST_RUN_NB_TEST\"; @@ -123,16 +124,17 @@ mod __doctest_mod {{ }} #[allow(unused)] - pub fn doctest_runner(bin: &std::path::Path, test_nb: usize) -> Result<(), String> {{ + pub fn doctest_runner(bin: &std::path::Path, test_nb: usize) -> ExitCode {{ let out = std::process::Command::new(bin) .env(self::RUN_OPTION, test_nb.to_string()) .args(std::env::args().skip(1).collect::>()) .output() .expect(\"failed to run command\"); if !out.status.success() {{ - Err(String::from_utf8_lossy(&out.stderr).to_string()) + eprint!(\"{{}}\", String::from_utf8_lossy(&out.stderr)); + ExitCode::FAILURE }} else {{ - Ok(()) + ExitCode::SUCCESS }} }} }} diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index 4150c5609a97e..a7ce2bf9048bf 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -1101,7 +1101,6 @@ function preLoadCss(cssUrl) { }); }()); - // @ts-expect-error window.rustdoc_add_line_numbers_to_examples = () => { // @ts-expect-error function generateLine(nb) { @@ -1123,7 +1122,6 @@ function preLoadCss(cssUrl) { }); }; - // @ts-expect-error window.rustdoc_remove_line_numbers_from_examples = () => { onEachLazy( document.querySelectorAll(".rustdoc:not(.src) :not(.scraped-example) > .example-wrap"), @@ -1132,7 +1130,6 @@ function preLoadCss(cssUrl) { }; if (getSettingValue("line-numbers") === "true") { - // @ts-expect-error window.rustdoc_add_line_numbers_to_examples(); } @@ -1596,7 +1593,7 @@ function preLoadCss(cssUrl) { /** * Hide popover menus, clickable tooltips, and the sidebar (if applicable). * - * Pass "true" to reset focus for tooltip popovers. + * Pass `true` to reset focus for tooltip popovers. */ window.hideAllModals = switchFocus => { hideSidebar(); diff --git a/src/librustdoc/html/static/js/rustdoc.d.ts b/src/librustdoc/html/static/js/rustdoc.d.ts index 91a58fab86eff..0d2e19e019f34 100644 --- a/src/librustdoc/html/static/js/rustdoc.d.ts +++ b/src/librustdoc/html/static/js/rustdoc.d.ts @@ -30,6 +30,8 @@ declare global { currentCrate: string|null; /** * Hide popovers, tooltips, or the mobile sidebar. + * + * Pass `true` to reset focus for tooltip popovers. */ hideAllModals: function(boolean), /** @@ -78,6 +80,8 @@ declare global { pending_implementors?: rustdoc.Implementors, register_type_impls?: function(rustdoc.TypeImpls): void, pending_type_impls?: rustdoc.TypeImpls, + rustdoc_add_line_numbers_to_examples?: function(), + rustdoc_remove_line_numbers_from_examples?: function(), } interface HTMLElement { /** Used by the popover tooltip code. */ @@ -477,4 +481,14 @@ declare namespace rustdoc { * is a tuple of (filename, subdirs, filenames). */ type Dir = [string, rustdoc.Dir[], string[]] + + /** + * Indivitual setting object, used in `settings.js` + */ + interface Setting { + js_name: string, + name: string, + options?: string[], + default: string | boolean, + } } diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js index 5f1bbd27328cb..2430b5829b2ba 100644 --- a/src/librustdoc/html/static/js/settings.js +++ b/src/librustdoc/html/static/js/settings.js @@ -1,22 +1,39 @@ // Local js definitions: /* global getSettingValue, updateLocalStorage, updateTheme */ /* global addClass, removeClass, onEach, onEachLazy */ -/* global MAIN_ID, getVar, getSettingsButton, getHelpButton */ - -// Eventually fix this. -// @ts-nocheck +/* global MAIN_ID, getVar, getSettingsButton, getHelpButton, nonnull */ "use strict"; (function() { const isSettingsPage = window.location.pathname.endsWith("/settings.html"); + /** + * @param {Element} elem + * @param {EventTarget|null} target + */ + function elemContainsTarget(elem, target) { + if (target instanceof Node) { + return elem.contains(target); + } else { + return false; + } + } + + /** + * @overload {"theme"|"preferred-dark-theme"|"preferred-light-theme"} + * @param {string} settingName + * @param {string} value + * @returns + * @param {string} settingName + * @param {string|boolean} value + */ function changeSetting(settingName, value) { if (settingName === "theme") { const useSystem = value === "system preference" ? "true" : "false"; updateLocalStorage("use-system-theme", useSystem); } - updateLocalStorage(settingName, value); + updateLocalStorage(settingName, "" + value); switch (settingName) { case "theme": @@ -27,9 +44,15 @@ break; case "line-numbers": if (value === true) { - window.rustdoc_add_line_numbers_to_examples(); + const f = window.rustdoc_add_line_numbers_to_examples; + if (f !== undefined) { + f(); + } } else { - window.rustdoc_remove_line_numbers_from_examples(); + const f = window.rustdoc_remove_line_numbers_from_examples; + if (f !== undefined) { + f(); + } } break; case "hide-sidebar": @@ -89,6 +112,9 @@ } } + /** + * @param {HTMLElement} settingsElement + */ function setEvents(settingsElement) { updateLightAndDark(); onEachLazy(settingsElement.querySelectorAll("input[type=\"checkbox\"]"), toggle => { @@ -101,23 +127,27 @@ changeSetting(toggle.id, toggle.checked); }; }); - onEachLazy(settingsElement.querySelectorAll("input[type=\"radio\"]"), elem => { - const settingId = elem.name; - let settingValue = getSettingValue(settingId); - if (settingId === "theme") { - const useSystem = getSettingValue("use-system-theme"); - if (useSystem === "true" || settingValue === null) { - // "light" is the default theme - settingValue = useSystem === "false" ? "light" : "system preference"; + onEachLazy( + settingsElement.querySelectorAll("input[type=\"radio\"]"), + /** @param {HTMLInputElement} elem */ + elem => { + const settingId = elem.name; + let settingValue = getSettingValue(settingId); + if (settingId === "theme") { + const useSystem = getSettingValue("use-system-theme"); + if (useSystem === "true" || settingValue === null) { + // "light" is the default theme + settingValue = useSystem === "false" ? "light" : "system preference"; + } } - } - if (settingValue !== null && settingValue !== "null") { - elem.checked = settingValue === elem.value; - } - elem.addEventListener("change", ev => { - changeSetting(ev.target.name, ev.target.value); - }); - }); + if (settingValue !== null && settingValue !== "null") { + elem.checked = settingValue === elem.value; + } + elem.addEventListener("change", () => { + changeSetting(elem.name, elem.value); + }); + }, + ); } /** @@ -125,7 +155,7 @@ * as argument which describes each setting and how to render it. It returns a string * representing the raw HTML. * - * @param {Array} settings + * @param {Array} settings * * @return {string} */ @@ -133,11 +163,6 @@ let output = ""; for (const setting of settings) { - if (setting === "hr") { - output += "
"; - continue; - } - const js_data_name = setting["js_name"]; const setting_name = setting["name"]; @@ -182,7 +207,9 @@ * @return {HTMLElement} */ function buildSettingsPage() { - const theme_names = getVar("themes").split(",").filter(t => t); + const theme_list = getVar("themes"); + const theme_names = (theme_list === null ? "" : theme_list) + .split(",").filter(t => t); theme_names.push("light", "dark", "ayu"); const settings = [ @@ -272,10 +299,16 @@ el.innerHTML = innerHTML; if (isSettingsPage) { - document.getElementById(MAIN_ID).appendChild(el); + const mainElem = document.getElementById(MAIN_ID); + if (mainElem !== null) { + mainElem.appendChild(el); + } } else { el.setAttribute("tabindex", "-1"); - getSettingsButton().appendChild(el); + const settingsBtn = getSettingsButton(); + if (settingsBtn !== null) { + settingsBtn.appendChild(el); + } } return el; } @@ -293,34 +326,44 @@ }); } + /** + * @param {FocusEvent} event + */ function settingsBlurHandler(event) { - if (!getHelpButton().contains(document.activeElement) && - !getHelpButton().contains(event.relatedTarget) && - !getSettingsButton().contains(document.activeElement) && - !getSettingsButton().contains(event.relatedTarget) - ) { + const helpBtn = getHelpButton(); + const settingsBtn = getSettingsButton(); + const helpUnfocused = helpBtn === null || + (!helpBtn.contains(document.activeElement) && + !elemContainsTarget(helpBtn, event.relatedTarget)); + const settingsUnfocused = settingsBtn === null || + (!settingsBtn.contains(document.activeElement) && + !elemContainsTarget(settingsBtn, event.relatedTarget)); + if (helpUnfocused && settingsUnfocused) { window.hidePopoverMenus(); } } if (!isSettingsPage) { // We replace the existing "onclick" callback. - const settingsButton = getSettingsButton(); - const settingsMenu = document.getElementById("settings"); + // These elements must exist, as (outside of the settings page) + // `settings.js` is only loaded after the settings button is clicked. + const settingsButton = nonnull(getSettingsButton()); + const settingsMenu = nonnull(document.getElementById("settings")); settingsButton.onclick = event => { - if (settingsMenu.contains(event.target)) { + if (elemContainsTarget(settingsMenu, event.target)) { return; } event.preventDefault(); const shouldDisplaySettings = settingsMenu.style.display === "none"; - window.hideAllModals(); + window.hideAllModals(false); if (shouldDisplaySettings) { displaySettings(); } }; settingsButton.onblur = settingsBlurHandler; - settingsButton.querySelector("a").onblur = settingsBlurHandler; + // the settings button should always have a link in it + nonnull(settingsButton.querySelector("a")).onblur = settingsBlurHandler; onEachLazy(settingsMenu.querySelectorAll("input"), el => { el.onblur = settingsBlurHandler; }); diff --git a/tests/codegen/autodiff.rs b/tests/codegen/autodiff.rs index cace0edb2b544..85358f5fcb693 100644 --- a/tests/codegen/autodiff.rs +++ b/tests/codegen/autodiff.rs @@ -11,7 +11,7 @@ fn square(x: &f64) -> f64 { x * x } -// CHECK:define internal fastcc double @diffesquare(double %x.0.val, ptr nocapture align 8 %"x'" +// CHECK:define internal fastcc double @diffesquare(double %x.0.val, ptr nocapture nonnull align 8 %"x'" // CHECK-NEXT:invertstart: // CHECK-NEXT: %_0 = fmul double %x.0.val, %x.0.val // CHECK-NEXT: %0 = fadd fast double %x.0.val, %x.0.val @@ -22,7 +22,7 @@ fn square(x: &f64) -> f64 { // CHECK-NEXT:} fn main() { - let x = 3.0; + let x = std::hint::black_box(3.0); let output = square(&x); assert_eq!(9.0, output); diff --git a/tests/codegen/autodiffv.rs b/tests/codegen/autodiffv.rs new file mode 100644 index 0000000000000..e004711640553 --- /dev/null +++ b/tests/codegen/autodiffv.rs @@ -0,0 +1,116 @@ +//@ 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. + +#![feature(autodiff)] + +use std::autodiff::autodiff; + +#[autodiff(d_square3, Forward, Dual, DualOnly)] +#[autodiff(d_square2, Forward, 4, Dual, DualOnly)] +#[autodiff(d_square1, Forward, 4, Dual, Dual)] +#[no_mangle] +fn square(x: &f32) -> f32 { + x * x +} + +// d_sqaure2 +// CHECK: define internal fastcc [4 x float] @fwddiffe4square(float %x.0.val, [4 x ptr] %"x'") +// CHECK-NEXT: start: +// CHECK-NEXT: %0 = extractvalue [4 x ptr] %"x'", 0 +// CHECK-NEXT: %"_2'ipl" = load float, ptr %0, align 4 +// CHECK-NEXT: %1 = extractvalue [4 x ptr] %"x'", 1 +// CHECK-NEXT: %"_2'ipl1" = load float, ptr %1, align 4 +// CHECK-NEXT: %2 = extractvalue [4 x ptr] %"x'", 2 +// CHECK-NEXT: %"_2'ipl2" = load float, ptr %2, align 4 +// CHECK-NEXT: %3 = extractvalue [4 x ptr] %"x'", 3 +// CHECK-NEXT: %"_2'ipl3" = load float, ptr %3, align 4 +// CHECK-NEXT: %4 = insertelement <4 x float> poison, float %"_2'ipl", i64 0 +// CHECK-NEXT: %5 = insertelement <4 x float> %4, float %"_2'ipl1", i64 1 +// CHECK-NEXT: %6 = insertelement <4 x float> %5, float %"_2'ipl2", i64 2 +// CHECK-NEXT: %7 = insertelement <4 x float> %6, float %"_2'ipl3", i64 3 +// CHECK-NEXT: %8 = fadd fast <4 x float> %7, %7 +// CHECK-NEXT: %9 = insertelement <4 x float> poison, float %x.0.val, i64 0 +// CHECK-NEXT: %10 = shufflevector <4 x float> %9, <4 x float> poison, <4 x i32> zeroinitializer +// CHECK-NEXT: %11 = fmul fast <4 x float> %8, %10 +// CHECK-NEXT: %12 = extractelement <4 x float> %11, i64 0 +// CHECK-NEXT: %13 = insertvalue [4 x float] undef, float %12, 0 +// CHECK-NEXT: %14 = extractelement <4 x float> %11, i64 1 +// CHECK-NEXT: %15 = insertvalue [4 x float] %13, float %14, 1 +// CHECK-NEXT: %16 = extractelement <4 x float> %11, i64 2 +// CHECK-NEXT: %17 = insertvalue [4 x float] %15, float %16, 2 +// CHECK-NEXT: %18 = extractelement <4 x float> %11, i64 3 +// CHECK-NEXT: %19 = insertvalue [4 x float] %17, float %18, 3 +// CHECK-NEXT: ret [4 x float] %19 +// CHECK-NEXT: } + +// d_square3, the extra float is the original return value (x * x) +// CHECK: define internal fastcc { float, [4 x float] } @fwddiffe4square.1(float %x.0.val, [4 x ptr] %"x'") +// CHECK-NEXT: start: +// CHECK-NEXT: %0 = extractvalue [4 x ptr] %"x'", 0 +// CHECK-NEXT: %"_2'ipl" = load float, ptr %0, align 4 +// CHECK-NEXT: %1 = extractvalue [4 x ptr] %"x'", 1 +// CHECK-NEXT: %"_2'ipl1" = load float, ptr %1, align 4 +// CHECK-NEXT: %2 = extractvalue [4 x ptr] %"x'", 2 +// CHECK-NEXT: %"_2'ipl2" = load float, ptr %2, align 4 +// CHECK-NEXT: %3 = extractvalue [4 x ptr] %"x'", 3 +// CHECK-NEXT: %"_2'ipl3" = load float, ptr %3, align 4 +// CHECK-NEXT: %_0 = fmul float %x.0.val, %x.0.val +// CHECK-NEXT: %4 = insertelement <4 x float> poison, float %"_2'ipl", i64 0 +// CHECK-NEXT: %5 = insertelement <4 x float> %4, float %"_2'ipl1", i64 1 +// CHECK-NEXT: %6 = insertelement <4 x float> %5, float %"_2'ipl2", i64 2 +// CHECK-NEXT: %7 = insertelement <4 x float> %6, float %"_2'ipl3", i64 3 +// CHECK-NEXT: %8 = fadd fast <4 x float> %7, %7 +// CHECK-NEXT: %9 = insertelement <4 x float> poison, float %x.0.val, i64 0 +// CHECK-NEXT: %10 = shufflevector <4 x float> %9, <4 x float> poison, <4 x i32> zeroinitializer +// CHECK-NEXT: %11 = fmul fast <4 x float> %8, %10 +// CHECK-NEXT: %12 = extractelement <4 x float> %11, i64 0 +// CHECK-NEXT: %13 = insertvalue [4 x float] undef, float %12, 0 +// CHECK-NEXT: %14 = extractelement <4 x float> %11, i64 1 +// CHECK-NEXT: %15 = insertvalue [4 x float] %13, float %14, 1 +// CHECK-NEXT: %16 = extractelement <4 x float> %11, i64 2 +// CHECK-NEXT: %17 = insertvalue [4 x float] %15, float %16, 2 +// CHECK-NEXT: %18 = extractelement <4 x float> %11, i64 3 +// CHECK-NEXT: %19 = insertvalue [4 x float] %17, float %18, 3 +// CHECK-NEXT: %20 = insertvalue { float, [4 x float] } undef, float %_0, 0 +// CHECK-NEXT: %21 = insertvalue { float, [4 x float] } %20, [4 x float] %19, 1 +// CHECK-NEXT: ret { float, [4 x float] } %21 +// CHECK-NEXT: } + +fn main() { + let x = std::hint::black_box(3.0); + let output = square(&x); + dbg!(&output); + assert_eq!(9.0, output); + dbg!(square(&x)); + + let mut df_dx1 = 1.0; + let mut df_dx2 = 2.0; + let mut df_dx3 = 3.0; + let mut df_dx4 = 0.0; + let [o1, o2, o3, o4] = d_square2(&x, &mut df_dx1, &mut df_dx2, &mut df_dx3, &mut df_dx4); + dbg!(o1, o2, o3, o4); + let [output2, o1, o2, o3, o4] = + d_square1(&x, &mut df_dx1, &mut df_dx2, &mut df_dx3, &mut df_dx4); + dbg!(o1, o2, o3, o4); + assert_eq!(output, output2); + assert!((6.0 - o1).abs() < 1e-10); + assert!((12.0 - o2).abs() < 1e-10); + assert!((18.0 - o3).abs() < 1e-10); + assert!((0.0 - o4).abs() < 1e-10); + assert_eq!(1.0, df_dx1); + assert_eq!(2.0, df_dx2); + assert_eq!(3.0, df_dx3); + assert_eq!(0.0, df_dx4); + assert_eq!(d_square3(&x, &mut df_dx1), 2.0 * o1); + assert_eq!(d_square3(&x, &mut df_dx2), 2.0 * o2); + assert_eq!(d_square3(&x, &mut df_dx3), 2.0 * o3); + assert_eq!(d_square3(&x, &mut df_dx4), 2.0 * o4); +} diff --git a/tests/pretty/autodiff_forward.pp b/tests/pretty/autodiff_forward.pp index dc7a2712f4231..4b2fb6166ff7e 100644 --- a/tests/pretty/autodiff_forward.pp +++ b/tests/pretty/autodiff_forward.pp @@ -25,27 +25,31 @@ // We want to be sure that the same function can be differentiated in different ways + + // Make sure, that we add the None for the default return. + + ::core::panicking::panic("not implemented") } -#[rustc_autodiff(Forward, Dual, Const, Dual,)] +#[rustc_autodiff(Forward, 1, Dual, Const, Dual)] #[inline(never)] -pub fn df1(x: &[f64], bx: &[f64], y: f64) -> (f64, f64) { +pub fn df1(x: &[f64], bx_0: &[f64], y: f64) -> (f64, f64) { unsafe { asm!("NOP", options(pure, nomem)); }; ::core::hint::black_box(f1(x, y)); - ::core::hint::black_box((bx,)); - ::core::hint::black_box((f1(x, y), f64::default())) + ::core::hint::black_box((bx_0,)); + ::core::hint::black_box(<(f64, f64)>::default()) } #[rustc_autodiff] #[inline(never)] pub fn f2(x: &[f64], y: f64) -> f64 { ::core::panicking::panic("not implemented") } -#[rustc_autodiff(Forward, Dual, Const, Const,)] +#[rustc_autodiff(Forward, 1, Dual, Const, Const)] #[inline(never)] -pub fn df2(x: &[f64], bx: &[f64], y: f64) -> f64 { +pub fn df2(x: &[f64], bx_0: &[f64], y: f64) -> f64 { unsafe { asm!("NOP", options(pure, nomem)); }; ::core::hint::black_box(f2(x, y)); - ::core::hint::black_box((bx,)); + ::core::hint::black_box((bx_0,)); ::core::hint::black_box(f2(x, y)) } #[rustc_autodiff] @@ -53,20 +57,20 @@ pub fn f3(x: &[f64], y: f64) -> f64 { ::core::panicking::panic("not implemented") } -#[rustc_autodiff(Forward, Dual, Const, Const,)] +#[rustc_autodiff(Forward, 1, Dual, Const, Const)] #[inline(never)] -pub fn df3(x: &[f64], bx: &[f64], y: f64) -> f64 { +pub fn df3(x: &[f64], bx_0: &[f64], y: f64) -> f64 { unsafe { asm!("NOP", options(pure, nomem)); }; ::core::hint::black_box(f3(x, y)); - ::core::hint::black_box((bx,)); + ::core::hint::black_box((bx_0,)); ::core::hint::black_box(f3(x, y)) } #[rustc_autodiff] #[inline(never)] pub fn f4() {} -#[rustc_autodiff(Forward, None)] +#[rustc_autodiff(Forward, 1, None)] #[inline(never)] -pub fn df4() { +pub fn df4() -> () { unsafe { asm!("NOP", options(pure, nomem)); }; ::core::hint::black_box(f4()); ::core::hint::black_box(()); @@ -76,28 +80,82 @@ pub fn f5(x: &[f64], y: f64) -> f64 { ::core::panicking::panic("not implemented") } -#[rustc_autodiff(Forward, Const, Dual, Const,)] +#[rustc_autodiff(Forward, 1, Const, Dual, Const)] #[inline(never)] -pub fn df5_y(x: &[f64], y: f64, by: f64) -> f64 { +pub fn df5_y(x: &[f64], y: f64, by_0: f64) -> f64 { unsafe { asm!("NOP", options(pure, nomem)); }; ::core::hint::black_box(f5(x, y)); - ::core::hint::black_box((by,)); + ::core::hint::black_box((by_0,)); ::core::hint::black_box(f5(x, y)) } -#[rustc_autodiff(Forward, Dual, Const, Const,)] +#[rustc_autodiff(Forward, 1, Dual, Const, Const)] #[inline(never)] -pub fn df5_x(x: &[f64], bx: &[f64], y: f64) -> f64 { +pub fn df5_x(x: &[f64], bx_0: &[f64], y: f64) -> f64 { unsafe { asm!("NOP", options(pure, nomem)); }; ::core::hint::black_box(f5(x, y)); - ::core::hint::black_box((bx,)); + ::core::hint::black_box((bx_0,)); ::core::hint::black_box(f5(x, y)) } -#[rustc_autodiff(Reverse, Duplicated, Const, Active,)] +#[rustc_autodiff(Reverse, 1, Duplicated, Const, Active)] #[inline(never)] -pub fn df5_rev(x: &[f64], dx: &mut [f64], y: f64, dret: f64) -> f64 { +pub fn df5_rev(x: &[f64], dx_0: &mut [f64], y: f64, dret: f64) -> f64 { unsafe { asm!("NOP", options(pure, nomem)); }; ::core::hint::black_box(f5(x, y)); - ::core::hint::black_box((dx, dret)); + ::core::hint::black_box((dx_0, dret)); ::core::hint::black_box(f5(x, y)) } +struct DoesNotImplDefault; +#[rustc_autodiff] +#[inline(never)] +pub fn f6() -> DoesNotImplDefault { + ::core::panicking::panic("not implemented") +} +#[rustc_autodiff(Forward, 1, Const)] +#[inline(never)] +pub fn df6() -> DoesNotImplDefault { + unsafe { asm!("NOP", options(pure, nomem)); }; + ::core::hint::black_box(f6()); + ::core::hint::black_box(()); + ::core::hint::black_box(f6()) +} +#[rustc_autodiff] +#[inline(never)] +pub fn f7(x: f32) -> () {} +#[rustc_autodiff(Forward, 1, Const, None)] +#[inline(never)] +pub fn df7(x: f32) -> () { + unsafe { asm!("NOP", options(pure, nomem)); }; + ::core::hint::black_box(f7(x)); + ::core::hint::black_box(()); +} +#[no_mangle] +#[rustc_autodiff] +#[inline(never)] +fn f8(x: &f32) -> f32 { ::core::panicking::panic("not implemented") } +#[rustc_autodiff(Forward, 4, Dual, Dual)] +#[inline(never)] +fn f8_3(x: &f32, bx_0: &f32, bx_1: &f32, bx_2: &f32, bx_3: &f32) + -> [f32; 5usize] { + unsafe { asm!("NOP", options(pure, nomem)); }; + ::core::hint::black_box(f8(x)); + ::core::hint::black_box((bx_0, bx_1, bx_2, bx_3)); + ::core::hint::black_box(<[f32; 5usize]>::default()) +} +#[rustc_autodiff(Forward, 4, Dual, DualOnly)] +#[inline(never)] +fn f8_2(x: &f32, bx_0: &f32, bx_1: &f32, bx_2: &f32, bx_3: &f32) + -> [f32; 4usize] { + unsafe { asm!("NOP", options(pure, nomem)); }; + ::core::hint::black_box(f8(x)); + ::core::hint::black_box((bx_0, bx_1, bx_2, bx_3)); + ::core::hint::black_box(<[f32; 4usize]>::default()) +} +#[rustc_autodiff(Forward, 1, Dual, DualOnly)] +#[inline(never)] +fn f8_1(x: &f32, bx_0: &f32) -> f32 { + unsafe { asm!("NOP", options(pure, nomem)); }; + ::core::hint::black_box(f8(x)); + ::core::hint::black_box((bx_0,)); + ::core::hint::black_box(::default()) +} fn main() {} diff --git a/tests/pretty/autodiff_forward.rs b/tests/pretty/autodiff_forward.rs index bc5582116322b..a765738c2a815 100644 --- a/tests/pretty/autodiff_forward.rs +++ b/tests/pretty/autodiff_forward.rs @@ -36,4 +36,22 @@ pub fn f5(x: &[f64], y: f64) -> f64 { unimplemented!() } +struct DoesNotImplDefault; +#[autodiff(df6, Forward, Const)] +pub fn f6() -> DoesNotImplDefault { + unimplemented!() +} + +// Make sure, that we add the None for the default return. +#[autodiff(df7, Forward, Const)] +pub fn f7(x: f32) -> () {} + +#[autodiff(f8_1, Forward, Dual, DualOnly)] +#[autodiff(f8_2, Forward, 4, Dual, DualOnly)] +#[autodiff(f8_3, Forward, 4, Dual, Dual)] +#[no_mangle] +fn f8(x: &f32) -> f32 { + unimplemented!() +} + fn main() {} diff --git a/tests/pretty/autodiff_reverse.pp b/tests/pretty/autodiff_reverse.pp index b2cf0244af4c6..31920694a3ad5 100644 --- a/tests/pretty/autodiff_reverse.pp +++ b/tests/pretty/autodiff_reverse.pp @@ -28,18 +28,18 @@ ::core::panicking::panic("not implemented") } -#[rustc_autodiff(Reverse, Duplicated, Const, Active,)] +#[rustc_autodiff(Reverse, 1, Duplicated, Const, Active)] #[inline(never)] -pub fn df1(x: &[f64], dx: &mut [f64], y: f64, dret: f64) -> f64 { +pub fn df1(x: &[f64], dx_0: &mut [f64], y: f64, dret: f64) -> f64 { unsafe { asm!("NOP", options(pure, nomem)); }; ::core::hint::black_box(f1(x, y)); - ::core::hint::black_box((dx, dret)); + ::core::hint::black_box((dx_0, dret)); ::core::hint::black_box(f1(x, y)) } #[rustc_autodiff] #[inline(never)] pub fn f2() {} -#[rustc_autodiff(Reverse, None)] +#[rustc_autodiff(Reverse, 1, None)] #[inline(never)] pub fn df2() { unsafe { asm!("NOP", options(pure, nomem)); }; @@ -51,12 +51,12 @@ pub fn f3(x: &[f64], y: f64) -> f64 { ::core::panicking::panic("not implemented") } -#[rustc_autodiff(Reverse, Duplicated, Const, Active,)] +#[rustc_autodiff(Reverse, 1, Duplicated, Const, Active)] #[inline(never)] -pub fn df3(x: &[f64], dx: &mut [f64], y: f64, dret: f64) -> f64 { +pub fn df3(x: &[f64], dx_0: &mut [f64], y: f64, dret: f64) -> f64 { unsafe { asm!("NOP", options(pure, nomem)); }; ::core::hint::black_box(f3(x, y)); - ::core::hint::black_box((dx, dret)); + ::core::hint::black_box((dx_0, dret)); ::core::hint::black_box(f3(x, y)) } enum Foo { Reverse, } @@ -64,7 +64,7 @@ #[rustc_autodiff] #[inline(never)] pub fn f4(x: f32) { ::core::panicking::panic("not implemented") } -#[rustc_autodiff(Reverse, Const, None)] +#[rustc_autodiff(Reverse, 1, Const, None)] #[inline(never)] pub fn df4(x: f32) { unsafe { asm!("NOP", options(pure, nomem)); }; @@ -76,11 +76,11 @@ pub fn f5(x: *const f32, y: &f32) { ::core::panicking::panic("not implemented") } -#[rustc_autodiff(Reverse, DuplicatedOnly, Duplicated, None)] +#[rustc_autodiff(Reverse, 1, DuplicatedOnly, Duplicated, None)] #[inline(never)] -pub unsafe fn df5(x: *const f32, dx: *mut f32, y: &f32, dy: &mut f32) { +pub unsafe fn df5(x: *const f32, dx_0: *mut f32, y: &f32, dy_0: &mut f32) { unsafe { asm!("NOP", options(pure, nomem)); }; ::core::hint::black_box(f5(x, y)); - ::core::hint::black_box((dx, dy)); + ::core::hint::black_box((dx_0, dy_0)); } fn main() {} diff --git a/tests/rustdoc-ui/doctest/edition-2024-error-output.rs b/tests/rustdoc-ui/doctest/edition-2024-error-output.rs new file mode 100644 index 0000000000000..82a85debcd191 --- /dev/null +++ b/tests/rustdoc-ui/doctest/edition-2024-error-output.rs @@ -0,0 +1,14 @@ +// This is a regression test for . +// The output must look nice and not like a `Debug` display of a `String`. + +//@ edition: 2024 +//@ compile-flags: --test +//@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR" +//@ normalize-stdout: "panicked at .+rs:" -> "panicked at $$TMP:" +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ rustc-env:RUST_BACKTRACE=0 +//@ failure-status: 101 + +//! ```rust +//! assert_eq!(2 + 2, 5); +//! ``` diff --git a/tests/rustdoc-ui/doctest/edition-2024-error-output.stdout b/tests/rustdoc-ui/doctest/edition-2024-error-output.stdout new file mode 100644 index 0000000000000..8f056a5f703ed --- /dev/null +++ b/tests/rustdoc-ui/doctest/edition-2024-error-output.stdout @@ -0,0 +1,20 @@ + +running 1 test +test $DIR/edition-2024-error-output.rs - (line 12) ... FAILED + +failures: + +---- $DIR/edition-2024-error-output.rs - (line 12) stdout ---- + +thread 'main' panicked at $TMP:6:1: +assertion `left == right` failed + left: 4 + right: 5 +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace + + +failures: + $DIR/edition-2024-error-output.rs - (line 12) + +test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/ui/autodiff/autodiff_illegal.rs b/tests/ui/autodiff/autodiff_illegal.rs index e810b9ba565ba..2f2cd8d93532f 100644 --- a/tests/ui/autodiff/autodiff_illegal.rs +++ b/tests/ui/autodiff/autodiff_illegal.rs @@ -177,4 +177,11 @@ fn f21(x: f32) -> f32 { unimplemented!() } +struct DoesNotImplDefault; +#[autodiff(df22, Forward, Dual)] +pub fn f22() -> DoesNotImplDefault { + //~^^ ERROR the function or associated item `default` exists for tuple `(DoesNotImplDefault, DoesNotImplDefault)`, but its trait bounds were not satisfied + unimplemented!() +} + fn main() {} diff --git a/tests/ui/autodiff/autodiff_illegal.stderr b/tests/ui/autodiff/autodiff_illegal.stderr index 47d53492700ba..3752b27e7dd1b 100644 --- a/tests/ui/autodiff/autodiff_illegal.stderr +++ b/tests/ui/autodiff/autodiff_illegal.stderr @@ -19,32 +19,24 @@ error: expected 1 activities, but found 2 | LL | #[autodiff(df3, Reverse, Duplicated, Const)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the attribute macro `autodiff` (in Nightly builds, run with -Z macro-backtrace for more info) error: expected 1 activities, but found 0 --> $DIR/autodiff_illegal.rs:27:1 | LL | #[autodiff(df4, Reverse)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the attribute macro `autodiff` (in Nightly builds, run with -Z macro-backtrace for more info) error: Dual can not be used in Reverse Mode --> $DIR/autodiff_illegal.rs:34:1 | LL | #[autodiff(df5, Reverse, Dual)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the attribute macro `autodiff` (in Nightly builds, run with -Z macro-backtrace for more info) error: Duplicated can not be used in Forward Mode --> $DIR/autodiff_illegal.rs:41:1 | LL | #[autodiff(df6, Forward, Duplicated)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the attribute macro `autodiff` (in Nightly builds, run with -Z macro-backtrace for more info) error: Duplicated can not be used for this type --> $DIR/autodiff_illegal.rs:42:14 @@ -107,7 +99,6 @@ LL | #[autodiff(fn_exists, Reverse, Active)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `fn_exists` redefined here | = note: `fn_exists` must be defined only once in the value namespace of this module - = note: this error originates in the attribute macro `autodiff` (in Nightly builds, run with -Z macro-backtrace for more info) error: autodiff requires at least a name and mode --> $DIR/autodiff_illegal.rs:95:1 @@ -135,42 +126,49 @@ error: invalid return activity Active in Forward Mode | LL | #[autodiff(df19, Forward, Dual, Active)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the attribute macro `autodiff` (in Nightly builds, run with -Z macro-backtrace for more info) error: invalid return activity Dual in Reverse Mode --> $DIR/autodiff_illegal.rs:167:1 | LL | #[autodiff(df20, Reverse, Active, Dual)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the attribute macro `autodiff` (in Nightly builds, run with -Z macro-backtrace for more info) error: invalid return activity Duplicated in Reverse Mode --> $DIR/autodiff_illegal.rs:174:1 | LL | #[autodiff(df21, Reverse, Active, Duplicated)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this error originates in the attribute macro `autodiff` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0433]: failed to resolve: use of undeclared type `MyFloat` --> $DIR/autodiff_illegal.rs:130:1 | LL | #[autodiff(df15, Reverse, Active, Active)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use of undeclared type `MyFloat` - | - = note: this error originates in the attribute macro `autodiff` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0433]: failed to resolve: use of undeclared type `F64Trans` --> $DIR/autodiff_illegal.rs:154:1 | LL | #[autodiff(df18, Reverse, Active, Active)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use of undeclared type `F64Trans` + +error[E0599]: the function or associated item `default` exists for tuple `(DoesNotImplDefault, DoesNotImplDefault)`, but its trait bounds were not satisfied + --> $DIR/autodiff_illegal.rs:181:1 + | +LL | struct DoesNotImplDefault; + | ------------------------- doesn't satisfy `DoesNotImplDefault: Default` +LL | #[autodiff(df22, Forward, Dual)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function or associated item cannot be called on `(DoesNotImplDefault, DoesNotImplDefault)` due to unsatisfied trait bounds + | + = note: the following trait bounds were not satisfied: + `DoesNotImplDefault: Default` + which is required by `(DoesNotImplDefault, DoesNotImplDefault): Default` +help: consider annotating `DoesNotImplDefault` with `#[derive(Default)]` + | +LL + #[derive(Default)] +LL | struct DoesNotImplDefault; | - = note: this error originates in the attribute macro `autodiff` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 22 previous errors +error: aborting due to 23 previous errors -Some errors have detailed explanations: E0428, E0433, E0658. +Some errors have detailed explanations: E0428, E0433, E0599, E0658. For more information about an error, try `rustc --explain E0428`.