@@ -39,7 +39,7 @@ enum MergingSucc {
3939
4040/// Indicates to the call terminator codegen whether a call
4141/// is a normal call or an explicit tail call.
42- #[ derive( Debug , PartialEq ) ]
42+ #[ derive( Debug , PartialEq , Clone , Copy ) ]
4343enum CallKind {
4444 Normal ,
4545 Tail ,
@@ -1195,8 +1195,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
11951195
11961196 // Special logic for tail calls with `PassMode::Indirect { on_stack: false, .. }` arguments.
11971197 //
1198- // Normally an indirect argument that is allocated in the caller's stack frame
1199- // would be passed as a pointer into the callee's stack frame .
1198+ // Normally an indirect pointer that is allocated in the caller's stack frame
1199+ // would be passed as an argument .
12001200 // For tail calls, that would be unsound, because the caller's
12011201 // stack frame is overwritten by the callee's stack frame.
12021202 //
@@ -1227,7 +1227,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
12271227
12281228 let op = self . codegen_operand ( bx, & arg. node ) ;
12291229 let tmp = PlaceRef :: alloca ( bx, op. layout ) ;
1230- bx . lifetime_start ( tmp. val . llval , tmp . layout . size ) ;
1230+ tmp. storage_live ( bx ) ;
12311231 op. store_with_annotation ( bx, tmp) ;
12321232
12331233 tail_call_temporaries[ i] = Some ( tmp) ;
@@ -1289,69 +1289,59 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
12891289 }
12901290 }
12911291
1292- let by_move = if let PassMode :: Indirect { on_stack : false , .. } = fn_abi. args [ i] . mode
1292+ let tmp = if let PassMode :: Indirect { on_stack : false , .. } = fn_abi. args [ i] . mode
12931293 && kind == CallKind :: Tail
12941294 {
12951295 // Special logic for tail calls with `PassMode::Indirect { on_stack: false, .. }` arguments.
12961296 //
1297- // Normally an indirect argument that is allocated in the caller's stack frame
1298- // would be passed as a pointer into the callee's stack frame .
1297+ // Normally an indirect pointer that is allocated in the caller's stack frame
1298+ // would be passed as an argument .
12991299 // For tail calls, that would be unsound, because the caller's
13001300 // stack frame is overwritten by the callee's stack frame.
13011301 //
13021302 // To handle the case, we introduce `tail_call_temporaries` to copy arguments into
13031303 // temporaries, then copy back to the caller's argument slots.
1304- // Finally, we pass the caller's argument slots as arguments.
1305- //
1306- // To do that, the argument must be MUST-by-move value.
1304+ // Finally, we pass the caller's argument slots as arguments which is implemented in
1305+ // `codegen_argument`.
13071306 let Some ( tmp) = tail_call_temporaries[ i] . take ( ) else {
13081307 span_bug ! ( fn_span, "missing temporary for indirect tail call argument #{i}" )
13091308 } ;
1310-
1311- let local = self . mir . args_iter ( ) . nth ( i) . unwrap ( ) ;
1312-
1313- match & self . locals [ local] {
1314- LocalRef :: Place ( arg) => {
1315- bx. typed_place_copy ( arg. val , tmp. val , fn_abi. args [ i] . layout ) ;
1316- op. val = Ref ( arg. val ) ;
1317- }
1318- LocalRef :: Operand ( arg) => {
1319- let Ref ( place_value) = arg. val else {
1320- bug ! ( "only `Ref` should use `PassMode::Indirect`" ) ;
1321- } ;
1322- bx. typed_place_copy ( place_value, tmp. val , fn_abi. args [ i] . layout ) ;
1323- op. val = arg. val ;
1324- }
1325- LocalRef :: UnsizedPlace ( _) => {
1326- span_bug ! ( fn_span, "unsized types are not supported" )
1327- }
1328- LocalRef :: PendingOperand => {
1329- span_bug ! ( fn_span, "argument local should not be pending" )
1330- }
1331- } ;
1332-
1333- bx. lifetime_end ( tmp. val . llval , tmp. layout . size ) ;
1334- true
1309+ op. val = Ref ( tmp. val ) ;
1310+ Some ( tmp)
13351311 } else {
1336- matches ! ( arg . node , mir :: Operand :: Move ( _ ) )
1312+ None
13371313 } ;
13381314
13391315 self . codegen_argument (
13401316 bx,
13411317 op,
1342- by_move ,
1318+ matches ! ( arg . node , mir :: Operand :: Move ( _ ) ) ,
13431319 & mut llargs,
13441320 & fn_abi. args [ i] ,
13451321 & mut lifetime_ends_after_call,
1322+ fn_span,
1323+ kind,
13461324 ) ;
1325+
1326+ if let Some ( tmp) = tmp {
1327+ tmp. storage_dead ( bx) ;
1328+ }
13471329 }
13481330 let num_untupled = untuple. map ( |tup| {
1331+ // For untupled arguments, it is safe to store them directly into the caller's
1332+ // argument slots without temporaries, because the untupled arguments are
1333+ // always passed through a tuple alloca. The alloca serves as a temporary
1334+ // that does not overlap with any caller argument.
1335+ // No temporaries are needed for the caller's untupled arguments either,
1336+ // because they are used last.
13491337 self . codegen_arguments_untupled (
13501338 bx,
13511339 & tup. node ,
13521340 & mut llargs,
13531341 & fn_abi. args [ first_args. len ( ) ..] ,
13541342 & mut lifetime_ends_after_call,
1343+ fn_span,
1344+ kind,
13551345 )
13561346 } ) ;
13571347
@@ -1382,6 +1372,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
13821372 & mut llargs,
13831373 last_arg,
13841374 & mut lifetime_ends_after_call,
1375+ fn_span,
1376+ kind,
13851377 ) ;
13861378 }
13871379
@@ -1701,6 +1693,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
17011693 llargs : & mut Vec < Bx :: Value > ,
17021694 arg : & ArgAbi < ' tcx , Ty < ' tcx > > ,
17031695 lifetime_ends_after_call : & mut Vec < ( Bx :: Value , Size ) > ,
1696+ fn_span : Span ,
1697+ kind : CallKind ,
17041698 ) {
17051699 match arg. mode {
17061700 PassMode :: Ignore => return ,
@@ -1753,23 +1747,34 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
17531747 PassMode :: Ignore | PassMode :: Pair ( ..) => unreachable ! ( "handled above" ) ,
17541748 } ,
17551749 Ref ( op_place_val) => match arg. mode {
1756- PassMode :: Indirect { attrs, on_stack, .. } => {
1750+ PassMode :: Indirect { attrs, meta_attrs , on_stack, .. } => {
17571751 // For `foo(packed.large_field)`, and types with <4 byte alignment on x86,
17581752 // alignment requirements may be higher than the type's alignment, so copy
17591753 // to a higher-aligned alloca.
17601754 let required_align = match attrs. pointee_align {
17611755 Some ( pointee_align) => cmp:: max ( pointee_align, arg. layout . align . abi ) ,
17621756 None => arg. layout . align . abi ,
17631757 } ;
1764- // Copy to an alloca when the argument is neither by-val nor by-move.
1765- if op_place_val. align < required_align || ( !on_stack && !by_move) {
1758+ if kind == CallKind :: Tail && !on_stack {
1759+ // Special logic for tail calls with `PassMode::Indirect { on_stack: false, .. }` arguments.
1760+ // We pass the caller's argument slots as arguments,
1761+ // because the caller's stack frame is overwritten by the callee's stack frame.
1762+ if meta_attrs. is_some ( ) {
1763+ span_bug ! ( fn_span, "unsized types are not supported" )
1764+ } ;
1765+ let caller_arg =
1766+ PlaceRef :: new_sized ( bx. get_param ( llargs. len ( ) ) , arg. layout ) ;
1767+ op. store_with_annotation ( bx, caller_arg) ;
1768+ ( caller_arg. val . llval , caller_arg. val . align , true )
1769+ } else if op_place_val. align >= required_align && ( on_stack || by_move) {
1770+ // We can skip copy to an alloca when the argument is by-val or by-move.
1771+ ( op_place_val. llval , op_place_val. align , true )
1772+ } else {
17661773 let scratch = PlaceValue :: alloca ( bx, arg. layout . size , required_align) ;
17671774 bx. lifetime_start ( scratch. llval , arg. layout . size ) ;
17681775 op. store_with_annotation ( bx, scratch. with_type ( arg. layout ) ) ;
17691776 lifetime_ends_after_call. push ( ( scratch. llval , arg. layout . size ) ) ;
17701777 ( scratch. llval , scratch. align , true )
1771- } else {
1772- ( op_place_val. llval , op_place_val. align , true )
17731778 }
17741779 }
17751780 _ => ( op_place_val. llval , op_place_val. align , true ) ,
@@ -1849,6 +1854,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
18491854 llargs : & mut Vec < Bx :: Value > ,
18501855 args : & [ ArgAbi < ' tcx , Ty < ' tcx > > ] ,
18511856 lifetime_ends_after_call : & mut Vec < ( Bx :: Value , Size ) > ,
1857+ fn_span : Span ,
1858+ kind : CallKind ,
18521859 ) -> usize {
18531860 let tuple = self . codegen_operand ( bx, operand) ;
18541861 let by_move = matches ! ( operand, mir:: Operand :: Move ( _) ) ;
@@ -1869,13 +1876,24 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
18691876 llargs,
18701877 & args[ i] ,
18711878 lifetime_ends_after_call,
1879+ fn_span,
1880+ kind,
18721881 ) ;
18731882 }
18741883 } else {
18751884 // If the tuple is immediate, the elements are as well.
18761885 for i in 0 ..tuple. layout . fields . count ( ) {
18771886 let op = tuple. extract_field ( self , bx, i) ;
1878- self . codegen_argument ( bx, op, by_move, llargs, & args[ i] , lifetime_ends_after_call) ;
1887+ self . codegen_argument (
1888+ bx,
1889+ op,
1890+ by_move,
1891+ llargs,
1892+ & args[ i] ,
1893+ lifetime_ends_after_call,
1894+ fn_span,
1895+ kind,
1896+ ) ;
18791897 }
18801898 }
18811899 tuple. layout . fields . count ( )
0 commit comments