Compare commits

...

37 Commits

Author SHA1 Message Date
bors
eabf390b4c Auto merge of #146697 - cjgillot:invalidate-patch, r=lcnr
Avoid invalidating CFG caches from MirPatch::apply.

Small effort to reduce invalidating CFG caches.
2025-09-25 17:19:29 +00:00
bors
6f34f4ee07 Auto merge of #147019 - Zalathar:rollup-boxzbmo, r=Zalathar
Rollup of 14 pull requests

Successful merges:

 - rust-lang/rust#145067 (RawVecInner: add missing `unsafe` to unsafe fns)
 - rust-lang/rust#145277 (Do not materialise X in [X; 0] when X is unsizing a const)
 - rust-lang/rust#145973 (Add `std` support for `armv7a-vex-v5`)
 - rust-lang/rust#146667 (Add an attribute to check the number of lanes in a SIMD vector after monomorphization)
 - rust-lang/rust#146735 (unstably constify float mul_add methods)
 - rust-lang/rust#146737 (f16_f128: enable some more tests in Miri)
 - rust-lang/rust#146766 (Add attributes for #[global_allocator] functions)
 - rust-lang/rust#146905 (llvm: update remarks support on LLVM 22)
 - rust-lang/rust#146982 (Remove erroneous normalization step in `tests/run-make/linker-warning`)
 - rust-lang/rust#147005 (Small string formatting cleanup)
 - rust-lang/rust#147007 (Explicitly note `&[SocketAddr]` impl of `ToSocketAddrs`)
 - rust-lang/rust#147008 (bootstrap.py: Respect build.jobs while building bootstrap tool)
 - rust-lang/rust#147013 (rustdoc: Fix documentation for `--doctest-build-arg`)
 - rust-lang/rust#147015 (Use `LLVMDisposeTargetMachine`)

r? `@ghost`
`@rustbot` modify labels: rollup
2025-09-25 14:03:21 +00:00
Stuart Cook
59866ef305
Rollup merge of #147015 - Zalathar:dispose-tm, r=lqd
Use `LLVMDisposeTargetMachine`

After bumping the minimum LLVM version to 20 (rust-lang/rust#145071), we no longer need to run any custom code when disposing of a TargetMachine, so we can just use the upstream LLVM-C function.
2025-09-25 20:32:00 +10:00
Stuart Cook
a6325412f8
Rollup merge of #147013 - fmease:fix-docs-doctest-build-arg, r=GuillaumeGomez
rustdoc: Fix documentation for `--doctest-build-arg`

In https://github.com/rust-lang/rust/pull/139863, I forgot to update the documentation.

Tracking issue: https://github.com/rust-lang/rust/issues/134172
2025-09-25 20:32:00 +10:00
Stuart Cook
fe4cceb73d
Rollup merge of #147008 - neuschaefer:bootstrap-jobs, r=Kobzol
bootstrap.py: Respect build.jobs while building bootstrap tool

On resource-constrained systems, it is vital to respect the value of build.jobs, in order to avoid overwhelming the available memory.
2025-09-25 20:31:59 +10:00
Stuart Cook
3053a18ec9
Rollup merge of #147007 - LawnGnome:tosocketaddrs-doc, r=tgross35
Explicitly note `&[SocketAddr]` impl of `ToSocketAddrs`

Although the examples below this list do imply that there's an impl of `ToSocketAddrs` for `&[SocketAddr]`, it's not actually noted in the list of default implementations.
2025-09-25 20:31:58 +10:00
Stuart Cook
62aa0ae29f
Rollup merge of #147005 - GuillaumeGomez:string-formatting-cleanup, r=jdonszelmann
Small string formatting cleanup

This PR is mostly useless. I was going through this file, saw that and corrected it. That's pretty much it. Feel free to close it if it's a bother.
2025-09-25 20:31:58 +10:00
Stuart Cook
231002f0c2
Rollup merge of #146982 - fmease:fix-rmake-linker-warning, r=bjorn3
Remove erroneous normalization step in `tests/run-make/linker-warning`

Fixes rust-lang/rust#146977.

r? bjorn3 or reassign
2025-09-25 20:31:57 +10:00
Stuart Cook
2565b27cc0
Rollup merge of #146905 - durin42:llvm-22-bitstream-remarks, r=nikic
llvm: update remarks support on LLVM 22

LLVM change dfbd76bda01e removed separate remark support entirely, but
it turns out we can just drop the parameter and everything appears to
work fine.

Fixes rust-lang/rust#146912 as far as I can tell (the test passes.)
2025-09-25 20:31:56 +10:00
Stuart Cook
46e25aa7a3
Rollup merge of #146766 - nikic:global-alloc-attr, r=nnethercote
Add attributes for #[global_allocator] functions

Emit `#[rustc_allocator]` etc. attributes on the functions generated by the `#[global_allocator]` macro, which will emit LLVM attributes like `"alloc-family"`. If the module with the global allocator participates in LTO, this ensures that the attributes typically emitted on the allocator declarations are not lost if the definition is imported.

There is a similar issue when the allocator shim is used, but I've opted not to fix that case in this PR, because doing that cleanly is somewhat gnarly.

Related to https://github.com/rust-lang/rust/issues/145995.
2025-09-25 20:31:56 +10:00
Stuart Cook
cec668fefc
Rollup merge of #146737 - RalfJung:f16-f128-miri, r=tgross35
f16_f128: enable some more tests in Miri

For some reason, a bunch of tests were disabled in Miri that don't use any fancy intrinsics. Let's enable them.

I verified this with `./x miri library/core --no-doc -- float`.

r? `@tgross35`
2025-09-25 20:31:55 +10:00
Stuart Cook
8e62f95376
Rollup merge of #146735 - Qelxiros:const_mul_add, r=tgross35,RalfJung
unstably constify float mul_add methods

Tracking issue: rust-lang/rust#146724
r? `@tgross35`
2025-09-25 20:31:54 +10:00
Stuart Cook
fab06469ee
Rollup merge of #146667 - calebzulawski:simd-mono-lane-limit, r=lcnr,RalfJung
Add an attribute to check the number of lanes in a SIMD vector after monomorphization

Allows std::simd to drop the `LaneCount<N>: SupportedLaneCount` trait and maintain good error messages.

Also, extends rust-lang/rust#145967 by including spans in layout errors for all ADTs.

r? ``@RalfJung``

cc ``@workingjubilee`` ``@programmerjake``
2025-09-25 20:31:53 +10:00
Stuart Cook
0a34928ad8
Rollup merge of #145973 - vexide:vex-std, r=tgross35
Add `std` support for `armv7a-vex-v5`

This PR adds standard library support for the VEX V5 Brain (`armv7a-vex-v5` target). It is more-or-less an updated version of the library-side work done in rust-lang/rust#131530.

This was a joint effort between me, `@lewisfm,` `@max-niederman,` `@Gavin-Niederman` and several other members of the [`vexide` project](https://github.com/vexide/).

## Background

VEXos is a fairly unconventional operating system, with user code running in a restricted enviornment with regards to I/O capabilities and whatnot. As such, several OS-dependent APIs are unsupported or have partial support (such as `std::net`, `std::process`, and most of `std::thread`). A more comprehensive list of what does or doesn't work is outlined in the [updated target documentation](https://github.com/vexide/rust/blob/vex-std/src/doc/rustc/src/platform-support/armv7a-vex-v5.md). Despite these limitations, we believe that `libstd` support on this target still has value to users, especially given the popular use of this hardware for educational purposes. For some previous discussion on this matter, see [this comment](https://github.com/rust-lang/rust/pull/131530#issuecomment-2432856841).

## SDK Linkage

VEXos doesn't really ship with an official `libc` or POSIX-style platform API (and though it does port newlib, these are stubbed on top of the underlying SDK). Instead, VEX provides their own SDK for calling platform APIs. Their official SDK is kept proprietary (with public headers), though open-source implementations exist. Following the precedent of the `armv6k-nintendo-3ds` team's work in rust-lang/rust#95897, we've opted not to directly link `libstd` to any SDK with the expectation that users will provide their own with one of the following options:
-  [`vex-sdk-download`](https://github.com/vexide/vex-sdk/tree/main/packages/vex-sdk-download), which downloads an official proprietary SDK from VEX using a build script.
- [`vex-sdk-jumptable`](https://crates.io/crates/vex-sdk-jumptable), which is a compatible, open-source reimplementation of the SDK using firmware jumps.
- [`vex-sdk-pros`](https://github.com/vexide/vex-sdk/tree/main/packages/vex-sdk-pros), which uses the [PROS kernel](https://github.com/purduesigbots/pros) as a provider for SDK functions.
- Linking their own implementation or stubbing the functions required by libstd.

 The `vex-sdk` crate used in the VEXos PAL provides `libc`-style FFI bindings for any compatible system library, so any of these options *should* work fine. A functional demo project using `vex-sdk-download` can be found [here](https://github.com/vexide/armv7a-vex-v5-demo/tree/main).

## Future Work

This PR implements virtually everything we are currently able to implement given the current capabilities of the platform. The exception to this is file directory enumeration, though the implementation of that is sufficiently [gross enough](c6c5bad11e/packages/vexide-core/src/fs/mod.rs (L987)) to drive us away from supporting this officially.

Additionally, I have a working branch implementing the `panic_unwind` runtime for this target, which is something that would be nice to see in the future, though given the volume of compiler changes i've deemed it out-of-scope for this PR.
2025-09-25 20:31:53 +10:00
Stuart Cook
21b0e12e01
Rollup merge of #145277 - dingxiangfei2009:fold-coercion-into-const, r=nnethercote
Do not materialise X in [X; 0] when X is unsizing a const

Fix rust-lang/rust#143671

It turns out that MIR builder materialise `X` in `[X; 0]` into a temporary local when `X` is unsizing a `const`. This led to a confusing call to destructor of `X` when such a destructor is declared. [Playground](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2024&gist=8dfc933af89efeb89c881bc77498ba63)

This patch may miss out other cases that we should avoid materialisation in case of `[X; 0]`. Suggestions to include is most welcome!
2025-09-25 20:31:52 +10:00
Stuart Cook
2acd80cfa9
Rollup merge of #145067 - btj:patch-3, r=tgross35
RawVecInner: add missing `unsafe` to unsafe fns

Some (module-private) functions in `library/alloc/src/raw_vec/mod.rs` are unsafe (i.e. may cause UB when called from safe code) but are not marked `unsafe`. Specifically:
- `RawVecInner::grow_exact` causes UB if called with `len` and `additional` arguments such that `len + additional` is less than the current capacity. Indeed, in that case it calls [Allocator::grow](https://doc.rust-lang.org/std/alloc/trait.Allocator.html#method.grow) with a `new_layout` that is smaller than `old_layout`, which violates a safety precondition.
- The RawVecInner methods for resizing the buffer cause UB if called with an `elem_layout` different from the one used to initially allocate the buffer, because in that case `Allocator::grow` or `Allocator::shrink` are called with an `old_layout` that does not *fit* the allocated block, which violates a safety precondition.
- `RawVecInner::current_memory` might cause UB if called with an `elem_layout` different from the one used to initially allocate the buffer, because the `unchecked_mul` might overflow.
- Furthermore, these methods cause UB if called with an `elem_layout` where the size is not a multiple of the alignment. This is because `Layout::repeat` is used (in `layout_array`) to compute the allocation's layout when allocating, which includes padding to ensure alignment of array elements, but simple multiplication is used (in `current_memory`) to compute the old allocation's layout when resizing or deallocating, which would cause the layout used to resize or deallocate to not *fit* the allocated block, which violates a safety precondition.

I discovered these issues while performing formal verification of `library/alloc/src/raw_vec/mod.rs` per [Challenge 19](https://model-checking.github.io/verify-rust-std/challenges/0019-rawvec.html) of the [AWS Rust Standard Library Verification Contest](https://aws.amazon.com/blogs/opensource/verify-the-safety-of-the-rust-standard-library/).
2025-09-25 20:31:51 +10:00
J. Neuschäfer
747019ce46 bootstrap.py: Respect build.jobs while building bootstrap tool
On resource-constrained systems, it is vital to respect the value of
build.jobs, in order to avoid overwhelming the available memory.
2025-09-25 12:08:58 +02:00
Zalathar
85018f09f6 Use LLVMDisposeTargetMachine 2025-09-25 18:10:55 +10:00
León Orell Valerian Liehr
932f3a8ecd
rustdoc: Fix documentation for --doctest-build-arg 2025-09-25 09:29:10 +02:00
León Orell Valerian Liehr
bc37dd4a72
Remove an erroneous normalization step in tests/run-make/linker-warning 2025-09-25 06:30:19 +02:00
Josh Stone
fe440ec934 llvm: add a destructor to call releaseSerializer 2025-09-24 16:53:17 -07:00
Adam Harvey
852da23a2d
Explicitly note &[SocketAddr] impl of ToSocketAddrs.
Although the examples below this list do imply that there's an impl of
`ToSocketAddrs` for `&[SocketAddr]`, it's not actually noted in the list
of default implementations.
2025-09-24 15:56:38 -07:00
Guillaume Gomez
aa75d34035 Small string formatting cleanup 2025-09-24 23:47:13 +02:00
Jeremy Smart
a00f24116e
unstably constify float mul_add methods
Co-authored-by: Ralf Jung <post@ralfj.de>
2025-09-24 15:21:31 -04:00
Ding Xiang Fei
b77de834c0
mark THIR use as candidate for constness check 2025-09-25 01:54:25 +08:00
Ding Xiang Fei
120162e30f
add test fixture for newly allowed const expr
Signed-off-by: Ding Xiang Fei <dingxiangfei2009@protonmail.ch>
Co-authored-by: Theemathas Chirananthavat <theemathas@gmail.com>
2025-09-25 01:54:24 +08:00
Ding Xiang Fei
a86f140727
do not materialise X in [X; 0] when X is unsizing a const 2025-09-25 01:54:23 +08:00
Tropical
b2634e31c4 std: add support for armv7a-vex-v5 target
Co-authored-by: Lewis McClelland <lewis@lewismcclelland.me>
2025-09-24 12:10:15 -05:00
Ralf Jung
f509dff56d f16_f128: enable some more tests in Miri 2025-09-24 16:47:13 +02:00
Caleb Zulawski
60548ffaa3 Including spans in layout errors for all ADTs 2025-09-23 22:15:06 -04:00
Caleb Zulawski
f5c6c9542e Add an attribute to check the number of lanes in a SIMD vector after monomorphization
Unify zero-length and oversized SIMD errors
2025-09-23 20:47:34 -04:00
Camille Gillot
ce677c7db8
Update compiler/rustc_mir_transform/src/patch.rs
Co-authored-by: lcnr <rust@lcnr.de>
2025-09-23 20:38:38 -03:00
Augie Fackler
42cf78f762 llvm: update remarks support on LLVM 22
LLVM change dfbd76bda01e removed separate remark support entirely, but
it turns out we can just drop the parameter and everything appears to
work fine.

Fixes 146912 as far as I can tell (the test passes.)

@rustbot label llvm-main
2025-09-23 13:25:04 -04:00
Nikita Popov
bc7986ec79 Add attributes for #[global_allocator] functions
Emit `#[rustc_allocator]` etc. attributes on the functions generated
by the `#[global_allocator]` macro, which will emit LLVM attributes
like `"alloc-family"`. If the module with the global allocator
participates in LTO, this ensures that the attributes typically
emitted on the allocator declarations are not lost if the
definition is imported.
2025-09-23 10:21:17 +02:00
Camille Gillot
3c232fe38f Make term_patch_map sparse. 2025-09-20 13:53:58 +00:00
Camille Gillot
b216cf34b1 Avoid invalidating from MirPatch::apply. 2025-09-18 01:09:53 +00:00
Bart Jacobs
96f385b20a RawVecInner: add missing unsafe to unsafe fns
- RawVecInner::grow_exact causes UB if called with len and additional
  arguments such that len + additional is less than the current
  capacity.  Indeed, in that case it calls Allocator::grow with a
  new_layout that is smaller than old_layout, which violates a safety
  precondition.

- All RawVecInner methods for resizing the buffer cause UB
  if called with an elem_layout different from the one used to initially
  allocate the buffer, because in that case Allocator::grow/shrink is called with
  an old_layout that does not fit the allocated block, which violates a
  safety precondition.

- RawVecInner::current_memory might cause UB if called with an elem_layout
  different from the one used to initially allocate the buffer, because
  the unchecked_mul might overflow.

- Furthermore, these methods cause UB if called with an elem_layout
  where the size is not a multiple of the alignment. This is because
  Layout::repeat is used (in layout_array) to compute the allocation's
  layout when allocating, which includes padding to ensure alignment of
  array elements, but simple multiplication is used (in current_memory) to
  compute the old allocation's layout when resizing or deallocating, which
  would cause the layout used to resize or deallocate to not fit the
  allocated block, which violates a safety precondition.
2025-09-05 10:21:21 +02:00
91 changed files with 1691 additions and 334 deletions

View File

@ -1,40 +1,4 @@
use std::num::IntErrorKind;
use rustc_hir::limit::Limit;
use super::prelude::*;
use crate::session_diagnostics::LimitInvalid;
impl<S: Stage> AcceptContext<'_, '_, S> {
fn parse_limit_int(&self, nv: &NameValueParser) -> Option<Limit> {
let Some(limit) = nv.value_as_str() else {
self.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
return None;
};
let error_str = match limit.as_str().parse() {
Ok(i) => return Some(Limit::new(i)),
Err(e) => match e.kind() {
IntErrorKind::PosOverflow => "`limit` is too large",
IntErrorKind::Empty => "`limit` must be a non-negative integer",
IntErrorKind::InvalidDigit => "not a valid integer",
IntErrorKind::NegOverflow => {
panic!(
"`limit` should never negatively overflow since we're parsing into a usize and we'd get Empty instead"
)
}
IntErrorKind::Zero => {
panic!("zero is a valid `limit` so should have returned Ok() when parsing")
}
kind => panic!("unimplemented IntErrorKind variant: {:?}", kind),
},
};
self.emit_err(LimitInvalid { span: self.attr_span, value_span: nv.value_span, error_str });
None
}
}
pub(crate) struct CrateNameParser;

View File

@ -49,3 +49,21 @@ impl<S: Stage> SingleAttributeParser<S> for RustcObjectLifetimeDefaultParser {
Some(AttributeKind::RustcObjectLifetimeDefault)
}
}
pub(crate) struct RustcSimdMonomorphizeLaneLimitParser;
impl<S: Stage> SingleAttributeParser<S> for RustcSimdMonomorphizeLaneLimitParser {
const PATH: &[Symbol] = &[sym::rustc_simd_monomorphize_lane_limit];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N");
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
let ArgParser::NameValue(nv) = args else {
cx.expected_name_value(cx.attr_span, None);
return None;
};
Some(AttributeKind::RustcSimdMonomorphizeLaneLimit(cx.parse_limit_int(nv)?))
}
}

View File

@ -1,11 +1,15 @@
use std::num::IntErrorKind;
use rustc_ast::LitKind;
use rustc_ast::attr::AttributeExt;
use rustc_feature::is_builtin_attr_name;
use rustc_hir::RustcVersion;
use rustc_hir::limit::Limit;
use rustc_span::{Symbol, sym};
use crate::context::{AcceptContext, Stage};
use crate::parser::ArgParser;
use crate::parser::{ArgParser, NameValueParser};
use crate::session_diagnostics::LimitInvalid;
/// Parse a rustc version number written inside string literal in an attribute,
/// like appears in `since = "1.0.0"`. Suffixes like "-dev" and "-nightly" are
@ -85,3 +89,34 @@ pub(crate) fn parse_single_integer<S: Stage>(
};
Some(num.0)
}
impl<S: Stage> AcceptContext<'_, '_, S> {
pub(crate) fn parse_limit_int(&self, nv: &NameValueParser) -> Option<Limit> {
let Some(limit) = nv.value_as_str() else {
self.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
return None;
};
let error_str = match limit.as_str().parse() {
Ok(i) => return Some(Limit::new(i)),
Err(e) => match e.kind() {
IntErrorKind::PosOverflow => "`limit` is too large",
IntErrorKind::Empty => "`limit` must be a non-negative integer",
IntErrorKind::InvalidDigit => "not a valid integer",
IntErrorKind::NegOverflow => {
panic!(
"`limit` should never negatively overflow since we're parsing into a usize and we'd get Empty instead"
)
}
IntErrorKind::Zero => {
panic!("zero is a valid `limit` so should have returned Ok() when parsing")
}
kind => panic!("unimplemented IntErrorKind variant: {:?}", kind),
},
};
self.emit_err(LimitInvalid { span: self.attr_span, value_span: nv.value_span, error_str });
None
}
}

View File

@ -53,7 +53,7 @@ use crate::attributes::prototype::CustomMirParser;
use crate::attributes::repr::{AlignParser, AlignStaticParser, ReprParser};
use crate::attributes::rustc_internal::{
RustcLayoutScalarValidRangeEnd, RustcLayoutScalarValidRangeStart,
RustcObjectLifetimeDefaultParser,
RustcObjectLifetimeDefaultParser, RustcSimdMonomorphizeLaneLimitParser,
};
use crate::attributes::semantics::MayDangleParser;
use crate::attributes::stability::{
@ -198,6 +198,7 @@ attribute_parsers!(
Single<RustcLayoutScalarValidRangeEnd>,
Single<RustcLayoutScalarValidRangeStart>,
Single<RustcObjectLifetimeDefaultParser>,
Single<RustcSimdMonomorphizeLaneLimitParser>,
Single<SanitizeParser>,
Single<ShouldPanicParser>,
Single<SkipDuringMethodDispatchParser>,

View File

@ -85,7 +85,7 @@ impl AllocFnFactory<'_, '_> {
body,
define_opaque: None,
}));
let item = self.cx.item(self.span, self.attrs(), kind);
let item = self.cx.item(self.span, self.attrs(method), kind);
self.cx.stmt_item(self.ty_span, item)
}
@ -100,8 +100,18 @@ impl AllocFnFactory<'_, '_> {
self.cx.expr_call(self.ty_span, method, args)
}
fn attrs(&self) -> AttrVec {
thin_vec![self.cx.attr_word(sym::rustc_std_internal_symbol, self.span)]
fn attrs(&self, method: &AllocatorMethod) -> AttrVec {
let alloc_attr = match method.name {
sym::alloc => sym::rustc_allocator,
sym::dealloc => sym::rustc_deallocator,
sym::realloc => sym::rustc_reallocator,
sym::alloc_zeroed => sym::rustc_allocator_zeroed,
_ => unreachable!("Unknown allocator method!"),
};
thin_vec![
self.cx.attr_word(sym::rustc_std_internal_symbol, self.span),
self.cx.attr_word(alloc_attr, self.span)
]
}
fn arg_ty(&self, input: &AllocatorMethodInput, args: &mut ThinVec<Param>) -> Box<Expr> {

View File

@ -439,7 +439,10 @@ pub(crate) struct FullyMonomorphizedLayoutCx<'tcx>(pub(crate) TyCtxt<'tcx>);
impl<'tcx> LayoutOfHelpers<'tcx> for FullyMonomorphizedLayoutCx<'tcx> {
#[inline]
fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! {
if let LayoutError::SizeOverflow(_) | LayoutError::ReferencesError(_) = err {
if let LayoutError::SizeOverflow(_)
| LayoutError::InvalidSimd { .. }
| LayoutError::ReferencesError(_) = err
{
self.0.sess.dcx().span_fatal(span, err.to_string())
} else {
self.0
@ -458,7 +461,9 @@ impl<'tcx> FnAbiOfHelpers<'tcx> for FullyMonomorphizedLayoutCx<'tcx> {
span: Span,
fn_abi_request: FnAbiRequest<'tcx>,
) -> ! {
if let FnAbiError::Layout(LayoutError::SizeOverflow(_)) = err {
if let FnAbiError::Layout(LayoutError::SizeOverflow(_) | LayoutError::InvalidSimd { .. }) =
err
{
self.0.sess.dcx().emit_fatal(Spanned { span, node: err })
} else {
match fn_abi_request {

View File

@ -42,7 +42,10 @@ impl<'tcx> AsmCodegenMethods<'tcx> for GlobalAsmContext<'_, 'tcx> {
impl<'tcx> LayoutOfHelpers<'tcx> for GlobalAsmContext<'_, 'tcx> {
#[inline]
fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! {
if let LayoutError::SizeOverflow(_) | LayoutError::ReferencesError(_) = err {
if let LayoutError::SizeOverflow(_)
| LayoutError::InvalidSimd { .. }
| LayoutError::ReferencesError(_) = err
{
self.tcx.sess.dcx().span_fatal(span, err.to_string())
} else {
self.tcx

View File

@ -529,7 +529,10 @@ impl<'gcc, 'tcx> HasX86AbiOpt for CodegenCx<'gcc, 'tcx> {
impl<'gcc, 'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'gcc, 'tcx> {
#[inline]
fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! {
if let LayoutError::SizeOverflow(_) | LayoutError::ReferencesError(_) = err {
if let LayoutError::SizeOverflow(_)
| LayoutError::InvalidSimd { .. }
| LayoutError::ReferencesError(_) = err
{
self.tcx.dcx().emit_fatal(respan(span, err.into_diagnostic()))
} else {
self.tcx.dcx().emit_fatal(ssa_errors::FailedToGetLayout { span, ty, err })
@ -545,7 +548,9 @@ impl<'gcc, 'tcx> FnAbiOfHelpers<'tcx> for CodegenCx<'gcc, 'tcx> {
span: Span,
fn_abi_request: FnAbiRequest<'tcx>,
) -> ! {
if let FnAbiError::Layout(LayoutError::SizeOverflow(_)) = err {
if let FnAbiError::Layout(LayoutError::SizeOverflow(_) | LayoutError::InvalidSimd { .. }) =
err
{
self.tcx.dcx().emit_fatal(respan(span, err))
} else {
match fn_abi_request {

View File

@ -95,8 +95,6 @@ impl Drop for OwnedTargetMachine {
// SAFETY: constructing ensures we have a valid pointer created by
// llvm::LLVMRustCreateTargetMachine OwnedTargetMachine is not copyable so there is no
// double free or use after free.
unsafe {
llvm::LLVMRustDisposeTargetMachine(self.tm_unique.as_ptr());
}
unsafe { llvm::LLVMDisposeTargetMachine(self.tm_unique) };
}
}

View File

@ -1044,7 +1044,10 @@ impl<'tcx, 'll> HasTypingEnv<'tcx> for CodegenCx<'ll, 'tcx> {
impl<'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'_, 'tcx> {
#[inline]
fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! {
if let LayoutError::SizeOverflow(_) | LayoutError::ReferencesError(_) = err {
if let LayoutError::SizeOverflow(_)
| LayoutError::ReferencesError(_)
| LayoutError::InvalidSimd { .. } = err
{
self.tcx.dcx().emit_fatal(Spanned { span, node: err.into_diagnostic() })
} else {
self.tcx.dcx().emit_fatal(ssa_errors::FailedToGetLayout { span, ty, err })
@ -1061,7 +1064,11 @@ impl<'tcx> FnAbiOfHelpers<'tcx> for CodegenCx<'_, 'tcx> {
fn_abi_request: FnAbiRequest<'tcx>,
) -> ! {
match err {
FnAbiError::Layout(LayoutError::SizeOverflow(_) | LayoutError::Cycle(_)) => {
FnAbiError::Layout(
LayoutError::SizeOverflow(_)
| LayoutError::Cycle(_)
| LayoutError::InvalidSimd { .. },
) => {
self.tcx.dcx().emit_fatal(Spanned { span, node: err });
}
_ => match fn_abi_request {

View File

@ -477,8 +477,8 @@ pub(crate) fn spanned_type_di_node<'ll, 'tcx>(
ty::CoroutineClosure(..) => build_closure_env_di_node(cx, unique_type_id),
ty::Coroutine(..) => enums::build_coroutine_di_node(cx, unique_type_id),
ty::Adt(def, ..) => match def.adt_kind() {
AdtKind::Struct => build_struct_type_di_node(cx, unique_type_id),
AdtKind::Union => build_union_type_di_node(cx, unique_type_id),
AdtKind::Struct => build_struct_type_di_node(cx, unique_type_id, span),
AdtKind::Union => build_union_type_di_node(cx, unique_type_id, span),
AdtKind::Enum => enums::build_enum_type_di_node(cx, unique_type_id, span),
},
ty::Tuple(_) => build_tuple_type_di_node(cx, unique_type_id),
@ -1076,6 +1076,7 @@ fn visibility_di_flags<'ll, 'tcx>(
fn build_struct_type_di_node<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
unique_type_id: UniqueTypeId<'tcx>,
span: Span,
) -> DINodeCreationResult<'ll> {
let struct_type = unique_type_id.expect_ty();
let ty::Adt(adt_def, _) = struct_type.kind() else {
@ -1083,7 +1084,7 @@ fn build_struct_type_di_node<'ll, 'tcx>(
};
assert!(adt_def.is_struct());
let containing_scope = get_namespace_for_item(cx, adt_def.did());
let struct_type_and_layout = cx.layout_of(struct_type);
let struct_type_and_layout = cx.spanned_layout_of(struct_type, span);
let variant_def = adt_def.non_enum_variant();
let def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers {
Some(file_metadata_from_def_id(cx, Some(adt_def.did())))
@ -1276,6 +1277,7 @@ fn build_closure_env_di_node<'ll, 'tcx>(
fn build_union_type_di_node<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
unique_type_id: UniqueTypeId<'tcx>,
span: Span,
) -> DINodeCreationResult<'ll> {
let union_type = unique_type_id.expect_ty();
let (union_def_id, variant_def) = match union_type.kind() {
@ -1283,7 +1285,7 @@ fn build_union_type_di_node<'ll, 'tcx>(
_ => bug!("build_union_type_di_node on a non-ADT"),
};
let containing_scope = get_namespace_for_item(cx, union_def_id);
let union_ty_and_layout = cx.layout_of(union_type);
let union_ty_and_layout = cx.spanned_layout_of(union_type, span);
let type_name = compute_debuginfo_type_name(cx.tcx, union_type, false);
let def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers {
Some(file_metadata_from_def_id(cx, Some(union_def_id)))

View File

@ -1053,6 +1053,8 @@ unsafe extern "C" {
SLen: c_uint,
) -> MetadataKindId;
pub(crate) fn LLVMDisposeTargetMachine(T: ptr::NonNull<TargetMachine>);
// Create modules.
pub(crate) fn LLVMModuleCreateWithNameInContext(
ModuleID: *const c_char,
@ -2499,7 +2501,6 @@ unsafe extern "C" {
UseWasmEH: bool,
) -> *mut TargetMachine;
pub(crate) fn LLVMRustDisposeTargetMachine(T: *mut TargetMachine);
pub(crate) fn LLVMRustAddLibraryInfo<'a>(
PM: &PassManager<'a>,
M: &'a Module,

View File

@ -636,6 +636,14 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
dest,
rustc_apfloat::Round::NearestTiesToEven,
)?,
sym::fmaf16 => self.fma_intrinsic::<Half>(args, dest)?,
sym::fmaf32 => self.fma_intrinsic::<Single>(args, dest)?,
sym::fmaf64 => self.fma_intrinsic::<Double>(args, dest)?,
sym::fmaf128 => self.fma_intrinsic::<Quad>(args, dest)?,
sym::fmuladdf16 => self.float_muladd_intrinsic::<Half>(args, dest)?,
sym::fmuladdf32 => self.float_muladd_intrinsic::<Single>(args, dest)?,
sym::fmuladdf64 => self.float_muladd_intrinsic::<Double>(args, dest)?,
sym::fmuladdf128 => self.float_muladd_intrinsic::<Quad>(args, dest)?,
// Unsupported intrinsic: skip the return_to_block below.
_ => return interp_ok(false),
@ -1035,4 +1043,42 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
self.write_scalar(res, dest)?;
interp_ok(())
}
fn fma_intrinsic<F>(
&mut self,
args: &[OpTy<'tcx, M::Provenance>],
dest: &PlaceTy<'tcx, M::Provenance>,
) -> InterpResult<'tcx, ()>
where
F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>,
{
let a: F = self.read_scalar(&args[0])?.to_float()?;
let b: F = self.read_scalar(&args[1])?.to_float()?;
let c: F = self.read_scalar(&args[2])?.to_float()?;
let res = a.mul_add(b, c).value;
let res = self.adjust_nan(res, &[a, b, c]);
self.write_scalar(res, dest)?;
interp_ok(())
}
fn float_muladd_intrinsic<F>(
&mut self,
args: &[OpTy<'tcx, M::Provenance>],
dest: &PlaceTy<'tcx, M::Provenance>,
) -> InterpResult<'tcx, ()>
where
F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>,
{
let a: F = self.read_scalar(&args[0])?.to_float()?;
let b: F = self.read_scalar(&args[1])?.to_float()?;
let c: F = self.read_scalar(&args[2])?.to_float()?;
let fuse = M::float_fuse_mul_add(self);
let res = if fuse { a.mul_add(b, c).value } else { ((a * b).value + c).value };
let res = self.adjust_nan(res, &[a, b, c]);
self.write_scalar(res, dest)?;
interp_ok(())
}
}

View File

@ -289,6 +289,9 @@ pub trait Machine<'tcx>: Sized {
a
}
/// Determines whether the `fmuladd` intrinsics fuse the multiply-add or use separate operations.
fn float_fuse_mul_add(_ecx: &mut InterpCx<'tcx, Self>) -> bool;
/// Called before a basic block terminator is executed.
#[inline]
fn before_terminator(_ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> {
@ -672,6 +675,11 @@ pub macro compile_time_machine(<$tcx: lifetime>) {
match fn_val {}
}
#[inline(always)]
fn float_fuse_mul_add(_ecx: &mut InterpCx<$tcx, Self>) -> bool {
true
}
#[inline(always)]
fn ub_checks(_ecx: &InterpCx<$tcx, Self>) -> InterpResult<$tcx, bool> {
// We can't look at `tcx.sess` here as that can differ across crates, which can lead to

View File

@ -1211,6 +1211,12 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
"the `#[rustc_layout_scalar_valid_range_end]` attribute is just used to enable \
niche optimizations in the standard library",
),
rustc_attr!(
rustc_simd_monomorphize_lane_limit, Normal, template!(NameValueStr: "N"), ErrorFollowing,
EncodeCrossCrate::Yes,
"the `#[rustc_simd_monomorphize_lane_limit]` attribute is just used by std::simd \
for better error messages",
),
rustc_attr!(
rustc_nonnull_optimization_guaranteed, Normal, template!(Word), WarnFollowing,
EncodeCrossCrate::Yes,

View File

@ -651,6 +651,9 @@ pub enum AttributeKind {
/// Represents `#[rustc_object_lifetime_default]`.
RustcObjectLifetimeDefault,
/// Represents `#[rustc_simd_monomorphize_lane_limit = "N"]`.
RustcSimdMonomorphizeLaneLimit(Limit),
/// Represents `#[sanitize]`
///
/// the on set and off set are distjoint since there's a third option: unset.

View File

@ -88,6 +88,7 @@ impl AttributeKind {
RustcLayoutScalarValidRangeEnd(..) => Yes,
RustcLayoutScalarValidRangeStart(..) => Yes,
RustcObjectLifetimeDefault => No,
RustcSimdMonomorphizeLaneLimit(..) => Yes, // Affects layout computation, which needs to work cross-crate
Sanitize { .. } => No,
ShouldPanic { .. } => No,
SkipDuringMethodDispatch { .. } => No,

View File

@ -219,6 +219,7 @@ fn should_emit_generic_error<'tcx>(abi: ExternAbi, layout_err: &'tcx LayoutError
}
Unknown(..)
| SizeOverflow(..)
| InvalidSimd { .. }
| NormalizationFailure(..)
| ReferencesError(..)
| Cycle(..) => {

View File

@ -386,8 +386,8 @@ fn get_codegen_sysroot(
.collect::<Vec<_>>()
.join("\n* ");
let err = format!(
"failed to find a `codegen-backends` folder \
in the sysroot candidates:\n* {candidates}"
"failed to find a `codegen-backends` folder in the sysroot candidates:\n\
* {candidates}"
);
early_dcx.early_fatal(err);
});
@ -396,10 +396,8 @@ fn get_codegen_sysroot(
let d = sysroot.read_dir().unwrap_or_else(|e| {
let err = format!(
"failed to load default codegen backend, couldn't \
read `{}`: {}",
"failed to load default codegen backend, couldn't read `{}`: {e}",
sysroot.display(),
e
);
early_dcx.early_fatal(err);
});

View File

@ -359,10 +359,6 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
return wrap(TM);
}
extern "C" void LLVMRustDisposeTargetMachine(LLVMTargetMachineRef TM) {
delete unwrap(TM);
}
// Unfortunately, the LLVM C API doesn't provide a way to create the
// TargetLibraryInfo pass, so we use this method to do so.
extern "C" void LLVMRustAddLibraryInfo(LLVMPassManagerRef PMR, LLVMModuleRef M,

View File

@ -1685,6 +1685,14 @@ extern "C" void LLVMRustContextConfigureDiagnosticHandler(
RemarkStreamer(std::move(RemarkStreamer)),
LlvmRemarkStreamer(std::move(LlvmRemarkStreamer)) {}
#if LLVM_VERSION_GE(22, 0)
~RustDiagnosticHandler() {
if (RemarkStreamer) {
RemarkStreamer->releaseSerializer();
}
}
#endif
virtual bool handleDiagnostics(const DiagnosticInfo &DI) override {
// If this diagnostic is one of the optimization remark kinds, we can
// check if it's enabled before emitting it. This can avoid many
@ -1779,9 +1787,14 @@ extern "C" void LLVMRustContextConfigureDiagnosticHandler(
// Do not delete the file after we gather remarks
RemarkFile->keep();
#if LLVM_VERSION_GE(22, 0)
auto RemarkSerializer = remarks::createRemarkSerializer(
llvm::remarks::Format::YAML, RemarkFile->os());
#else
auto RemarkSerializer = remarks::createRemarkSerializer(
llvm::remarks::Format::YAML, remarks::SerializerMode::Separate,
RemarkFile->os());
#endif
if (Error E = RemarkSerializer.takeError()) {
std::string Error = std::string("Cannot create remark serializer: ") +
toString(std::move(E));

View File

@ -98,6 +98,12 @@ middle_layout_references_error =
middle_layout_size_overflow =
values of the type `{$ty}` are too big for the target architecture
middle_layout_simd_too_many =
the SIMD type `{$ty}` has more elements than the limit {$max_lanes}
middle_layout_simd_zero_length =
the SIMD type `{$ty}` has zero elements
middle_layout_too_generic = the type `{$ty}` does not have a fixed layout
middle_layout_unknown =

View File

@ -143,6 +143,12 @@ pub enum LayoutError<'tcx> {
#[diag(middle_layout_size_overflow)]
Overflow { ty: Ty<'tcx> },
#[diag(middle_layout_simd_too_many)]
SimdTooManyLanes { ty: Ty<'tcx>, max_lanes: u64 },
#[diag(middle_layout_simd_zero_length)]
SimdZeroLength { ty: Ty<'tcx> },
#[diag(middle_layout_normalization_failure)]
NormalizationFailure { ty: Ty<'tcx>, failure_ty: String },

View File

@ -218,6 +218,15 @@ impl fmt::Display for ValidityRequirement {
}
}
#[derive(Copy, Clone, Debug, HashStable, TyEncodable, TyDecodable)]
pub enum SimdLayoutError {
/// The vector has 0 lanes.
ZeroLength,
/// The vector has more lanes than supported or permitted by
/// #\[rustc_simd_monomorphize_lane_limit\].
TooManyLanes(u64),
}
#[derive(Copy, Clone, Debug, HashStable, TyEncodable, TyDecodable)]
pub enum LayoutError<'tcx> {
/// A type doesn't have a sensible layout.
@ -230,6 +239,8 @@ pub enum LayoutError<'tcx> {
Unknown(Ty<'tcx>),
/// The size of a type exceeds [`TargetDataLayout::obj_size_bound`].
SizeOverflow(Ty<'tcx>),
/// A SIMD vector has invalid layout, such as zero-length or too many lanes.
InvalidSimd { ty: Ty<'tcx>, kind: SimdLayoutError },
/// The layout can vary due to a generic parameter.
///
/// Unlike `Unknown`, this variant is a "soft" error and indicates that the layout
@ -257,6 +268,10 @@ impl<'tcx> LayoutError<'tcx> {
match self {
Unknown(_) => middle_layout_unknown,
SizeOverflow(_) => middle_layout_size_overflow,
InvalidSimd { kind: SimdLayoutError::TooManyLanes(_), .. } => {
middle_layout_simd_too_many
}
InvalidSimd { kind: SimdLayoutError::ZeroLength, .. } => middle_layout_simd_zero_length,
TooGeneric(_) => middle_layout_too_generic,
NormalizationFailure(_, _) => middle_layout_normalization_failure,
Cycle(_) => middle_layout_cycle,
@ -271,6 +286,10 @@ impl<'tcx> LayoutError<'tcx> {
match self {
Unknown(ty) => E::Unknown { ty },
SizeOverflow(ty) => E::Overflow { ty },
InvalidSimd { ty, kind: SimdLayoutError::TooManyLanes(max_lanes) } => {
E::SimdTooManyLanes { ty, max_lanes }
}
InvalidSimd { ty, kind: SimdLayoutError::ZeroLength } => E::SimdZeroLength { ty },
TooGeneric(ty) => E::TooGeneric { ty },
NormalizationFailure(ty, e) => {
E::NormalizationFailure { ty, failure_ty: e.get_type_for_failure() }
@ -293,6 +312,12 @@ impl<'tcx> fmt::Display for LayoutError<'tcx> {
LayoutError::SizeOverflow(ty) => {
write!(f, "values of the type `{ty}` are too big for the target architecture")
}
LayoutError::InvalidSimd { ty, kind: SimdLayoutError::TooManyLanes(max_lanes) } => {
write!(f, "the SIMD type `{ty}` has more elements than the limit {max_lanes}")
}
LayoutError::InvalidSimd { ty, kind: SimdLayoutError::ZeroLength } => {
write!(f, "the SIMD type `{ty}` has zero elements")
}
LayoutError::NormalizationFailure(t, e) => write!(
f,
"unable to determine layout for `{}` because `{}` cannot be normalized",
@ -374,6 +399,7 @@ impl<'tcx> SizeSkeleton<'tcx> {
e @ LayoutError::Cycle(_)
| e @ LayoutError::Unknown(_)
| e @ LayoutError::SizeOverflow(_)
| e @ LayoutError::InvalidSimd { .. }
| e @ LayoutError::NormalizationFailure(..)
| e @ LayoutError::ReferencesError(_),
) => return Err(e),

View File

@ -8,6 +8,7 @@ use rustc_middle::middle::region;
use rustc_middle::mir::interpret::Scalar;
use rustc_middle::mir::*;
use rustc_middle::thir::*;
use rustc_middle::ty::adjustment::PointerCoercion;
use rustc_middle::ty::cast::{CastTy, mir_cast_kind};
use rustc_middle::ty::util::IntTypeExt;
use rustc_middle::ty::{self, Ty, UpvarArgs};
@ -656,6 +657,27 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
block.and(rvalue)
}
/// Recursively inspect a THIR expression and probe through unsizing
/// operations that can be const-folded today.
fn check_constness(&self, mut kind: &'a ExprKind<'tcx>) -> bool {
loop {
debug!(?kind, "check_constness");
match kind {
&ExprKind::ValueTypeAscription { source: eid, user_ty: _, user_ty_span: _ }
| &ExprKind::Use { source: eid }
| &ExprKind::PointerCoercion {
cast: PointerCoercion::Unsize,
source: eid,
is_from_as_cast: _,
}
| &ExprKind::Scope { region_scope: _, lint_level: _, value: eid } => {
kind = &self.thir[eid].kind
}
_ => return matches!(Category::of(&kind), Some(Category::Constant)),
}
}
}
fn build_zero_repeat(
&mut self,
mut block: BasicBlock,
@ -666,7 +688,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let this = self;
let value_expr = &this.thir[value];
let elem_ty = value_expr.ty;
if let Some(Category::Constant) = Category::of(&value_expr.kind) {
if this.check_constness(&value_expr.kind) {
// Repeating a const does nothing
} else {
// For a non-const, we may need to generate an appropriate `Drop`

View File

@ -1,4 +1,5 @@
use rustc_index::{Idx, IndexVec};
use rustc_data_structures::fx::FxHashMap;
use rustc_index::Idx;
use rustc_middle::mir::*;
use rustc_middle::ty::Ty;
use rustc_span::Span;
@ -9,7 +10,7 @@ use tracing::debug;
/// and replacement of terminators, and then apply the queued changes all at
/// once with `apply`. This is useful for MIR transformation passes.
pub(crate) struct MirPatch<'tcx> {
term_patch_map: IndexVec<BasicBlock, Option<TerminatorKind<'tcx>>>,
term_patch_map: FxHashMap<BasicBlock, TerminatorKind<'tcx>>,
new_blocks: Vec<BasicBlockData<'tcx>>,
new_statements: Vec<(Location, StatementKind<'tcx>)>,
new_locals: Vec<LocalDecl<'tcx>>,
@ -22,17 +23,21 @@ pub(crate) struct MirPatch<'tcx> {
terminate_block: Option<(BasicBlock, UnwindTerminateReason)>,
body_span: Span,
next_local: usize,
/// The number of blocks at the start of the transformation. New blocks
/// get appended at the end.
next_block: usize,
}
impl<'tcx> MirPatch<'tcx> {
/// Creates a new, empty patch.
pub(crate) fn new(body: &Body<'tcx>) -> Self {
let mut result = MirPatch {
term_patch_map: IndexVec::from_elem(None, &body.basic_blocks),
term_patch_map: Default::default(),
new_blocks: vec![],
new_statements: vec![],
new_locals: vec![],
next_local: body.local_decls.len(),
next_block: body.basic_blocks.len(),
resume_block: None,
unreachable_cleanup_block: None,
unreachable_no_cleanup_block: None,
@ -141,7 +146,7 @@ impl<'tcx> MirPatch<'tcx> {
/// Has a replacement of this block's terminator been queued in this patch?
pub(crate) fn is_term_patched(&self, bb: BasicBlock) -> bool {
self.term_patch_map[bb].is_some()
self.term_patch_map.contains_key(&bb)
}
/// Universal getter for block data, either it is in 'old' blocks or in patched ones
@ -194,18 +199,17 @@ impl<'tcx> MirPatch<'tcx> {
/// Queues the addition of a new basic block.
pub(crate) fn new_block(&mut self, data: BasicBlockData<'tcx>) -> BasicBlock {
let block = self.term_patch_map.next_index();
let block = BasicBlock::from_usize(self.next_block + self.new_blocks.len());
debug!("MirPatch: new_block: {:?}: {:?}", block, data);
self.new_blocks.push(data);
self.term_patch_map.push(None);
block
}
/// Queues the replacement of a block's terminator.
pub(crate) fn patch_terminator(&mut self, block: BasicBlock, new: TerminatorKind<'tcx>) {
assert!(self.term_patch_map[block].is_none());
assert!(!self.term_patch_map.contains_key(&block));
debug!("MirPatch: patch_terminator({:?}, {:?})", block, new);
self.term_patch_map[block] = Some(new);
self.term_patch_map.insert(block, new);
}
/// Queues the insertion of a statement at a given location. The statement
@ -244,6 +248,7 @@ impl<'tcx> MirPatch<'tcx> {
self.new_blocks.len(),
body.basic_blocks.len()
);
debug_assert_eq!(self.next_block, body.basic_blocks.len());
let bbs = if self.term_patch_map.is_empty() && self.new_blocks.is_empty() {
body.basic_blocks.as_mut_preserves_cfg()
} else {
@ -251,11 +256,12 @@ impl<'tcx> MirPatch<'tcx> {
};
bbs.extend(self.new_blocks);
body.local_decls.extend(self.new_locals);
for (src, patch) in self.term_patch_map.into_iter_enumerated() {
if let Some(patch) = patch {
debug!("MirPatch: patching block {:?}", src);
bbs[src].terminator_mut().kind = patch;
}
// The order in which we patch terminators does not change the result.
#[allow(rustc::potential_query_instability)]
for (src, patch) in self.term_patch_map {
debug!("MirPatch: patching block {:?}", src);
bbs[src].terminator_mut().kind = patch;
}
let mut new_statements = self.new_statements;
@ -273,8 +279,8 @@ impl<'tcx> MirPatch<'tcx> {
}
debug!("MirPatch: adding statement {:?} at loc {:?}+{}", stmt, loc, delta);
loc.statement_index += delta;
let source_info = Self::source_info_for_index(&body[loc.block], loc);
body[loc.block]
let source_info = Self::source_info_for_index(&bbs[loc.block], loc);
bbs[loc.block]
.statements
.insert(loc.statement_index, Statement::new(source_info, stmt));
delta += 1;

View File

@ -256,6 +256,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
| AttributeKind::MacroEscape( .. )
| AttributeKind::RustcLayoutScalarValidRangeStart(..)
| AttributeKind::RustcLayoutScalarValidRangeEnd(..)
| AttributeKind::RustcSimdMonomorphizeLaneLimit(..)
| AttributeKind::ExportStable
| AttributeKind::FfiConst(..)
| AttributeKind::UnstableFeatureBound(..)

View File

@ -1943,6 +1943,7 @@ symbols! {
rustc_regions,
rustc_reservation_impl,
rustc_serialize,
rustc_simd_monomorphize_lane_limit,
rustc_skip_during_method_dispatch,
rustc_specialization_trait,
rustc_std_internal_symbol,

View File

@ -34,7 +34,7 @@ pub(crate) fn target() -> Target {
description: Some("ARMv7-A Cortex-A9 VEX V5 Brain".into()),
tier: Some(3),
host_tools: Some(false),
std: Some(false),
std: Some(true),
},
pointer_width: 32,
data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),

View File

@ -279,6 +279,7 @@ pub(crate) mod rustc {
LayoutError::Unknown(..)
| LayoutError::ReferencesError(..)
| LayoutError::TooGeneric(..)
| LayoutError::InvalidSimd { .. }
| LayoutError::NormalizationFailure(..) => Self::UnknownLayout,
LayoutError::SizeOverflow(..) => Self::SizeOverflow,
LayoutError::Cycle(err) => Self::TypeError(*err),

View File

@ -78,19 +78,6 @@ pub(crate) struct UnexpectedFnPtrAssociatedItem {
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(ty_utils_zero_length_simd_type)]
pub(crate) struct ZeroLengthSimdType<'tcx> {
pub ty: Ty<'tcx>,
}
#[derive(Diagnostic)]
#[diag(ty_utils_oversized_simd_type)]
pub(crate) struct OversizedSimdType<'tcx> {
pub ty: Ty<'tcx>,
pub max_lanes: u64,
}
#[derive(Diagnostic)]
#[diag(ty_utils_non_primitive_simd_type)]
pub(crate) struct NonPrimitiveSimdType<'tcx> {

View File

@ -7,12 +7,14 @@ use rustc_abi::{
VariantIdx, Variants, WrappingRange,
};
use rustc_hashes::Hash64;
use rustc_hir::attrs::AttributeKind;
use rustc_hir::find_attr;
use rustc_index::IndexVec;
use rustc_middle::bug;
use rustc_middle::query::Providers;
use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::layout::{
FloatExt, HasTyCtxt, IntegerExt, LayoutCx, LayoutError, LayoutOf, TyAndLayout,
FloatExt, HasTyCtxt, IntegerExt, LayoutCx, LayoutError, LayoutOf, SimdLayoutError, TyAndLayout,
};
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{
@ -23,7 +25,7 @@ use rustc_span::{Symbol, sym};
use tracing::{debug, instrument};
use {rustc_abi as abi, rustc_hir as hir};
use crate::errors::{NonPrimitiveSimdType, OversizedSimdType, ZeroLengthSimdType};
use crate::errors::NonPrimitiveSimdType;
mod invariant;
@ -121,11 +123,11 @@ fn map_error<'tcx>(
}
LayoutCalculatorError::ZeroLengthSimdType => {
// Can't be caught in typeck if the array length is generic.
cx.tcx().dcx().emit_fatal(ZeroLengthSimdType { ty })
LayoutError::InvalidSimd { ty, kind: SimdLayoutError::ZeroLength }
}
LayoutCalculatorError::OversizedSimdType { max_lanes } => {
// Can't be caught in typeck if the array length is generic.
cx.tcx().dcx().emit_fatal(OversizedSimdType { ty, max_lanes })
LayoutError::InvalidSimd { ty, kind: SimdLayoutError::TooManyLanes(max_lanes) }
}
LayoutCalculatorError::NonPrimitiveSimdType(field) => {
// This error isn't caught in typeck, e.g., if
@ -563,6 +565,22 @@ fn layout_of_uncached<'tcx>(
let e_ly = cx.layout_of(e_ty)?;
// Check for the rustc_simd_monomorphize_lane_limit attribute and check the lane limit
if let Some(limit) = find_attr!(
tcx.get_all_attrs(def.did()),
AttributeKind::RustcSimdMonomorphizeLaneLimit(limit) => limit
) {
if !limit.value_within_limit(e_len as usize) {
return Err(map_error(
&cx,
ty,
rustc_abi::LayoutCalculatorError::OversizedSimdType {
max_lanes: limit.0 as u64,
},
));
}
}
map_layout(cx.calc.simd_type(e_ly, e_len, def.repr().packed()))?
}

View File

@ -326,6 +326,7 @@ dependencies = [
"rustc-demangle",
"std_detect",
"unwind",
"vex-sdk",
"wasi 0.11.1+wasi-snapshot-preview1",
"wasi 0.14.4+wasi-0.2.4",
"windows-targets 0.0.0",
@ -379,6 +380,15 @@ dependencies = [
"rustc-std-workspace-core",
]
[[package]]
name = "vex-sdk"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89f74fce61d7a7ba1589da9634c6305a72befb7cc9150c1f872d87d8060f32b9"
dependencies = [
"rustc-std-workspace-core",
]
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"

View File

@ -177,6 +177,8 @@ impl<T, A: Allocator> RawVec<T, A> {
/// the returned `RawVec`.
#[inline]
pub(crate) const fn new_in(alloc: A) -> Self {
// Check assumption made in `current_memory`
const { assert!(T::LAYOUT.size() % T::LAYOUT.align() == 0) };
Self { inner: RawVecInner::new_in(alloc, Alignment::of::<T>()), _marker: PhantomData }
}
@ -328,7 +330,8 @@ impl<T, A: Allocator> RawVec<T, A> {
#[inline]
#[track_caller]
pub(crate) fn reserve(&mut self, len: usize, additional: usize) {
self.inner.reserve(len, additional, T::LAYOUT)
// SAFETY: All calls on self.inner pass T::LAYOUT as the elem_layout
unsafe { self.inner.reserve(len, additional, T::LAYOUT) }
}
/// A specialized version of `self.reserve(len, 1)` which requires the
@ -337,7 +340,8 @@ impl<T, A: Allocator> RawVec<T, A> {
#[inline(never)]
#[track_caller]
pub(crate) fn grow_one(&mut self) {
self.inner.grow_one(T::LAYOUT)
// SAFETY: All calls on self.inner pass T::LAYOUT as the elem_layout
unsafe { self.inner.grow_one(T::LAYOUT) }
}
/// The same as `reserve`, but returns on errors instead of panicking or aborting.
@ -346,7 +350,8 @@ impl<T, A: Allocator> RawVec<T, A> {
len: usize,
additional: usize,
) -> Result<(), TryReserveError> {
self.inner.try_reserve(len, additional, T::LAYOUT)
// SAFETY: All calls on self.inner pass T::LAYOUT as the elem_layout
unsafe { self.inner.try_reserve(len, additional, T::LAYOUT) }
}
/// Ensures that the buffer contains at least enough space to hold `len +
@ -369,7 +374,8 @@ impl<T, A: Allocator> RawVec<T, A> {
#[cfg(not(no_global_oom_handling))]
#[track_caller]
pub(crate) fn reserve_exact(&mut self, len: usize, additional: usize) {
self.inner.reserve_exact(len, additional, T::LAYOUT)
// SAFETY: All calls on self.inner pass T::LAYOUT as the elem_layout
unsafe { self.inner.reserve_exact(len, additional, T::LAYOUT) }
}
/// The same as `reserve_exact`, but returns on errors instead of panicking or aborting.
@ -378,7 +384,8 @@ impl<T, A: Allocator> RawVec<T, A> {
len: usize,
additional: usize,
) -> Result<(), TryReserveError> {
self.inner.try_reserve_exact(len, additional, T::LAYOUT)
// SAFETY: All calls on self.inner pass T::LAYOUT as the elem_layout
unsafe { self.inner.try_reserve_exact(len, additional, T::LAYOUT) }
}
/// Shrinks the buffer down to the specified capacity. If the given amount
@ -395,7 +402,8 @@ impl<T, A: Allocator> RawVec<T, A> {
#[track_caller]
#[inline]
pub(crate) fn shrink_to_fit(&mut self, cap: usize) {
self.inner.shrink_to_fit(cap, T::LAYOUT)
// SAFETY: All calls on self.inner pass T::LAYOUT as the elem_layout
unsafe { self.inner.shrink_to_fit(cap, T::LAYOUT) }
}
}
@ -518,8 +526,12 @@ impl<A: Allocator> RawVecInner<A> {
&self.alloc
}
/// # Safety
/// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to
/// initially construct `self`
/// - `elem_layout`'s size must be a multiple of its alignment
#[inline]
fn current_memory(&self, elem_layout: Layout) -> Option<(NonNull<u8>, Layout)> {
unsafe fn current_memory(&self, elem_layout: Layout) -> Option<(NonNull<u8>, Layout)> {
if elem_layout.size() == 0 || self.cap.as_inner() == 0 {
None
} else {
@ -535,48 +547,67 @@ impl<A: Allocator> RawVecInner<A> {
}
}
/// # Safety
/// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to
/// initially construct `self`
/// - `elem_layout`'s size must be a multiple of its alignment
#[cfg(not(no_global_oom_handling))]
#[inline]
#[track_caller]
fn reserve(&mut self, len: usize, additional: usize, elem_layout: Layout) {
unsafe fn reserve(&mut self, len: usize, additional: usize, elem_layout: Layout) {
// Callers expect this function to be very cheap when there is already sufficient capacity.
// Therefore, we move all the resizing and error-handling logic from grow_amortized and
// handle_reserve behind a call, while making sure that this function is likely to be
// inlined as just a comparison and a call if the comparison fails.
#[cold]
fn do_reserve_and_handle<A: Allocator>(
unsafe fn do_reserve_and_handle<A: Allocator>(
slf: &mut RawVecInner<A>,
len: usize,
additional: usize,
elem_layout: Layout,
) {
if let Err(err) = slf.grow_amortized(len, additional, elem_layout) {
// SAFETY: Precondition passed to caller
if let Err(err) = unsafe { slf.grow_amortized(len, additional, elem_layout) } {
handle_error(err);
}
}
if self.needs_to_grow(len, additional, elem_layout) {
do_reserve_and_handle(self, len, additional, elem_layout);
unsafe {
do_reserve_and_handle(self, len, additional, elem_layout);
}
}
}
/// # Safety
/// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to
/// initially construct `self`
/// - `elem_layout`'s size must be a multiple of its alignment
#[cfg(not(no_global_oom_handling))]
#[inline]
#[track_caller]
fn grow_one(&mut self, elem_layout: Layout) {
if let Err(err) = self.grow_amortized(self.cap.as_inner(), 1, elem_layout) {
unsafe fn grow_one(&mut self, elem_layout: Layout) {
// SAFETY: Precondition passed to caller
if let Err(err) = unsafe { self.grow_amortized(self.cap.as_inner(), 1, elem_layout) } {
handle_error(err);
}
}
fn try_reserve(
/// # Safety
/// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to
/// initially construct `self`
/// - `elem_layout`'s size must be a multiple of its alignment
unsafe fn try_reserve(
&mut self,
len: usize,
additional: usize,
elem_layout: Layout,
) -> Result<(), TryReserveError> {
if self.needs_to_grow(len, additional, elem_layout) {
self.grow_amortized(len, additional, elem_layout)?;
// SAFETY: Precondition passed to caller
unsafe {
self.grow_amortized(len, additional, elem_layout)?;
}
}
unsafe {
// Inform the optimizer that the reservation has succeeded or wasn't needed
@ -585,22 +616,34 @@ impl<A: Allocator> RawVecInner<A> {
Ok(())
}
/// # Safety
/// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to
/// initially construct `self`
/// - `elem_layout`'s size must be a multiple of its alignment
#[cfg(not(no_global_oom_handling))]
#[track_caller]
fn reserve_exact(&mut self, len: usize, additional: usize, elem_layout: Layout) {
if let Err(err) = self.try_reserve_exact(len, additional, elem_layout) {
unsafe fn reserve_exact(&mut self, len: usize, additional: usize, elem_layout: Layout) {
// SAFETY: Precondition passed to caller
if let Err(err) = unsafe { self.try_reserve_exact(len, additional, elem_layout) } {
handle_error(err);
}
}
fn try_reserve_exact(
/// # Safety
/// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to
/// initially construct `self`
/// - `elem_layout`'s size must be a multiple of its alignment
unsafe fn try_reserve_exact(
&mut self,
len: usize,
additional: usize,
elem_layout: Layout,
) -> Result<(), TryReserveError> {
if self.needs_to_grow(len, additional, elem_layout) {
self.grow_exact(len, additional, elem_layout)?;
// SAFETY: Precondition passed to caller
unsafe {
self.grow_exact(len, additional, elem_layout)?;
}
}
unsafe {
// Inform the optimizer that the reservation has succeeded or wasn't needed
@ -609,11 +652,16 @@ impl<A: Allocator> RawVecInner<A> {
Ok(())
}
/// # Safety
/// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to
/// initially construct `self`
/// - `elem_layout`'s size must be a multiple of its alignment
/// - `cap` must be less than or equal to `self.capacity(elem_layout.size())`
#[cfg(not(no_global_oom_handling))]
#[inline]
#[track_caller]
fn shrink_to_fit(&mut self, cap: usize, elem_layout: Layout) {
if let Err(err) = self.shrink(cap, elem_layout) {
unsafe fn shrink_to_fit(&mut self, cap: usize, elem_layout: Layout) {
if let Err(err) = unsafe { self.shrink(cap, elem_layout) } {
handle_error(err);
}
}
@ -632,7 +680,13 @@ impl<A: Allocator> RawVecInner<A> {
self.cap = unsafe { Cap::new_unchecked(cap) };
}
fn grow_amortized(
/// # Safety
/// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to
/// initially construct `self`
/// - `elem_layout`'s size must be a multiple of its alignment
/// - The sum of `len` and `additional` must be greater than or equal to
/// `self.capacity(elem_layout.size())`
unsafe fn grow_amortized(
&mut self,
len: usize,
additional: usize,
@ -657,14 +711,25 @@ impl<A: Allocator> RawVecInner<A> {
let new_layout = layout_array(cap, elem_layout)?;
let ptr = finish_grow(new_layout, self.current_memory(elem_layout), &mut self.alloc)?;
// SAFETY: layout_array would have resulted in a capacity overflow if we tried to allocate more than `isize::MAX` items
// SAFETY:
// - For the `current_memory` call: Precondition passed to caller
// - For the `finish_grow` call: Precondition passed to caller
// + `current_memory` does the right thing
let ptr =
unsafe { finish_grow(new_layout, self.current_memory(elem_layout), &mut self.alloc)? };
// SAFETY: layout_array would have resulted in a capacity overflow if we tried to allocate more than `isize::MAX` items
unsafe { self.set_ptr_and_cap(ptr, cap) };
Ok(())
}
fn grow_exact(
/// # Safety
/// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to
/// initially construct `self`
/// - `elem_layout`'s size must be a multiple of its alignment
/// - The sum of `len` and `additional` must be greater than or equal to
/// `self.capacity(elem_layout.size())`
unsafe fn grow_exact(
&mut self,
len: usize,
additional: usize,
@ -679,7 +744,12 @@ impl<A: Allocator> RawVecInner<A> {
let cap = len.checked_add(additional).ok_or(CapacityOverflow)?;
let new_layout = layout_array(cap, elem_layout)?;
let ptr = finish_grow(new_layout, self.current_memory(elem_layout), &mut self.alloc)?;
// SAFETY:
// - For the `current_memory` call: Precondition passed to caller
// - For the `finish_grow` call: Precondition passed to caller
// + `current_memory` does the right thing
let ptr =
unsafe { finish_grow(new_layout, self.current_memory(elem_layout), &mut self.alloc)? };
// SAFETY: layout_array would have resulted in a capacity overflow if we tried to allocate more than `isize::MAX` items
unsafe {
self.set_ptr_and_cap(ptr, cap);
@ -687,9 +757,14 @@ impl<A: Allocator> RawVecInner<A> {
Ok(())
}
/// # Safety
/// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to
/// initially construct `self`
/// - `elem_layout`'s size must be a multiple of its alignment
/// - `cap` must be less than or equal to `self.capacity(elem_layout.size())`
#[cfg(not(no_global_oom_handling))]
#[inline]
fn shrink(&mut self, cap: usize, elem_layout: Layout) -> Result<(), TryReserveError> {
unsafe fn shrink(&mut self, cap: usize, elem_layout: Layout) -> Result<(), TryReserveError> {
assert!(cap <= self.capacity(elem_layout.size()), "Tried to shrink to a larger capacity");
// SAFETY: Just checked this isn't trying to grow
unsafe { self.shrink_unchecked(cap, elem_layout) }
@ -711,8 +786,12 @@ impl<A: Allocator> RawVecInner<A> {
cap: usize,
elem_layout: Layout,
) -> Result<(), TryReserveError> {
let (ptr, layout) =
if let Some(mem) = self.current_memory(elem_layout) { mem } else { return Ok(()) };
// SAFETY: Precondition passed to caller
let (ptr, layout) = if let Some(mem) = unsafe { self.current_memory(elem_layout) } {
mem
} else {
return Ok(());
};
// If shrinking to 0, deallocate the buffer. We don't reach this point
// for the T::IS_ZST case since current_memory() will have returned
@ -748,7 +827,8 @@ impl<A: Allocator> RawVecInner<A> {
/// Ideally this function would take `self` by move, but it cannot because it exists to be
/// called from a `Drop` impl.
unsafe fn deallocate(&mut self, elem_layout: Layout) {
if let Some((ptr, layout)) = self.current_memory(elem_layout) {
// SAFETY: Precondition passed to caller
if let Some((ptr, layout)) = unsafe { self.current_memory(elem_layout) } {
unsafe {
self.alloc.deallocate(ptr, layout);
}
@ -756,10 +836,17 @@ impl<A: Allocator> RawVecInner<A> {
}
}
/// # Safety
/// If `current_memory` matches `Some((ptr, old_layout))`:
/// - `ptr` must denote a block of memory *currently allocated* via `alloc`
/// - `old_layout` must *fit* that block of memory
/// - `new_layout` must have the same alignment as `old_layout`
/// - `new_layout.size()` must be greater than or equal to `old_layout.size()`
/// If `current_memory` is `None`, this function is safe.
// not marked inline(never) since we want optimizers to be able to observe the specifics of this
// function, see tests/codegen-llvm/vec-reserve-extend.rs.
#[cold]
fn finish_grow<A>(
unsafe fn finish_grow<A>(
new_layout: Layout,
current_memory: Option<(NonNull<u8>, Layout)>,
alloc: &mut A,

View File

@ -85,7 +85,7 @@ struct ZST;
fn zst_sanity<T>(v: &RawVec<T>) {
assert_eq!(v.capacity(), usize::MAX);
assert_eq!(v.ptr(), core::ptr::Unique::<T>::dangling().as_ptr());
assert_eq!(v.inner.current_memory(T::LAYOUT), None);
assert_eq!(unsafe { v.inner.current_memory(T::LAYOUT) }, None);
}
#[test]
@ -126,12 +126,12 @@ fn zst() {
assert_eq!(v.try_reserve_exact(101, usize::MAX - 100), cap_err);
zst_sanity(&v);
assert_eq!(v.inner.grow_amortized(100, usize::MAX - 100, ZST::LAYOUT), cap_err);
assert_eq!(v.inner.grow_amortized(101, usize::MAX - 100, ZST::LAYOUT), cap_err);
assert_eq!(unsafe { v.inner.grow_amortized(100, usize::MAX - 100, ZST::LAYOUT) }, cap_err);
assert_eq!(unsafe { v.inner.grow_amortized(101, usize::MAX - 100, ZST::LAYOUT) }, cap_err);
zst_sanity(&v);
assert_eq!(v.inner.grow_exact(100, usize::MAX - 100, ZST::LAYOUT), cap_err);
assert_eq!(v.inner.grow_exact(101, usize::MAX - 100, ZST::LAYOUT), cap_err);
assert_eq!(unsafe { v.inner.grow_exact(100, usize::MAX - 100, ZST::LAYOUT) }, cap_err);
assert_eq!(unsafe { v.inner.grow_exact(101, usize::MAX - 100, ZST::LAYOUT) }, cap_err);
zst_sanity(&v);
}

View File

@ -1312,28 +1312,28 @@ pub fn log2f128(x: f128) -> f128;
/// [`f16::mul_add`](../../std/primitive.f16.html#method.mul_add)
#[rustc_intrinsic]
#[rustc_nounwind]
pub fn fmaf16(a: f16, b: f16, c: f16) -> f16;
pub const fn fmaf16(a: f16, b: f16, c: f16) -> f16;
/// Returns `a * b + c` for `f32` values.
///
/// The stabilized version of this intrinsic is
/// [`f32::mul_add`](../../std/primitive.f32.html#method.mul_add)
#[rustc_intrinsic]
#[rustc_nounwind]
pub fn fmaf32(a: f32, b: f32, c: f32) -> f32;
pub const fn fmaf32(a: f32, b: f32, c: f32) -> f32;
/// Returns `a * b + c` for `f64` values.
///
/// The stabilized version of this intrinsic is
/// [`f64::mul_add`](../../std/primitive.f64.html#method.mul_add)
#[rustc_intrinsic]
#[rustc_nounwind]
pub fn fmaf64(a: f64, b: f64, c: f64) -> f64;
pub const fn fmaf64(a: f64, b: f64, c: f64) -> f64;
/// Returns `a * b + c` for `f128` values.
///
/// The stabilized version of this intrinsic is
/// [`f128::mul_add`](../../std/primitive.f128.html#method.mul_add)
#[rustc_intrinsic]
#[rustc_nounwind]
pub fn fmaf128(a: f128, b: f128, c: f128) -> f128;
pub const fn fmaf128(a: f128, b: f128, c: f128) -> f128;
/// Returns `a * b + c` for `f16` values, non-deterministically executing
/// either a fused multiply-add or two operations with rounding of the
@ -1347,7 +1347,7 @@ pub fn fmaf128(a: f128, b: f128, c: f128) -> f128;
/// example.
#[rustc_intrinsic]
#[rustc_nounwind]
pub fn fmuladdf16(a: f16, b: f16, c: f16) -> f16;
pub const fn fmuladdf16(a: f16, b: f16, c: f16) -> f16;
/// Returns `a * b + c` for `f32` values, non-deterministically executing
/// either a fused multiply-add or two operations with rounding of the
/// intermediate result.
@ -1360,7 +1360,7 @@ pub fn fmuladdf16(a: f16, b: f16, c: f16) -> f16;
/// example.
#[rustc_intrinsic]
#[rustc_nounwind]
pub fn fmuladdf32(a: f32, b: f32, c: f32) -> f32;
pub const fn fmuladdf32(a: f32, b: f32, c: f32) -> f32;
/// Returns `a * b + c` for `f64` values, non-deterministically executing
/// either a fused multiply-add or two operations with rounding of the
/// intermediate result.
@ -1373,7 +1373,7 @@ pub fn fmuladdf32(a: f32, b: f32, c: f32) -> f32;
/// example.
#[rustc_intrinsic]
#[rustc_nounwind]
pub fn fmuladdf64(a: f64, b: f64, c: f64) -> f64;
pub const fn fmuladdf64(a: f64, b: f64, c: f64) -> f64;
/// Returns `a * b + c` for `f128` values, non-deterministically executing
/// either a fused multiply-add or two operations with rounding of the
/// intermediate result.
@ -1386,7 +1386,7 @@ pub fn fmuladdf64(a: f64, b: f64, c: f64) -> f64;
/// example.
#[rustc_intrinsic]
#[rustc_nounwind]
pub fn fmuladdf128(a: f128, b: f128, c: f128) -> f128;
pub const fn fmuladdf128(a: f128, b: f128, c: f128) -> f128;
/// Returns the largest integer less than or equal to an `f16`.
///

View File

@ -1659,7 +1659,8 @@ impl f128 {
#[doc(alias = "fmaf128", alias = "fusedMultiplyAdd")]
#[unstable(feature = "f128", issue = "116909")]
#[must_use = "method returns a new number and does not mutate the original value"]
pub fn mul_add(self, a: f128, b: f128) -> f128 {
#[rustc_const_unstable(feature = "const_mul_add", issue = "146724")]
pub const fn mul_add(self, a: f128, b: f128) -> f128 {
intrinsics::fmaf128(self, a, b)
}

View File

@ -1634,7 +1634,8 @@ impl f16 {
#[unstable(feature = "f16", issue = "116909")]
#[doc(alias = "fmaf16", alias = "fusedMultiplyAdd")]
#[must_use = "method returns a new number and does not mutate the original value"]
pub fn mul_add(self, a: f16, b: f16) -> f16 {
#[rustc_const_unstable(feature = "const_mul_add", issue = "146724")]
pub const fn mul_add(self, a: f16, b: f16) -> f16 {
intrinsics::fmaf16(self, a, b)
}

View File

@ -1798,7 +1798,8 @@ pub mod math {
#[doc(alias = "fmaf", alias = "fusedMultiplyAdd")]
#[must_use = "method returns a new number and does not mutate the original value"]
#[unstable(feature = "core_float_math", issue = "137578")]
pub fn mul_add(x: f32, y: f32, z: f32) -> f32 {
#[rustc_const_unstable(feature = "const_mul_add", issue = "146724")]
pub const fn mul_add(x: f32, y: f32, z: f32) -> f32 {
intrinsics::fmaf32(x, y, z)
}

View File

@ -1796,7 +1796,8 @@ pub mod math {
#[doc(alias = "fma", alias = "fusedMultiplyAdd")]
#[unstable(feature = "core_float_math", issue = "137578")]
#[must_use = "method returns a new number and does not mutate the original value"]
pub fn mul_add(x: f64, a: f64, b: f64) -> f64 {
#[rustc_const_unstable(feature = "const_mul_add", issue = "146724")]
pub const fn mul_add(x: f64, a: f64, b: f64) -> f64 {
intrinsics::fmaf64(x, a, b)
}

View File

@ -20,24 +20,6 @@ const TOL_PRECISE: f128 = 1e-28;
// FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support
// the intrinsics.
#[test]
#[cfg(not(miri))]
#[cfg(target_has_reliable_f128_math)]
fn test_mul_add() {
let nan: f128 = f128::NAN;
let inf: f128 = f128::INFINITY;
let neg_inf: f128 = f128::NEG_INFINITY;
assert_biteq!(12.3f128.mul_add(4.5, 6.7), 62.0500000000000000000000000000000037);
assert_biteq!((-12.3f128).mul_add(-4.5, -6.7), 48.6500000000000000000000000000000049);
assert_biteq!(0.0f128.mul_add(8.9, 1.2), 1.2);
assert_biteq!(3.4f128.mul_add(-0.0, 5.6), 5.6);
assert!(nan.mul_add(7.8, 9.0).is_nan());
assert_biteq!(inf.mul_add(7.8, 9.0), inf);
assert_biteq!(neg_inf.mul_add(7.8, 9.0), neg_inf);
assert_biteq!(8.9f128.mul_add(inf, 3.2), inf);
assert_biteq!((-3.2f128).mul_add(2.4, neg_inf), neg_inf);
}
#[test]
#[cfg(any(miri, target_has_reliable_f128_math))]
fn test_max_recip() {

View File

@ -22,24 +22,6 @@ const TOL_P4: f16 = 10.0;
// FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support
// the intrinsics.
#[test]
#[cfg(not(miri))]
#[cfg(target_has_reliable_f16_math)]
fn test_mul_add() {
let nan: f16 = f16::NAN;
let inf: f16 = f16::INFINITY;
let neg_inf: f16 = f16::NEG_INFINITY;
assert_biteq!(12.3f16.mul_add(4.5, 6.7), 62.031);
assert_biteq!((-12.3f16).mul_add(-4.5, -6.7), 48.625);
assert_biteq!(0.0f16.mul_add(8.9, 1.2), 1.2);
assert_biteq!(3.4f16.mul_add(-0.0, 5.6), 5.6);
assert!(nan.mul_add(7.8, 9.0).is_nan());
assert_biteq!(inf.mul_add(7.8, 9.0), inf);
assert_biteq!(neg_inf.mul_add(7.8, 9.0), neg_inf);
assert_biteq!(8.9f16.mul_add(inf, 3.2), inf);
assert_biteq!((-3.2f16).mul_add(2.4, neg_inf), neg_inf);
}
#[test]
#[cfg(any(miri, target_has_reliable_f16_math))]
fn test_max_recip() {

View File

@ -1,21 +0,0 @@
use core::f32;
use super::assert_biteq;
// FIXME(#140515): mingw has an incorrect fma https://sourceforge.net/p/mingw-w64/bugs/848/
#[cfg_attr(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")), ignore)]
#[test]
fn test_mul_add() {
let nan: f32 = f32::NAN;
let inf: f32 = f32::INFINITY;
let neg_inf: f32 = f32::NEG_INFINITY;
assert_biteq!(f32::math::mul_add(12.3f32, 4.5, 6.7), 62.05);
assert_biteq!(f32::math::mul_add(-12.3f32, -4.5, -6.7), 48.65);
assert_biteq!(f32::math::mul_add(0.0f32, 8.9, 1.2), 1.2);
assert_biteq!(f32::math::mul_add(3.4f32, -0.0, 5.6), 5.6);
assert!(f32::math::mul_add(nan, 7.8, 9.0).is_nan());
assert_biteq!(f32::math::mul_add(inf, 7.8, 9.0), inf);
assert_biteq!(f32::math::mul_add(neg_inf, 7.8, 9.0), neg_inf);
assert_biteq!(f32::math::mul_add(8.9f32, inf, 3.2), inf);
assert_biteq!(f32::math::mul_add(-3.2f32, 2.4, neg_inf), neg_inf);
}

View File

@ -1,21 +0,0 @@
use core::f64;
use super::assert_biteq;
// FIXME(#140515): mingw has an incorrect fma https://sourceforge.net/p/mingw-w64/bugs/848/
#[cfg_attr(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")), ignore)]
#[test]
fn test_mul_add() {
let nan: f64 = f64::NAN;
let inf: f64 = f64::INFINITY;
let neg_inf: f64 = f64::NEG_INFINITY;
assert_biteq!(12.3f64.mul_add(4.5, 6.7), 62.050000000000004);
assert_biteq!((-12.3f64).mul_add(-4.5, -6.7), 48.650000000000006);
assert_biteq!(0.0f64.mul_add(8.9, 1.2), 1.2);
assert_biteq!(3.4f64.mul_add(-0.0, 5.6), 5.6);
assert!(nan.mul_add(7.8, 9.0).is_nan());
assert_biteq!(inf.mul_add(7.8, 9.0), inf);
assert_biteq!(neg_inf.mul_add(7.8, 9.0), neg_inf);
assert_biteq!(8.9f64.mul_add(inf, 3.2), inf);
assert_biteq!((-3.2f64).mul_add(2.4, neg_inf), neg_inf);
}

View File

@ -34,6 +34,10 @@ trait TestableFloat: Sized {
const RAW_12_DOT_5: Self;
const RAW_1337: Self;
const RAW_MINUS_14_DOT_25: Self;
/// The result of 12.3.mul_add(4.5, 6.7)
const MUL_ADD_RESULT: Self;
/// The result of (-12.3).mul_add(-4.5, -6.7)
const NEG_MUL_ADD_RESULT: Self;
}
impl TestableFloat for f16 {
@ -58,6 +62,8 @@ impl TestableFloat for f16 {
const RAW_12_DOT_5: Self = Self::from_bits(0x4a40);
const RAW_1337: Self = Self::from_bits(0x6539);
const RAW_MINUS_14_DOT_25: Self = Self::from_bits(0xcb20);
const MUL_ADD_RESULT: Self = 62.031;
const NEG_MUL_ADD_RESULT: Self = 48.625;
}
impl TestableFloat for f32 {
@ -84,6 +90,8 @@ impl TestableFloat for f32 {
const RAW_12_DOT_5: Self = Self::from_bits(0x41480000);
const RAW_1337: Self = Self::from_bits(0x44a72000);
const RAW_MINUS_14_DOT_25: Self = Self::from_bits(0xc1640000);
const MUL_ADD_RESULT: Self = 62.05;
const NEG_MUL_ADD_RESULT: Self = 48.65;
}
impl TestableFloat for f64 {
@ -106,6 +114,8 @@ impl TestableFloat for f64 {
const RAW_12_DOT_5: Self = Self::from_bits(0x4029000000000000);
const RAW_1337: Self = Self::from_bits(0x4094e40000000000);
const RAW_MINUS_14_DOT_25: Self = Self::from_bits(0xc02c800000000000);
const MUL_ADD_RESULT: Self = 62.050000000000004;
const NEG_MUL_ADD_RESULT: Self = 48.650000000000006;
}
impl TestableFloat for f128 {
@ -128,6 +138,8 @@ impl TestableFloat for f128 {
const RAW_12_DOT_5: Self = Self::from_bits(0x40029000000000000000000000000000);
const RAW_1337: Self = Self::from_bits(0x40094e40000000000000000000000000);
const RAW_MINUS_14_DOT_25: Self = Self::from_bits(0xc002c800000000000000000000000000);
const MUL_ADD_RESULT: Self = 62.0500000000000000000000000000000037;
const NEG_MUL_ADD_RESULT: Self = 48.6500000000000000000000000000000049;
}
/// Determine the tolerance for values of the argument type.
@ -359,8 +371,6 @@ macro_rules! float_test {
mod f128;
mod f16;
mod f32;
mod f64;
float_test! {
name: num,
@ -1180,15 +1190,12 @@ float_test! {
}
}
// FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support
// the intrinsics.
float_test! {
name: sqrt_domain,
attrs: {
const: #[cfg(false)],
f16: #[cfg(all(not(miri), target_has_reliable_f16_math))],
f128: #[cfg(all(not(miri), target_has_reliable_f128_math))],
f16: #[cfg(any(miri, target_has_reliable_f16_math))],
f128: #[cfg(any(miri, target_has_reliable_f128_math))],
},
test<Float> {
assert!(Float::NAN.sqrt().is_nan());
@ -1246,8 +1253,8 @@ float_test! {
float_test! {
name: total_cmp,
attrs: {
f16: #[cfg(all(not(miri), target_has_reliable_f16_math))],
f128: #[cfg(all(not(miri), target_has_reliable_f128_math))],
f16: #[cfg(any(miri, target_has_reliable_f16_math))],
f128: #[cfg(any(miri, target_has_reliable_f128_math))],
},
test<Float> {
use core::cmp::Ordering;
@ -1355,8 +1362,8 @@ float_test! {
name: total_cmp_s_nan,
attrs: {
const: #[cfg(false)],
f16: #[cfg(false)],
f128: #[cfg(all(not(miri), target_has_reliable_f128_math))],
f16: #[cfg(miri)],
f128: #[cfg(any(miri, target_has_reliable_f128_math))],
},
test<Float> {
use core::cmp::Ordering;
@ -1432,6 +1439,7 @@ float_test! {
name: powi,
attrs: {
const: #[cfg(false)],
// FIXME(f16_f128): `powi` does not work in Miri for these types
f16: #[cfg(all(not(miri), target_has_reliable_f16_math))],
f128: #[cfg(all(not(miri), target_has_reliable_f128_math))],
},
@ -1452,8 +1460,8 @@ float_test! {
float_test! {
name: to_degrees,
attrs: {
f16: #[cfg(target_has_reliable_f16)],
f128: #[cfg(target_has_reliable_f128)],
f16: #[cfg(any(miri, target_has_reliable_f16))],
f128: #[cfg(any(miri, target_has_reliable_f128))],
},
test<Float> {
let pi: Float = Float::PI;
@ -1473,8 +1481,8 @@ float_test! {
float_test! {
name: to_radians,
attrs: {
f16: #[cfg(target_has_reliable_f16)],
f128: #[cfg(target_has_reliable_f128)],
f16: #[cfg(any(miri, target_has_reliable_f16))],
f128: #[cfg(any(miri, target_has_reliable_f128))],
},
test<Float> {
let pi: Float = Float::PI;
@ -1494,8 +1502,8 @@ float_test! {
float_test! {
name: to_algebraic,
attrs: {
f16: #[cfg(target_has_reliable_f16)],
f128: #[cfg(target_has_reliable_f128)],
f16: #[cfg(any(miri, target_has_reliable_f16))],
f128: #[cfg(any(miri, target_has_reliable_f128))],
},
test<Float> {
let a: Float = 123.0;
@ -1518,8 +1526,8 @@ float_test! {
float_test! {
name: to_bits_conv,
attrs: {
f16: #[cfg(target_has_reliable_f16)],
f128: #[cfg(target_has_reliable_f128)],
f16: #[cfg(any(miri, target_has_reliable_f16))],
f128: #[cfg(any(miri, target_has_reliable_f128))],
},
test<Float> {
assert_biteq!(flt(1.0), Float::RAW_1);
@ -1541,3 +1549,28 @@ float_test! {
assert_biteq!(Float::from_bits(masked_nan2), Float::from_bits(masked_nan2));
}
}
float_test! {
name: mul_add,
attrs: {
f16: #[cfg(any(miri, target_has_reliable_f16))],
// FIXME(#140515): mingw has an incorrect fma https://sourceforge.net/p/mingw-w64/bugs/848/
f32: #[cfg_attr(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")), ignore)],
f64: #[cfg_attr(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")), ignore)],
f128: #[cfg(any(miri, target_has_reliable_f128))],
},
test<Float> {
let nan: Float = Float::NAN;
let inf: Float = Float::INFINITY;
let neg_inf: Float = Float::NEG_INFINITY;
assert_biteq!(flt(12.3).mul_add(4.5, 6.7), Float::MUL_ADD_RESULT);
assert_biteq!((flt(-12.3)).mul_add(-4.5, -6.7), Float::NEG_MUL_ADD_RESULT);
assert_biteq!(flt(0.0).mul_add(8.9, 1.2), 1.2);
assert_biteq!(flt(3.4).mul_add(-0.0, 5.6), 5.6);
assert!(nan.mul_add(7.8, 9.0).is_nan());
assert_biteq!(inf.mul_add(7.8, 9.0), inf);
assert_biteq!(neg_inf.mul_add(7.8, 9.0), neg_inf);
assert_biteq!(flt(8.9).mul_add(inf, 3.2), inf);
assert_biteq!((flt(-3.2)).mul_add(2.4, neg_inf), neg_inf);
}
}

View File

@ -20,6 +20,7 @@
#![feature(const_convert)]
#![feature(const_destruct)]
#![feature(const_eval_select)]
#![feature(const_mul_add)]
#![feature(const_ops)]
#![feature(const_option_ops)]
#![feature(const_ref_cell)]

View File

@ -62,7 +62,7 @@ path = "../windows_targets"
rand = { version = "0.9.0", default-features = false, features = ["alloc"] }
rand_xorshift = "0.4.0"
[target.'cfg(any(all(target_family = "wasm", target_os = "unknown"), target_os = "xous", all(target_vendor = "fortanix", target_env = "sgx")))'.dependencies]
[target.'cfg(any(all(target_family = "wasm", target_os = "unknown"), target_os = "xous", target_os = "vexos", all(target_vendor = "fortanix", target_env = "sgx")))'.dependencies]
dlmalloc = { version = "0.2.10", features = ['rustc-dep-of-std'] }
[target.x86_64-fortanix-unknown-sgx.dependencies]
@ -89,6 +89,11 @@ wasip2 = { version = '0.14.4', features = [
r-efi = { version = "5.2.0", features = ['rustc-dep-of-std'] }
r-efi-alloc = { version = "2.0.0", features = ['rustc-dep-of-std'] }
[target.'cfg(target_os = "vexos")'.dependencies]
vex-sdk = { version = "0.27.0", features = [
'rustc-dep-of-std',
], default-features = false }
[features]
backtrace = [
'addr2line/rustc-dep-of-std',

View File

@ -52,6 +52,7 @@ fn main() {
|| target_os == "rtems"
|| target_os == "nuttx"
|| target_os == "cygwin"
|| target_os == "vexos"
// See src/bootstrap/src/core/build_steps/synthetic_targets.rs
|| env::var("RUSTC_BOOTSTRAP_SYNTHETIC_TARGET").is_ok()

View File

@ -1098,6 +1098,7 @@ pub mod consts {
/// * `"redox"`
/// * `"solaris"`
/// * `"solid_asp3`
/// * `"vexos"`
/// * `"vita"`
/// * `"vxworks"`
/// * `"xous"`
@ -1148,6 +1149,7 @@ pub mod consts {
///
/// <details><summary>Full list of possible values</summary>
///
/// * `"bin"`
/// * `"exe"`
/// * `"efi"`
/// * `"js"`

View File

@ -332,6 +332,7 @@
#![feature(char_internals)]
#![feature(clone_to_uninit)]
#![feature(const_convert)]
#![feature(const_mul_add)]
#![feature(core_intrinsics)]
#![feature(core_io_borrowed_buf)]
#![feature(drop_guard)]

View File

@ -28,6 +28,8 @@ use crate::{io, iter, option, slice, vec};
/// [`SocketAddr`] as expected by its [`FromStr`] implementation or a string like
/// `<host_name>:<port>` pair where `<port>` is a [`u16`] value.
///
/// * <code>&[[SocketAddr]]</code>: all [`SocketAddr`] values in the slice will be used.
///
/// This trait allows constructing network objects like [`TcpStream`] or
/// [`UdpSocket`] easily with values of various types for the bind/connection
/// address. It is needed because sometimes one type is more appropriate than

View File

@ -217,7 +217,8 @@ impl f32 {
#[must_use = "method returns a new number and does not mutate the original value"]
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn mul_add(self, a: f32, b: f32) -> f32 {
#[rustc_const_unstable(feature = "const_mul_add", issue = "146724")]
pub const fn mul_add(self, a: f32, b: f32) -> f32 {
core::f32::math::mul_add(self, a, b)
}

View File

@ -217,7 +217,8 @@ impl f64 {
#[must_use = "method returns a new number and does not mutate the original value"]
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn mul_add(self, a: f64, b: f64) -> f64 {
#[rustc_const_unstable(feature = "const_mul_add", issue = "146724")]
pub const fn mul_add(self, a: f64, b: f64) -> f64 {
core::f64::math::mul_add(self, a, b)
}

View File

@ -92,6 +92,9 @@ cfg_select! {
target_os = "uefi" => {
mod uefi;
}
target_os = "vexos" => {
mod vexos;
}
target_family = "wasm" => {
mod wasm;
}

View File

@ -0,0 +1,96 @@
// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint
#![allow(static_mut_refs)]
use crate::alloc::{GlobalAlloc, Layout, System};
use crate::ptr;
use crate::sync::atomic::{AtomicBool, Ordering};
// Symbols for heap section boundaries defined in the target's linkerscript
unsafe extern "C" {
static mut __heap_start: u8;
static mut __heap_end: u8;
}
static mut DLMALLOC: dlmalloc::Dlmalloc<Vexos> = dlmalloc::Dlmalloc::new_with_allocator(Vexos);
struct Vexos;
unsafe impl dlmalloc::Allocator for Vexos {
/// Allocs system resources
fn alloc(&self, _size: usize) -> (*mut u8, usize, u32) {
static INIT: AtomicBool = AtomicBool::new(false);
if !INIT.swap(true, Ordering::Relaxed) {
// This target has no growable heap, as user memory has a fixed
// size/location and VEXos does not manage allocation for us.
unsafe {
(
(&raw mut __heap_start).cast::<u8>(),
(&raw const __heap_end).offset_from_unsigned(&raw const __heap_start),
0,
)
}
} else {
(ptr::null_mut(), 0, 0)
}
}
fn remap(&self, _ptr: *mut u8, _oldsize: usize, _newsize: usize, _can_move: bool) -> *mut u8 {
ptr::null_mut()
}
fn free_part(&self, _ptr: *mut u8, _oldsize: usize, _newsize: usize) -> bool {
false
}
fn free(&self, _ptr: *mut u8, _size: usize) -> bool {
return false;
}
fn can_release_part(&self, _flags: u32) -> bool {
false
}
fn allocates_zeros(&self) -> bool {
false
}
fn page_size(&self) -> usize {
0x1000
}
}
#[stable(feature = "alloc_system_type", since = "1.28.0")]
unsafe impl GlobalAlloc for System {
#[inline]
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
// SAFETY: DLMALLOC access is guaranteed to be safe because we are a single-threaded target, which
// guarantees unique and non-reentrant access to the allocator. As such, no allocator lock is used.
// Calling malloc() is safe because preconditions on this function match the trait method preconditions.
unsafe { DLMALLOC.malloc(layout.size(), layout.align()) }
}
#[inline]
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
// SAFETY: DLMALLOC access is guaranteed to be safe because we are a single-threaded target, which
// guarantees unique and non-reentrant access to the allocator. As such, no allocator lock is used.
// Calling calloc() is safe because preconditions on this function match the trait method preconditions.
unsafe { DLMALLOC.calloc(layout.size(), layout.align()) }
}
#[inline]
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
// SAFETY: DLMALLOC access is guaranteed to be safe because we are a single-threaded target, which
// guarantees unique and non-reentrant access to the allocator. As such, no allocator lock is used.
// Calling free() is safe because preconditions on this function match the trait method preconditions.
unsafe { DLMALLOC.free(ptr, layout.size(), layout.align()) }
}
#[inline]
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
// SAFETY: DLMALLOC access is guaranteed to be safe because we are a single-threaded target, which
// guarantees unique and non-reentrant access to the allocator. As such, no allocator lock is used.
// Calling realloc() is safe because preconditions on this function match the trait method preconditions.
unsafe { DLMALLOC.realloc(ptr, layout.size(), layout.align(), new_size) }
}
}

View File

@ -323,6 +323,17 @@ pub mod os {
pub const EXE_EXTENSION: &str = "efi";
}
#[cfg(target_os = "vexos")]
pub mod os {
pub const FAMILY: &str = "";
pub const OS: &str = "vexos";
pub const DLL_PREFIX: &str = "";
pub const DLL_SUFFIX: &str = "";
pub const DLL_EXTENSION: &str = "";
pub const EXE_SUFFIX: &str = ".bin";
pub const EXE_EXTENSION: &str = "bin";
}
#[cfg(target_os = "visionos")]
pub mod os {
pub const FAMILY: &str = "unix";

View File

@ -35,6 +35,10 @@ cfg_select! {
mod uefi;
use uefi as imp;
}
target_os = "vexos" => {
mod vexos;
use vexos as imp;
}
target_os = "wasi" => {
mod wasi;
use wasi as imp;

View File

@ -0,0 +1,615 @@
use crate::ffi::{OsString, c_char};
use crate::fmt;
use crate::fs::TryLockError;
use crate::hash::Hash;
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom};
use crate::path::{Path, PathBuf};
use crate::sys::common::small_c_string::run_path_with_cstr;
use crate::sys::time::SystemTime;
use crate::sys::{unsupported, unsupported_err};
#[expect(dead_code)]
#[path = "unsupported.rs"]
mod unsupported_fs;
pub use unsupported_fs::{
DirBuilder, FileTimes, canonicalize, link, readlink, remove_dir_all, rename, rmdir, symlink,
unlink,
};
/// VEXos file descriptor.
///
/// This stores an opaque pointer to a [FatFs file object structure] managed by VEXos
/// representing an open file on disk.
///
/// [FatFs file object structure]: https://github.com/Xilinx/embeddedsw/blob/master/lib/sw_services/xilffs/src/include/ff.h?rgh-link-date=2025-09-23T20%3A03%3A43Z#L215
///
/// # Safety
///
/// Since this platform uses a pointer to to an internal filesystem structure with a lifetime
/// associated with it (rather than a UNIX-style file descriptor table), care must be taken to
/// ensure that the pointer held by `FileDesc` is valid for as long as it exists.
#[derive(Debug)]
struct FileDesc(*mut vex_sdk::FIL);
// SAFETY: VEXos's FDs can be used on a thread other than the one they were created on.
unsafe impl Send for FileDesc {}
// SAFETY: We assume an environment without threads (i.e. no RTOS).
// (If there were threads, it is possible that a mutex would be required.)
unsafe impl Sync for FileDesc {}
pub struct File {
fd: FileDesc,
}
#[derive(Clone)]
pub enum FileAttr {
Dir,
File { size: u64 },
}
pub struct ReadDir(!);
pub struct DirEntry {
path: PathBuf,
}
#[derive(Clone, Debug)]
pub struct OpenOptions {
read: bool,
write: bool,
append: bool,
truncate: bool,
create: bool,
create_new: bool,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct FilePermissions {}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct FileType {
is_dir: bool,
}
impl FileAttr {
pub fn size(&self) -> u64 {
match self {
Self::File { size } => *size,
Self::Dir => 0,
}
}
pub fn perm(&self) -> FilePermissions {
FilePermissions {}
}
pub fn file_type(&self) -> FileType {
FileType { is_dir: matches!(self, FileAttr::Dir) }
}
pub fn modified(&self) -> io::Result<SystemTime> {
unsupported()
}
pub fn accessed(&self) -> io::Result<SystemTime> {
unsupported()
}
pub fn created(&self) -> io::Result<SystemTime> {
unsupported()
}
}
impl FilePermissions {
pub fn readonly(&self) -> bool {
false
}
pub fn set_readonly(&mut self, _readonly: bool) {
panic!("Perimissions do not exist")
}
}
impl FileType {
pub fn is_dir(&self) -> bool {
self.is_dir
}
pub fn is_file(&self) -> bool {
!self.is_dir
}
pub fn is_symlink(&self) -> bool {
// No symlinks in VEXos - entries are either files or directories.
false
}
}
impl fmt::Debug for ReadDir {
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0
}
}
impl Iterator for ReadDir {
type Item = io::Result<DirEntry>;
fn next(&mut self) -> Option<io::Result<DirEntry>> {
self.0
}
}
impl DirEntry {
pub fn path(&self) -> PathBuf {
self.path.clone()
}
pub fn file_name(&self) -> OsString {
self.path.file_name().unwrap_or_default().into()
}
pub fn metadata(&self) -> io::Result<FileAttr> {
stat(&self.path)
}
pub fn file_type(&self) -> io::Result<FileType> {
Ok(self.metadata()?.file_type())
}
}
impl OpenOptions {
pub fn new() -> OpenOptions {
OpenOptions {
read: false,
write: false,
append: false,
truncate: false,
create: false,
create_new: false,
}
}
pub fn read(&mut self, read: bool) {
self.read = read;
}
pub fn write(&mut self, write: bool) {
self.write = write;
}
pub fn append(&mut self, append: bool) {
self.append = append;
}
pub fn truncate(&mut self, truncate: bool) {
self.truncate = truncate;
}
pub fn create(&mut self, create: bool) {
self.create = create;
}
pub fn create_new(&mut self, create_new: bool) {
self.create_new = create_new;
}
}
impl File {
pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
run_path_with_cstr(path, &|path| {
// Enforce the invariants of `create_new`/`create`.
//
// Since VEXos doesn't have anything akin to POSIX's `oflags`, we need to enforce
// the requirements that `create_new` can't have an existing file and `!create`
// doesn't create a file ourselves.
if !opts.read && (opts.write || opts.append) && (opts.create_new || !opts.create) {
let status = unsafe { vex_sdk::vexFileStatus(path.as_ptr()) };
if opts.create_new && status != 0 {
return Err(io::const_error!(io::ErrorKind::AlreadyExists, "file exists",));
} else if !opts.create && status == 0 {
return Err(io::const_error!(
io::ErrorKind::NotFound,
"no such file or directory",
));
}
}
let file = match opts {
// read + write - unsupported
OpenOptions { read: true, write: true, .. } => {
return Err(io::const_error!(
io::ErrorKind::InvalidInput,
"opening files with read and write access is unsupported on this target",
));
}
// read
OpenOptions {
read: true,
write: false,
append: _,
truncate: false,
create: false,
create_new: false,
} => unsafe { vex_sdk::vexFileOpen(path.as_ptr(), c"".as_ptr()) },
// append
OpenOptions {
read: false,
write: _,
append: true,
truncate: false,
create: _,
create_new: _,
} => unsafe { vex_sdk::vexFileOpenWrite(path.as_ptr()) },
// write
OpenOptions {
read: false,
write: true,
append: false,
truncate,
create: _,
create_new: _,
} => unsafe {
if *truncate {
vex_sdk::vexFileOpenCreate(path.as_ptr())
} else {
// Open in append, but jump to the start of the file.
let fd = vex_sdk::vexFileOpenWrite(path.as_ptr());
vex_sdk::vexFileSeek(fd, 0, 0);
fd
}
},
_ => {
return Err(io::const_error!(io::ErrorKind::InvalidInput, "invalid argument"));
}
};
if file.is_null() {
Err(io::const_error!(io::ErrorKind::NotFound, "could not open file"))
} else {
Ok(Self { fd: FileDesc(file) })
}
})
}
pub fn file_attr(&self) -> io::Result<FileAttr> {
// `vexFileSize` returns -1 upon error, so u64::try_from will fail on error.
if let Ok(size) = u64::try_from(unsafe {
// SAFETY: `self.fd` contains a valid pointer to `FIL` for this struct's lifetime.
vex_sdk::vexFileSize(self.fd.0)
}) {
Ok(FileAttr::File { size })
} else {
Err(io::const_error!(io::ErrorKind::InvalidData, "failed to get file size"))
}
}
pub fn fsync(&self) -> io::Result<()> {
self.flush()
}
pub fn datasync(&self) -> io::Result<()> {
self.flush()
}
pub fn lock(&self) -> io::Result<()> {
unsupported()
}
pub fn lock_shared(&self) -> io::Result<()> {
unsupported()
}
pub fn try_lock(&self) -> Result<(), TryLockError> {
Err(TryLockError::Error(unsupported_err()))
}
pub fn try_lock_shared(&self) -> Result<(), TryLockError> {
Err(TryLockError::Error(unsupported_err()))
}
pub fn unlock(&self) -> io::Result<()> {
unsupported()
}
pub fn truncate(&self, _size: u64) -> io::Result<()> {
unsupported()
}
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
let len = buf.len() as u32;
let buf_ptr = buf.as_mut_ptr();
let read = unsafe {
// SAFETY: `self.fd` contains a valid pointer to `FIL` for this struct's lifetime.
vex_sdk::vexFileRead(buf_ptr.cast::<c_char>(), 1, len, self.fd.0)
};
if read < 0 {
Err(io::const_error!(io::ErrorKind::Other, "could not read from file"))
} else {
Ok(read as usize)
}
}
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
crate::io::default_read_vectored(|b| self.read(b), bufs)
}
#[inline]
pub fn is_read_vectored(&self) -> bool {
false
}
pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
crate::io::default_read_buf(|b| self.read(b), cursor)
}
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
let len = buf.len() as u32;
let buf_ptr = buf.as_ptr();
let written = unsafe {
// SAFETY: `self.fd` contains a valid pointer to `FIL` for this struct's lifetime.
vex_sdk::vexFileWrite(buf_ptr.cast_mut().cast::<c_char>(), 1, len, self.fd.0)
};
if written < 0 {
Err(io::const_error!(io::ErrorKind::Other, "could not write to file"))
} else {
Ok(written as usize)
}
}
pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
crate::io::default_write_vectored(|b| self.write(b), bufs)
}
#[inline]
pub fn is_write_vectored(&self) -> bool {
false
}
pub fn flush(&self) -> io::Result<()> {
unsafe {
// SAFETY: `self.fd` contains a valid pointer to `FIL` for this struct's lifetime.
vex_sdk::vexFileSync(self.fd.0);
}
Ok(())
}
pub fn tell(&self) -> io::Result<u64> {
// SAFETY: `self.fd` contains a valid pointer to `FIL` for this struct's lifetime.
let position = unsafe { vex_sdk::vexFileTell(self.fd.0) };
position.try_into().map_err(|_| {
io::const_error!(io::ErrorKind::InvalidData, "failed to get current location in file")
})
}
pub fn size(&self) -> Option<io::Result<u64>> {
None
}
pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
const SEEK_SET: i32 = 0;
const SEEK_CUR: i32 = 1;
const SEEK_END: i32 = 2;
fn try_convert_offset<T: TryInto<u32>>(offset: T) -> io::Result<u32> {
offset.try_into().map_err(|_| {
io::const_error!(
io::ErrorKind::InvalidInput,
"cannot seek to an offset too large to fit in a 32 bit integer",
)
})
}
// SAFETY: `self.fd` contains a valid pointer to `FIL` for this struct's lifetime.
match pos {
SeekFrom::Start(offset) => unsafe {
map_fresult(vex_sdk::vexFileSeek(self.fd.0, try_convert_offset(offset)?, SEEK_SET))?
},
SeekFrom::End(offset) => unsafe {
if offset >= 0 {
map_fresult(vex_sdk::vexFileSeek(
self.fd.0,
try_convert_offset(offset)?,
SEEK_END,
))?
} else {
// `vexFileSeek` does not support seeking with negative offset, meaning
// we have to calculate the offset from the end of the file ourselves.
// Seek to the end of the file to get the end position in the open buffer.
map_fresult(vex_sdk::vexFileSeek(self.fd.0, 0, SEEK_END))?;
let end_position = self.tell()?;
map_fresult(vex_sdk::vexFileSeek(
self.fd.0,
// NOTE: Files internally use a 32-bit representation for stream
// position, so `end_position as i64` should never overflow.
try_convert_offset(end_position as i64 + offset)?,
SEEK_SET,
))?
}
},
SeekFrom::Current(offset) => unsafe {
if offset >= 0 {
map_fresult(vex_sdk::vexFileSeek(
self.fd.0,
try_convert_offset(offset)?,
SEEK_CUR,
))?
} else {
// `vexFileSeek` does not support seeking with negative offset, meaning
// we have to calculate the offset from the stream position ourselves.
map_fresult(vex_sdk::vexFileSeek(
self.fd.0,
try_convert_offset((self.tell()? as i64) + offset)?,
SEEK_SET,
))?
}
},
}
Ok(self.tell()?)
}
pub fn duplicate(&self) -> io::Result<File> {
unsupported()
}
pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> {
unsupported()
}
pub fn set_times(&self, _times: FileTimes) -> io::Result<()> {
unsupported()
}
}
impl fmt::Debug for File {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("File").field("fd", &self.fd.0).finish()
}
}
impl Drop for File {
fn drop(&mut self) {
unsafe { vex_sdk::vexFileClose(self.fd.0) };
}
}
pub fn readdir(_p: &Path) -> io::Result<ReadDir> {
// While there *is* a userspace function for reading file directories,
// the necessary implementation cannot currently be done cleanly, as
// VEXos does not expose directory length to user programs.
//
// This means that we would need to create a large fixed-length buffer
// and hope that the folder's contents didn't exceed that buffer's length,
// which obviously isn't behavior we want to rely on in the standard library.
unsupported()
}
pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> {
unsupported()
}
pub fn exists(path: &Path) -> io::Result<bool> {
run_path_with_cstr(path, &|path| Ok(unsafe { vex_sdk::vexFileStatus(path.as_ptr()) } != 0))
}
pub fn stat(p: &Path) -> io::Result<FileAttr> {
// `vexFileStatus` returns 3 if the given path is a directory, 1 if the path is a
// file, or 0 if no such path exists.
const FILE_STATUS_DIR: u32 = 3;
run_path_with_cstr(p, &|c_path| {
let file_type = unsafe { vex_sdk::vexFileStatus(c_path.as_ptr()) };
// We can't get the size if its a directory because we cant open it as a file
if file_type == FILE_STATUS_DIR {
Ok(FileAttr::Dir)
} else {
let mut opts = OpenOptions::new();
opts.read(true);
let file = File::open(p, &opts)?;
file.file_attr()
}
})
}
pub fn lstat(p: &Path) -> io::Result<FileAttr> {
// Symlinks aren't supported in this filesystem
stat(p)
}
// Cannot use `copy` from `common` here, since `File::set_permissions` is unsupported on this target.
pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
use crate::fs::File;
// NOTE: If `from` is a directory, this call should fail due to vexFileOpen* returning null.
let mut reader = File::open(from)?;
let mut writer = File::create(to)?;
io::copy(&mut reader, &mut writer)
}
fn map_fresult(fresult: vex_sdk::FRESULT) -> io::Result<()> {
// VEX uses a derivative of FatFs (Xilinx's xilffs library) for filesystem operations.
match fresult {
vex_sdk::FRESULT::FR_OK => Ok(()),
vex_sdk::FRESULT::FR_DISK_ERR => Err(io::const_error!(
io::ErrorKind::Uncategorized,
"internal function reported an unrecoverable hard error",
)),
vex_sdk::FRESULT::FR_INT_ERR => Err(io::const_error!(
io::ErrorKind::Uncategorized,
"internal error in filesystem runtime",
)),
vex_sdk::FRESULT::FR_NOT_READY => Err(io::const_error!(
io::ErrorKind::Uncategorized,
"the storage device could not be prepared to work",
)),
vex_sdk::FRESULT::FR_NO_FILE => Err(io::const_error!(
io::ErrorKind::NotFound,
"could not find the file in the directory"
)),
vex_sdk::FRESULT::FR_NO_PATH => Err(io::const_error!(
io::ErrorKind::NotFound,
"a directory in the path name could not be found",
)),
vex_sdk::FRESULT::FR_INVALID_NAME => Err(io::const_error!(
io::ErrorKind::InvalidInput,
"the given string is invalid as a path name",
)),
vex_sdk::FRESULT::FR_DENIED => Err(io::const_error!(
io::ErrorKind::PermissionDenied,
"the required access for this operation was denied",
)),
vex_sdk::FRESULT::FR_EXIST => Err(io::const_error!(
io::ErrorKind::AlreadyExists,
"an object with the same name already exists in the directory",
)),
vex_sdk::FRESULT::FR_INVALID_OBJECT => Err(io::const_error!(
io::ErrorKind::Uncategorized,
"invalid or null file/directory object",
)),
vex_sdk::FRESULT::FR_WRITE_PROTECTED => Err(io::const_error!(
io::ErrorKind::PermissionDenied,
"a write operation was performed on write-protected media",
)),
vex_sdk::FRESULT::FR_INVALID_DRIVE => Err(io::const_error!(
io::ErrorKind::InvalidInput,
"an invalid drive number was specified in the path name",
)),
vex_sdk::FRESULT::FR_NOT_ENABLED => Err(io::const_error!(
io::ErrorKind::Uncategorized,
"work area for the logical drive has not been registered",
)),
vex_sdk::FRESULT::FR_NO_FILESYSTEM => Err(io::const_error!(
io::ErrorKind::Uncategorized,
"valid FAT volume could not be found on the drive",
)),
vex_sdk::FRESULT::FR_MKFS_ABORTED => Err(io::const_error!(
io::ErrorKind::Uncategorized,
"failed to create filesystem volume"
)),
vex_sdk::FRESULT::FR_TIMEOUT => Err(io::const_error!(
io::ErrorKind::TimedOut,
"the function was canceled due to a timeout of thread-safe control",
)),
vex_sdk::FRESULT::FR_LOCKED => Err(io::const_error!(
io::ErrorKind::Uncategorized,
"the operation to the object was rejected by file sharing control",
)),
vex_sdk::FRESULT::FR_NOT_ENOUGH_CORE => {
Err(io::const_error!(io::ErrorKind::OutOfMemory, "not enough memory for the operation"))
}
vex_sdk::FRESULT::FR_TOO_MANY_OPEN_FILES => Err(io::const_error!(
io::ErrorKind::Uncategorized,
"maximum number of open files has been reached",
)),
vex_sdk::FRESULT::FR_INVALID_PARAMETER => {
Err(io::const_error!(io::ErrorKind::InvalidInput, "a given parameter was invalid"))
}
_ => unreachable!(), // C-style enum
}
}

View File

@ -45,6 +45,10 @@ cfg_select! {
mod trusty;
pub use self::trusty::*;
}
target_os = "vexos" => {
mod vexos;
pub use self::vexos::*;
}
all(target_os = "wasi", target_env = "p2") => {
mod wasip2;
pub use self::wasip2::*;

View File

@ -0,0 +1,80 @@
#[path = "../unsupported/os.rs"]
pub mod os;
#[path = "../unsupported/pipe.rs"]
pub mod pipe;
pub mod time;
#[expect(dead_code)]
#[path = "../unsupported/common.rs"]
mod unsupported_common;
pub use unsupported_common::{
decode_error_kind, init, is_interrupted, unsupported, unsupported_err,
};
use crate::arch::global_asm;
use crate::ptr;
use crate::sys::stdio;
use crate::time::{Duration, Instant};
global_asm!(
r#"
.section .boot, "ax"
.global _boot
_boot:
ldr sp, =__stack_top @ Set up the user stack.
b _start @ Jump to the Rust entrypoint.
"#
);
#[cfg(not(test))]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn _start() -> ! {
unsafe extern "C" {
static mut __bss_start: u8;
static mut __bss_end: u8;
fn main() -> i32;
}
// Clear the .bss (uninitialized statics) section by filling it with zeroes.
// This is required, since the compiler assumes it will be zeroed on first access.
ptr::write_bytes(
&raw mut __bss_start,
0,
(&raw mut __bss_end).offset_from_unsigned(&raw mut __bss_start),
);
main();
cleanup();
abort_internal()
}
// SAFETY: must be called only once during runtime cleanup.
// NOTE: this is not guaranteed to run, for example when the program aborts.
pub unsafe fn cleanup() {
let exit_time = Instant::now();
const FLUSH_TIMEOUT: Duration = Duration::from_millis(15);
// Force the serial buffer to flush
while exit_time.elapsed() < FLUSH_TIMEOUT {
vex_sdk::vexTasksRun();
// If the buffer has been fully flushed, exit the loop
if vex_sdk::vexSerialWriteFree(stdio::STDIO_CHANNEL) == (stdio::STDOUT_BUF_SIZE as i32) {
break;
}
}
}
pub fn abort_internal() -> ! {
unsafe {
vex_sdk::vexSystemExitRequest();
loop {
vex_sdk::vexTasksRun();
}
}
}

View File

@ -0,0 +1,28 @@
use crate::time::Duration;
#[expect(dead_code)]
#[path = "../unsupported/time.rs"]
mod unsupported_time;
pub use unsupported_time::{SystemTime, UNIX_EPOCH};
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct Instant(Duration);
impl Instant {
pub fn now() -> Instant {
let micros = unsafe { vex_sdk::vexSystemHighResTimeGet() };
Self(Duration::from_micros(micros))
}
pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
self.0.checked_sub(other.0)
}
pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
Some(Instant(self.0.checked_add(*other)?))
}
pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
Some(Instant(self.0.checked_sub(*other)?))
}
}

View File

@ -101,6 +101,7 @@ cfg_select! {
any(
all(target_family = "wasm", target_os = "unknown"),
target_os = "xous",
target_os = "vexos",
) => {
// FIXME: finally remove std support for wasm32-unknown-unknown
// FIXME: add random data generation to xous
@ -116,6 +117,7 @@ cfg_select! {
all(target_family = "wasm", target_os = "unknown"),
all(target_os = "wasi", target_env = "p2"),
target_os = "xous",
target_os = "vexos",
)))]
pub fn hashmap_random_keys() -> (u64, u64) {
let mut buf = [0; 16];

View File

@ -29,6 +29,10 @@ cfg_select! {
mod uefi;
pub use uefi::*;
}
target_os = "vexos" => {
mod vexos;
pub use vexos::*;
}
all(target_os = "wasi", target_env = "p1") => {
mod wasip1;
pub use wasip1::*;

View File

@ -0,0 +1,100 @@
use crate::io;
pub struct Stdin;
pub struct Stdout;
pub type Stderr = Stdout;
pub const STDIO_CHANNEL: u32 = 1;
impl Stdin {
pub const fn new() -> Stdin {
Stdin
}
}
impl io::Read for Stdin {
fn read(&mut self, mut buf: &mut [u8]) -> io::Result<usize> {
let mut count = 0;
for out_byte in buf.iter_mut() {
let byte = unsafe { vex_sdk::vexSerialReadChar(STDIO_CHANNEL) };
if byte < 0 {
break;
}
*out_byte = byte as u8;
count += 1;
}
Ok(count)
}
}
impl Stdout {
pub const fn new() -> Stdout {
Stdout
}
}
impl io::Write for Stdout {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let mut written = 0;
// HACK: VEXos holds an internal ringbuffer for serial writes that is flushed to USB1
// roughly every millisecond by `vexTasksRun`. For writes larger than 2048 bytes, we
// must block until that buffer is flushed to USB1 before writing the rest of `buf`.
//
// This is fairly nonstandard for a `write` implementation, but it avoids a guaranteed
// recursive panic when using macros such as `print!` to write large amounts of data
// (buf.len() > 2048) to stdout at once.
for chunk in buf.chunks(STDOUT_BUF_SIZE) {
if unsafe { vex_sdk::vexSerialWriteFree(STDIO_CHANNEL) as usize } < chunk.len() {
self.flush().unwrap();
}
let count: usize = unsafe {
vex_sdk::vexSerialWriteBuffer(STDIO_CHANNEL, chunk.as_ptr(), chunk.len() as u32)
}
.try_into()
.map_err(|_| {
io::const_error!(io::ErrorKind::Uncategorized, "internal write error occurred")
})?;
written += count;
// This is a sanity check to ensure that we don't end up with non-contiguous
// buffer writes. e.g. a chunk gets only partially written, but we continue
// attempting to write the remaining chunks.
//
// In practice, this should never really occur since the previous flush ensures
// enough space in FIFO to write the entire chunk to vexSerialWriteBuffer.
if count != chunk.len() {
break;
}
}
Ok(written)
}
fn flush(&mut self) -> io::Result<()> {
// This may block for up to a millisecond.
unsafe {
while (vex_sdk::vexSerialWriteFree(STDIO_CHANNEL) as usize) != STDOUT_BUF_SIZE {
vex_sdk::vexTasksRun();
}
}
Ok(())
}
}
pub const STDIN_BUF_SIZE: usize = 4096;
pub const STDOUT_BUF_SIZE: usize = 2048;
pub fn is_ebadf(_err: &io::Error) -> bool {
false
}
pub fn panic_output() -> Option<impl io::Write> {
Some(Stdout::new())
}

View File

@ -81,6 +81,13 @@ cfg_select! {
))]
pub use unsupported::set_name;
}
target_os = "vexos" => {
mod vexos;
pub use vexos::{sleep, yield_now};
#[expect(dead_code)]
mod unsupported;
pub use unsupported::{Thread, available_parallelism, current_os_id, set_name, DEFAULT_MIN_STACK_SIZE};
}
all(target_os = "wasi", target_env = "p1") => {
mod wasip1;
pub use wasip1::{DEFAULT_MIN_STACK_SIZE, sleep, yield_now};

View File

@ -0,0 +1,17 @@
use crate::time::{Duration, Instant};
pub fn yield_now() {
unsafe {
vex_sdk::vexTasksRun();
}
}
pub fn sleep(dur: Duration) {
let start = Instant::now();
while start.elapsed() < dur {
unsafe {
vex_sdk::vexTasksRun();
}
}
}

View File

@ -29,6 +29,7 @@ cfg_select! {
target_os = "uefi",
target_os = "zkvm",
target_os = "trusty",
target_os = "vexos",
) => {
mod no_threads;
pub use no_threads::{EagerStorage, LazyStorage, thread_local_inner};
@ -98,6 +99,7 @@ pub(crate) mod guard {
target_os = "uefi",
target_os = "zkvm",
target_os = "trusty",
target_os = "vexos",
) => {
pub(crate) fn enable() {
// FIXME: Right now there is no concept of "thread exit" on

View File

@ -596,6 +596,7 @@ class RustBuild(object):
self.download_url = (
os.getenv("RUSTUP_DIST_SERVER") or self.stage0_data["dist_server"]
)
self.jobs = self.get_toml("jobs", "build") or "default"
self.build = args.build or self.build_triple()
@ -1144,6 +1145,7 @@ class RustBuild(object):
args = [
self.cargo(),
"build",
"--jobs=" + self.jobs,
"--manifest-path",
os.path.join(self.rust_root, "src/bootstrap/Cargo.toml"),
"-Zroot-dir=" + self.rust_root,

View File

@ -4,7 +4,7 @@
Allows compiling user programs for the [VEX V5 Brain](https://www.vexrobotics.com/276-4810.html), a microcontroller for educational and competitive robotics.
Rust support for this target is not affiliated with VEX Robotics or IFI.
Rust support for this target is not affiliated with VEX Robotics or IFI, and does not link to any official VEX SDK.
## Target maintainers
@ -17,11 +17,24 @@ This target is maintained by members of the [vexide](https://github.com/vexide)
## Requirements
This target is cross-compiled and currently requires `#![no_std]`. Dynamic linking is unsupported.
This target is cross-compiled. Dynamic linking is unsupported.
When compiling for this target, the "C" calling convention maps to AAPCS with VFP registers (hard float ABI) and the "system" calling convention maps to AAPCS without VFP registers (soft float ABI).
`#![no_std]` crates can be built using `build-std` to build `core` and `panic_abort` and optionally `alloc`. Unwinding panics are not yet supported on this target.
This target generates binaries in the ELF format that may uploaded to the brain with external tools.
`std` has only partial support due platform limitations. Notably:
- `std::process` and `std::net` are unimplemented. `std::thread` only supports sleeping and yielding, as this is a single-threaded environment.
- `std::time` has full support for `Instant`, but no support for `SystemTime`.
- `std::io` has full support for `stdin`/`stdout`/`stderr`. `stdout` and `stderr` both write to to USB channel 1 on this platform and are not differentiated.
- `std::fs` has limited support for reading or writing to files. Directory operations, file deletion, and some file opening features are unsupported and will return errors.
- A global allocator implemented on top of `dlmalloc` is provided.
- Modules that do not need to interact with the OS beyond allocation such as `std::collections`, `std::hash`, `std::future`, `std::sync`, etc are fully supported.
- Random number generation and hashing is insecure, as there is no reliable source of entropy on this platform.
In order to support some APIs, users are expected to provide a supporting runtime SDK for `libstd` to link against. This library may be provided either by [`vex-sdk-build`](https://github.com/vexide/vex-sdk/tree/main/packages/vex-sdk-build) (which will download an official SDK from VEX) or through an open-source implementation such as [`vex-sdk-jumptable`](https://crates.io/crates/vex-sdk-jumptable).
When compiling for this target, the "C" calling convention maps to AAPCS with VFP registers (hard float ABI) and the "system" calling convention maps to AAPCS without VFP registers (softfp ABI).
This target generates binaries in the ELF format that may be uploaded to the brain with external tools.
## Building the target
@ -29,10 +42,7 @@ You can build Rust with support for this target by adding it to the `target` lis
## Building Rust programs
Rust does not yet ship pre-compiled artifacts for this target. To compile for
this target, you will either need to build Rust with the target enabled (see
"Building the target" above), or build your own copy of `core` by using
`build-std` or similar.
Rust does not yet ship pre-compiled artifacts for this target. To compile for this target, you will either need to build Rust with the target enabled (see "Building the target" above), or build your own copy of `core` by using `build-std` or similar.
When the compiler builds a binary, an ELF build artifact will be produced. Additional tools are required for this artifact to be recognizable to VEXos as a user program.

View File

@ -739,7 +739,7 @@ This flag can be passed multiple times to nest wrappers.
## Passing arguments to rustc when compiling doctests
You can use the `--doctest-compilation-args` flag if you want to add options when compiling the
You can use the `--doctest-build-arg` flag if you want to add options when compiling the
doctest. For example if you have:
```rust,no_run
@ -784,10 +784,10 @@ failures:
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.03s
```
But if you can limit the lint level to warning by using `--doctest_compilation_args=--cap-lints=warn`:
But if you can limit the lint level to warning by using `--doctest-build-arg=--cap-lints=warn`:
```console
$ rustdoc --test --doctest_compilation_args=--cap-lints=warn file.rs
$ rustdoc --test --doctest-build-arg=--cap-lints=warn file.rs
running 1 test
test tests/rustdoc-ui/doctest/rustflags.rs - Bar (line 5) ... ok
@ -795,24 +795,8 @@ test tests/rustdoc-ui/doctest/rustflags.rs - Bar (line 5) ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.06s
```
The parsing of arguments works as follows: if it encounters a `"` or a `'`, it will continue
until it finds the character unescaped (without a prepending `\`). If not inside a string, a
whitespace character will also split arguments. Example:
```text
"hello 'a'\" ok" how are 'you today?'
```
will be split as follows:
```text
[
"hello 'a'\" ok",
"how",
"are",
"you today?",
]
```
In order to pass multiple arguments to the underlying compiler,
pass `--doctest-build-arg ARG` for each argument `ARG`.
## `--generate-macro-expansion`: Generate macros expansion toggles in source code

View File

@ -65,5 +65,10 @@
<strong>Note:</strong> Encountered an error during type layout; {#+ #}
the type's layout depended on the type's layout itself. {# #}
</p>
{% when Err(LayoutError::InvalidSimd {..}) %}
<p> {# #}
<strong>Note:</strong> Encountered an error during type layout; {#+ #}
the vector type had zero elements or too many elements. {# #}
</p>
{% endmatch %}
</div> {# #}

View File

@ -1,4 +1,3 @@
use rand::Rng;
use rustc_apfloat::{self, Float, FloatConvert, Round};
use rustc_middle::mir;
use rustc_middle::ty::{self, FloatTy};
@ -39,46 +38,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
"sqrtf64" => sqrt::<rustc_apfloat::ieee::Double>(this, args, dest)?,
"sqrtf128" => sqrt::<rustc_apfloat::ieee::Quad>(this, args, dest)?,
"fmaf32" => {
let [a, b, c] = check_intrinsic_arg_count(args)?;
let a = this.read_scalar(a)?.to_f32()?;
let b = this.read_scalar(b)?.to_f32()?;
let c = this.read_scalar(c)?.to_f32()?;
let res = a.mul_add(b, c).value;
let res = this.adjust_nan(res, &[a, b, c]);
this.write_scalar(res, dest)?;
}
"fmaf64" => {
let [a, b, c] = check_intrinsic_arg_count(args)?;
let a = this.read_scalar(a)?.to_f64()?;
let b = this.read_scalar(b)?.to_f64()?;
let c = this.read_scalar(c)?.to_f64()?;
let res = a.mul_add(b, c).value;
let res = this.adjust_nan(res, &[a, b, c]);
this.write_scalar(res, dest)?;
}
"fmuladdf32" => {
let [a, b, c] = check_intrinsic_arg_count(args)?;
let a = this.read_scalar(a)?.to_f32()?;
let b = this.read_scalar(b)?.to_f32()?;
let c = this.read_scalar(c)?.to_f32()?;
let fuse: bool = this.machine.float_nondet && this.machine.rng.get_mut().random();
let res = if fuse { a.mul_add(b, c).value } else { ((a * b).value + c).value };
let res = this.adjust_nan(res, &[a, b, c]);
this.write_scalar(res, dest)?;
}
"fmuladdf64" => {
let [a, b, c] = check_intrinsic_arg_count(args)?;
let a = this.read_scalar(a)?.to_f64()?;
let b = this.read_scalar(b)?.to_f64()?;
let c = this.read_scalar(c)?.to_f64()?;
let fuse: bool = this.machine.float_nondet && this.machine.rng.get_mut().random();
let res = if fuse { a.mul_add(b, c).value } else { ((a * b).value + c).value };
let res = this.adjust_nan(res, &[a, b, c]);
this.write_scalar(res, dest)?;
}
#[rustfmt::skip]
| "fadd_fast"
| "fsub_fast"

View File

@ -1293,6 +1293,11 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
ecx.equal_float_min_max(a, b)
}
#[inline(always)]
fn float_fuse_mul_add(ecx: &mut InterpCx<'tcx, Self>) -> bool {
ecx.machine.float_nondet && ecx.machine.rng.get_mut().random()
}
#[inline(always)]
fn ub_checks(ecx: &InterpCx<'tcx, Self>) -> InterpResult<'tcx, bool> {
interp_ok(ecx.tcx.sess.ub_checks())

View File

@ -578,6 +578,7 @@ const PERMITTED_STDLIB_DEPENDENCIES: &[&str] = &[
"rustc-literal-escaper",
"shlex",
"unwinding",
"vex-sdk",
"wasi",
"windows-sys",
"windows-targets",

View File

@ -0,0 +1,41 @@
//@ compile-flags: -C opt-level=3
#![crate_type = "lib"]
mod foobar {
use std::alloc::{GlobalAlloc, Layout};
struct Allocator;
unsafe impl GlobalAlloc for Allocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
// CHECK-LABEL: ; __rustc::__rust_alloc
// CHECK-NEXT: ; Function Attrs: {{.*}}allockind("alloc,uninitialized,aligned") allocsize(0){{.*}}
// CHECK-NEXT: define{{.*}} noalias{{.*}} ptr @{{.*}}__rust_alloc(i[[SIZE:[0-9]+]] {{.*}}%size, i[[SIZE]] allocalign{{.*}} %align)
panic!()
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
// CHECK-LABEL: ; __rustc::__rust_dealloc
// CHECK-NEXT: ; Function Attrs: {{.*}}allockind("free"){{.*}}
// CHECK-NEXT: define{{.*}} void @{{.*}}__rust_dealloc(ptr allocptr{{.*}} %ptr, i[[SIZE]] {{.*}} %size, i[[SIZE]] {{.*}} %align)
panic!()
}
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
// CHECK-LABEL: ; __rustc::__rust_realloc
// CHECK-NEXT: ; Function Attrs: {{.*}}allockind("realloc,aligned") allocsize(3){{.*}}
// CHECK-NEXT: define{{.*}} noalias{{.*}} ptr @{{.*}}__rust_realloc(ptr allocptr{{.*}} %ptr, i[[SIZE]] {{.*}} %size, i[[SIZE]] allocalign{{.*}} %align, i[[SIZE]] {{.*}} %new_size)
panic!()
}
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
// CHECK-LABEL: ; __rustc::__rust_alloc_zeroed
// CHECK-NEXT: ; Function Attrs: {{.*}}allockind("alloc,zeroed,aligned") allocsize(0){{.*}}
// CHECK-NEXT: define{{.*}} noalias{{.*}} ptr @{{.*}}__rust_alloc_zeroed(i[[SIZE]] {{.*}} %size, i[[SIZE]] allocalign{{.*}} %align)
panic!()
}
}
#[global_allocator]
static GLOBAL: Allocator = Allocator;
}

View File

@ -61,7 +61,6 @@ fn main() {
diff()
.expected_file("short-error.txt")
.actual_text("(linker error)", out.stderr())
.normalize(r#"/rustc[^/_-]*/"#, "/rustc/")
.normalize("libpanic_abort", "libpanic_unwind")
.normalize(
regex::escape(

View File

@ -0,0 +1,46 @@
//@ run-pass
#![feature(unsize)]
#![feature(coerce_unsized)]
use std::fmt::Display;
use std::marker::Unsize;
use std::ops::CoerceUnsized;
use std::rc::Weak;
#[repr(transparent)]
struct X<'a, T: ?Sized> {
f: &'a T,
}
impl<'a, T: ?Sized> Drop for X<'a, T> {
fn drop(&mut self) {
panic!()
}
}
impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<X<'a, U>> for X<'a, T> where
&'a T: CoerceUnsized<&'a U>
{
}
const Y: X<'static, i32> = X { f: &0 };
fn main() {
let _: [X<'static, dyn Display>; 0] = [Y; 0];
coercion_on_weak_in_const();
coercion_on_weak_as_cast();
}
fn coercion_on_weak_in_const() {
const X: Weak<i32> = Weak::new();
const Y: [Weak<dyn Send>; 0] = [X; 0];
let _ = Y;
}
fn coercion_on_weak_as_cast() {
const Y: X<'static, i32> = X { f: &0 };
// What happens in the following code is that
// a constant is explicitly coerced into
let _a: [X<'static, dyn Display>; 0] = [Y as X<'static, dyn Display>; 0];
}

View File

@ -0,0 +1,5 @@
#![feature(rustc_attrs, repr_simd)]
#[repr(simd, packed)]
#[rustc_simd_monomorphize_lane_limit = "8"]
pub struct Simd<T, const N: usize>(pub [T; N]);

View File

@ -6,7 +6,5 @@
struct Simd<T, const N: usize>([T; N]);
fn main() {
let _too_big = Simd([1_u16; 54321]);
let _too_big = Simd([1_u16; 54321]); //~ ERROR the SIMD type `Simd<u16, 54321>` has more elements than the limit 32768
}
//~? ERROR monomorphising SIMD type `Simd<u16, 54321>` of length greater than 32768

View File

@ -1,4 +1,8 @@
error: monomorphising SIMD type `Simd<u16, 54321>` of length greater than 32768
error: the SIMD type `Simd<u16, 54321>` has more elements than the limit 32768
--> $DIR/monomorphize-too-long.rs:9:9
|
LL | let _too_big = Simd([1_u16; 54321]);
| ^^^^^^^^
error: aborting due to 1 previous error

View File

@ -6,7 +6,5 @@
struct Simd<T, const N: usize>([T; N]);
fn main() {
let _empty = Simd([1.0; 0]);
let _empty = Simd([1.0; 0]); //~ ERROR the SIMD type `Simd<f64, 0>` has zero elements
}
//~? ERROR monomorphising SIMD type `Simd<f64, 0>` of zero length

View File

@ -1,4 +1,8 @@
error: monomorphising SIMD type `Simd<f64, 0>` of zero length
error: the SIMD type `Simd<f64, 0>` has zero elements
--> $DIR/monomorphize-zero-length.rs:9:9
|
LL | let _empty = Simd([1.0; 0]);
| ^^^^^^
error: aborting due to 1 previous error

View File

@ -0,0 +1,12 @@
//@ build-fail
//@ aux-crate:simd=simd-lane-limit.rs
extern crate simd;
use simd::Simd;
fn main() {
// test non-power-of-two, since #[repr(simd, packed)] has unusual layout
let _x: Simd<i32, 24> = Simd([0; 24]);
//~^ ERROR the SIMD type `simd::Simd<i32, 24>` has more elements than the limit 8
}

View File

@ -0,0 +1,8 @@
error: the SIMD type `simd::Simd<i32, 24>` has more elements than the limit 8
--> $DIR/simd-lane-limit-err-npow2.rs:10:9
|
LL | let _x: Simd<i32, 24> = Simd([0; 24]);
| ^^
error: aborting due to 1 previous error

View File

@ -0,0 +1,11 @@
//@ build-fail
//@ aux-crate:simd=simd-lane-limit.rs
extern crate simd;
use simd::Simd;
fn main() {
let _x: Simd<i32, 16> = Simd([0; 16]);
//~^ ERROR the SIMD type `simd::Simd<i32, 16>` has more elements than the limit 8
}

View File

@ -0,0 +1,8 @@
error: the SIMD type `simd::Simd<i32, 16>` has more elements than the limit 8
--> $DIR/simd-lane-limit-err.rs:9:9
|
LL | let _x: Simd<i32, 16> = Simd([0; 16]);
| ^^
error: aborting due to 1 previous error

View File

@ -0,0 +1,14 @@
//@ build-pass
//@ aux-crate:simd=simd-lane-limit.rs
extern crate simd;
use simd::Simd;
fn main() {
let _x: Simd<i32, 4> = Simd([0; 4]);
let _y: Simd<i32, 8> = Simd([0; 8]);
// test non-power-of-two, since #[repr(simd, packed)] has unusual layout
let _z: Simd<i32, 6> = Simd([0; 6]);
}

View File

@ -6,7 +6,5 @@
struct Simd<const N: usize>([f32; N]);
fn main() {
let _ = Simd::<0>([]);
let _empty = Simd::<0>([]); //~ ERROR the SIMD type `Simd<0>` has zero elements
}
//~? ERROR monomorphising SIMD type `Simd<0>` of zero length

View File

@ -1,4 +1,8 @@
error: monomorphising SIMD type `Simd<0>` of zero length
error: the SIMD type `Simd<0>` has zero elements
--> $DIR/type-generic-monomorphisation-empty.rs:9:9
|
LL | let _empty = Simd::<0>([]);
| ^^^^^^
error: aborting due to 1 previous error

View File

@ -6,7 +6,6 @@
struct Simd<const N: usize>([f32; N]);
fn main() {
let _ = Simd::<65536>([0.; 65536]);
let _x = Simd::<65536>([0.; 65536]);
//~^ ERROR the SIMD type `Simd<65536>` has more elements than the limit 32768
}
//~? ERROR monomorphising SIMD type `Simd<65536>` of length greater than 32768

View File

@ -1,4 +1,8 @@
error: monomorphising SIMD type `Simd<65536>` of length greater than 32768
error: the SIMD type `Simd<65536>` has more elements than the limit 32768
--> $DIR/type-generic-monomorphisation-oversized.rs:9:9
|
LL | let _x = Simd::<65536>([0.; 65536]);
| ^^
error: aborting due to 1 previous error