diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index d2e3c00bb0c07..dc6bc0a8123e9 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -466,7 +466,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { let spans = sess.psess.gated_spans.spans.borrow(); macro_rules! gate_all { ($feature:ident, $explain:literal $(, $help:literal)?) => { - for &span in spans.get(&sym::$feature).into_iter().flatten() { + for &span in spans.get(&sym::$feature).into_flat_iter() { gate!(visitor, $feature, span, $explain $(, $help)?); } }; @@ -527,13 +527,13 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { ); // `associated_const_equality` will be stabilized as part of `min_generic_const_args`. - for &span in spans.get(&sym::associated_const_equality).into_iter().flatten() { + for &span in spans.get(&sym::associated_const_equality).into_flat_iter() { gate!(visitor, min_generic_const_args, span, "associated const equality is incomplete"); } // `mgca_type_const_syntax` is part of `min_generic_const_args` so if // either or both are enabled we don't need to emit a feature error. - for &span in spans.get(&sym::mgca_type_const_syntax).into_iter().flatten() { + for &span in spans.get(&sym::mgca_type_const_syntax).into_flat_iter() { if visitor.features.min_generic_const_args() || visitor.features.mgca_type_const_syntax() || span.allows_unstable(sym::min_generic_const_args) @@ -561,13 +561,13 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { // it does **not** mean "`T` doesn't implement `Bound` (positively or negatively)"! // The latter would be a SemVer hazard! if !sess.opts.unstable_opts.internal_testing_features || !visitor.features.negative_bounds() { - for &span in spans.get(&sym::negative_bounds).into_iter().flatten() { + for &span in spans.get(&sym::negative_bounds).into_flat_iter() { sess.dcx().emit_err(diagnostics::NegativeBoundUnsupported { span }); } } if !visitor.features.never_patterns() { - for &span in spans.get(&sym::never_patterns).into_iter().flatten() { + for &span in spans.get(&sym::never_patterns).into_flat_iter() { if span.allows_unstable(sym::never_patterns) { continue; } @@ -585,7 +585,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { } // Yield exprs can be enabled either by `yield_expr`, by `coroutines` or by `gen_blocks`. - for &span in spans.get(&sym::yield_expr).into_iter().flatten() { + for &span in spans.get(&sym::yield_expr).into_flat_iter() { if (!visitor.features.coroutines() && !span.allows_unstable(sym::coroutines)) && (!visitor.features.gen_blocks() && !span.allows_unstable(sym::gen_blocks)) && (!visitor.features.yield_expr() && !span.allows_unstable(sym::yield_expr)) @@ -607,7 +607,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { macro_rules! soft_gate_all_legacy_dont_use { ($feature:ident, $explain:literal) => { - for &span in spans.get(&sym::$feature).into_iter().flatten() { + for &span in spans.get(&sym::$feature).into_flat_iter() { if !visitor.features.$feature() && !span.allows_unstable(sym::$feature) { feature_warn(&visitor.sess, sym::$feature, span, $explain); } @@ -625,7 +625,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { soft_gate_all_legacy_dont_use!(try_blocks, "`try` blocks are unstable"); // tidy-alphabetical-end - for &span in spans.get(&sym::min_specialization).into_iter().flatten() { + for &span in spans.get(&sym::min_specialization).into_flat_iter() { if !visitor.features.specialization() && !visitor.features.min_specialization() && !span.allows_unstable(sym::specialization) diff --git a/compiler/rustc_ast_passes/src/lib.rs b/compiler/rustc_ast_passes/src/lib.rs index 86ad758807584..918a623b41752 100644 --- a/compiler/rustc_ast_passes/src/lib.rs +++ b/compiler/rustc_ast_passes/src/lib.rs @@ -6,6 +6,7 @@ #![feature(deref_patterns)] #![feature(iter_intersperse)] #![feature(iter_is_partitioned)] +#![feature(option_into_flat_iter)] // tidy-alphabetical-end pub mod ast_validation; diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_type_error.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_type_error.rs index 89cae4b7c55f1..38c1f9ab6c945 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_type_error.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_type_error.rs @@ -18,6 +18,8 @@ pub(crate) struct OnTypeErrorParser { impl OnTypeErrorParser { fn parse<'sess>(&mut self, cx: &mut AcceptContext<'_, 'sess>, args: &ArgParser, mode: Mode) { if !cx.features().diagnostic_on_type_error() { + // `UnknownDiagnosticAttribute` is emitted in rustc_resolve/macros.rs + args.ignore_args(); return; } diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index 42af99fc64d81..0bc06ade95ea1 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -476,9 +476,8 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> { .borrow_set .local_map .get(&place.local) - .into_iter() - .flat_map(|bs| bs.iter()) - .copied(); + .map(|bs| bs.iter().copied()) + .into_flat_iter(); // If the borrowed place is a local with no projections, all other borrows of this // local must conflict. This is purely an optimization so we don't have to call diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 22e7d83e5b509..9d66863ef70e1 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -7,6 +7,7 @@ #![feature(file_buffered)] #![feature(negative_impls)] #![feature(never_type)] +#![feature(option_into_flat_iter)] #![feature(rustc_attrs)] #![feature(stmt_expr_attributes)] #![feature(try_blocks)] diff --git a/compiler/rustc_borrowck/src/polonius/constraints.rs b/compiler/rustc_borrowck/src/polonius/constraints.rs index 559b1bdc38d83..2808cbee99fb0 100644 --- a/compiler/rustc_borrowck/src/polonius/constraints.rs +++ b/compiler/rustc_borrowck/src/polonius/constraints.rs @@ -152,7 +152,7 @@ impl LocalizedConstraintGraph { // The physical edges present at this node are: // // 1. the typeck edges that flow from region to region *at this point*. - for &succ in self.edges.get(&node).into_iter().flatten() { + for &succ in self.edges.get(&node).into_flat_iter() { let succ = LocalizedNode { region: succ, point: node.point }; successor_found(succ); } @@ -229,7 +229,7 @@ impl LocalizedConstraintGraph { } // And finally, we have the logical edges, materialized at this point. - for &logical_succ in self.logical_edges.get(&node.region).into_iter().flatten() { + for &logical_succ in self.logical_edges.get(&node.region).into_flat_iter() { let succ = LocalizedNode { region: logical_succ, point: node.point }; successor_found(succ); } diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types/member_constraints.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types/member_constraints.rs index db38879ea1d9b..e7e05e561d13f 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types/member_constraints.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types/member_constraints.rs @@ -49,7 +49,7 @@ pub(super) fn apply_member_constraints<'tcx>( rcx.scc_values.add_region(scc_a, scc_b); } - for defining_use in member_constraints.get(&scc_a).into_iter().flatten() { + for defining_use in member_constraints.get(&scc_a).into_flat_iter() { apply_member_constraint(rcx, scc_a, &defining_use.arg_regions); } } diff --git a/compiler/rustc_borrowck/src/region_infer/values.rs b/compiler/rustc_borrowck/src/region_infer/values.rs index e96dc44fab7c4..841e5713751cd 100644 --- a/compiler/rustc_borrowck/src/region_infer/values.rs +++ b/compiler/rustc_borrowck/src/region_infer/values.rs @@ -178,7 +178,7 @@ impl LivenessValues { /// Returns an iterator of all the points where `region` is live. fn live_points(&self, region: RegionVid) -> impl Iterator { - self.point_liveness(region).into_iter().flat_map(|set| set.iter()) + self.point_liveness(region).map(|set| set.iter()).into_flat_iter() } /// For debugging purposes, returns a pretty-printed string of the points where the `region` is @@ -348,13 +348,13 @@ impl<'tcx, N: Idx> RegionValues<'tcx, N> { pub(crate) fn locations_outlived_by(&self, r: N) -> impl Iterator { self.points .row(r) - .into_iter() - .flat_map(move |set| set.iter().map(move |p| self.location_map.to_location(p))) + .map(move |set| set.iter().map(move |p| self.location_map.to_location(p))) + .into_flat_iter() } /// Returns just the universal regions that are contained in a given region's value. pub(crate) fn universal_regions_outlived_by(&self, r: N) -> impl Iterator { - self.free_regions.row(r).into_iter().flat_map(|set| set.iter()) + self.free_regions.row(r).map(|set| set.iter()).into_flat_iter() } /// Returns all the elements contained in a given region's value. @@ -364,8 +364,8 @@ impl<'tcx, N: Idx> RegionValues<'tcx, N> { ) -> impl Iterator> { self.placeholders .row(r) - .into_iter() - .flat_map(|set| set.iter()) + .map(|set| set.iter()) + .into_flat_iter() .map(move |p| self.placeholder_indices.lookup_placeholder(p)) } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 123bc1f568495..a0a2d1c135aa3 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -928,7 +928,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { self.super_local_decl(local, local_decl); for user_ty in - local_decl.user_ty.as_deref().into_iter().flat_map(UserTypeProjections::projections) + local_decl.user_ty.as_deref().map(UserTypeProjections::projections).into_flat_iter() { let span = self.user_type_annotations[user_ty.base].span; diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 7bd604bdbbd76..7a437295ac054 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -3,8 +3,8 @@ use std::ffi::c_uint; use std::{assert_matches, iter, ptr}; use rustc_abi::{ - AddressSpace, Align, BackendRepr, CVariadicStatus, Float, HasDataLayout, Integer, - NumScalableVectors, Primitive, Size, WrappingRange, + AddressSpace, Align, BackendRepr, CVariadicStatus, Float, HasDataLayout, NumScalableVectors, + Primitive, Size, WrappingRange, }; use rustc_codegen_ssa::RetagInfo; use rustc_codegen_ssa::base::{compare_simd_types, wants_msvc_seh, wants_wasm_eh}; @@ -315,11 +315,6 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { Primitive::Pointer(_) => { // Pointers are always OK. } - Primitive::Int(Integer::I128, _) => { - // FIXME: maybe we should support these? At least on 32-bit powerpc - // the logic in LLVM does not handle i128 correctly though. - bug!("the va_arg intrinsic does not support `i128`/`u128`") - } Primitive::Int(..) => { let int_width = self.cx().size_of(result_layout.ty).bits(); let target_c_int_width = self.cx().sess().target.options.c_int_width; diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index b8d1cd3504517..d148036bd3390 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -1,5 +1,6 @@ -use rustc_abi::{Align, BackendRepr, CVariadicStatus, Endian, HasDataLayout, Primitive, Size}; -use rustc_codegen_ssa::MemFlags; +use rustc_abi::{ + Align, BackendRepr, CVariadicStatus, Endian, Float, HasDataLayout, Integer, Primitive, Size, +}; use rustc_codegen_ssa::common::IntPredicate; use rustc_codegen_ssa::mir::operand::OperandRef; use rustc_codegen_ssa::traits::{ @@ -77,6 +78,35 @@ fn emit_direct_ptr_va_arg<'ll, 'tcx>( } } +/// Some backends apply special alignment rules to c-variadic arguments. +fn get_param_type_alignment<'ll, 'tcx>( + bx: &mut Builder<'_, 'll, 'tcx>, + layout: TyAndLayout<'tcx>, +) -> Align { + let BackendRepr::Scalar(scalar) = layout.backend_repr else { + bug!("unexpected backend repr {:?}", layout.backend_repr); + }; + + match bx.cx.tcx.sess.target.arch { + Arch::PowerPC64 => match scalar.primitive() { + Primitive::Int(integer, _) => match integer { + Integer::I8 | Integer::I16 => unreachable!(), + Integer::I32 | Integer::I64 => { /* fall through */ } + Integer::I128 => return Align::EIGHT, + }, + Primitive::Float(float) => match float { + Float::F16 | Float::F32 => unreachable!(), + Float::F64 => { /* fall through */ } + Float::F128 => return Align::from_bytes(16).unwrap(), + }, + Primitive::Pointer(_) => { /* fall through */ } + }, + _ => { /* fall through */ } + } + + layout.align.abi +} + enum PassMode { Direct, Indirect, @@ -136,23 +166,23 @@ fn emit_ptr_va_arg<'ll, 'tcx>( ( bx.cx.layout_of(Ty::new_imm_ptr(bx.cx.tcx, target_ty)).llvm_type(bx.cx), bx.cx.data_layout().pointer_size(), - bx.cx.data_layout().pointer_align(), + bx.cx.data_layout().pointer_align().abi, ) } else { - (layout.llvm_type(bx.cx), layout.size, layout.align) + (layout.llvm_type(bx.cx), layout.size, get_param_type_alignment(bx, layout)) }; let (addr, addr_align) = emit_direct_ptr_va_arg( bx, list, size, - align.abi, + align, slot_size, allow_higher_align, force_right_adjust, ); if indirect { let tmp_ret = bx.load(llty, addr, addr_align); - bx.load(layout.llvm_type(bx.cx), tmp_ret, align.abi) + bx.load(layout.llvm_type(bx.cx), tmp_ret, align) } else { bx.load(llty, addr, addr_align) } @@ -585,8 +615,10 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>( // registers. In the case: l->gp_offset > 48 - num_gp * 8 or // l->fp_offset > 176 - num_fp * 16 go to step 7. + // We support x86_64-unknown-linux-gnux32 which uses 4-byte pointers. let unsigned_int_offset = 4; - let ptr_offset = 8; + let ptr_offset = bx.tcx().data_layout.pointer_size().bytes(); + let gp_offset_ptr = va_list_addr; let fp_offset_ptr = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(unsigned_int_offset)); @@ -660,7 +692,7 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>( let reg_hi_addr = bx.inbounds_ptradd(reg_lo_addr, bx.const_i32(16)); let align = layout.layout.align().abi; - let tmp = bx.alloca(layout.layout.size(), align); + let tmp = bx.alloca(layout.size, layout.align.abi); let reg_lo = bx.load(ty_lo, reg_lo_addr, align_lo); let reg_hi = bx.load(ty_hi, reg_hi_addr, align_hi); @@ -683,7 +715,7 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>( Primitive::Int(_, _) | Primitive::Pointer(_) => (gp_addr, fp_addr), }; - let tmp = bx.alloca(layout.layout.size(), layout.layout.align().abi); + let tmp = bx.alloca(layout.size, layout.align.abi); let reg_lo = bx.load(ty_lo, reg_lo_addr, align_lo); let reg_hi = bx.load(ty_hi, reg_hi_addr, align_hi); @@ -751,16 +783,12 @@ fn copy_to_temporary_if_more_aligned<'ll, 'tcx>( src_align: Align, ) -> &'ll Value { if layout.layout.align.abi > src_align { - let tmp = bx.alloca(layout.layout.size(), layout.layout.align().abi); - bx.memcpy( - tmp, - layout.layout.align.abi, - reg_addr, - src_align, - bx.const_u32(layout.layout.size().bytes() as u32), - MemFlags::empty(), - None, - ); + assert!(layout.ty.is_integral()); + + // A memcpy below optimizes poorly for 128-bit integers. + let tmp = bx.alloca(layout.size, layout.align.abi); + let val = bx.load(layout.llvm_type(bx), reg_addr, src_align); + bx.store(val, tmp, layout.align.abi); tmp } else { reg_addr @@ -782,9 +810,14 @@ fn x86_64_sysv64_va_arg_from_memory<'ll, 'tcx>( // byte boundary if alignment needed by type exceeds 8 byte boundary. // It isn't stated explicitly in the standard, but in practice we use // alignment greater than 16 where necessary. - if layout.layout.align.bytes() > 8 { - unreachable!("all instances of VaArgSafe have an alignment <= 8"); - } + // The AMD64 psABI leaves unspecified what to do for alignments above 16, but + // this behavior for 32+ alignment matches clang. + // It currently (2026 July) can only occur for 16-byte-aligned types. + let overflow_arg_area_v = if layout.layout.align.bytes() > 8 { + round_pointer_up_to_alignment(bx, overflow_arg_area_v, layout.layout.align.abi) + } else { + overflow_arg_area_v + }; // AMD64-ABI 3.5.7p5: Step 8. Fetch type from l->overflow_arg_area. let mem_addr = overflow_arg_area_v; @@ -1071,7 +1104,8 @@ pub(super) fn emit_va_arg<'ll, 'tcx>( Arch::AArch64 => emit_aapcs_va_arg(bx, addr, target_ty), Arch::Arm => { // Types wider than 16 bytes are not currently supported. Clang has special logic for - // such types, but `VaArgSafe` is not implemented for any type that is this large. + // such types, but `VaArgSafe` is not implemented for any type that is this large on + // arm (i.e. 32-bit) targets. assert!(bx.cx.size_of(target_ty).bytes() <= 16); emit_ptr_va_arg( @@ -1093,6 +1127,7 @@ pub(super) fn emit_va_arg<'ll, 'tcx>( PassMode::Direct, SlotSize::Bytes8, AllowHigherAlign::Yes, + // ForceRightAdjust only takes effect on big-endian architectures. ForceRightAdjust::Yes, ), Arch::RiscV32 if target.llvm_abiname == LlvmAbi::Ilp32e => { diff --git a/compiler/rustc_codegen_ssa/src/assert_module_sources.rs b/compiler/rustc_codegen_ssa/src/assert_module_sources.rs index 39123b8bade59..f34a7b956e040 100644 --- a/compiler/rustc_codegen_ssa/src/assert_module_sources.rs +++ b/compiler/rustc_codegen_ssa/src/assert_module_sources.rs @@ -90,8 +90,7 @@ impl<'tcx> AssertModuleSource<'tcx> { fn check_attrs(&mut self, attrs: &[hir::Attribute]) { for &(span, cgu_fields) in find_attr!(attrs, RustcCguTestAttr(e) => e) - .into_iter() - .flatten() + .into_flat_iter() { let (expected_reuse, comp_kind) = match cgu_fields { CguFields::PartitionReused { .. } => (CguReuse::PreLto, ComparisonKind::AtLeast), diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index c41f6f30a1da3..3d09b7aab8854 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1091,9 +1091,7 @@ fn link_natively( let get_objects = |objects: &CrtObjects, kind| { objects .get(&kind) - .iter() - .copied() - .flatten() + .into_flat_iter() .map(|obj| { get_object_file_path(sess, obj, self_contained_crt_objects).into_os_string() }) @@ -2073,7 +2071,7 @@ fn add_pre_link_objects( } else { &empty }; - for obj in objects.get(&link_output_kind).iter().copied().flatten() { + for obj in objects.get(&link_output_kind).into_flat_iter() { cmd.add_object(&get_object_file_path(sess, obj, self_contained)); } } @@ -2090,7 +2088,7 @@ fn add_post_link_objects( } else { &sess.target.post_link_objects }; - for obj in objects.get(&link_output_kind).iter().copied().flatten() { + for obj in objects.get(&link_output_kind).into_flat_iter() { cmd.add_object(&get_object_file_path(sess, obj, self_contained)); } } diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index f3f19f9e90d83..200f6aac12502 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -2,6 +2,7 @@ #![feature(deref_patterns)] #![feature(file_buffered)] #![feature(negative_impls)] +#![feature(option_into_flat_iter)] #![feature(string_from_utf8_lossy_owned)] #![feature(trait_alias)] #![feature(try_blocks)] diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 9d8e0a512ab66..f0c5f6b85423c 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -479,10 +479,9 @@ fn fn_sig_suggestion<'tcx>( } } }; - Some(format!("{splat}{arg_ty}")) + format!("{splat}{arg_ty}") }) - .chain(std::iter::once(if sig.c_variadic() { Some("...".to_string()) } else { None })) - .flatten() + .chain(if sig.c_variadic() { Some("...".to_string()) } else { None }) .collect::>() .join(", "); let mut output = sig.output(); diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 1d17621be217f..f2a6c2747f380 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1217,7 +1217,7 @@ fn check_eiis_fn(tcx: TyCtxt<'_>, def_id: LocalDefId) { // does the function have an EiiImpl attribute? that contains the defid of a *macro* // that was used to mark the implementation. This is a two step process. for EiiImpl { resolution, span, .. } in - find_attr!(tcx, def_id, EiiImpls(impls) => impls).into_iter().flatten() + find_attr!(tcx, def_id, EiiImpls(impls) => impls).into_flat_iter() { let (foreign_item, name) = match resolution { EiiImplResolution::Macro(def_id) => { @@ -1244,7 +1244,7 @@ fn check_eiis_static<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, ty: Ty<'tcx>) // does the function have an EiiImpl attribute? that contains the defid of a *macro* // that was used to mark the implementation. This is a two step process. for EiiImpl { resolution, span, .. } in - find_attr!(tcx, def_id, EiiImpls(impls) => impls).into_iter().flatten() + find_attr!(tcx, def_id, EiiImpls(impls) => impls).into_flat_iter() { let (foreign_item, name) = match resolution { EiiImplResolution::Macro(def_id) => { diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index ff00e18783706..a6f55eb8dea72 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -60,6 +60,7 @@ This API is completely unstable and subject to change. #![feature(gen_blocks)] #![feature(iter_intersperse)] #![feature(never_type)] +#![feature(option_into_flat_iter)] #![feature(slice_partition_dedup)] #![feature(try_blocks)] #![feature(unwrap_infallible)] diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index f7a58d278a519..4c843eefd5c4c 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -673,7 +673,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { let mut coercion = self.unify_and( coerce_target, target, - reborrow.into_iter().flat_map(|(deref, autoref)| [deref, autoref]), + reborrow.map(|(deref, autoref)| [deref, autoref]).into_flat_iter(), Adjust::Pointer(PointerCoercion::Unsize), ForceLeakCheck::No, )?; diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 3b3f5caa85c09..335caf571aa6f 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -3,6 +3,7 @@ #![feature(iter_intersperse)] #![feature(iter_order_by)] #![feature(never_type)] +#![feature(option_into_flat_iter)] #![feature(option_reference_flattening)] #![feature(trim_prefix_suffix)] // tidy-alphabetical-end diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index e7116b7492584..0a72f1d47b8b8 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -266,7 +266,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // NOTE: Reporting a method error should also suppress any unused trait errors, // since the method error is very possibly the reason why the trait wasn't used. for &import_id in - self.tcx.in_scope_traits(call_id).into_iter().flatten().flat_map(|c| c.import_ids) + self.tcx.in_scope_traits(call_id).into_flat_iter().flat_map(|c| c.import_ids) { self.typeck_results.borrow_mut().used_trait_imports.insert(import_id); } diff --git a/compiler/rustc_lint/src/if_let_rescope.rs b/compiler/rustc_lint/src/if_let_rescope.rs index 97b7cdc135142..60ee55e299d87 100644 --- a/compiler/rustc_lint/src/if_let_rescope.rs +++ b/compiler/rustc_lint/src/if_let_rescope.rs @@ -349,8 +349,7 @@ impl Subdiagnostic for IfLetRescopeRewrite { closing_brackets .empty_alt .then_some(" _ => {}".chars()) - .into_iter() - .flatten() + .into_flat_iter() .chain(repeat_n('}', closing_brackets.count)) .collect(), )); diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 0e96b9f118790..eaf9360cc3358 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -23,6 +23,7 @@ #![allow(internal_features)] #![feature(deref_patterns)] #![feature(iter_order_by)] +#![feature(option_into_flat_iter)] #![feature(rustc_attrs)] #![feature(titlecase)] #![feature(try_blocks)] diff --git a/compiler/rustc_metadata/src/eii.rs b/compiler/rustc_metadata/src/eii.rs index f3ce07aa75a11..11ea4cc492921 100644 --- a/compiler/rustc_metadata/src/eii.rs +++ b/compiler/rustc_metadata/src/eii.rs @@ -25,7 +25,7 @@ pub(crate) fn collect<'tcx>(tcx: TyCtxt<'tcx>, LocalCrate: LocalCrate) -> EiiMap // iterate over all items in the current crate for id in tcx.hir_crate_items(()).eiis() { - for i in find_attr!(tcx, id, EiiImpls(e) => e).into_iter().flatten() { + for i in find_attr!(tcx, id, EiiImpls(e) => e).into_flat_iter() { let decl = match i.resolution { EiiImplResolution::Macro(macro_defid) => { // find the decl for this one if it wasn't in yet (maybe it's from the local crate? not very useful but not illegal) diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs index 840b1c4c6fa2f..9ef9422a9e5a0 100644 --- a/compiler/rustc_metadata/src/lib.rs +++ b/compiler/rustc_metadata/src/lib.rs @@ -8,6 +8,7 @@ #![feature(macro_metavar_expr)] #![feature(min_specialization)] #![feature(never_type)] +#![feature(option_into_flat_iter)] #![feature(proc_macro_internals)] #![feature(trusted_len)] // tidy-alphabetical-end diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index 874d4812502e6..f970be089f2ee 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -325,9 +325,8 @@ impl<'a> CrateLocator<'a> { sess.opts .externs .get(crate_name.as_str()) - .into_iter() - .filter_map(|entry| entry.files()) - .flatten() + .and_then(|entry| entry.files()) + .into_flat_iter() .cloned() .collect() } else { diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index c9e4ce60e1a01..b2cfc2f079e89 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -248,9 +248,7 @@ impl<'tcx> Collector<'tcx> { return; } - for attr in - find_attr!(self.tcx, def_id, Link(links, _) => links).iter().map(|v| v.iter()).flatten() - { + for attr in find_attr!(self.tcx, def_id, Link(links, _) => links).into_flat_iter() { let dll_imports = match attr.kind { NativeLibKind::RawDylib { .. } => foreign_items .iter() diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index bd3b2445759e7..bed2be51a3468 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -2038,10 +2038,12 @@ impl CrateMetadata { krate: CrateNum, ) -> impl Iterator { gen move { - for def_id in self.root.proc_macro_data.as_ref().into_iter().flat_map(move |data| { - data.macros.decode((self, tcx)).map(move |(index, _)| DefId { index, krate }) - }) { - yield def_id; + if let Some(data) = &self.root.proc_macro_data { + for def_id in + data.macros.decode((self, tcx)).map(move |(index, _)| DefId { index, krate }) + { + yield def_id; + } } } } diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 3ab2690c7a6f4..da7ad32ae9dc0 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -46,6 +46,7 @@ #![feature(min_specialization)] #![feature(negative_impls)] #![feature(never_type)] +#![feature(option_into_flat_iter)] #![feature(ptr_alignment_type)] #![feature(range_bounds_is_empty)] #![feature(rustc_attrs)] diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 0042c87c3eda7..ad3b8e009320d 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -769,25 +769,22 @@ impl<'tcx> Ty<'tcx> { .projection_bounds() .filter(|item| !tcx.generics_require_sized_self(item.item_def_id())) .count(); - let expected_count: usize = obj - .principal_def_id() - .into_iter() - .flat_map(|principal_def_id| { - // IMPORTANT: This has to agree with HIR ty lowering of dyn trait! - elaborate::supertraits( - tcx, - ty::Binder::dummy(ty::TraitRef::identity(tcx, principal_def_id)), - ) - .map(|principal| { - tcx.associated_items(principal.def_id()) - .in_definition_order() - .filter(|item| item.is_type() || item.is_type_const()) - .filter(|item| !item.is_impl_trait_in_trait()) - .filter(|item| !tcx.generics_require_sized_self(item.def_id)) - .count() - }) + let expected_count: usize = obj.principal_def_id().map_or(0, |principal_def_id| { + // IMPORTANT: This has to agree with HIR ty lowering of dyn trait! + elaborate::supertraits( + tcx, + ty::Binder::dummy(ty::TraitRef::identity(tcx, principal_def_id)), + ) + .map(|principal| { + tcx.associated_items(principal.def_id()) + .in_definition_order() + .filter(|item| item.is_type() || item.is_type_const()) + .filter(|item| !item.is_impl_trait_in_trait()) + .filter(|item| !tcx.generics_require_sized_self(item.def_id)) + .count() }) - .sum(); + .sum() + }); assert_eq!( projection_count, expected_count, "expected {obj:?} to have {expected_count} projections, \ diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 77820beef0228..65a713602ed07 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -537,8 +537,8 @@ impl<'tcx> TypeckResults<'tcx> { ) -> impl Iterator> { self.closure_min_captures .get(&closure_def_id) - .map(|closure_min_captures| closure_min_captures.values().flat_map(|v| v.iter())) - .into_iter() + .map(|closure_min_captures| closure_min_captures.values()) + .into_flat_iter() .flatten() } diff --git a/compiler/rustc_mir_transform/src/coverage/counters/balanced_flow.rs b/compiler/rustc_mir_transform/src/coverage/counters/balanced_flow.rs index 4c20722a04347..616d24d87b719 100644 --- a/compiler/rustc_mir_transform/src/coverage/counters/balanced_flow.rs +++ b/compiler/rustc_mir_transform/src/coverage/counters/balanced_flow.rs @@ -126,6 +126,6 @@ where sink_edge = self.sink_edge_nodes.contains(node).then_some(self.sink); } - real_edges.into_iter().flatten().chain(sink_edge) + real_edges.into_flat_iter().chain(sink_edge) } } diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index edf0dcca775ca..2bb14589db36a 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -4,6 +4,7 @@ #![feature(deref_patterns)] #![feature(impl_trait_in_assoc_type)] #![feature(iterator_try_collect)] +#![feature(option_into_flat_iter)] #![feature(try_blocks)] #![feature(yeet_expr)] // tidy-alphabetical-end diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs index ea26d4043b25f..66cc83b41ac3a 100644 --- a/compiler/rustc_passes/src/lib.rs +++ b/compiler/rustc_passes/src/lib.rs @@ -4,6 +4,10 @@ //! //! This API is completely unstable and subject to change. +// tidy-alphabetical-start +#![feature(option_into_flat_iter)] +// tidy-alphabetical-end + use rustc_middle::query::Providers; pub mod abi_test; diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 14f631b84a781..bf5a1dc2ec4b8 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -1112,8 +1112,7 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { .iter() .flat_map(|&cnum| { find_attr!(tcx, cnum.as_def_id(), UnstableRemoved(rem_features) => rem_features) - .into_iter() - .flatten() + .into_flat_iter() }) .collect::>(); diff --git a/compiler/rustc_resolve/src/error_helper.rs b/compiler/rustc_resolve/src/error_helper.rs index c8abc3556f8fe..8ab193afe0eee 100644 --- a/compiler/rustc_resolve/src/error_helper.rs +++ b/compiler/rustc_resolve/src/error_helper.rs @@ -1469,13 +1469,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Scope::DeriveHelpers(expn_id) => { let res = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper); if filter_fn(res) { - suggestions.extend( - this.helper_attrs.get(&expn_id).into_iter().flatten().map( - |&(ident, orig_ident_span, _)| { - TypoSuggestion::new(ident.name, orig_ident_span, res) - }, - ), - ); + suggestions.extend(this.helper_attrs.get(&expn_id).into_flat_iter().map( + |&(ident, orig_ident_span, _)| { + TypoSuggestion::new(ident.name, orig_ident_span, res) + }, + )); } } Scope::DeriveHelpersCompat => { diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 3b93f2d1fd130..ca0704a9f4352 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -15,6 +15,7 @@ #![feature(default_field_values)] #![feature(deref_patterns)] #![feature(iter_intersperse)] +#![feature(option_into_flat_iter)] #![feature(rustc_attrs)] #![feature(trim_prefix_suffix)] #![recursion_limit = "256"] diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index c5b7a5e8450da..cc83867dc1a9e 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -6,6 +6,7 @@ #![feature(iter_intersperse)] #![feature(macro_derive)] #![feature(macro_metavar_expr)] +#![feature(option_into_flat_iter)] #![feature(rustc_attrs)] // To generate CodegenOptionsTargetModifiers and UnstableOptionsTargetModifiers enums // with macro_rules, it is necessary to use recursive mechanic ("Incremental TT Munchers"). diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 4e5b38d34b824..e8e405f4a3df0 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1621,7 +1621,7 @@ pub mod parse { let mut seen_instruction_threshold = false; let mut seen_skip_entry = false; let mut seen_skip_exit = false; - for option in v.into_iter().flat_map(|v| v.split(',')) { + for option in v.map(|v| v.split(',')).into_flat_iter() { match option { "always" if !seen_always && !seen_never => { options.always = true; diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index b0fda96078c54..139aa6bd77a87 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -1613,7 +1613,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { _ => None, }; - [typeck_results.expr_ty_adjusted_opt(expr)].into_iter().flatten().any(|expr_ty| { + typeck_results.expr_ty_adjusted_opt(expr).is_some_and(|expr_ty| { self.can_eq(obligation.param_env, expr_ty, old_self_ty) || inner_old_self_ty .is_some_and(|inner_ty| self.can_eq(obligation.param_env, expr_ty, inner_ty)) diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index 8900687036d41..463555c456264 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -19,6 +19,7 @@ #![feature(iter_intersperse)] #![feature(iterator_try_reduce)] #![feature(never_type)] +#![feature(option_into_flat_iter)] #![feature(try_blocks)] #![feature(unwrap_infallible)] #![feature(yeet_expr)] diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs index 098dcfb30a7b6..0aa7dc79ea121 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs @@ -264,8 +264,7 @@ impl<'tcx> BestObligation<'tcx> { let body_id = self.obligation.cause.body_id; for obligation in wf::unnormalized_obligations(infcx, param_env, term, self.span(), body_id) - .into_iter() - .flatten() + .into_flat_iter() { let nested_goal = candidate.instantiate_proof_tree_for_nested_goal( GoalSource::Misc, diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index 233e124fc60f5..a3a73ac56b68a 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -98,8 +98,7 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( // From the full set of obligations, just filter down to the region relationships. for obligation in wf::unnormalized_obligations(ocx.infcx, param_env, arg, DUMMY_SP, CRATE_DEF_ID) - .into_iter() - .flatten() + .into_flat_iter() { let pred = ocx .deeply_normalize( diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index b278c32cb61ae..90fdc3ff939d2 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -1030,10 +1030,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // supertraits. let a_auto_traits: FxIndexSet = a_data .auto_traits() - .chain(principal_def_id_a.into_iter().flat_map(|principal_def_id| { - elaborate::supertrait_def_ids(self.tcx(), principal_def_id) - .filter(|def_id| self.tcx().trait_is_auto(*def_id)) - })) + .chain( + principal_def_id_a + .map(|principal_def_id| { + elaborate::supertrait_def_ids(self.tcx(), principal_def_id) + .filter(|def_id| self.tcx().trait_is_auto(*def_id)) + }) + .into_flat_iter(), + ) .collect(); let auto_traits_compatible = b_data .auto_traits() diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 73dc9bff6343d..f407bec8df60d 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -2594,10 +2594,15 @@ impl<'tcx> SelectionContext<'_, 'tcx> { // supertraits. let a_auto_traits: FxIndexSet = a_data .auto_traits() - .chain(a_data.principal_def_id().into_iter().flat_map(|principal_def_id| { - elaborate::supertrait_def_ids(tcx, principal_def_id) - .filter(|def_id| tcx.trait_is_auto(*def_id)) - })) + .chain( + a_data + .principal_def_id() + .map(|principal_def_id| { + elaborate::supertrait_def_ids(tcx, principal_def_id) + .filter(|def_id| tcx.trait_is_auto(*def_id)) + }) + .into_flat_iter(), + ) .collect(); let upcast_principal = normalize_with_depth_to( diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index 72f1b51d19e9d..83d493f077539 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -188,8 +188,7 @@ fn prepare_vtable_segments_inner<'tcx, T>( /// Turns option of iterator into an iterator (this is just flatten) fn maybe_iter(i: Option) -> impl Iterator { - // Flatten is bad perf-vise, we could probably implement a special case here that is better - i.into_iter().flatten() + i.into_flat_iter() } fn has_own_existential_vtable_entries(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool { diff --git a/compiler/rustc_transmute/src/layout/dfa.rs b/compiler/rustc_transmute/src/layout/dfa.rs index 6fc40ce42d88c..92f1898658b07 100644 --- a/compiler/rustc_transmute/src/layout/dfa.rs +++ b/compiler/rustc_transmute/src/layout/dfa.rs @@ -257,15 +257,15 @@ where pub(crate) fn bytes_from(&self, start: State) -> impl Iterator { self.transitions .get(&start) - .into_iter() - .flat_map(|transitions| transitions.byte_transitions.iter()) + .map(|transitions| transitions.byte_transitions.iter()) + .into_flat_iter() } pub(crate) fn refs_from(&self, start: State) -> impl Iterator, State)> { self.transitions .get(&start) - .into_iter() - .flat_map(|transitions| transitions.ref_transitions.iter()) + .map(|transitions| transitions.ref_transitions.iter()) + .into_flat_iter() .map(|(r, s)| (*r, *s)) } diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs index 732881f12d2cb..e504ce0051398 100644 --- a/compiler/rustc_transmute/src/lib.rs +++ b/compiler/rustc_transmute/src/lib.rs @@ -1,6 +1,7 @@ // tidy-alphabetical-start #![cfg_attr(test, feature(test))] #![feature(never_type)] +#![feature(option_into_flat_iter)] // tidy-alphabetical-end pub(crate) use rustc_data_structures::fx::{FxIndexMap as Map, FxIndexSet as Set}; diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs index 58fadff1e6e5d..268386692dc3f 100644 --- a/compiler/rustc_ty_utils/src/assoc.rs +++ b/compiler/rustc_ty_utils/src/assoc.rs @@ -32,7 +32,7 @@ fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &[DefId] { let item_def_id = trait_item_ref.owner_id.to_def_id(); [item_def_id] .into_iter() - .chain(rpitit_items.get(&item_def_id).into_iter().flatten().copied()) + .chain(rpitit_items.get(&item_def_id).into_flat_iter().copied()) })) } hir::ItemKind::Impl(impl_) => { @@ -44,7 +44,7 @@ fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &[DefId] { let item_def_id = impl_item_ref.owner_id.to_def_id(); [item_def_id] .into_iter() - .chain(rpitit_items.get(&item_def_id).into_iter().flatten().copied()) + .chain(rpitit_items.get(&item_def_id).into_flat_iter().copied()) })) } _ => span_bug!(item.span, "associated_item_def_ids: not impl or trait"), diff --git a/compiler/rustc_ty_utils/src/implied_bounds.rs b/compiler/rustc_ty_utils/src/implied_bounds.rs index e83525c16b965..174f1c50964a9 100644 --- a/compiler/rustc_ty_utils/src/implied_bounds.rs +++ b/compiler/rustc_ty_utils/src/implied_bounds.rs @@ -176,13 +176,13 @@ fn impl_spans(tcx: TyCtxt<'_>, def_id: LocalDefId) -> impl Iterator if let hir::ItemKind::Impl(impl_) = item.kind { let trait_args = impl_ .of_trait - .into_iter() - .flat_map(|of_trait| of_trait.trait_ref.path.segments.last().unwrap().args().args) + .map(|of_trait| of_trait.trait_ref.path.segments.last().unwrap().args().args) + .into_flat_iter() .map(|arg| arg.span()); let dummy_spans_for_default_args = impl_ .of_trait - .into_iter() - .flat_map(|of_trait| iter::repeat(of_trait.trait_ref.path.span)); + .map(|of_trait| iter::repeat(of_trait.trait_ref.path.span)) + .into_flat_iter(); iter::once(impl_.self_ty.span).chain(trait_args).chain(dummy_spans_for_default_args) } else { bug!("unexpected item for impl {def_id:?}: {item:?}") diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index cdf280381247a..be0042f697e5d 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -718,8 +718,7 @@ fn layout_of_uncached<'tcx>( let discriminants_iter = || { def.is_enum() .then(|| def.discriminants(tcx).map(|(v, d)| (v, d.val as i128))) - .into_iter() - .flatten() + .into_flat_iter() }; let maybe_unsized = def.is_struct() diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs index 6d7afba71098a..d775a7bfceff2 100644 --- a/compiler/rustc_ty_utils/src/lib.rs +++ b/compiler/rustc_ty_utils/src/lib.rs @@ -9,6 +9,7 @@ #![feature(deref_patterns)] #![feature(iterator_try_collect)] #![feature(never_type)] +#![feature(option_into_flat_iter)] // tidy-alphabetical-end use rustc_middle::query::Providers; diff --git a/library/core/src/ffi/va_list.rs b/library/core/src/ffi/va_list.rs index 55d3cd22354e5..e27f581c04ca2 100644 --- a/library/core/src/ffi/va_list.rs +++ b/library/core/src/ffi/va_list.rs @@ -299,6 +299,9 @@ const impl<'f> Drop for VaList<'f> { /// and [`c_float`] is promoted to [`c_double`]. Implementing this trait for types that are /// subject to this promotion rule is invalid. /// +/// This trait is only implemented for 128-bit integers when the platform defines the `__int128` +/// type. +/// /// [`c_int`]: core::ffi::c_int /// [`c_long`]: core::ffi::c_long /// [`c_longlong`]: core::ffi::c_longlong @@ -310,8 +313,8 @@ const impl<'f> Drop for VaList<'f> { /// [`c_float`]: core::ffi::c_float /// [`c_double`]: core::ffi::c_double // We may unseal this trait in the future, but currently our `va_arg` implementations don't support -// types with an alignment larger than 8, or with a non-scalar layout. Inline assembly can be used -// to accept unsupported types in the meantime. +// types with a non-scalar layout. Inline assembly can be used to accept unsupported types in the +// meantime. #[lang = "va_arg_safe"] pub impl(self) unsafe trait VaArgSafe: Copy {} @@ -352,6 +355,61 @@ unsafe impl VaArgSafe for u32 {} unsafe impl VaArgSafe for u64 {} unsafe impl VaArgSafe for usize {} +// Implement `VaArgSafe` for 128-bit integers on targets where clang provides `__int128`. +// +// GCC does not implement `__int128` for any 16-bit/32-bit target: +// +// https://gcc.gnu.org/onlinedocs/gcc-15.2.0/gcc/_005f_005fint128.html +// +// > There is no support in GCC for expressing an integer constant of type __int128 for targets +// > with long long integer less than 128 bits wide. +// +// Per https://learn.microsoft.com/en-us/cpp/cpp/data-type-ranges?view=msvc-170, MSVC does not +// define `__int128`. +// +// Clang is slightly more permissive: it defines `__int128` on wasm32 (a 32-bit target) and also +// does provide `__int128` on 64-bit `*-pc-windows-msvc`, and we follow suit. +cfg_select! { + any( + target_arch = "aarch64", + target_arch = "amdgpu", + target_arch = "arm64ec", + target_arch = "bpf", + target_arch = "loongarch64", + target_arch = "mips64", + target_arch = "mips64r6", + target_arch = "nvptx64", + target_arch = "powerpc64", + target_arch = "riscv64", + target_arch = "s390x", + target_arch = "sparc64", + target_arch = "wasm32", + target_arch = "wasm64", + target_arch = "x86_64", + ) => { + #[cfg(not(any(target_arch = "wasm32", target_abi = "x32", target_pointer_width = "64")))] + compile_error!("unexpected target architecture for 128-bit c-variadic"); + + #[unstable_feature_bound(c_variadic_int128)] + #[unstable(feature = "c_variadic_int128", issue = "155752")] + unsafe impl VaArgSafe for i128 {} + #[unstable_feature_bound(c_variadic_int128)] + #[unstable(feature = "c_variadic_int128", issue = "155752")] + unsafe impl VaArgSafe for u128 {} + } + _ => { + #[repr(transparent)] + #[derive(Clone, Copy)] + struct S(i32); + + // When there are no actual implementations on i128, declare the c_variadic_int128 feature + // on a private type so that the feature is defined on all targets. + #[unstable(feature = "c_variadic_int128", issue = "155752")] + unsafe impl VaArgSafe for S {} + + } +} + unsafe impl VaArgSafe for f64 {} unsafe impl VaArgSafe for *mut T {} diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 58b5ad4a924b1..bc96c768c0c94 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -818,19 +818,19 @@ pub const fn forget(_: T); /// /// // This is how the standard library does it. This is the best method, if /// // you need to do something like this -/// fn split_at_stdlib(slice: &mut [T], mid: usize) +/// fn split_at_stdlib(to_split: &mut [T], mid: usize) /// -> (&mut [T], &mut [T]) { -/// let len = slice.len(); +/// let len = to_split.len(); /// assert!(mid <= len); /// unsafe { -/// let ptr = slice.as_mut_ptr(); -/// // This now has three mutable references pointing at the same -/// // memory. `slice`, the rvalue ret.0, and the rvalue ret.1. -/// // `slice` is never used after `let ptr = ...`, and so one can -/// // treat it as "dead", and therefore, you only have two real -/// // mutable slices. -/// (slice::from_raw_parts_mut(ptr, mid), -/// slice::from_raw_parts_mut(ptr.add(mid), len - mid)) +/// let ptr = to_split.as_mut_ptr(); +/// let fst = slice::from_raw_parts_mut(ptr, mid); +/// let snd = slice::from_raw_parts_mut(ptr.add(mid), len - mid); +/// // The function now has three mutable references to overlapping memory: +/// // `to_split`, `fst`, and `snd`. +/// // `to_split` is never used after `let ptr = ...` so it can be treated as "dead". +/// // This leaves two "live" mutable slice references, `fst` and `snd`, with no overlap. +/// (fst, snd) /// } /// } /// ``` diff --git a/src/tools/test-float-parse/Cargo.lock b/src/tools/test-float-parse/Cargo.lock deleted file mode 100644 index 3f60423fed352..0000000000000 --- a/src/tools/test-float-parse/Cargo.lock +++ /dev/null @@ -1,75 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "getrandom" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "libc" -version = "0.2.147" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "test-float-parse" -version = "0.1.0" -dependencies = [ - "rand", -] - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/tests/assembly-llvm/c-variadic/aarch64.rs b/tests/assembly-llvm/c-variadic/aarch64.rs new file mode 100644 index 0000000000000..eb242a1a41e21 --- /dev/null +++ b/tests/assembly-llvm/c-variadic/aarch64.rs @@ -0,0 +1,294 @@ +//@ add-minicore +//@ assembly-output: emit-asm +// +//@ revisions: AARCH64_LINUX AARCH64_DARWIN AARCH64_BE ARM64EC_MSVC +//@ [AARCH64_LINUX] compile-flags: -Copt-level=3 --target aarch64-unknown-linux-gnu +//@ [AARCH64_LINUX] needs-llvm-components: aarch64 +//@ [AARCH64_BE] compile-flags: -Copt-level=3 --target aarch64_be-unknown-linux-gnu +//@ [AARCH64_BE] needs-llvm-components: aarch64 +//@ [AARCH64_DARWIN] compile-flags: -Copt-level=3 --target aarch64-apple-darwin +//@ [AARCH64_DARWIN] needs-llvm-components: aarch64 +//@ [ARM64EC_MSVC] compile-flags: -Copt-level=3 --target arm64ec-pc-windows-msvc +//@ [ARM64EC_MSVC] needs-llvm-components: aarch64 +#![feature(c_variadic, no_core, lang_items, intrinsics, rustc_attrs)] +#![no_core] +#![crate_type = "lib"] + +// Check that the assembly that rustc generates matches what clang emits. + +// For aarch64-unknown-linux-gnu LLVM canonicalizes a comparison, leading to slightly different +// assembly. +// +// For aarch64-apple-darwin LLVM is able to optimize our output better, because we effectively +// desugar va_arg early, hence we don't actually match Clang there. + +extern crate minicore; +use minicore::*; + +#[lang = "va_arg_safe"] +pub unsafe trait VaArgSafe {} + +unsafe impl VaArgSafe for i32 {} +unsafe impl VaArgSafe for i64 {} +unsafe impl VaArgSafe for i128 {} +unsafe impl VaArgSafe for f64 {} +unsafe impl VaArgSafe for *const T {} + +#[repr(transparent)] +struct VaListInner { + ptr: *const c_void, +} + +#[repr(transparent)] +#[lang = "va_list"] +pub struct VaList<'a> { + inner: VaListInner, + _marker: PhantomData<&'a mut ()>, +} + +#[rustc_intrinsic] +#[rustc_nounwind] +pub const unsafe fn va_arg(ap: &mut VaList<'_>) -> T; + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_f64(ap: &mut VaList<'_>) -> f64 { + // AARCH64_LINUX-LABEL: read_f64: + // AARCH64_LINUX: ldrsw x8, [x0, #28] + // AARCH64_LINUX-NEXT: tbz w8, #31, .LBB0_2 + // AARCH64_LINUX-NEXT: add w9, w8, #16 + // AARCH64_LINUX-NEXT: cmn w8, #16 + // AARCH64_LINUX-NEXT: str w9, [x0, #28] + // AARCH64_LINUX-NEXT: b.ls .LBB0_3 + // AARCH64_LINUX-NEXT: .LBB0_2: + // AARCH64_LINUX-NEXT: ldr x8, [x0] + // AARCH64_LINUX-NEXT: ldr d0, [x8] + // AARCH64_LINUX-NEXT: add x9, x8, #8 + // AARCH64_LINUX-NEXT: str x9, [x0] + // AARCH64_LINUX-NEXT: ret + // AARCH64_LINUX-NEXT: .LBB0_3 + // AARCH64_LINUX-NEXT: ldr x9, [x0, #16] + // AARCH64_LINUX-NEXT: add x8, x9, x8 + // AARCH64_LINUX-NEXT: ldr d0, [x8] + // AARCH64_LINUX-NEXT: ret + + // AARCH64_BE-LABEL: read_f64: + // AARCH64_BE: ldrsw x8, [x0, #28] + // AARCH64_BE-NEXT: tbz w8, #31, .LBB0_2 + // AARCH64_BE-NEXT: add w9, w8, #16 + // AARCH64_BE-NEXT: cmn w8, #16 + // AARCH64_BE-NEXT: str w9, [x0, #28] + // AARCH64_BE-NEXT: b.ls .LBB0_3 + // AARCH64_BE-NEXT: .LBB0_2: + // AARCH64_BE-NEXT: ldr x8, [x0] + // AARCH64_BE-NEXT: ldr d0, [x8] + // AARCH64_BE-NEXT: add x9, x8, #8 + // AARCH64_BE-NEXT: str x9, [x0] + // AARCH64_BE-NEXT: ret + // AARCH64_BE-NEXT: .LBB0_3: + // AARCH64_BE-NEXT: ldr x9, [x0, #16] + // AARCH64_BE-NEXT: add x8, x9, x8 + // AARCH64_BE-NEXT: ldr d0, [x8, #8]! + // AARCH64_BE-NEXT: ret + + // ARM64EC_MSVC-LABEL: read_f64 = "#read_f64" + // ARM64EC_MSVC: ldr x8, [x0] + // ARM64EC_MSVC-NEXT: ldr d0, [x8], #8 + // ARM64EC_MSVC-NEXT: str x8, [x0] + // ARM64EC_MSVC-NEXT: ret + + // AARCH64_DARWIN-LABEL: _read_f64: + // AARCH64_DARWIN: ldr x8, [x0] + // AARCH64_DARWIN-NEXT: ldr d0, [x8], #8 + // AARCH64_DARWIN-NEXT: str x8, [x0] + // AARCH64_DARWIN-NEXT: ret + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i32(ap: &mut VaList<'_>) -> i32 { + // AARCH64_LINUX-LABEL: read_i32: + // AARCH64_LINUX: ldrsw x8, [x0, #24] + // AARCH64_LINUX-NEXT: tbz w8, #31, .LBB1_2 + // AARCH64_LINUX-NEXT: add w9, w8, #8 + // AARCH64_LINUX-NEXT: cmn w8, #8 + // AARCH64_LINUX-NEXT: str w9, [x0, #24] + // AARCH64_LINUX-NEXT: b.ls .LBB1_3 + // AARCH64_LINUX-NEXT: .LBB1_2: + // AARCH64_LINUX-NEXT: ldr x8, [x0] + // AARCH64_LINUX-NEXT: add x9, x8, #8 + // AARCH64_LINUX-NEXT: str x9, [x0] + // AARCH64_LINUX-NEXT: ldr w0, [x8] + // AARCH64_LINUX-NEXT: ret + // AARCH64_LINUX-NEXT: .LBB1_3 + // AARCH64_LINUX-NEXT: ldr x9, [x0, #8] + // AARCH64_LINUX-NEXT: add x8, x9, x8 + // AARCH64_LINUX-NEXT: ldr w0, [x8] + // AARCH64_LINUX-NEXT: ret + + // AARCH64_BE-LABEL: read_i32: + // AARCH64_BE: ldrsw x8, [x0, #24] + // AARCH64_BE-NEXT: tbz w8, #31, .LBB1_2 + // AARCH64_BE-NEXT: add w9, w8, #8 + // AARCH64_BE-NEXT: cmn w8, #8 + // AARCH64_BE-NEXT: str w9, [x0, #24] + // AARCH64_BE-NEXT: b.ls .LBB1_3 + // AARCH64_BE-NEXT: .LBB1_2: + // AARCH64_BE-NEXT: ldr x8, [x0] + // AARCH64_BE-NEXT: add x9, x8, #8 + // AARCH64_BE-NEXT: str x9, [x0] + // AARCH64_BE-NEXT: ldr w0, [x8] + // AARCH64_BE-NEXT: ret + // AARCH64_BE-NEXT: .LBB1_3: + // AARCH64_BE-NEXT: ldr x9, [x0, #8] + // AARCH64_BE-NEXT: add x8, x9, x8 + // AARCH64_BE-NEXT: ldr w0, [x8, #4]! + // AARCH64_BE-NEXT: ret + + // ARM64EC_MSVC-LABEL: read_i32 = "#read_i32" + // ARM64EC_MSVC: ldr x9, [x0] + // ARM64EC_MSVC-NEXT: mov x8, x0 + // ARM64EC_MSVC-NEXT: ldr w0, [x9], #8 + // ARM64EC_MSVC-NEXT: str x9, [x8] + // ARM64EC_MSVC-NEXT: ret + + // AARCH64_DARWIN-LABEL: _read_i32: + // AARCH64_DARWIN: ldr x9, [x0] + // AARCH64_DARWIN-NEXT: ldr w8, [x9], #8 + // AARCH64_DARWIN-NEXT: str x9, [x0] + // AARCH64_DARWIN-NEXT: mov x0, x8 + // AARCH64_DARWIN-NEXT: ret + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i64(ap: &mut VaList<'_>) -> i64 { + // AARCH64_LINUX-LABEL: read_i64: + // AARCH64_LINUX: ldrsw x8, [x0, #24] + // AARCH64_LINUX-NEXT: tbz w8, #31, .LBB2_2 + // AARCH64_LINUX-NEXT: add w9, w8, #8 + // AARCH64_LINUX-NEXT: cmn w8, #8 + // AARCH64_LINUX-NEXT: str w9, [x0, #24] + // AARCH64_LINUX-NEXT: b.ls .LBB2_3 + // AARCH64_LINUX-NEXT: .LBB2_2: + // AARCH64_LINUX-NEXT: ldr x8, [x0] + // AARCH64_LINUX-NEXT: add x9, x8, #8 + // AARCH64_LINUX-NEXT: str x9, [x0] + // AARCH64_LINUX-NEXT: ldr x0, [x8] + // AARCH64_LINUX-NEXT: ret + // AARCH64_LINUX-NEXT: .LBB2_3 + // AARCH64_LINUX-NEXT: ldr x9, [x0, #8] + // AARCH64_LINUX-NEXT: add x8, x9, x8 + // AARCH64_LINUX-NEXT: ldr x0, [x8] + // AARCH64_LINUX-NEXT: ret + + // AARCH64_BE-LABEL: read_i64: + // AARCH64_BE: ldrsw x8, [x0, #24] + // AARCH64_BE-NEXT: tbz w8, #31, .LBB2_2 + // AARCH64_BE-NEXT: add w9, w8, #8 + // AARCH64_BE-NEXT: cmn w8, #8 + // AARCH64_BE-NEXT: str w9, [x0, #24] + // AARCH64_BE-NEXT: b.ls .LBB2_3 + // AARCH64_BE-NEXT: .LBB2_2: + // AARCH64_BE-NEXT: ldr x8, [x0] + // AARCH64_BE-NEXT: add x9, x8, #8 + // AARCH64_BE-NEXT: str x9, [x0] + // AARCH64_BE-NEXT: ldr x0, [x8] + // AARCH64_BE-NEXT: ret + // AARCH64_BE-NEXT: .LBB2_3: + // AARCH64_BE-NEXT: ldr x9, [x0, #8] + // AARCH64_BE-NEXT: add x8, x9, x8 + // AARCH64_BE-NEXT: ldr x0, [x8] + // AARCH64_BE-NEXT: ret + + // ARM64EC_MSVC-LABEL: read_ptr = "#read_ptr" + // ARM64EC_MSVC-LABEL: read_i64 = "#read_i64" + // ARM64EC_MSVC: ldr x9, [x0] + // ARM64EC_MSVC-NEXT: mov x8, x0 + // ARM64EC_MSVC-NEXT: ldr x0, [x9], #8 + // ARM64EC_MSVC-NEXT: str x9, [x8] + // ARM64EC_MSVC-NEXT: ret + + // AARCH64_DARWIN-LABEL: _read_i64: + // AARCH64_DARWIN: ldr x9, [x0] + // AARCH64_DARWIN-NEXT: ldr x8, [x9], #8 + // AARCH64_DARWIN-NEXT: str x9, [x0] + // AARCH64_DARWIN-NEXT: mov x0, x8 + // AARCH64_DARWIN-NEXT: ret + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i128(ap: &mut VaList<'_>) -> i128 { + // AARCH64_LINUX-LABEL: read_i128: + // AARCH64_LINUX: ldrsw x8, [x0, #24] + // AARCH64_LINUX-NEXT: tbz w8, #31, .LBB3_2 + // AARCH64_LINUX-NEXT: add x8, x8, #15 + // AARCH64_LINUX-NEXT: and x8, x8, #0xfffffffffffffff0 + // AARCH64_LINUX-NEXT: add w9, w8, #16 + // AARCH64_LINUX-NEXT: cmp w9, #0 + // AARCH64_LINUX-NEXT: str w9, [x0, #24] + // AARCH64_LINUX-NEXT: b.le .LBB3_3 + // AARCH64_LINUX-NEXT: .LBB3_2: + // AARCH64_LINUX-NEXT: ldr x8, [x0] + // AARCH64_LINUX-NEXT: add x8, x8, #15 + // AARCH64_LINUX-NEXT: and x8, x8, #0xfffffffffffffff0 + // AARCH64_LINUX-NEXT: add x9, x8, #16 + // AARCH64_LINUX-NEXT: str x9, [x0] + // AARCH64_LINUX-NEXT: ldp x0, x1, [x8] + // AARCH64_LINUX-NEXT: ret + // AARCH64_LINUX-NEXT: .LBB3_3 + // AARCH64_LINUX-NEXT: ldr x9, [x0, #8] + // AARCH64_LINUX-NEXT: add x8, x9, x8 + // AARCH64_LINUX-NEXT: ldp x0, x1, [x8] + // AARCH64_LINUX-NEXT: ret + + // AARCH64_BE-LABEL: read_i128: + // AARCH64_BE: ldrsw x8, [x0, #24] + // AARCH64_BE-NEXT: tbz w8, #31, .LBB3_2 + // AARCH64_BE-NEXT: add x8, x8, #15 + // AARCH64_BE-NEXT: and x8, x8, #0xfffffffffffffff0 + // AARCH64_BE-NEXT: add w9, w8, #16 + // AARCH64_BE-NEXT: cmp w9, #0 + // AARCH64_BE-NEXT: str w9, [x0, #24] + // AARCH64_BE-NEXT: b.le .LBB3_3 + // AARCH64_BE-NEXT: .LBB3_2: + // AARCH64_BE-NEXT: ldr x8, [x0] + // AARCH64_BE-NEXT: add x8, x8, #15 + // AARCH64_BE-NEXT: and x8, x8, #0xfffffffffffffff0 + // AARCH64_BE-NEXT: add x9, x8, #16 + // AARCH64_BE-NEXT: str x9, [x0] + // AARCH64_BE-NEXT: ldp x0, x1, [x8] + // AARCH64_BE-NEXT: ret + // AARCH64_BE-NEXT: .LBB3_3: + // AARCH64_BE-NEXT: ldr x9, [x0, #8] + // AARCH64_BE-NEXT: add x8, x9, x8 + // AARCH64_BE-NEXT: ldp x0, x1, [x8] + // AARCH64_BE-NEXT: ret + + // ARM64EC_MSVC-LABEL: read_i128 = "#read_i128" + // ARM64EC_MSVC: ldr x9, [x0] + // ARM64EC_MSVC-NEXT: mov x8, x0 + // ARM64EC_MSVC-NEXT: ldp x0, x1, [x9], #16 + // ARM64EC_MSVC-NEXT: str x9, [x8] + // ARM64EC_MSVC-NEXT: ret + + // AARCH64_DARWIN-LABEL: _read_i128: + // AARCH64_DARWIN: ldr x8, [x0] + // AARCH64_DARWIN-NEXT: add x8, x8, #15 + // AARCH64_DARWIN-NEXT: and x9, x8, #0xfffffffffffffff0 + // AARCH64_DARWIN-NEXT: ldr x1, [x9, #8] + // AARCH64_DARWIN-NEXT: ldr x8, [x9], #16 + // AARCH64_DARWIN-NEXT: str x9, [x0] + // AARCH64_DARWIN-NEXT: mov x0, x8 + // AARCH64_DARWIN-NEXT: ret + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_ptr(ap: &mut VaList<'_>) -> *const u8 { + // AARCH64_LINUX-CHECK: read_ptr = read_i64 + // AARCH64_BE-CHECK: read_ptr = read_i64 + // ARM64EC_MSVC: "#read_ptr" = "#read_i64" + // AARCH64_DARWIN-CHECK: _read_ptr = _read_i64 + va_arg(ap) +} diff --git a/tests/assembly-llvm/c-variadic/gpu.rs b/tests/assembly-llvm/c-variadic/gpu.rs new file mode 100644 index 0000000000000..02798354bbf3d --- /dev/null +++ b/tests/assembly-llvm/c-variadic/gpu.rs @@ -0,0 +1,178 @@ +//@ add-minicore +//@ assembly-output: emit-asm +// +//@ revisions: AMDGPU NVPTX +//@ [AMDGPU] compile-flags: --crate-type=rlib --target=amdgcn-amd-amdhsa -Ctarget-cpu=gfx900 +//@ [AMDGPU] needs-llvm-components: amdgpu +//@ [NVPTX] compile-flags: --crate-type=rlib --target=nvptx64-nvidia-cuda +//@ [NVPTX] needs-llvm-components: nvptx +#![feature(c_variadic, no_core, lang_items, intrinsics, rustc_attrs)] +#![no_core] +#![crate_type = "lib"] + +extern crate minicore; +use minicore::*; + +#[lang = "va_arg_safe"] +pub unsafe trait VaArgSafe {} + +unsafe impl VaArgSafe for i32 {} +unsafe impl VaArgSafe for i64 {} +unsafe impl VaArgSafe for i128 {} +unsafe impl VaArgSafe for f64 {} +unsafe impl VaArgSafe for *const T {} + +#[repr(transparent)] +struct VaListInner { + ptr: *const c_void, +} + +#[repr(transparent)] +#[lang = "va_list"] +pub struct VaList<'a> { + inner: VaListInner, + _marker: PhantomData<&'a mut ()>, +} + +#[rustc_intrinsic] +#[rustc_nounwind] +pub const unsafe fn va_arg(ap: &mut VaList<'_>) -> T; + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_f64(ap: &mut VaList<'_>) -> f64 { + // CHECK-LABEL: read_f64 + // + // AMDGPU: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) + // AMDGPU-NEXT: flat_load_dwordx2 v[4:5], v[0:1] + // AMDGPU-NEXT: s_waitcnt vmcnt(0) lgkmcnt(0) + // AMDGPU-NEXT: flat_load_dwordx2 v[2:3], v[4:5] + // AMDGPU-NEXT: v_add_co_u32_e32 v4, vcc, 8, v4 + // AMDGPU-NEXT: v_addc_co_u32_e32 v5, vcc, 0, v5, vcc + // AMDGPU-NEXT: flat_store_dwordx2 v[0:1], v[4:5] + // AMDGPU-NEXT: s_waitcnt vmcnt(0) lgkmcnt(0) + // AMDGPU-NEXT: v_mov_b32_e32 v0, v2 + // AMDGPU-NEXT: v_mov_b32_e32 v1, v3 + // AMDGPU-NEXT: s_setpc_b64 s[30:31] + // + // NVPTX: ld.param.b64 %rd1, [read_f64_param_0]; + // NVPTX-NEXT: ld.b64 %rd2, [%rd1]; + // NVPTX-NEXT: add.s64 %rd3, %rd2, 7; + // NVPTX-NEXT: and.b64 %rd4, %rd3, -8; + // NVPTX-NEXT: add.s64 %rd5, %rd4, 8; + // NVPTX-NEXT: st.b64 [%rd1], %rd5; + // NVPTX-NEXT: ld.b64 %rd6, [%rd4]; + // NVPTX-NEXT: st.param.b64 [func_retval0], %rd6; + // NVPTX-NEXT: ret; + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i32(ap: &mut VaList<'_>) -> i32 { + // CHECK-LABEL: read_i32 + // + // AMDGPU: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) + // AMDGPU-NEXT: flat_load_dwordx2 v[3:4], v[0:1] + // AMDGPU-NEXT: s_waitcnt vmcnt(0) lgkmcnt(0) + // AMDGPU-NEXT: flat_load_dword v2, v[3:4] + // AMDGPU-NEXT: v_add_co_u32_e32 v3, vcc, 4, v3 + // AMDGPU-NEXT: v_addc_co_u32_e32 v4, vcc, 0, v4, vcc + // AMDGPU-NEXT: flat_store_dwordx2 v[0:1], v[3:4] + // AMDGPU-NEXT: s_waitcnt vmcnt(0) lgkmcnt(0) + // AMDGPU-NEXT: v_mov_b32_e32 v0, v2 + // AMDGPU-NEXT: s_setpc_b64 s[30:31] + // + // NVPTX: ld.param.b64 %rd1, [read_i32_param_0]; + // NVPTX-NEXT: ld.b64 %rd2, [%rd1]; + // NVPTX-NEXT: add.s64 %rd3, %rd2, 3; + // NVPTX-NEXT: and.b64 %rd4, %rd3, -4; + // NVPTX-NEXT: add.s64 %rd5, %rd4, 4; + // NVPTX-NEXT: st.b64 [%rd1], %rd5; + // NVPTX-NEXT: ld.b32 %r1, [%rd4]; + // NVPTX-NEXT: st.param.b32 [func_retval0], %r1; + // NVPTX-NEXT: ret; + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i64(ap: &mut VaList<'_>) -> i64 { + // CHECK-LABEL: read_i64 + // + // AMDGPU: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) + // AMDGPU-NEXT: flat_load_dwordx2 v[4:5], v[0:1] + // AMDGPU-NEXT: s_waitcnt vmcnt(0) lgkmcnt(0) + // AMDGPU-NEXT: flat_load_dwordx2 v[2:3], v[4:5] + // AMDGPU-NEXT: v_add_co_u32_e32 v4, vcc, 8, v4 + // AMDGPU-NEXT: v_addc_co_u32_e32 v5, vcc, 0, v5, vcc + // AMDGPU-NEXT: flat_store_dwordx2 v[0:1], v[4:5] + // AMDGPU-NEXT: s_waitcnt vmcnt(0) lgkmcnt(0) + // AMDGPU-NEXT: v_mov_b32_e32 v0, v2 + // AMDGPU-NEXT: v_mov_b32_e32 v1, v3 + // AMDGPU-NEXT: s_setpc_b64 s[30:31] + // + // NVPTX: ld.param.b64 %rd1, [read_i64_param_0]; + // NVPTX-NEXT: ld.b64 %rd2, [%rd1]; + // NVPTX-NEXT: add.s64 %rd3, %rd2, 7; + // NVPTX-NEXT: and.b64 %rd4, %rd3, -8; + // NVPTX-NEXT: add.s64 %rd5, %rd4, 8; + // NVPTX-NEXT: st.b64 [%rd1], %rd5; + // NVPTX-NEXT: ld.b64 %rd6, [%rd4]; + // NVPTX-NEXT: st.param.b64 [func_retval0], %rd6; + // NVPTX-NEXT: ret; + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i128(ap: &mut VaList<'_>) -> i128 { + // CHECK-LABEL: read_i128 + // + // AMDGPU: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) + // AMDGPU-NEXT: v_mov_b32_e32 v5, v1 + // AMDGPU-NEXT: v_mov_b32_e32 v4, v0 + // AMDGPU-NEXT: flat_load_dwordx2 v[6:7], v[4:5] + // AMDGPU-NEXT: s_waitcnt vmcnt(0) lgkmcnt(0) + // AMDGPU-NEXT: flat_load_dwordx4 v[0:3], v[6:7] + // AMDGPU-NEXT: v_add_co_u32_e32 v6, vcc, 16, v6 + // AMDGPU-NEXT: v_addc_co_u32_e32 v7, vcc, 0, v7, vcc + // AMDGPU-NEXT: flat_store_dwordx2 v[4:5], v[6:7] + // AMDGPU-NEXT: s_waitcnt vmcnt(0) lgkmcnt(0) + // AMDGPU-NEXT: s_setpc_b64 s[30:31] + // + // NVPTX: ld.param.b64 %rd1, [read_i128_param_0]; + // NVPTX-NEXT: ld.b64 %rd2, [%rd1]; + // NVPTX-NEXT: add.s64 %rd3, %rd2, 15; + // NVPTX-NEXT: and.b64 %rd4, %rd3, -16; + // NVPTX-NEXT: add.s64 %rd5, %rd4, 16; + // NVPTX-NEXT: st.b64 [%rd1], %rd5; + // NVPTX-NEXT: ld.v2.b64 {%rd6, %rd7}, [%rd4]; + // NVPTX-NEXT: st.param.v2.b64 [func_retval0], {%rd6, %rd7}; + // NVPTX-NEXT: ret; + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_ptr(ap: &mut VaList<'_>) -> *const u8 { + // CHECK-LABEL: read_ptr + // + // AMDGPU: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) + // AMDGPU-NEXT: flat_load_dwordx2 v[4:5], v[0:1] + // AMDGPU-NEXT: s_waitcnt vmcnt(0) lgkmcnt(0) + // AMDGPU-NEXT: flat_load_dwordx2 v[2:3], v[4:5] + // AMDGPU-NEXT: v_add_co_u32_e32 v4, vcc, 8, v4 + // AMDGPU-NEXT: v_addc_co_u32_e32 v5, vcc, 0, v5, vcc + // AMDGPU-NEXT: flat_store_dwordx2 v[0:1], v[4:5] + // AMDGPU-NEXT: s_waitcnt vmcnt(0) lgkmcnt(0) + // AMDGPU-NEXT: v_mov_b32_e32 v0, v2 + // AMDGPU-NEXT: v_mov_b32_e32 v1, v3 + // AMDGPU-NEXT: s_setpc_b64 s[30:31] + // + // NVPTX: ld.param.b64 %rd1, [read_ptr_param_0]; + // NVPTX-NEXT: ld.b64 %rd2, [%rd1]; + // NVPTX-NEXT: add.s64 %rd3, %rd2, 7; + // NVPTX-NEXT: and.b64 %rd4, %rd3, -8; + // NVPTX-NEXT: add.s64 %rd5, %rd4, 8; + // NVPTX-NEXT: st.b64 [%rd1], %rd5; + // NVPTX-NEXT: ld.b64 %rd6, [%rd4]; + // NVPTX-NEXT: st.param.b64 [func_retval0], %rd6; + // NVPTX-NEXT: ret; + va_arg(ap) +} diff --git a/tests/assembly-llvm/c-variadic/powerpc.rs b/tests/assembly-llvm/c-variadic/powerpc.rs new file mode 100644 index 0000000000000..47d35e7579737 --- /dev/null +++ b/tests/assembly-llvm/c-variadic/powerpc.rs @@ -0,0 +1,198 @@ +//@ add-minicore +//@ assembly-output: emit-asm +// +//@ revisions: POWERPC POWERPC64 POWERPC64LE AIX +//@ [POWERPC] compile-flags: -Copt-level=3 --target powerpc-unknown-linux-gnu +//@ [POWERPC] needs-llvm-components: powerpc +//@ [POWERPC64] compile-flags: -Copt-level=3 --target powerpc64-unknown-linux-gnu +//@ [POWERPC64] needs-llvm-components: powerpc +//@ [POWERPC64LE] compile-flags: -Copt-level=3 --target powerpc64le-unknown-linux-gnu +//@ [POWERPC64LE] needs-llvm-components: powerpc +//@ [AIX] compile-flags: -Copt-level=3 --target powerpc64-ibm-aix +//@ [AIX] needs-llvm-components: powerpc +#![feature(c_variadic, no_core, lang_items, intrinsics, rustc_attrs)] +#![no_core] +#![crate_type = "lib"] + +// Check that the assembly that rustc generates matches what clang emits. + +extern crate minicore; +use minicore::*; + +#[lang = "va_arg_safe"] +pub unsafe trait VaArgSafe {} + +unsafe impl VaArgSafe for i32 {} +unsafe impl VaArgSafe for i64 {} +#[cfg(target_pointer_width = "64")] +unsafe impl VaArgSafe for i128 {} +unsafe impl VaArgSafe for f64 {} +unsafe impl VaArgSafe for *const T {} + +#[repr(transparent)] +struct VaListInner { + ptr: *const c_void, +} + +#[repr(transparent)] +#[lang = "va_list"] +pub struct VaList<'a> { + inner: VaListInner, + _marker: PhantomData<&'a mut ()>, +} + +#[rustc_intrinsic] +#[rustc_nounwind] +pub const unsafe fn va_arg(ap: &mut VaList<'_>) -> T; + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_f64(ap: &mut VaList<'_>) -> f64 { + // CHECK-LABEL: read_f64 + // + // POWERPC: lbz 5, 1(3) + // POWERPC-NEXT: cmplwi 5, 7 + // POWERPC-NEXT: bgt 0, .LBB0_2 + // POWERPC-NEXT: lwz 4, 8(3) + // POWERPC-NEXT: rlwinm 6, 5, 3, 24, 28 + // POWERPC-NEXT: addi 5, 5, 1 + // POWERPC-NEXT: add 4, 4, 6 + // POWERPC-NEXT: addi 4, 4, 32 + // POWERPC-NEXT: lfd 1, 0(4) + // POWERPC-NEXT: stb 5, 1(3) + // POWERPC-NEXT: blr + // + // POWERPC64: ld 4, 0(3) + // POWERPC64-NEXT: lfd 1, 0(4) + // POWERPC64-NEXT: addi 4, 4, 8 + // POWERPC64-NEXT: std 4, 0(3) + // POWERPC64-NEXT: blr + // + // POWERPC64LE: ld 4, 0(3) + // POWERPC64LE-NEXT: lfd 1, 0(4) + // POWERPC64LE-NEXT: addi 5, 4, 8 + // POWERPC64LE-NEXT: std 5, 0(3) + // POWERPC64LE-NEXT: blr + // + // AIX: ld 4, 0(3) + // AIX-NEXT: lfd 1, 0(4) + // AIX-NEXT: addi 5, 4, 8 + // AIX-NEXT: std 5, 0(3) + // AIX-NEXT: blr + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i32(ap: &mut VaList<'_>) -> i32 { + // CHECK-LABEL: read_i32 + // + // POWERPC: lbz 5, 0(3) + // POWERPC-NEXT: mr 4, 3 + // POWERPC-NEXT: cmplwi 5, 7 + // POWERPC-NEXT: bgt 0, .LBB1_2 + // POWERPC-NEXT: lwz 3, 8(4) + // POWERPC-NEXT: rlwinm 6, 5, 2, 24, 29 + // POWERPC-NEXT: addi 5, 5, 1 + // POWERPC-NEXT: add 3, 3, 6 + // POWERPC-NEXT: lwz 3, 0(3) + // POWERPC-NEXT: stb 5, 0(4) + // POWERPC-NEXT: blr + // + // POWERPC64: ld 5, 0(3) + // POWERPC64-NEXT: mr 4, 3 + // POWERPC64-NEXT: lwa 3, 4(5) + // POWERPC64-NEXT: addi 5, 5, 8 + // POWERPC64-NEXT: std 5, 0(4) + // POWERPC64-NEXT: blr + // + // POWERPC64LE: ld 4, 0(3) + // POWERPC64LE-NEXT: addi 5, 4, 8 + // POWERPC64LE-NEXT: std 5, 0(3) + // POWERPC64LE-NEXT: lwa 3, 0(4) + // POWERPC64LE-NEXT: blr + // + // AIX: ld 4, 0(3) + // AIX-NEXT: addi 5, 4, 8 + // AIX-NEXT: std 5, 0(3) + // AIX-NEXT: lwa 3, 4(4) + // AIX-NEXT: blr + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i64(ap: &mut VaList<'_>) -> i64 { + // CHECK-LABEL: read_i64 + // + // POWERPC: mr 5, 3 + // POWERPC-NEXT: lbz 3, 0(3) + // POWERPC-NEXT: addi 3, 3, 1 + // POWERPC-NEXT: clrlwi 4, 3, 24 + // POWERPC-NEXT: cmplwi 4, 7 + // POWERPC-NEXT: bgt 0, .LBB2_2 + // POWERPC-NEXT: lwz 4, 8(5) + // POWERPC-NEXT: rlwinm 6, 3, 0, 29, 30 + // POWERPC-NEXT: rlwinm 3, 3, 2, 27, 28 + // POWERPC-NEXT: addi 6, 6, 2 + // POWERPC-NEXT: add 4, 4, 3 + // POWERPC-NEXT: lwz 3, 0(4) + // POWERPC-NEXT: lwz 4, 4(4) + // POWERPC-NEXT: stb 6, 0(5) + // POWERPC-NEXT: blr + // + // POWERPC64: ld 5, 0(3) + // POWERPC64-NEXT: mr 4, 3 + // POWERPC64-NEXT: ld 3, 0(5) + // POWERPC64-NEXT: addi 5, 5, 8 + // POWERPC64-NEXT: std 5, 0(4) + // POWERPC64-NEXT: blr + // + // POWERPC64LE: ld 4, 0(3) + // POWERPC64LE-NEXT: addi 5, 4, 8 + // POWERPC64LE-NEXT: std 5, 0(3) + // POWERPC64LE-NEXT: ld 3, 0(4) + // POWERPC64LE-NEXT: blr + // + // AIX: ld 4, 0(3) + // AIX-NEXT: addi 5, 4, 8 + // AIX-NEXT: std 5, 0(3) + // AIX-NEXT: ld 3, 0(4) + // AIX-NEXT: blr + va_arg(ap) +} + +#[unsafe(no_mangle)] +#[cfg(target_pointer_width = "64")] +unsafe extern "C" fn read_i128(ap: &mut VaList<'_>) -> i128 { + // POWERPC64-LABEL: read_i128 + // POWERPC64: ld 6, 0(3) + // POWERPC64-NEXT: mr 5, 3 + // POWERPC64-NEXT: ld 3, 0(6) + // POWERPC64-NEXT: ld 4, 8(6) + // POWERPC64-NEXT: addi 6, 6, 16 + // POWERPC64-NEXT: std 6, 0(5) + // POWERPC64-NEXT: blr + // + // POWERPC64LE-LABEL: read_i128 + // POWERPC64LE: ld 4, 0(3) + // POWERPC64LE-NEXT: addi 5, 4, 16 + // POWERPC64LE-NEXT: std 5, 0(3) + // POWERPC64LE-NEXT: ld 3, 0(4) + // POWERPC64LE-NEXT: ld 4, 8(4) + // POWERPC64LE-NEXT: blr + // + // AIX-LABEL: read_i128 + // AIX: ld 4, 0(3) + // AIX-NEXT: addi 5, 4, 16 + // AIX-NEXT: std 5, 0(3) + // AIX-NEXT: ld 3, 0(4) + // AIX-NEXT: ld 4, 8(4) + // AIX-NEXT: blr + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_ptr(ap: &mut VaList<'_>) -> *const u8 { + // POWERPC: read_ptr = read_i32 + // POWERPC64: read_ptr = read_i64 + // POWERPC64LE: read_ptr = read_i64 + va_arg(ap) +} diff --git a/tests/assembly-llvm/c-variadic/riscv.rs b/tests/assembly-llvm/c-variadic/riscv.rs new file mode 100644 index 0000000000000..893d2a65eb37d --- /dev/null +++ b/tests/assembly-llvm/c-variadic/riscv.rs @@ -0,0 +1,127 @@ +//@ add-minicore +//@ assembly-output: emit-asm +// +//@ revisions: RISCV32 RISCV64 +//@ [RISCV32] compile-flags: -Copt-level=3 --target riscv32gc-unknown-linux-gnu +//@ [RISCV32] needs-llvm-components: riscv +//@ [RISCV64] compile-flags: -Copt-level=3 --target riscv64gc-unknown-linux-gnu +//@ [RISCV64] needs-llvm-components: riscv +#![feature(c_variadic, no_core, lang_items, intrinsics, rustc_attrs)] +#![no_core] +#![crate_type = "lib"] + +extern crate minicore; +use minicore::*; + +#[lang = "va_arg_safe"] +pub unsafe trait VaArgSafe {} + +unsafe impl VaArgSafe for i32 {} +unsafe impl VaArgSafe for i64 {} +#[cfg(target_pointer_width = "64")] +unsafe impl VaArgSafe for i128 {} +unsafe impl VaArgSafe for f64 {} +unsafe impl VaArgSafe for *const T {} + +#[repr(transparent)] +struct VaListInner { + ptr: *const c_void, +} + +#[repr(transparent)] +#[lang = "va_list"] +pub struct VaList<'a> { + inner: VaListInner, + _marker: PhantomData<&'a mut ()>, +} + +#[rustc_intrinsic] +#[rustc_nounwind] +pub const unsafe fn va_arg(ap: &mut VaList<'_>) -> T; + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_f64(ap: &mut VaList<'_>) -> f64 { + // CHECK-LABEL: read_f64 + // + // RISCV32: lw a1, 0(a0) + // RISCV32-NEXT: addi a1, a1, 7 + // RISCV32-NEXT: andi a1, a1, -8 + // RISCV32-NEXT: fld fa0, 0(a1) + // RISCV32-NEXT: addi a1, a1, 8 + // RISCV32-NEXT: sw a1, 0(a0) + // RISCV32-NEXT: ret + // + // RISCV64: ld a1, 0(a0) + // RISCV64-NEXT: fld fa0, 0(a1) + // RISCV64-NEXT: addi a1, a1, 8 + // RISCV64-NEXT: sd a1, 0(a0) + // RISCV64-NEXT: ret + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i32(ap: &mut VaList<'_>) -> i32 { + // CHECK-LABEL: read_i32 + // + // RISCV32: lw a2, 0(a0) + // RISCV32-NEXT: lw a1, 0(a2) + // RISCV32-NEXT: addi a2, a2, 4 + // RISCV32-NEXT: sw a2, 0(a0) + // RISCV32-NEXT: mv a0, a1 + // RISCV32-NEXT: ret + // + // RISCV64: ld a2, 0(a0) + // RISCV64-NEXT: lw a1, 0(a2) + // RISCV64-NEXT: addi a2, a2, 8 + // RISCV64-NEXT: sd a2, 0(a0) + // RISCV64-NEXT: mv a0, a1 + // RISCV64-NEXT: ret + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i64(ap: &mut VaList<'_>) -> i64 { + // CHECK-LABEL: read_i64 + // + // RISCV32: lw a1, 0(a0) + // RISCV32-NEXT: addi a1, a1, 7 + // RISCV32-NEXT: andi a3, a1, -8 + // RISCV32-NEXT: lw a2, 0(a3) + // RISCV32-NEXT: lw a1, 4(a3) + // RISCV32-NEXT: addi a3, a3, 8 + // RISCV32-NEXT: sw a3, 0(a0) + // RISCV32-NEXT: mv a0, a2 + // RISCV32-NEXT: ret + // + // RISCV64: ld a2, 0(a0) + // RISCV64-NEXT: ld a1, 0(a2) + // RISCV64-NEXT: addi a2, a2, 8 + // RISCV64-NEXT: sd a2, 0(a0) + // RISCV64-NEXT: mv a0, a1 + // RISCV64-NEXT: ret + va_arg(ap) +} + +#[unsafe(no_mangle)] +#[cfg(target_pointer_width = "64")] +unsafe extern "C" fn read_i128(ap: &mut VaList<'_>) -> i128 { + // RISCV64-LABEL: read_i128 + // + // RISCV64: ld a1, 0(a0) + // RISCV64-NEXT: addi a1, a1, 15 + // RISCV64-NEXT: andi a3, a1, -16 + // RISCV64-NEXT: ld a2, 0(a3) + // RISCV64-NEXT: ld a1, 8(a3) + // RISCV64-NEXT: addi a3, a3, 16 + // RISCV64-NEXT: sd a3, 0(a0) + // RISCV64-NEXT: mv a0, a2 + // RISCV64-NEXT: ret + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_ptr(ap: &mut VaList<'_>) -> *const u8 { + // RISCV32: read_ptr = read_i32 + // RISCV64: read_ptr = read_i64 + va_arg(ap) +} diff --git a/tests/assembly-llvm/c-variadic/s390x.rs b/tests/assembly-llvm/c-variadic/s390x.rs new file mode 100644 index 0000000000000..fef239273e2c5 --- /dev/null +++ b/tests/assembly-llvm/c-variadic/s390x.rs @@ -0,0 +1,133 @@ +//@ add-minicore +//@ assembly-output: emit-asm +// +//@ compile-flags: -Copt-level=3 --target s390x-unknown-linux-gnu +//@ needs-llvm-components: systemz +#![feature(c_variadic, no_core, lang_items, intrinsics, rustc_attrs)] +#![no_core] +#![crate_type = "lib"] + +// Check that the assembly that rustc generates matches what clang emits. + +extern crate minicore; +use minicore::*; + +#[lang = "va_arg_safe"] +pub unsafe trait VaArgSafe {} + +unsafe impl VaArgSafe for i32 {} +unsafe impl VaArgSafe for i64 {} +unsafe impl VaArgSafe for i128 {} +unsafe impl VaArgSafe for f64 {} +unsafe impl VaArgSafe for *const T {} + +#[repr(transparent)] +struct VaListInner { + ptr: *const c_void, +} + +#[repr(transparent)] +#[lang = "va_list"] +pub struct VaList<'a> { + inner: VaListInner, + _marker: PhantomData<&'a mut ()>, +} + +#[rustc_intrinsic] +#[rustc_nounwind] +pub const unsafe fn va_arg(ap: &mut VaList<'_>) -> T; + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_f64(ap: &mut VaList<'_>) -> f64 { + // CHECK-LABEL: read_f64: + // CHECK: lg %r3, 8(%r2) + // CHECK-NEXT: clgijh %r3, 3, .LBB0_2 + // CHECK-NEXT: lg %r1, 24(%r2) + // CHECK-NEXT: sllg %r4, %r3, 3 + // CHECK-NEXT: la %r1, 128(%r4,%r1) + // CHECK-NEXT: la %r0, 1(%r3) + // CHECK-NEXT: stg %r0, 8(%r2) + // CHECK-NEXT: ld %f0, 0(%r1) + // CHECK-NEXT: br %r14 + // CHECK-NEXT: .LBB0_2: + // CHECK-NEXT: lg %r1, 16(%r2) + // CHECK-NEXT: la %r0, 8(%r1) + // CHECK-NEXT: stg %r0, 16(%r2) + // CHECK-NEXT: ld %f0, 0(%r1) + // CHECK-NEXT: br %r14 + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i32(ap: &mut VaList<'_>) -> i32 { + // CHECK-LABEL: read_i32: + // CHECK: lg %r3, 0(%r2) + // CHECK-NEXT: clgijh %r3, 4, .LBB1_2 + // CHECK-NEXT: lg %r1, 24(%r2) + // CHECK-NEXT: sllg %r4, %r3, 3 + // CHECK-NEXT: la %r1, 20(%r4,%r1) + // CHECK-NEXT: la %r0, 1(%r3) + // CHECK-NEXT: stg %r0, 0(%r2) + // CHECK-NEXT: lgf %r2, 0(%r1) + // CHECK-NEXT: br %r14 + // CHECK-NEXT: .LBB1_2: + // CHECK-NEXT: lg %r3, 16(%r2) + // CHECK-NEXT: la %r1, 4(%r3) + // CHECK-NEXT: la %r0, 8(%r3) + // CHECK-NEXT: stg %r0, 16(%r2) + // CHECK-NEXT: lgf %r2, 0(%r1) + // CHECK-NEXT: br %r14 + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i64(ap: &mut VaList<'_>) -> i64 { + // CHECK-LABEL: read_i64: + // CHECK: lg %r3, 0(%r2) + // CHECK-NEXT: clgijh %r3, 4, .LBB2_2 + // CHECK-NEXT: lg %r1, 24(%r2) + // CHECK-NEXT: sllg %r4, %r3, 3 + // CHECK-NEXT: la %r1, 16(%r4,%r1) + // CHECK-NEXT: la %r0, 1(%r3) + // CHECK-NEXT: stg %r0, 0(%r2) + // CHECK-NEXT: lg %r2, 0(%r1) + // CHECK-NEXT: br %r14 + // CHECK-NEXT: .LBB2_2: + // CHECK-NEXT: lg %r1, 16(%r2) + // CHECK-NEXT: la %r0, 8(%r1) + // CHECK-NEXT: stg %r0, 16(%r2) + // CHECK-NEXT: lg %r2, 0(%r1) + // CHECK-NEXT: br %r14 + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i128(ap: &mut VaList<'_>) -> i128 { + // CHECK-LABEL: read_i128: + // CHECK: lg %r4, 0(%r3) + // CHECK-NEXT: clgijh %r4, 4, .LBB3_2 + // CHECK-NEXT: lg %r1, 24(%r3) + // CHECK-NEXT: sllg %r5, %r4, 3 + // CHECK-NEXT: la %r1, 16(%r5,%r1) + // CHECK-NEXT: la %r0, 1(%r4) + // CHECK-NEXT: stg %r0, 0(%r3) + // CHECK-NEXT: lg %r1, 0(%r1) + // CHECK-NEXT: mvc 8(8,%r2), 8(%r1) + // CHECK-NEXT: mvc 0(8,%r2), 0(%r1) + // CHECK-NEXT: br %r14 + // CHECK-NEXT: .LBB3_2: + // CHECK-NEXT: lg %r1, 16(%r3) + // CHECK-NEXT: la %r0, 8(%r1) + // CHECK-NEXT: stg %r0, 16(%r3) + // CHECK-NEXT: lg %r1, 0(%r1) + // CHECK-NEXT: mvc 8(8,%r2), 8(%r1) + // CHECK-NEXT: mvc 0(8,%r2), 0(%r1) + // CHECK-NEXT: br %r14 + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_ptr(ap: &mut VaList<'_>) -> *const u8 { + // CHECK: read_ptr = read_i64 + va_arg(ap) +} diff --git a/tests/assembly-llvm/c-variadic/sparc.rs b/tests/assembly-llvm/c-variadic/sparc.rs index 6ce25119a135c..81e3313c244cc 100644 --- a/tests/assembly-llvm/c-variadic/sparc.rs +++ b/tests/assembly-llvm/c-variadic/sparc.rs @@ -21,6 +21,8 @@ pub unsafe trait VaArgSafe {} unsafe impl VaArgSafe for i32 {} unsafe impl VaArgSafe for i64 {} +#[cfg(target_pointer_width = "64")] +unsafe impl VaArgSafe for i128 {} unsafe impl VaArgSafe for f64 {} unsafe impl VaArgSafe for *const T {} @@ -105,6 +107,22 @@ unsafe extern "C" fn read_i64(ap: &mut VaList<'_>) -> i64 { va_arg(ap) } +#[unsafe(no_mangle)] +#[cfg(target_pointer_width = "64")] +unsafe extern "C" fn read_i128(ap: &mut VaList<'_>) -> i128 { + // SPARC64-LABEL: read_i128 + // SPARC64: ldx [%o0], %o1 + // SPARC64-NEXT: add %o1, 15, %o1 + // SPARC64-NEXT: and %o1, -16, %o1 + // SPARC64-NEXT: add %o1, 16, %o2 + // SPARC64-NEXT: stx %o2, [%o0] + // SPARC64-NEXT: ldx [%o1], %o0 + // SPARC64-NEXT: or %o1, 8, %o1 + // SPARC64-NEXT: retl + // SPARC64-NEXT: ldx [%o1], %o1 + va_arg(ap) +} + #[unsafe(no_mangle)] unsafe extern "C" fn read_ptr(ap: &mut VaList<'_>) -> *const u8 { // SPARC: read_ptr = read_i32 diff --git a/tests/assembly-llvm/c-variadic/wasm.rs b/tests/assembly-llvm/c-variadic/wasm.rs new file mode 100644 index 0000000000000..8d05cdb1923b4 --- /dev/null +++ b/tests/assembly-llvm/c-variadic/wasm.rs @@ -0,0 +1,224 @@ +//@ add-minicore +//@ assembly-output: emit-asm +// +//@ revisions: WASM32 WASM64 +//@ [WASM32] compile-flags: -Copt-level=3 -Zmerge-functions=disabled --target wasm32-unknown-unknown +//@ [WASM32] needs-llvm-components: webassembly +//@ [WASM64] compile-flags: -Copt-level=3 -Zmerge-functions=disabled --target wasm64-unknown-unknown +//@ [WASM64] needs-llvm-components: webassembly +#![feature(c_variadic, no_core, lang_items, intrinsics, rustc_attrs)] +#![no_core] +#![crate_type = "lib"] + +// Check that the assembly that rustc generates matches what clang emits. + +extern crate minicore; +use minicore::*; + +#[lang = "va_arg_safe"] +pub unsafe trait VaArgSafe {} + +unsafe impl VaArgSafe for i32 {} +unsafe impl VaArgSafe for i64 {} +unsafe impl VaArgSafe for i128 {} +unsafe impl VaArgSafe for f64 {} +unsafe impl VaArgSafe for *const T {} + +#[repr(transparent)] +struct VaListInner { + ptr: *const c_void, +} + +#[repr(transparent)] +#[lang = "va_list"] +pub struct VaList<'a> { + inner: VaListInner, + _marker: PhantomData<&'a mut ()>, +} + +#[rustc_intrinsic] +#[rustc_nounwind] +pub const unsafe fn va_arg(ap: &mut VaList<'_>) -> T; + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_f64(ap: &mut VaList<'_>) -> f64 { + // WASM32-LABEL: read_f64: + // WASM32: local.get 0 + // WASM32-NEXT: local.get 0 + // WASM32-NEXT: i32.load 0 + // WASM32-NEXT: i32.const 7 + // WASM32-NEXT: i32.add + // WASM32-NEXT: i32.const -8 + // WASM32-NEXT: i32.and + // WASM32-NEXT: local.tee 1 + // WASM32-NEXT: i32.const 8 + // WASM32-NEXT: i32.add + // WASM32-NEXT: i32.store 0 + // WASM32-NEXT: local.get 1 + // WASM32-NEXT: f64.load 0 + // WASM32-NEXT: end_function + // + // WASM64-LABEL: read_f64: + // WASM64: local.get 0 + // WASM64-NEXT: local.get 0 + // WASM64-NEXT: i64.load 0 + // WASM64-NEXT: i64.const 7 + // WASM64-NEXT: i64.add + // WASM64-NEXT: i64.const -8 + // WASM64-NEXT: i64.and + // WASM64-NEXT: local.tee 1 + // WASM64-NEXT: i64.const 8 + // WASM64-NEXT: i64.add + // WASM64-NEXT: i64.store 0 + // WASM64-NEXT: local.get 1 + // WASM64-NEXT: f64.load 0 + // WASM64-NEXT: end_function + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i32(ap: &mut VaList<'_>) -> i32 { + // WASM32-LABEL: read_i32: + // WASM32: local.get 0 + // WASM32-NEXT: local.get 0 + // WASM32-NEXT: i32.load 0 + // WASM32-NEXT: local.tee 1 + // WASM32-NEXT: i32.const 4 + // WASM32-NEXT: i32.add + // WASM32-NEXT: i32.store 0 + // WASM32-NEXT: local.get 1 + // WASM32-NEXT: i32.load 0 + // WASM32-NEXT: end_function + // + // WASM64-LABEL: read_i32: + // WASM64: local.get 0 + // WASM64-NEXT: local.get 0 + // WASM64-NEXT: i64.load 0 + // WASM64-NEXT: local.tee 1 + // WASM64-NEXT: i64.const 4 + // WASM64-NEXT: i64.add + // WASM64-NEXT: i64.store 0 + // WASM64-NEXT: local.get 1 + // WASM64-NEXT: i32.load 0 + // WASM64-NEXT: end_function + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_ptr(ap: &mut VaList<'_>) -> *const u8 { + // WASM32-LABEL: read_ptr: + // WASM32: local.get 0 + // WASM32-NEXT: local.get 0 + // WASM32-NEXT: i32.load 0 + // WASM32-NEXT: local.tee 1 + // WASM32-NEXT: i32.const 4 + // WASM32-NEXT: i32.add + // WASM32-NEXT: i32.store 0 + // WASM32-NEXT: local.get 1 + // WASM32-NEXT: i32.load 0 + // WASM32-NEXT: end_function + // + // WASM64-LABEL: read_ptr: + // WASM64: local.get 0 + // WASM64-NEXT: local.get 0 + // WASM64-NEXT: i64.load 0 + // WASM64-NEXT: i64.const 7 + // WASM64-NEXT: i64.add + // WASM64-NEXT: i64.const -8 + // WASM64-NEXT: i64.and + // WASM64-NEXT: local.tee 1 + // WASM64-NEXT: i64.const 8 + // WASM64-NEXT: i64.add + // WASM64-NEXT: i64.store 0 + // WASM64-NEXT: local.get 1 + // WASM64-NEXT: i64.load 0 + // WASM64-NEXT: end_function + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i64(ap: &mut VaList<'_>) -> i64 { + // WASM32-LABEL: read_i64: + // WASM32: local.get 0 + // WASM32-NEXT: local.get 0 + // WASM32-NEXT: i32.load 0 + // WASM32-NEXT: i32.const 7 + // WASM32-NEXT: i32.add + // WASM32-NEXT: i32.const -8 + // WASM32-NEXT: i32.and + // WASM32-NEXT: local.tee 1 + // WASM32-NEXT: i32.const 8 + // WASM32-NEXT: i32.add + // WASM32-NEXT: i32.store 0 + // WASM32-NEXT: local.get 1 + // WASM32-NEXT: i64.load 0 + // WASM32-NEXT: end_function + // + // WASM64-LABEL: read_i64: + // WASM64: local.get 0 + // WASM64-NEXT: local.get 0 + // WASM64-NEXT: i64.load 0 + // WASM64-NEXT: i64.const 7 + // WASM64-NEXT: i64.add + // WASM64-NEXT: i64.const -8 + // WASM64-NEXT: i64.and + // WASM64-NEXT: local.tee 1 + // WASM64-NEXT: i64.const 8 + // WASM64-NEXT: i64.add + // WASM64-NEXT: i64.store 0 + // WASM64-NEXT: local.get 1 + // WASM64-NEXT: i64.load 0 + // WASM64-NEXT: end_function + va_arg(ap) +} + +// Clang and Rustc use a different ABI for i128 on wasm32, and LLVM optimizes differently if we use +// a mutable reference instead of just a pointer. With this setup we match the equivalent Clang +// input exactly. +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i128(out: *mut i128, ap: *mut VaList<'_>) { + // WASM32-LABEL: read_i128: + // WASM32: local.get 1 + // WASM32-NEXT: local.get 1 + // WASM32-NEXT: i32.load 0 + // WASM32-NEXT: i32.const 15 + // WASM32-NEXT: i32.add + // WASM32-NEXT: i32.const -16 + // WASM32-NEXT: i32.and + // WASM32-NEXT: local.tee 2 + // WASM32-NEXT: i32.const 16 + // WASM32-NEXT: i32.add + // WASM32-NEXT: i32.store 0 + // WASM32-NEXT: local.get 0 + // WASM32-NEXT: local.get 2 + // WASM32-NEXT: i64.load 0 + // WASM32-NEXT: i64.store 0 + // WASM32-NEXT: local.get 0 + // WASM32-NEXT: local.get 2 + // WASM32-NEXT: i64.load 8 + // WASM32-NEXT: i64.store 8 + // WASM32-NEXT: end_function + // + // WASM64-LABEL: read_i128: + // WASM64: local.get 1 + // WASM64-NEXT: local.get 1 + // WASM64-NEXT: i64.load 0 + // WASM64-NEXT: i64.const 15 + // WASM64-NEXT: i64.add + // WASM64-NEXT: i64.const -16 + // WASM64-NEXT: i64.and + // WASM64-NEXT: local.tee 2 + // WASM64-NEXT: i64.const 16 + // WASM64-NEXT: i64.add + // WASM64-NEXT: i64.store 0 + // WASM64-NEXT: local.get 0 + // WASM64-NEXT: local.get 2 + // WASM64-NEXT: i64.load 0 + // WASM64-NEXT: i64.store 0 + // WASM64-NEXT: local.get 0 + // WASM64-NEXT: local.get 2 + // WASM64-NEXT: i64.load 8 + // WASM64-NEXT: i64.store 8 + // WASM64-NEXT: end_function + *out = va_arg(mem::transmute(ap)); +} diff --git a/tests/assembly-llvm/c-variadic/x86-linux.rs b/tests/assembly-llvm/c-variadic/x86-linux.rs new file mode 100644 index 0000000000000..8cc4731346b17 --- /dev/null +++ b/tests/assembly-llvm/c-variadic/x86-linux.rs @@ -0,0 +1,237 @@ +//@ add-minicore +//@ assembly-output: emit-asm +// +//@ revisions: X86_64 X86_64_GNUX32 I686 +//@ [X86_64] compile-flags: -Copt-level=3 -Cllvm-args=-x86-asm-syntax=intel +//@ [X86_64] compile-flags: --target x86_64-unknown-linux-gnu +//@ [X86_64] needs-llvm-components: x86 +//@ [X86_64_GNUX32] compile-flags: -Copt-level=3 -Cllvm-args=-x86-asm-syntax=intel +//@ [X86_64_GNUX32] compile-flags: --target x86_64-unknown-linux-gnux32 +//@ [X86_64_GNUX32] needs-llvm-components: x86 +//@ [I686] compile-flags: -Copt-level=3 -Cllvm-args=-x86-asm-syntax=intel +//@ [I686] compile-flags: --target i686-unknown-linux-gnu +//@ [I686] needs-llvm-components: x86 +#![feature(c_variadic, no_core, lang_items, intrinsics, rustc_attrs)] +#![no_core] +#![crate_type = "lib"] + +// Check that the assembly that rustc generates matches what clang emits. + +extern crate minicore; +use minicore::*; + +#[lang = "va_arg_safe"] +pub unsafe trait VaArgSafe {} + +unsafe impl VaArgSafe for i32 {} +unsafe impl VaArgSafe for i64 {} +#[cfg(target_pointer_width = "64")] +unsafe impl VaArgSafe for i128 {} +unsafe impl VaArgSafe for f64 {} +unsafe impl VaArgSafe for *const T {} + +#[repr(transparent)] +struct VaListInner { + ptr: *const c_void, +} + +#[repr(transparent)] +#[lang = "va_list"] +pub struct VaList<'a> { + inner: VaListInner, + _marker: PhantomData<&'a mut ()>, +} + +#[rustc_intrinsic] +#[rustc_nounwind] +pub const unsafe fn va_arg(ap: &mut VaList<'_>) -> T; + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_f64(ap: &mut VaList<'_>) -> f64 { + // CHECK-LABEL: read_f64 + + // X86_64: mov ecx, dword ptr [rdi + 4] + // X86_64-NEXT: cmp rcx, 160 + // X86_64-NEXT: ja .LBB0_2 + // X86_64-NEXT: mov rax, rcx + // X86_64-NEXT: add rax, qword ptr [rdi + 16] + // X86_64-NEXT: add ecx, 16 + // X86_64-NEXT: mov dword ptr [rdi + 4], ecx + // X86_64-NEXT: movsd xmm0, qword ptr [rax] + // X86_64-NEXT: ret + // X86_64-NEXT: .LBB0_2: + // X86_64-NEXT: mov rax, qword ptr [rdi + 8] + // X86_64-NEXT: lea rcx, [rax + 8] + // X86_64-NEXT: mov qword ptr [rdi + 8], rcx + // X86_64-NEXT: movsd xmm0, qword ptr [rax] + // X86_64-NEXT: ret + + // X86_64-NEXT_GNUX32: mov ecx, dword ptr [edi + 4] + // X86_64-NEXT_GNUX32-NEXT: cmp ecx, 160 + // X86_64-NEXT_GNUX32-NEXT: ja .LBB0_2 + // X86_64-NEXT_GNUX32-NEXT: mov eax, dword ptr [edi + 12] + // X86_64-NEXT_GNUX32-NEXT: add eax, ecx + // X86_64-NEXT_GNUX32-NEXT: add ecx, 16 + // X86_64-NEXT_GNUX32-NEXT: mov dword ptr [edi + 4], ecx + // X86_64-NEXT_GNUX32-NEXT: movsd xmm0, qword ptr [eax] + // X86_64-NEXT_GNUX32-NEXT: ret + // X86_64-NEXT_GNUX32-NEXT: .LBB0_2: + // X86_64-NEXT_GNUX32-NEXT: mov eax, dword ptr [edi + 8] + // X86_64-NEXT_GNUX32-NEXT: lea ecx, [rax + 8] + // X86_64-NEXT_GNUX32-NEXT: mov dword ptr [edi + 8], ecx + // X86_64-NEXT_GNUX32-NEXT: movsd xmm0, qword ptr [eax] + // X86_64-NEXT_GNUX32-NEXT: ret + + // I686: mov eax, dword ptr [esp + 4] + // I686-NEXT: mov ecx, dword ptr [eax] + // I686-NEXT: lea edx, [ecx + 8] + // I686-NEXT: mov dword ptr [eax], edx + // I686-NEXT: fld qword ptr [ecx] + // I686-NEXT: ret + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i32(ap: &mut VaList<'_>) -> i32 { + // CHECK-LABEL: read_i32 + // + // X86_64: mov ecx, dword ptr [rdi] + // X86_64-NEXT: cmp rcx, 40 + // X86_64-NEXT: ja .LBB1_2 + // X86_64-NEXT: mov rax, rcx + // X86_64-NEXT: add rax, qword ptr [rdi + 16] + // X86_64-NEXT: add ecx, 8 + // X86_64-NEXT: mov dword ptr [rdi], ecx + // X86_64-NEXT: mov eax, dword ptr [rax] + // X86_64-NEXT: ret + // X86_64-NEXT: .LBB1_2: + // X86_64-NEXT: mov rax, qword ptr [rdi + 8] + // X86_64-NEXT: lea rcx, [rax + 8] + // X86_64-NEXT: mov qword ptr [rdi + 8], rcx + // X86_64-NEXT: mov eax, dword ptr [rax] + // X86_64-NEXT: ret + + // X86_64-NEXT_GNUX32: mov ecx, dword ptr [edi] + // X86_64-NEXT_GNUX32-NEXT: cmp ecx, 40 + // X86_64-NEXT_GNUX32-NEXT: ja .LBB1_2 + // X86_64-NEXT_GNUX32-NEXT: mov eax, dword ptr [edi + 12] + // X86_64-NEXT_GNUX32-NEXT: add eax, ecx + // X86_64-NEXT_GNUX32-NEXT: add ecx, 8 + // X86_64-NEXT_GNUX32-NEXT: mov dword ptr [edi], ecx + // X86_64-NEXT_GNUX32-NEXT: mov eax, dword ptr [eax] + // X86_64-NEXT_GNUX32-NEXT: ret + // X86_64-NEXT_GNUX32-NEXT: .LBB1_2: + // X86_64-NEXT_GNUX32-NEXT: mov eax, dword ptr [edi + 8] + // X86_64-NEXT_GNUX32-NEXT: lea ecx, [rax + 8] + // X86_64-NEXT_GNUX32-NEXT: mov dword ptr [edi + 8], ecx + // X86_64-NEXT_GNUX32-NEXT: mov eax, dword ptr [eax] + // X86_64-NEXT_GNUX32-NEXT: ret + + // I686: mov eax, dword ptr [esp + 4] + // I686-NEXT: mov ecx, dword ptr [eax] + // I686-NEXT: lea edx, [ecx + 4] + // I686-NEXT: mov dword ptr [eax], edx + // I686-NEXT: mov eax, dword ptr [ecx] + // I686-NEXT: ret + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i64(ap: &mut VaList<'_>) -> i64 { + // CHECK-LABEL: read_i64 + + // X86_64: mov ecx, dword ptr [rdi] + // X86_64-NEXT: cmp rcx, 40 + // X86_64-NEXT: ja .LBB2_2 + // X86_64-NEXT: mov rax, rcx + // X86_64-NEXT: add rax, qword ptr [rdi + 16] + // X86_64-NEXT: add ecx, 8 + // X86_64-NEXT: mov dword ptr [rdi], ecx + // X86_64-NEXT: mov rax, qword ptr [rax] + // X86_64-NEXT: ret + // X86_64-NEXT: .LBB2_2: + // X86_64-NEXT: mov rax, qword ptr [rdi + 8] + // X86_64-NEXT: lea rcx, [rax + 8] + // X86_64-NEXT: mov qword ptr [rdi + 8], rcx + // X86_64-NEXT: mov rax, qword ptr [rax] + // X86_64-NEXT: ret + + // X86_64-NEXT_GNUX32: mov ecx, dword ptr [edi] + // X86_64-NEXT_GNUX32-NEXT: cmp ecx, 40 + // X86_64-NEXT_GNUX32-NEXT: ja .LBB2_2 + // X86_64-NEXT_GNUX32-NEXT: mov eax, dword ptr [edi + 12] + // X86_64-NEXT_GNUX32-NEXT: add eax, ecx + // X86_64-NEXT_GNUX32-NEXT: add ecx, 8 + // X86_64-NEXT_GNUX32-NEXT: mov dword ptr [edi], ecx + // X86_64-NEXT_GNUX32-NEXT: mov rax, qword ptr [eax] + // X86_64-NEXT_GNUX32-NEXT: ret + // X86_64-NEXT_GNUX32-NEXT: .LBB2_2: + // X86_64-NEXT_GNUX32-NEXT: mov eax, dword ptr [edi + 8] + // X86_64-NEXT_GNUX32-NEXT: lea ecx, [rax + 8] + // X86_64-NEXT_GNUX32-NEXT: mov dword ptr [edi + 8], ecx + // X86_64-NEXT_GNUX32-NEXT: mov rax, qword ptr [eax] + // X86_64-NEXT_GNUX32-NEXT: ret + + // I686: mov eax, dword ptr [esp + 4] + // I686-NEXT: mov ecx, dword ptr [eax] + // I686-NEXT: lea edx, [ecx + 8] + // I686-NEXT: mov dword ptr [eax], edx + // I686-NEXT: mov eax, dword ptr [ecx] + // I686-NEXT: mov edx, dword ptr [ecx + 4] + // I686-NEXT: ret + va_arg(ap) +} + +#[unsafe(no_mangle)] +#[cfg(target_pointer_width = "64")] +unsafe extern "C" fn read_i128(ap: &mut VaList<'_>) -> i128 { + // X86_64-LABEL: read_i128 + // + // X86_64: mov ecx, dword ptr [rdi] + // X86_64-NEXT: cmp rcx, 32 + // X86_64-NEXT: ja .LBB3_2 + // X86_64-NEXT: mov rdx, qword ptr [rdi + 16] + // X86_64-NEXT: mov rax, qword ptr [rdx + rcx] + // X86_64-NEXT: mov rdx, qword ptr [rdx + rcx + 8] + // X86_64-NEXT: add ecx, 16 + // X86_64-NEXT: mov dword ptr [rdi], ecx + // X86_64-NEXT: ret + // X86_64-NEXT: .LBB3_2: + // X86_64-NEXT: mov rcx, qword ptr [rdi + 8] + // X86_64-NEXT: add rcx, 15 + // X86_64-NEXT: and rcx, -16 + // X86_64-NEXT: lea rax, [rcx + 16] + // X86_64-NEXT: mov qword ptr [rdi + 8], rax + // X86_64-NEXT: mov rax, qword ptr [rcx] + // X86_64-NEXT: mov rdx, qword ptr [rcx + 8] + // X86_64-NEXT: ret + + // X86_64-NEXT_GNUX32: mov ecx, dword ptr [edi] + // X86_64-NEXT_GNUX32-NEXT: cmp ecx, 32 + // X86_64-NEXT_GNUX32-NEXT: ja .LBB3_2 + // X86_64-NEXT_GNUX32-NEXT: mov edx, dword ptr [edi + 12] + // X86_64-NEXT_GNUX32-NEXT: mov rax, qword ptr [edx + ecx] + // X86_64-NEXT_GNUX32-NEXT: mov rdx, qword ptr [edx + ecx + 8] + // X86_64-NEXT_GNUX32-NEXT: add ecx, 16 + // X86_64-NEXT_GNUX32-NEXT: mov dword ptr [edi], ecx + // X86_64-NEXT_GNUX32-NEXT: ret + // X86_64-NEXT_GNUX32-NEXT: .LBB3_2: + // X86_64-NEXT_GNUX32-NEXT: mov ecx, dword ptr [edi + 8] + // X86_64-NEXT_GNUX32-NEXT: add ecx, 15 + // X86_64-NEXT_GNUX32-NEXT: and ecx, -16 + // X86_64-NEXT_GNUX32-NEXT: lea eax, [rcx + 16] + // X86_64-NEXT_GNUX32-NEXT: mov dword ptr [edi + 8], eax + // X86_64-NEXT_GNUX32-NEXT: mov rax, qword ptr [ecx] + // X86_64-NEXT_GNUX32-NEXT: mov rdx, qword ptr [ecx + 8] + // X86_64-NEXT_GNUX32-NEXT: ret + + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_ptr(ap: &mut VaList<'_>) -> *const u8 { + // X86_64: read_ptr = read_i64 + // X86_64_GNUX32: read_ptr = read_i32 + // I686: read_ptr = read_i32 + va_arg(ap) +} diff --git a/tests/assembly-llvm/c-variadic/x86_64-windows.rs b/tests/assembly-llvm/c-variadic/x86_64-windows.rs new file mode 100644 index 0000000000000..fa40699af80b4 --- /dev/null +++ b/tests/assembly-llvm/c-variadic/x86_64-windows.rs @@ -0,0 +1,100 @@ +//@ add-minicore +//@ assembly-output: emit-asm +// +//@ revisions: WINDOWS_GNU WINDOWS_MSVC +//@ [WINDOWS_GNU] compile-flags: -Copt-level=3 -Cllvm-args=-x86-asm-syntax=intel +//@ [WINDOWS_GNU] compile-flags: --target x86_64-pc-windows-gnu +//@ [WINDOWS_GNU] needs-llvm-components: x86 +//@ [WINDOWS_MSVC] compile-flags: -Copt-level=3 -Cllvm-args=-x86-asm-syntax=intel +//@ [WINDOWS_MSVC] compile-flags: --target x86_64-pc-windows-msvc +//@ [WINDOWS_MSVC] needs-llvm-components: x86 +#![feature(c_variadic, no_core, lang_items, intrinsics, rustc_attrs)] +#![no_core] +#![crate_type = "lib"] + +// Check that the assembly that rustc generates matches what clang emits for +// +// ```c +// T function(va_list ap) { +// return va_arg(ap, T); +// } +// ``` + +extern crate minicore; +use minicore::*; + +#[lang = "va_arg_safe"] +pub unsafe trait VaArgSafe {} + +unsafe impl VaArgSafe for i32 {} +unsafe impl VaArgSafe for i64 {} +unsafe impl VaArgSafe for i128 {} +unsafe impl VaArgSafe for f64 {} +unsafe impl VaArgSafe for *const T {} + +#[repr(transparent)] +struct VaListInner { + ptr: *const c_void, +} + +#[repr(transparent)] +#[lang = "va_list"] +pub struct VaList<'a> { + inner: VaListInner, + _marker: PhantomData<&'a mut ()>, +} + +#[rustc_intrinsic] +#[rustc_nounwind] +pub const unsafe fn va_arg(ap: &mut VaList<'_>) -> T; + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_f64(ap: &mut VaList<'_>) -> f64 { + // CHECK-LABEL: read_f64: + // CHECK: mov rax, qword ptr [rcx] + // CHECK-NEXT: lea rdx, [rax + 8] + // CHECK-NEXT: mov qword ptr [rcx], rdx + // CHECK-NEXT: movsd xmm0, qword ptr [rax] + // CHECK-NEXT: ret + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i32(ap: &mut VaList<'_>) -> i32 { + // CHECK-LABEL: read_i32: + // CHECK: mov rax, qword ptr [rcx] + // CHECK-NEXT: lea rdx, [rax + 8] + // CHECK-NEXT: mov qword ptr [rcx], rdx + // CHECK-NEXT: mov eax, dword ptr [rax] + // CHECK-NEXT: ret + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i64(ap: &mut VaList<'_>) -> i64 { + // CHECK-LABEL: read_i64: + // CHECK: mov rax, qword ptr [rcx] + // CHECK-NEXT: lea rdx, [rax + 8] + // CHECK-NEXT: mov qword ptr [rcx], rdx + // CHECK-NEXT: mov rax, qword ptr [rax] + // CHECK-NEXT: ret + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i128(ap: &mut VaList<'_>) -> i128 { + // CHECK-LABEL: read_i128: + // CHECK: mov rax, qword ptr [rcx] + // CHECK-NEXT: lea rdx, [rax + 8] + // CHECK-NEXT: mov qword ptr [rcx], rdx + // CHECK-NEXT: mov rax, qword ptr [rax] + // CHECK-NEXT: movups xmm0, xmmword ptr [rax] + // CHECK-NEXT: ret + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_ptr(ap: &mut VaList<'_>) -> *const u8 { + // CHECK: read_ptr = read_i64 + va_arg(ap) +} diff --git a/tests/run-make/c-link-to-rust-va-list-fn/checkrust.rs b/tests/run-make/c-link-to-rust-va-list-fn/checkrust.rs index f3d82474e94aa..9fc7523ecc7f3 100644 --- a/tests/run-make/c-link-to-rust-va-list-fn/checkrust.rs +++ b/tests/run-make/c-link-to-rust-va-list-fn/checkrust.rs @@ -1,5 +1,5 @@ #![crate_type = "staticlib"] -#![feature(c_variadic)] +#![feature(c_variadic, c_variadic_int128)] use core::ffi::{CStr, VaList, c_char, c_double, c_int, c_long, c_longlong}; @@ -57,6 +57,49 @@ pub unsafe extern "C" fn check_list_copy_0(mut ap: VaList) -> usize { if compare_c_str(ap.next_arg::<*const c_char>(), c"Correct") { 0 } else { 0xff } } +#[unsafe(no_mangle)] +pub unsafe extern "C" fn check_list_i128(mut ap: VaList) -> usize { + cfg_select! { + any( + target_arch = "aarch64", + target_arch = "amdgpu", + target_arch = "arm64ec", + target_arch = "bpf", + target_arch = "loongarch64", + target_arch = "mips64", + target_arch = "mips64r6", + target_arch = "nvptx64", + target_arch = "powerpc64", + target_arch = "riscv64", + target_arch = "s390x", + target_arch = "sparc64", + target_arch = "wasm32", + target_arch = "wasm64", + target_arch = "x86_64", + ) => { + #[cfg(not(any( + target_arch = "wasm32", + target_abi = "x32", + target_pointer_width = "64", + )))] + compile_error!("unexpected target architecture for 128-bit c-variadic"); + + continue_if!(ap.next_arg::() == -42); + // use a 32-bit value here to test the alignment logic. + continue_if!(ap.next_arg::() == 0xAAAA_AAAAu32.cast_signed()); + continue_if!(ap.next_arg::() == u128::MAX); + + return 0; + } + _ => { + // This function was called a platform where rustc does not implement + // VaArgSafe for i128 but clang does define __int128. Rustc should add + // the implementation if this comes up. + 0xFF + } + } +} + #[unsafe(no_mangle)] pub unsafe extern "C" fn check_varargs_0(_: c_int, mut ap: ...) -> usize { continue_if!(ap.next_arg::() == 42); diff --git a/tests/run-make/c-link-to-rust-va-list-fn/test.c b/tests/run-make/c-link-to-rust-va-list-fn/test.c index b368302326c71..c7510a29445a5 100644 --- a/tests/run-make/c-link-to-rust-va-list-fn/test.c +++ b/tests/run-make/c-link-to-rust-va-list-fn/test.c @@ -8,6 +8,7 @@ extern size_t check_list_0(va_list ap); extern size_t check_list_1(va_list ap); extern size_t check_list_2(va_list ap); extern size_t check_list_copy_0(va_list ap); +extern size_t check_list_i128(va_list ap); extern size_t check_varargs_0(int fixed, ...); extern size_t check_varargs_1(int fixed, ...); extern size_t check_varargs_2(int fixed, ...); @@ -38,6 +39,10 @@ int main(int argc, char* argv[]) { assert(test_rust(check_list_copy_0, 6.28, 16, 'A', "Skip Me!", "Correct") == 0); +#if defined(__SIZEOF_INT128__) + assert(test_rust(check_list_i128, (__int128)-42, 0xAAAAAAAA, (unsigned __int128)-1) == 0); +#endif + assert(check_varargs_0(0, 42, "Hello, World!") == 0); assert(check_varargs_1(0, 3.14, 12l, 'A', 0x1LL) == 0); diff --git a/tests/ui/c-variadic/roundtrip.rs b/tests/ui/c-variadic/roundtrip.rs index 22a545fec5a88..ab6d61a5b8250 100644 --- a/tests/ui/c-variadic/roundtrip.rs +++ b/tests/ui/c-variadic/roundtrip.rs @@ -1,6 +1,13 @@ //@ run-pass //@ ignore-backends: gcc -#![feature(c_variadic, const_c_variadic, const_destruct, const_raw_ptr_comparison)] +#![feature( + c_variadic, + const_c_variadic, + c_variadic_int128, + const_destruct, + const_raw_ptr_comparison +)] +#![allow(unused_features)] // c_variadic_int128 is only used on 64-bit targets. use std::ffi::*; @@ -12,7 +19,8 @@ use std::ffi::*; const unsafe extern "C" fn variadic(mut ap: ...) -> (T, T) { let x = ap.next_arg::(); // Intersperse a small type to test alignment logic. A `u32` (i.e. `c_uint`) is the smallest - // type that implements `VaArgSafe`: smaller types would automatically be promoted. + // type that implements `VaArgSafe` (except on some 16-bit targets): smaller types would + // automatically be promoted. assert!(ap.next_arg::() == 0xAAAA_AAAA); let y = ap.next_arg::(); @@ -74,5 +82,38 @@ fn main() { static mut B: u32 = 2u32; roundtrip_ptr!(*const u32, &raw const A, &raw const B); roundtrip_ptr!(*mut u32, &raw mut A, &raw mut B); + + // The 128-bit integers only implement VaArgSafe on some targets, a subset of those that + // define `__int128`. We test some of those targets here. + cfg_select! { + any( + target_arch = "aarch64", + target_arch = "amdgpu", + target_arch = "arm64ec", + target_arch = "bpf", + target_arch = "loongarch64", + target_arch = "mips64", + target_arch = "mips64r6", + target_arch = "nvptx64", + target_arch = "powerpc64", + target_arch = "riscv64", + target_arch = "s390x", + target_arch = "sparc64", + target_arch = "wasm32", + target_arch = "wasm64", + target_arch = "x86_64", + ) => { + #[cfg(not(any( + target_arch = "wasm32", + target_abi = "x32", + target_pointer_width = "64", + )))] + compile_error!("unexpected target architecture for 128-bit c-variadic"); + + roundtrip!(i128, -1, -2); + roundtrip!(u128, 1, 2); + } + _ => {} + } } } diff --git a/tests/ui/feature-gates/feature-gate-diagnostic-on-type-error-malformed-args.rs b/tests/ui/feature-gates/feature-gate-diagnostic-on-type-error-malformed-args.rs new file mode 100644 index 0000000000000..5bacedcb76493 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-diagnostic-on-type-error-malformed-args.rs @@ -0,0 +1,9 @@ +//@ check-pass + +// Regression test for https://github.com/rust-lang/rust/issues/158628. + +#[diagnostic::on_type_error(unknown = "")] +//~^ WARN unknown diagnostic attribute +pub struct Foo {} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-diagnostic-on-type-error-malformed-args.stderr b/tests/ui/feature-gates/feature-gate-diagnostic-on-type-error-malformed-args.stderr new file mode 100644 index 0000000000000..c8b4aac78d6e1 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-diagnostic-on-type-error-malformed-args.stderr @@ -0,0 +1,11 @@ +warning: unknown diagnostic attribute + --> $DIR/feature-gate-diagnostic-on-type-error-malformed-args.rs:5:15 + | +LL | #[diagnostic::on_type_error(unknown = "")] + | ^^^^^^^^^^^^^ + | + = help: add `#![feature(diagnostic_on_type_error)]` to the crate attributes to enable + = note: `#[warn(unknown_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +warning: 1 warning emitted +