Skip to content

Commit 97f9c08

Browse files
committed
set up VaList global storage
1 parent 145da5a commit 97f9c08

3 files changed

Lines changed: 90 additions & 11 deletions

File tree

compiler/rustc_const_eval/src/interpret/call.rs

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ use std::assert_matches::assert_matches;
44
use std::borrow::Cow;
55

66
use either::{Left, Right};
7-
use rustc_abi::{self as abi, ExternAbi, FieldIdx, Integer, VariantIdx};
7+
use rustc_abi::{self as abi, ExternAbi, FieldIdx, HasDataLayout, Integer, Size, VariantIdx};
88
use rustc_hir::def_id::DefId;
9-
use rustc_middle::ty::layout::{IntegerExt, TyAndLayout};
9+
use rustc_middle::mir::interpret::AllocId;
10+
use rustc_middle::ty::layout::{HasTyCtxt, IntegerExt, TyAndLayout};
1011
use rustc_middle::ty::{self, AdtDef, Instance, Ty, VariantDef};
1112
use rustc_middle::{bug, mir, span_bug};
1213
use rustc_span::sym;
@@ -15,9 +16,9 @@ use tracing::field::Empty;
1516
use tracing::{info, instrument, trace};
1617

1718
use super::{
18-
CtfeProvenance, FnVal, ImmTy, InterpCx, InterpResult, MPlaceTy, Machine, OpTy, PlaceTy,
19-
Projectable, Provenance, ReturnAction, ReturnContinuation, Scalar, StackPopInfo, interp_ok,
20-
throw_ub, throw_ub_custom,
19+
CtfeProvenance, FnVal, ImmTy, InterpCx, InterpResult, MPlaceTy, Machine, MemoryKind, OpTy,
20+
PlaceTy, Pointer, Projectable, Provenance, ReturnAction, ReturnContinuation, Scalar,
21+
StackPopInfo, interp_ok, throw_ub, throw_ub_custom,
2122
};
2223
use crate::interpret::EnteredTraceSpan;
2324
use crate::{enter_trace_span, fluent_generated as fluent};
@@ -334,6 +335,38 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
334335
interp_ok(())
335336
}
336337

338+
/// Store the (c-variadic) variable argument list in global memory.
339+
fn init_va_list(
340+
&mut self,
341+
caller_fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
342+
args: &[FnArg<'tcx, M::Provenance>],
343+
fixed_count: usize,
344+
) -> InterpResult<'tcx, AllocId> {
345+
let id = self.tcx().reserve_and_set_va_list_alloc();
346+
347+
// Slice out the vararg part of the actual arguments.
348+
let vararg_args = &args[fixed_count..];
349+
let vararg_abis = &caller_fn_abi.args[fixed_count..];
350+
debug_assert_eq!(vararg_args.len(), vararg_abis.len());
351+
352+
let mut varargs = Vec::with_capacity(vararg_args.len());
353+
for (fn_arg, abi) in vararg_args.iter().zip(vararg_abis) {
354+
let op = self.copy_fn_arg(fn_arg);
355+
let mplace = self.allocate(abi.layout, MemoryKind::Stack)?;
356+
self.copy_op(&op, &mplace)?;
357+
358+
varargs.push(mplace);
359+
}
360+
361+
// When the frame is dropped, this ID is used to deallocate the variable arguments list.
362+
self.frame_mut().va_list = Some(id);
363+
364+
// A global map that is used to implement `va_arg`.
365+
self.memory.va_list_map.insert(id, varargs);
366+
367+
interp_ok(id)
368+
}
369+
337370
/// The main entry point for creating a new stack frame: performs ABI checks and initializes
338371
/// arguments.
339372
#[instrument(skip(self), level = "trace")]
@@ -470,7 +503,20 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
470503
let place = self.eval_place(dest)?;
471504
let mplace = self.force_allocation(&place)?;
472505

473-
let _ = mplace;
506+
let alloc_id = self.init_va_list(caller_fn_abi, args, fixed_count)?;
507+
508+
// Functions are global allocations, so make sure we get the right root pointer.
509+
// We know this is not an `extern static` so this cannot fail.
510+
let ptr = self.global_root_pointer(Pointer::from(alloc_id)).unwrap();
511+
let addr = Scalar::from_pointer(ptr, self);
512+
513+
// Store the pointer to the global variable arguments list allocation in the
514+
// first bytes of the `VaList` value.
515+
let mut alloc = self
516+
.get_ptr_alloc_mut(mplace.ptr(), self.data_layout().pointer_size())?
517+
.expect("not a ZST");
518+
519+
alloc.write_ptr_sized(Size::ZERO, addr)?;
474520
} else if Some(local) == body.spread_arg {
475521
// Make the local live once, then fill in the value field by field.
476522
self.storage_live(local)?;

compiler/rustc_const_eval/src/interpret/memory.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ use tracing::{debug, instrument, trace};
2222

2323
use super::{
2424
AllocBytes, AllocId, AllocInit, AllocMap, AllocRange, Allocation, CheckAlignMsg,
25-
CheckInAllocMsg, CtfeProvenance, GlobalAlloc, InterpCx, InterpResult, Machine, MayLeak,
26-
Misalignment, Pointer, PointerArithmetic, Provenance, Scalar, alloc_range, err_ub,
25+
CheckInAllocMsg, CtfeProvenance, GlobalAlloc, InterpCx, InterpResult, MPlaceTy, Machine,
26+
MayLeak, Misalignment, Pointer, PointerArithmetic, Provenance, Scalar, alloc_range, err_ub,
2727
err_ub_custom, interp_ok, throw_ub, throw_ub_custom, throw_unsup, throw_unsup_format,
2828
};
2929
use crate::const_eval::ConstEvalErrKind;
@@ -128,6 +128,8 @@ pub struct Memory<'tcx, M: Machine<'tcx>> {
128128
/// Map for "extra" function pointers.
129129
extra_fn_ptr_map: FxIndexMap<AllocId, M::ExtraFnVal>,
130130

131+
pub(super) va_list_map: FxIndexMap<AllocId, Vec<MPlaceTy<'tcx, M::Provenance>>>,
132+
131133
/// To be able to compare pointers with null, and to check alignment for accesses
132134
/// to ZSTs (where pointers may dangle), we keep track of the size even for allocations
133135
/// that do not exist any more.
@@ -163,6 +165,7 @@ impl<'tcx, M: Machine<'tcx>> Memory<'tcx, M> {
163165
Memory {
164166
alloc_map: M::MemoryMap::default(),
165167
extra_fn_ptr_map: FxIndexMap::default(),
168+
va_list_map: FxIndexMap::default(),
166169
dead_alloc_map: FxIndexMap::default(),
167170
validation_in_progress: Cell::new(false),
168171
}

compiler/rustc_const_eval/src/interpret/stack.rs

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ use tracing::field::Empty;
1616
use tracing::{info_span, instrument, trace};
1717

1818
use super::{
19-
AllocId, CtfeProvenance, Immediate, InterpCx, InterpResult, Machine, MemPlace, MemPlaceMeta,
20-
MemoryKind, Operand, PlaceTy, Pointer, Provenance, ReturnAction, Scalar, from_known_layout,
21-
interp_ok, throw_ub, throw_unsup,
19+
AllocId, CtfeProvenance, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemPlace,
20+
MemPlaceMeta, MemoryKind, Operand, PlaceTy, Pointer, Provenance, ReturnAction, Scalar,
21+
from_known_layout, interp_ok, throw_ub, throw_unsup,
2222
};
2323
use crate::{enter_trace_span, errors};
2424

@@ -91,6 +91,10 @@ pub struct Frame<'tcx, Prov: Provenance = CtfeProvenance, Extra = ()> {
9191
/// Do *not* access this directly; always go through the machine hook!
9292
pub locals: IndexVec<mir::Local, LocalState<'tcx, Prov>>,
9393

94+
/// Key into `Memory`'s `va_list_map` field. When this frame is popped the key should be
95+
/// removed from `va_list_map` and the elements deallocated.
96+
pub(super) va_list: Option<AllocId>,
97+
9498
/// The span of the `tracing` crate is stored here.
9599
/// When the guard is dropped, the span is exited. This gives us
96100
/// a full stack trace on all tracing statements.
@@ -259,6 +263,7 @@ impl<'tcx, Prov: Provenance> Frame<'tcx, Prov> {
259263
return_cont: self.return_cont,
260264
return_place: self.return_place,
261265
locals: self.locals,
266+
va_list: self.va_list,
262267
loc: self.loc,
263268
extra,
264269
tracing_span: self.tracing_span,
@@ -377,6 +382,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
377382
return_cont,
378383
return_place: return_place.clone(),
379384
locals,
385+
va_list: None,
380386
instance,
381387
tracing_span: SpanGuard::new(),
382388
extra: (),
@@ -454,6 +460,14 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
454460
self.deallocate_local(local.value)?;
455461
}
456462

463+
// Deallocate any c-variadic arguments. This takes the place of `va_end`.
464+
if let Some(alloc_id) = frame.va_list {
465+
let arguments = self.memory.va_list_map.shift_remove(&alloc_id).unwrap();
466+
for mplace in arguments {
467+
self.deallocate_vararg(&mplace)?;
468+
}
469+
}
470+
457471
// Call the machine hook, which determines the next steps.
458472
let return_action = M::after_stack_pop(self, frame, unwinding)?;
459473
assert_ne!(return_action, ReturnAction::NoCleanup);
@@ -599,6 +613,22 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
599613
interp_ok(())
600614
}
601615

616+
fn deallocate_vararg(&mut self, vararg: &MPlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx> {
617+
let ptr = vararg.ptr();
618+
619+
// FIXME: is the `unwrap` valid here?
620+
trace!(
621+
"deallocating vararg {:?}: {:?}",
622+
vararg,
623+
// FIXME: what do we do with this comment?
624+
// Locals always have a `alloc_id` (they are never the result of a int2ptr).
625+
self.dump_alloc(ptr.provenance.unwrap().get_alloc_id().unwrap())
626+
);
627+
self.deallocate_ptr(ptr, None, MemoryKind::Stack)?;
628+
629+
interp_ok(())
630+
}
631+
602632
/// This is public because it is used by [Aquascope](https://github.com/cognitive-engineering-lab/aquascope/)
603633
/// to analyze all the locals in a stack frame.
604634
#[inline(always)]

0 commit comments

Comments
 (0)