Skip to content

Commit 20fc35f

Browse files
Allocate arguments from topmost frame into temporary in init_fn_tail_call
1 parent 0060d5a commit 20fc35f

File tree

4 files changed

+64
-5
lines changed

4 files changed

+64
-5
lines changed

compiler/rustc_const_eval/src/interpret/call.rs

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::borrow::Cow;
55

66
use either::{Left, Right};
77
use rustc_abi::{self as abi, ExternAbi, FieldIdx, Integer, VariantIdx};
8+
use rustc_data_structures::fx::FxHashSet;
89
use rustc_hir::def_id::DefId;
910
use rustc_middle::ty::layout::{IntegerExt, TyAndLayout};
1011
use rustc_middle::ty::{self, AdtDef, Instance, Ty, VariantDef};
@@ -19,7 +20,7 @@ use super::{
1920
Projectable, Provenance, ReturnAction, ReturnContinuation, Scalar, StackPopInfo, interp_ok,
2021
throw_ub, throw_ub_custom, throw_unsup_format,
2122
};
22-
use crate::interpret::EnteredTraceSpan;
23+
use crate::interpret::{EnteredTraceSpan, MemoryKind};
2324
use crate::{enter_trace_span, fluent_generated as fluent};
2425

2526
/// An argument passed to a function.
@@ -752,11 +753,41 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
752753
&mut self,
753754
fn_val: FnVal<'tcx, M::ExtraFnVal>,
754755
(caller_abi, caller_fn_abi): (ExternAbi, &FnAbi<'tcx, Ty<'tcx>>),
755-
args: &[FnArg<'tcx, M::Provenance>],
756+
mut args: Vec<FnArg<'tcx, M::Provenance>>,
756757
with_caller_location: bool,
757758
) -> InterpResult<'tcx> {
758759
trace!("init_fn_tail_call: {:#?}", fn_val);
759760

761+
let mut local_temps = vec![];
762+
let frame_locals = &self.stack().last().unwrap().locals;
763+
if frame_locals.iter().any(|frame_local| frame_local.is_allocation()) {
764+
// Allocations corresponding to the locals in the last frame.
765+
let local_allocs: FxHashSet<_> = frame_locals
766+
.iter()
767+
.filter_map(|local| local.as_mplace_or_imm()?.left()?.0.provenance?.get_alloc_id())
768+
.collect();
769+
770+
for arg in &mut args {
771+
let mplace = match arg {
772+
FnArg::Copy(op) => match op.as_mplace_or_imm() {
773+
Left(mplace) => mplace,
774+
Right(_) => continue,
775+
},
776+
FnArg::InPlace(mplace) => mplace.clone(),
777+
};
778+
779+
if let Some(prov) = mplace.ptr().provenance
780+
&& let Some(alloc_id) = prov.get_alloc_id()
781+
&& local_allocs.contains(&alloc_id)
782+
{
783+
let temp_mplace = self.allocate(*arg.layout(), MemoryKind::Stack)?;
784+
self.copy_op(&mplace, &temp_mplace)?;
785+
local_temps.push(temp_mplace.clone());
786+
*arg = FnArg::Copy(temp_mplace.into());
787+
}
788+
}
789+
}
790+
760791
// This is the "canonical" implementation of tails calls,
761792
// a pop of the current stack frame, followed by a normal call
762793
// which pushes a new stack frame, with the return address from
@@ -785,12 +816,18 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
785816
self.init_fn_call(
786817
fn_val,
787818
(caller_abi, caller_fn_abi),
788-
args,
819+
&args,
789820
with_caller_location,
790821
&return_place,
791822
ret,
792823
unwind,
793-
)
824+
)?;
825+
826+
for local_temp in local_temps {
827+
self.deallocate_ptr(local_temp.ptr(), None, MemoryKind::Stack)?;
828+
}
829+
830+
interp_ok(())
794831
}
795832

796833
pub(super) fn init_drop_in_place_call(

compiler/rustc_const_eval/src/interpret/stack.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,16 @@ impl<'tcx, Prov: Provenance> LocalState<'tcx, Prov> {
205205
LocalValue::Live(val) => interp_ok(val),
206206
}
207207
}
208+
209+
pub(super) fn is_allocation(&self) -> bool {
210+
match self.value {
211+
LocalValue::Dead => false,
212+
LocalValue::Live(val) => match val {
213+
Operand::Immediate(_) => false,
214+
Operand::Indirect(_) => true,
215+
},
216+
}
217+
}
208218
}
209219

210220
/// What we store about a frame in an interpreter backtrace.

compiler/rustc_const_eval/src/interpret/step.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -544,7 +544,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
544544
let EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location } =
545545
self.eval_callee_and_args(terminator, func, args)?;
546546

547-
self.init_fn_tail_call(callee, (fn_sig.abi, fn_abi), &args, with_caller_location)?;
547+
self.init_fn_tail_call(callee, (fn_sig.abi, fn_abi), args, with_caller_location)?;
548548

549549
if self.frame_idx() != old_frame_idx {
550550
span_bug!(
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#![feature(explicit_tail_calls)]
2+
#![expect(incomplete_features)]
3+
4+
struct Wrapper(#[expect(unused)] usize);
5+
6+
fn f(t: bool, x: Wrapper) {
7+
if t { become f(false, x); }
8+
}
9+
10+
fn main() {
11+
f(true, Wrapper(5));
12+
}

0 commit comments

Comments
 (0)