@@ -7,7 +7,7 @@ use rustc_codegen_ssa::traits::{
77} ;
88use rustc_middle:: ty:: Ty ;
99use rustc_middle:: ty:: layout:: { HasTyCtxt , LayoutOf } ;
10- use rustc_target:: spec:: Arch ;
10+ use rustc_target:: spec:: { Arch , Env } ;
1111
1212use crate :: builder:: Builder ;
1313use crate :: llvm:: { Type , Value } ;
@@ -780,6 +780,91 @@ fn x86_64_sysv64_va_arg_from_memory<'ll, 'tcx>(
780780 mem_addr
781781}
782782
783+ fn emit_hexagon_va_arg < ' ll , ' tcx > (
784+ bx : & mut Builder < ' _ , ' ll , ' tcx > ,
785+ list : OperandRef < ' tcx , & ' ll Value > ,
786+ target_ty : Ty < ' tcx > ,
787+ ) -> & ' ll Value {
788+ // Implementation of va_arg for Hexagon musl target.
789+ // Based on LLVM's HexagonBuiltinVaList implementation.
790+ //
791+ // struct __va_list_tag {
792+ // void *__current_saved_reg_area_pointer;
793+ // void *__saved_reg_area_end_pointer;
794+ // void *__overflow_area_pointer;
795+ // };
796+ //
797+ // All variadic arguments are passed on the stack, but the musl implementation
798+ // uses a register save area for compatibility.
799+ let va_list_addr = list. immediate ( ) ;
800+ let layout = bx. cx . layout_of ( target_ty) ;
801+ let ptr_align_abi = bx. tcx ( ) . data_layout . pointer_align ( ) . abi ;
802+ let ptr_size = bx. tcx ( ) . data_layout . pointer_size ( ) . bytes ( ) ;
803+
804+ // Check if argument fits in register save area
805+ let maybe_reg = bx. append_sibling_block ( "va_arg.maybe_reg" ) ;
806+ let from_overflow = bx. append_sibling_block ( "va_arg.from_overflow" ) ;
807+ let end = bx. append_sibling_block ( "va_arg.end" ) ;
808+
809+ // Load the three pointers from va_list
810+ let current_ptr_addr = va_list_addr;
811+ let end_ptr_addr = bx. inbounds_ptradd ( va_list_addr, bx. const_usize ( ptr_size) ) ;
812+ let overflow_ptr_addr = bx. inbounds_ptradd ( va_list_addr, bx. const_usize ( 2 * ptr_size) ) ;
813+
814+ let current_ptr = bx. load ( bx. type_ptr ( ) , current_ptr_addr, ptr_align_abi) ;
815+ let end_ptr = bx. load ( bx. type_ptr ( ) , end_ptr_addr, ptr_align_abi) ;
816+ let overflow_ptr = bx. load ( bx. type_ptr ( ) , overflow_ptr_addr, ptr_align_abi) ;
817+
818+ // Align current pointer based on argument type size (following LLVM's implementation)
819+ // Arguments <= 32 bits (4 bytes) use 4-byte alignment, > 32 bits use 8-byte alignment
820+ let type_size_bits = bx. cx . size_of ( target_ty) . bits ( ) ;
821+ let arg_align = if type_size_bits > 32 {
822+ Align :: from_bytes ( 8 ) . unwrap ( )
823+ } else {
824+ Align :: from_bytes ( 4 ) . unwrap ( )
825+ } ;
826+ let aligned_current = round_pointer_up_to_alignment ( bx, current_ptr, arg_align, bx. type_ptr ( ) ) ;
827+
828+ // Calculate next pointer position (following LLVM's logic)
829+ // Arguments <= 32 bits take 4 bytes, > 32 bits take 8 bytes
830+ let arg_size = if type_size_bits > 32 { 8 } else { 4 } ;
831+ let next_ptr = bx. inbounds_ptradd ( aligned_current, bx. const_usize ( arg_size) ) ;
832+
833+ // Check if argument fits in register save area
834+ let fits_in_regs = bx. icmp ( IntPredicate :: IntULE , next_ptr, end_ptr) ;
835+ bx. cond_br ( fits_in_regs, maybe_reg, from_overflow) ;
836+
837+ // Load from register save area
838+ bx. switch_to_block ( maybe_reg) ;
839+ let reg_value_addr = aligned_current;
840+ // Update current pointer
841+ bx. store ( next_ptr, current_ptr_addr, ptr_align_abi) ;
842+ bx. br ( end) ;
843+
844+ // Load from overflow area (stack)
845+ bx. switch_to_block ( from_overflow) ;
846+
847+ // Align overflow pointer using the same alignment rules
848+ let aligned_overflow =
849+ round_pointer_up_to_alignment ( bx, overflow_ptr, arg_align, bx. type_ptr ( ) ) ;
850+
851+ let overflow_value_addr = aligned_overflow;
852+ // Update overflow pointer - use the same size calculation
853+ let next_overflow = bx. inbounds_ptradd ( aligned_overflow, bx. const_usize ( arg_size) ) ;
854+ bx. store ( next_overflow, overflow_ptr_addr, ptr_align_abi) ;
855+
856+ // IMPORTANT: Also update the current saved register area pointer to match
857+ // This synchronizes the pointers when switching to overflow area
858+ bx. store ( next_overflow, current_ptr_addr, ptr_align_abi) ;
859+ bx. br ( end) ;
860+
861+ // Return the value
862+ bx. switch_to_block ( end) ;
863+ let value_addr =
864+ bx. phi ( bx. type_ptr ( ) , & [ reg_value_addr, overflow_value_addr] , & [ maybe_reg, from_overflow] ) ;
865+ bx. load ( layout. llvm_type ( bx) , value_addr, layout. align . abi )
866+ }
867+
783868fn emit_xtensa_va_arg < ' ll , ' tcx > (
784869 bx : & mut Builder < ' _ , ' ll , ' tcx > ,
785870 list : OperandRef < ' tcx , & ' ll Value > ,
@@ -964,6 +1049,7 @@ pub(super) fn emit_va_arg<'ll, 'tcx>(
9641049 // This includes `target.is_like_darwin`, which on x86_64 targets is like sysv64.
9651050 Arch :: X86_64 => emit_x86_64_sysv64_va_arg ( bx, addr, target_ty) ,
9661051 Arch :: Xtensa => emit_xtensa_va_arg ( bx, addr, target_ty) ,
1052+ Arch :: Hexagon if target. env == Env :: Musl => emit_hexagon_va_arg ( bx, addr, target_ty) ,
9671053 // For all other architecture/OS combinations fall back to using
9681054 // the LLVM va_arg instruction.
9691055 // https://llvm.org/docs/LangRef.html#va-arg-instruction
0 commit comments