From 0c1612eb98b42e5faa9a366774c4f7fb21c41772 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Sun, 14 Dec 2025 00:50:00 -0600 Subject: [PATCH 1/5] Implement va_arg for Hexagon Linux musl targets Implements proper variadic argument handling for hexagon-unknown-linux-musl targets using a 3-pointer VaList structure compatible with LLVM's HexagonBuiltinVaList implementation. * Handles register save area vs overflow area transition * Provides proper 4-byte and 8-byte alignment for arguments * Only activates for hexagon+musl targets via Arch::Hexagon & Env::Musl --- compiler/rustc_codegen_llvm/src/va_arg.rs | 88 ++++++++++++++++++++++- 1 file changed, 87 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index add25da025b2d..aa5776b298d28 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}; @@ -780,6 +780,91 @@ fn x86_64_sysv64_va_arg_from_memory<'ll, 'tcx>( mem_addr } +fn emit_hexagon_va_arg<'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_xtensa_va_arg<'ll, 'tcx>( bx: &mut Builder<'_, 'll, 'tcx>, list: OperandRef<'tcx, &'ll Value>, @@ -964,6 +1049,7 @@ 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(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 From d8a0f800f7cc6b977e2bde9b28ad97964ed4381b Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Mon, 15 Dec 2025 16:22:55 -0600 Subject: [PATCH 2/5] fixup! Implement va_arg for Hexagon Linux musl targets --- compiler/rustc_codegen_llvm/src/va_arg.rs | 55 ++++++++++++++++++++++- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index aa5776b298d28..82cc45d161c43 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -780,7 +780,7 @@ fn x86_64_sysv64_va_arg_from_memory<'ll, 'tcx>( mem_addr } -fn emit_hexagon_va_arg<'ll, 'tcx>( +fn emit_hexagon_va_arg_musl<'ll, 'tcx>( bx: &mut Builder<'_, 'll, 'tcx>, list: OperandRef<'tcx, &'ll Value>, target_ty: Ty<'tcx>, @@ -865,6 +865,57 @@ fn emit_hexagon_va_arg<'ll, 'tcx>( 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 + 3) / 4) * 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_hexagon_va_arg<'ll, 'tcx>( + bx: &mut Builder<'_, 'll, 'tcx>, + list: OperandRef<'tcx, &'ll Value>, + target_ty: Ty<'tcx>, + is_musl: bool, +) -> &'ll Value { + if is_musl { + emit_hexagon_va_arg_musl(bx, list, target_ty) + } else { + emit_hexagon_va_arg_bare_metal(bx, list, target_ty) + } +} + fn emit_xtensa_va_arg<'ll, 'tcx>( bx: &mut Builder<'_, 'll, 'tcx>, list: OperandRef<'tcx, &'ll Value>, @@ -1049,7 +1100,7 @@ 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(bx, addr, target_ty), + Arch::Hexagon => emit_hexagon_va_arg(bx, addr, target_ty, target.env == Env::Musl), // For all other architecture/OS combinations fall back to using // the LLVM va_arg instruction. // https://llvm.org/docs/LangRef.html#va-arg-instruction From 4ff4b255d078225d9c0244f9c4f0ade2ae5bb0d5 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Tue, 16 Dec 2025 21:05:13 -0600 Subject: [PATCH 3/5] fixup! Implement va_arg for Hexagon Linux musl targets --- compiler/rustc_codegen_llvm/src/va_arg.rs | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index 82cc45d161c43..163b83ee08cd4 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -893,7 +893,7 @@ fn emit_hexagon_va_arg_bare_metal<'ll, 'tcx>( // Calculate offset: round up type size to 4-byte boundary (minimum stack slot size) let type_size = layout.size.bytes(); - let offset = ((type_size + 3) / 4) * 4; // align to 4 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)); @@ -903,19 +903,6 @@ fn emit_hexagon_va_arg_bare_metal<'ll, 'tcx>( bx.load(layout.llvm_type(bx), aligned_ptr, layout.align.abi) } -fn emit_hexagon_va_arg<'ll, 'tcx>( - bx: &mut Builder<'_, 'll, 'tcx>, - list: OperandRef<'tcx, &'ll Value>, - target_ty: Ty<'tcx>, - is_musl: bool, -) -> &'ll Value { - if is_musl { - emit_hexagon_va_arg_musl(bx, list, target_ty) - } else { - emit_hexagon_va_arg_bare_metal(bx, list, target_ty) - } -} - fn emit_xtensa_va_arg<'ll, 'tcx>( bx: &mut Builder<'_, 'll, 'tcx>, list: OperandRef<'tcx, &'ll Value>, @@ -1100,7 +1087,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 => emit_hexagon_va_arg(bx, addr, target_ty, target.env == Env::Musl), + 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 From 25cc98f1168f7e7c40718bb00deefb9112b71282 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Mon, 15 Dec 2025 20:35:01 -0800 Subject: [PATCH 4/5] Avoid unhelpful suggestion when crate name is invalid Pointing out they can set the crate's name is non-actionable: their problem is they found out how and set it incorrectly. Remove extraneous information that can only confuse the matter. --- compiler/rustc_session/messages.ftl | 1 - compiler/rustc_session/src/errors.rs | 2 -- compiler/rustc_session/src/output.rs | 1 - .../compile-flags/invalid/need-crate-arg-ignore-tidy$x.stderr | 2 -- 4 files changed, 6 deletions(-) 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 Date: Wed, 17 Dec 2025 11:54:35 +0100 Subject: [PATCH 5/5] Port `#[rustc_never_returns_null_ptr]` to attribute parser --- .../src/attributes/rustc_internal.rs | 15 +++++++++++++++ compiler/rustc_attr_parsing/src/context.rs | 6 ++++-- compiler/rustc_hir/src/attrs/data_structures.rs | 3 +++ .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_lint/src/ptr_nulls.rs | 7 ++++--- compiler/rustc_passes/src/check_attr.rs | 4 +--- 6 files changed, 28 insertions(+), 8 deletions(-) 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_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) }