Skip to content

Commit bc145ce

Browse files
committedDec 6, 2024
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
2 parents acf4842 + 875df6c commit bc145ce

File tree

40 files changed

+822
-114
lines changed

40 files changed

+822
-114
lines changed
 

‎compiler/rustc_codegen_gcc/src/context.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -544,7 +544,10 @@ impl<'gcc, 'tcx> HasWasmCAbiOpt for CodegenCx<'gcc, 'tcx> {
544544

545545
impl<'gcc, 'tcx> HasX86AbiOpt for CodegenCx<'gcc, 'tcx> {
546546
fn x86_abi_opt(&self) -> X86Abi {
547-
X86Abi { regparm: self.tcx.sess.opts.unstable_opts.regparm }
547+
X86Abi {
548+
regparm: self.tcx.sess.opts.unstable_opts.regparm,
549+
reg_struct_return: self.tcx.sess.opts.unstable_opts.reg_struct_return,
550+
}
548551
}
549552
}
550553

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -168,9 +168,9 @@ impl<'tcx> interpret::Machine<'tcx> for DummyMachine {
168168
})
169169
}
170170

171-
fn expose_ptr(
172-
_ecx: &mut InterpCx<'tcx, Self>,
173-
_ptr: interpret::Pointer<Self::Provenance>,
171+
fn expose_provenance(
172+
_ecx: &InterpCx<'tcx, Self>,
173+
_provenance: Self::Provenance,
174174
) -> interpret::InterpResult<'tcx> {
175175
unimplemented!()
176176
}

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

+6-4
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,8 @@ use crate::errors::{LongRunning, LongRunningWarn};
2121
use crate::fluent_generated as fluent;
2222
use crate::interpret::{
2323
self, AllocId, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame, GlobalAlloc, ImmTy,
24-
InterpCx, InterpResult, MPlaceTy, OpTy, Pointer, RangeSet, Scalar, compile_time_machine,
25-
interp_ok, throw_exhaust, throw_inval, throw_ub, throw_ub_custom, throw_unsup,
26-
throw_unsup_format,
24+
InterpCx, InterpResult, MPlaceTy, OpTy, RangeSet, Scalar, compile_time_machine, interp_ok,
25+
throw_exhaust, throw_inval, throw_ub, throw_ub_custom, throw_unsup, throw_unsup_format,
2726
};
2827

2928
/// 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> {
586585
}
587586

588587
#[inline(always)]
589-
fn expose_ptr(_ecx: &mut InterpCx<'tcx, Self>, _ptr: Pointer) -> InterpResult<'tcx> {
588+
fn expose_provenance(
589+
_ecx: &InterpCx<'tcx, Self>,
590+
_provenance: Self::Provenance,
591+
) -> InterpResult<'tcx> {
590592
// This is only reachable with -Zunleash-the-miri-inside-of-you.
591593
throw_unsup_format!("exposing pointers is not possible at compile-time")
592594
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
238238
let scalar = src.to_scalar();
239239
let ptr = scalar.to_pointer(self)?;
240240
match ptr.into_pointer_or_addr() {
241-
Ok(ptr) => M::expose_ptr(self, ptr)?,
241+
Ok(ptr) => M::expose_provenance(self, ptr.provenance)?,
242242
Err(_) => {} // Do nothing, exposing an invalid pointer (`None` provenance) is a NOP.
243243
};
244244
interp_ok(ImmTy::from_scalar(

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

+4-4
Original file line numberDiff line numberDiff line change
@@ -327,11 +327,11 @@ pub trait Machine<'tcx>: Sized {
327327
addr: u64,
328328
) -> InterpResult<'tcx, Pointer<Option<Self::Provenance>>>;
329329

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

337337
/// Convert a pointer with provenance into an allocation-offset pair and extra provenance info.

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

+46
Original file line numberDiff line numberDiff line change
@@ -944,6 +944,52 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
944944
interp_ok(())
945945
}
946946

947+
/// Handle the effect an FFI call might have on the state of allocations.
948+
/// This overapproximates the modifications which external code might make to memory:
949+
/// We set all reachable allocations as initialized, mark all provenances as exposed
950+
/// and overwrite them with `Provenance::WILDCARD`.
951+
pub fn prepare_for_native_call(
952+
&mut self,
953+
id: AllocId,
954+
initial_prov: M::Provenance,
955+
) -> InterpResult<'tcx> {
956+
// Expose provenance of the root allocation.
957+
M::expose_provenance(self, initial_prov)?;
958+
959+
let mut done = FxHashSet::default();
960+
let mut todo = vec![id];
961+
while let Some(id) = todo.pop() {
962+
if !done.insert(id) {
963+
// We already saw this allocation before, don't process it again.
964+
continue;
965+
}
966+
let info = self.get_alloc_info(id);
967+
968+
// If there is no data behind this pointer, skip this.
969+
if !matches!(info.kind, AllocKind::LiveData) {
970+
continue;
971+
}
972+
973+
// Expose all provenances in this allocation, and add them to `todo`.
974+
let alloc = self.get_alloc_raw(id)?;
975+
for prov in alloc.provenance().provenances() {
976+
M::expose_provenance(self, prov)?;
977+
if let Some(id) = prov.get_alloc_id() {
978+
todo.push(id);
979+
}
980+
}
981+
982+
// Prepare for possible write from native code if mutable.
983+
if info.mutbl.is_mut() {
984+
self.get_alloc_raw_mut(id)?
985+
.0
986+
.prepare_for_native_write()
987+
.map_err(|e| e.to_interp_error(id))?;
988+
}
989+
}
990+
interp_ok(())
991+
}
992+
947993
/// Create a lazy debug printer that prints the given allocation and all allocations it points
948994
/// to, recursively.
949995
#[must_use]

‎compiler/rustc_interface/src/tests.rs

+1
Original file line numberDiff line numberDiff line change
@@ -832,6 +832,7 @@ fn test_unstable_options_tracking_hash() {
832832
tracked!(precise_enum_drop_elaboration, false);
833833
tracked!(profile_sample_use, Some(PathBuf::from("abc")));
834834
tracked!(profiler_runtime, "abc".to_string());
835+
tracked!(reg_struct_return, true);
835836
tracked!(regparm, Some(3));
836837
tracked!(relax_elf_relocations, Some(true));
837838
tracked!(remap_cwd_prefix, Some(PathBuf::from("abc")));

‎compiler/rustc_middle/src/mir/interpret/allocation.rs

+22
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,28 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes>
643643
Ok(())
644644
}
645645

646+
/// Initialize all previously uninitialized bytes in the entire allocation, and set
647+
/// provenance of everything to `Wildcard`. Before calling this, make sure all
648+
/// provenance in this allocation is exposed!
649+
pub fn prepare_for_native_write(&mut self) -> AllocResult {
650+
let full_range = AllocRange { start: Size::ZERO, size: Size::from_bytes(self.len()) };
651+
// Overwrite uninitialized bytes with 0, to ensure we don't leak whatever their value happens to be.
652+
for chunk in self.init_mask.range_as_init_chunks(full_range) {
653+
if !chunk.is_init() {
654+
let uninit_bytes = &mut self.bytes
655+
[chunk.range().start.bytes_usize()..chunk.range().end.bytes_usize()];
656+
uninit_bytes.fill(0);
657+
}
658+
}
659+
// Mark everything as initialized now.
660+
self.mark_init(full_range, true);
661+
662+
// Set provenance of all bytes to wildcard.
663+
self.provenance.write_wildcards(self.len());
664+
665+
Ok(())
666+
}
667+
646668
/// Remove all provenance in the given memory range.
647669
pub fn clear_provenance(&mut self, cx: &impl HasDataLayout, range: AllocRange) -> AllocResult {
648670
self.provenance.clear(range, cx)?;

‎compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs

+19
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,25 @@ impl<Prov: Provenance> ProvenanceMap<Prov> {
195195

196196
Ok(())
197197
}
198+
199+
/// Overwrites all provenance in the allocation with wildcard provenance.
200+
///
201+
/// Provided for usage in Miri and panics otherwise.
202+
pub fn write_wildcards(&mut self, alloc_size: usize) {
203+
assert!(
204+
Prov::OFFSET_IS_ADDR,
205+
"writing wildcard provenance is not supported when `OFFSET_IS_ADDR` is false"
206+
);
207+
let wildcard = Prov::WILDCARD.unwrap();
208+
209+
// Remove all pointer provenances, then write wildcards into the whole byte range.
210+
self.ptrs.clear();
211+
let last = Size::from_bytes(alloc_size);
212+
let bytes = self.bytes.get_or_insert_with(Box::default);
213+
for offset in Size::ZERO..last {
214+
bytes.insert(offset, wildcard);
215+
}
216+
}
198217
}
199218

200219
/// A partial, owned list of provenance to transfer into another allocation.

‎compiler/rustc_middle/src/mir/interpret/pointer.rs

+9
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ pub trait Provenance: Copy + fmt::Debug + 'static {
6666
/// pointer, and implement ptr-to-int transmutation by stripping provenance.
6767
const OFFSET_IS_ADDR: bool;
6868

69+
/// If wildcard provenance is implemented, contains the unique, general wildcard provenance variant.
70+
const WILDCARD: Option<Self>;
71+
6972
/// Determines how a pointer should be printed.
7073
fn fmt(ptr: &Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result;
7174

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

174+
// `CtfeProvenance` does not implement wildcard provenance.
175+
const WILDCARD: Option<Self> = None;
176+
171177
fn fmt(ptr: &Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
172178
// Print AllocId.
173179
fmt::Debug::fmt(&ptr.provenance.alloc_id(), f)?; // propagates `alternate` flag
@@ -197,6 +203,9 @@ impl Provenance for AllocId {
197203
// so ptr-to-int casts are not possible (since we do not know the global physical offset).
198204
const OFFSET_IS_ADDR: bool = false;
199205

206+
// `AllocId` does not implement wildcard provenance.
207+
const WILDCARD: Option<Self> = None;
208+
200209
fn fmt(ptr: &Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
201210
// Forward `alternate` flag to `alloc_id` printing.
202211
if f.alternate() {

‎compiler/rustc_middle/src/ty/layout.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,10 @@ impl<'tcx> HasWasmCAbiOpt for TyCtxt<'tcx> {
552552

553553
impl<'tcx> HasX86AbiOpt for TyCtxt<'tcx> {
554554
fn x86_abi_opt(&self) -> X86Abi {
555-
X86Abi { regparm: self.sess.opts.unstable_opts.regparm }
555+
X86Abi {
556+
regparm: self.sess.opts.unstable_opts.regparm,
557+
reg_struct_return: self.sess.opts.unstable_opts.reg_struct_return,
558+
}
556559
}
557560
}
558561

‎compiler/rustc_session/messages.ftl

+1
Original file line numberDiff line numberDiff line change
@@ -135,5 +135,6 @@ session_unsupported_crate_type_for_target =
135135
136136
session_unsupported_dwarf_version = requested DWARF version {$dwarf_version} is greater than 5
137137
138+
session_unsupported_reg_struct_return_arch = `-Zreg-struct-return` is only supported on x86
138139
session_unsupported_regparm = `-Zregparm={$regparm}` is unsupported (valid values 0-3)
139140
session_unsupported_regparm_arch = `-Zregparm=N` is only supported on x86

‎compiler/rustc_session/src/errors.rs

+4
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,10 @@ pub(crate) struct UnsupportedRegparm {
489489
#[diag(session_unsupported_regparm_arch)]
490490
pub(crate) struct UnsupportedRegparmArch;
491491

492+
#[derive(Diagnostic)]
493+
#[diag(session_unsupported_reg_struct_return_arch)]
494+
pub(crate) struct UnsupportedRegStructReturnArch;
495+
492496
#[derive(Diagnostic)]
493497
#[diag(session_failed_to_create_profiler)]
494498
pub(crate) struct FailedToCreateProfiler {

‎compiler/rustc_session/src/options.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1988,6 +1988,9 @@ options! {
19881988
"enable queries of the dependency graph for regression testing (default: no)"),
19891989
randomize_layout: bool = (false, parse_bool, [TRACKED],
19901990
"randomize the layout of types (default: no)"),
1991+
reg_struct_return: bool = (false, parse_bool, [TRACKED],
1992+
"On x86-32 targets, it overrides the default ABI to return small structs in registers.
1993+
It is UNSOUND to link together crates that use different values for this flag!"),
19911994
regparm: Option<u32> = (None, parse_opt_number, [TRACKED],
19921995
"On x86-32 targets, setting this to N causes the compiler to pass N arguments \
19931996
in registers EAX, EDX, and ECX instead of on the stack for\

‎compiler/rustc_session/src/session.rs

+5
Original file line numberDiff line numberDiff line change
@@ -1305,6 +1305,11 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
13051305
sess.dcx().emit_err(errors::UnsupportedRegparmArch);
13061306
}
13071307
}
1308+
if sess.opts.unstable_opts.reg_struct_return {
1309+
if sess.target.arch != "x86" {
1310+
sess.dcx().emit_err(errors::UnsupportedRegStructReturnArch);
1311+
}
1312+
}
13081313

13091314
// The code model check applies to `thunk` and `thunk-extern`, but not `thunk-inline`, so it is
13101315
// kept as a `match` to force a change if new ones are added, even if we currently only support

‎compiler/rustc_target/src/callconv/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -661,7 +661,9 @@ impl<'a, Ty> FnAbi<'a, Ty> {
661661
}
662662
_ => (x86::Flavor::General, None),
663663
};
664-
x86::compute_abi_info(cx, self, x86::X86Options { flavor, regparm });
664+
let reg_struct_return = cx.x86_abi_opt().reg_struct_return;
665+
let opts = x86::X86Options { flavor, regparm, reg_struct_return };
666+
x86::compute_abi_info(cx, self, opts);
665667
}
666668
"x86_64" => match abi {
667669
spec::abi::Abi::SysV64 { .. } => x86_64::compute_abi_info(cx, self),

‎compiler/rustc_target/src/callconv/x86.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ pub(crate) enum Flavor {
1414
pub(crate) struct X86Options {
1515
pub flavor: Flavor,
1616
pub regparm: Option<u32>,
17+
pub reg_struct_return: bool,
1718
}
1819

1920
pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, opts: X86Options)
@@ -31,7 +32,7 @@ where
3132
// https://www.angelcode.com/dev/callconv/callconv.html
3233
// Clang's ABI handling is in lib/CodeGen/TargetInfo.cpp
3334
let t = cx.target_spec();
34-
if t.abi_return_struct_as_int {
35+
if t.abi_return_struct_as_int || opts.reg_struct_return {
3536
// According to Clang, everyone but MSVC returns single-element
3637
// float aggregates directly in a floating-point register.
3738
if !t.is_like_msvc && fn_abi.ret.layout.is_single_fp_element(cx) {

‎compiler/rustc_target/src/spec/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -2117,6 +2117,8 @@ pub struct X86Abi {
21172117
/// On x86-32 targets, the regparm N causes the compiler to pass arguments
21182118
/// in registers EAX, EDX, and ECX instead of on the stack.
21192119
pub regparm: Option<u32>,
2120+
/// Override the default ABI to return small structs in registers
2121+
pub reg_struct_return: bool,
21202122
}
21212123

21222124
pub trait HasX86AbiOpt {

‎compiler/rustc_ty_utils/src/abi.rs

+20-10
Original file line numberDiff line numberDiff line change
@@ -473,20 +473,30 @@ fn fn_abi_sanity_check<'tcx>(
473473
// This really shouldn't happen even for sized aggregates, since
474474
// `immediate_llvm_type` will use `layout.fields` to turn this Rust type into an
475475
// LLVM type. This means all sorts of Rust type details leak into the ABI.
476-
// However wasm sadly *does* currently use this mode so we have to allow it --
477-
// but we absolutely shouldn't let any more targets do that.
478-
// (Also see <https://github.com/rust-lang/rust/issues/115666>.)
476+
// However wasm sadly *does* currently use this mode for it's "C" ABI so we
477+
// have to allow it -- but we absolutely shouldn't let any more targets do
478+
// that. (Also see <https://github.com/rust-lang/rust/issues/115666>.)
479479
//
480480
// The unstable abi `PtxKernel` also uses Direct for now.
481481
// It needs to switch to something else before stabilization can happen.
482482
// (See issue: https://github.com/rust-lang/rust/issues/117271)
483-
assert!(
484-
matches!(&*tcx.sess.target.arch, "wasm32" | "wasm64")
485-
|| matches!(spec_abi, ExternAbi::PtxKernel | ExternAbi::Unadjusted),
486-
"`PassMode::Direct` for aggregates only allowed for \"unadjusted\" and \"ptx-kernel\" functions and on wasm\n\
487-
Problematic type: {:#?}",
488-
arg.layout,
489-
);
483+
//
484+
// And finally the unadjusted ABI is ill specified and uses Direct for all
485+
// args, but unfortunately we need it for calling certain LLVM intrinsics.
486+
487+
match spec_abi {
488+
ExternAbi::Unadjusted => {}
489+
ExternAbi::PtxKernel => {}
490+
ExternAbi::C { unwind: _ }
491+
if matches!(&*tcx.sess.target.arch, "wasm32" | "wasm64") => {}
492+
_ => {
493+
panic!(
494+
"`PassMode::Direct` for aggregates only allowed for \"unadjusted\" and \"ptx-kernel\" functions and on wasm\n\
495+
Problematic type: {:#?}",
496+
arg.layout,
497+
);
498+
}
499+
}
490500
}
491501
}
492502
}

‎library/alloc/src/vec/mod.rs

+14-11
Original file line numberDiff line numberDiff line change
@@ -3025,26 +3025,29 @@ impl<T: Clone, A: Allocator> Vec<T, A> {
30253025
self.spec_extend(other.iter())
30263026
}
30273027

3028-
/// Copies elements from `src` range to the end of the vector.
3028+
/// Given a range `src`, clones a slice of elements in that range and appends it to the end.
3029+
///
3030+
/// `src` must be a range that can form a valid subslice of the `Vec`.
30293031
///
30303032
/// # Panics
30313033
///
3032-
/// Panics if the starting point is greater than the end point or if
3033-
/// the end point is greater than the length of the vector.
3034+
/// Panics if starting index is greater than the end index
3035+
/// or if the index is greater than the length of the vector.
30343036
///
30353037
/// # Examples
30363038
///
30373039
/// ```
3038-
/// let mut vec = vec![0, 1, 2, 3, 4];
3039-
///
3040-
/// vec.extend_from_within(2..);
3041-
/// assert_eq!(vec, [0, 1, 2, 3, 4, 2, 3, 4]);
3040+
/// let mut characters = vec!['a', 'b', 'c', 'd', 'e'];
3041+
/// characters.extend_from_within(2..);
3042+
/// assert_eq!(characters, ['a', 'b', 'c', 'd', 'e', 'c', 'd', 'e']);
30423043
///
3043-
/// vec.extend_from_within(..2);
3044-
/// assert_eq!(vec, [0, 1, 2, 3, 4, 2, 3, 4, 0, 1]);
3044+
/// let mut numbers = vec![0, 1, 2, 3, 4];
3045+
/// numbers.extend_from_within(..2);
3046+
/// assert_eq!(numbers, [0, 1, 2, 3, 4, 0, 1]);
30453047
///
3046-
/// vec.extend_from_within(4..8);
3047-
/// assert_eq!(vec, [0, 1, 2, 3, 4, 2, 3, 4, 0, 1, 4, 2, 3, 4]);
3048+
/// let mut strings = vec![String::from("hello"), String::from("world"), String::from("!")];
3049+
/// strings.extend_from_within(1..=2);
3050+
/// assert_eq!(strings, ["hello", "world", "!", "world", "!"]);
30483051
/// ```
30493052
#[cfg(not(no_global_oom_handling))]
30503053
#[stable(feature = "vec_extend_from_within", since = "1.53.0")]

‎src/doc/book

Submodule book updated 147 files
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# `reg-struct-return`
2+
3+
The tracking issue for this feature is: https://github.com/rust-lang/rust/issues/116973.
4+
5+
------------------------
6+
7+
Option -Zreg-struct-return causes the compiler to return small structs in registers
8+
instead of on the stack for extern "C"-like functions.
9+
It is UNSOUND to link together crates that use different values for this flag.
10+
It is only supported on `x86`.
11+
12+
It is equivalent to [Clang]'s and [GCC]'s `-freg-struct-return`.
13+
14+
[Clang]: https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-freg-struct-return
15+
[GCC]: https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html#index-freg-struct-return

‎src/tools/miri/src/alloc_addresses/mod.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -286,9 +286,9 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
286286

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

‎src/tools/miri/src/borrow_tracker/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -302,8 +302,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
302302
}
303303
}
304304

305-
fn expose_tag(&mut self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
306-
let this = self.eval_context_mut();
305+
fn expose_tag(&self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
306+
let this = self.eval_context_ref();
307307
let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method;
308308
match method {
309309
BorrowTrackerMethod::StackedBorrows => this.sb_expose_tag(alloc_id, tag),

‎src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1011,8 +1011,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
10111011
}
10121012

10131013
/// Mark the given tag as exposed. It was found on a pointer with the given AllocId.
1014-
fn sb_expose_tag(&mut self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
1015-
let this = self.eval_context_mut();
1014+
fn sb_expose_tag(&self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
1015+
let this = self.eval_context_ref();
10161016

10171017
// Function pointers and dead objects don't have an alloc_extra so we ignore them.
10181018
// This is okay because accessing them is UB anyway, no need for any Stacked Borrows checks.

‎src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -532,8 +532,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
532532
}
533533

534534
/// Mark the given tag as exposed. It was found on a pointer with the given AllocId.
535-
fn tb_expose_tag(&mut self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
536-
let this = self.eval_context_mut();
535+
fn tb_expose_tag(&self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
536+
let this = self.eval_context_ref();
537537

538538
// Function pointers and dead objects don't have an alloc_extra so we ignore them.
539539
// This is okay because accessing them is UB anyway, no need for any Tree Borrows checks.

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

+5-2
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,9 @@ impl interpret::Provenance for Provenance {
270270
/// We use absolute addresses in the `offset` of a `StrictPointer`.
271271
const OFFSET_IS_ADDR: bool = true;
272272

273+
/// Miri implements wildcard provenance.
274+
const WILDCARD: Option<Self> = Some(Provenance::Wildcard);
275+
273276
fn get_alloc_id(self) -> Option<AllocId> {
274277
match self {
275278
Provenance::Concrete { alloc_id, .. } => Some(alloc_id),
@@ -1242,8 +1245,8 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
12421245
/// Called on `ptr as usize` casts.
12431246
/// (Actually computing the resulting `usize` doesn't need machine help,
12441247
/// that's just `Scalar::try_to_int`.)
1245-
fn expose_ptr(ecx: &mut InterpCx<'tcx, Self>, ptr: StrictPointer) -> InterpResult<'tcx> {
1246-
match ptr.provenance {
1248+
fn expose_provenance(ecx: &InterpCx<'tcx, Self>, provenance: Self::Provenance) -> InterpResult<'tcx> {
1249+
match provenance {
12471250
Provenance::Concrete { alloc_id, tag } => ecx.expose_ptr(alloc_id, tag),
12481251
Provenance::Wildcard => {
12491252
// No need to do anything for wildcard pointers as

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

+34-16
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@ use std::ops::Deref;
33

44
use libffi::high::call as ffi;
55
use libffi::low::CodePtr;
6-
use rustc_abi::{BackendRepr, HasDataLayout};
7-
use rustc_middle::ty::{self as ty, IntTy, UintTy};
6+
use rustc_abi::{BackendRepr, HasDataLayout, Size};
7+
use rustc_middle::{
8+
mir::interpret::Pointer,
9+
ty::{self as ty, IntTy, UintTy},
10+
};
811
use rustc_span::Symbol;
912

1013
use crate::*;
@@ -75,6 +78,11 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
7578
unsafe { ffi::call::<()>(ptr, libffi_args.as_slice()) };
7679
return interp_ok(ImmTy::uninit(dest.layout));
7780
}
81+
ty::RawPtr(..) => {
82+
let x = unsafe { ffi::call::<*const ()>(ptr, libffi_args.as_slice()) };
83+
let ptr = Pointer::new(Provenance::Wildcard, Size::from_bytes(x.addr()));
84+
Scalar::from_pointer(ptr, this)
85+
}
7886
_ => throw_unsup_format!("unsupported return type for native call: {:?}", link_name),
7987
};
8088
interp_ok(ImmTy::from_scalar(scalar, dest.layout))
@@ -152,8 +160,26 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
152160
if !matches!(arg.layout.backend_repr, BackendRepr::Scalar(_)) {
153161
throw_unsup_format!("only scalar argument types are support for native calls")
154162
}
155-
libffi_args.push(imm_to_carg(this.read_immediate(arg)?, this)?);
163+
let imm = this.read_immediate(arg)?;
164+
libffi_args.push(imm_to_carg(&imm, this)?);
165+
// If we are passing a pointer, prepare the memory it points to.
166+
if matches!(arg.layout.ty.kind(), ty::RawPtr(..)) {
167+
let ptr = imm.to_scalar().to_pointer(this)?;
168+
let Some(prov) = ptr.provenance else {
169+
// Pointer without provenance may not access any memory.
170+
continue;
171+
};
172+
// We use `get_alloc_id` for its best-effort behaviour with Wildcard provenance.
173+
let Some(alloc_id) = prov.get_alloc_id() else {
174+
// Wildcard pointer, whatever it points to must be already exposed.
175+
continue;
176+
};
177+
this.prepare_for_native_call(alloc_id, prov)?;
178+
}
156179
}
180+
181+
// FIXME: In the future, we should also call `prepare_for_native_call` on all previously
182+
// exposed allocations, since C may access any of them.
157183

158184
// Convert them to `libffi::high::Arg` type.
159185
let libffi_args = libffi_args
@@ -220,7 +246,7 @@ impl<'a> CArg {
220246

221247
/// Extract the scalar value from the result of reading a scalar from the machine,
222248
/// and convert it to a `CArg`.
223-
fn imm_to_carg<'tcx>(v: ImmTy<'tcx>, cx: &impl HasDataLayout) -> InterpResult<'tcx, CArg> {
249+
fn imm_to_carg<'tcx>(v: &ImmTy<'tcx>, cx: &impl HasDataLayout) -> InterpResult<'tcx, CArg> {
224250
interp_ok(match v.layout.ty.kind() {
225251
// If the primitive provided can be converted to a type matching the type pattern
226252
// then create a `CArg` of this primitive value with the corresponding `CArg` constructor.
@@ -238,18 +264,10 @@ fn imm_to_carg<'tcx>(v: ImmTy<'tcx>, cx: &impl HasDataLayout) -> InterpResult<'t
238264
ty::Uint(UintTy::U64) => CArg::UInt64(v.to_scalar().to_u64()?),
239265
ty::Uint(UintTy::Usize) =>
240266
CArg::USize(v.to_scalar().to_target_usize(cx)?.try_into().unwrap()),
241-
ty::RawPtr(_, mutability) => {
242-
// Arbitrary mutable pointer accesses are not currently supported in Miri.
243-
if mutability.is_mut() {
244-
throw_unsup_format!(
245-
"unsupported mutable pointer type for native call: {}",
246-
v.layout.ty
247-
);
248-
} else {
249-
let s = v.to_scalar().to_pointer(cx)?.addr();
250-
// This relies on the `expose_provenance` in `addr_from_alloc_id`.
251-
CArg::RawPtr(std::ptr::with_exposed_provenance_mut(s.bytes_usize()))
252-
}
267+
ty::RawPtr(..) => {
268+
let s = v.to_scalar().to_pointer(cx)?.addr();
269+
// This relies on the `expose_provenance` in `addr_from_alloc_id`.
270+
CArg::RawPtr(std::ptr::with_exposed_provenance_mut(s.bytes_usize()))
253271
}
254272
_ => throw_unsup_format!("unsupported argument type for native call: {}", v.layout.ty),
255273
})

‎src/tools/miri/tests/native-lib/pass/ptr_read_access.rs

+12-15
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,14 @@
33
//@only-on-host
44

55
fn main() {
6-
test_pointer();
7-
8-
test_simple();
9-
10-
test_nested();
11-
12-
test_static();
6+
test_access_pointer();
7+
test_access_simple();
8+
test_access_nested();
9+
test_access_static();
1310
}
1411

15-
// Test void function that dereferences a pointer and prints its contents from C.
16-
fn test_pointer() {
12+
/// Test function that dereferences an int pointer and prints its contents from C.
13+
fn test_access_pointer() {
1714
extern "C" {
1815
fn print_pointer(ptr: *const i32);
1916
}
@@ -23,8 +20,8 @@ fn test_pointer() {
2320
unsafe { print_pointer(&x) };
2421
}
2522

26-
// Test function that dereferences a simple struct pointer and accesses a field.
27-
fn test_simple() {
23+
/// Test function that dereferences a simple struct pointer and accesses a field.
24+
fn test_access_simple() {
2825
#[repr(C)]
2926
struct Simple {
3027
field: i32,
@@ -39,8 +36,8 @@ fn test_simple() {
3936
assert_eq!(unsafe { access_simple(&simple) }, -42);
4037
}
4138

42-
// Test function that dereferences nested struct pointers and accesses fields.
43-
fn test_nested() {
39+
/// Test function that dereferences nested struct pointers and accesses fields.
40+
fn test_access_nested() {
4441
use std::ptr::NonNull;
4542

4643
#[derive(Debug, PartialEq, Eq)]
@@ -61,8 +58,8 @@ fn test_nested() {
6158
assert_eq!(unsafe { access_nested(&nested_2) }, 97);
6259
}
6360

64-
// Test function that dereferences static struct pointers and accesses fields.
65-
fn test_static() {
61+
/// Test function that dereferences a static struct pointer and accesses fields.
62+
fn test_access_static() {
6663
#[repr(C)]
6764
struct Static {
6865
value: i32,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
// Only works on Unix targets
2+
//@ignore-target: windows wasm
3+
//@only-on-host
4+
//@compile-flags: -Zmiri-permissive-provenance
5+
6+
7+
#![feature(box_as_ptr)]
8+
9+
use std::mem::MaybeUninit;
10+
use std::ptr::null;
11+
12+
fn main() {
13+
test_increment_int();
14+
test_init_int();
15+
test_init_array();
16+
test_init_static_inner();
17+
test_exposed();
18+
test_swap_ptr();
19+
test_swap_ptr_tuple();
20+
test_overwrite_dangling();
21+
test_pass_dangling();
22+
test_swap_ptr_triple_dangling();
23+
test_return_ptr();
24+
}
25+
26+
/// Test function that modifies an int.
27+
fn test_increment_int() {
28+
extern "C" {
29+
fn increment_int(ptr: *mut i32);
30+
}
31+
32+
let mut x = 11;
33+
34+
unsafe { increment_int(&mut x) };
35+
assert_eq!(x, 12);
36+
}
37+
38+
/// Test function that initializes an int.
39+
fn test_init_int() {
40+
extern "C" {
41+
fn init_int(ptr: *mut i32, val: i32);
42+
}
43+
44+
let mut x = MaybeUninit::<i32>::uninit();
45+
let val = 21;
46+
47+
let x = unsafe {
48+
init_int(x.as_mut_ptr(), val);
49+
x.assume_init()
50+
};
51+
assert_eq!(x, val);
52+
}
53+
54+
/// Test function that initializes an array.
55+
fn test_init_array() {
56+
extern "C" {
57+
fn init_array(ptr: *mut i32, len: usize, val: i32);
58+
}
59+
60+
const LEN: usize = 3;
61+
let mut array = MaybeUninit::<[i32; LEN]>::uninit();
62+
let val = 31;
63+
64+
let array = unsafe {
65+
init_array(array.as_mut_ptr().cast::<i32>(), LEN, val);
66+
array.assume_init()
67+
};
68+
assert_eq!(array, [val; LEN]);
69+
}
70+
71+
/// Test function that initializes an int pointed to by an immutable static.
72+
fn test_init_static_inner() {
73+
#[repr(C)]
74+
struct SyncPtr {
75+
ptr: *mut i32
76+
}
77+
unsafe impl Sync for SyncPtr {}
78+
79+
extern "C" {
80+
fn init_static_inner(s_ptr: *const SyncPtr, val: i32);
81+
}
82+
83+
static mut INNER: MaybeUninit<i32> = MaybeUninit::uninit();
84+
#[allow(static_mut_refs)]
85+
static STATIC: SyncPtr = SyncPtr { ptr: unsafe { INNER.as_mut_ptr() } };
86+
let val = 41;
87+
88+
let inner = unsafe {
89+
init_static_inner(&STATIC, val);
90+
INNER.assume_init()
91+
};
92+
assert_eq!(inner, val);
93+
}
94+
95+
// Test function that marks an allocation as exposed.
96+
fn test_exposed() {
97+
extern "C" {
98+
fn ignore_ptr(ptr: *const i32);
99+
}
100+
101+
let x = 51;
102+
let ptr = &raw const x;
103+
let p = ptr.addr();
104+
105+
unsafe { ignore_ptr(ptr) };
106+
assert_eq!(unsafe { *(p as *const i32) }, x);
107+
}
108+
109+
/// Test function that swaps two pointers and exposes the alloc of an int.
110+
fn test_swap_ptr() {
111+
extern "C" {
112+
fn swap_ptr(pptr0: *mut *const i32, pptr1: *mut *const i32);
113+
}
114+
115+
let x = 61;
116+
let (mut ptr0, mut ptr1) = (&raw const x, null());
117+
118+
unsafe { swap_ptr(&mut ptr0, &mut ptr1) };
119+
assert_eq!(unsafe { *ptr1 }, x);
120+
}
121+
122+
/// Test function that swaps two pointers in a struct and exposes the alloc of an int.
123+
fn test_swap_ptr_tuple() {
124+
#[repr(C)]
125+
struct Tuple {
126+
ptr0: *const i32,
127+
ptr1: *const i32,
128+
}
129+
130+
extern "C" {
131+
fn swap_ptr_tuple(t_ptr: *mut Tuple);
132+
}
133+
134+
let x = 71;
135+
let mut tuple = Tuple { ptr0: &raw const x, ptr1: null() };
136+
137+
unsafe { swap_ptr_tuple(&mut tuple) }
138+
assert_eq!(unsafe { *tuple.ptr1 }, x);
139+
}
140+
141+
/// Test function that interacts with a dangling pointer.
142+
fn test_overwrite_dangling() {
143+
extern "C" {
144+
fn overwrite_ptr(pptr: *mut *const i32);
145+
}
146+
147+
let b = Box::new(81);
148+
let mut ptr = Box::as_ptr(&b);
149+
drop(b);
150+
151+
unsafe { overwrite_ptr(&mut ptr) };
152+
assert_eq!(ptr, null());
153+
}
154+
155+
/// Test function that passes a dangling pointer.
156+
fn test_pass_dangling() {
157+
extern "C" {
158+
fn ignore_ptr(ptr: *const i32);
159+
}
160+
161+
let b = Box::new(91);
162+
let ptr = Box::as_ptr(&b);
163+
drop(b);
164+
165+
unsafe { ignore_ptr(ptr) };
166+
}
167+
168+
/// Test function that interacts with a struct storing a dangling pointer.
169+
fn test_swap_ptr_triple_dangling() {
170+
#[repr(C)]
171+
struct Triple {
172+
ptr0: *const i32,
173+
ptr1: *const i32,
174+
ptr2: *const i32,
175+
}
176+
177+
extern "C" {
178+
fn swap_ptr_triple_dangling(t_ptr: *const Triple);
179+
}
180+
181+
let x = 101;
182+
let b = Box::new(111);
183+
let ptr = Box::as_ptr(&b);
184+
drop(b);
185+
let z = 121;
186+
let triple = Triple {
187+
ptr0: &raw const x,
188+
ptr1: ptr,
189+
ptr2: &raw const z
190+
};
191+
192+
unsafe { swap_ptr_triple_dangling(&triple) }
193+
assert_eq!(unsafe { *triple.ptr2 }, x);
194+
}
195+
196+
197+
/// Test function that directly returns its pointer argument.
198+
fn test_return_ptr() {
199+
extern "C" {
200+
fn return_ptr(ptr: *const i32) -> *const i32;
201+
}
202+
203+
let x = 131;
204+
let ptr = &raw const x;
205+
206+
let ptr = unsafe { return_ptr(ptr) };
207+
assert_eq!(unsafe { *ptr }, x);
208+
}

‎src/tools/miri/tests/native-lib/ptr_read_access.c

+4-4
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33
// See comments in build_native_lib()
44
#define EXPORT __attribute__((visibility("default")))
55

6-
/* Test: test_pointer */
6+
/* Test: test_access_pointer */
77

88
EXPORT void print_pointer(const int *ptr) {
99
printf("printing pointer dereference from C: %d\n", *ptr);
1010
}
1111

12-
/* Test: test_simple */
12+
/* Test: test_access_simple */
1313

1414
typedef struct Simple {
1515
int field;
@@ -19,7 +19,7 @@ EXPORT int access_simple(const Simple *s_ptr) {
1919
return s_ptr->field;
2020
}
2121

22-
/* Test: test_nested */
22+
/* Test: test_access_nested */
2323

2424
typedef struct Nested {
2525
int value;
@@ -38,7 +38,7 @@ EXPORT int access_nested(const Nested *n_ptr) {
3838
return n_ptr->value;
3939
}
4040

41-
/* Test: test_static */
41+
/* Test: test_access_static */
4242

4343
typedef struct Static {
4444
int value;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
#include <stddef.h>
2+
3+
// See comments in build_native_lib()
4+
#define EXPORT __attribute__((visibility("default")))
5+
6+
/* Test: test_increment_int */
7+
8+
EXPORT void increment_int(int *ptr) {
9+
*ptr += 1;
10+
}
11+
12+
/* Test: test_init_int */
13+
14+
EXPORT void init_int(int *ptr, int val) {
15+
*ptr = val;
16+
}
17+
18+
/* Test: test_init_array */
19+
20+
EXPORT void init_array(int *array, size_t len, int val) {
21+
for (size_t i = 0; i < len; i++) {
22+
array[i] = val;
23+
}
24+
}
25+
26+
/* Test: test_init_static_inner */
27+
28+
typedef struct SyncPtr {
29+
int *ptr;
30+
} SyncPtr;
31+
32+
EXPORT void init_static_inner(const SyncPtr *s_ptr, int val) {
33+
*(s_ptr->ptr) = val;
34+
}
35+
36+
/* Tests: test_exposed, test_pass_dangling */
37+
38+
EXPORT void ignore_ptr(__attribute__((unused)) const int *ptr) {
39+
return;
40+
}
41+
42+
/* Test: test_expose_int */
43+
EXPORT void expose_int(const int *int_ptr, const int **pptr) {
44+
*pptr = int_ptr;
45+
}
46+
47+
/* Test: test_swap_ptr */
48+
49+
EXPORT void swap_ptr(const int **pptr0, const int **pptr1) {
50+
const int *tmp = *pptr0;
51+
*pptr0 = *pptr1;
52+
*pptr1 = tmp;
53+
}
54+
55+
/* Test: test_swap_ptr_tuple */
56+
57+
typedef struct Tuple {
58+
int *ptr0;
59+
int *ptr1;
60+
} Tuple;
61+
62+
EXPORT void swap_ptr_tuple(Tuple *t_ptr) {
63+
int *tmp = t_ptr->ptr0;
64+
t_ptr->ptr0 = t_ptr->ptr1;
65+
t_ptr->ptr1 = tmp;
66+
}
67+
68+
/* Test: test_overwrite_dangling */
69+
70+
EXPORT void overwrite_ptr(const int **pptr) {
71+
*pptr = NULL;
72+
}
73+
74+
/* Test: test_swap_ptr_triple_dangling */
75+
76+
typedef struct Triple {
77+
int *ptr0;
78+
int *ptr1;
79+
int *ptr2;
80+
} Triple;
81+
82+
EXPORT void swap_ptr_triple_dangling(Triple *t_ptr) {
83+
int *tmp = t_ptr->ptr0;
84+
t_ptr->ptr0 = t_ptr->ptr2;
85+
t_ptr->ptr2 = tmp;
86+
}
87+
88+
EXPORT const int *return_ptr(const int *ptr) {
89+
return ptr;
90+
}

‎src/tools/miri/tests/ui.rs

+1
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ fn build_native_lib() -> PathBuf {
6464
// FIXME: Automate gathering of all relevant C source files in the directory.
6565
"tests/native-lib/scalar_arguments.c",
6666
"tests/native-lib/ptr_read_access.c",
67+
"tests/native-lib/ptr_write_access.c",
6768
// Ensure we notice serious problems in the C code.
6869
"-Wall",
6970
"-Wextra",

‎src/tools/rustbook/Cargo.lock

+24-23
Original file line numberDiff line numberDiff line change

‎src/tools/rustbook/Cargo.toml

+1-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ edition = "2021"
99
[dependencies]
1010
clap = "4.0.32"
1111
env_logger = "0.11"
12-
mdbook-trpl-listing = { path = "../../doc/book/packages/mdbook-trpl-listing" }
13-
mdbook-trpl-note = { path = "../../doc/book/packages/mdbook-trpl-note" }
12+
mdbook-trpl = { path = "../../doc/book/packages/mdbook-trpl" }
1413
mdbook-i18n-helpers = "0.3.3"
1514
mdbook-spec = { path = "../../doc/reference/mdbook-spec" }
1615

‎src/tools/rustbook/src/main.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ use mdbook::MDBook;
66
use mdbook::errors::Result as Result3;
77
use mdbook_i18n_helpers::preprocessors::Gettext;
88
use mdbook_spec::Spec;
9-
use mdbook_trpl_listing::TrplListing;
10-
use mdbook_trpl_note::TrplNote;
9+
use mdbook_trpl::{Figure, Listing, Note};
1110

1211
fn main() {
1312
let crate_version = concat!("v", crate_version!());
@@ -109,11 +108,15 @@ pub fn build(args: &ArgMatches) -> Result3<()> {
109108
// preprocessor, or this should modify the config and use
110109
// MDBook::load_with_config.
111110
if book.config.get_preprocessor("trpl-note").is_some() {
112-
book.with_preprocessor(TrplNote);
111+
book.with_preprocessor(Note);
113112
}
114113

115114
if book.config.get_preprocessor("trpl-listing").is_some() {
116-
book.with_preprocessor(TrplListing);
115+
book.with_preprocessor(Listing);
116+
}
117+
118+
if book.config.get_preprocessor("trpl-figure").is_some() {
119+
book.with_preprocessor(Figure);
117120
}
118121

119122
if book.config.get_preprocessor("spec").is_some() {

‎tests/codegen/reg-struct-return.rs

+206
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
// Checks how `reg-struct-return` flag works with different calling conventions:
2+
// Return struct with 8/16/32/64 bit size will be converted into i8/i16/i32/i64
3+
// (like abi_return_struct_as_int target spec).
4+
// x86 only.
5+
6+
//@ revisions: ENABLED DISABLED
7+
//@ add-core-stubs
8+
//@ compile-flags: --target i686-unknown-linux-gnu -O -C no-prepopulate-passes
9+
//@ [ENABLED] compile-flags: -Zreg-struct-return
10+
//@ needs-llvm-components: x86
11+
12+
#![crate_type = "lib"]
13+
#![no_std]
14+
#![no_core]
15+
#![feature(no_core, lang_items)]
16+
17+
extern crate minicore;
18+
use minicore::*;
19+
20+
#[repr(C)]
21+
pub struct Foo {
22+
x: u32,
23+
y: u32,
24+
}
25+
26+
#[repr(C)]
27+
pub struct Foo1 {
28+
x: u32,
29+
}
30+
31+
#[repr(C)]
32+
pub struct Foo2 {
33+
x: bool,
34+
y: bool,
35+
z: i16,
36+
}
37+
38+
#[repr(C)]
39+
pub struct Foo3 {
40+
x: i16,
41+
y: bool,
42+
z: bool,
43+
}
44+
45+
#[repr(C)]
46+
pub struct Foo4 {
47+
x: char,
48+
y: bool,
49+
z: u8,
50+
}
51+
52+
#[repr(C)]
53+
pub struct Foo5 {
54+
x: u32,
55+
y: u16,
56+
z: u8,
57+
a: bool,
58+
}
59+
60+
#[repr(C)]
61+
pub struct FooOversize1 {
62+
x: u32,
63+
y: u32,
64+
z: u32,
65+
}
66+
67+
#[repr(C)]
68+
pub struct FooOversize2 {
69+
f0: u16,
70+
f1: u16,
71+
f2: u16,
72+
f3: u16,
73+
f4: u16,
74+
}
75+
76+
#[repr(C)]
77+
pub struct FooFloat1 {
78+
x: f32,
79+
y: f32,
80+
}
81+
82+
#[repr(C)]
83+
pub struct FooFloat2 {
84+
x: f64,
85+
}
86+
87+
#[repr(C)]
88+
pub struct FooFloat3 {
89+
x: f32,
90+
}
91+
92+
pub mod tests {
93+
use {
94+
Foo, Foo1, Foo2, Foo3, Foo4, Foo5, FooFloat1, FooFloat2, FooFloat3, FooOversize1,
95+
FooOversize2,
96+
};
97+
98+
// ENABLED: i64 @f1()
99+
// DISABLED: void @f1(ptr {{.*}}sret
100+
#[no_mangle]
101+
pub extern "fastcall" fn f1() -> Foo {
102+
Foo { x: 1, y: 2 }
103+
}
104+
105+
// CHECK: { i32, i32 } @f2()
106+
#[no_mangle]
107+
pub extern "Rust" fn f2() -> Foo {
108+
Foo { x: 1, y: 2 }
109+
}
110+
111+
// ENABLED: i64 @f3()
112+
// DISABLED: void @f3(ptr {{.*}}sret
113+
#[no_mangle]
114+
pub extern "C" fn f3() -> Foo {
115+
Foo { x: 1, y: 2 }
116+
}
117+
118+
// ENABLED: i64 @f4()
119+
// DISABLED: void @f4(ptr {{.*}}sret
120+
#[no_mangle]
121+
pub extern "cdecl" fn f4() -> Foo {
122+
Foo { x: 1, y: 2 }
123+
}
124+
125+
// ENABLED: i64 @f5()
126+
// DISABLED: void @f5(ptr {{.*}}sret
127+
#[no_mangle]
128+
pub extern "stdcall" fn f5() -> Foo {
129+
Foo { x: 1, y: 2 }
130+
}
131+
132+
// ENABLED: i64 @f6()
133+
// DISABLED: void @f6(ptr {{.*}}sret
134+
#[no_mangle]
135+
pub extern "thiscall" fn f6() -> Foo {
136+
Foo { x: 1, y: 2 }
137+
}
138+
139+
// ENABLED: i32 @f7()
140+
// DISABLED: void @f7(ptr {{.*}}sret
141+
#[no_mangle]
142+
pub extern "C" fn f7() -> Foo1 {
143+
Foo1 { x: 1 }
144+
}
145+
146+
// ENABLED: i32 @f8()
147+
// DISABLED: void @f8(ptr {{.*}}sret
148+
#[no_mangle]
149+
pub extern "C" fn f8() -> Foo2 {
150+
Foo2 { x: true, y: false, z: 5 }
151+
}
152+
153+
// ENABLED: i32 @f9()
154+
// DISABLED: void @f9(ptr {{.*}}sret
155+
#[no_mangle]
156+
pub extern "C" fn f9() -> Foo3 {
157+
Foo3 { x: 5, y: false, z: true }
158+
}
159+
160+
// ENABLED: i64 @f10()
161+
// DISABLED: void @f10(ptr {{.*}}sret
162+
#[no_mangle]
163+
pub extern "C" fn f10() -> Foo4 {
164+
Foo4 { x: 'x', y: true, z: 170 }
165+
}
166+
167+
// ENABLED: i64 @f11()
168+
// DISABLED: void @f11(ptr {{.*}}sret
169+
#[no_mangle]
170+
pub extern "C" fn f11() -> Foo5 {
171+
Foo5 { x: 1, y: 2, z: 3, a: true }
172+
}
173+
174+
// CHECK: void @f12(ptr {{.*}}sret
175+
#[no_mangle]
176+
pub extern "C" fn f12() -> FooOversize1 {
177+
FooOversize1 { x: 1, y: 2, z: 3 }
178+
}
179+
180+
// CHECK: void @f13(ptr {{.*}}sret
181+
#[no_mangle]
182+
pub extern "C" fn f13() -> FooOversize2 {
183+
FooOversize2 { f0: 1, f1: 2, f2: 3, f3: 4, f4: 5 }
184+
}
185+
186+
// ENABLED: i64 @f14()
187+
// DISABLED: void @f14(ptr {{.*}}sret
188+
#[no_mangle]
189+
pub extern "C" fn f14() -> FooFloat1 {
190+
FooFloat1 { x: 1.0, y: 1.0 }
191+
}
192+
193+
// ENABLED: double @f15()
194+
// DISABLED: void @f15(ptr {{.*}}sret
195+
#[no_mangle]
196+
pub extern "C" fn f15() -> FooFloat2 {
197+
FooFloat2 { x: 1.0 }
198+
}
199+
200+
// ENABLED: float @f16()
201+
// DISABLED: void @f16(ptr {{.*}}sret
202+
#[no_mangle]
203+
pub extern "C" fn f16() -> FooFloat3 {
204+
FooFloat3 { x: 1.0 }
205+
}
206+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
error: `-Zreg-struct-return` is only supported on x86
2+
3+
error: aborting due to 1 previous error
4+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//@ revisions: x86 x86_64 aarch64
2+
3+
//@ compile-flags: -Zreg-struct-return
4+
5+
//@[x86] check-pass
6+
//@[x86] needs-llvm-components: x86
7+
//@[x86] compile-flags: --target i686-unknown-linux-gnu
8+
9+
//@[x86_64] check-fail
10+
//@[x86_64] needs-llvm-components: x86
11+
//@[x86_64] compile-flags: --target x86_64-unknown-linux-gnu
12+
//@[x86_64] error-pattern: `-Zreg-struct-return` is only supported on x86
13+
14+
//@[aarch64] check-fail
15+
//@[aarch64] needs-llvm-components: aarch64
16+
//@[aarch64] compile-flags: --target aarch64-unknown-linux-gnu
17+
//@[aarch64] error-pattern: `-Zreg-struct-return` is only supported on x86
18+
19+
#![feature(no_core)]
20+
#![no_core]
21+
#![no_main]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
error: `-Zreg-struct-return` is only supported on x86
2+
3+
error: aborting due to 1 previous error
4+

0 commit comments

Comments
 (0)
Please sign in to comment.