Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 52c365b

Browse files
authoredAug 6, 2024
Rollup merge of #128687 - RalfJung:interpret-call-refactor, r=WaffleLapkin
interpret: refactor function call handling to be better-abstracted Add a new function `init_stack_frame` that pushes a stack frame and passes the arguments, and use that basically everywhere that the raw underlying `push_stack_frame` used to be called. This splits the previous monster function `eval_fn_call` into two parts: figuring out the MIR to call and the arguments to pass, and then actually setting up the stack frame. Also re-organize the files a bit: - The previous `terminator.rs` is split into a new `call.rs` with all the argument-passing logic, and the rest goes into `step.rs` where the other main dispatcher functions already live (in particular, `eval_statement`). - All the stack frame handling from `eval_context.rs` is moved to a new `stack.rs`.
2 parents f00a551 + 1c2705c commit 52c365b

24 files changed

+1354
-1330
lines changed
 

‎compiler/rustc_const_eval/src/const_eval/eval_queries.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,9 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>(
7373
cid.promoted.map_or_else(String::new, |p| format!("::{p:?}"))
7474
);
7575

76-
ecx.push_stack_frame(
76+
// This can't use `init_stack_frame` since `body` is not a function,
77+
// so computing its ABI would fail. It's also not worth it since there are no arguments to pass.
78+
ecx.push_stack_frame_raw(
7779
cid.instance,
7880
body,
7981
&ret.clone().into(),

‎compiler/rustc_const_eval/src/const_eval/machine.rs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ use crate::errors::{LongRunning, LongRunningWarn};
2424
use crate::fluent_generated as fluent;
2525
use crate::interpret::{
2626
self, compile_time_machine, err_ub, throw_exhaust, throw_inval, throw_ub_custom, throw_unsup,
27-
throw_unsup_format, AllocId, AllocRange, ConstAllocation, CtfeProvenance, FnArg, FnVal, Frame,
27+
throw_unsup_format, AllocId, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame,
2828
GlobalAlloc, ImmTy, InterpCx, InterpResult, MPlaceTy, OpTy, Pointer, PointerArithmetic, Scalar,
29+
StackPopCleanup,
2930
};
3031

3132
/// When hitting this many interpreted terminators we emit a deny by default lint
@@ -306,17 +307,15 @@ impl<'tcx> CompileTimeInterpCx<'tcx> {
306307
let align = ImmTy::from_uint(target_align, args[1].layout).into();
307308
let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty())?;
308309

309-
// We replace the entire function call with a "tail call".
310-
// Note that this happens before the frame of the original function
311-
// is pushed on the stack.
312-
self.eval_fn_call(
313-
FnVal::Instance(instance),
314-
(CallAbi::Rust, fn_abi),
310+
// Push the stack frame with our own adjusted arguments.
311+
self.init_stack_frame(
312+
instance,
313+
self.load_mir(instance.def, None)?,
314+
fn_abi,
315315
&[FnArg::Copy(addr), FnArg::Copy(align)],
316316
/* with_caller_location = */ false,
317317
dest,
318-
ret,
319-
mir::UnwindAction::Unreachable,
318+
StackPopCleanup::Goto { ret, unwind: mir::UnwindAction::Unreachable },
320319
)?;
321320
Ok(ControlFlow::Break(()))
322321
} else {

‎compiler/rustc_const_eval/src/interpret/terminator.rs renamed to ‎compiler/rustc_const_eval/src/interpret/call.rs

Lines changed: 354 additions & 452 deletions
Large diffs are not rendered by default.

‎compiler/rustc_const_eval/src/interpret/eval_context.rs

Lines changed: 10 additions & 753 deletions
Large diffs are not rendered by default.

‎compiler/rustc_const_eval/src/interpret/machine.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ pub enum ReturnAction {
3737
/// took care of everything.
3838
NoJump,
3939

40-
/// Returned by [`InterpCx::pop_stack_frame`] when no cleanup should be done.
40+
/// Returned by [`InterpCx::pop_stack_frame_raw`] when no cleanup should be done.
4141
NoCleanup,
4242
}
4343

‎compiler/rustc_const_eval/src/interpret/mod.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! An interpreter for MIR used in CTFE and by miri
22
3+
mod call;
34
mod cast;
45
mod discriminant;
56
mod eval_context;
@@ -11,8 +12,8 @@ mod operand;
1112
mod operator;
1213
mod place;
1314
mod projection;
15+
mod stack;
1416
mod step;
15-
mod terminator;
1617
mod traits;
1718
mod util;
1819
mod validity;
@@ -22,7 +23,8 @@ use eval_context::{from_known_layout, mir_assign_valid_types};
2223
#[doc(no_inline)]
2324
pub use rustc_middle::mir::interpret::*; // have all the `interpret` symbols in one place: here
2425

25-
pub use self::eval_context::{format_interp_error, Frame, FrameInfo, InterpCx, StackPopCleanup};
26+
pub use self::call::FnArg;
27+
pub use self::eval_context::{format_interp_error, InterpCx};
2628
pub use self::intern::{
2729
intern_const_alloc_for_constprop, intern_const_alloc_recursive, HasStaticRootDefId, InternKind,
2830
InternResult,
@@ -35,7 +37,7 @@ pub use self::operand::{ImmTy, Immediate, OpTy, Readable};
3537
pub use self::place::{MPlaceTy, MemPlaceMeta, PlaceTy, Writeable};
3638
use self::place::{MemPlace, Place};
3739
pub use self::projection::{OffsetMode, Projectable};
38-
pub use self::terminator::FnArg;
40+
pub use self::stack::{Frame, FrameInfo, LocalState, StackPopCleanup, StackPopInfo};
3941
pub(crate) use self::util::create_static_alloc;
4042
pub use self::validity::{CtfeValidationMode, RefTracking};
4143
pub use self::visitor::ValueVisitor;

‎compiler/rustc_const_eval/src/interpret/operand.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
184184
#[inline]
185185
pub fn from_scalar(val: Scalar<Prov>, layout: TyAndLayout<'tcx>) -> Self {
186186
debug_assert!(layout.abi.is_scalar(), "`ImmTy::from_scalar` on non-scalar layout");
187+
debug_assert_eq!(val.size(), layout.size);
187188
ImmTy { imm: val.into(), layout }
188189
}
189190

‎compiler/rustc_const_eval/src/interpret/stack.rs

Lines changed: 651 additions & 0 deletions
Large diffs are not rendered by default.

‎compiler/rustc_const_eval/src/interpret/step.rs

Lines changed: 236 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,29 @@
44
55
use either::Either;
66
use rustc_index::IndexSlice;
7-
use rustc_middle::{bug, mir};
7+
use rustc_middle::ty::layout::FnAbiOf;
8+
use rustc_middle::ty::{self, Instance, Ty};
9+
use rustc_middle::{bug, mir, span_bug};
10+
use rustc_span::source_map::Spanned;
11+
use rustc_target::abi::call::FnAbi;
812
use rustc_target::abi::{FieldIdx, FIRST_VARIANT};
913
use tracing::{info, instrument, trace};
1014

1115
use super::{
12-
ImmTy, Immediate, InterpCx, InterpResult, Machine, MemPlaceMeta, PlaceTy, Projectable, Scalar,
16+
throw_ub, FnArg, FnVal, ImmTy, Immediate, InterpCx, InterpResult, Machine, MemPlaceMeta,
17+
PlaceTy, Projectable, Scalar,
1318
};
1419
use crate::util;
1520

21+
struct EvaluatedCalleeAndArgs<'tcx, M: Machine<'tcx>> {
22+
callee: FnVal<'tcx, M::ExtraFnVal>,
23+
args: Vec<FnArg<'tcx, M::Provenance>>,
24+
fn_sig: ty::FnSig<'tcx>,
25+
fn_abi: &'tcx FnAbi<'tcx, Ty<'tcx>>,
26+
/// True if the function is marked as `#[track_caller]` ([`ty::InstanceKind::requires_caller_location`])
27+
with_caller_location: bool,
28+
}
29+
1630
impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
1731
/// Returns `true` as long as there are more things to do.
1832
///
@@ -36,7 +50,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
3650

3751
if let Some(stmt) = basic_block.statements.get(loc.statement_index) {
3852
let old_frames = self.frame_idx();
39-
self.statement(stmt)?;
53+
self.eval_statement(stmt)?;
4054
// Make sure we are not updating `statement_index` of the wrong frame.
4155
assert_eq!(old_frames, self.frame_idx());
4256
// Advance the program counter.
@@ -47,15 +61,20 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
4761
M::before_terminator(self)?;
4862

4963
let terminator = basic_block.terminator();
50-
self.terminator(terminator)?;
64+
self.eval_terminator(terminator)?;
65+
if !self.stack().is_empty() {
66+
if let Either::Left(loc) = self.frame().loc {
67+
info!("// executing {:?}", loc.block);
68+
}
69+
}
5170
Ok(true)
5271
}
5372

5473
/// Runs the interpretation logic for the given `mir::Statement` at the current frame and
5574
/// statement counter.
5675
///
5776
/// This does NOT move the statement counter forward, the caller has to do that!
58-
pub fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> InterpResult<'tcx> {
77+
pub fn eval_statement(&mut self, stmt: &mir::Statement<'tcx>) -> InterpResult<'tcx> {
5978
info!("{:?}", stmt);
6079

6180
use rustc_middle::mir::StatementKind::*;
@@ -349,16 +368,222 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
349368
Ok(())
350369
}
351370

352-
/// Evaluate the given terminator. Will also adjust the stack frame and statement position accordingly.
353-
fn terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> InterpResult<'tcx> {
371+
/// Evaluate the arguments of a function call
372+
fn eval_fn_call_argument(
373+
&self,
374+
op: &mir::Operand<'tcx>,
375+
) -> InterpResult<'tcx, FnArg<'tcx, M::Provenance>> {
376+
Ok(match op {
377+
mir::Operand::Copy(_) | mir::Operand::Constant(_) => {
378+
// Make a regular copy.
379+
let op = self.eval_operand(op, None)?;
380+
FnArg::Copy(op)
381+
}
382+
mir::Operand::Move(place) => {
383+
// If this place lives in memory, preserve its location.
384+
// We call `place_to_op` which will be an `MPlaceTy` whenever there exists
385+
// an mplace for this place. (This is in contrast to `PlaceTy::as_mplace_or_local`
386+
// which can return a local even if that has an mplace.)
387+
let place = self.eval_place(*place)?;
388+
let op = self.place_to_op(&place)?;
389+
390+
match op.as_mplace_or_imm() {
391+
Either::Left(mplace) => FnArg::InPlace(mplace),
392+
Either::Right(_imm) => {
393+
// This argument doesn't live in memory, so there's no place
394+
// to make inaccessible during the call.
395+
// We rely on there not being any stray `PlaceTy` that would let the
396+
// caller directly access this local!
397+
// This is also crucial for tail calls, where we want the `FnArg` to
398+
// stay valid when the old stack frame gets popped.
399+
FnArg::Copy(op)
400+
}
401+
}
402+
}
403+
})
404+
}
405+
406+
/// Shared part of `Call` and `TailCall` implementation — finding and evaluating all the
407+
/// necessary information about callee and arguments to make a call.
408+
fn eval_callee_and_args(
409+
&self,
410+
terminator: &mir::Terminator<'tcx>,
411+
func: &mir::Operand<'tcx>,
412+
args: &[Spanned<mir::Operand<'tcx>>],
413+
) -> InterpResult<'tcx, EvaluatedCalleeAndArgs<'tcx, M>> {
414+
let func = self.eval_operand(func, None)?;
415+
let args = args
416+
.iter()
417+
.map(|arg| self.eval_fn_call_argument(&arg.node))
418+
.collect::<InterpResult<'tcx, Vec<_>>>()?;
419+
420+
let fn_sig_binder = func.layout.ty.fn_sig(*self.tcx);
421+
let fn_sig = self.tcx.normalize_erasing_late_bound_regions(self.param_env, fn_sig_binder);
422+
let extra_args = &args[fn_sig.inputs().len()..];
423+
let extra_args =
424+
self.tcx.mk_type_list_from_iter(extra_args.iter().map(|arg| arg.layout().ty));
425+
426+
let (callee, fn_abi, with_caller_location) = match *func.layout.ty.kind() {
427+
ty::FnPtr(_sig) => {
428+
let fn_ptr = self.read_pointer(&func)?;
429+
let fn_val = self.get_ptr_fn(fn_ptr)?;
430+
(fn_val, self.fn_abi_of_fn_ptr(fn_sig_binder, extra_args)?, false)
431+
}
432+
ty::FnDef(def_id, args) => {
433+
let instance = self.resolve(def_id, args)?;
434+
(
435+
FnVal::Instance(instance),
436+
self.fn_abi_of_instance(instance, extra_args)?,
437+
instance.def.requires_caller_location(*self.tcx),
438+
)
439+
}
440+
_ => {
441+
span_bug!(terminator.source_info.span, "invalid callee of type {}", func.layout.ty)
442+
}
443+
};
444+
445+
Ok(EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location })
446+
}
447+
448+
fn eval_terminator(&mut self, terminator: &mir::Terminator<'tcx>) -> InterpResult<'tcx> {
354449
info!("{:?}", terminator.kind);
355450

356-
self.eval_terminator(terminator)?;
357-
if !self.stack().is_empty() {
358-
if let Either::Left(loc) = self.frame().loc {
359-
info!("// executing {:?}", loc.block);
451+
use rustc_middle::mir::TerminatorKind::*;
452+
match terminator.kind {
453+
Return => {
454+
self.return_from_current_stack_frame(/* unwinding */ false)?
455+
}
456+
457+
Goto { target } => self.go_to_block(target),
458+
459+
SwitchInt { ref discr, ref targets } => {
460+
let discr = self.read_immediate(&self.eval_operand(discr, None)?)?;
461+
trace!("SwitchInt({:?})", *discr);
462+
463+
// Branch to the `otherwise` case by default, if no match is found.
464+
let mut target_block = targets.otherwise();
465+
466+
for (const_int, target) in targets.iter() {
467+
// Compare using MIR BinOp::Eq, to also support pointer values.
468+
// (Avoiding `self.binary_op` as that does some redundant layout computation.)
469+
let res = self.binary_op(
470+
mir::BinOp::Eq,
471+
&discr,
472+
&ImmTy::from_uint(const_int, discr.layout),
473+
)?;
474+
if res.to_scalar().to_bool()? {
475+
target_block = target;
476+
break;
477+
}
478+
}
479+
480+
self.go_to_block(target_block);
481+
}
482+
483+
Call {
484+
ref func,
485+
ref args,
486+
destination,
487+
target,
488+
unwind,
489+
call_source: _,
490+
fn_span: _,
491+
} => {
492+
let old_stack = self.frame_idx();
493+
let old_loc = self.frame().loc;
494+
495+
let EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location } =
496+
self.eval_callee_and_args(terminator, func, args)?;
497+
498+
let destination = self.force_allocation(&self.eval_place(destination)?)?;
499+
self.init_fn_call(
500+
callee,
501+
(fn_sig.abi, fn_abi),
502+
&args,
503+
with_caller_location,
504+
&destination,
505+
target,
506+
if fn_abi.can_unwind { unwind } else { mir::UnwindAction::Unreachable },
507+
)?;
508+
// Sanity-check that `eval_fn_call` either pushed a new frame or
509+
// did a jump to another block.
510+
if self.frame_idx() == old_stack && self.frame().loc == old_loc {
511+
span_bug!(terminator.source_info.span, "evaluating this call made no progress");
512+
}
513+
}
514+
515+
TailCall { ref func, ref args, fn_span: _ } => {
516+
let old_frame_idx = self.frame_idx();
517+
518+
let EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location } =
519+
self.eval_callee_and_args(terminator, func, args)?;
520+
521+
self.init_fn_tail_call(callee, (fn_sig.abi, fn_abi), &args, with_caller_location)?;
522+
523+
if self.frame_idx() != old_frame_idx {
524+
span_bug!(
525+
terminator.source_info.span,
526+
"evaluating this tail call pushed a new stack frame"
527+
);
528+
}
529+
}
530+
531+
Drop { place, target, unwind, replace: _ } => {
532+
let place = self.eval_place(place)?;
533+
let instance = Instance::resolve_drop_in_place(*self.tcx, place.layout.ty);
534+
if let ty::InstanceKind::DropGlue(_, None) = instance.def {
535+
// This is the branch we enter if and only if the dropped type has no drop glue
536+
// whatsoever. This can happen as a result of monomorphizing a drop of a
537+
// generic. In order to make sure that generic and non-generic code behaves
538+
// roughly the same (and in keeping with Mir semantics) we do nothing here.
539+
self.go_to_block(target);
540+
return Ok(());
541+
}
542+
trace!("TerminatorKind::drop: {:?}, type {}", place, place.layout.ty);
543+
self.init_drop_in_place_call(&place, instance, target, unwind)?;
544+
}
545+
546+
Assert { ref cond, expected, ref msg, target, unwind } => {
547+
let ignored =
548+
M::ignore_optional_overflow_checks(self) && msg.is_optional_overflow_check();
549+
let cond_val = self.read_scalar(&self.eval_operand(cond, None)?)?.to_bool()?;
550+
if ignored || expected == cond_val {
551+
self.go_to_block(target);
552+
} else {
553+
M::assert_panic(self, msg, unwind)?;
554+
}
555+
}
556+
557+
UnwindTerminate(reason) => {
558+
M::unwind_terminate(self, reason)?;
559+
}
560+
561+
// When we encounter Resume, we've finished unwinding
562+
// cleanup for the current stack frame. We pop it in order
563+
// to continue unwinding the next frame
564+
UnwindResume => {
565+
trace!("unwinding: resuming from cleanup");
566+
// By definition, a Resume terminator means
567+
// that we're unwinding
568+
self.return_from_current_stack_frame(/* unwinding */ true)?;
569+
return Ok(());
570+
}
571+
572+
// It is UB to ever encounter this.
573+
Unreachable => throw_ub!(Unreachable),
574+
575+
// These should never occur for MIR we actually run.
576+
FalseEdge { .. } | FalseUnwind { .. } | Yield { .. } | CoroutineDrop => span_bug!(
577+
terminator.source_info.span,
578+
"{:#?} should have been eliminated by MIR pass",
579+
terminator.kind
580+
),
581+
582+
InlineAsm { template, ref operands, options, ref targets, .. } => {
583+
M::eval_inline_asm(self, template, operands, options, targets)?;
360584
}
361585
}
586+
362587
Ok(())
363588
}
364589
}

‎src/tools/miri/src/concurrency/thread.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ pub struct Thread<'tcx> {
256256
/// which then forwards it to 'Resume'. However this argument is implicit in MIR,
257257
/// so we have to store it out-of-band. When there are multiple active unwinds,
258258
/// the innermost one is always caught first, so we can store them as a stack.
259-
pub(crate) panic_payloads: Vec<Scalar>,
259+
pub(crate) panic_payloads: Vec<ImmTy<'tcx>>,
260260

261261
/// Last OS error location in memory. It is a 32-bit integer.
262262
pub(crate) last_error: Option<MPlaceTy<'tcx>>,
@@ -377,10 +377,6 @@ impl VisitProvenance for Frame<'_, Provenance, FrameExtra<'_>> {
377377
return_place,
378378
locals,
379379
extra,
380-
body: _,
381-
instance: _,
382-
return_to_block: _,
383-
loc: _,
384380
// There are some private fields we cannot access; they contain no tags.
385381
..
386382
} = self;
@@ -952,7 +948,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
952948
this.call_function(
953949
instance,
954950
start_abi,
955-
&[*func_arg],
951+
&[func_arg],
956952
Some(&ret_place),
957953
StackPopCleanup::Root { cleanup: true },
958954
)?;

‎src/tools/miri/src/eval.rs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,8 @@ pub fn create_ecx<'tcx>(
307307
// First argument is constructed later, because it's skipped if the entry function uses #[start].
308308

309309
// Second argument (argc): length of `config.args`.
310-
let argc = Scalar::from_target_usize(u64::try_from(config.args.len()).unwrap(), &ecx);
310+
let argc =
311+
ImmTy::from_int(i64::try_from(config.args.len()).unwrap(), ecx.machine.layouts.isize);
311312
// Third argument (`argv`): created from `config.args`.
312313
let argv = {
313314
// Put each argument in memory, collect pointers.
@@ -334,21 +335,19 @@ pub fn create_ecx<'tcx>(
334335
ecx.write_immediate(arg, &place)?;
335336
}
336337
ecx.mark_immutable(&argvs_place);
337-
// A pointer to that place is the 3rd argument for main.
338-
let argv = argvs_place.to_ref(&ecx);
339338
// Store `argc` and `argv` for macOS `_NSGetArg{c,v}`.
340339
{
341340
let argc_place =
342341
ecx.allocate(ecx.machine.layouts.isize, MiriMemoryKind::Machine.into())?;
343-
ecx.write_scalar(argc, &argc_place)?;
342+
ecx.write_immediate(*argc, &argc_place)?;
344343
ecx.mark_immutable(&argc_place);
345344
ecx.machine.argc = Some(argc_place.ptr());
346345

347346
let argv_place = ecx.allocate(
348347
ecx.layout_of(Ty::new_imm_ptr(tcx, tcx.types.unit))?,
349348
MiriMemoryKind::Machine.into(),
350349
)?;
351-
ecx.write_immediate(argv, &argv_place)?;
350+
ecx.write_pointer(argvs_place.ptr(), &argv_place)?;
352351
ecx.mark_immutable(&argv_place);
353352
ecx.machine.argv = Some(argv_place.ptr());
354353
}
@@ -369,7 +368,7 @@ pub fn create_ecx<'tcx>(
369368
}
370369
ecx.mark_immutable(&cmd_place);
371370
}
372-
argv
371+
ecx.mplace_to_ref(&argvs_place)?
373372
};
374373

375374
// Return place (in static memory so that it does not count as leak).
@@ -405,10 +404,14 @@ pub fn create_ecx<'tcx>(
405404
start_instance,
406405
Abi::Rust,
407406
&[
408-
Scalar::from_pointer(main_ptr, &ecx).into(),
409-
argc.into(),
407+
ImmTy::from_scalar(
408+
Scalar::from_pointer(main_ptr, &ecx),
409+
// FIXME use a proper fn ptr type
410+
ecx.machine.layouts.const_raw_ptr,
411+
),
412+
argc,
410413
argv,
411-
Scalar::from_u8(sigpipe).into(),
414+
ImmTy::from_uint(sigpipe, ecx.machine.layouts.u8),
412415
],
413416
Some(&ret_place),
414417
StackPopCleanup::Root { cleanup: true },
@@ -418,7 +421,7 @@ pub fn create_ecx<'tcx>(
418421
ecx.call_function(
419422
entry_instance,
420423
Abi::Rust,
421-
&[argc.into(), argv],
424+
&[argc, argv],
422425
Some(&ret_place),
423426
StackPopCleanup::Root { cleanup: true },
424427
)?;

‎src/tools/miri/src/helpers.rs

Lines changed: 28 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,14 @@ use rustc_apfloat::Float;
1212
use rustc_hir::{
1313
def::{DefKind, Namespace},
1414
def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE},
15+
Safety,
1516
};
1617
use rustc_index::IndexVec;
1718
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
1819
use rustc_middle::middle::dependency_format::Linkage;
1920
use rustc_middle::middle::exported_symbols::ExportedSymbol;
2021
use rustc_middle::mir;
21-
use rustc_middle::ty::layout::MaybeResult;
22+
use rustc_middle::ty::layout::{FnAbiOf, MaybeResult};
2223
use rustc_middle::ty::{
2324
self,
2425
layout::{LayoutOf, TyAndLayout},
@@ -492,48 +493,38 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
492493
&mut self,
493494
f: ty::Instance<'tcx>,
494495
caller_abi: Abi,
495-
args: &[Immediate<Provenance>],
496+
args: &[ImmTy<'tcx>],
496497
dest: Option<&MPlaceTy<'tcx>>,
497498
stack_pop: StackPopCleanup,
498499
) -> InterpResult<'tcx> {
499500
let this = self.eval_context_mut();
500-
let param_env = ty::ParamEnv::reveal_all(); // in Miri this is always the param_env we use... and this.param_env is private.
501-
let callee_abi = f.ty(*this.tcx, param_env).fn_sig(*this.tcx).abi();
502-
if callee_abi != caller_abi {
503-
throw_ub_format!(
504-
"calling a function with ABI {} using caller ABI {}",
505-
callee_abi.name(),
506-
caller_abi.name()
507-
)
508-
}
509501

510-
// Push frame.
502+
// Get MIR.
511503
let mir = this.load_mir(f.def, None)?;
512504
let dest = match dest {
513505
Some(dest) => dest.clone(),
514506
None => MPlaceTy::fake_alloc_zst(this.layout_of(mir.return_ty())?),
515507
};
516-
this.push_stack_frame(f, mir, &dest, stack_pop)?;
517-
518-
// Initialize arguments.
519-
let mut callee_args = this.frame().body.args_iter();
520-
for arg in args {
521-
let local = callee_args
522-
.next()
523-
.ok_or_else(|| err_ub_format!("callee has fewer arguments than expected"))?;
524-
// Make the local live, and insert the initial value.
525-
this.storage_live(local)?;
526-
let callee_arg = this.local_to_place(local)?;
527-
this.write_immediate(*arg, &callee_arg)?;
528-
}
529-
if callee_args.next().is_some() {
530-
throw_ub_format!("callee has more arguments than expected");
531-
}
532-
533-
// Initialize remaining locals.
534-
this.storage_live_for_always_live_locals()?;
535508

536-
Ok(())
509+
// Construct a function pointer type representing the caller perspective.
510+
let sig = this.tcx.mk_fn_sig(
511+
args.iter().map(|a| a.layout.ty),
512+
dest.layout.ty,
513+
/*c_variadic*/ false,
514+
Safety::Safe,
515+
caller_abi,
516+
);
517+
let caller_fn_abi = this.fn_abi_of_fn_ptr(ty::Binder::dummy(sig), ty::List::empty())?;
518+
519+
this.init_stack_frame(
520+
f,
521+
mir,
522+
caller_fn_abi,
523+
&args.iter().map(|a| FnArg::Copy(a.clone().into())).collect::<Vec<_>>(),
524+
/*with_caller_location*/ false,
525+
&dest,
526+
stack_pop,
527+
)
537528
}
538529

539530
/// Visits the memory covered by `place`, sensitive to freezing: the 2nd parameter
@@ -1114,12 +1105,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
11141105
// Make an attempt to get at the instance of the function this is inlined from.
11151106
let instance: Option<_> = try {
11161107
let scope = frame.current_source_info()?.scope;
1117-
let inlined_parent = frame.body.source_scopes[scope].inlined_parent_scope?;
1118-
let source = &frame.body.source_scopes[inlined_parent];
1108+
let inlined_parent = frame.body().source_scopes[scope].inlined_parent_scope?;
1109+
let source = &frame.body().source_scopes[inlined_parent];
11191110
source.inlined.expect("inlined_parent_scope points to scope without inline info").0
11201111
};
11211112
// Fall back to the instance of the function itself.
1122-
let instance = instance.unwrap_or(frame.instance);
1113+
let instance = instance.unwrap_or(frame.instance());
11231114
// Now check the crate it is in. We could try to be clever here and e.g. check if this is
11241115
// the same crate as `start_fn`, but that would not work for running std tests in Miri, so
11251116
// we'd need some more hacks anyway. So we just check the name of the crate. If someone
@@ -1359,9 +1350,9 @@ impl<'tcx> MiriMachine<'tcx> {
13591350

13601351
/// This is the source of truth for the `is_user_relevant` flag in our `FrameExtra`.
13611352
pub fn is_user_relevant(&self, frame: &Frame<'tcx, Provenance>) -> bool {
1362-
let def_id = frame.instance.def_id();
1353+
let def_id = frame.instance().def_id();
13631354
(def_id.is_local() || self.local_crates.contains(&def_id.krate))
1364-
&& !frame.instance.def.requires_caller_location(self.tcx)
1355+
&& !frame.instance().def.requires_caller_location(self.tcx)
13651356
}
13661357
}
13671358

‎src/tools/miri/src/machine.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1352,7 +1352,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
13521352
) -> InterpResult<'tcx, Frame<'tcx, Provenance, FrameExtra<'tcx>>> {
13531353
// Start recording our event before doing anything else
13541354
let timing = if let Some(profiler) = ecx.machine.profiler.as_ref() {
1355-
let fn_name = frame.instance.to_string();
1355+
let fn_name = frame.instance().to_string();
13561356
let entry = ecx.machine.string_cache.entry(fn_name.clone());
13571357
let name = entry.or_insert_with(|| profiler.alloc_string(&*fn_name));
13581358

@@ -1443,7 +1443,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
14431443
// tracing-tree can autoamtically annotate scope changes, but it gets very confused by our
14441444
// concurrency and what it prints is just plain wrong. So we print our own information
14451445
// instead. (Cc https://github.com/rust-lang/miri/issues/2266)
1446-
info!("Leaving {}", ecx.frame().instance);
1446+
info!("Leaving {}", ecx.frame().instance());
14471447
Ok(())
14481448
}
14491449

@@ -1473,7 +1473,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
14731473
// Needs to be done after dropping frame to show up on the right nesting level.
14741474
// (Cc https://github.com/rust-lang/miri/issues/2266)
14751475
if !ecx.active_thread_stack().is_empty() {
1476-
info!("Continuing in {}", ecx.frame().instance);
1476+
info!("Continuing in {}", ecx.frame().instance());
14771477
}
14781478
res
14791479
}
@@ -1486,7 +1486,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
14861486
let Some(Provenance::Concrete { alloc_id, .. }) = mplace.ptr().provenance else {
14871487
panic!("after_local_allocated should only be called on fresh allocations");
14881488
};
1489-
let local_decl = &ecx.frame().body.local_decls[local];
1489+
let local_decl = &ecx.frame().body().local_decls[local];
14901490
let span = local_decl.source_info.span;
14911491
ecx.machine.allocation_spans.borrow_mut().insert(alloc_id, (span, None));
14921492
Ok(())

‎src/tools/miri/src/shims/backtrace.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
4646
let mut data = Vec::new();
4747
for frame in this.active_thread_stack().iter().rev() {
4848
// Match behavior of debuginfo (`FunctionCx::adjusted_span_and_dbg_scope`).
49-
let span = hygiene::walk_chain_collapsed(frame.current_span(), frame.body.span);
50-
data.push((frame.instance, span.lo()));
49+
let span = hygiene::walk_chain_collapsed(frame.current_span(), frame.body().span);
50+
data.push((frame.instance(), span.lo()));
5151
}
5252

5353
let ptrs: Vec<_> = data

‎src/tools/miri/src/shims/panic.rs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ pub struct CatchUnwindData<'tcx> {
2525
/// The `catch_fn` callback to call in case of a panic.
2626
catch_fn: Pointer,
2727
/// The `data` argument for that callback.
28-
data: Scalar,
28+
data: ImmTy<'tcx>,
2929
/// The return place from the original call to `try`.
3030
dest: MPlaceTy<'tcx>,
3131
/// The return block from the original call to `try`.
@@ -48,9 +48,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
4848
fn handle_miri_start_unwind(&mut self, payload: &OpTy<'tcx>) -> InterpResult<'tcx> {
4949
let this = self.eval_context_mut();
5050

51-
trace!("miri_start_unwind: {:?}", this.frame().instance);
51+
trace!("miri_start_unwind: {:?}", this.frame().instance());
5252

53-
let payload = this.read_scalar(payload)?;
53+
let payload = this.read_immediate(payload)?;
5454
let thread = this.active_thread_mut();
5555
thread.panic_payloads.push(payload);
5656

@@ -80,7 +80,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
8080
// Get all the arguments.
8181
let [try_fn, data, catch_fn] = check_arg_count(args)?;
8282
let try_fn = this.read_pointer(try_fn)?;
83-
let data = this.read_scalar(data)?;
83+
let data = this.read_immediate(data)?;
8484
let catch_fn = this.read_pointer(catch_fn)?;
8585

8686
// Now we make a function call, and pass `data` as first and only argument.
@@ -89,7 +89,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
8989
this.call_function(
9090
f_instance,
9191
Abi::Rust,
92-
&[data.into()],
92+
&[data.clone()],
9393
None,
9494
// Directly return to caller.
9595
StackPopCleanup::Goto { ret, unwind: mir::UnwindAction::Continue },
@@ -124,7 +124,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
124124
// and we are unwinding, so we should catch that.
125125
trace!(
126126
"unwinding: found catch_panic frame during unwinding: {:?}",
127-
this.frame().instance
127+
this.frame().instance()
128128
);
129129

130130
// We set the return value of `try` to 1, since there was a panic.
@@ -140,7 +140,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
140140
this.call_function(
141141
f_instance,
142142
Abi::Rust,
143-
&[catch_unwind.data.into(), payload.into()],
143+
&[catch_unwind.data, payload],
144144
None,
145145
// Directly return to caller of `try`.
146146
StackPopCleanup::Goto {
@@ -169,7 +169,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
169169
this.call_function(
170170
panic,
171171
Abi::Rust,
172-
&[msg.to_ref(this)],
172+
&[this.mplace_to_ref(&msg)?],
173173
None,
174174
StackPopCleanup::Goto { ret: None, unwind },
175175
)
@@ -188,7 +188,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
188188
this.call_function(
189189
panic,
190190
Abi::Rust,
191-
&[msg.to_ref(this)],
191+
&[this.mplace_to_ref(&msg)?],
192192
None,
193193
StackPopCleanup::Goto { ret: None, unwind: mir::UnwindAction::Unreachable },
194194
)
@@ -207,17 +207,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
207207
// Forward to `panic_bounds_check` lang item.
208208

209209
// First arg: index.
210-
let index = this.read_scalar(&this.eval_operand(index, None)?)?;
210+
let index = this.read_immediate(&this.eval_operand(index, None)?)?;
211211
// Second arg: len.
212-
let len = this.read_scalar(&this.eval_operand(len, None)?)?;
212+
let len = this.read_immediate(&this.eval_operand(len, None)?)?;
213213

214214
// Call the lang item.
215215
let panic_bounds_check = this.tcx.lang_items().panic_bounds_check_fn().unwrap();
216216
let panic_bounds_check = ty::Instance::mono(this.tcx.tcx, panic_bounds_check);
217217
this.call_function(
218218
panic_bounds_check,
219219
Abi::Rust,
220-
&[index.into(), len.into()],
220+
&[index, len],
221221
None,
222222
StackPopCleanup::Goto { ret: None, unwind },
223223
)?;
@@ -226,9 +226,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
226226
// Forward to `panic_misaligned_pointer_dereference` lang item.
227227

228228
// First arg: required.
229-
let required = this.read_scalar(&this.eval_operand(required, None)?)?;
229+
let required = this.read_immediate(&this.eval_operand(required, None)?)?;
230230
// Second arg: found.
231-
let found = this.read_scalar(&this.eval_operand(found, None)?)?;
231+
let found = this.read_immediate(&this.eval_operand(found, None)?)?;
232232

233233
// Call the lang item.
234234
let panic_misaligned_pointer_dereference =
@@ -238,7 +238,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
238238
this.call_function(
239239
panic_misaligned_pointer_dereference,
240240
Abi::Rust,
241-
&[required.into(), found.into()],
241+
&[required, found],
242242
None,
243243
StackPopCleanup::Goto { ret: None, unwind },
244244
)?;

‎src/tools/miri/src/shims/tls.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -315,14 +315,16 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
315315
// FIXME: Technically, the reason should be `DLL_PROCESS_DETACH` when the main thread exits
316316
// but std treats both the same.
317317
let reason = this.eval_windows("c", "DLL_THREAD_DETACH");
318+
let null_ptr =
319+
ImmTy::from_scalar(Scalar::null_ptr(this), this.machine.layouts.const_raw_ptr);
318320

319321
// The signature of this function is `unsafe extern "system" fn(h: c::LPVOID, dwReason: c::DWORD, pv: c::LPVOID)`.
320322
// FIXME: `h` should be a handle to the current module and what `pv` should be is unknown
321323
// but both are ignored by std.
322324
this.call_function(
323325
thread_callback,
324326
Abi::System { unwind: false },
325-
&[Scalar::null_ptr(this).into(), reason.into(), Scalar::null_ptr(this).into()],
327+
&[null_ptr.clone(), ImmTy::from_scalar(reason, this.machine.layouts.u32), null_ptr],
326328
None,
327329
StackPopCleanup::Root { cleanup: true },
328330
)?;
@@ -343,7 +345,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
343345
this.call_function(
344346
instance,
345347
Abi::C { unwind: false },
346-
&[data.into()],
348+
&[ImmTy::from_scalar(data, this.machine.layouts.mut_raw_ptr)],
347349
None,
348350
StackPopCleanup::Root { cleanup: true },
349351
)?;
@@ -380,7 +382,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
380382
this.call_function(
381383
instance,
382384
Abi::C { unwind: false },
383-
&[ptr.into()],
385+
&[ImmTy::from_scalar(ptr, this.machine.layouts.mut_raw_ptr)],
384386
None,
385387
StackPopCleanup::Root { cleanup: true },
386388
)?;

‎src/tools/miri/src/shims/unix/thread.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use crate::*;
2-
use rustc_middle::ty::layout::LayoutOf;
32
use rustc_target::spec::abi::Abi;
43

54
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
@@ -24,7 +23,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
2423
start_routine,
2524
Abi::C { unwind: false },
2625
func_arg,
27-
this.layout_of(this.tcx.types.usize)?,
26+
this.machine.layouts.mut_raw_ptr,
2827
)?;
2928

3029
Ok(())

‎src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
//@ignore-target-windows: No pthreads on Windows
2+
//~^ERROR: calling a function with more arguments than it expected
23

34
//! The thread function must have exactly one argument.
45
56
use std::{mem, ptr};
67

78
extern "C" fn thread_start() -> *mut libc::c_void {
8-
panic!() //~ ERROR: callee has fewer arguments than expected
9+
panic!()
910
}
1011

1112
fn main() {
Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
1-
error: Undefined Behavior: callee has fewer arguments than expected
2-
--> $DIR/libc_pthread_create_too_few_args.rs:LL:CC
3-
|
4-
LL | panic!()
5-
| ^^^^^^^^ callee has fewer arguments than expected
1+
error: Undefined Behavior: calling a function with more arguments than it expected
62
|
3+
= note: calling a function with more arguments than it expected
4+
= note: (no span available)
75
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
86
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
97
= note: BACKTRACE on thread `unnamed-ID`:
10-
= note: inside `thread_start` at RUSTLIB/core/src/panic.rs:LL:CC
11-
= note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
128

139
error: aborting due to 1 previous error
1410

‎src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
//@ignore-target-windows: No pthreads on Windows
2+
//~^ERROR: calling a function with fewer arguments than it requires
23

34
//! The thread function must have exactly one argument.
45
56
use std::{mem, ptr};
67

78
extern "C" fn thread_start(_null: *mut libc::c_void, _x: i32) -> *mut libc::c_void {
8-
panic!() //~ ERROR: callee has more arguments than expected
9+
panic!()
910
}
1011

1112
fn main() {
Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
1-
error: Undefined Behavior: callee has more arguments than expected
2-
--> $DIR/libc_pthread_create_too_many_args.rs:LL:CC
3-
|
4-
LL | panic!()
5-
| ^^^^^^^^ callee has more arguments than expected
1+
error: Undefined Behavior: calling a function with fewer arguments than it requires
62
|
3+
= note: calling a function with fewer arguments than it requires
4+
= note: (no span available)
75
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
86
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
97
= note: BACKTRACE on thread `unnamed-ID`:
10-
= note: inside `thread_start` at RUSTLIB/core/src/panic.rs:LL:CC
11-
= note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
128

139
error: aborting due to 1 previous error
1410

‎src/tools/miri/tests/fail/function_calls/check_callback_abi.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ fn main() {
99
// Make sure we check the ABI when Miri itself invokes a function
1010
// as part of a shim implementation.
1111
std::intrinsics::catch_unwind(
12-
//~^ ERROR: calling a function with ABI C using caller ABI Rust
12+
//~^ ERROR: calling a function with calling convention C using calling convention Rust
1313
std::mem::transmute::<extern "C" fn(*mut u8), _>(try_fn),
1414
std::ptr::null_mut(),
1515
|_, _| unreachable!(),

‎src/tools/miri/tests/fail/function_calls/check_callback_abi.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
error: Undefined Behavior: calling a function with ABI C using caller ABI Rust
1+
error: Undefined Behavior: calling a function with calling convention C using calling convention Rust
22
--> $DIR/check_callback_abi.rs:LL:CC
33
|
44
LL | / std::intrinsics::catch_unwind(
@@ -7,7 +7,7 @@ LL | | std::mem::transmute::<extern "C" fn(*mut u8), _>(try_fn),
77
LL | | std::ptr::null_mut(),
88
LL | | |_, _| unreachable!(),
99
LL | | );
10-
| |_________^ calling a function with ABI C using caller ABI Rust
10+
| |_________^ calling a function with calling convention C using calling convention Rust
1111
|
1212
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
1313
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information

‎src/tools/miri/tests/pass/alloc-access-tracking.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#![feature(start)]
22
#![no_std]
3-
//@compile-flags: -Zmiri-track-alloc-id=20 -Zmiri-track-alloc-accesses -Cpanic=abort
4-
//@normalize-stderr-test: "id 20" -> "id $$ALLOC"
3+
//@compile-flags: -Zmiri-track-alloc-id=21 -Zmiri-track-alloc-accesses -Cpanic=abort
4+
//@normalize-stderr-test: "id 21" -> "id $$ALLOC"
55
//@only-target-linux: alloc IDs differ between OSes (due to extern static allocations)
66

77
extern "Rust" {

0 commit comments

Comments
 (0)
Please sign in to comment.