diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index f04d4ceea35a1..620878fb87a7e 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -13,6 +13,21 @@ impl NoArgsAttributeParser for RustcMainParser { const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcMain; } +pub(crate) struct RustcNeverReturnsNullPointerParser; + +impl NoArgsAttributeParser for RustcNeverReturnsNullPointerParser { + const PATH: &[Symbol] = &[sym::rustc_never_returns_null_ptr]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Fn), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::Trait { body: false })), + Allow(Target::Method(MethodKind::Trait { body: true })), + Allow(Target::Method(MethodKind::TraitImpl)), + ]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNeverReturnsNullPointer; +} + pub(crate) struct RustcLayoutScalarValidRangeStartParser; impl SingleAttributeParser for RustcLayoutScalarValidRangeStartParser { diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 9e83ea4114937..54c9785a42f6c 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -61,8 +61,9 @@ use crate::attributes::prototype::CustomMirParser; use crate::attributes::repr::{AlignParser, AlignStaticParser, ReprParser}; use crate::attributes::rustc_internal::{ RustcLayoutScalarValidRangeEndParser, RustcLayoutScalarValidRangeStartParser, - RustcLegacyConstGenericsParser, RustcMainParser, RustcObjectLifetimeDefaultParser, - RustcScalableVectorParser, RustcSimdMonomorphizeLaneLimitParser, + RustcLegacyConstGenericsParser, RustcMainParser, RustcNeverReturnsNullPointerParser, + RustcObjectLifetimeDefaultParser, RustcScalableVectorParser, + RustcSimdMonomorphizeLaneLimitParser, }; use crate::attributes::semantics::MayDangleParser; use crate::attributes::stability::{ @@ -255,6 +256,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index 8d77c25bbf907..b23415a732cc7 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -7,7 +7,7 @@ use rustc_codegen_ssa::traits::{ }; use rustc_middle::ty::Ty; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; -use rustc_target::spec::{Abi, Arch}; +use rustc_target::spec::{Abi, Arch, Env}; use crate::builder::Builder; use crate::llvm::{Type, Value}; @@ -782,6 +782,129 @@ fn x86_64_sysv64_va_arg_from_memory<'ll, 'tcx>( mem_addr } +fn emit_hexagon_va_arg_musl<'ll, 'tcx>( + bx: &mut Builder<'_, 'll, 'tcx>, + list: OperandRef<'tcx, &'ll Value>, + target_ty: Ty<'tcx>, +) -> &'ll Value { + // Implementation of va_arg for Hexagon musl target. + // Based on LLVM's HexagonBuiltinVaList implementation. + // + // struct __va_list_tag { + // void *__current_saved_reg_area_pointer; + // void *__saved_reg_area_end_pointer; + // void *__overflow_area_pointer; + // }; + // + // All variadic arguments are passed on the stack, but the musl implementation + // uses a register save area for compatibility. + let va_list_addr = list.immediate(); + let layout = bx.cx.layout_of(target_ty); + let ptr_align_abi = bx.tcx().data_layout.pointer_align().abi; + let ptr_size = bx.tcx().data_layout.pointer_size().bytes(); + + // Check if argument fits in register save area + let maybe_reg = bx.append_sibling_block("va_arg.maybe_reg"); + let from_overflow = bx.append_sibling_block("va_arg.from_overflow"); + let end = bx.append_sibling_block("va_arg.end"); + + // Load the three pointers from va_list + let current_ptr_addr = va_list_addr; + let end_ptr_addr = bx.inbounds_ptradd(va_list_addr, bx.const_usize(ptr_size)); + let overflow_ptr_addr = bx.inbounds_ptradd(va_list_addr, bx.const_usize(2 * ptr_size)); + + let current_ptr = bx.load(bx.type_ptr(), current_ptr_addr, ptr_align_abi); + let end_ptr = bx.load(bx.type_ptr(), end_ptr_addr, ptr_align_abi); + let overflow_ptr = bx.load(bx.type_ptr(), overflow_ptr_addr, ptr_align_abi); + + // Align current pointer based on argument type size (following LLVM's implementation) + // Arguments <= 32 bits (4 bytes) use 4-byte alignment, > 32 bits use 8-byte alignment + let type_size_bits = bx.cx.size_of(target_ty).bits(); + let arg_align = if type_size_bits > 32 { + Align::from_bytes(8).unwrap() + } else { + Align::from_bytes(4).unwrap() + }; + let aligned_current = round_pointer_up_to_alignment(bx, current_ptr, arg_align, bx.type_ptr()); + + // Calculate next pointer position (following LLVM's logic) + // Arguments <= 32 bits take 4 bytes, > 32 bits take 8 bytes + let arg_size = if type_size_bits > 32 { 8 } else { 4 }; + let next_ptr = bx.inbounds_ptradd(aligned_current, bx.const_usize(arg_size)); + + // Check if argument fits in register save area + let fits_in_regs = bx.icmp(IntPredicate::IntULE, next_ptr, end_ptr); + bx.cond_br(fits_in_regs, maybe_reg, from_overflow); + + // Load from register save area + bx.switch_to_block(maybe_reg); + let reg_value_addr = aligned_current; + // Update current pointer + bx.store(next_ptr, current_ptr_addr, ptr_align_abi); + bx.br(end); + + // Load from overflow area (stack) + bx.switch_to_block(from_overflow); + + // Align overflow pointer using the same alignment rules + let aligned_overflow = + round_pointer_up_to_alignment(bx, overflow_ptr, arg_align, bx.type_ptr()); + + let overflow_value_addr = aligned_overflow; + // Update overflow pointer - use the same size calculation + let next_overflow = bx.inbounds_ptradd(aligned_overflow, bx.const_usize(arg_size)); + bx.store(next_overflow, overflow_ptr_addr, ptr_align_abi); + + // IMPORTANT: Also update the current saved register area pointer to match + // This synchronizes the pointers when switching to overflow area + bx.store(next_overflow, current_ptr_addr, ptr_align_abi); + bx.br(end); + + // Return the value + bx.switch_to_block(end); + let value_addr = + bx.phi(bx.type_ptr(), &[reg_value_addr, overflow_value_addr], &[maybe_reg, from_overflow]); + bx.load(layout.llvm_type(bx), value_addr, layout.align.abi) +} + +fn emit_hexagon_va_arg_bare_metal<'ll, 'tcx>( + bx: &mut Builder<'_, 'll, 'tcx>, + list: OperandRef<'tcx, &'ll Value>, + target_ty: Ty<'tcx>, +) -> &'ll Value { + // Implementation of va_arg for Hexagon bare-metal (non-musl) targets. + // Based on LLVM's EmitVAArgForHexagon implementation. + // + // va_list is a simple pointer (char *) + let va_list_addr = list.immediate(); + let layout = bx.cx.layout_of(target_ty); + let ptr_align_abi = bx.tcx().data_layout.pointer_align().abi; + + // Load current pointer from va_list + let current_ptr = bx.load(bx.type_ptr(), va_list_addr, ptr_align_abi); + + // Handle address alignment for types with alignment > 4 bytes + let ty_align = layout.align.abi; + let aligned_ptr = if ty_align.bytes() > 4 { + // Ensure alignment is a power of 2 + debug_assert!(ty_align.bytes().is_power_of_two(), "Alignment is not power of 2!"); + round_pointer_up_to_alignment(bx, current_ptr, ty_align, bx.type_ptr()) + } else { + current_ptr + }; + + // Calculate offset: round up type size to 4-byte boundary (minimum stack slot size) + let type_size = layout.size.bytes(); + let offset = type_size.next_multiple_of(4); // align to 4 bytes + + // Update va_list to point to next argument + let next_ptr = bx.inbounds_ptradd(aligned_ptr, bx.const_usize(offset)); + bx.store(next_ptr, va_list_addr, ptr_align_abi); + + // Load and return the argument value + bx.load(layout.llvm_type(bx), aligned_ptr, layout.align.abi) +} + fn emit_xtensa_va_arg<'ll, 'tcx>( bx: &mut Builder<'_, 'll, 'tcx>, list: OperandRef<'tcx, &'ll Value>, @@ -966,6 +1089,13 @@ pub(super) fn emit_va_arg<'ll, 'tcx>( // This includes `target.is_like_darwin`, which on x86_64 targets is like sysv64. Arch::X86_64 => emit_x86_64_sysv64_va_arg(bx, addr, target_ty), Arch::Xtensa => emit_xtensa_va_arg(bx, addr, target_ty), + Arch::Hexagon => { + if target.env == Env::Musl { + emit_hexagon_va_arg_musl(bx, addr, target_ty) + } else { + emit_hexagon_va_arg_bare_metal(bx, addr, target_ty) + } + } // For all other architecture/OS combinations fall back to using // the LLVM va_arg instruction. // https://llvm.org/docs/LangRef.html#va-arg-instruction diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index fb3d9a6368ab0..4a81c4ebe1e7f 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -878,6 +878,9 @@ pub enum AttributeKind { /// Represents `#[rustc_main]`. RustcMain, + /// Represents `#[rustc_never_returns_null_ptr]` + RustcNeverReturnsNullPointer, + /// Represents `#[rustc_object_lifetime_default]`. RustcObjectLifetimeDefault, diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 56446e8959c10..e631d6a2ab872 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -95,6 +95,7 @@ impl AttributeKind { RustcLayoutScalarValidRangeStart(..) => Yes, RustcLegacyConstGenerics { .. } => Yes, RustcMain => No, + RustcNeverReturnsNullPointer => Yes, RustcObjectLifetimeDefault => No, RustcPassIndirectlyInNonRusticAbis(..) => No, RustcScalableVector { .. } => Yes, diff --git a/compiler/rustc_lint/src/ptr_nulls.rs b/compiler/rustc_lint/src/ptr_nulls.rs index b2fa0fba76d98..b89e00dcbaae3 100644 --- a/compiler/rustc_lint/src/ptr_nulls.rs +++ b/compiler/rustc_lint/src/ptr_nulls.rs @@ -1,5 +1,6 @@ use rustc_ast::LitKind; -use rustc_hir::{BinOpKind, Expr, ExprKind, TyKind}; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::{BinOpKind, Expr, ExprKind, TyKind, find_attr}; use rustc_middle::ty::RawPtr; use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::{Span, sym}; @@ -72,14 +73,14 @@ fn useless_check<'a, 'tcx: 'a>( e = e.peel_blocks(); if let ExprKind::MethodCall(_, _expr, [], _) = e.kind && let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) - && cx.tcx.has_attr(def_id, sym::rustc_never_returns_null_ptr) + && find_attr!(cx.tcx.get_all_attrs(def_id), AttributeKind::RustcNeverReturnsNullPointer) && let Some(fn_name) = cx.tcx.opt_item_ident(def_id) { return Some(UselessPtrNullChecksDiag::FnRet { fn_name }); } else if let ExprKind::Call(path, _args) = e.kind && let ExprKind::Path(ref qpath) = path.kind && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() - && cx.tcx.has_attr(def_id, sym::rustc_never_returns_null_ptr) + && find_attr!(cx.tcx.get_all_attrs(def_id), AttributeKind::RustcNeverReturnsNullPointer) && let Some(fn_name) = cx.tcx.opt_item_ident(def_id) { return Some(UselessPtrNullChecksDiag::FnRet { fn_name }); diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index d8ecbbc491388..d1f96721fc65d 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -257,6 +257,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::NoLink | AttributeKind::RustcLayoutScalarValidRangeStart(..) | AttributeKind::RustcLayoutScalarValidRangeEnd(..) + | AttributeKind::RustcNeverReturnsNullPointer | AttributeKind::RustcScalableVector { .. } | AttributeKind::RustcSimdMonomorphizeLaneLimit(..) | AttributeKind::RustcShouldNotBeCalledOnConstItems(..) @@ -307,9 +308,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { [sym::rustc_no_implicit_autorefs, ..] => { self.check_applied_to_fn_or_method(hir_id, attr.span(), span, target) } - [sym::rustc_never_returns_null_ptr, ..] => { - self.check_applied_to_fn_or_method(hir_id, attr.span(), span, target) - } [sym::rustc_lint_query_instability, ..] => { self.check_applied_to_fn_or_method(hir_id, attr.span(), span, target) } diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl index a4166951e350a..2fdce628f0c99 100644 --- a/compiler/rustc_session/messages.ftl +++ b/compiler/rustc_session/messages.ftl @@ -57,7 +57,6 @@ session_int_literal_too_large = integer literal is too large .note = value exceeds limit of `{$limit}` session_invalid_character_in_crate_name = invalid character {$character} in crate name: `{$crate_name}` - .help = you can either pass `--crate-name` on the command line or add `#![crate_name = "…"]` to set the crate name session_invalid_float_literal_suffix = invalid suffix `{$suffix}` for float literal .label = invalid suffix `{$suffix}` diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index 07b41a3de714d..1f2d386a23cbc 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -238,8 +238,6 @@ pub(crate) struct InvalidCharacterInCrateName { pub(crate) span: Option, pub(crate) character: char, pub(crate) crate_name: Symbol, - #[help] - pub(crate) help: Option<()>, } #[derive(Subdiagnostic)] diff --git a/compiler/rustc_session/src/output.rs b/compiler/rustc_session/src/output.rs index 99c736c3b1a5e..de8f1d6009ab1 100644 --- a/compiler/rustc_session/src/output.rs +++ b/compiler/rustc_session/src/output.rs @@ -69,7 +69,6 @@ pub fn validate_crate_name(sess: &Session, crate_name: Symbol, span: Option