diff options
| author | bors <bors@rust-lang.org> | 2024-12-06 10:48:10 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2024-12-06 10:48:10 +0000 |
| commit | bc145cec4565a97a1b08df52d26ddf48ce3d7d0a (patch) | |
| tree | 30f783c50c112664ad05afd584d81073773a8502 /compiler | |
| parent | acf48426b64d24f372d534f634072de1f4c7e588 (diff) | |
| parent | 875df6c59c6870d37ffa175172a6dafca94e1198 (diff) | |
| download | rust-bc145cec4565a97a1b08df52d26ddf48ce3d7d0a.tar.gz rust-bc145cec4565a97a1b08df52d26ddf48ce3d7d0a.zip | |
Auto merge of #133950 - matthiaskrgr:rollup-b7g5p73, r=matthiaskrgr
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) - #133790 (Improve documentation for Vec::extend_from_within) - #133930 (rustbook: update to use new mdbook-trpl package from The Book) - #133931 (Only allow PassMode::Direct for aggregates on wasm when using the C ABI) r? `@ghost` `@rustbot` modify labels: rollup
Diffstat (limited to 'compiler')
19 files changed, 159 insertions, 26 deletions
diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index 3846d025537..f67dcf0cb11 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -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, + } } } diff --git a/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs b/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs index e49d702127d..817acfcca74 100644 --- a/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs @@ -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!() } diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index b27e3606f38..11e0fac51d8 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -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 @@ -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") } diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index c95e51f0a1f..ef3e96784ce 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -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( diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index dbe09d55b2d..a180d5da941 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -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. diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 277d293597a..027ba9644cb 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -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] diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 3c4d9c2e928..e76e9ca9f85 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -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"))); diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 509f2667b35..d6f8fed755f 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -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)?; diff --git a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs index 5c47fc6a399..3a83b184d83 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs @@ -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. diff --git a/compiler/rustc_middle/src/mir/interpret/pointer.rs b/compiler/rustc_middle/src/mir/interpret/pointer.rs index 1d5afe22573..25c7c26ddd9 100644 --- a/compiler/rustc_middle/src/mir/interpret/pointer.rs +++ b/compiler/rustc_middle/src/mir/interpret/pointer.rs @@ -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; @@ -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 @@ -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() { diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 01ad76aedc3..07573a79260 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -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, + } } } diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl index 8fd87893a98..eb14b78a003 100644 --- a/compiler/rustc_session/messages.ftl +++ b/compiler/rustc_session/messages.ftl @@ -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 diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index 736a5ce0704..6c26a781487 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -490,6 +490,10 @@ pub(crate) struct UnsupportedRegparm { 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 { pub(crate) err: String, diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 2c0302bbb2b..fea37904914 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -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\ diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 120ae9946ea..7db3b7b7d9d 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -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 diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index fb0fe402934..746e8173807 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -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), diff --git a/compiler/rustc_target/src/callconv/x86.rs b/compiler/rustc_target/src/callconv/x86.rs index a5af975d4d2..cd8465c09ca 100644 --- a/compiler/rustc_target/src/callconv/x86.rs +++ b/compiler/rustc_target/src/callconv/x86.rs @@ -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) @@ -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) { diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index a2e9430830a..210d67fa1aa 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -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 { diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index b746d6299ef..9a625c67ccd 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -473,20 +473,30 @@ fn fn_abi_sanity_check<'tcx>( // This really shouldn't happen even for sized aggregates, since // `immediate_llvm_type` will use `layout.fields` to turn this Rust type into an // LLVM type. This means all sorts of Rust type details leak into the ABI. - // However wasm sadly *does* currently use this mode so we have to allow it -- - // but we absolutely shouldn't let any more targets do that. - // (Also see <https://github.com/rust-lang/rust/issues/115666>.) + // However wasm sadly *does* currently use this mode for it's "C" ABI so we + // have to allow it -- but we absolutely shouldn't let any more targets do + // that. (Also see <https://github.com/rust-lang/rust/issues/115666>.) // // The unstable abi `PtxKernel` also uses Direct for now. // It needs to switch to something else before stabilization can happen. // (See issue: https://github.com/rust-lang/rust/issues/117271) - assert!( - matches!(&*tcx.sess.target.arch, "wasm32" | "wasm64") - || matches!(spec_abi, ExternAbi::PtxKernel | ExternAbi::Unadjusted), - "`PassMode::Direct` for aggregates only allowed for \"unadjusted\" and \"ptx-kernel\" functions and on wasm\n\ - Problematic type: {:#?}", - arg.layout, - ); + // + // And finally the unadjusted ABI is ill specified and uses Direct for all + // args, but unfortunately we need it for calling certain LLVM intrinsics. + + match spec_abi { + ExternAbi::Unadjusted => {} + ExternAbi::PtxKernel => {} + ExternAbi::C { unwind: _ } + if matches!(&*tcx.sess.target.arch, "wasm32" | "wasm64") => {} + _ => { + panic!( + "`PassMode::Direct` for aggregates only allowed for \"unadjusted\" and \"ptx-kernel\" functions and on wasm\n\ + Problematic type: {:#?}", + arg.layout, + ); + } + } } } } |
