@@ -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:: { Abi , Arch } ;
10+ use rustc_target:: spec:: { Abi , Arch , Env } ;
1111
1212use crate :: builder:: Builder ;
1313use crate :: llvm:: { Type , Value } ;
@@ -782,6 +782,129 @@ fn x86_64_sysv64_va_arg_from_memory<'ll, 'tcx>(
782782 mem_addr
783783}
784784
785+ fn emit_hexagon_va_arg_musl < ' ll , ' tcx > (
786+ bx : & mut Builder < ' _ , ' ll , ' tcx > ,
787+ list : OperandRef < ' tcx , & ' ll Value > ,
788+ target_ty : Ty < ' tcx > ,
789+ ) -> & ' ll Value {
790+ // Implementation of va_arg for Hexagon musl target.
791+ // Based on LLVM's HexagonBuiltinVaList implementation.
792+ //
793+ // struct __va_list_tag {
794+ // void *__current_saved_reg_area_pointer;
795+ // void *__saved_reg_area_end_pointer;
796+ // void *__overflow_area_pointer;
797+ // };
798+ //
799+ // All variadic arguments are passed on the stack, but the musl implementation
800+ // uses a register save area for compatibility.
801+ let va_list_addr = list. immediate ( ) ;
802+ let layout = bx. cx . layout_of ( target_ty) ;
803+ let ptr_align_abi = bx. tcx ( ) . data_layout . pointer_align ( ) . abi ;
804+ let ptr_size = bx. tcx ( ) . data_layout . pointer_size ( ) . bytes ( ) ;
805+
806+ // Check if argument fits in register save area
807+ let maybe_reg = bx. append_sibling_block ( "va_arg.maybe_reg" ) ;
808+ let from_overflow = bx. append_sibling_block ( "va_arg.from_overflow" ) ;
809+ let end = bx. append_sibling_block ( "va_arg.end" ) ;
810+
811+ // Load the three pointers from va_list
812+ let current_ptr_addr = va_list_addr;
813+ let end_ptr_addr = bx. inbounds_ptradd ( va_list_addr, bx. const_usize ( ptr_size) ) ;
814+ let overflow_ptr_addr = bx. inbounds_ptradd ( va_list_addr, bx. const_usize ( 2 * ptr_size) ) ;
815+
816+ let current_ptr = bx. load ( bx. type_ptr ( ) , current_ptr_addr, ptr_align_abi) ;
817+ let end_ptr = bx. load ( bx. type_ptr ( ) , end_ptr_addr, ptr_align_abi) ;
818+ let overflow_ptr = bx. load ( bx. type_ptr ( ) , overflow_ptr_addr, ptr_align_abi) ;
819+
820+ // Align current pointer based on argument type size (following LLVM's implementation)
821+ // Arguments <= 32 bits (4 bytes) use 4-byte alignment, > 32 bits use 8-byte alignment
822+ let type_size_bits = bx. cx . size_of ( target_ty) . bits ( ) ;
823+ let arg_align = if type_size_bits > 32 {
824+ Align :: from_bytes ( 8 ) . unwrap ( )
825+ } else {
826+ Align :: from_bytes ( 4 ) . unwrap ( )
827+ } ;
828+ let aligned_current = round_pointer_up_to_alignment ( bx, current_ptr, arg_align, bx. type_ptr ( ) ) ;
829+
830+ // Calculate next pointer position (following LLVM's logic)
831+ // Arguments <= 32 bits take 4 bytes, > 32 bits take 8 bytes
832+ let arg_size = if type_size_bits > 32 { 8 } else { 4 } ;
833+ let next_ptr = bx. inbounds_ptradd ( aligned_current, bx. const_usize ( arg_size) ) ;
834+
835+ // Check if argument fits in register save area
836+ let fits_in_regs = bx. icmp ( IntPredicate :: IntULE , next_ptr, end_ptr) ;
837+ bx. cond_br ( fits_in_regs, maybe_reg, from_overflow) ;
838+
839+ // Load from register save area
840+ bx. switch_to_block ( maybe_reg) ;
841+ let reg_value_addr = aligned_current;
842+ // Update current pointer
843+ bx. store ( next_ptr, current_ptr_addr, ptr_align_abi) ;
844+ bx. br ( end) ;
845+
846+ // Load from overflow area (stack)
847+ bx. switch_to_block ( from_overflow) ;
848+
849+ // Align overflow pointer using the same alignment rules
850+ let aligned_overflow =
851+ round_pointer_up_to_alignment ( bx, overflow_ptr, arg_align, bx. type_ptr ( ) ) ;
852+
853+ let overflow_value_addr = aligned_overflow;
854+ // Update overflow pointer - use the same size calculation
855+ let next_overflow = bx. inbounds_ptradd ( aligned_overflow, bx. const_usize ( arg_size) ) ;
856+ bx. store ( next_overflow, overflow_ptr_addr, ptr_align_abi) ;
857+
858+ // IMPORTANT: Also update the current saved register area pointer to match
859+ // This synchronizes the pointers when switching to overflow area
860+ bx. store ( next_overflow, current_ptr_addr, ptr_align_abi) ;
861+ bx. br ( end) ;
862+
863+ // Return the value
864+ bx. switch_to_block ( end) ;
865+ let value_addr =
866+ bx. phi ( bx. type_ptr ( ) , & [ reg_value_addr, overflow_value_addr] , & [ maybe_reg, from_overflow] ) ;
867+ bx. load ( layout. llvm_type ( bx) , value_addr, layout. align . abi )
868+ }
869+
870+ fn emit_hexagon_va_arg_bare_metal < ' ll , ' tcx > (
871+ bx : & mut Builder < ' _ , ' ll , ' tcx > ,
872+ list : OperandRef < ' tcx , & ' ll Value > ,
873+ target_ty : Ty < ' tcx > ,
874+ ) -> & ' ll Value {
875+ // Implementation of va_arg for Hexagon bare-metal (non-musl) targets.
876+ // Based on LLVM's EmitVAArgForHexagon implementation.
877+ //
878+ // va_list is a simple pointer (char *)
879+ let va_list_addr = list. immediate ( ) ;
880+ let layout = bx. cx . layout_of ( target_ty) ;
881+ let ptr_align_abi = bx. tcx ( ) . data_layout . pointer_align ( ) . abi ;
882+
883+ // Load current pointer from va_list
884+ let current_ptr = bx. load ( bx. type_ptr ( ) , va_list_addr, ptr_align_abi) ;
885+
886+ // Handle address alignment for types with alignment > 4 bytes
887+ let ty_align = layout. align . abi ;
888+ let aligned_ptr = if ty_align. bytes ( ) > 4 {
889+ // Ensure alignment is a power of 2
890+ debug_assert ! ( ty_align. bytes( ) . is_power_of_two( ) , "Alignment is not power of 2!" ) ;
891+ round_pointer_up_to_alignment ( bx, current_ptr, ty_align, bx. type_ptr ( ) )
892+ } else {
893+ current_ptr
894+ } ;
895+
896+ // Calculate offset: round up type size to 4-byte boundary (minimum stack slot size)
897+ let type_size = layout. size . bytes ( ) ;
898+ let offset = type_size. next_multiple_of ( 4 ) ; // align to 4 bytes
899+
900+ // Update va_list to point to next argument
901+ let next_ptr = bx. inbounds_ptradd ( aligned_ptr, bx. const_usize ( offset) ) ;
902+ bx. store ( next_ptr, va_list_addr, ptr_align_abi) ;
903+
904+ // Load and return the argument value
905+ bx. load ( layout. llvm_type ( bx) , aligned_ptr, layout. align . abi )
906+ }
907+
785908fn emit_xtensa_va_arg < ' ll , ' tcx > (
786909 bx : & mut Builder < ' _ , ' ll , ' tcx > ,
787910 list : OperandRef < ' tcx , & ' ll Value > ,
@@ -966,6 +1089,13 @@ pub(super) fn emit_va_arg<'ll, 'tcx>(
9661089 // This includes `target.is_like_darwin`, which on x86_64 targets is like sysv64.
9671090 Arch :: X86_64 => emit_x86_64_sysv64_va_arg ( bx, addr, target_ty) ,
9681091 Arch :: Xtensa => emit_xtensa_va_arg ( bx, addr, target_ty) ,
1092+ Arch :: Hexagon => {
1093+ if target. env == Env :: Musl {
1094+ emit_hexagon_va_arg_musl ( bx, addr, target_ty)
1095+ } else {
1096+ emit_hexagon_va_arg_bare_metal ( bx, addr, target_ty)
1097+ }
1098+ }
9691099 // For all other architecture/OS combinations fall back to using
9701100 // the LLVM va_arg instruction.
9711101 // https://llvm.org/docs/LangRef.html#va-arg-instruction
0 commit comments