Skip to content

Commit

Permalink
Auto merge of #133948 - matthiaskrgr:rollup-ifyq71o, r=matthiaskrgr
Browse files Browse the repository at this point in the history
Rollup of 5 pull requests

Successful merges:

 - #130777 (rust_for_linux: -Zreg-struct-return commandline flag for X86 (#116973))
 - #133211 (Extend Miri to correctly pass mutable pointers through FFI)
 - #133294 (crashes: more tests)
 - #133790 (Improve documentation for Vec::extend_from_within)
 - #133930 (rustbook: update to use new mdbook-trpl package from The Book)

r? `@ghost`
`@rustbot` modify labels: rollup
  • Loading branch information
bors committed Dec 6, 2024
2 parents acf4842 + b4eb64b commit 2af3896
Show file tree
Hide file tree
Showing 49 changed files with 913 additions and 104 deletions.
5 changes: 4 additions & 1 deletion compiler/rustc_codegen_gcc/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,10 @@ impl<'gcc, 'tcx> HasWasmCAbiOpt for CodegenCx<'gcc, 'tcx> {

impl<'gcc, 'tcx> HasX86AbiOpt for CodegenCx<'gcc, 'tcx> {
fn x86_abi_opt(&self) -> X86Abi {
X86Abi { regparm: self.tcx.sess.opts.unstable_opts.regparm }
X86Abi {
regparm: self.tcx.sess.opts.unstable_opts.regparm,
reg_struct_return: self.tcx.sess.opts.unstable_opts.reg_struct_return,
}
}
}

Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_const_eval/src/const_eval/dummy_machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,9 @@ impl<'tcx> interpret::Machine<'tcx> for DummyMachine {
})
}

fn expose_ptr(
_ecx: &mut InterpCx<'tcx, Self>,
_ptr: interpret::Pointer<Self::Provenance>,
fn expose_provenance(
_ecx: &InterpCx<'tcx, Self>,
_provenance: Self::Provenance,
) -> interpret::InterpResult<'tcx> {
unimplemented!()
}
Expand Down
10 changes: 6 additions & 4 deletions compiler/rustc_const_eval/src/const_eval/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,8 @@ use crate::errors::{LongRunning, LongRunningWarn};
use crate::fluent_generated as fluent;
use crate::interpret::{
self, AllocId, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame, GlobalAlloc, ImmTy,
InterpCx, InterpResult, MPlaceTy, OpTy, Pointer, RangeSet, Scalar, compile_time_machine,
interp_ok, throw_exhaust, throw_inval, throw_ub, throw_ub_custom, throw_unsup,
throw_unsup_format,
InterpCx, InterpResult, MPlaceTy, OpTy, RangeSet, Scalar, compile_time_machine, interp_ok,
throw_exhaust, throw_inval, throw_ub, throw_ub_custom, throw_unsup, throw_unsup_format,
};

/// When hitting this many interpreted terminators we emit a deny by default lint
Expand Down Expand Up @@ -586,7 +585,10 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
}

#[inline(always)]
fn expose_ptr(_ecx: &mut InterpCx<'tcx, Self>, _ptr: Pointer) -> InterpResult<'tcx> {
fn expose_provenance(
_ecx: &InterpCx<'tcx, Self>,
_provenance: Self::Provenance,
) -> InterpResult<'tcx> {
// This is only reachable with -Zunleash-the-miri-inside-of-you.
throw_unsup_format!("exposing pointers is not possible at compile-time")
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_const_eval/src/interpret/cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
let scalar = src.to_scalar();
let ptr = scalar.to_pointer(self)?;
match ptr.into_pointer_or_addr() {
Ok(ptr) => M::expose_ptr(self, ptr)?,
Ok(ptr) => M::expose_provenance(self, ptr.provenance)?,
Err(_) => {} // Do nothing, exposing an invalid pointer (`None` provenance) is a NOP.
};
interp_ok(ImmTy::from_scalar(
Expand Down
8 changes: 4 additions & 4 deletions compiler/rustc_const_eval/src/interpret/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,11 +327,11 @@ pub trait Machine<'tcx>: Sized {
addr: u64,
) -> InterpResult<'tcx, Pointer<Option<Self::Provenance>>>;

/// Marks a pointer as exposed, allowing it's provenance
/// Marks a pointer as exposed, allowing its provenance
/// to be recovered. "Pointer-to-int cast"
fn expose_ptr(
ecx: &mut InterpCx<'tcx, Self>,
ptr: Pointer<Self::Provenance>,
fn expose_provenance(
ecx: &InterpCx<'tcx, Self>,
provenance: Self::Provenance,
) -> InterpResult<'tcx>;

/// Convert a pointer with provenance into an allocation-offset pair and extra provenance info.
Expand Down
46 changes: 46 additions & 0 deletions compiler/rustc_const_eval/src/interpret/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,52 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
interp_ok(())
}

/// Handle the effect an FFI call might have on the state of allocations.
/// This overapproximates the modifications which external code might make to memory:
/// We set all reachable allocations as initialized, mark all provenances as exposed
/// and overwrite them with `Provenance::WILDCARD`.
pub fn prepare_for_native_call(
&mut self,
id: AllocId,
initial_prov: M::Provenance,
) -> InterpResult<'tcx> {
// Expose provenance of the root allocation.
M::expose_provenance(self, initial_prov)?;

let mut done = FxHashSet::default();
let mut todo = vec![id];
while let Some(id) = todo.pop() {
if !done.insert(id) {
// We already saw this allocation before, don't process it again.
continue;
}
let info = self.get_alloc_info(id);

// If there is no data behind this pointer, skip this.
if !matches!(info.kind, AllocKind::LiveData) {
continue;
}

// Expose all provenances in this allocation, and add them to `todo`.
let alloc = self.get_alloc_raw(id)?;
for prov in alloc.provenance().provenances() {
M::expose_provenance(self, prov)?;
if let Some(id) = prov.get_alloc_id() {
todo.push(id);
}
}

// Prepare for possible write from native code if mutable.
if info.mutbl.is_mut() {
self.get_alloc_raw_mut(id)?
.0
.prepare_for_native_write()
.map_err(|e| e.to_interp_error(id))?;
}
}
interp_ok(())
}

/// Create a lazy debug printer that prints the given allocation and all allocations it points
/// to, recursively.
#[must_use]
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_interface/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -832,6 +832,7 @@ fn test_unstable_options_tracking_hash() {
tracked!(precise_enum_drop_elaboration, false);
tracked!(profile_sample_use, Some(PathBuf::from("abc")));
tracked!(profiler_runtime, "abc".to_string());
tracked!(reg_struct_return, true);
tracked!(regparm, Some(3));
tracked!(relax_elf_relocations, Some(true));
tracked!(remap_cwd_prefix, Some(PathBuf::from("abc")));
Expand Down
22 changes: 22 additions & 0 deletions compiler/rustc_middle/src/mir/interpret/allocation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,28 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes>
Ok(())
}

/// Initialize all previously uninitialized bytes in the entire allocation, and set
/// provenance of everything to `Wildcard`. Before calling this, make sure all
/// provenance in this allocation is exposed!
pub fn prepare_for_native_write(&mut self) -> AllocResult {
let full_range = AllocRange { start: Size::ZERO, size: Size::from_bytes(self.len()) };
// Overwrite uninitialized bytes with 0, to ensure we don't leak whatever their value happens to be.
for chunk in self.init_mask.range_as_init_chunks(full_range) {
if !chunk.is_init() {
let uninit_bytes = &mut self.bytes
[chunk.range().start.bytes_usize()..chunk.range().end.bytes_usize()];
uninit_bytes.fill(0);
}
}
// Mark everything as initialized now.
self.mark_init(full_range, true);

// Set provenance of all bytes to wildcard.
self.provenance.write_wildcards(self.len());

Ok(())
}

/// Remove all provenance in the given memory range.
pub fn clear_provenance(&mut self, cx: &impl HasDataLayout, range: AllocRange) -> AllocResult {
self.provenance.clear(range, cx)?;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,25 @@ impl<Prov: Provenance> ProvenanceMap<Prov> {

Ok(())
}

/// Overwrites all provenance in the allocation with wildcard provenance.
///
/// Provided for usage in Miri and panics otherwise.
pub fn write_wildcards(&mut self, alloc_size: usize) {
assert!(
Prov::OFFSET_IS_ADDR,
"writing wildcard provenance is not supported when `OFFSET_IS_ADDR` is false"
);
let wildcard = Prov::WILDCARD.unwrap();

// Remove all pointer provenances, then write wildcards into the whole byte range.
self.ptrs.clear();
let last = Size::from_bytes(alloc_size);
let bytes = self.bytes.get_or_insert_with(Box::default);
for offset in Size::ZERO..last {
bytes.insert(offset, wildcard);
}
}
}

/// A partial, owned list of provenance to transfer into another allocation.
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_middle/src/mir/interpret/pointer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ pub trait Provenance: Copy + fmt::Debug + 'static {
/// pointer, and implement ptr-to-int transmutation by stripping provenance.
const OFFSET_IS_ADDR: bool;

/// If wildcard provenance is implemented, contains the unique, general wildcard provenance variant.
const WILDCARD: Option<Self>;

/// Determines how a pointer should be printed.
fn fmt(ptr: &Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result;

Expand Down Expand Up @@ -168,6 +171,9 @@ impl Provenance for CtfeProvenance {
// so ptr-to-int casts are not possible (since we do not know the global physical offset).
const OFFSET_IS_ADDR: bool = false;

// `CtfeProvenance` does not implement wildcard provenance.
const WILDCARD: Option<Self> = None;

fn fmt(ptr: &Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// Print AllocId.
fmt::Debug::fmt(&ptr.provenance.alloc_id(), f)?; // propagates `alternate` flag
Expand Down Expand Up @@ -197,6 +203,9 @@ impl Provenance for AllocId {
// so ptr-to-int casts are not possible (since we do not know the global physical offset).
const OFFSET_IS_ADDR: bool = false;

// `AllocId` does not implement wildcard provenance.
const WILDCARD: Option<Self> = None;

fn fmt(ptr: &Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// Forward `alternate` flag to `alloc_id` printing.
if f.alternate() {
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_middle/src/ty/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -552,7 +552,10 @@ impl<'tcx> HasWasmCAbiOpt for TyCtxt<'tcx> {

impl<'tcx> HasX86AbiOpt for TyCtxt<'tcx> {
fn x86_abi_opt(&self) -> X86Abi {
X86Abi { regparm: self.sess.opts.unstable_opts.regparm }
X86Abi {
regparm: self.sess.opts.unstable_opts.regparm,
reg_struct_return: self.sess.opts.unstable_opts.reg_struct_return,
}
}
}

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_session/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -135,5 +135,6 @@ session_unsupported_crate_type_for_target =
session_unsupported_dwarf_version = requested DWARF version {$dwarf_version} is greater than 5
session_unsupported_reg_struct_return_arch = `-Zreg-struct-return` is only supported on x86
session_unsupported_regparm = `-Zregparm={$regparm}` is unsupported (valid values 0-3)
session_unsupported_regparm_arch = `-Zregparm=N` is only supported on x86
4 changes: 4 additions & 0 deletions compiler/rustc_session/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,10 @@ pub(crate) struct UnsupportedRegparm {
#[diag(session_unsupported_regparm_arch)]
pub(crate) struct UnsupportedRegparmArch;

#[derive(Diagnostic)]
#[diag(session_unsupported_reg_struct_return_arch)]
pub(crate) struct UnsupportedRegStructReturnArch;

#[derive(Diagnostic)]
#[diag(session_failed_to_create_profiler)]
pub(crate) struct FailedToCreateProfiler {
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_session/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1988,6 +1988,9 @@ options! {
"enable queries of the dependency graph for regression testing (default: no)"),
randomize_layout: bool = (false, parse_bool, [TRACKED],
"randomize the layout of types (default: no)"),
reg_struct_return: bool = (false, parse_bool, [TRACKED],
"On x86-32 targets, it overrides the default ABI to return small structs in registers.
It is UNSOUND to link together crates that use different values for this flag!"),
regparm: Option<u32> = (None, parse_opt_number, [TRACKED],
"On x86-32 targets, setting this to N causes the compiler to pass N arguments \
in registers EAX, EDX, and ECX instead of on the stack for\
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_session/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1305,6 +1305,11 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
sess.dcx().emit_err(errors::UnsupportedRegparmArch);
}
}
if sess.opts.unstable_opts.reg_struct_return {
if sess.target.arch != "x86" {
sess.dcx().emit_err(errors::UnsupportedRegStructReturnArch);
}
}

// The code model check applies to `thunk` and `thunk-extern`, but not `thunk-inline`, so it is
// kept as a `match` to force a change if new ones are added, even if we currently only support
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_target/src/callconv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -661,7 +661,9 @@ impl<'a, Ty> FnAbi<'a, Ty> {
}
_ => (x86::Flavor::General, None),
};
x86::compute_abi_info(cx, self, x86::X86Options { flavor, regparm });
let reg_struct_return = cx.x86_abi_opt().reg_struct_return;
let opts = x86::X86Options { flavor, regparm, reg_struct_return };
x86::compute_abi_info(cx, self, opts);
}
"x86_64" => match abi {
spec::abi::Abi::SysV64 { .. } => x86_64::compute_abi_info(cx, self),
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_target/src/callconv/x86.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub(crate) enum Flavor {
pub(crate) struct X86Options {
pub flavor: Flavor,
pub regparm: Option<u32>,
pub reg_struct_return: bool,
}

pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, opts: X86Options)
Expand All @@ -31,7 +32,7 @@ where
// https://www.angelcode.com/dev/callconv/callconv.html
// Clang's ABI handling is in lib/CodeGen/TargetInfo.cpp
let t = cx.target_spec();
if t.abi_return_struct_as_int {
if t.abi_return_struct_as_int || opts.reg_struct_return {
// According to Clang, everyone but MSVC returns single-element
// float aggregates directly in a floating-point register.
if !t.is_like_msvc && fn_abi.ret.layout.is_single_fp_element(cx) {
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_target/src/spec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2117,6 +2117,8 @@ pub struct X86Abi {
/// On x86-32 targets, the regparm N causes the compiler to pass arguments
/// in registers EAX, EDX, and ECX instead of on the stack.
pub regparm: Option<u32>,
/// Override the default ABI to return small structs in registers
pub reg_struct_return: bool,
}

pub trait HasX86AbiOpt {
Expand Down
25 changes: 14 additions & 11 deletions library/alloc/src/vec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3025,26 +3025,29 @@ impl<T: Clone, A: Allocator> Vec<T, A> {
self.spec_extend(other.iter())
}

/// Copies elements from `src` range to the end of the vector.
/// Given a range `src`, clones a slice of elements in that range and appends it to the end.
///
/// `src` must be a range that can form a valid subslice of the `Vec`.
///
/// # Panics
///
/// Panics if the starting point is greater than the end point or if
/// the end point is greater than the length of the vector.
/// Panics if starting index is greater than the end index
/// or if the index is greater than the length of the vector.
///
/// # Examples
///
/// ```
/// let mut vec = vec![0, 1, 2, 3, 4];
///
/// vec.extend_from_within(2..);
/// assert_eq!(vec, [0, 1, 2, 3, 4, 2, 3, 4]);
/// let mut characters = vec!['a', 'b', 'c', 'd', 'e'];
/// characters.extend_from_within(2..);
/// assert_eq!(characters, ['a', 'b', 'c', 'd', 'e', 'c', 'd', 'e']);
///
/// vec.extend_from_within(..2);
/// assert_eq!(vec, [0, 1, 2, 3, 4, 2, 3, 4, 0, 1]);
/// let mut numbers = vec![0, 1, 2, 3, 4];
/// numbers.extend_from_within(..2);
/// assert_eq!(numbers, [0, 1, 2, 3, 4, 0, 1]);
///
/// vec.extend_from_within(4..8);
/// assert_eq!(vec, [0, 1, 2, 3, 4, 2, 3, 4, 0, 1, 4, 2, 3, 4]);
/// let mut strings = vec![String::from("hello"), String::from("world"), String::from("!")];
/// strings.extend_from_within(1..=2);
/// assert_eq!(strings, ["hello", "world", "!", "world", "!"]);
/// ```
#[cfg(not(no_global_oom_handling))]
#[stable(feature = "vec_extend_from_within", since = "1.53.0")]
Expand Down
2 changes: 1 addition & 1 deletion src/doc/book
Submodule book updated 147 files
15 changes: 15 additions & 0 deletions src/doc/unstable-book/src/compiler-flags/reg-struct-return.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# `reg-struct-return`

The tracking issue for this feature is: https://github.com/rust-lang/rust/issues/116973.

------------------------

Option -Zreg-struct-return causes the compiler to return small structs in registers
instead of on the stack for extern "C"-like functions.
It is UNSOUND to link together crates that use different values for this flag.
It is only supported on `x86`.

It is equivalent to [Clang]'s and [GCC]'s `-freg-struct-return`.

[Clang]: https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-freg-struct-return
[GCC]: https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html#index-freg-struct-return
10 changes: 6 additions & 4 deletions src/tools/miri/src/alloc_addresses/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,9 +286,9 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {

impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn expose_ptr(&mut self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let global_state = this.machine.alloc_addresses.get_mut();
fn expose_ptr(&self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
let this = self.eval_context_ref();
let mut global_state = this.machine.alloc_addresses.borrow_mut();
// In strict mode, we don't need this, so we can save some cycles by not tracking it.
if global_state.provenance_mode == ProvenanceMode::Strict {
return interp_ok(());
Expand All @@ -299,8 +299,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
return interp_ok(());
}
trace!("Exposing allocation id {alloc_id:?}");
let global_state = this.machine.alloc_addresses.get_mut();
global_state.exposed.insert(alloc_id);
// Release the global state before we call `expose_tag`, which may call `get_alloc_info_extra`,
// which may need access to the global state.
drop(global_state);
if this.machine.borrow_tracker.is_some() {
this.expose_tag(alloc_id, tag)?;
}
Expand Down
Loading

0 comments on commit 2af3896

Please sign in to comment.