Auto merge of #151749 - JonathanBrouwer:rollup-IRCTaVD, r=JonathanBrouwer

Rollup of 4 pull requests

Successful merges:

 - rust-lang/rust#151161 (std: move time implementations to `sys`)
 - rust-lang/rust#151694 (more `proc_macro` bridge cleanups)
 - rust-lang/rust#151711 (stdarch subtree update)
 - rust-lang/rust#150557 (Don't try to evaluate const blocks during constant promotion)
This commit is contained in:
bors 2026-01-27 17:40:26 +00:00
commit e96bb7e44f
73 changed files with 8194 additions and 7734 deletions

View file

@ -458,13 +458,11 @@ impl<'a, 'b> Rustc<'a, 'b> {
}
}
impl server::Types for Rustc<'_, '_> {
impl server::Server for Rustc<'_, '_> {
type TokenStream = TokenStream;
type Span = Span;
type Symbol = Symbol;
}
impl server::Server for Rustc<'_, '_> {
fn globals(&mut self) -> ExpnGlobals<Self::Span> {
ExpnGlobals {
def_site: self.def_site,

View file

@ -18,6 +18,7 @@ use rustc_const_eval::check_consts::{ConstCx, qualifs};
use rustc_data_structures::assert_matches;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_index::{IndexSlice, IndexVec};
use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::*;
@ -329,6 +330,7 @@ impl<'tcx> Validator<'_, 'tcx> {
if let TempState::Defined { location: loc, .. } = self.temps[local]
&& let Left(statement) = self.body.stmt_at(loc)
&& let Some((_, Rvalue::Use(Operand::Constant(c)))) = statement.kind.as_assign()
&& self.should_evaluate_for_promotion_checks(c.const_)
&& let Some(idx) = c.const_.try_eval_target_usize(self.tcx, self.typing_env)
// Determine the type of the thing we are indexing.
&& let ty::Array(_, len) = place_base.ty(self.body, self.tcx).ty.kind()
@ -484,7 +486,9 @@ impl<'tcx> Validator<'_, 'tcx> {
let sz = lhs_ty.primitive_size(self.tcx);
// Integer division: the RHS must be a non-zero const.
let rhs_val = match rhs {
Operand::Constant(c) => {
Operand::Constant(c)
if self.should_evaluate_for_promotion_checks(c.const_) =>
{
c.const_.try_eval_scalar_int(self.tcx, self.typing_env)
}
_ => None,
@ -502,9 +506,14 @@ impl<'tcx> Validator<'_, 'tcx> {
// The RHS is -1 or unknown, so we have to be careful.
// But is the LHS int::MIN?
let lhs_val = match lhs {
Operand::Constant(c) => c
.const_
.try_eval_scalar_int(self.tcx, self.typing_env),
Operand::Constant(c)
if self.should_evaluate_for_promotion_checks(
c.const_,
) =>
{
c.const_
.try_eval_scalar_int(self.tcx, self.typing_env)
}
_ => None,
};
let lhs_min = sz.signed_int_min();
@ -683,6 +692,28 @@ impl<'tcx> Validator<'_, 'tcx> {
// This passed all checks, so let's accept.
Ok(())
}
/// Can we try to evaluate a given constant at this point in compilation? Attempting to evaluate
/// a const block before borrow-checking will result in a query cycle (#150464).
fn should_evaluate_for_promotion_checks(&self, constant: Const<'tcx>) -> bool {
match constant {
// `Const::Ty` is always a `ConstKind::Param` right now and that can never be turned
// into a mir value for promotion
// FIXME(mgca): do we want uses of type_const to be normalized during promotion?
Const::Ty(..) => false,
Const::Val(..) => true,
// Evaluating a MIR constant requires borrow-checking it. For inline consts, as of
// #138499, this means borrow-checking its typeck root. Since borrow-checking the
// typeck root requires promoting its constants, trying to evaluate an inline const here
// will result in a query cycle. To avoid the cycle, we can't evaluate const blocks yet.
// Other kinds of unevaluated's can cause query cycles too when they arise from
// self-reference in user code; e.g. evaluating a constant can require evaluating a
// const function that uses that constant, again requiring evaluation of the constant.
// However, this form of cycle renders both the constant and function unusable in
// general, so we don't need to special-case it here.
Const::Unevaluated(uc, _) => self.tcx.def_kind(uc.def) != DefKind::InlineConst,
}
}
}
fn validate_candidates(

View file

@ -182,6 +182,7 @@
#![feature(staged_api)]
#![feature(stmt_expr_attributes)]
#![feature(strict_provenance_lints)]
#![feature(target_feature_inline_always)]
#![feature(trait_alias)]
#![feature(transparent_unions)]
#![feature(try_blocks)]

View file

@ -30,19 +30,19 @@ impl Drop for TokenStream {
}
impl<S> Encode<S> for TokenStream {
fn encode(self, w: &mut Writer, s: &mut S) {
fn encode(self, w: &mut Buffer, s: &mut S) {
mem::ManuallyDrop::new(self).handle.encode(w, s);
}
}
impl<S> Encode<S> for &TokenStream {
fn encode(self, w: &mut Writer, s: &mut S) {
fn encode(self, w: &mut Buffer, s: &mut S) {
self.handle.encode(w, s);
}
}
impl<S> Decode<'_, '_, S> for TokenStream {
fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
fn decode(r: &mut &[u8], s: &mut S) -> Self {
TokenStream { handle: handle::Handle::decode(r, s) }
}
}
@ -56,23 +56,17 @@ impl !Send for Span {}
impl !Sync for Span {}
impl<S> Encode<S> for Span {
fn encode(self, w: &mut Writer, s: &mut S) {
fn encode(self, w: &mut Buffer, s: &mut S) {
self.handle.encode(w, s);
}
}
impl<S> Decode<'_, '_, S> for Span {
fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
fn decode(r: &mut &[u8], s: &mut S) -> Self {
Span { handle: handle::Handle::decode(r, s) }
}
}
// FIXME(eddyb) generate these impls by pattern-matching on the
// names of methods - also could use the presence of `fn drop`
// to distinguish between 'owned and 'interned, above.
// Alternatively, special "modes" could be listed of types in with_api
// instead of pattern matching on methods, here and in server decl.
impl Clone for TokenStream {
fn clone(&self) -> Self {
Methods::ts_clone(self)
@ -104,10 +98,7 @@ pub(crate) use super::symbol::Symbol;
macro_rules! define_client_side {
(
Methods {
$(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)*
},
$($name:ident),* $(,)?
$(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)*
) => {
impl Methods {
$(pub(crate) fn $method($($arg: $arg_ty),*) $(-> $ret_ty)? {
@ -115,7 +106,7 @@ macro_rules! define_client_side {
let mut buf = bridge.cached_buffer.take();
buf.clear();
api_tags::Method::$method.encode(&mut buf, &mut ());
ApiTags::$method.encode(&mut buf, &mut ());
$($arg.encode(&mut buf, &mut ());)*
buf = bridge.dispatch.call(buf);
@ -130,7 +121,7 @@ macro_rules! define_client_side {
}
}
}
with_api!(self, self, define_client_side);
with_api!(self, define_client_side);
struct Bridge<'a> {
/// Reusable buffer (only `clear`-ed, never shrunk), primarily

View file

@ -13,92 +13,76 @@ use std::ops::{Bound, Range};
use std::sync::Once;
use std::{fmt, marker, mem, panic, thread};
use crate::{Delimiter, Level, Spacing};
use crate::{Delimiter, Level};
/// Higher-order macro describing the server RPC API, allowing automatic
/// generation of type-safe Rust APIs, both client-side and server-side.
///
/// `with_api!(MySelf, my_self, my_macro)` expands to:
/// `with_api!(MySelf, my_macro)` expands to:
/// ```rust,ignore (pseudo-code)
/// my_macro! {
/// Methods {
/// // ...
/// fn lit_character(ch: char) -> MySelf::Literal;
/// // ...
/// fn lit_span(my_self: &MySelf::Literal) -> MySelf::Span;
/// fn lit_set_span(my_self: &mut MySelf::Literal, span: MySelf::Span);
/// },
/// Literal,
/// Span,
/// fn lit_character(ch: char) -> MySelf::Literal;
/// fn lit_span(lit: &MySelf::Literal) -> MySelf::Span;
/// fn lit_set_span(lit: &mut MySelf::Literal, span: MySelf::Span);
/// // ...
/// }
/// ```
///
/// The first two arguments serve to customize the arguments names
/// and argument/return types, to enable several different usecases:
///
/// If `my_self` is just `self`, then each `fn` signature can be used
/// as-is for a method. If it's anything else (`self_` in practice),
/// then the signatures don't have a special `self` argument, and
/// can, therefore, have a different one introduced.
/// The first argument serves to customize the argument/return types,
/// to enable several different usecases:
///
/// If `MySelf` is just `Self`, then the types are only valid inside
/// a trait or a trait impl, where the trait has associated types
/// for each of the API types. If non-associated types are desired,
/// a module name (`self` in practice) can be used instead of `Self`.
macro_rules! with_api {
($S:ident, $self:ident, $m:ident) => {
($S:ident, $m:ident) => {
$m! {
Methods {
fn injected_env_var(var: &str) -> Option<String>;
fn track_env_var(var: &str, value: Option<&str>);
fn track_path(path: &str);
fn literal_from_str(s: &str) -> Result<Literal<$S::Span, $S::Symbol>, ()>;
fn emit_diagnostic(diagnostic: Diagnostic<$S::Span>);
fn injected_env_var(var: &str) -> Option<String>;
fn track_env_var(var: &str, value: Option<&str>);
fn track_path(path: &str);
fn literal_from_str(s: &str) -> Result<Literal<$S::Span, $S::Symbol>, ()>;
fn emit_diagnostic(diagnostic: Diagnostic<$S::Span>);
fn ts_drop(stream: $S::TokenStream);
fn ts_clone(stream: &$S::TokenStream) -> $S::TokenStream;
fn ts_is_empty(stream: &$S::TokenStream) -> bool;
fn ts_expand_expr(stream: &$S::TokenStream) -> Result<$S::TokenStream, ()>;
fn ts_from_str(src: &str) -> $S::TokenStream;
fn ts_to_string(stream: &$S::TokenStream) -> String;
fn ts_from_token_tree(
tree: TokenTree<$S::TokenStream, $S::Span, $S::Symbol>,
) -> $S::TokenStream;
fn ts_concat_trees(
base: Option<$S::TokenStream>,
trees: Vec<TokenTree<$S::TokenStream, $S::Span, $S::Symbol>>,
) -> $S::TokenStream;
fn ts_concat_streams(
base: Option<$S::TokenStream>,
streams: Vec<$S::TokenStream>,
) -> $S::TokenStream;
fn ts_into_trees(
stream: $S::TokenStream
) -> Vec<TokenTree<$S::TokenStream, $S::Span, $S::Symbol>>;
fn ts_drop(stream: $S::TokenStream);
fn ts_clone(stream: &$S::TokenStream) -> $S::TokenStream;
fn ts_is_empty(stream: &$S::TokenStream) -> bool;
fn ts_expand_expr(stream: &$S::TokenStream) -> Result<$S::TokenStream, ()>;
fn ts_from_str(src: &str) -> $S::TokenStream;
fn ts_to_string(stream: &$S::TokenStream) -> String;
fn ts_from_token_tree(
tree: TokenTree<$S::TokenStream, $S::Span, $S::Symbol>,
) -> $S::TokenStream;
fn ts_concat_trees(
base: Option<$S::TokenStream>,
trees: Vec<TokenTree<$S::TokenStream, $S::Span, $S::Symbol>>,
) -> $S::TokenStream;
fn ts_concat_streams(
base: Option<$S::TokenStream>,
streams: Vec<$S::TokenStream>,
) -> $S::TokenStream;
fn ts_into_trees(
stream: $S::TokenStream
) -> Vec<TokenTree<$S::TokenStream, $S::Span, $S::Symbol>>;
fn span_debug(span: $S::Span) -> String;
fn span_parent(span: $S::Span) -> Option<$S::Span>;
fn span_source(span: $S::Span) -> $S::Span;
fn span_byte_range(span: $S::Span) -> Range<usize>;
fn span_start(span: $S::Span) -> $S::Span;
fn span_end(span: $S::Span) -> $S::Span;
fn span_line(span: $S::Span) -> usize;
fn span_column(span: $S::Span) -> usize;
fn span_file(span: $S::Span) -> String;
fn span_local_file(span: $S::Span) -> Option<String>;
fn span_join(span: $S::Span, other: $S::Span) -> Option<$S::Span>;
fn span_subspan(span: $S::Span, start: Bound<usize>, end: Bound<usize>) -> Option<$S::Span>;
fn span_resolved_at(span: $S::Span, at: $S::Span) -> $S::Span;
fn span_source_text(span: $S::Span) -> Option<String>;
fn span_save_span(span: $S::Span) -> usize;
fn span_recover_proc_macro_span(id: usize) -> $S::Span;
fn span_debug(span: $S::Span) -> String;
fn span_parent(span: $S::Span) -> Option<$S::Span>;
fn span_source(span: $S::Span) -> $S::Span;
fn span_byte_range(span: $S::Span) -> Range<usize>;
fn span_start(span: $S::Span) -> $S::Span;
fn span_end(span: $S::Span) -> $S::Span;
fn span_line(span: $S::Span) -> usize;
fn span_column(span: $S::Span) -> usize;
fn span_file(span: $S::Span) -> String;
fn span_local_file(span: $S::Span) -> Option<String>;
fn span_join(span: $S::Span, other: $S::Span) -> Option<$S::Span>;
fn span_subspan(span: $S::Span, start: Bound<usize>, end: Bound<usize>) -> Option<$S::Span>;
fn span_resolved_at(span: $S::Span, at: $S::Span) -> $S::Span;
fn span_source_text(span: $S::Span) -> Option<String>;
fn span_save_span(span: $S::Span) -> usize;
fn span_recover_proc_macro_span(id: usize) -> $S::Span;
fn symbol_normalize_and_validate_ident(string: &str) -> Result<$S::Symbol, ()>;
},
TokenStream,
Span,
Symbol,
fn symbol_normalize_and_validate_ident(string: &str) -> Result<$S::Symbol, ()>;
}
};
}
@ -129,7 +113,7 @@ mod symbol;
use buffer::Buffer;
pub use rpc::PanicMessage;
use rpc::{Decode, Encode, Reader, Writer};
use rpc::{Decode, Encode};
/// Configuration for establishing an active connection between a server and a
/// client. The server creates the bridge config (`run_server` in `server.rs`),
@ -151,26 +135,18 @@ pub struct BridgeConfig<'a> {
impl !Send for BridgeConfig<'_> {}
impl !Sync for BridgeConfig<'_> {}
#[forbid(unsafe_code)]
#[allow(non_camel_case_types)]
mod api_tags {
use super::rpc::{Decode, Encode, Reader, Writer};
macro_rules! declare_tags {
(
Methods {
$(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)*
},
$($name:ident),* $(,)?
) => {
pub(super) enum Method {
$($method),*
}
rpc_encode_decode!(enum Method { $($method),* });
macro_rules! declare_tags {
(
$(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)*
) => {
#[allow(non_camel_case_types)]
pub(super) enum ApiTags {
$($method),*
}
rpc_encode_decode!(enum ApiTags { $($method),* });
}
with_api!(self, self, declare_tags);
}
with_api!(self, declare_tags);
/// Helper to wrap associated types to allow trait impl dispatch.
/// That is, normally a pair of impls for `T::Foo` and `T::Bar`
@ -179,11 +155,6 @@ mod api_tags {
trait Mark {
type Unmarked;
fn mark(unmarked: Self::Unmarked) -> Self;
}
/// Unwrap types wrapped by `Mark::mark` (see `Mark` for details).
trait Unmark {
type Unmarked;
fn unmark(self) -> Self::Unmarked;
}
@ -198,25 +169,19 @@ impl<T, M> Mark for Marked<T, M> {
fn mark(unmarked: Self::Unmarked) -> Self {
Marked { value: unmarked, _marker: marker::PhantomData }
}
}
impl<T, M> Unmark for Marked<T, M> {
type Unmarked = T;
fn unmark(self) -> Self::Unmarked {
self.value
}
}
impl<'a, T, M> Unmark for &'a Marked<T, M> {
impl<'a, T, M> Mark for &'a Marked<T, M> {
type Unmarked = &'a T;
fn mark(_: Self::Unmarked) -> Self {
unreachable!()
}
fn unmark(self) -> Self::Unmarked {
&self.value
}
}
impl<'a, T, M> Unmark for &'a mut Marked<T, M> {
type Unmarked = &'a mut T;
fn unmark(self) -> Self::Unmarked {
&mut self.value
}
}
impl<T: Mark> Mark for Vec<T> {
type Unmarked = Vec<T::Unmarked>;
@ -224,9 +189,6 @@ impl<T: Mark> Mark for Vec<T> {
// Should be a no-op due to std's in-place collect optimizations.
unmarked.into_iter().map(T::mark).collect()
}
}
impl<T: Unmark> Unmark for Vec<T> {
type Unmarked = Vec<T::Unmarked>;
fn unmark(self) -> Self::Unmarked {
// Should be a no-op due to std's in-place collect optimizations.
self.into_iter().map(T::unmark).collect()
@ -241,9 +203,6 @@ macro_rules! mark_noop {
fn mark(unmarked: Self::Unmarked) -> Self {
unmarked
}
}
impl Unmark for $ty {
type Unmarked = Self;
fn unmark(self) -> Self::Unmarked {
self
}
@ -254,8 +213,6 @@ macro_rules! mark_noop {
mark_noop! {
(),
bool,
char,
&'_ [u8],
&'_ str,
String,
u8,
@ -263,7 +220,6 @@ mark_noop! {
Delimiter,
LitKind,
Level,
Spacing,
}
rpc_encode_decode!(
@ -282,12 +238,6 @@ rpc_encode_decode!(
Help,
}
);
rpc_encode_decode!(
enum Spacing {
Alone,
Joint,
}
);
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum LitKind {
@ -333,13 +283,9 @@ macro_rules! mark_compound {
$($field: Mark::mark(unmarked.$field)),*
}
}
}
impl<$($T: Unmark),+> Unmark for $name <$($T),+> {
type Unmarked = $name <$($T::Unmarked),+>;
fn unmark(self) -> Self::Unmarked {
$name {
$($field: Unmark::unmark(self.$field)),*
$($field: Mark::unmark(self.$field)),*
}
}
}
@ -354,14 +300,10 @@ macro_rules! mark_compound {
})*
}
}
}
impl<$($T: Unmark),+> Unmark for $name <$($T),+> {
type Unmarked = $name <$($T::Unmarked),+>;
fn unmark(self) -> Self::Unmarked {
match self {
$($name::$variant $(($field))? => {
$name::$variant $((Unmark::unmark($field)))?
$name::$variant $((Mark::unmark($field)))?
})*
}
}

View file

@ -4,28 +4,26 @@ use std::any::Any;
use std::io::Write;
use std::num::NonZero;
pub(super) type Writer = super::buffer::Buffer;
use super::buffer::Buffer;
pub(super) trait Encode<S>: Sized {
fn encode(self, w: &mut Writer, s: &mut S);
fn encode(self, w: &mut Buffer, s: &mut S);
}
pub(super) type Reader<'a> = &'a [u8];
pub(super) trait Decode<'a, 's, S>: Sized {
fn decode(r: &mut Reader<'a>, s: &'s mut S) -> Self;
fn decode(r: &mut &'a [u8], s: &'s mut S) -> Self;
}
macro_rules! rpc_encode_decode {
(le $ty:ty) => {
impl<S> Encode<S> for $ty {
fn encode(self, w: &mut Writer, _: &mut S) {
fn encode(self, w: &mut Buffer, _: &mut S) {
w.extend_from_array(&self.to_le_bytes());
}
}
impl<S> Decode<'_, '_, S> for $ty {
fn decode(r: &mut Reader<'_>, _: &mut S) -> Self {
fn decode(r: &mut &[u8], _: &mut S) -> Self {
const N: usize = size_of::<$ty>();
let mut bytes = [0; N];
@ -38,7 +36,7 @@ macro_rules! rpc_encode_decode {
};
(struct $name:ident $(<$($T:ident),+>)? { $($field:ident),* $(,)? }) => {
impl<S, $($($T: Encode<S>),+)?> Encode<S> for $name $(<$($T),+>)? {
fn encode(self, w: &mut Writer, s: &mut S) {
fn encode(self, w: &mut Buffer, s: &mut S) {
$(self.$field.encode(w, s);)*
}
}
@ -46,7 +44,7 @@ macro_rules! rpc_encode_decode {
impl<'a, S, $($($T: for<'s> Decode<'a, 's, S>),+)?> Decode<'a, '_, S>
for $name $(<$($T),+>)?
{
fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
fn decode(r: &mut &'a [u8], s: &mut S) -> Self {
$name {
$($field: Decode::decode(r, s)),*
}
@ -55,10 +53,12 @@ macro_rules! rpc_encode_decode {
};
(enum $name:ident $(<$($T:ident),+>)? { $($variant:ident $(($field:ident))*),* $(,)? }) => {
impl<S, $($($T: Encode<S>),+)?> Encode<S> for $name $(<$($T),+>)? {
fn encode(self, w: &mut Writer, s: &mut S) {
fn encode(self, w: &mut Buffer, s: &mut S) {
// HACK(eddyb): `Tag` enum duplicated between the
// two impls as there's no other place to stash it.
#[repr(u8)] enum Tag { $($variant),* }
#[allow(non_camel_case_types)]
#[repr(u8)]
enum Tag { $($variant),* }
match self {
$($name::$variant $(($field))* => {
@ -72,10 +72,10 @@ macro_rules! rpc_encode_decode {
impl<'a, S, $($($T: for<'s> Decode<'a, 's, S>),+)?> Decode<'a, '_, S>
for $name $(<$($T),+>)?
{
fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
fn decode(r: &mut &'a [u8], s: &mut S) -> Self {
// HACK(eddyb): `Tag` enum duplicated between the
// two impls as there's no other place to stash it.
#[allow(non_upper_case_globals)]
#[allow(non_upper_case_globals, non_camel_case_types)]
mod tag {
#[repr(u8)] enum Tag { $($variant),* }
@ -95,21 +95,21 @@ macro_rules! rpc_encode_decode {
}
impl<S> Encode<S> for () {
fn encode(self, _: &mut Writer, _: &mut S) {}
fn encode(self, _: &mut Buffer, _: &mut S) {}
}
impl<S> Decode<'_, '_, S> for () {
fn decode(_: &mut Reader<'_>, _: &mut S) -> Self {}
fn decode(_: &mut &[u8], _: &mut S) -> Self {}
}
impl<S> Encode<S> for u8 {
fn encode(self, w: &mut Writer, _: &mut S) {
fn encode(self, w: &mut Buffer, _: &mut S) {
w.push(self);
}
}
impl<S> Decode<'_, '_, S> for u8 {
fn decode(r: &mut Reader<'_>, _: &mut S) -> Self {
fn decode(r: &mut &[u8], _: &mut S) -> Self {
let x = r[0];
*r = &r[1..];
x
@ -120,13 +120,13 @@ rpc_encode_decode!(le u32);
rpc_encode_decode!(le usize);
impl<S> Encode<S> for bool {
fn encode(self, w: &mut Writer, s: &mut S) {
fn encode(self, w: &mut Buffer, s: &mut S) {
(self as u8).encode(w, s);
}
}
impl<S> Decode<'_, '_, S> for bool {
fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
fn decode(r: &mut &[u8], s: &mut S) -> Self {
match u8::decode(r, s) {
0 => false,
1 => true,
@ -135,32 +135,20 @@ impl<S> Decode<'_, '_, S> for bool {
}
}
impl<S> Encode<S> for char {
fn encode(self, w: &mut Writer, s: &mut S) {
(self as u32).encode(w, s);
}
}
impl<S> Decode<'_, '_, S> for char {
fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
char::from_u32(u32::decode(r, s)).unwrap()
}
}
impl<S> Encode<S> for NonZero<u32> {
fn encode(self, w: &mut Writer, s: &mut S) {
fn encode(self, w: &mut Buffer, s: &mut S) {
self.get().encode(w, s);
}
}
impl<S> Decode<'_, '_, S> for NonZero<u32> {
fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
fn decode(r: &mut &[u8], s: &mut S) -> Self {
Self::new(u32::decode(r, s)).unwrap()
}
}
impl<S, A: Encode<S>, B: Encode<S>> Encode<S> for (A, B) {
fn encode(self, w: &mut Writer, s: &mut S) {
fn encode(self, w: &mut Buffer, s: &mut S) {
self.0.encode(w, s);
self.1.encode(w, s);
}
@ -169,53 +157,42 @@ impl<S, A: Encode<S>, B: Encode<S>> Encode<S> for (A, B) {
impl<'a, S, A: for<'s> Decode<'a, 's, S>, B: for<'s> Decode<'a, 's, S>> Decode<'a, '_, S>
for (A, B)
{
fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
fn decode(r: &mut &'a [u8], s: &mut S) -> Self {
(Decode::decode(r, s), Decode::decode(r, s))
}
}
impl<S> Encode<S> for &[u8] {
fn encode(self, w: &mut Writer, s: &mut S) {
self.len().encode(w, s);
w.write_all(self).unwrap();
}
}
impl<'a, S> Decode<'a, '_, S> for &'a [u8] {
fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
let len = usize::decode(r, s);
let xs = &r[..len];
*r = &r[len..];
xs
}
}
impl<S> Encode<S> for &str {
fn encode(self, w: &mut Writer, s: &mut S) {
self.as_bytes().encode(w, s);
fn encode(self, w: &mut Buffer, s: &mut S) {
let bytes = self.as_bytes();
bytes.len().encode(w, s);
w.write_all(bytes).unwrap();
}
}
impl<'a, S> Decode<'a, '_, S> for &'a str {
fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
str::from_utf8(<&[u8]>::decode(r, s)).unwrap()
fn decode(r: &mut &'a [u8], s: &mut S) -> Self {
let len = usize::decode(r, s);
let xs = &r[..len];
*r = &r[len..];
str::from_utf8(xs).unwrap()
}
}
impl<S> Encode<S> for String {
fn encode(self, w: &mut Writer, s: &mut S) {
fn encode(self, w: &mut Buffer, s: &mut S) {
self[..].encode(w, s);
}
}
impl<S> Decode<'_, '_, S> for String {
fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
fn decode(r: &mut &[u8], s: &mut S) -> Self {
<&str>::decode(r, s).to_string()
}
}
impl<S, T: Encode<S>> Encode<S> for Vec<T> {
fn encode(self, w: &mut Writer, s: &mut S) {
fn encode(self, w: &mut Buffer, s: &mut S) {
self.len().encode(w, s);
for x in self {
x.encode(w, s);
@ -224,7 +201,7 @@ impl<S, T: Encode<S>> Encode<S> for Vec<T> {
}
impl<'a, S, T: for<'s> Decode<'a, 's, S>> Decode<'a, '_, S> for Vec<T> {
fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
fn decode(r: &mut &'a [u8], s: &mut S) -> Self {
let len = usize::decode(r, s);
let mut vec = Vec::with_capacity(len);
for _ in 0..len {
@ -278,13 +255,13 @@ impl PanicMessage {
}
impl<S> Encode<S> for PanicMessage {
fn encode(self, w: &mut Writer, s: &mut S) {
fn encode(self, w: &mut Buffer, s: &mut S) {
self.as_str().encode(w, s);
}
}
impl<S> Decode<'_, '_, S> for PanicMessage {
fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
fn decode(r: &mut &[u8], s: &mut S) -> Self {
match Option::<String>::decode(r, s) {
Some(s) => PanicMessage::String(s),
None => PanicMessage::Unknown,

View file

@ -5,12 +5,12 @@ use std::marker::PhantomData;
use super::*;
pub(super) struct HandleStore<S: Types> {
token_stream: handle::OwnedStore<Marked<S::TokenStream, client::TokenStream>>,
span: handle::InternedStore<Marked<S::Span, client::Span>>,
pub(super) struct HandleStore<S: Server> {
token_stream: handle::OwnedStore<MarkedTokenStream<S>>,
span: handle::InternedStore<MarkedSpan<S>>,
}
impl<S: Types> HandleStore<S> {
impl<S: Server> HandleStore<S> {
fn new(handle_counters: &'static client::HandleCounters) -> Self {
HandleStore {
token_stream: handle::OwnedStore::new(&handle_counters.token_stream),
@ -19,52 +19,54 @@ impl<S: Types> HandleStore<S> {
}
}
impl<S: Types> Encode<HandleStore<S>> for Marked<S::TokenStream, client::TokenStream> {
fn encode(self, w: &mut Writer, s: &mut HandleStore<S>) {
pub(super) type MarkedTokenStream<S> = Marked<<S as Server>::TokenStream, client::TokenStream>;
pub(super) type MarkedSpan<S> = Marked<<S as Server>::Span, client::Span>;
pub(super) type MarkedSymbol<S> = Marked<<S as Server>::Symbol, client::Symbol>;
impl<S: Server> Encode<HandleStore<S>> for MarkedTokenStream<S> {
fn encode(self, w: &mut Buffer, s: &mut HandleStore<S>) {
s.token_stream.alloc(self).encode(w, s);
}
}
impl<S: Types> Decode<'_, '_, HandleStore<S>> for Marked<S::TokenStream, client::TokenStream> {
fn decode(r: &mut Reader<'_>, s: &mut HandleStore<S>) -> Self {
impl<S: Server> Decode<'_, '_, HandleStore<S>> for MarkedTokenStream<S> {
fn decode(r: &mut &[u8], s: &mut HandleStore<S>) -> Self {
s.token_stream.take(handle::Handle::decode(r, &mut ()))
}
}
impl<'s, S: Types> Decode<'_, 's, HandleStore<S>>
for &'s Marked<S::TokenStream, client::TokenStream>
{
fn decode(r: &mut Reader<'_>, s: &'s mut HandleStore<S>) -> Self {
impl<'s, S: Server> Decode<'_, 's, HandleStore<S>> for &'s MarkedTokenStream<S> {
fn decode(r: &mut &[u8], s: &'s mut HandleStore<S>) -> Self {
&s.token_stream[handle::Handle::decode(r, &mut ())]
}
}
impl<S: Types> Encode<HandleStore<S>> for Marked<S::Span, client::Span> {
fn encode(self, w: &mut Writer, s: &mut HandleStore<S>) {
impl<S: Server> Encode<HandleStore<S>> for MarkedSpan<S> {
fn encode(self, w: &mut Buffer, s: &mut HandleStore<S>) {
s.span.alloc(self).encode(w, s);
}
}
impl<S: Types> Decode<'_, '_, HandleStore<S>> for Marked<S::Span, client::Span> {
fn decode(r: &mut Reader<'_>, s: &mut HandleStore<S>) -> Self {
impl<S: Server> Decode<'_, '_, HandleStore<S>> for MarkedSpan<S> {
fn decode(r: &mut &[u8], s: &mut HandleStore<S>) -> Self {
s.span.copy(handle::Handle::decode(r, &mut ()))
}
}
pub trait Types {
type TokenStream: 'static + Clone;
type Span: 'static + Copy + Eq + Hash;
type Symbol: 'static;
struct Dispatcher<S: Server> {
handle_store: HandleStore<S>,
server: S,
}
macro_rules! declare_server_traits {
macro_rules! define_server_dispatcher_impl {
(
Methods {
$(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)*
},
$($name:ident),* $(,)?
$(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)*
) => {
pub trait Server: Types {
pub trait Server {
type TokenStream: 'static + Clone;
type Span: 'static + Copy + Eq + Hash;
type Symbol: 'static;
fn globals(&mut self) -> ExpnGlobals<Self::Span>;
/// Intern a symbol received from RPC
@ -75,39 +77,28 @@ macro_rules! declare_server_traits {
$(fn $method(&mut self, $($arg: $arg_ty),*) $(-> $ret_ty)?;)*
}
}
}
with_api!(Self, self_, declare_server_traits);
struct Dispatcher<S: Types> {
handle_store: HandleStore<S>,
server: S,
}
macro_rules! define_dispatcher_impl {
(
Methods {
$(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)*
},
$($name:ident),* $(,)?
) => {
// FIXME(eddyb) `pub` only for `ExecutionStrategy` below.
pub trait DispatcherTrait {
// HACK(eddyb) these are here to allow `Self::$name` to work below.
$(type $name;)*
type TokenStream;
type Span;
type Symbol;
fn dispatch(&mut self, buf: Buffer) -> Buffer;
}
impl<S: Server> DispatcherTrait for Dispatcher<S> {
$(type $name = Marked<S::$name, client::$name>;)*
type TokenStream = MarkedTokenStream<S>;
type Span = MarkedSpan<S>;
type Symbol = MarkedSymbol<S>;
fn dispatch(&mut self, mut buf: Buffer) -> Buffer {
let Dispatcher { handle_store, server } = self;
let mut reader = &buf[..];
match api_tags::Method::decode(&mut reader, &mut ()) {
$(api_tags::Method::$method => {
match ApiTags::decode(&mut reader, &mut ()) {
$(ApiTags::$method => {
let mut call_method = || {
$(let $arg = <$arg_ty>::decode(&mut reader, handle_store).unmark();)*
let r = server.$method($($arg),*);
@ -136,7 +127,7 @@ macro_rules! define_dispatcher_impl {
}
}
}
with_api!(Self, self_, define_dispatcher_impl);
with_api!(Self, define_server_dispatcher_impl);
pub trait ExecutionStrategy {
fn run_bridge_and_client(
@ -303,7 +294,7 @@ fn run_server<
let globals = dispatcher.server.globals();
let mut buf = Buffer::new();
(<ExpnGlobals<Marked<S::Span, client::Span>> as Mark>::mark(globals), input)
(<ExpnGlobals<MarkedSpan<S>> as Mark>::mark(globals), input)
.encode(&mut buf, &mut dispatcher.handle_store);
buf = strategy.run_bridge_and_client(&mut dispatcher, buf, run_client, force_show_panics);
@ -328,13 +319,11 @@ impl client::Client<crate::TokenStream, crate::TokenStream> {
strategy,
handle_counters,
server,
<Marked<S::TokenStream, client::TokenStream>>::mark(input),
<MarkedTokenStream<S>>::mark(input),
run,
force_show_panics,
)
.map(|s| {
<Option<Marked<S::TokenStream, client::TokenStream>>>::unmark(s).unwrap_or_default()
})
.map(|s| <Option<MarkedTokenStream<S>>>::unmark(s).unwrap_or_default())
}
}
@ -356,15 +345,10 @@ impl client::Client<(crate::TokenStream, crate::TokenStream), crate::TokenStream
strategy,
handle_counters,
server,
(
<Marked<S::TokenStream, client::TokenStream>>::mark(input),
<Marked<S::TokenStream, client::TokenStream>>::mark(input2),
),
(<MarkedTokenStream<S>>::mark(input), <MarkedTokenStream<S>>::mark(input2)),
run,
force_show_panics,
)
.map(|s| {
<Option<Marked<S::TokenStream, client::TokenStream>>>::unmark(s).unwrap_or_default()
})
.map(|s| <Option<MarkedTokenStream<S>>>::unmark(s).unwrap_or_default())
}
}

View file

@ -94,25 +94,25 @@ impl fmt::Display for Symbol {
}
impl<S> Encode<S> for Symbol {
fn encode(self, w: &mut Writer, s: &mut S) {
fn encode(self, w: &mut Buffer, s: &mut S) {
self.with(|sym| sym.encode(w, s))
}
}
impl<S: server::Server> Decode<'_, '_, server::HandleStore<S>> for Marked<S::Symbol, Symbol> {
fn decode(r: &mut Reader<'_>, s: &mut server::HandleStore<S>) -> Self {
impl<S: server::Server> Decode<'_, '_, server::HandleStore<S>> for server::MarkedSymbol<S> {
fn decode(r: &mut &[u8], s: &mut server::HandleStore<S>) -> Self {
Mark::mark(S::intern_symbol(<&str>::decode(r, s)))
}
}
impl<S: server::Server> Encode<server::HandleStore<S>> for Marked<S::Symbol, Symbol> {
fn encode(self, w: &mut Writer, s: &mut server::HandleStore<S>) {
impl<S: server::Server> Encode<server::HandleStore<S>> for server::MarkedSymbol<S> {
fn encode(self, w: &mut Buffer, s: &mut server::HandleStore<S>) {
S::with_symbol_string(&self.unmark(), |sym| sym.encode(w, s))
}
}
impl<S> Decode<'_, '_, S> for Symbol {
fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
fn decode(r: &mut &[u8], s: &mut S) -> Self {
Symbol::new(<&str>::decode(r, s))
}
}

View file

@ -309,6 +309,7 @@
#![feature(staged_api)]
#![feature(stmt_expr_attributes)]
#![feature(strict_provenance_lints)]
#![feature(target_feature_inline_always)]
#![feature(thread_local)]
#![feature(try_blocks)]
#![feature(try_trait_v2)]

View file

@ -580,7 +580,8 @@ mod uefi_fs {
use crate::path::Path;
use crate::ptr::NonNull;
use crate::sys::pal::helpers::{self, UefiBox};
use crate::sys::time::{self, SystemTime};
use crate::sys::pal::system_time;
use crate::sys::time::SystemTime;
pub(crate) struct File {
protocol: NonNull<file::Protocol>,
@ -879,7 +880,7 @@ mod uefi_fs {
/// conversion to SystemTime, we use the current time to get the timezone in such cases.
pub(crate) fn uefi_to_systemtime(mut time: r_efi::efi::Time) -> Option<SystemTime> {
time.timezone = if time.timezone == r_efi::efi::UNSPECIFIED_TIMEZONE {
time::system_time_internal::now().timezone
system_time::now().timezone
} else {
time.timezone
};
@ -888,7 +889,7 @@ mod uefi_fs {
/// Convert to UEFI Time with the current timezone.
pub(crate) fn systemtime_to_uefi(time: SystemTime) -> r_efi::efi::Time {
let now = time::system_time_internal::now();
let now = system_time::now();
time.to_uefi_loose(now.timezone, now.daylight)
}

View file

@ -1822,7 +1822,7 @@ impl File {
_ => {
#[cfg(all(target_os = "linux", target_env = "gnu", target_pointer_width = "32", not(target_arch = "riscv32")))]
{
use crate::sys::{time::__timespec64, weak::weak};
use crate::sys::pal::{time::__timespec64, weak::weak};
// Added in glibc 2.34
weak!(
@ -2258,7 +2258,7 @@ fn set_times_impl(p: &CStr, times: FileTimes, follow_symlinks: bool) -> io::Resu
let flags = if follow_symlinks { 0 } else { libc::AT_SYMLINK_NOFOLLOW };
#[cfg(all(target_os = "linux", target_env = "gnu", target_pointer_width = "32", not(target_arch = "riscv32")))]
{
use crate::sys::{time::__timespec64, weak::weak};
use crate::sys::pal::{time::__timespec64, weak::weak};
// Added in glibc 2.34
weak!(

View file

@ -26,6 +26,7 @@ pub mod stdio;
pub mod sync;
pub mod thread;
pub mod thread_local;
pub mod time;
// FIXME(117276): remove this, move feature implementations into individual
// submodules.

View file

@ -9,10 +9,9 @@ use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut};
use crate::net::{Shutdown, SocketAddr};
use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, RawFd};
use crate::sys::fd::FileDesc;
use crate::sys::time::Instant;
use crate::sys::{AsInner, FromInner, IntoInner};
pub use crate::sys::{cvt, cvt_r};
use crate::time::Duration;
use crate::time::{Duration, Instant};
use crate::{cmp, mem};
#[expect(non_camel_case_types)]

View file

@ -1,35 +1,32 @@
#![allow(dead_code)]
use hermit_abi::{self, timespec};
use core::hash::{Hash, Hasher};
use super::hermit_abi::{self, CLOCK_MONOTONIC, CLOCK_REALTIME, timespec};
use crate::cmp::Ordering;
use crate::ops::{Add, AddAssign, Sub, SubAssign};
use crate::hash::{Hash, Hasher};
use crate::time::Duration;
const NSEC_PER_SEC: i32 = 1_000_000_000;
#[derive(Copy, Clone, Debug)]
struct Timespec {
t: timespec,
pub struct Timespec {
pub t: timespec,
}
impl Timespec {
const MAX: Timespec = Self::new(i64::MAX, 1_000_000_000 - 1);
pub const MAX: Timespec = Self::new(i64::MAX, 1_000_000_000 - 1);
const MIN: Timespec = Self::new(i64::MIN, 0);
pub const MIN: Timespec = Self::new(i64::MIN, 0);
const fn zero() -> Timespec {
pub const fn zero() -> Timespec {
Timespec { t: timespec { tv_sec: 0, tv_nsec: 0 } }
}
const fn new(tv_sec: i64, tv_nsec: i32) -> Timespec {
pub const fn new(tv_sec: i64, tv_nsec: i32) -> Timespec {
assert!(tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC);
// SAFETY: The assert above checks tv_nsec is within the valid range
Timespec { t: timespec { tv_sec, tv_nsec } }
}
fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> {
pub fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> {
fn sub_ge_to_unsigned(a: i64, b: i64) -> u64 {
debug_assert!(a >= b);
a.wrapping_sub(b).cast_unsigned()
@ -57,7 +54,7 @@ impl Timespec {
}
}
fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> {
pub fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> {
let mut secs = self.t.tv_sec.checked_add_unsigned(other.as_secs())?;
// Nano calculations can't overflow because nanos are <1B which fit
@ -70,7 +67,7 @@ impl Timespec {
Some(Timespec { t: timespec { tv_sec: secs, tv_nsec: nsec as _ } })
}
fn checked_sub_duration(&self, other: &Duration) -> Option<Timespec> {
pub fn checked_sub_duration(&self, other: &Duration) -> Option<Timespec> {
let mut secs = self.t.tv_sec.checked_sub_unsigned(other.as_secs())?;
// Similar to above, nanos can't overflow.
@ -111,132 +108,3 @@ impl Hash for Timespec {
self.t.tv_nsec.hash(state);
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct Instant(Timespec);
impl Instant {
pub fn now() -> Instant {
let mut time: Timespec = Timespec::zero();
let _ = unsafe { hermit_abi::clock_gettime(CLOCK_MONOTONIC, &raw mut time.t) };
Instant(time)
}
#[stable(feature = "time2", since = "1.8.0")]
pub fn elapsed(&self) -> Duration {
Instant::now() - *self
}
pub fn duration_since(&self, earlier: Instant) -> Duration {
self.checked_duration_since(earlier).unwrap_or_default()
}
pub fn checked_duration_since(&self, earlier: Instant) -> Option<Duration> {
self.checked_sub_instant(&earlier)
}
pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
self.0.sub_timespec(&other.0).ok()
}
pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
Some(Instant(self.0.checked_add_duration(other)?))
}
pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
Some(Instant(self.0.checked_sub_duration(other)?))
}
pub fn checked_add(&self, duration: Duration) -> Option<Instant> {
self.0.checked_add_duration(&duration).map(Instant)
}
pub fn checked_sub(&self, duration: Duration) -> Option<Instant> {
self.0.checked_sub_duration(&duration).map(Instant)
}
}
impl Add<Duration> for Instant {
type Output = Instant;
/// # Panics
///
/// This function may panic if the resulting point in time cannot be represented by the
/// underlying data structure. See [`Instant::checked_add`] for a version without panic.
fn add(self, other: Duration) -> Instant {
self.checked_add(other).expect("overflow when adding duration to instant")
}
}
impl AddAssign<Duration> for Instant {
fn add_assign(&mut self, other: Duration) {
*self = *self + other;
}
}
impl Sub<Duration> for Instant {
type Output = Instant;
fn sub(self, other: Duration) -> Instant {
self.checked_sub(other).expect("overflow when subtracting duration from instant")
}
}
impl SubAssign<Duration> for Instant {
fn sub_assign(&mut self, other: Duration) {
*self = *self - other;
}
}
impl Sub<Instant> for Instant {
type Output = Duration;
/// Returns the amount of time elapsed from another instant to this one,
/// or zero duration if that instant is later than this one.
///
/// # Panics
///
/// Previous Rust versions panicked when `other` was later than `self`. Currently this
/// method saturates. Future versions may reintroduce the panic in some circumstances.
/// See [Monotonicity].
///
/// [Monotonicity]: Instant#monotonicity
fn sub(self, other: Instant) -> Duration {
self.duration_since(other)
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct SystemTime(Timespec);
pub const UNIX_EPOCH: SystemTime = SystemTime(Timespec::zero());
impl SystemTime {
pub const MAX: SystemTime = SystemTime(Timespec::MAX);
pub const MIN: SystemTime = SystemTime(Timespec::MIN);
pub fn new(tv_sec: i64, tv_nsec: i32) -> SystemTime {
SystemTime(Timespec::new(tv_sec, tv_nsec))
}
pub fn now() -> SystemTime {
let mut time: Timespec = Timespec::zero();
let _ = unsafe { hermit_abi::clock_gettime(CLOCK_REALTIME, &raw mut time.t) };
SystemTime(time)
}
pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
self.0.sub_timespec(&other.0)
}
pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
Some(SystemTime(self.0.checked_add_duration(other)?))
}
pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
Some(SystemTime(self.0.checked_sub_duration(other)?))
}
}

View file

@ -3,38 +3,16 @@ use super::error::expect_success;
use crate::mem::MaybeUninit;
use crate::time::Duration;
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct Instant(abi::SYSTIM);
#[cfg(test)]
mod tests;
impl Instant {
pub fn now() -> Instant {
// Safety: The provided pointer is valid
unsafe {
let mut out = MaybeUninit::uninit();
expect_success(abi::get_tim(out.as_mut_ptr()), &"get_tim");
Instant(out.assume_init())
}
}
pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
self.0.checked_sub(other.0).map(|ticks| {
// `SYSTIM` is measured in microseconds
Duration::from_micros(ticks)
})
}
pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
// `SYSTIM` is measured in microseconds
let ticks = other.as_micros();
Some(Instant(self.0.checked_add(ticks.try_into().ok()?)?))
}
pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
// `SYSTIM` is measured in microseconds
let ticks = other.as_micros();
Some(Instant(self.0.checked_sub(ticks.try_into().ok()?)?))
#[inline]
pub fn get_tim() -> abi::SYSTIM {
// Safety: The provided pointer is valid
unsafe {
let mut out = MaybeUninit::uninit();
expect_success(abi::get_tim(out.as_mut_ptr()), &"get_tim");
out.assume_init()
}
}
@ -98,7 +76,7 @@ pub fn with_tmos_strong(dur: Duration, mut f: impl FnMut(abi::TMO) -> abi::ER) -
// a problem in practice. (`u64::MAX` μs ≈ 584942 years)
let ticks = dur.as_micros().min(abi::SYSTIM::MAX as u128) as abi::SYSTIM;
let start = Instant::now().0;
let start = get_tim();
let mut elapsed = 0;
let mut er = abi::E_TMOUT;
while elapsed <= ticks {
@ -106,11 +84,8 @@ pub fn with_tmos_strong(dur: Duration, mut f: impl FnMut(abi::TMO) -> abi::ER) -
if er != abi::E_TMOUT {
break;
}
elapsed = Instant::now().0.wrapping_sub(start);
elapsed = get_tim().wrapping_sub(start);
}
er
}
#[cfg(test)]
mod tests;

View file

@ -1,7 +1,6 @@
#![allow(unsafe_op_in_unsafe_fn)]
pub mod os;
pub mod time;
pub use moto_rt::futex;

View file

@ -1 +0,0 @@
pub use moto_rt::time::{Instant, SystemTime, UNIX_EPOCH};

View file

@ -12,7 +12,6 @@ pub mod abi;
mod libunwind_integration;
pub mod os;
pub mod thread_parking;
pub mod time;
pub mod waitqueue;
// SAFETY: must be called only once during runtime initialization.

View file

@ -21,7 +21,6 @@ pub mod itron {
pub(crate) mod error;
pub mod os;
pub use self::itron::thread_parking;
pub mod time;
// SAFETY: must be called only once during runtime initialization.
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.

View file

@ -7,7 +7,6 @@
#![allow(dead_code)]
pub mod os;
#[allow(non_upper_case_globals)]
#[path = "../unix/time.rs"]
pub mod time;

View file

@ -5,7 +5,5 @@
mod common;
#[path = "../unsupported/os.rs"]
pub mod os;
#[path = "../unsupported/time.rs"]
pub mod time;
pub use common::*;

View file

@ -15,7 +15,7 @@
pub mod helpers;
pub mod os;
pub mod time;
pub mod system_time;
#[cfg(test)]
mod tests;

View file

@ -0,0 +1,151 @@
use r_efi::efi::{RuntimeServices, Time};
use super::helpers;
use crate::mem::MaybeUninit;
use crate::ptr::NonNull;
use crate::time::Duration;
const SECS_IN_MINUTE: u64 = 60;
const SECS_IN_HOUR: u64 = SECS_IN_MINUTE * 60;
const SECS_IN_DAY: u64 = SECS_IN_HOUR * 24;
const SYSTEMTIME_TIMEZONE: i64 = -1440 * SECS_IN_MINUTE as i64;
pub(crate) fn now() -> Time {
let runtime_services: NonNull<RuntimeServices> =
helpers::runtime_services().expect("Runtime services are not available");
let mut t: MaybeUninit<Time> = MaybeUninit::uninit();
let r =
unsafe { ((*runtime_services.as_ptr()).get_time)(t.as_mut_ptr(), crate::ptr::null_mut()) };
if r.is_error() {
panic!("time not implemented on this platform");
}
unsafe { t.assume_init() }
}
/// This algorithm is a modified form of the one described in the post
/// https://blog.reverberate.org/2020/05/12/optimizing-date-algorithms.html
///
/// The changes are to use 1900-01-01-00:00:00 with timezone -1440 as anchor instead of UNIX
/// epoch used in the original algorithm.
pub(crate) const fn from_uefi(t: &Time) -> Option<Duration> {
if !(t.month <= 12
&& t.month != 0
&& t.year >= 1900
&& t.year <= 9999
&& t.day <= 31
&& t.day != 0
&& t.second < 60
&& t.minute <= 60
&& t.hour < 24
&& t.nanosecond < 1_000_000_000
&& ((t.timezone <= 1440 && t.timezone >= -1440)
|| t.timezone == r_efi::efi::UNSPECIFIED_TIMEZONE))
{
return None;
}
const YEAR_BASE: u32 = 4800; /* Before min year, multiple of 400. */
// Calculate the number of days since 1/1/1900. This is the earliest supported date in UEFI
// time.
// Use 1 March as the start
let (m_adj, overflow): (u32, bool) = (t.month as u32).overflowing_sub(3);
let (carry, adjust): (u32, u32) = if overflow { (1, 12) } else { (0, 0) };
let y_adj: u32 = (t.year as u32) + YEAR_BASE - carry;
let month_days: u32 = (m_adj.wrapping_add(adjust) * 62719 + 769) / 2048;
let leap_days: u32 = y_adj / 4 - y_adj / 100 + y_adj / 400;
let days: u32 = y_adj * 365 + leap_days + month_days + (t.day as u32 - 1) - 2447065;
let localtime_epoch: u64 = (days as u64) * SECS_IN_DAY
+ (t.second as u64)
+ (t.minute as u64) * SECS_IN_MINUTE
+ (t.hour as u64) * SECS_IN_HOUR;
let normalized_timezone = if t.timezone == r_efi::efi::UNSPECIFIED_TIMEZONE {
-SYSTEMTIME_TIMEZONE
} else {
(t.timezone as i64) * SECS_IN_MINUTE as i64 - SYSTEMTIME_TIMEZONE
};
// Calculate the offset from 1/1/1900 at timezone -1440 min
let epoch = localtime_epoch.checked_add_signed(normalized_timezone).unwrap();
Some(Duration::new(epoch, t.nanosecond))
}
/// This algorithm is a modified version of the one described in the post:
/// https://howardhinnant.github.io/date_algorithms.html#clive_from_days
///
/// The changes are to use 1900-01-01-00:00:00 with timezone -1440 as anchor instead of UNIX
/// epoch used in the original algorithm.
pub(crate) const fn to_uefi(dur: &Duration, timezone: i16, daylight: u8) -> Result<Time, i16> {
const MIN_IN_HOUR: u64 = 60;
const MIN_IN_DAY: u64 = MIN_IN_HOUR * 24;
// Check timezone validity
assert!(timezone <= 1440 && timezone >= -1440);
// Convert to seconds since 1900-01-01-00:00:00 in timezone.
let Some(secs) = dur
.as_secs()
.checked_add_signed(SYSTEMTIME_TIMEZONE - (timezone as i64 * SECS_IN_MINUTE as i64))
else {
// If the current timezone cannot be used, find the closest timezone that will allow the
// conversion to succeed.
let new_tz = (dur.as_secs() / SECS_IN_MINUTE) as i16
+ (SYSTEMTIME_TIMEZONE / SECS_IN_MINUTE as i64) as i16;
return Err(new_tz);
};
let days = secs / SECS_IN_DAY;
let remaining_secs = secs % SECS_IN_DAY;
let z = days + 693901;
let era = z / 146097;
let doe = z - (era * 146097);
let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
let mut y = yoe + era * 400;
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
let mp = (5 * doy + 2) / 153;
let d = doy - (153 * mp + 2) / 5 + 1;
let m = if mp < 10 { mp + 3 } else { mp - 9 };
if m <= 2 {
y += 1;
}
let hour = (remaining_secs / SECS_IN_HOUR) as u8;
let minute = ((remaining_secs % SECS_IN_HOUR) / SECS_IN_MINUTE) as u8;
let second = (remaining_secs % SECS_IN_MINUTE) as u8;
// At this point, invalid time will be greater than MAX representable time. It cannot be less
// than minimum time since we already take care of that case above.
if y <= 9999 {
Ok(Time {
year: y as u16,
month: m as u8,
day: d as u8,
hour,
minute,
second,
nanosecond: dur.subsec_nanos(),
timezone,
daylight,
pad1: 0,
pad2: 0,
})
} else {
assert!(y == 10000);
assert!(m == 1);
let delta = ((d - 1) as u64 * MIN_IN_DAY
+ hour as u64 * MIN_IN_HOUR
+ minute as u64
+ if second == 0 { 0 } else { 1 }) as i16;
let new_tz = timezone + delta;
assert!(new_tz <= 1440 && new_tz >= -1440);
Err(new_tz)
}
}

View file

@ -1,11 +1,10 @@
use core::num::niche_types::Nanoseconds;
use crate::sys::AsInner;
use crate::io;
use crate::time::Duration;
use crate::{fmt, io};
const NSEC_PER_SEC: u64 = 1_000_000_000;
pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() };
#[allow(dead_code)] // Used for pthread condvar timeouts
pub const TIMESPEC_MAX: libc::timespec =
libc::timespec { tv_sec: <libc::time_t>::MAX, tv_nsec: 1_000_000_000 - 1 };
@ -18,60 +17,19 @@ pub(in crate::sys) const TIMESPEC_MAX_CAPPED: libc::timespec = libc::timespec {
tv_nsec: (u64::MAX % NSEC_PER_SEC) as i64,
};
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SystemTime {
pub(crate) t: Timespec,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct Timespec {
tv_sec: i64,
tv_nsec: Nanoseconds,
}
impl SystemTime {
pub const MAX: SystemTime = SystemTime { t: Timespec::MAX };
pub const MIN: SystemTime = SystemTime { t: Timespec::MIN };
#[cfg_attr(any(target_os = "horizon", target_os = "hurd"), allow(unused))]
pub fn new(tv_sec: i64, tv_nsec: i64) -> Result<SystemTime, io::Error> {
Ok(SystemTime { t: Timespec::new(tv_sec, tv_nsec)? })
}
pub fn now() -> SystemTime {
SystemTime { t: Timespec::now(libc::CLOCK_REALTIME) }
}
pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
self.t.sub_timespec(&other.t)
}
pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
Some(SystemTime { t: self.t.checked_add_duration(other)? })
}
pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
Some(SystemTime { t: self.t.checked_sub_duration(other)? })
}
}
impl fmt::Debug for SystemTime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SystemTime")
.field("tv_sec", &self.t.tv_sec)
.field("tv_nsec", &self.t.tv_nsec)
.finish()
}
pub tv_sec: i64,
pub tv_nsec: Nanoseconds,
}
impl Timespec {
const MAX: Timespec = unsafe { Self::new_unchecked(i64::MAX, 1_000_000_000 - 1) };
pub const MAX: Timespec = unsafe { Self::new_unchecked(i64::MAX, 1_000_000_000 - 1) };
// As described below, on Apple OS, dates before epoch are represented differently.
// This is not an issue here however, because we are using tv_sec = i64::MIN,
// which will cause the compatibility wrapper to not be executed at all.
const MIN: Timespec = unsafe { Self::new_unchecked(i64::MIN, 0) };
pub const MIN: Timespec = unsafe { Self::new_unchecked(i64::MIN, 0) };
const unsafe fn new_unchecked(tv_sec: i64, tv_nsec: i64) -> Timespec {
Timespec { tv_sec, tv_nsec: unsafe { Nanoseconds::new_unchecked(tv_nsec as u32) } }
@ -81,7 +39,7 @@ impl Timespec {
unsafe { Self::new_unchecked(0, 0) }
}
const fn new(tv_sec: i64, tv_nsec: i64) -> Result<Timespec, io::Error> {
pub const fn new(tv_sec: i64, tv_nsec: i64) -> Result<Timespec, io::Error> {
// On Apple OS, dates before epoch are represented differently than on other
// Unix platforms: e.g. 1/10th of a second before epoch is represented as `seconds=-1`
// and `nanoseconds=100_000_000` on other platforms, but is `seconds=0` and
@ -263,98 +221,3 @@ impl __timespec64 {
Self { tv_sec, tv_nsec, _padding: 0 }
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Instant {
t: Timespec,
}
impl Instant {
// CLOCK_UPTIME_RAW clock that increments monotonically, in the same man-
// ner as CLOCK_MONOTONIC_RAW, but that does not incre-
// ment while the system is asleep. The returned value
// is identical to the result of mach_absolute_time()
// after the appropriate mach_timebase conversion is
// applied.
//
// We use `CLOCK_UPTIME_RAW` instead of `CLOCK_MONOTONIC` since
// `CLOCK_UPTIME_RAW` is based on `mach_absolute_time`, which is the
// clock that all timeouts and deadlines are measured against inside
// the kernel.
#[cfg(target_vendor = "apple")]
pub(crate) const CLOCK_ID: libc::clockid_t = libc::CLOCK_UPTIME_RAW;
#[cfg(not(target_vendor = "apple"))]
pub(crate) const CLOCK_ID: libc::clockid_t = libc::CLOCK_MONOTONIC;
pub fn now() -> Instant {
// https://pubs.opengroup.org/onlinepubs/9799919799/functions/clock_getres.html
Instant { t: Timespec::now(Self::CLOCK_ID) }
}
pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
self.t.sub_timespec(&other.t).ok()
}
pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
Some(Instant { t: self.t.checked_add_duration(other)? })
}
pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
Some(Instant { t: self.t.checked_sub_duration(other)? })
}
#[cfg_attr(
not(target_os = "linux"),
allow(unused, reason = "needed by the `sleep_until` on some unix platforms")
)]
pub(crate) fn into_timespec(self) -> Timespec {
self.t
}
/// Returns `self` converted into units of `mach_absolute_time`, or `None`
/// if `self` is before the system boot time. If the conversion cannot be
/// performed precisely, this ceils the result up to the nearest
/// representable value.
#[cfg(target_vendor = "apple")]
pub fn into_mach_absolute_time_ceil(self) -> Option<u128> {
#[repr(C)]
struct mach_timebase_info {
numer: u32,
denom: u32,
}
unsafe extern "C" {
unsafe fn mach_timebase_info(info: *mut mach_timebase_info) -> libc::kern_return_t;
}
let secs = u64::try_from(self.t.tv_sec).ok()?;
let mut timebase = mach_timebase_info { numer: 0, denom: 0 };
assert_eq!(unsafe { mach_timebase_info(&mut timebase) }, libc::KERN_SUCCESS);
// Since `tv_sec` is 64-bit and `tv_nsec` is smaller than 1 billion,
// this cannot overflow. The resulting number needs at most 94 bits.
let nanos =
u128::from(secs) * u128::from(NSEC_PER_SEC) + u128::from(self.t.tv_nsec.as_inner());
// This multiplication cannot overflow since multiplying a 94-bit
// number by a 32-bit number yields a number that needs at most
// 126 bits.
Some((nanos * u128::from(timebase.denom)).div_ceil(u128::from(timebase.numer)))
}
}
impl AsInner<Timespec> for Instant {
fn as_inner(&self) -> &Timespec {
&self.t
}
}
impl fmt::Debug for Instant {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Instant")
.field("tv_sec", &self.t.tv_sec)
.field("tv_nsec", &self.t.tv_nsec)
.finish()
}
}

View file

@ -1,7 +1,6 @@
#![deny(unsafe_op_in_unsafe_fn)]
pub mod os;
pub mod time;
mod common;
pub use common::*;

View file

@ -1,5 +1,4 @@
pub mod os;
pub mod time;
#[expect(dead_code)]
#[path = "../unsupported/common.rs"]

View file

@ -18,8 +18,6 @@
#[path = "../unsupported/os.rs"]
pub mod os;
#[path = "../unsupported/time.rs"]
pub mod time;
#[cfg(target_feature = "atomics")]
#[path = "atomics/futex.rs"]

View file

@ -1,170 +1,12 @@
use core::hash::{Hash, Hasher};
use core::ops::Neg;
use crate::cmp::Ordering;
use crate::ops::Neg;
use crate::ptr::null;
use crate::sys::{IntoInner, c};
use crate::sys::pal::c;
use crate::time::Duration;
use crate::{fmt, mem};
const NANOS_PER_SEC: u64 = 1_000_000_000;
const INTERVALS_PER_SEC: u64 = NANOS_PER_SEC / 100;
pub const INTERVALS_PER_SEC: u64 = NANOS_PER_SEC / 100;
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)]
pub struct Instant {
// This duration is relative to an arbitrary microsecond epoch
// from the winapi QueryPerformanceCounter function.
t: Duration,
}
#[derive(Copy, Clone)]
pub struct SystemTime {
t: c::FILETIME,
}
const INTERVALS_TO_UNIX_EPOCH: u64 = 11_644_473_600 * INTERVALS_PER_SEC;
pub const UNIX_EPOCH: SystemTime = SystemTime {
t: c::FILETIME {
dwLowDateTime: INTERVALS_TO_UNIX_EPOCH as u32,
dwHighDateTime: (INTERVALS_TO_UNIX_EPOCH >> 32) as u32,
},
};
impl Instant {
pub fn now() -> Instant {
// High precision timing on windows operates in "Performance Counter"
// units, as returned by the WINAPI QueryPerformanceCounter function.
// These relate to seconds by a factor of QueryPerformanceFrequency.
// In order to keep unit conversions out of normal interval math, we
// measure in QPC units and immediately convert to nanoseconds.
perf_counter::PerformanceCounterInstant::now().into()
}
pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
// On windows there's a threshold below which we consider two timestamps
// equivalent due to measurement error. For more details + doc link,
// check the docs on epsilon.
let epsilon = perf_counter::PerformanceCounterInstant::epsilon();
if other.t > self.t && other.t - self.t <= epsilon {
Some(Duration::new(0, 0))
} else {
self.t.checked_sub(other.t)
}
}
pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
Some(Instant { t: self.t.checked_add(*other)? })
}
pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
Some(Instant { t: self.t.checked_sub(*other)? })
}
}
impl SystemTime {
pub const MAX: SystemTime = SystemTime {
t: c::FILETIME {
dwLowDateTime: (i64::MAX & 0xFFFFFFFF) as u32,
dwHighDateTime: (i64::MAX >> 32) as u32,
},
};
pub const MIN: SystemTime =
SystemTime { t: c::FILETIME { dwLowDateTime: 0, dwHighDateTime: 0 } };
pub fn now() -> SystemTime {
unsafe {
let mut t: SystemTime = mem::zeroed();
c::GetSystemTimePreciseAsFileTime(&mut t.t);
t
}
}
fn from_intervals(intervals: i64) -> SystemTime {
SystemTime {
t: c::FILETIME {
dwLowDateTime: intervals as u32,
dwHighDateTime: (intervals >> 32) as u32,
},
}
}
fn intervals(&self) -> i64 {
(self.t.dwLowDateTime as i64) | ((self.t.dwHighDateTime as i64) << 32)
}
pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
let me = self.intervals();
let other = other.intervals();
if me >= other {
Ok(intervals2dur((me - other) as u64))
} else {
Err(intervals2dur((other - me) as u64))
}
}
pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
let intervals = self.intervals().checked_add(checked_dur2intervals(other)?)?;
Some(SystemTime::from_intervals(intervals))
}
pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
// Windows does not support times before 1601, hence why we don't
// support negatives. In order to tackle this, we try to convert the
// resulting value into an u64, which should obviously fail in the case
// that the value is below zero.
let intervals: u64 =
self.intervals().checked_sub(checked_dur2intervals(other)?)?.try_into().ok()?;
Some(SystemTime::from_intervals(intervals as i64))
}
}
impl PartialEq for SystemTime {
fn eq(&self, other: &SystemTime) -> bool {
self.intervals() == other.intervals()
}
}
impl Eq for SystemTime {}
impl PartialOrd for SystemTime {
fn partial_cmp(&self, other: &SystemTime) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for SystemTime {
fn cmp(&self, other: &SystemTime) -> Ordering {
self.intervals().cmp(&other.intervals())
}
}
impl fmt::Debug for SystemTime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SystemTime").field("intervals", &self.intervals()).finish()
}
}
impl From<c::FILETIME> for SystemTime {
fn from(t: c::FILETIME) -> SystemTime {
SystemTime { t }
}
}
impl IntoInner<c::FILETIME> for SystemTime {
fn into_inner(self) -> c::FILETIME {
self.t
}
}
impl Hash for SystemTime {
fn hash<H: Hasher>(&self, state: &mut H) {
self.intervals().hash(state)
}
}
fn checked_dur2intervals(dur: &Duration) -> Option<i64> {
pub fn checked_dur2intervals(dur: &Duration) -> Option<i64> {
dur.as_secs()
.checked_mul(INTERVALS_PER_SEC)?
.checked_add(dur.subsec_nanos() as u64 / 100)?
@ -172,43 +14,23 @@ fn checked_dur2intervals(dur: &Duration) -> Option<i64> {
.ok()
}
fn intervals2dur(intervals: u64) -> Duration {
pub fn intervals2dur(intervals: u64) -> Duration {
Duration::new(intervals / INTERVALS_PER_SEC, ((intervals % INTERVALS_PER_SEC) * 100) as u32)
}
mod perf_counter {
pub mod perf_counter {
use super::NANOS_PER_SEC;
use crate::sync::atomic::{Atomic, AtomicU64, Ordering};
use crate::sys::helpers::mul_div_u64;
use crate::sys::{c, cvt};
use crate::time::Duration;
pub struct PerformanceCounterInstant {
ts: i64,
}
impl PerformanceCounterInstant {
pub fn now() -> Self {
Self { ts: query() }
}
// Per microsoft docs, the margin of error for cross-thread time comparisons
// using QueryPerformanceCounter is 1 "tick" -- defined as 1/frequency().
// Reference: https://docs.microsoft.com/en-us/windows/desktop/SysInfo
// /acquiring-high-resolution-time-stamps
pub fn epsilon() -> Duration {
let epsilon = NANOS_PER_SEC / (frequency() as u64);
Duration::from_nanos(epsilon)
}
}
impl From<PerformanceCounterInstant> for super::Instant {
fn from(other: PerformanceCounterInstant) -> Self {
let freq = frequency() as u64;
let instant_nsec = mul_div_u64(other.ts as u64, NANOS_PER_SEC, freq);
Self { t: Duration::from_nanos(instant_nsec) }
}
pub fn now() -> i64 {
let mut qpc_value: i64 = 0;
cvt(unsafe { c::QueryPerformanceCounter(&mut qpc_value) }).unwrap();
qpc_value
}
fn frequency() -> i64 {
pub fn frequency() -> i64 {
// Either the cached result of `QueryPerformanceFrequency` or `0` for
// uninitialized. Storing this as a single `AtomicU64` allows us to use
// `Relaxed` operations, as we are only interested in the effects on a
@ -230,10 +52,13 @@ mod perf_counter {
frequency
}
fn query() -> i64 {
let mut qpc_value: i64 = 0;
cvt(unsafe { c::QueryPerformanceCounter(&mut qpc_value) }).unwrap();
qpc_value
// Per microsoft docs, the margin of error for cross-thread time comparisons
// using QueryPerformanceCounter is 1 "tick" -- defined as 1/frequency().
// Reference: https://docs.microsoft.com/en-us/windows/desktop/SysInfo
// /acquiring-high-resolution-time-stamps
pub fn epsilon() -> Duration {
let epsilon = NANOS_PER_SEC / (frequency() as u64);
Duration::from_nanos(epsilon)
}
}
@ -241,6 +66,7 @@ mod perf_counter {
pub(crate) struct WaitableTimer {
handle: c::HANDLE,
}
impl WaitableTimer {
/// Creates a high-resolution timer. Will fail before Windows 10, version 1803.
pub fn high_resolution() -> Result<Self, ()> {
@ -254,6 +80,7 @@ impl WaitableTimer {
};
if !handle.is_null() { Ok(Self { handle }) } else { Err(()) }
}
pub fn set(&self, duration: Duration) -> Result<(), ()> {
// Convert the Duration to a format similar to FILETIME.
// Negative values are relative times whereas positive values are absolute.
@ -262,11 +89,13 @@ impl WaitableTimer {
let result = unsafe { c::SetWaitableTimer(self.handle, &time, 0, None, null(), c::FALSE) };
if result != 0 { Ok(()) } else { Err(()) }
}
pub fn wait(&self) -> Result<(), ()> {
let result = unsafe { c::WaitForSingleObject(self.handle, c::INFINITE) };
if result != c::WAIT_FAILED { Ok(()) } else { Err(()) }
}
}
impl Drop for WaitableTimer {
fn drop(&mut self) {
unsafe { c::CloseHandle(self.handle) };

View file

@ -1,7 +1,6 @@
#![forbid(unsafe_op_in_unsafe_fn)]
pub mod os;
pub mod time;
#[path = "../unsupported/common.rs"]
mod common;

View file

@ -12,8 +12,6 @@ pub const WORD_SIZE: usize = size_of::<u32>();
pub mod abi;
pub mod os;
#[path = "../unsupported/time.rs"]
pub mod time;
use crate::io as std_io;

View file

@ -0,0 +1,63 @@
use hermit_abi::{self, CLOCK_MONOTONIC, CLOCK_REALTIME};
use crate::hash::Hash;
use crate::sys::pal::time::Timespec;
use crate::time::Duration;
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct Instant(Timespec);
impl Instant {
pub fn now() -> Instant {
let mut time: Timespec = Timespec::zero();
let _ = unsafe { hermit_abi::clock_gettime(CLOCK_MONOTONIC, &raw mut time.t) };
Instant(time)
}
pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
self.0.sub_timespec(&other.0).ok()
}
pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
Some(Instant(self.0.checked_add_duration(other)?))
}
pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
Some(Instant(self.0.checked_sub_duration(other)?))
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct SystemTime(Timespec);
pub const UNIX_EPOCH: SystemTime = SystemTime(Timespec::zero());
impl SystemTime {
pub const MAX: SystemTime = SystemTime(Timespec::MAX);
pub const MIN: SystemTime = SystemTime(Timespec::MIN);
pub fn new(tv_sec: i64, tv_nsec: i32) -> SystemTime {
SystemTime(Timespec::new(tv_sec, tv_nsec))
}
pub fn now() -> SystemTime {
let mut time: Timespec = Timespec::zero();
let _ = unsafe { hermit_abi::clock_gettime(CLOCK_REALTIME, &raw mut time.t) };
SystemTime(time)
}
pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
self.0.sub_timespec(&other.0)
}
pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
Some(SystemTime(self.0.checked_add_duration(other)?))
}
pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
Some(SystemTime(self.0.checked_sub_duration(other)?))
}
}

View file

@ -0,0 +1,53 @@
cfg_select! {
target_os = "hermit" => {
mod hermit;
use hermit as imp;
}
target_os = "motor" => {
use moto_rt::time as imp;
}
all(target_vendor = "fortanix", target_env = "sgx") => {
mod sgx;
use sgx as imp;
}
target_os = "solid_asp3" => {
mod solid;
use solid as imp;
}
target_os = "uefi" => {
mod uefi;
use uefi as imp;
}
any(
target_os = "teeos",
target_family = "unix",
target_os = "wasi",
) => {
mod unix;
use unix as imp;
}
target_os = "vexos" => {
mod vexos;
#[expect(unused)]
mod unsupported;
mod imp {
pub use super::vexos::Instant;
pub use super::unsupported::{SystemTime, UNIX_EPOCH};
}
}
target_os = "windows" => {
mod windows;
use windows as imp;
}
target_os = "xous" => {
mod xous;
use xous as imp;
}
_ => {
mod unsupported;
use unsupported as imp;
}
}
pub use imp::{Instant, SystemTime, UNIX_EPOCH};

View file

@ -1,4 +1,4 @@
use super::abi::usercalls;
use crate::sys::pal::abi::usercalls;
use crate::time::Duration;
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]

View file

@ -1,9 +1,38 @@
use super::abi;
use super::error::expect_success;
pub use super::itron::time::Instant;
use crate::mem::MaybeUninit;
use crate::sys::pal::error::expect_success;
use crate::sys::pal::{abi, itron};
use crate::time::Duration;
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct Instant(itron::abi::SYSTIM);
impl Instant {
pub fn now() -> Instant {
Instant(itron::time::get_tim())
}
pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
self.0.checked_sub(other.0).map(|ticks| {
// `SYSTIM` is measured in microseconds
Duration::from_micros(ticks)
})
}
pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
// `SYSTIM` is measured in microseconds
let ticks = other.as_micros();
Some(Instant(self.0.checked_add(ticks.try_into().ok()?)?))
}
pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
// `SYSTIM` is measured in microseconds
let ticks = other.as_micros();
Some(Instant(self.0.checked_sub(ticks.try_into().ok()?)?))
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct SystemTime(abi::time_t);

View file

@ -1,7 +1,6 @@
use crate::sys::pal::system_time;
use crate::time::Duration;
const SECS_IN_MINUTE: u64 = 60;
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct Instant(Duration);
@ -88,7 +87,7 @@ impl SystemTime {
.unwrap();
pub(crate) const fn from_uefi(t: r_efi::efi::Time) -> Option<Self> {
match system_time_internal::from_uefi(&t) {
match system_time::from_uefi(&t) {
Some(x) => Some(Self(x)),
None => None,
}
@ -99,12 +98,12 @@ impl SystemTime {
timezone: i16,
daylight: u8,
) -> Result<r_efi::efi::Time, i16> {
// system_time_internal::to_uefi requires a valid timezone. In case of unspecified timezone,
// system_time::to_uefi requires a valid timezone. In case of unspecified timezone,
// we just pass 0 since it is assumed that no timezone related adjustments are required.
if timezone == r_efi::efi::UNSPECIFIED_TIMEZONE {
system_time_internal::to_uefi(&self.0, 0, daylight)
system_time::to_uefi(&self.0, 0, daylight)
} else {
system_time_internal::to_uefi(&self.0, timezone, daylight)
system_time::to_uefi(&self.0, timezone, daylight)
}
}
@ -118,8 +117,7 @@ impl SystemTime {
}
pub fn now() -> SystemTime {
Self::from_uefi(system_time_internal::now())
.expect("time incorrectly implemented on this platform")
Self::from_uefi(system_time::now()).expect("time incorrectly implemented on this platform")
}
pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
@ -138,169 +136,15 @@ impl SystemTime {
}
}
pub(crate) mod system_time_internal {
use r_efi::efi::{RuntimeServices, Time};
use super::super::helpers;
use super::*;
use crate::mem::MaybeUninit;
use crate::ptr::NonNull;
const SECS_IN_HOUR: u64 = SECS_IN_MINUTE * 60;
const SECS_IN_DAY: u64 = SECS_IN_HOUR * 24;
const SYSTEMTIME_TIMEZONE: i64 = -1440 * SECS_IN_MINUTE as i64;
pub(crate) fn now() -> Time {
let runtime_services: NonNull<RuntimeServices> =
helpers::runtime_services().expect("Runtime services are not available");
let mut t: MaybeUninit<Time> = MaybeUninit::uninit();
let r = unsafe {
((*runtime_services.as_ptr()).get_time)(t.as_mut_ptr(), crate::ptr::null_mut())
};
if r.is_error() {
panic!("time not implemented on this platform");
}
unsafe { t.assume_init() }
}
/// This algorithm is a modified form of the one described in the post
/// https://blog.reverberate.org/2020/05/12/optimizing-date-algorithms.html
///
/// The changes are to use 1900-01-01-00:00:00 with timezone -1440 as anchor instead of UNIX
/// epoch used in the original algorithm.
pub(crate) const fn from_uefi(t: &Time) -> Option<Duration> {
if !(t.month <= 12
&& t.month != 0
&& t.year >= 1900
&& t.year <= 9999
&& t.day <= 31
&& t.day != 0
&& t.second < 60
&& t.minute <= 60
&& t.hour < 24
&& t.nanosecond < 1_000_000_000
&& ((t.timezone <= 1440 && t.timezone >= -1440)
|| t.timezone == r_efi::efi::UNSPECIFIED_TIMEZONE))
{
return None;
}
const YEAR_BASE: u32 = 4800; /* Before min year, multiple of 400. */
// Calculate the number of days since 1/1/1900. This is the earliest supported date in UEFI
// time.
// Use 1 March as the start
let (m_adj, overflow): (u32, bool) = (t.month as u32).overflowing_sub(3);
let (carry, adjust): (u32, u32) = if overflow { (1, 12) } else { (0, 0) };
let y_adj: u32 = (t.year as u32) + YEAR_BASE - carry;
let month_days: u32 = (m_adj.wrapping_add(adjust) * 62719 + 769) / 2048;
let leap_days: u32 = y_adj / 4 - y_adj / 100 + y_adj / 400;
let days: u32 = y_adj * 365 + leap_days + month_days + (t.day as u32 - 1) - 2447065;
let localtime_epoch: u64 = (days as u64) * SECS_IN_DAY
+ (t.second as u64)
+ (t.minute as u64) * SECS_IN_MINUTE
+ (t.hour as u64) * SECS_IN_HOUR;
let normalized_timezone = if t.timezone == r_efi::efi::UNSPECIFIED_TIMEZONE {
-SYSTEMTIME_TIMEZONE
} else {
(t.timezone as i64) * SECS_IN_MINUTE as i64 - SYSTEMTIME_TIMEZONE
};
// Calculate the offset from 1/1/1900 at timezone -1440 min
let epoch = localtime_epoch.checked_add_signed(normalized_timezone).unwrap();
Some(Duration::new(epoch, t.nanosecond))
}
/// This algorithm is a modified version of the one described in the post:
/// https://howardhinnant.github.io/date_algorithms.html#clive_from_days
///
/// The changes are to use 1900-01-01-00:00:00 with timezone -1440 as anchor instead of UNIX
/// epoch used in the original algorithm.
pub(crate) const fn to_uefi(dur: &Duration, timezone: i16, daylight: u8) -> Result<Time, i16> {
const MIN_IN_HOUR: u64 = 60;
const MIN_IN_DAY: u64 = MIN_IN_HOUR * 24;
// Check timezone validity
assert!(timezone <= 1440 && timezone >= -1440);
// Convert to seconds since 1900-01-01-00:00:00 in timezone.
let Some(secs) = dur
.as_secs()
.checked_add_signed(SYSTEMTIME_TIMEZONE - (timezone as i64 * SECS_IN_MINUTE as i64))
else {
// If the current timezone cannot be used, find the closest timezone that will allow the
// conversion to succeed.
let new_tz = (dur.as_secs() / SECS_IN_MINUTE) as i16
+ (SYSTEMTIME_TIMEZONE / SECS_IN_MINUTE as i64) as i16;
return Err(new_tz);
};
let days = secs / SECS_IN_DAY;
let remaining_secs = secs % SECS_IN_DAY;
let z = days + 693901;
let era = z / 146097;
let doe = z - (era * 146097);
let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
let mut y = yoe + era * 400;
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
let mp = (5 * doy + 2) / 153;
let d = doy - (153 * mp + 2) / 5 + 1;
let m = if mp < 10 { mp + 3 } else { mp - 9 };
if m <= 2 {
y += 1;
}
let hour = (remaining_secs / SECS_IN_HOUR) as u8;
let minute = ((remaining_secs % SECS_IN_HOUR) / SECS_IN_MINUTE) as u8;
let second = (remaining_secs % SECS_IN_MINUTE) as u8;
// At this point, invalid time will be greater than MAX representable time. It cannot be less
// than minimum time since we already take care of that case above.
if y <= 9999 {
Ok(Time {
year: y as u16,
month: m as u8,
day: d as u8,
hour,
minute,
second,
nanosecond: dur.subsec_nanos(),
timezone,
daylight,
pad1: 0,
pad2: 0,
})
} else {
assert!(y == 10000);
assert!(m == 1);
let delta = ((d - 1) as u64 * MIN_IN_DAY
+ hour as u64 * MIN_IN_HOUR
+ minute as u64
+ if second == 0 { 0 } else { 1 }) as i16;
let new_tz = timezone + delta;
assert!(new_tz <= 1440 && new_tz >= -1440);
Err(new_tz)
}
}
}
pub(crate) mod instant_internal {
mod instant_internal {
use r_efi::protocols::timestamp;
use super::super::helpers;
use super::*;
use crate::mem::MaybeUninit;
use crate::ptr::NonNull;
use crate::sync::atomic::{Atomic, AtomicPtr, Ordering};
use crate::sys::helpers::mul_div_u64;
use crate::sys::pal::helpers;
const NS_PER_SEC: u64 = 1_000_000_000;

View file

@ -0,0 +1,141 @@
use crate::sys::AsInner;
use crate::sys::pal::time::Timespec;
use crate::time::Duration;
use crate::{fmt, io};
pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() };
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SystemTime {
pub(crate) t: Timespec,
}
impl SystemTime {
pub const MAX: SystemTime = SystemTime { t: Timespec::MAX };
pub const MIN: SystemTime = SystemTime { t: Timespec::MIN };
#[cfg_attr(any(target_os = "horizon", target_os = "hurd", target_os = "teeos"), expect(unused))]
pub fn new(tv_sec: i64, tv_nsec: i64) -> Result<SystemTime, io::Error> {
Ok(SystemTime { t: Timespec::new(tv_sec, tv_nsec)? })
}
pub fn now() -> SystemTime {
SystemTime { t: Timespec::now(libc::CLOCK_REALTIME) }
}
pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
self.t.sub_timespec(&other.t)
}
pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
Some(SystemTime { t: self.t.checked_add_duration(other)? })
}
pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
Some(SystemTime { t: self.t.checked_sub_duration(other)? })
}
}
impl fmt::Debug for SystemTime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SystemTime")
.field("tv_sec", &self.t.tv_sec)
.field("tv_nsec", &self.t.tv_nsec)
.finish()
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Instant {
t: Timespec,
}
impl Instant {
// CLOCK_UPTIME_RAW clock that increments monotonically, in the same man-
// ner as CLOCK_MONOTONIC_RAW, but that does not incre-
// ment while the system is asleep. The returned value
// is identical to the result of mach_absolute_time()
// after the appropriate mach_timebase conversion is
// applied.
//
// We use `CLOCK_UPTIME_RAW` instead of `CLOCK_MONOTONIC` since
// `CLOCK_UPTIME_RAW` is based on `mach_absolute_time`, which is the
// clock that all timeouts and deadlines are measured against inside
// the kernel.
#[cfg(target_vendor = "apple")]
pub(crate) const CLOCK_ID: libc::clockid_t = libc::CLOCK_UPTIME_RAW;
#[cfg(not(target_vendor = "apple"))]
pub(crate) const CLOCK_ID: libc::clockid_t = libc::CLOCK_MONOTONIC;
pub fn now() -> Instant {
// https://pubs.opengroup.org/onlinepubs/9799919799/functions/clock_getres.html
Instant { t: Timespec::now(Self::CLOCK_ID) }
}
pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
self.t.sub_timespec(&other.t).ok()
}
pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
Some(Instant { t: self.t.checked_add_duration(other)? })
}
pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
Some(Instant { t: self.t.checked_sub_duration(other)? })
}
#[cfg_attr(
not(target_os = "linux"),
allow(unused, reason = "needed by the `sleep_until` on some unix platforms")
)]
pub(crate) fn into_timespec(self) -> Timespec {
self.t
}
/// Returns `self` converted into units of `mach_absolute_time`, or `None`
/// if `self` is before the system boot time. If the conversion cannot be
/// performed precisely, this ceils the result up to the nearest
/// representable value.
#[cfg(target_vendor = "apple")]
pub fn into_mach_absolute_time_ceil(self) -> Option<u128> {
#[repr(C)]
struct mach_timebase_info {
numer: u32,
denom: u32,
}
unsafe extern "C" {
unsafe fn mach_timebase_info(info: *mut mach_timebase_info) -> libc::kern_return_t;
}
let secs = u64::try_from(self.t.tv_sec).ok()?;
let mut timebase = mach_timebase_info { numer: 0, denom: 0 };
assert_eq!(unsafe { mach_timebase_info(&mut timebase) }, libc::KERN_SUCCESS);
// Since `tv_sec` is 64-bit and `tv_nsec` is smaller than 1 billion,
// this cannot overflow. The resulting number needs at most 94 bits.
let nanos = 1_000_000_000 * u128::from(secs) + u128::from(self.t.tv_nsec.as_inner());
// This multiplication cannot overflow since multiplying a 94-bit
// number by a 32-bit number yields a number that needs at most
// 126 bits.
Some((nanos * u128::from(timebase.denom)).div_ceil(u128::from(timebase.numer)))
}
}
impl AsInner<Timespec> for Instant {
fn as_inner(&self) -> &Timespec {
&self.t
}
}
impl fmt::Debug for Instant {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Instant")
.field("tv_sec", &self.t.tv_sec)
.field("tv_nsec", &self.t.tv_nsec)
.finish()
}
}

View file

@ -1,10 +1,5 @@
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);

View file

@ -0,0 +1,156 @@
use crate::cmp::Ordering;
use crate::hash::{Hash, Hasher};
use crate::sys::helpers::mul_div_u64;
use crate::sys::pal::time::{
INTERVALS_PER_SEC, checked_dur2intervals, intervals2dur, perf_counter,
};
use crate::sys::{IntoInner, c};
use crate::time::Duration;
use crate::{fmt, mem};
const NANOS_PER_SEC: u64 = 1_000_000_000;
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)]
pub struct Instant {
// This duration is relative to an arbitrary microsecond epoch
// from the winapi QueryPerformanceCounter function.
t: Duration,
}
#[derive(Copy, Clone)]
pub struct SystemTime {
t: c::FILETIME,
}
pub const UNIX_EPOCH: SystemTime =
SystemTime::from_intervals(11_644_473_600 * INTERVALS_PER_SEC as i64);
impl Instant {
pub fn now() -> Instant {
// High precision timing on windows operates in "Performance Counter"
// units, as returned by the WINAPI QueryPerformanceCounter function.
// These relate to seconds by a factor of QueryPerformanceFrequency.
// In order to keep unit conversions out of normal interval math, we
// measure in QPC units and immediately convert to nanoseconds.
let freq = perf_counter::frequency() as u64;
let now = perf_counter::now();
let instant_nsec = mul_div_u64(now as u64, NANOS_PER_SEC, freq);
Self { t: Duration::from_nanos(instant_nsec) }
}
pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
// On windows there's a threshold below which we consider two timestamps
// equivalent due to measurement error. For more details + doc link,
// check the docs on epsilon.
let epsilon = perf_counter::epsilon();
if other.t > self.t && other.t - self.t <= epsilon {
Some(Duration::new(0, 0))
} else {
self.t.checked_sub(other.t)
}
}
pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
Some(Instant { t: self.t.checked_add(*other)? })
}
pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
Some(Instant { t: self.t.checked_sub(*other)? })
}
}
impl SystemTime {
pub const MAX: SystemTime = SystemTime::from_intervals(i64::MAX);
pub const MIN: SystemTime = SystemTime::from_intervals(0);
pub fn now() -> SystemTime {
unsafe {
let mut t: SystemTime = mem::zeroed();
c::GetSystemTimePreciseAsFileTime(&mut t.t);
t
}
}
const fn from_intervals(intervals: i64) -> SystemTime {
SystemTime {
t: c::FILETIME {
dwLowDateTime: intervals as u32,
dwHighDateTime: (intervals >> 32) as u32,
},
}
}
fn intervals(&self) -> i64 {
(self.t.dwLowDateTime as i64) | ((self.t.dwHighDateTime as i64) << 32)
}
pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
let me = self.intervals();
let other = other.intervals();
if me >= other {
Ok(intervals2dur((me - other) as u64))
} else {
Err(intervals2dur((other - me) as u64))
}
}
pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
let intervals = self.intervals().checked_add(checked_dur2intervals(other)?)?;
Some(SystemTime::from_intervals(intervals))
}
pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
// Windows does not support times before 1601, hence why we don't
// support negatives. In order to tackle this, we try to convert the
// resulting value into an u64, which should obviously fail in the case
// that the value is below zero.
let intervals: u64 =
self.intervals().checked_sub(checked_dur2intervals(other)?)?.try_into().ok()?;
Some(SystemTime::from_intervals(intervals as i64))
}
}
impl PartialEq for SystemTime {
fn eq(&self, other: &SystemTime) -> bool {
self.intervals() == other.intervals()
}
}
impl Eq for SystemTime {}
impl PartialOrd for SystemTime {
fn partial_cmp(&self, other: &SystemTime) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for SystemTime {
fn cmp(&self, other: &SystemTime) -> Ordering {
self.intervals().cmp(&other.intervals())
}
}
impl fmt::Debug for SystemTime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SystemTime").field("intervals", &self.intervals()).finish()
}
}
impl From<c::FILETIME> for SystemTime {
fn from(t: c::FILETIME) -> SystemTime {
SystemTime { t }
}
}
impl IntoInner<c::FILETIME> for SystemTime {
fn into_inner(self) -> c::FILETIME {
self.t
}
}
impl Hash for SystemTime {
fn hash<H: Hasher>(&self, state: &mut H) {
self.intervals().hash(state)
}
}

View file

@ -40,7 +40,7 @@ case ${TARGET} in
export RUSTFLAGS="${RUSTFLAGS} -C llvm-args=-fast-isel=false"
;;
armv7-*eabihf | thumbv7-*eabihf)
export RUSTFLAGS="${RUSTFLAGS} -Ctarget-feature=+neon"
export RUSTFLAGS="${RUSTFLAGS} -Ctarget-feature=+neon,+fp16"
;;
amdgcn-*)
export RUSTFLAGS="${RUSTFLAGS} -Ctarget-cpu=gfx1200"

View file

@ -1,3 +0,0 @@
ignore = [
"src/simd.rs",
]

File diff suppressed because it is too large Load diff

View file

@ -13,194 +13,195 @@ use crate::core_arch::aarch64::*;
use crate::core_arch::simd::*;
use std::mem;
use stdarch_test::simd_test;
#[simd_test(enable = "neon")]
unsafe fn test_vld1_s8() {
fn test_vld1_s8() {
let a: [i8; 9] = [0, 1, 2, 3, 4, 5, 6, 7, 8];
let e = i8x8::new(1, 2, 3, 4, 5, 6, 7, 8);
let r: i8x8 = transmute(vld1_s8(a[1..].as_ptr()));
let r = unsafe { i8x8::from(vld1_s8(a[1..].as_ptr())) };
assert_eq!(r, e)
}
#[simd_test(enable = "neon")]
unsafe fn test_vld1q_s8() {
fn test_vld1q_s8() {
let a: [i8; 17] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
let e = i8x16::new(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
let r: i8x16 = transmute(vld1q_s8(a[1..].as_ptr()));
let r = unsafe { i8x16::from(vld1q_s8(a[1..].as_ptr())) };
assert_eq!(r, e)
}
#[simd_test(enable = "neon")]
unsafe fn test_vld1_s16() {
fn test_vld1_s16() {
let a: [i16; 5] = [0, 1, 2, 3, 4];
let e = i16x4::new(1, 2, 3, 4);
let r: i16x4 = transmute(vld1_s16(a[1..].as_ptr()));
let r = unsafe { i16x4::from(vld1_s16(a[1..].as_ptr())) };
assert_eq!(r, e)
}
#[simd_test(enable = "neon")]
unsafe fn test_vld1q_s16() {
fn test_vld1q_s16() {
let a: [i16; 9] = [0, 1, 2, 3, 4, 5, 6, 7, 8];
let e = i16x8::new(1, 2, 3, 4, 5, 6, 7, 8);
let r: i16x8 = transmute(vld1q_s16(a[1..].as_ptr()));
let r = unsafe { i16x8::from(vld1q_s16(a[1..].as_ptr())) };
assert_eq!(r, e)
}
#[simd_test(enable = "neon")]
unsafe fn test_vld1_s32() {
fn test_vld1_s32() {
let a: [i32; 3] = [0, 1, 2];
let e = i32x2::new(1, 2);
let r: i32x2 = transmute(vld1_s32(a[1..].as_ptr()));
let r = unsafe { i32x2::from(vld1_s32(a[1..].as_ptr())) };
assert_eq!(r, e)
}
#[simd_test(enable = "neon")]
unsafe fn test_vld1q_s32() {
fn test_vld1q_s32() {
let a: [i32; 5] = [0, 1, 2, 3, 4];
let e = i32x4::new(1, 2, 3, 4);
let r: i32x4 = transmute(vld1q_s32(a[1..].as_ptr()));
let r = unsafe { i32x4::from(vld1q_s32(a[1..].as_ptr())) };
assert_eq!(r, e)
}
#[simd_test(enable = "neon")]
unsafe fn test_vld1_s64() {
fn test_vld1_s64() {
let a: [i64; 2] = [0, 1];
let e = i64x1::new(1);
let r: i64x1 = transmute(vld1_s64(a[1..].as_ptr()));
let r = unsafe { i64x1::from(vld1_s64(a[1..].as_ptr())) };
assert_eq!(r, e)
}
#[simd_test(enable = "neon")]
unsafe fn test_vld1q_s64() {
fn test_vld1q_s64() {
let a: [i64; 3] = [0, 1, 2];
let e = i64x2::new(1, 2);
let r: i64x2 = transmute(vld1q_s64(a[1..].as_ptr()));
let r = unsafe { i64x2::from(vld1q_s64(a[1..].as_ptr())) };
assert_eq!(r, e)
}
#[simd_test(enable = "neon")]
unsafe fn test_vld1_u8() {
fn test_vld1_u8() {
let a: [u8; 9] = [0, 1, 2, 3, 4, 5, 6, 7, 8];
let e = u8x8::new(1, 2, 3, 4, 5, 6, 7, 8);
let r: u8x8 = transmute(vld1_u8(a[1..].as_ptr()));
let r = unsafe { u8x8::from(vld1_u8(a[1..].as_ptr())) };
assert_eq!(r, e)
}
#[simd_test(enable = "neon")]
unsafe fn test_vld1q_u8() {
fn test_vld1q_u8() {
let a: [u8; 17] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
let e = u8x16::new(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
let r: u8x16 = transmute(vld1q_u8(a[1..].as_ptr()));
let r = unsafe { u8x16::from(vld1q_u8(a[1..].as_ptr())) };
assert_eq!(r, e)
}
#[simd_test(enable = "neon")]
unsafe fn test_vld1_u16() {
fn test_vld1_u16() {
let a: [u16; 5] = [0, 1, 2, 3, 4];
let e = u16x4::new(1, 2, 3, 4);
let r: u16x4 = transmute(vld1_u16(a[1..].as_ptr()));
let r = unsafe { u16x4::from(vld1_u16(a[1..].as_ptr())) };
assert_eq!(r, e)
}
#[simd_test(enable = "neon")]
unsafe fn test_vld1q_u16() {
fn test_vld1q_u16() {
let a: [u16; 9] = [0, 1, 2, 3, 4, 5, 6, 7, 8];
let e = u16x8::new(1, 2, 3, 4, 5, 6, 7, 8);
let r: u16x8 = transmute(vld1q_u16(a[1..].as_ptr()));
let r = unsafe { u16x8::from(vld1q_u16(a[1..].as_ptr())) };
assert_eq!(r, e)
}
#[simd_test(enable = "neon")]
unsafe fn test_vld1_u32() {
fn test_vld1_u32() {
let a: [u32; 3] = [0, 1, 2];
let e = u32x2::new(1, 2);
let r: u32x2 = transmute(vld1_u32(a[1..].as_ptr()));
let r = unsafe { u32x2::from(vld1_u32(a[1..].as_ptr())) };
assert_eq!(r, e)
}
#[simd_test(enable = "neon")]
unsafe fn test_vld1q_u32() {
fn test_vld1q_u32() {
let a: [u32; 5] = [0, 1, 2, 3, 4];
let e = u32x4::new(1, 2, 3, 4);
let r: u32x4 = transmute(vld1q_u32(a[1..].as_ptr()));
let r = unsafe { u32x4::from(vld1q_u32(a[1..].as_ptr())) };
assert_eq!(r, e)
}
#[simd_test(enable = "neon")]
unsafe fn test_vld1_u64() {
fn test_vld1_u64() {
let a: [u64; 2] = [0, 1];
let e = u64x1::new(1);
let r: u64x1 = transmute(vld1_u64(a[1..].as_ptr()));
let r = unsafe { u64x1::from(vld1_u64(a[1..].as_ptr())) };
assert_eq!(r, e)
}
#[simd_test(enable = "neon")]
unsafe fn test_vld1q_u64() {
fn test_vld1q_u64() {
let a: [u64; 3] = [0, 1, 2];
let e = u64x2::new(1, 2);
let r: u64x2 = transmute(vld1q_u64(a[1..].as_ptr()));
let r = unsafe { u64x2::from(vld1q_u64(a[1..].as_ptr())) };
assert_eq!(r, e)
}
#[simd_test(enable = "neon")]
unsafe fn test_vld1_p8() {
fn test_vld1_p8() {
let a: [p8; 9] = [0, 1, 2, 3, 4, 5, 6, 7, 8];
let e = u8x8::new(1, 2, 3, 4, 5, 6, 7, 8);
let r: u8x8 = transmute(vld1_p8(a[1..].as_ptr()));
let r = unsafe { u8x8::from(vld1_p8(a[1..].as_ptr())) };
assert_eq!(r, e)
}
#[simd_test(enable = "neon")]
unsafe fn test_vld1q_p8() {
fn test_vld1q_p8() {
let a: [p8; 17] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
let e = u8x16::new(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
let r: u8x16 = transmute(vld1q_p8(a[1..].as_ptr()));
let r = unsafe { u8x16::from(vld1q_p8(a[1..].as_ptr())) };
assert_eq!(r, e)
}
#[simd_test(enable = "neon")]
unsafe fn test_vld1_p16() {
fn test_vld1_p16() {
let a: [p16; 5] = [0, 1, 2, 3, 4];
let e = u16x4::new(1, 2, 3, 4);
let r: u16x4 = transmute(vld1_p16(a[1..].as_ptr()));
let r = unsafe { u16x4::from(vld1_p16(a[1..].as_ptr())) };
assert_eq!(r, e)
}
#[simd_test(enable = "neon")]
unsafe fn test_vld1q_p16() {
fn test_vld1q_p16() {
let a: [p16; 9] = [0, 1, 2, 3, 4, 5, 6, 7, 8];
let e = u16x8::new(1, 2, 3, 4, 5, 6, 7, 8);
let r: u16x8 = transmute(vld1q_p16(a[1..].as_ptr()));
let r = unsafe { u16x8::from(vld1q_p16(a[1..].as_ptr())) };
assert_eq!(r, e)
}
#[simd_test(enable = "neon,aes")]
unsafe fn test_vld1_p64() {
fn test_vld1_p64() {
let a: [p64; 2] = [0, 1];
let e = u64x1::new(1);
let r: u64x1 = transmute(vld1_p64(a[1..].as_ptr()));
let r = unsafe { u64x1::from(vld1_p64(a[1..].as_ptr())) };
assert_eq!(r, e)
}
#[simd_test(enable = "neon,aes")]
unsafe fn test_vld1q_p64() {
fn test_vld1q_p64() {
let a: [p64; 3] = [0, 1, 2];
let e = u64x2::new(1, 2);
let r: u64x2 = transmute(vld1q_p64(a[1..].as_ptr()));
let r = unsafe { u64x2::from(vld1q_p64(a[1..].as_ptr())) };
assert_eq!(r, e)
}
#[simd_test(enable = "neon")]
unsafe fn test_vld1_f32() {
fn test_vld1_f32() {
let a: [f32; 3] = [0., 1., 2.];
let e = f32x2::new(1., 2.);
let r: f32x2 = transmute(vld1_f32(a[1..].as_ptr()));
let r = unsafe { f32x2::from(vld1_f32(a[1..].as_ptr())) };
assert_eq!(r, e)
}
#[simd_test(enable = "neon")]
unsafe fn test_vld1q_f32() {
fn test_vld1q_f32() {
let a: [f32; 5] = [0., 1., 2., 3., 4.];
let e = f32x4::new(1., 2., 3., 4.);
let r: f32x4 = transmute(vld1q_f32(a[1..].as_ptr()));
let r = unsafe { f32x4::from(vld1q_f32(a[1..].as_ptr())) };
assert_eq!(r, e)
}

File diff suppressed because it is too large Load diff

View file

@ -14,11 +14,13 @@ use crate::core_arch::simd::*;
use stdarch_test::simd_test;
#[simd_test(enable = "neon")]
unsafe fn test_vst1_s8() {
fn test_vst1_s8() {
let mut vals = [0_i8; 9];
let a = i8x8::new(1, 2, 3, 4, 5, 6, 7, 8);
vst1_s8(vals[1..].as_mut_ptr(), transmute(a));
unsafe {
vst1_s8(vals[1..].as_mut_ptr(), a.into());
}
assert_eq!(vals[0], 0);
assert_eq!(vals[1], 1);
@ -32,11 +34,13 @@ unsafe fn test_vst1_s8() {
}
#[simd_test(enable = "neon")]
unsafe fn test_vst1q_s8() {
fn test_vst1q_s8() {
let mut vals = [0_i8; 17];
let a = i8x16::new(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
vst1q_s8(vals[1..].as_mut_ptr(), transmute(a));
unsafe {
vst1q_s8(vals[1..].as_mut_ptr(), a.into());
}
assert_eq!(vals[0], 0);
assert_eq!(vals[1], 1);
@ -58,11 +62,13 @@ unsafe fn test_vst1q_s8() {
}
#[simd_test(enable = "neon")]
unsafe fn test_vst1_s16() {
fn test_vst1_s16() {
let mut vals = [0_i16; 5];
let a = i16x4::new(1, 2, 3, 4);
vst1_s16(vals[1..].as_mut_ptr(), transmute(a));
unsafe {
vst1_s16(vals[1..].as_mut_ptr(), a.into());
}
assert_eq!(vals[0], 0);
assert_eq!(vals[1], 1);
@ -72,11 +78,13 @@ unsafe fn test_vst1_s16() {
}
#[simd_test(enable = "neon")]
unsafe fn test_vst1q_s16() {
fn test_vst1q_s16() {
let mut vals = [0_i16; 9];
let a = i16x8::new(1, 2, 3, 4, 5, 6, 7, 8);
vst1q_s16(vals[1..].as_mut_ptr(), transmute(a));
unsafe {
vst1q_s16(vals[1..].as_mut_ptr(), a.into());
}
assert_eq!(vals[0], 0);
assert_eq!(vals[1], 1);
@ -90,11 +98,13 @@ unsafe fn test_vst1q_s16() {
}
#[simd_test(enable = "neon")]
unsafe fn test_vst1_s32() {
fn test_vst1_s32() {
let mut vals = [0_i32; 3];
let a = i32x2::new(1, 2);
vst1_s32(vals[1..].as_mut_ptr(), transmute(a));
unsafe {
vst1_s32(vals[1..].as_mut_ptr(), a.into());
}
assert_eq!(vals[0], 0);
assert_eq!(vals[1], 1);
@ -102,11 +112,13 @@ unsafe fn test_vst1_s32() {
}
#[simd_test(enable = "neon")]
unsafe fn test_vst1q_s32() {
fn test_vst1q_s32() {
let mut vals = [0_i32; 5];
let a = i32x4::new(1, 2, 3, 4);
vst1q_s32(vals[1..].as_mut_ptr(), transmute(a));
unsafe {
vst1q_s32(vals[1..].as_mut_ptr(), a.into());
}
assert_eq!(vals[0], 0);
assert_eq!(vals[1], 1);
@ -116,22 +128,26 @@ unsafe fn test_vst1q_s32() {
}
#[simd_test(enable = "neon")]
unsafe fn test_vst1_s64() {
fn test_vst1_s64() {
let mut vals = [0_i64; 2];
let a = i64x1::new(1);
vst1_s64(vals[1..].as_mut_ptr(), transmute(a));
unsafe {
vst1_s64(vals[1..].as_mut_ptr(), a.into());
}
assert_eq!(vals[0], 0);
assert_eq!(vals[1], 1);
}
#[simd_test(enable = "neon")]
unsafe fn test_vst1q_s64() {
fn test_vst1q_s64() {
let mut vals = [0_i64; 3];
let a = i64x2::new(1, 2);
vst1q_s64(vals[1..].as_mut_ptr(), transmute(a));
unsafe {
vst1q_s64(vals[1..].as_mut_ptr(), a.into());
}
assert_eq!(vals[0], 0);
assert_eq!(vals[1], 1);
@ -139,11 +155,13 @@ unsafe fn test_vst1q_s64() {
}
#[simd_test(enable = "neon")]
unsafe fn test_vst1_u8() {
fn test_vst1_u8() {
let mut vals = [0_u8; 9];
let a = u8x8::new(1, 2, 3, 4, 5, 6, 7, 8);
vst1_u8(vals[1..].as_mut_ptr(), transmute(a));
unsafe {
vst1_u8(vals[1..].as_mut_ptr(), a.into());
}
assert_eq!(vals[0], 0);
assert_eq!(vals[1], 1);
@ -157,11 +175,13 @@ unsafe fn test_vst1_u8() {
}
#[simd_test(enable = "neon")]
unsafe fn test_vst1q_u8() {
fn test_vst1q_u8() {
let mut vals = [0_u8; 17];
let a = u8x16::new(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
vst1q_u8(vals[1..].as_mut_ptr(), transmute(a));
unsafe {
vst1q_u8(vals[1..].as_mut_ptr(), a.into());
}
assert_eq!(vals[0], 0);
assert_eq!(vals[1], 1);
@ -183,11 +203,13 @@ unsafe fn test_vst1q_u8() {
}
#[simd_test(enable = "neon")]
unsafe fn test_vst1_u16() {
fn test_vst1_u16() {
let mut vals = [0_u16; 5];
let a = u16x4::new(1, 2, 3, 4);
vst1_u16(vals[1..].as_mut_ptr(), transmute(a));
unsafe {
vst1_u16(vals[1..].as_mut_ptr(), a.into());
}
assert_eq!(vals[0], 0);
assert_eq!(vals[1], 1);
@ -197,11 +219,13 @@ unsafe fn test_vst1_u16() {
}
#[simd_test(enable = "neon")]
unsafe fn test_vst1q_u16() {
fn test_vst1q_u16() {
let mut vals = [0_u16; 9];
let a = u16x8::new(1, 2, 3, 4, 5, 6, 7, 8);
vst1q_u16(vals[1..].as_mut_ptr(), transmute(a));
unsafe {
vst1q_u16(vals[1..].as_mut_ptr(), a.into());
}
assert_eq!(vals[0], 0);
assert_eq!(vals[1], 1);
@ -215,11 +239,13 @@ unsafe fn test_vst1q_u16() {
}
#[simd_test(enable = "neon")]
unsafe fn test_vst1_u32() {
fn test_vst1_u32() {
let mut vals = [0_u32; 3];
let a = u32x2::new(1, 2);
vst1_u32(vals[1..].as_mut_ptr(), transmute(a));
unsafe {
vst1_u32(vals[1..].as_mut_ptr(), a.into());
}
assert_eq!(vals[0], 0);
assert_eq!(vals[1], 1);
@ -227,11 +253,13 @@ unsafe fn test_vst1_u32() {
}
#[simd_test(enable = "neon")]
unsafe fn test_vst1q_u32() {
fn test_vst1q_u32() {
let mut vals = [0_u32; 5];
let a = u32x4::new(1, 2, 3, 4);
vst1q_u32(vals[1..].as_mut_ptr(), transmute(a));
unsafe {
vst1q_u32(vals[1..].as_mut_ptr(), a.into());
}
assert_eq!(vals[0], 0);
assert_eq!(vals[1], 1);
@ -241,22 +269,26 @@ unsafe fn test_vst1q_u32() {
}
#[simd_test(enable = "neon")]
unsafe fn test_vst1_u64() {
fn test_vst1_u64() {
let mut vals = [0_u64; 2];
let a = u64x1::new(1);
vst1_u64(vals[1..].as_mut_ptr(), transmute(a));
unsafe {
vst1_u64(vals[1..].as_mut_ptr(), a.into());
}
assert_eq!(vals[0], 0);
assert_eq!(vals[1], 1);
}
#[simd_test(enable = "neon")]
unsafe fn test_vst1q_u64() {
fn test_vst1q_u64() {
let mut vals = [0_u64; 3];
let a = u64x2::new(1, 2);
vst1q_u64(vals[1..].as_mut_ptr(), transmute(a));
unsafe {
vst1q_u64(vals[1..].as_mut_ptr(), a.into());
}
assert_eq!(vals[0], 0);
assert_eq!(vals[1], 1);
@ -264,11 +296,13 @@ unsafe fn test_vst1q_u64() {
}
#[simd_test(enable = "neon")]
unsafe fn test_vst1_p8() {
fn test_vst1_p8() {
let mut vals = [0_u8; 9];
let a = u8x8::new(1, 2, 3, 4, 5, 6, 7, 8);
vst1_p8(vals[1..].as_mut_ptr(), transmute(a));
unsafe {
vst1_p8(vals[1..].as_mut_ptr(), a.into());
}
assert_eq!(vals[0], 0);
assert_eq!(vals[1], 1);
@ -282,11 +316,13 @@ unsafe fn test_vst1_p8() {
}
#[simd_test(enable = "neon")]
unsafe fn test_vst1q_p8() {
fn test_vst1q_p8() {
let mut vals = [0_u8; 17];
let a = u8x16::new(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
vst1q_p8(vals[1..].as_mut_ptr(), transmute(a));
unsafe {
vst1q_p8(vals[1..].as_mut_ptr(), a.into());
}
assert_eq!(vals[0], 0);
assert_eq!(vals[1], 1);
@ -308,11 +344,13 @@ unsafe fn test_vst1q_p8() {
}
#[simd_test(enable = "neon")]
unsafe fn test_vst1_p16() {
fn test_vst1_p16() {
let mut vals = [0_u16; 5];
let a = u16x4::new(1, 2, 3, 4);
vst1_p16(vals[1..].as_mut_ptr(), transmute(a));
unsafe {
vst1_p16(vals[1..].as_mut_ptr(), a.into());
}
assert_eq!(vals[0], 0);
assert_eq!(vals[1], 1);
@ -322,11 +360,13 @@ unsafe fn test_vst1_p16() {
}
#[simd_test(enable = "neon")]
unsafe fn test_vst1q_p16() {
fn test_vst1q_p16() {
let mut vals = [0_u16; 9];
let a = u16x8::new(1, 2, 3, 4, 5, 6, 7, 8);
vst1q_p16(vals[1..].as_mut_ptr(), transmute(a));
unsafe {
vst1q_p16(vals[1..].as_mut_ptr(), a.into());
}
assert_eq!(vals[0], 0);
assert_eq!(vals[1], 1);
@ -340,22 +380,26 @@ unsafe fn test_vst1q_p16() {
}
#[simd_test(enable = "neon,aes")]
unsafe fn test_vst1_p64() {
fn test_vst1_p64() {
let mut vals = [0_u64; 2];
let a = u64x1::new(1);
vst1_p64(vals[1..].as_mut_ptr(), transmute(a));
unsafe {
vst1_p64(vals[1..].as_mut_ptr(), a.into());
}
assert_eq!(vals[0], 0);
assert_eq!(vals[1], 1);
}
#[simd_test(enable = "neon,aes")]
unsafe fn test_vst1q_p64() {
fn test_vst1q_p64() {
let mut vals = [0_u64; 3];
let a = u64x2::new(1, 2);
vst1q_p64(vals[1..].as_mut_ptr(), transmute(a));
unsafe {
vst1q_p64(vals[1..].as_mut_ptr(), a.into());
}
assert_eq!(vals[0], 0);
assert_eq!(vals[1], 1);
@ -363,11 +407,13 @@ unsafe fn test_vst1q_p64() {
}
#[simd_test(enable = "neon")]
unsafe fn test_vst1_f32() {
fn test_vst1_f32() {
let mut vals = [0_f32; 3];
let a = f32x2::new(1., 2.);
vst1_f32(vals[1..].as_mut_ptr(), transmute(a));
unsafe {
vst1_f32(vals[1..].as_mut_ptr(), a.into());
}
assert_eq!(vals[0], 0.);
assert_eq!(vals[1], 1.);
@ -375,11 +421,13 @@ unsafe fn test_vst1_f32() {
}
#[simd_test(enable = "neon")]
unsafe fn test_vst1q_f32() {
fn test_vst1q_f32() {
let mut vals = [0_f32; 5];
let a = f32x4::new(1., 2., 3., 4.);
vst1q_f32(vals[1..].as_mut_ptr(), transmute(a));
unsafe {
vst1q_f32(vals[1..].as_mut_ptr(), a.into());
}
assert_eq!(vals[0], 0.);
assert_eq!(vals[1], 1.);

View file

@ -21,19 +21,19 @@ macro_rules! test_vtbl {
) => {
#[cfg(target_endian = "little")]
#[simd_test(enable = "neon")]
unsafe fn $test_name() {
fn $test_name() {
// create table as array, and transmute it to
// arm's table type
let table: $table_t = mem::transmute([$($table_v),*]);
let table: $table_t = unsafe { mem::transmute([$($table_v),*]) };
// For each control vector, perform a table lookup and
// verify the result:
$(
{
let ctrl: $ctrl_t = mem::transmute([$($ctrl_v),*]);
let result = $fn_id(table, mem::transmute(ctrl));
let result: $ctrl_t = mem::transmute(result);
let expected: $ctrl_t = mem::transmute([$($exp_v),*]);
let ctrl: $ctrl_t = unsafe { mem::transmute([$($ctrl_v),*]) };
let result = $fn_id(table, unsafe { mem::transmute(ctrl) });
let result: $ctrl_t = unsafe { mem::transmute(result) };
let expected: $ctrl_t = unsafe { mem::transmute([$($exp_v),*]) };
assert_eq!(result, expected);
}
)*
@ -171,20 +171,19 @@ macro_rules! test_vtbx {
) => {
#[cfg(target_endian = "little")]
#[simd_test(enable = "neon")]
unsafe fn $test_name() {
fn $test_name() {
// create table as array, and transmute it to
// arm's table type
let table: $table_t = mem::transmute([$($table_v),*]);
let ext: $ext_t = mem::transmute([$($ext_v),*]);
let table: $table_t = unsafe { mem::transmute([$($table_v),*]) };
let ext: $ext_t = unsafe { mem::transmute([$($ext_v),*]) };
// For each control vector, perform a table lookup and
// verify the result:
$(
{
let ctrl: $ctrl_t = mem::transmute([$($ctrl_v),*]);
let result = $fn_id(ext, table, mem::transmute(ctrl));
let result: $ctrl_t = mem::transmute(result);
let expected: $ctrl_t = mem::transmute([$($exp_v),*]);
let ctrl: $ctrl_t = unsafe { mem::transmute([$($ctrl_v),*]) };
let result = $fn_id(ext, table, unsafe { mem::transmute(ctrl) });
let result: $ctrl_t = unsafe { mem::transmute(result) };
let expected: $ctrl_t = unsafe { mem::transmute([$($exp_v),*]) };
assert_eq!(result, expected);
}
)*

View file

@ -111,13 +111,13 @@ macro_rules! V_f32 {
macro_rules! to64 {
($t : ident) => {
|v: $t| -> u64 { transmute(v) }
|v: $t| -> u64 { unsafe { transmute(v) } }
};
}
macro_rules! to128 {
($t : ident) => {
|v: $t| -> u128 { transmute(v) }
|v: $t| -> u128 { unsafe { transmute(v) } }
};
}
@ -158,9 +158,7 @@ pub(crate) fn test<T, U, V, W, X>(
macro_rules! gen_test_fn {
($n: ident, $t: ident, $u: ident, $v: ident, $w: ident, $x: ident, $vals: expr, $fill1: expr, $fill2: expr, $cast: expr) => {
pub(crate) fn $n(test_fun: fn($v, $v) -> $w, verify_fun: fn($t, $t) -> $u) {
unsafe {
test::<$t, $u, $v, $w, $x>($vals, $fill1, $fill2, $cast, test_fun, verify_fun)
};
test::<$t, $u, $v, $w, $x>($vals, $fill1, $fill2, $cast, test_fun, verify_fun);
}
};
}

View file

@ -32,12 +32,14 @@
x86_amx_intrinsics,
f16,
aarch64_unstable_target_feature,
target_feature_inline_always,
bigint_helper_methods,
funnel_shifts,
avx10_target_feature,
const_trait_impl,
const_cmp,
const_eval_select
const_eval_select,
maybe_uninit_as_bytes
)]
#![cfg_attr(test, feature(test, abi_vectorcall, stdarch_internal))]
#![deny(clippy::missing_inline_in_public_items)]
@ -87,4 +89,4 @@ pub mod arch {
}
#[allow(unused_imports)]
use core::{array, convert, ffi, fmt, hint, intrinsics, marker, mem, ops, ptr, sync};
use core::{array, cmp, convert, ffi, fmt, hint, intrinsics, marker, mem, ops, ptr, sync};

View file

@ -90,17 +90,10 @@ macro_rules! types {
pub struct $name($v [$elem_type; $len]);
impl $name {
/// Using `my_simd([x; N])` seemingly fails tests,
/// so use this internal helper for it instead.
/// Put the same value in every lane.
#[inline(always)]
$v fn splat(value: $elem_type) -> $name {
#[derive(Copy, Clone)]
#[repr(simd)]
struct JustOne([$elem_type; 1]);
let one = JustOne([value]);
// SAFETY: 0 is always in-bounds because we're shuffling
// a simd type with exactly one element.
unsafe { simd_shuffle!(one, one, [0; $len]) }
unsafe { $crate::intrinsics::simd::simd_splat(value) }
}
/// Returns an array reference containing the entire SIMD vector.
@ -135,6 +128,22 @@ macro_rules! types {
crate::core_arch::simd::debug_simd_finish(f, stringify!($name), self.as_array())
}
}
$(#[$stability])+
impl crate::convert::From<crate::core_arch::simd::Simd<$elem_type, $len>> for $name {
#[inline(always)]
fn from(simd: crate::core_arch::simd::Simd<$elem_type, $len>) -> Self {
unsafe { crate::mem::transmute(simd) }
}
}
$(#[$stability])+
impl crate::convert::From<$name> for crate::core_arch::simd::Simd<$elem_type, $len> {
#[inline(always)]
fn from(simd: $name) -> Self {
unsafe { crate::mem::transmute(simd) }
}
}
)*);
}

View file

@ -23,29 +23,29 @@ unsafe extern "C" {
#[link_name = "llvm.nvvm.barrier0"]
fn syncthreads() -> ();
#[link_name = "llvm.nvvm.read.ptx.sreg.ntid.x"]
fn block_dim_x() -> i32;
fn block_dim_x() -> u32;
#[link_name = "llvm.nvvm.read.ptx.sreg.ntid.y"]
fn block_dim_y() -> i32;
fn block_dim_y() -> u32;
#[link_name = "llvm.nvvm.read.ptx.sreg.ntid.z"]
fn block_dim_z() -> i32;
fn block_dim_z() -> u32;
#[link_name = "llvm.nvvm.read.ptx.sreg.ctaid.x"]
fn block_idx_x() -> i32;
fn block_idx_x() -> u32;
#[link_name = "llvm.nvvm.read.ptx.sreg.ctaid.y"]
fn block_idx_y() -> i32;
fn block_idx_y() -> u32;
#[link_name = "llvm.nvvm.read.ptx.sreg.ctaid.z"]
fn block_idx_z() -> i32;
fn block_idx_z() -> u32;
#[link_name = "llvm.nvvm.read.ptx.sreg.nctaid.x"]
fn grid_dim_x() -> i32;
fn grid_dim_x() -> u32;
#[link_name = "llvm.nvvm.read.ptx.sreg.nctaid.y"]
fn grid_dim_y() -> i32;
fn grid_dim_y() -> u32;
#[link_name = "llvm.nvvm.read.ptx.sreg.nctaid.z"]
fn grid_dim_z() -> i32;
fn grid_dim_z() -> u32;
#[link_name = "llvm.nvvm.read.ptx.sreg.tid.x"]
fn thread_idx_x() -> i32;
fn thread_idx_x() -> u32;
#[link_name = "llvm.nvvm.read.ptx.sreg.tid.y"]
fn thread_idx_y() -> i32;
fn thread_idx_y() -> u32;
#[link_name = "llvm.nvvm.read.ptx.sreg.tid.z"]
fn thread_idx_z() -> i32;
fn thread_idx_z() -> u32;
}
/// Synchronizes all threads in the block.
@ -58,84 +58,84 @@ pub unsafe fn _syncthreads() -> () {
/// x-th thread-block dimension.
#[inline]
#[unstable(feature = "stdarch_nvptx", issue = "111199")]
pub unsafe fn _block_dim_x() -> i32 {
pub unsafe fn _block_dim_x() -> u32 {
block_dim_x()
}
/// y-th thread-block dimension.
#[inline]
#[unstable(feature = "stdarch_nvptx", issue = "111199")]
pub unsafe fn _block_dim_y() -> i32 {
pub unsafe fn _block_dim_y() -> u32 {
block_dim_y()
}
/// z-th thread-block dimension.
#[inline]
#[unstable(feature = "stdarch_nvptx", issue = "111199")]
pub unsafe fn _block_dim_z() -> i32 {
pub unsafe fn _block_dim_z() -> u32 {
block_dim_z()
}
/// x-th thread-block index.
#[inline]
#[unstable(feature = "stdarch_nvptx", issue = "111199")]
pub unsafe fn _block_idx_x() -> i32 {
pub unsafe fn _block_idx_x() -> u32 {
block_idx_x()
}
/// y-th thread-block index.
#[inline]
#[unstable(feature = "stdarch_nvptx", issue = "111199")]
pub unsafe fn _block_idx_y() -> i32 {
pub unsafe fn _block_idx_y() -> u32 {
block_idx_y()
}
/// z-th thread-block index.
#[inline]
#[unstable(feature = "stdarch_nvptx", issue = "111199")]
pub unsafe fn _block_idx_z() -> i32 {
pub unsafe fn _block_idx_z() -> u32 {
block_idx_z()
}
/// x-th block-grid dimension.
#[inline]
#[unstable(feature = "stdarch_nvptx", issue = "111199")]
pub unsafe fn _grid_dim_x() -> i32 {
pub unsafe fn _grid_dim_x() -> u32 {
grid_dim_x()
}
/// y-th block-grid dimension.
#[inline]
#[unstable(feature = "stdarch_nvptx", issue = "111199")]
pub unsafe fn _grid_dim_y() -> i32 {
pub unsafe fn _grid_dim_y() -> u32 {
grid_dim_y()
}
/// z-th block-grid dimension.
#[inline]
#[unstable(feature = "stdarch_nvptx", issue = "111199")]
pub unsafe fn _grid_dim_z() -> i32 {
pub unsafe fn _grid_dim_z() -> u32 {
grid_dim_z()
}
/// x-th thread index.
#[inline]
#[unstable(feature = "stdarch_nvptx", issue = "111199")]
pub unsafe fn _thread_idx_x() -> i32 {
pub unsafe fn _thread_idx_x() -> u32 {
thread_idx_x()
}
/// y-th thread index.
#[inline]
#[unstable(feature = "stdarch_nvptx", issue = "111199")]
pub unsafe fn _thread_idx_y() -> i32 {
pub unsafe fn _thread_idx_y() -> u32 {
thread_idx_y()
}
/// z-th thread index.
#[inline]
#[unstable(feature = "stdarch_nvptx", issue = "111199")]
pub unsafe fn _thread_idx_z() -> i32 {
pub unsafe fn _thread_idx_z() -> u32 {
thread_idx_z()
}

View file

@ -364,17 +364,46 @@ unsafe extern "C" {
fn vrfin(a: vector_float) -> vector_float;
}
impl_from! { i8x16, u8x16, i16x8, u16x8, i32x4, u32x4, f32x4 }
impl_neg! { i8x16 : 0 }
impl_neg! { i16x8 : 0 }
impl_neg! { i32x4 : 0 }
impl_neg! { f32x4 : 0f32 }
#[macro_use]
mod sealed {
use super::*;
#[unstable(feature = "stdarch_powerpc", issue = "111145")]
pub trait VectorNeg {
unsafe fn vec_neg(self) -> Self;
}
macro_rules! impl_neg {
($($v:ty)*) => {
$(
#[unstable(feature = "stdarch_powerpc", issue = "111145")]
impl VectorNeg for $v {
#[inline]
#[target_feature(enable = "altivec")]
unsafe fn vec_neg(self) -> Self {
simd_neg(self)
}
}
)*
}
}
impl_neg! {
vector_signed_char
vector_unsigned_char
vector_bool_char
vector_signed_short
vector_unsigned_short
vector_bool_short
vector_signed_int
vector_unsigned_int
vector_bool_int
vector_float
}
#[unstable(feature = "stdarch_powerpc", issue = "111145")]
pub trait VectorInsert {
type Scalar;
@ -1380,7 +1409,7 @@ mod sealed {
#[inline]
#[target_feature(enable = "altivec")]
unsafe fn $name(v: s_t_l!($ty)) -> s_t_l!($ty) {
v.vec_max(-v)
v.vec_max(simd_neg(v))
}
impl_vec_trait! { [VectorAbs vec_abs] $name (s_t_l!($ty)) }
@ -1428,7 +1457,7 @@ mod sealed {
#[cfg_attr(test, assert_instr(vspltb, IMM4 = 15))]
unsafe fn vspltb<const IMM4: u32>(a: vector_signed_char) -> vector_signed_char {
static_assert_uimm_bits!(IMM4, 4);
simd_shuffle(a, a, const { u32x16::from_array([IMM4; 16]) })
simd_shuffle(a, a, const { u32x16::splat(IMM4) })
}
#[inline]
@ -1436,7 +1465,7 @@ mod sealed {
#[cfg_attr(test, assert_instr(vsplth, IMM3 = 7))]
unsafe fn vsplth<const IMM3: u32>(a: vector_signed_short) -> vector_signed_short {
static_assert_uimm_bits!(IMM3, 3);
simd_shuffle(a, a, const { u32x8::from_array([IMM3; 8]) })
simd_shuffle(a, a, const { u32x8::splat(IMM3) })
}
#[inline]
@ -1445,7 +1474,7 @@ mod sealed {
#[cfg_attr(all(test, target_feature = "vsx"), assert_instr(xxspltw, IMM2 = 3))]
unsafe fn vspltw<const IMM2: u32>(a: vector_signed_int) -> vector_signed_int {
static_assert_uimm_bits!(IMM2, 2);
simd_shuffle(a, a, const { u32x4::from_array([IMM2; 4]) })
simd_shuffle(a, a, const { u32x4::splat(IMM2) })
}
#[unstable(feature = "stdarch_powerpc", issue = "111145")]
@ -4032,6 +4061,14 @@ pub unsafe fn vec_mfvscr() -> vector_unsigned_short {
mfvscr()
}
/// Vector Negate
#[inline]
#[target_feature(enable = "altivec")]
#[unstable(feature = "stdarch_powerpc", issue = "111145")]
pub unsafe fn vec_neg<T: sealed::VectorNeg>(a: T) -> T {
a.vec_neg()
}
/// Vector add.
#[inline]
#[target_feature(enable = "altivec")]
@ -4703,7 +4740,7 @@ mod tests {
for off in 0..16 {
let val: u8x16 = transmute(vec_xl(0, (pat.as_ptr() as *const u8).offset(off)));
for i in 0..16 {
let v = val.extract(i);
let v = val.extract_dyn(i);
assert_eq!(off as usize + i, v as usize);
}
}
@ -4758,7 +4795,7 @@ mod tests {
)];
for off in 0..16 {
let v: u8x16 = transmute(vec_lde(off, pat.as_ptr() as *const u8));
assert_eq!(off as u8, v.extract(off as _));
assert_eq!(off as u8, v.extract_dyn(off as _));
}
}
@ -4767,7 +4804,7 @@ mod tests {
let pat = [u16x8::new(0, 1, 2, 3, 4, 5, 6, 7)];
for off in 0..8 {
let v: u16x8 = transmute(vec_lde(off * 2, pat.as_ptr() as *const u16));
assert_eq!(off as u16, v.extract(off as _));
assert_eq!(off as u16, v.extract_dyn(off as _));
}
}
@ -4776,7 +4813,7 @@ mod tests {
let pat = [u32x4::new(0, 1, 2, 3)];
for off in 0..4 {
let v: u32x4 = transmute(vec_lde(off * 4, pat.as_ptr() as *const u32));
assert_eq!(off as u32, v.extract(off as _));
assert_eq!(off as u32, v.extract_dyn(off as _));
}
}

View file

@ -274,40 +274,6 @@ macro_rules! t_b {
};
}
macro_rules! impl_from {
($s: ident) => {
#[unstable(feature = "stdarch_powerpc", issue = "111145")]
impl From<$s> for s_t_l!($s) {
#[inline]
fn from (v: $s) -> Self {
unsafe {
transmute(v)
}
}
}
};
($($s: ident),*) => {
$(
impl_from! { $s }
)*
};
}
macro_rules! impl_neg {
($s: ident : $zero: expr) => {
#[unstable(feature = "stdarch_powerpc", issue = "111145")]
impl crate::ops::Neg for s_t_l!($s) {
type Output = s_t_l!($s);
#[inline]
fn neg(self) -> Self::Output {
unsafe { simd_neg(self) }
}
}
};
}
pub(crate) use impl_from;
pub(crate) use impl_neg;
pub(crate) use impl_vec_trait;
pub(crate) use s_t_l;
pub(crate) use t_b;

View file

@ -431,40 +431,6 @@ macro_rules! t_b {
};
}
macro_rules! impl_from {
($s: ident) => {
#[unstable(feature = "stdarch_s390x", issue = "135681")]
impl From<$s> for s_t_l!($s) {
#[inline]
fn from (v: $s) -> Self {
unsafe {
transmute(v)
}
}
}
};
($($s: ident),*) => {
$(
impl_from! { $s }
)*
};
}
macro_rules! impl_neg {
($s: ident : $zero: expr) => {
#[unstable(feature = "stdarch_s390x", issue = "135681")]
impl crate::ops::Neg for s_t_l!($s) {
type Output = s_t_l!($s);
#[inline]
fn neg(self) -> Self::Output {
unsafe { simd_neg(self) }
}
}
};
}
pub(crate) use impl_from;
pub(crate) use impl_neg;
pub(crate) use impl_vec_trait;
pub(crate) use l_t_t;
pub(crate) use s_t_l;

View file

@ -281,17 +281,14 @@ unsafe extern "unadjusted" {
#[link_name = "llvm.s390.vfenezbs"] fn vfenezbs(a: i8x16, b: i8x16) -> PackedTuple<i8x16, i32>;
#[link_name = "llvm.s390.vfenezhs"] fn vfenezhs(a: i16x8, b: i16x8) -> PackedTuple<i16x8, i32>;
#[link_name = "llvm.s390.vfenezfs"] fn vfenezfs(a: i32x4, b: i32x4) -> PackedTuple<i32x4, i32>;
#[link_name = "llvm.s390.vclfnhs"] fn vclfnhs(a: vector_signed_short, immarg: i32) -> vector_float;
#[link_name = "llvm.s390.vclfnls"] fn vclfnls(a: vector_signed_short, immarg: i32) -> vector_float;
#[link_name = "llvm.s390.vcfn"] fn vcfn(a: vector_signed_short, immarg: i32) -> vector_signed_short;
#[link_name = "llvm.s390.vcnf"] fn vcnf(a: vector_signed_short, immarg: i32) -> vector_signed_short;
#[link_name = "llvm.s390.vcrnfs"] fn vcrnfs(a: vector_float, b: vector_float, immarg: i32) -> vector_signed_short;
}
impl_from! { i8x16, u8x16, i16x8, u16x8, i32x4, u32x4, i64x2, u64x2, f32x4, f64x2 }
impl_neg! { i8x16 : 0 }
impl_neg! { i16x8 : 0 }
impl_neg! { i32x4 : 0 }
impl_neg! { i64x2 : 0 }
impl_neg! { f32x4 : 0f32 }
impl_neg! { f64x2 : 0f64 }
#[repr(simd)]
struct ShuffleMask<const N: usize>([u32; N]);
@ -439,6 +436,43 @@ enum FindImm {
mod sealed {
use super::*;
#[unstable(feature = "stdarch_s390x", issue = "135681")]
pub trait VectorNeg {
unsafe fn vec_neg(self) -> Self;
}
macro_rules! impl_neg {
($($v:ty)*) => {
$(
#[unstable(feature = "stdarch_s390x", issue = "135681")]
impl VectorNeg for $v {
#[inline]
#[target_feature(enable = "vector")]
unsafe fn vec_neg(self) -> Self {
simd_neg(self)
}
}
)*
}
}
impl_neg! {
vector_signed_char
vector_unsigned_char
vector_signed_short
vector_unsigned_short
vector_signed_int
vector_unsigned_int
vector_signed_long_long
vector_unsigned_long_long
vector_float
vector_double
}
#[unstable(feature = "stdarch_s390x", issue = "135681")]
pub trait VectorAdd<Other> {
type Result;
@ -761,7 +795,7 @@ mod sealed {
#[inline]
#[target_feature(enable = "vector")]
unsafe fn $name(v: s_t_l!($ty)) -> s_t_l!($ty) {
v.vec_max(-v)
v.vec_max(simd_neg(v))
}
impl_vec_trait! { [VectorAbs vec_abs] $name (s_t_l!($ty)) }
@ -4055,6 +4089,14 @@ unsafe fn __lcbb<const BLOCK_BOUNDARY: u16>(ptr: *const u8) -> u32 {
lcbb(ptr, const { validate_block_boundary(BLOCK_BOUNDARY) })
}
/// Vector Negate
#[inline]
#[target_feature(enable = "vector")]
#[unstable(feature = "stdarch_s390x", issue = "135681")]
pub unsafe fn vec_neg<T: sealed::VectorNeg>(a: T) -> T {
a.vec_neg()
}
/// Vector Add
#[inline]
#[target_feature(enable = "vector")]
@ -5875,6 +5917,74 @@ pub unsafe fn vec_promote<T: sealed::VectorPromote>(a: T::ElementType, b: i32) -
T::vec_promote(a, b)
}
/// Converts the left-most half of `a` to a vector of single-precision numbers.
/// The format of the source vector elements is specified by `B`.
#[inline]
#[target_feature(enable = "nnp-assist")]
#[cfg_attr(test, assert_instr(vclfnh, B = 0))]
#[unstable(feature = "stdarch_s390x", issue = "135681")]
pub unsafe fn vec_extend_to_fp32_hi<const B: i32>(a: vector_signed_short) -> vector_float {
// On processors implementing the IBM z16 architecture, only the value 0 is supported.
static_assert_uimm_bits!(B, 4);
vclfnhs(a, B)
}
/// Converts the right-most half of `a` to a vector of single-precision numbers.
/// The format of the source vector elements is specified by `B`.
#[inline]
#[target_feature(enable = "nnp-assist")]
#[cfg_attr(test, assert_instr(vclfnl, B = 0))]
#[unstable(feature = "stdarch_s390x", issue = "135681")]
pub unsafe fn vec_extend_to_fp32_lo<const B: i32>(a: vector_signed_short) -> vector_float {
// On processors implementing the IBM z16 architecture, only the value 0 is supported.
static_assert_uimm_bits!(B, 4);
vclfnls(a, B)
}
/// Converts the elements of vector `a` to the 16-bit IEEE floating point format.
/// The format of the source vector elements is specified by `B`.
#[inline]
#[target_feature(enable = "nnp-assist")]
#[cfg_attr(test, assert_instr(vcfn, B = 0))]
#[unstable(feature = "stdarch_s390x", issue = "135681")]
pub unsafe fn vec_convert_to_fp16<const B: i32>(a: vector_signed_short) -> vector_signed_short {
// On processors implementing the IBM z16 architecture, only the value 0 is supported.
static_assert_uimm_bits!(B, 4);
vcfn(a, B)
}
/// Converts the elements of vector `a` to an internal floating point format.
/// The format of the target vector elements is specified by `B`.
#[inline]
#[target_feature(enable = "nnp-assist")]
#[cfg_attr(test, assert_instr(vcnf, B = 0))]
#[unstable(feature = "stdarch_s390x", issue = "135681")]
pub unsafe fn vec_convert_from_fp16<const B: i32>(a: vector_signed_short) -> vector_signed_short {
// On processors implementing the IBM z16 architecture, only the value 0 is supported.
static_assert_uimm_bits!(B, 4);
vcnf(a, B)
}
/// Converts the elements of single-precision vectors `a` and `b` to an internal floating point
/// format with 16-bit sized elements. The format of the target vector elements is specified by `C`.
#[inline]
#[target_feature(enable = "nnp-assist")]
#[unstable(feature = "stdarch_s390x", issue = "135681")]
#[cfg_attr(test, assert_instr(vcrnf, C = 0))]
pub unsafe fn vec_round_from_fp32<const C: i32>(
a: vector_float,
b: vector_float,
) -> vector_signed_short {
// On processors implementing the IBM z16 architecture, only the value 0 is supported.
static_assert_uimm_bits!(C, 4);
vcrnfs(a, b, C)
}
#[cfg(test)]
mod tests {
use super::*;

View file

@ -16,129 +16,216 @@ pub(crate) const unsafe fn simd_imin<T: Copy>(a: T, b: T) -> T {
crate::intrinsics::simd::simd_select(mask, a, b)
}
/// SAFETY: All bits patterns must be valid
pub(crate) unsafe trait SimdElement:
Copy + const PartialEq + crate::fmt::Debug
{
}
unsafe impl SimdElement for u8 {}
unsafe impl SimdElement for u16 {}
unsafe impl SimdElement for u32 {}
unsafe impl SimdElement for u64 {}
unsafe impl SimdElement for i8 {}
unsafe impl SimdElement for i16 {}
unsafe impl SimdElement for i32 {}
unsafe impl SimdElement for i64 {}
unsafe impl SimdElement for f16 {}
unsafe impl SimdElement for f32 {}
unsafe impl SimdElement for f64 {}
#[repr(simd)]
#[derive(Copy)]
pub(crate) struct Simd<T: SimdElement, const N: usize>([T; N]);
impl<T: SimdElement, const N: usize> Simd<T, N> {
/// A value of this type where all elements are zeroed out.
// SAFETY: `T` implements `SimdElement`, so it is zeroable.
pub(crate) const ZERO: Self = unsafe { crate::mem::zeroed() };
#[inline(always)]
pub(crate) const fn from_array(elements: [T; N]) -> Self {
Self(elements)
}
#[inline]
#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")]
pub(crate) const fn splat(value: T) -> Self {
unsafe { crate::intrinsics::simd::simd_splat(value) }
}
/// Extract the element at position `index`. Note that `index` is not a constant so this
/// operation is not efficient on most platforms. Use for testing only.
#[inline]
#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")]
pub(crate) const fn extract_dyn(&self, index: usize) -> T {
assert!(index < N);
// SAFETY: self is a vector, T its element type.
unsafe { crate::intrinsics::simd::simd_extract_dyn(*self, index as u32) }
}
#[inline]
pub(crate) const fn as_array(&self) -> &[T; N] {
let simd_ptr: *const Self = self;
let array_ptr: *const [T; N] = simd_ptr.cast();
// SAFETY: We can always read the prefix of a simd type as an array.
// There might be more padding afterwards for some widths, but
// that's not a problem for reading less than that.
unsafe { &*array_ptr }
}
}
// `#[derive(Clone)]` causes ICE "Projecting into SIMD type core_arch::simd::Simd is banned by MCP#838"
impl<T: SimdElement, const N: usize> Clone for Simd<T, N> {
#[inline]
fn clone(&self) -> Self {
*self
}
}
#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")]
impl<T: SimdElement, const N: usize> const crate::cmp::PartialEq for Simd<T, N> {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.as_array() == other.as_array()
}
}
impl<T: SimdElement, const N: usize> crate::fmt::Debug for Simd<T, N> {
#[inline]
fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result {
debug_simd_finish(f, "Simd", self.as_array())
}
}
impl<const N: usize> Simd<f16, N> {
#[inline]
pub(crate) const fn to_bits(self) -> Simd<u16, N> {
assert!(size_of::<Self>() == size_of::<Simd<u16, N>>());
unsafe { crate::mem::transmute_copy(&self) }
}
#[inline]
pub(crate) const fn from_bits(bits: Simd<u16, N>) -> Self {
assert!(size_of::<Self>() == size_of::<Simd<u16, N>>());
unsafe { crate::mem::transmute_copy(&bits) }
}
}
impl<const N: usize> Simd<f32, N> {
#[inline]
pub(crate) const fn to_bits(self) -> Simd<u32, N> {
assert!(size_of::<Self>() == size_of::<Simd<u32, N>>());
unsafe { crate::mem::transmute_copy(&self) }
}
#[inline]
pub(crate) const fn from_bits(bits: Simd<u32, N>) -> Self {
assert!(size_of::<Self>() == size_of::<Simd<u32, N>>());
unsafe { crate::mem::transmute_copy(&bits) }
}
}
impl<const N: usize> Simd<f64, N> {
#[inline]
pub(crate) const fn to_bits(self) -> Simd<u64, N> {
assert!(size_of::<Self>() == size_of::<Simd<u64, N>>());
unsafe { crate::mem::transmute_copy(&self) }
}
#[inline]
pub(crate) const fn from_bits(bits: Simd<u64, N>) -> Self {
assert!(size_of::<Self>() == size_of::<Simd<u64, N>>());
unsafe { crate::mem::transmute_copy(&bits) }
}
}
macro_rules! simd_ty {
($id:ident [$elem_type:ty ; $len:literal]: $($param_name:ident),*) => {
#[repr(simd)]
#[derive(Copy, Clone)]
pub(crate) struct $id([$elem_type; $len]);
pub(crate) type $id = Simd<$elem_type, $len>;
#[allow(clippy::use_self)]
impl $id {
/// A value of this type where all elements are zeroed out.
pub(crate) const ZERO: Self = unsafe { crate::mem::zeroed() };
#[inline(always)]
pub(crate) const fn new($($param_name: $elem_type),*) -> Self {
$id([$($param_name),*])
}
#[inline(always)]
pub(crate) const fn from_array(elements: [$elem_type; $len]) -> Self {
$id(elements)
}
// FIXME: Workaround rust@60637
#[inline(always)]
#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")]
pub(crate) const fn splat(value: $elem_type) -> Self {
#[derive(Copy, Clone)]
#[repr(simd)]
struct JustOne([$elem_type; 1]);
let one = JustOne([value]);
// SAFETY: 0 is always in-bounds because we're shuffling
// a simd type with exactly one element.
unsafe { simd_shuffle!(one, one, [0; $len]) }
}
/// Extract the element at position `index`.
/// `index` is not a constant so this is not efficient!
/// Use for testing only.
// FIXME: Workaround rust@60637
#[inline(always)]
pub(crate) const fn extract(&self, index: usize) -> $elem_type {
self.as_array()[index]
}
#[inline]
pub(crate) const fn as_array(&self) -> &[$elem_type; $len] {
let simd_ptr: *const Self = self;
let array_ptr: *const [$elem_type; $len] = simd_ptr.cast();
// SAFETY: We can always read the prefix of a simd type as an array.
// There might be more padding afterwards for some widths, but
// that's not a problem for reading less than that.
unsafe { &*array_ptr }
}
}
#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")]
const impl core::cmp::PartialEq for $id {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.as_array() == other.as_array()
}
}
impl core::fmt::Debug for $id {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
debug_simd_finish(f, stringify!($id), self.as_array())
Self([$($param_name),*])
}
}
}
}
#[repr(simd)]
#[derive(Copy)]
pub(crate) struct SimdM<T: SimdElement, const N: usize>([T; N]);
impl<T: SimdElement, const N: usize> SimdM<T, N> {
#[inline(always)]
const fn bool_to_internal(x: bool) -> T {
// SAFETY: `T` implements `SimdElement`, so all bit patterns are valid.
let zeros = const { unsafe { crate::mem::zeroed::<T>() } };
let ones = const {
// Ideally, this would be `transmute([0xFFu8; size_of::<T>()])`, but
// `size_of::<T>()` is not allowed to use a generic parameter there.
let mut r = crate::mem::MaybeUninit::<T>::uninit();
let mut i = 0;
while i < crate::mem::size_of::<T>() {
r.as_bytes_mut()[i] = crate::mem::MaybeUninit::new(0xFF);
i += 1;
}
unsafe { r.assume_init() }
};
[zeros, ones][x as usize]
}
#[inline]
#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")]
pub(crate) const fn splat(value: bool) -> Self {
unsafe { crate::intrinsics::simd::simd_splat(value) }
}
#[inline]
pub(crate) const fn as_array(&self) -> &[T; N] {
let simd_ptr: *const Self = self;
let array_ptr: *const [T; N] = simd_ptr.cast();
// SAFETY: We can always read the prefix of a simd type as an array.
// There might be more padding afterwards for some widths, but
// that's not a problem for reading less than that.
unsafe { &*array_ptr }
}
}
// `#[derive(Clone)]` causes ICE "Projecting into SIMD type core_arch::simd::SimdM is banned by MCP#838"
impl<T: SimdElement, const N: usize> Clone for SimdM<T, N> {
#[inline]
fn clone(&self) -> Self {
*self
}
}
#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")]
impl<T: SimdElement, const N: usize> const crate::cmp::PartialEq for SimdM<T, N> {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.as_array() == other.as_array()
}
}
impl<T: SimdElement, const N: usize> crate::fmt::Debug for SimdM<T, N> {
#[inline]
fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result {
debug_simd_finish(f, "SimdM", self.as_array())
}
}
macro_rules! simd_m_ty {
($id:ident [$elem_type:ident ; $len:literal]: $($param_name:ident),*) => {
#[repr(simd)]
#[derive(Copy, Clone)]
pub(crate) struct $id([$elem_type; $len]);
pub(crate) type $id = SimdM<$elem_type, $len>;
#[allow(clippy::use_self)]
impl $id {
#[inline(always)]
const fn bool_to_internal(x: bool) -> $elem_type {
[0 as $elem_type, !(0 as $elem_type)][x as usize]
}
#[inline(always)]
pub(crate) const fn new($($param_name: bool),*) -> Self {
$id([$(Self::bool_to_internal($param_name)),*])
}
// FIXME: Workaround rust@60637
#[inline(always)]
#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")]
pub(crate) const fn splat(value: bool) -> Self {
#[derive(Copy, Clone)]
#[repr(simd)]
struct JustOne([$elem_type; 1]);
let one = JustOne([Self::bool_to_internal(value)]);
// SAFETY: 0 is always in-bounds because we're shuffling
// a simd type with exactly one element.
unsafe { simd_shuffle!(one, one, [0; $len]) }
}
#[inline]
pub(crate) const fn as_array(&self) -> &[$elem_type; $len] {
let simd_ptr: *const Self = self;
let array_ptr: *const [$elem_type; $len] = simd_ptr.cast();
// SAFETY: We can always read the prefix of a simd type as an array.
// There might be more padding afterwards for some widths, but
// that's not a problem for reading less than that.
unsafe { &*array_ptr }
}
}
#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")]
const impl core::cmp::PartialEq for $id {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.as_array() == other.as_array()
}
}
impl core::fmt::Debug for $id {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
debug_simd_finish(f, stringify!($id), self.as_array())
Self([$(Self::bool_to_internal($param_name)),*])
}
}
}

View file

@ -2746,7 +2746,7 @@ pub const fn _mm256_setr_epi64x(a: i64, b: i64, c: i64, d: i64) -> __m256i {
#[stable(feature = "simd_x86", since = "1.27.0")]
#[rustc_const_unstable(feature = "stdarch_const_x86", issue = "149298")]
pub const fn _mm256_set1_pd(a: f64) -> __m256d {
_mm256_setr_pd(a, a, a, a)
f64x4::splat(a).as_m256d()
}
/// Broadcasts single-precision (32-bit) floating-point value `a` to all
@ -2759,7 +2759,7 @@ pub const fn _mm256_set1_pd(a: f64) -> __m256d {
#[stable(feature = "simd_x86", since = "1.27.0")]
#[rustc_const_unstable(feature = "stdarch_const_x86", issue = "149298")]
pub const fn _mm256_set1_ps(a: f32) -> __m256 {
_mm256_setr_ps(a, a, a, a, a, a, a, a)
f32x8::splat(a).as_m256()
}
/// Broadcasts 8-bit integer `a` to all elements of returned vector.
@ -2772,13 +2772,7 @@ pub const fn _mm256_set1_ps(a: f32) -> __m256 {
#[stable(feature = "simd_x86", since = "1.27.0")]
#[rustc_const_unstable(feature = "stdarch_const_x86", issue = "149298")]
pub const fn _mm256_set1_epi8(a: i8) -> __m256i {
#[rustfmt::skip]
_mm256_setr_epi8(
a, a, a, a, a, a, a, a,
a, a, a, a, a, a, a, a,
a, a, a, a, a, a, a, a,
a, a, a, a, a, a, a, a,
)
i8x32::splat(a).as_m256i()
}
/// Broadcasts 16-bit integer `a` to all elements of returned vector.
@ -2793,7 +2787,7 @@ pub const fn _mm256_set1_epi8(a: i8) -> __m256i {
#[stable(feature = "simd_x86", since = "1.27.0")]
#[rustc_const_unstable(feature = "stdarch_const_x86", issue = "149298")]
pub const fn _mm256_set1_epi16(a: i16) -> __m256i {
_mm256_setr_epi16(a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a)
i16x16::splat(a).as_m256i()
}
/// Broadcasts 32-bit integer `a` to all elements of returned vector.
@ -2806,7 +2800,7 @@ pub const fn _mm256_set1_epi16(a: i16) -> __m256i {
#[stable(feature = "simd_x86", since = "1.27.0")]
#[rustc_const_unstable(feature = "stdarch_const_x86", issue = "149298")]
pub const fn _mm256_set1_epi32(a: i32) -> __m256i {
_mm256_setr_epi32(a, a, a, a, a, a, a, a)
i32x8::splat(a).as_m256i()
}
/// Broadcasts 64-bit integer `a` to all elements of returned vector.
@ -2821,7 +2815,7 @@ pub const fn _mm256_set1_epi32(a: i32) -> __m256i {
#[stable(feature = "simd_x86", since = "1.27.0")]
#[rustc_const_unstable(feature = "stdarch_const_x86", issue = "149298")]
pub const fn _mm256_set1_epi64x(a: i64) -> __m256i {
_mm256_setr_epi64x(a, a, a, a)
i64x4::splat(a).as_m256i()
}
/// Cast vector of type __m256d to type __m256.

View file

@ -932,7 +932,7 @@ pub const fn _mm_set_ss(a: f32) -> __m128 {
#[stable(feature = "simd_x86", since = "1.27.0")]
#[rustc_const_unstable(feature = "stdarch_const_x86", issue = "149298")]
pub const fn _mm_set1_ps(a: f32) -> __m128 {
__m128([a, a, a, a])
f32x4::splat(a).as_m128()
}
/// Alias for [`_mm_set1_ps`](fn._mm_set1_ps.html)
@ -2079,7 +2079,7 @@ pub unsafe fn _mm_stream_ps(mem_addr: *mut f32, a: __m128) {
#[cfg(test)]
mod tests {
use crate::core_arch::assert_eq_const as assert_eq;
use crate::{hint::black_box, mem::transmute, ptr};
use crate::{hint::black_box, ptr};
use std::boxed;
use stdarch_test::simd_test;
@ -2221,7 +2221,7 @@ mod tests {
}
#[simd_test(enable = "sse")]
unsafe fn test_mm_min_ps() {
fn test_mm_min_ps() {
let a = _mm_setr_ps(-1.0, 5.0, 0.0, -10.0);
let b = _mm_setr_ps(-100.0, 20.0, 0.0, -5.0);
let r = _mm_min_ps(a, b);
@ -2234,10 +2234,10 @@ mod tests {
// `r1` to `a` and `r2` to `b`.
let a = _mm_setr_ps(-0.0, 0.0, 0.0, 0.0);
let b = _mm_setr_ps(0.0, 0.0, 0.0, 0.0);
let r1: [u8; 16] = transmute(_mm_min_ps(a, b));
let r2: [u8; 16] = transmute(_mm_min_ps(b, a));
let a: [u8; 16] = transmute(a);
let b: [u8; 16] = transmute(b);
let r1 = _mm_min_ps(a, b).as_f32x4().to_bits();
let r2 = _mm_min_ps(b, a).as_f32x4().to_bits();
let a = a.as_f32x4().to_bits();
let b = b.as_f32x4().to_bits();
assert_eq!(r1, b);
assert_eq!(r2, a);
assert_ne!(a, b); // sanity check that -0.0 is actually present
@ -2252,7 +2252,7 @@ mod tests {
}
#[simd_test(enable = "sse")]
unsafe fn test_mm_max_ps() {
fn test_mm_max_ps() {
let a = _mm_setr_ps(-1.0, 5.0, 0.0, -10.0);
let b = _mm_setr_ps(-100.0, 20.0, 0.0, -5.0);
let r = _mm_max_ps(a, b);
@ -2261,67 +2261,67 @@ mod tests {
// Check SSE-specific semantics for -0.0 handling.
let a = _mm_setr_ps(-0.0, 0.0, 0.0, 0.0);
let b = _mm_setr_ps(0.0, 0.0, 0.0, 0.0);
let r1: [u8; 16] = transmute(_mm_max_ps(a, b));
let r2: [u8; 16] = transmute(_mm_max_ps(b, a));
let a: [u8; 16] = transmute(a);
let b: [u8; 16] = transmute(b);
let r1 = _mm_max_ps(a, b).as_f32x4().to_bits();
let r2 = _mm_max_ps(b, a).as_f32x4().to_bits();
let a = a.as_f32x4().to_bits();
let b = b.as_f32x4().to_bits();
assert_eq!(r1, b);
assert_eq!(r2, a);
assert_ne!(a, b); // sanity check that -0.0 is actually present
}
#[simd_test(enable = "sse")]
const unsafe fn test_mm_and_ps() {
let a = transmute(u32x4::splat(0b0011));
let b = transmute(u32x4::splat(0b0101));
const fn test_mm_and_ps() {
let a = f32x4::from_bits(u32x4::splat(0b0011)).as_m128();
let b = f32x4::from_bits(u32x4::splat(0b0101)).as_m128();
let r = _mm_and_ps(*black_box(&a), *black_box(&b));
let e = transmute(u32x4::splat(0b0001));
let e = f32x4::from_bits(u32x4::splat(0b0001)).as_m128();
assert_eq_m128(r, e);
}
#[simd_test(enable = "sse")]
const unsafe fn test_mm_andnot_ps() {
let a = transmute(u32x4::splat(0b0011));
let b = transmute(u32x4::splat(0b0101));
const fn test_mm_andnot_ps() {
let a = f32x4::from_bits(u32x4::splat(0b0011)).as_m128();
let b = f32x4::from_bits(u32x4::splat(0b0101)).as_m128();
let r = _mm_andnot_ps(*black_box(&a), *black_box(&b));
let e = transmute(u32x4::splat(0b0100));
let e = f32x4::from_bits(u32x4::splat(0b0100)).as_m128();
assert_eq_m128(r, e);
}
#[simd_test(enable = "sse")]
const unsafe fn test_mm_or_ps() {
let a = transmute(u32x4::splat(0b0011));
let b = transmute(u32x4::splat(0b0101));
const fn test_mm_or_ps() {
let a = f32x4::from_bits(u32x4::splat(0b0011)).as_m128();
let b = f32x4::from_bits(u32x4::splat(0b0101)).as_m128();
let r = _mm_or_ps(*black_box(&a), *black_box(&b));
let e = transmute(u32x4::splat(0b0111));
let e = f32x4::from_bits(u32x4::splat(0b0111)).as_m128();
assert_eq_m128(r, e);
}
#[simd_test(enable = "sse")]
const unsafe fn test_mm_xor_ps() {
let a = transmute(u32x4::splat(0b0011));
let b = transmute(u32x4::splat(0b0101));
const fn test_mm_xor_ps() {
let a = f32x4::from_bits(u32x4::splat(0b0011)).as_m128();
let b = f32x4::from_bits(u32x4::splat(0b0101)).as_m128();
let r = _mm_xor_ps(*black_box(&a), *black_box(&b));
let e = transmute(u32x4::splat(0b0110));
let e = f32x4::from_bits(u32x4::splat(0b0110)).as_m128();
assert_eq_m128(r, e);
}
#[simd_test(enable = "sse")]
unsafe fn test_mm_cmpeq_ss() {
fn test_mm_cmpeq_ss() {
let a = _mm_setr_ps(1.0, 2.0, 3.0, 4.0);
let b = _mm_setr_ps(-1.0, 5.0, 6.0, 7.0);
let r: u32x4 = transmute(_mm_cmpeq_ss(a, b));
let e: u32x4 = transmute(_mm_setr_ps(f32::from_bits(0), 2.0, 3.0, 4.0));
let r = _mm_cmpeq_ss(a, b).as_f32x4().to_bits();
let e = f32x4::new(f32::from_bits(0), 2.0, 3.0, 4.0).to_bits();
assert_eq!(r, e);
let b2 = _mm_setr_ps(1.0, 5.0, 6.0, 7.0);
let r2: u32x4 = transmute(_mm_cmpeq_ss(a, b2));
let e2: u32x4 = transmute(_mm_setr_ps(f32::from_bits(0xffffffff), 2.0, 3.0, 4.0));
let r2 = _mm_cmpeq_ss(a, b2).as_f32x4().to_bits();
let e2 = f32x4::new(f32::from_bits(0xffffffff), 2.0, 3.0, 4.0).to_bits();
assert_eq!(r2, e2);
}
#[simd_test(enable = "sse")]
unsafe fn test_mm_cmplt_ss() {
fn test_mm_cmplt_ss() {
let a = _mm_setr_ps(1.0, 2.0, 3.0, 4.0);
let b = _mm_setr_ps(0.0, 5.0, 6.0, 7.0);
let c = _mm_setr_ps(1.0, 5.0, 6.0, 7.0);
@ -2331,21 +2331,21 @@ mod tests {
let c1 = 0u32; // a.extract(0) < c.extract(0)
let d1 = !0u32; // a.extract(0) < d.extract(0)
let rb: u32x4 = transmute(_mm_cmplt_ss(a, b));
let eb: u32x4 = transmute(_mm_setr_ps(f32::from_bits(b1), 2.0, 3.0, 4.0));
let rb = _mm_cmplt_ss(a, b).as_f32x4().to_bits();
let eb = f32x4::new(f32::from_bits(b1), 2.0, 3.0, 4.0).to_bits();
assert_eq!(rb, eb);
let rc: u32x4 = transmute(_mm_cmplt_ss(a, c));
let ec: u32x4 = transmute(_mm_setr_ps(f32::from_bits(c1), 2.0, 3.0, 4.0));
let rc = _mm_cmplt_ss(a, c).as_f32x4().to_bits();
let ec = f32x4::new(f32::from_bits(c1), 2.0, 3.0, 4.0).to_bits();
assert_eq!(rc, ec);
let rd: u32x4 = transmute(_mm_cmplt_ss(a, d));
let ed: u32x4 = transmute(_mm_setr_ps(f32::from_bits(d1), 2.0, 3.0, 4.0));
let rd = _mm_cmplt_ss(a, d).as_f32x4().to_bits();
let ed = f32x4::new(f32::from_bits(d1), 2.0, 3.0, 4.0).to_bits();
assert_eq!(rd, ed);
}
#[simd_test(enable = "sse")]
unsafe fn test_mm_cmple_ss() {
fn test_mm_cmple_ss() {
let a = _mm_setr_ps(1.0, 2.0, 3.0, 4.0);
let b = _mm_setr_ps(0.0, 5.0, 6.0, 7.0);
let c = _mm_setr_ps(1.0, 5.0, 6.0, 7.0);
@ -2355,21 +2355,21 @@ mod tests {
let c1 = !0u32; // a.extract(0) <= c.extract(0)
let d1 = !0u32; // a.extract(0) <= d.extract(0)
let rb: u32x4 = transmute(_mm_cmple_ss(a, b));
let eb: u32x4 = transmute(_mm_setr_ps(f32::from_bits(b1), 2.0, 3.0, 4.0));
let rb = _mm_cmple_ss(a, b).as_f32x4().to_bits();
let eb = f32x4::new(f32::from_bits(b1), 2.0, 3.0, 4.0).to_bits();
assert_eq!(rb, eb);
let rc: u32x4 = transmute(_mm_cmple_ss(a, c));
let ec: u32x4 = transmute(_mm_setr_ps(f32::from_bits(c1), 2.0, 3.0, 4.0));
let rc = _mm_cmple_ss(a, c).as_f32x4().to_bits();
let ec = f32x4::new(f32::from_bits(c1), 2.0, 3.0, 4.0).to_bits();
assert_eq!(rc, ec);
let rd: u32x4 = transmute(_mm_cmple_ss(a, d));
let ed: u32x4 = transmute(_mm_setr_ps(f32::from_bits(d1), 2.0, 3.0, 4.0));
let rd = _mm_cmple_ss(a, d).as_f32x4().to_bits();
let ed = f32x4::new(f32::from_bits(d1), 2.0, 3.0, 4.0).to_bits();
assert_eq!(rd, ed);
}
#[simd_test(enable = "sse")]
unsafe fn test_mm_cmpgt_ss() {
fn test_mm_cmpgt_ss() {
let a = _mm_setr_ps(1.0, 2.0, 3.0, 4.0);
let b = _mm_setr_ps(0.0, 5.0, 6.0, 7.0);
let c = _mm_setr_ps(1.0, 5.0, 6.0, 7.0);
@ -2379,21 +2379,21 @@ mod tests {
let c1 = 0u32; // a.extract(0) > c.extract(0)
let d1 = 0u32; // a.extract(0) > d.extract(0)
let rb: u32x4 = transmute(_mm_cmpgt_ss(a, b));
let eb: u32x4 = transmute(_mm_setr_ps(f32::from_bits(b1), 2.0, 3.0, 4.0));
let rb = _mm_cmpgt_ss(a, b).as_f32x4().to_bits();
let eb = f32x4::new(f32::from_bits(b1), 2.0, 3.0, 4.0).to_bits();
assert_eq!(rb, eb);
let rc: u32x4 = transmute(_mm_cmpgt_ss(a, c));
let ec: u32x4 = transmute(_mm_setr_ps(f32::from_bits(c1), 2.0, 3.0, 4.0));
let rc = _mm_cmpgt_ss(a, c).as_f32x4().to_bits();
let ec = f32x4::new(f32::from_bits(c1), 2.0, 3.0, 4.0).to_bits();
assert_eq!(rc, ec);
let rd: u32x4 = transmute(_mm_cmpgt_ss(a, d));
let ed: u32x4 = transmute(_mm_setr_ps(f32::from_bits(d1), 2.0, 3.0, 4.0));
let rd = _mm_cmpgt_ss(a, d).as_f32x4().to_bits();
let ed = f32x4::new(f32::from_bits(d1), 2.0, 3.0, 4.0).to_bits();
assert_eq!(rd, ed);
}
#[simd_test(enable = "sse")]
unsafe fn test_mm_cmpge_ss() {
fn test_mm_cmpge_ss() {
let a = _mm_setr_ps(1.0, 2.0, 3.0, 4.0);
let b = _mm_setr_ps(0.0, 5.0, 6.0, 7.0);
let c = _mm_setr_ps(1.0, 5.0, 6.0, 7.0);
@ -2403,21 +2403,21 @@ mod tests {
let c1 = !0u32; // a.extract(0) >= c.extract(0)
let d1 = 0u32; // a.extract(0) >= d.extract(0)
let rb: u32x4 = transmute(_mm_cmpge_ss(a, b));
let eb: u32x4 = transmute(_mm_setr_ps(f32::from_bits(b1), 2.0, 3.0, 4.0));
let rb = _mm_cmpge_ss(a, b).as_f32x4().to_bits();
let eb = f32x4::new(f32::from_bits(b1), 2.0, 3.0, 4.0).to_bits();
assert_eq!(rb, eb);
let rc: u32x4 = transmute(_mm_cmpge_ss(a, c));
let ec: u32x4 = transmute(_mm_setr_ps(f32::from_bits(c1), 2.0, 3.0, 4.0));
let rc = _mm_cmpge_ss(a, c).as_f32x4().to_bits();
let ec = f32x4::new(f32::from_bits(c1), 2.0, 3.0, 4.0).to_bits();
assert_eq!(rc, ec);
let rd: u32x4 = transmute(_mm_cmpge_ss(a, d));
let ed: u32x4 = transmute(_mm_setr_ps(f32::from_bits(d1), 2.0, 3.0, 4.0));
let rd = _mm_cmpge_ss(a, d).as_f32x4().to_bits();
let ed = f32x4::new(f32::from_bits(d1), 2.0, 3.0, 4.0).to_bits();
assert_eq!(rd, ed);
}
#[simd_test(enable = "sse")]
unsafe fn test_mm_cmpneq_ss() {
fn test_mm_cmpneq_ss() {
let a = _mm_setr_ps(1.0, 2.0, 3.0, 4.0);
let b = _mm_setr_ps(0.0, 5.0, 6.0, 7.0);
let c = _mm_setr_ps(1.0, 5.0, 6.0, 7.0);
@ -2427,21 +2427,21 @@ mod tests {
let c1 = 0u32; // a.extract(0) != c.extract(0)
let d1 = !0u32; // a.extract(0) != d.extract(0)
let rb: u32x4 = transmute(_mm_cmpneq_ss(a, b));
let eb: u32x4 = transmute(_mm_setr_ps(f32::from_bits(b1), 2.0, 3.0, 4.0));
let rb = _mm_cmpneq_ss(a, b).as_f32x4().to_bits();
let eb = f32x4::new(f32::from_bits(b1), 2.0, 3.0, 4.0).to_bits();
assert_eq!(rb, eb);
let rc: u32x4 = transmute(_mm_cmpneq_ss(a, c));
let ec: u32x4 = transmute(_mm_setr_ps(f32::from_bits(c1), 2.0, 3.0, 4.0));
let rc = _mm_cmpneq_ss(a, c).as_f32x4().to_bits();
let ec = f32x4::new(f32::from_bits(c1), 2.0, 3.0, 4.0).to_bits();
assert_eq!(rc, ec);
let rd: u32x4 = transmute(_mm_cmpneq_ss(a, d));
let ed: u32x4 = transmute(_mm_setr_ps(f32::from_bits(d1), 2.0, 3.0, 4.0));
let rd = _mm_cmpneq_ss(a, d).as_f32x4().to_bits();
let ed = f32x4::new(f32::from_bits(d1), 2.0, 3.0, 4.0).to_bits();
assert_eq!(rd, ed);
}
#[simd_test(enable = "sse")]
unsafe fn test_mm_cmpnlt_ss() {
fn test_mm_cmpnlt_ss() {
// TODO: this test is exactly the same as for `_mm_cmpge_ss`, but there
// must be a difference. It may have to do with behavior in the
// presence of NaNs (signaling or quiet). If so, we should add tests
@ -2456,21 +2456,21 @@ mod tests {
let c1 = !0u32; // a.extract(0) >= c.extract(0)
let d1 = 0u32; // a.extract(0) >= d.extract(0)
let rb: u32x4 = transmute(_mm_cmpnlt_ss(a, b));
let eb: u32x4 = transmute(_mm_setr_ps(f32::from_bits(b1), 2.0, 3.0, 4.0));
let rb = _mm_cmpnlt_ss(a, b).as_f32x4().to_bits();
let eb = f32x4::new(f32::from_bits(b1), 2.0, 3.0, 4.0).to_bits();
assert_eq!(rb, eb);
let rc: u32x4 = transmute(_mm_cmpnlt_ss(a, c));
let ec: u32x4 = transmute(_mm_setr_ps(f32::from_bits(c1), 2.0, 3.0, 4.0));
let rc = _mm_cmpnlt_ss(a, c).as_f32x4().to_bits();
let ec = f32x4::new(f32::from_bits(c1), 2.0, 3.0, 4.0).to_bits();
assert_eq!(rc, ec);
let rd: u32x4 = transmute(_mm_cmpnlt_ss(a, d));
let ed: u32x4 = transmute(_mm_setr_ps(f32::from_bits(d1), 2.0, 3.0, 4.0));
let rd = _mm_cmpnlt_ss(a, d).as_f32x4().to_bits();
let ed = f32x4::new(f32::from_bits(d1), 2.0, 3.0, 4.0).to_bits();
assert_eq!(rd, ed);
}
#[simd_test(enable = "sse")]
unsafe fn test_mm_cmpnle_ss() {
fn test_mm_cmpnle_ss() {
// TODO: this test is exactly the same as for `_mm_cmpgt_ss`, but there
// must be a difference. It may have to do with behavior in the
// presence
@ -2485,21 +2485,21 @@ mod tests {
let c1 = 0u32; // a.extract(0) > c.extract(0)
let d1 = 0u32; // a.extract(0) > d.extract(0)
let rb: u32x4 = transmute(_mm_cmpnle_ss(a, b));
let eb: u32x4 = transmute(_mm_setr_ps(f32::from_bits(b1), 2.0, 3.0, 4.0));
let rb = _mm_cmpnle_ss(a, b).as_f32x4().to_bits();
let eb = f32x4::new(f32::from_bits(b1), 2.0, 3.0, 4.0).to_bits();
assert_eq!(rb, eb);
let rc: u32x4 = transmute(_mm_cmpnle_ss(a, c));
let ec: u32x4 = transmute(_mm_setr_ps(f32::from_bits(c1), 2.0, 3.0, 4.0));
let rc = _mm_cmpnle_ss(a, c).as_f32x4().to_bits();
let ec = f32x4::new(f32::from_bits(c1), 2.0, 3.0, 4.0).to_bits();
assert_eq!(rc, ec);
let rd: u32x4 = transmute(_mm_cmpnle_ss(a, d));
let ed: u32x4 = transmute(_mm_setr_ps(f32::from_bits(d1), 2.0, 3.0, 4.0));
let rd = _mm_cmpnle_ss(a, d).as_f32x4().to_bits();
let ed = f32x4::new(f32::from_bits(d1), 2.0, 3.0, 4.0).to_bits();
assert_eq!(rd, ed);
}
#[simd_test(enable = "sse")]
unsafe fn test_mm_cmpngt_ss() {
fn test_mm_cmpngt_ss() {
// TODO: this test is exactly the same as for `_mm_cmple_ss`, but there
// must be a difference. It may have to do with behavior in the
// presence of NaNs (signaling or quiet). If so, we should add tests
@ -2514,21 +2514,21 @@ mod tests {
let c1 = !0u32; // a.extract(0) <= c.extract(0)
let d1 = !0u32; // a.extract(0) <= d.extract(0)
let rb: u32x4 = transmute(_mm_cmpngt_ss(a, b));
let eb: u32x4 = transmute(_mm_setr_ps(f32::from_bits(b1), 2.0, 3.0, 4.0));
let rb = _mm_cmpngt_ss(a, b).as_f32x4().to_bits();
let eb = f32x4::new(f32::from_bits(b1), 2.0, 3.0, 4.0).to_bits();
assert_eq!(rb, eb);
let rc: u32x4 = transmute(_mm_cmpngt_ss(a, c));
let ec: u32x4 = transmute(_mm_setr_ps(f32::from_bits(c1), 2.0, 3.0, 4.0));
let rc = _mm_cmpngt_ss(a, c).as_f32x4().to_bits();
let ec = f32x4::new(f32::from_bits(c1), 2.0, 3.0, 4.0).to_bits();
assert_eq!(rc, ec);
let rd: u32x4 = transmute(_mm_cmpngt_ss(a, d));
let ed: u32x4 = transmute(_mm_setr_ps(f32::from_bits(d1), 2.0, 3.0, 4.0));
let rd = _mm_cmpngt_ss(a, d).as_f32x4().to_bits();
let ed = f32x4::new(f32::from_bits(d1), 2.0, 3.0, 4.0).to_bits();
assert_eq!(rd, ed);
}
#[simd_test(enable = "sse")]
unsafe fn test_mm_cmpnge_ss() {
fn test_mm_cmpnge_ss() {
// TODO: this test is exactly the same as for `_mm_cmplt_ss`, but there
// must be a difference. It may have to do with behavior in the
// presence of NaNs (signaling or quiet). If so, we should add tests
@ -2543,21 +2543,21 @@ mod tests {
let c1 = 0u32; // a.extract(0) < c.extract(0)
let d1 = !0u32; // a.extract(0) < d.extract(0)
let rb: u32x4 = transmute(_mm_cmpnge_ss(a, b));
let eb: u32x4 = transmute(_mm_setr_ps(f32::from_bits(b1), 2.0, 3.0, 4.0));
let rb = _mm_cmpnge_ss(a, b).as_f32x4().to_bits();
let eb = f32x4::new(f32::from_bits(b1), 2.0, 3.0, 4.0).to_bits();
assert_eq!(rb, eb);
let rc: u32x4 = transmute(_mm_cmpnge_ss(a, c));
let ec: u32x4 = transmute(_mm_setr_ps(f32::from_bits(c1), 2.0, 3.0, 4.0));
let rc = _mm_cmpnge_ss(a, c).as_f32x4().to_bits();
let ec = f32x4::new(f32::from_bits(c1), 2.0, 3.0, 4.0).to_bits();
assert_eq!(rc, ec);
let rd: u32x4 = transmute(_mm_cmpnge_ss(a, d));
let ed: u32x4 = transmute(_mm_setr_ps(f32::from_bits(d1), 2.0, 3.0, 4.0));
let rd = _mm_cmpnge_ss(a, d).as_f32x4().to_bits();
let ed = f32x4::new(f32::from_bits(d1), 2.0, 3.0, 4.0).to_bits();
assert_eq!(rd, ed);
}
#[simd_test(enable = "sse")]
unsafe fn test_mm_cmpord_ss() {
fn test_mm_cmpord_ss() {
let a = _mm_setr_ps(1.0, 2.0, 3.0, 4.0);
let b = _mm_setr_ps(0.0, 5.0, 6.0, 7.0);
let c = _mm_setr_ps(NAN, 5.0, 6.0, 7.0);
@ -2567,21 +2567,21 @@ mod tests {
let c1 = 0u32; // a.extract(0) ord c.extract(0)
let d1 = !0u32; // a.extract(0) ord d.extract(0)
let rb: u32x4 = transmute(_mm_cmpord_ss(a, b));
let eb: u32x4 = transmute(_mm_setr_ps(f32::from_bits(b1), 2.0, 3.0, 4.0));
let rb = _mm_cmpord_ss(a, b).as_f32x4().to_bits();
let eb = f32x4::new(f32::from_bits(b1), 2.0, 3.0, 4.0).to_bits();
assert_eq!(rb, eb);
let rc: u32x4 = transmute(_mm_cmpord_ss(a, c));
let ec: u32x4 = transmute(_mm_setr_ps(f32::from_bits(c1), 2.0, 3.0, 4.0));
let rc = _mm_cmpord_ss(a, c).as_f32x4().to_bits();
let ec = f32x4::new(f32::from_bits(c1), 2.0, 3.0, 4.0).to_bits();
assert_eq!(rc, ec);
let rd: u32x4 = transmute(_mm_cmpord_ss(a, d));
let ed: u32x4 = transmute(_mm_setr_ps(f32::from_bits(d1), 2.0, 3.0, 4.0));
let rd = _mm_cmpord_ss(a, d).as_f32x4().to_bits();
let ed = f32x4::new(f32::from_bits(d1), 2.0, 3.0, 4.0).to_bits();
assert_eq!(rd, ed);
}
#[simd_test(enable = "sse")]
unsafe fn test_mm_cmpunord_ss() {
fn test_mm_cmpunord_ss() {
let a = _mm_setr_ps(1.0, 2.0, 3.0, 4.0);
let b = _mm_setr_ps(0.0, 5.0, 6.0, 7.0);
let c = _mm_setr_ps(NAN, 5.0, 6.0, 7.0);
@ -2591,160 +2591,160 @@ mod tests {
let c1 = !0u32; // a.extract(0) unord c.extract(0)
let d1 = 0u32; // a.extract(0) unord d.extract(0)
let rb: u32x4 = transmute(_mm_cmpunord_ss(a, b));
let eb: u32x4 = transmute(_mm_setr_ps(f32::from_bits(b1), 2.0, 3.0, 4.0));
let rb = _mm_cmpunord_ss(a, b).as_f32x4().to_bits();
let eb = f32x4::new(f32::from_bits(b1), 2.0, 3.0, 4.0).to_bits();
assert_eq!(rb, eb);
let rc: u32x4 = transmute(_mm_cmpunord_ss(a, c));
let ec: u32x4 = transmute(_mm_setr_ps(f32::from_bits(c1), 2.0, 3.0, 4.0));
let rc = _mm_cmpunord_ss(a, c).as_f32x4().to_bits();
let ec = f32x4::new(f32::from_bits(c1), 2.0, 3.0, 4.0).to_bits();
assert_eq!(rc, ec);
let rd: u32x4 = transmute(_mm_cmpunord_ss(a, d));
let ed: u32x4 = transmute(_mm_setr_ps(f32::from_bits(d1), 2.0, 3.0, 4.0));
let rd = _mm_cmpunord_ss(a, d).as_f32x4().to_bits();
let ed = f32x4::new(f32::from_bits(d1), 2.0, 3.0, 4.0).to_bits();
assert_eq!(rd, ed);
}
#[simd_test(enable = "sse")]
unsafe fn test_mm_cmpeq_ps() {
fn test_mm_cmpeq_ps() {
let a = _mm_setr_ps(10.0, 50.0, 1.0, NAN);
let b = _mm_setr_ps(15.0, 20.0, 1.0, NAN);
let tru = !0u32;
let fls = 0u32;
let e = u32x4::new(fls, fls, tru, fls);
let r: u32x4 = transmute(_mm_cmpeq_ps(a, b));
let r = _mm_cmpeq_ps(a, b).as_f32x4().to_bits();
assert_eq!(r, e);
}
#[simd_test(enable = "sse")]
unsafe fn test_mm_cmplt_ps() {
fn test_mm_cmplt_ps() {
let a = _mm_setr_ps(10.0, 50.0, 1.0, NAN);
let b = _mm_setr_ps(15.0, 20.0, 1.0, NAN);
let tru = !0u32;
let fls = 0u32;
let e = u32x4::new(tru, fls, fls, fls);
let r: u32x4 = transmute(_mm_cmplt_ps(a, b));
let r = _mm_cmplt_ps(a, b).as_f32x4().to_bits();
assert_eq!(r, e);
}
#[simd_test(enable = "sse")]
unsafe fn test_mm_cmple_ps() {
fn test_mm_cmple_ps() {
let a = _mm_setr_ps(10.0, 50.0, 1.0, 4.0);
let b = _mm_setr_ps(15.0, 20.0, 1.0, NAN);
let tru = !0u32;
let fls = 0u32;
let e = u32x4::new(tru, fls, tru, fls);
let r: u32x4 = transmute(_mm_cmple_ps(a, b));
let r = _mm_cmple_ps(a, b).as_f32x4().to_bits();
assert_eq!(r, e);
}
#[simd_test(enable = "sse")]
unsafe fn test_mm_cmpgt_ps() {
fn test_mm_cmpgt_ps() {
let a = _mm_setr_ps(10.0, 50.0, 1.0, NAN);
let b = _mm_setr_ps(15.0, 20.0, 1.0, 42.0);
let tru = !0u32;
let fls = 0u32;
let e = u32x4::new(fls, tru, fls, fls);
let r: u32x4 = transmute(_mm_cmpgt_ps(a, b));
let r = _mm_cmpgt_ps(a, b).as_f32x4().to_bits();
assert_eq!(r, e);
}
#[simd_test(enable = "sse")]
unsafe fn test_mm_cmpge_ps() {
fn test_mm_cmpge_ps() {
let a = _mm_setr_ps(10.0, 50.0, 1.0, NAN);
let b = _mm_setr_ps(15.0, 20.0, 1.0, 42.0);
let tru = !0u32;
let fls = 0u32;
let e = u32x4::new(fls, tru, tru, fls);
let r: u32x4 = transmute(_mm_cmpge_ps(a, b));
let r = _mm_cmpge_ps(a, b).as_f32x4().to_bits();
assert_eq!(r, e);
}
#[simd_test(enable = "sse")]
unsafe fn test_mm_cmpneq_ps() {
fn test_mm_cmpneq_ps() {
let a = _mm_setr_ps(10.0, 50.0, 1.0, NAN);
let b = _mm_setr_ps(15.0, 20.0, 1.0, NAN);
let tru = !0u32;
let fls = 0u32;
let e = u32x4::new(tru, tru, fls, tru);
let r: u32x4 = transmute(_mm_cmpneq_ps(a, b));
let r = _mm_cmpneq_ps(a, b).as_f32x4().to_bits();
assert_eq!(r, e);
}
#[simd_test(enable = "sse")]
unsafe fn test_mm_cmpnlt_ps() {
fn test_mm_cmpnlt_ps() {
let a = _mm_setr_ps(10.0, 50.0, 1.0, NAN);
let b = _mm_setr_ps(15.0, 20.0, 1.0, 5.0);
let tru = !0u32;
let fls = 0u32;
let e = u32x4::new(fls, tru, tru, tru);
let r: u32x4 = transmute(_mm_cmpnlt_ps(a, b));
let r = _mm_cmpnlt_ps(a, b).as_f32x4().to_bits();
assert_eq!(r, e);
}
#[simd_test(enable = "sse")]
unsafe fn test_mm_cmpnle_ps() {
fn test_mm_cmpnle_ps() {
let a = _mm_setr_ps(10.0, 50.0, 1.0, NAN);
let b = _mm_setr_ps(15.0, 20.0, 1.0, 5.0);
let tru = !0u32;
let fls = 0u32;
let e = u32x4::new(fls, tru, fls, tru);
let r: u32x4 = transmute(_mm_cmpnle_ps(a, b));
let r = _mm_cmpnle_ps(a, b).as_f32x4().to_bits();
assert_eq!(r, e);
}
#[simd_test(enable = "sse")]
unsafe fn test_mm_cmpngt_ps() {
fn test_mm_cmpngt_ps() {
let a = _mm_setr_ps(10.0, 50.0, 1.0, NAN);
let b = _mm_setr_ps(15.0, 20.0, 1.0, 5.0);
let tru = !0u32;
let fls = 0u32;
let e = u32x4::new(tru, fls, tru, tru);
let r: u32x4 = transmute(_mm_cmpngt_ps(a, b));
let r = _mm_cmpngt_ps(a, b).as_f32x4().to_bits();
assert_eq!(r, e);
}
#[simd_test(enable = "sse")]
unsafe fn test_mm_cmpnge_ps() {
fn test_mm_cmpnge_ps() {
let a = _mm_setr_ps(10.0, 50.0, 1.0, NAN);
let b = _mm_setr_ps(15.0, 20.0, 1.0, 5.0);
let tru = !0u32;
let fls = 0u32;
let e = u32x4::new(tru, fls, fls, tru);
let r: u32x4 = transmute(_mm_cmpnge_ps(a, b));
let r = _mm_cmpnge_ps(a, b).as_f32x4().to_bits();
assert_eq!(r, e);
}
#[simd_test(enable = "sse")]
unsafe fn test_mm_cmpord_ps() {
fn test_mm_cmpord_ps() {
let a = _mm_setr_ps(10.0, 50.0, NAN, NAN);
let b = _mm_setr_ps(15.0, NAN, 1.0, NAN);
let tru = !0u32;
let fls = 0u32;
let e = u32x4::new(tru, fls, fls, fls);
let r: u32x4 = transmute(_mm_cmpord_ps(a, b));
let r = _mm_cmpord_ps(a, b).as_f32x4().to_bits();
assert_eq!(r, e);
}
#[simd_test(enable = "sse")]
unsafe fn test_mm_cmpunord_ps() {
fn test_mm_cmpunord_ps() {
let a = _mm_setr_ps(10.0, 50.0, NAN, NAN);
let b = _mm_setr_ps(15.0, NAN, 1.0, NAN);
let tru = !0u32;
let fls = 0u32;
let e = u32x4::new(fls, tru, tru, tru);
let r: u32x4 = transmute(_mm_cmpunord_ps(a, b));
let r = _mm_cmpunord_ps(a, b).as_f32x4().to_bits();
assert_eq!(r, e);
}

View file

@ -1176,7 +1176,7 @@ pub const fn _mm_set_epi8(
#[stable(feature = "simd_x86", since = "1.27.0")]
#[rustc_const_unstable(feature = "stdarch_const_x86", issue = "149298")]
pub const fn _mm_set1_epi64x(a: i64) -> __m128i {
_mm_set_epi64x(a, a)
i64x2::splat(a).as_m128i()
}
/// Broadcasts 32-bit integer `a` to all elements.
@ -1188,7 +1188,7 @@ pub const fn _mm_set1_epi64x(a: i64) -> __m128i {
#[stable(feature = "simd_x86", since = "1.27.0")]
#[rustc_const_unstable(feature = "stdarch_const_x86", issue = "149298")]
pub const fn _mm_set1_epi32(a: i32) -> __m128i {
_mm_set_epi32(a, a, a, a)
i32x4::splat(a).as_m128i()
}
/// Broadcasts 16-bit integer `a` to all elements.
@ -1200,7 +1200,7 @@ pub const fn _mm_set1_epi32(a: i32) -> __m128i {
#[stable(feature = "simd_x86", since = "1.27.0")]
#[rustc_const_unstable(feature = "stdarch_const_x86", issue = "149298")]
pub const fn _mm_set1_epi16(a: i16) -> __m128i {
_mm_set_epi16(a, a, a, a, a, a, a, a)
i16x8::splat(a).as_m128i()
}
/// Broadcasts 8-bit integer `a` to all elements.
@ -1212,7 +1212,7 @@ pub const fn _mm_set1_epi16(a: i16) -> __m128i {
#[stable(feature = "simd_x86", since = "1.27.0")]
#[rustc_const_unstable(feature = "stdarch_const_x86", issue = "149298")]
pub const fn _mm_set1_epi8(a: i8) -> __m128i {
_mm_set_epi8(a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a)
i8x16::splat(a).as_m128i()
}
/// Sets packed 32-bit integers with the supplied values in reverse order.
@ -3280,11 +3280,7 @@ mod tests {
core_arch::{simd::*, x86::*},
hint::black_box,
};
use std::{
boxed, f32, f64,
mem::{self, transmute},
ptr,
};
use std::{boxed, f32, f64, mem, ptr};
use stdarch_test::simd_test;
const NAN: f64 = f64::NAN;
@ -4593,38 +4589,38 @@ mod tests {
}
#[simd_test(enable = "sse2")]
const unsafe fn test_mm_and_pd() {
let a = transmute(u64x2::splat(5));
let b = transmute(u64x2::splat(3));
const fn test_mm_and_pd() {
let a = f64x2::from_bits(u64x2::splat(5)).as_m128d();
let b = f64x2::from_bits(u64x2::splat(3)).as_m128d();
let r = _mm_and_pd(a, b);
let e = transmute(u64x2::splat(1));
let e = f64x2::from_bits(u64x2::splat(1)).as_m128d();
assert_eq_m128d(r, e);
}
#[simd_test(enable = "sse2")]
const unsafe fn test_mm_andnot_pd() {
let a = transmute(u64x2::splat(5));
let b = transmute(u64x2::splat(3));
const fn test_mm_andnot_pd() {
let a = f64x2::from_bits(u64x2::splat(5)).as_m128d();
let b = f64x2::from_bits(u64x2::splat(3)).as_m128d();
let r = _mm_andnot_pd(a, b);
let e = transmute(u64x2::splat(2));
let e = f64x2::from_bits(u64x2::splat(2)).as_m128d();
assert_eq_m128d(r, e);
}
#[simd_test(enable = "sse2")]
const unsafe fn test_mm_or_pd() {
let a = transmute(u64x2::splat(5));
let b = transmute(u64x2::splat(3));
const fn test_mm_or_pd() {
let a = f64x2::from_bits(u64x2::splat(5)).as_m128d();
let b = f64x2::from_bits(u64x2::splat(3)).as_m128d();
let r = _mm_or_pd(a, b);
let e = transmute(u64x2::splat(7));
let e = f64x2::from_bits(u64x2::splat(7)).as_m128d();
assert_eq_m128d(r, e);
}
#[simd_test(enable = "sse2")]
const unsafe fn test_mm_xor_pd() {
let a = transmute(u64x2::splat(5));
let b = transmute(u64x2::splat(3));
const fn test_mm_xor_pd() {
let a = f64x2::from_bits(u64x2::splat(5)).as_m128d();
let b = f64x2::from_bits(u64x2::splat(3)).as_m128d();
let r = _mm_xor_pd(a, b);
let e = transmute(u64x2::splat(6));
let e = f64x2::from_bits(u64x2::splat(6)).as_m128d();
assert_eq_m128d(r, e);
}

View file

@ -78,38 +78,45 @@ pub(crate) const fn assert_eq_m512h(a: __m512h, b: __m512h) {
}
#[target_feature(enable = "sse2")]
#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")]
pub(crate) const fn get_m128d(a: __m128d, idx: usize) -> f64 {
a.as_f64x2().extract(idx)
a.as_f64x2().extract_dyn(idx)
}
#[target_feature(enable = "sse")]
#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")]
pub(crate) const fn get_m128(a: __m128, idx: usize) -> f32 {
a.as_f32x4().extract(idx)
a.as_f32x4().extract_dyn(idx)
}
#[target_feature(enable = "avx")]
#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")]
pub(crate) const fn get_m256d(a: __m256d, idx: usize) -> f64 {
a.as_f64x4().extract(idx)
a.as_f64x4().extract_dyn(idx)
}
#[target_feature(enable = "avx")]
#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")]
pub(crate) const fn get_m256(a: __m256, idx: usize) -> f32 {
a.as_f32x8().extract(idx)
a.as_f32x8().extract_dyn(idx)
}
#[target_feature(enable = "avx512f")]
#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")]
pub(crate) const fn get_m512(a: __m512, idx: usize) -> f32 {
a.as_f32x16().extract(idx)
a.as_f32x16().extract_dyn(idx)
}
#[target_feature(enable = "avx512f")]
#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")]
pub(crate) const fn get_m512d(a: __m512d, idx: usize) -> f64 {
a.as_f64x8().extract(idx)
a.as_f64x8().extract_dyn(idx)
}
#[target_feature(enable = "avx512f")]
#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")]
pub(crate) const fn get_m512i(a: __m512i, idx: usize) -> i64 {
a.as_i64x8().extract(idx)
a.as_i64x8().extract_dyn(idx)
}
// not actually an intrinsic but useful in various tests as we ported from

View file

@ -59,6 +59,3 @@ vluti4q_laneq_u8
# Broken in Clang
vcvth_s16_f16
# FIXME: Broken output due to missing f16 printing support in Rust, see git blame for this line
vmulh_lane_f16
vmulh_laneq_f16

View file

@ -100,6 +100,3 @@ vluti4q_laneq_u8
# Broken in Clang
vcvth_s16_f16
# FIXME: Broken output due to missing f16 printing support in Rust
vmulh_lane_f16
vmulh_laneq_f16

View file

@ -17,6 +17,15 @@ pub fn simd_test(
item: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let tokens = TokenStream::from(attr).into_iter().collect::<Vec<_>>();
let target = env::var("TARGET").expect(
"TARGET environment variable should be set for rustc (e.g. TARGET=x86_64-apple-darwin cargo test)"
);
let target_arch = target
.split('-')
.next()
.unwrap_or_else(|| panic!("target triple contained no \"-\": {target}"));
let (target_features, target_feature_attr) = match &tokens[..] {
[] => (Vec::new(), TokenStream::new()),
[
@ -24,13 +33,20 @@ pub fn simd_test(
TokenTree::Punct(equals),
TokenTree::Literal(literal),
] if enable == "enable" && equals.as_char() == '=' => {
let enable_feature = literal.to_string();
let enable_feature = enable_feature.trim_start_matches('"').trim_end_matches('"');
let mut enable_feature = literal
.to_string()
.trim_start_matches('"')
.trim_end_matches('"')
.to_string();
let target_features: Vec<_> = enable_feature
.replace('+', "")
.split(',')
.map(String::from)
.collect();
// Allows using `#[simd_test(enable = "neon")]` on aarch64/armv7 shared tests.
if target_arch == "armv7" && target_features.iter().any(|feat| feat == "neon") {
enable_feature.push_str(",v7");
}
(
target_features,
@ -46,14 +62,7 @@ pub fn simd_test(
let item_attrs = std::mem::take(&mut item.attrs);
let name = &item.sig.ident;
let target = env::var("TARGET").expect(
"TARGET environment variable should be set for rustc (e.g. TARGET=x86_64-apple-darwin cargo test)"
);
let macro_test = match target
.split('-')
.next()
.unwrap_or_else(|| panic!("target triple contained no \"-\": {target}"))
{
let macro_test = match target_arch {
"i686" | "x86_64" | "i586" => "is_x86_feature_detected",
"arm" | "armv7" | "thumbv7neon" => "is_arm_feature_detected",
"aarch64" | "arm64ec" | "aarch64_be" => "is_aarch64_feature_detected",
@ -85,10 +94,20 @@ pub fn simd_test(
let mut detect_missing_features = TokenStream::new();
for feature in target_features {
let q = quote_spanned! {
proc_macro2::Span::call_site() =>
if !::std::arch::#macro_test!(#feature) {
missing_features.push(#feature);
let q = if target_arch == "armv7" && feature == "fp16" {
// "fp16" cannot be checked at runtime
quote_spanned! {
proc_macro2::Span::call_site() =>
if !cfg!(target_feature = #feature) {
missing_features.push(#feature);
}
}
} else {
quote_spanned! {
proc_macro2::Span::call_site() =>
if !::std::arch::#macro_test!(#feature) {
missing_features.push(#feature);
}
}
};
q.to_tokens(&mut detect_missing_features);

View file

@ -63,8 +63,8 @@ neon-unstable-f16: &neon-unstable-f16
neon-unstable-feat-lut: &neon-unstable-feat-lut
FnCall: [unstable, ['feature = "stdarch_neon_feat_lut"', 'issue = "138050"']]
aarch64-unstable-jscvt: &aarch64-unstable-jscvt
FnCall: [unstable, ['feature = "stdarch_aarch64_jscvt"', 'issue = "147555"']]
aarch64-stable-jscvt: &aarch64-stable-jscvt
FnCall: [stable, ['feature = "stdarch_aarch64_jscvt"', 'since = "CURRENT_RUSTC_VERSION"']]
# #[cfg(target_endian = "little")]
little-endian: &little-endian
@ -14275,7 +14275,7 @@ intrinsics:
attr:
- FnCall: [target_feature, ['enable = "jsconv"']]
- FnCall: [cfg_attr, [test, { FnCall: [assert_instr, ["fjcvtzs"]] }]]
- *aarch64-unstable-jscvt
- *aarch64-stable-jscvt
safety: safe
types:
- f64

View file

@ -1736,7 +1736,7 @@ fn create_tokens(intrinsic: &Intrinsic, endianness: Endianness, tokens: &mut Tok
);
}
tokens.append_all(quote! { #[inline] });
tokens.append_all(quote! { #[inline(always)] });
match endianness {
Endianness::Little => tokens.append_all(quote! { #[cfg(target_endian = "little")] }),

View file

@ -1 +1 @@
48622726c4a91c87bf6cd4dbe1000c95df59906e
873d4682c7d285540b8f28bfe637006cef8918a6

View file

@ -275,26 +275,23 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
interp_ok(Scalar::from_i32(-1)) // Return non-zero on success
}
#[allow(non_snake_case, clippy::arithmetic_side_effects)]
#[allow(clippy::arithmetic_side_effects)]
fn system_time_since_windows_epoch(&self, time: &SystemTime) -> InterpResult<'tcx, Duration> {
let this = self.eval_context_ref();
let INTERVALS_PER_SEC = this.eval_windows_u64("time", "INTERVALS_PER_SEC");
let INTERVALS_TO_UNIX_EPOCH = this.eval_windows_u64("time", "INTERVALS_TO_UNIX_EPOCH");
let SECONDS_TO_UNIX_EPOCH = INTERVALS_TO_UNIX_EPOCH / INTERVALS_PER_SEC;
// The amount of seconds between 1601/1/1 and 1970/1/1.
// See https://learn.microsoft.com/en-us/windows/win32/sysinfo/converting-a-time-t-value-to-a-file-time
// (just divide by the number of 100 ns intervals per second).
const SECONDS_TO_UNIX_EPOCH: u64 = 11_644_473_600;
interp_ok(system_time_to_duration(time)? + Duration::from_secs(SECONDS_TO_UNIX_EPOCH))
}
#[allow(non_snake_case, clippy::arithmetic_side_effects)]
fn windows_ticks_for(&self, duration: Duration) -> InterpResult<'tcx, u64> {
let this = self.eval_context_ref();
// 1 interval = 100 ns.
// See https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime
const NANOS_PER_INTERVAL: u128 = 100;
let NANOS_PER_SEC = this.eval_windows_u64("time", "NANOS_PER_SEC");
let INTERVALS_PER_SEC = this.eval_windows_u64("time", "INTERVALS_PER_SEC");
let NANOS_PER_INTERVAL = NANOS_PER_SEC / INTERVALS_PER_SEC;
let ticks = u64::try_from(duration.as_nanos() / u128::from(NANOS_PER_INTERVAL))
let ticks = u64::try_from(duration.as_nanos() / NANOS_PER_INTERVAL)
.map_err(|_| err_unsup_format!("programs running more than 2^64 Windows ticks after the Windows epoch are not supported"))?;
interp_ok(ticks)
}

View file

@ -48,7 +48,7 @@ impl Expander {
callback: Option<ProcMacroClientHandle<'_>>,
) -> Result<TokenStream<S>, PanicMessage>
where
<S::Server<'a> as bridge::server::Types>::TokenStream: Default,
<S::Server<'a> as bridge::server::Server>::TokenStream: Default,
{
self.inner
.proc_macros

View file

@ -30,13 +30,11 @@ pub struct RaSpanServer<'a> {
pub callback: Option<ProcMacroClientHandle<'a>>,
}
impl server::Types for RaSpanServer<'_> {
impl server::Server for RaSpanServer<'_> {
type TokenStream = crate::token_stream::TokenStream<Span>;
type Span = Span;
type Symbol = Symbol;
}
impl server::Server for RaSpanServer<'_> {
fn globals(&mut self) -> ExpnGlobals<Self::Span> {
ExpnGlobals {
def_site: self.def_site,

View file

@ -36,13 +36,11 @@ pub struct SpanIdServer<'a> {
pub callback: Option<ProcMacroClientHandle<'a>>,
}
impl server::Types for SpanIdServer<'_> {
impl server::Server for SpanIdServer<'_> {
type TokenStream = crate::token_stream::TokenStream<Span>;
type Span = Span;
type Symbol = Symbol;
}
impl server::Server for SpanIdServer<'_> {
fn globals(&mut self) -> ExpnGlobals<Self::Span> {
ExpnGlobals {
def_site: self.def_site,

View file

@ -0,0 +1,42 @@
error[E0716]: temporary value dropped while borrowed
--> $DIR/dont-eval-const-block-during-promotion.rs:48:14
|
LL | x = &([0][const { 0 }] & 0);
| ^^^^^^^^^^^^^^^^^^^^^^- temporary value is freed at the end of this statement
| |
| creates a temporary value which is freed while still in use
...
LL | (x, y, z);
| - borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
error[E0716]: temporary value dropped while borrowed
--> $DIR/dont-eval-const-block-during-promotion.rs:50:14
|
LL | y = &(1 / const { 1 });
| ^^^^^^^^^^^^^^^^^- temporary value is freed at the end of this statement
| |
| creates a temporary value which is freed while still in use
...
LL | (x, y, z);
| - borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
error[E0716]: temporary value dropped while borrowed
--> $DIR/dont-eval-const-block-during-promotion.rs:52:14
|
LL | z = &(const { 1 } / -1);
| ^^^^^^^^^^^^^^^^^^- temporary value is freed at the end of this statement
| |
| creates a temporary value which is freed while still in use
LL |
LL | (x, y, z);
| - borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0716`.

View file

@ -0,0 +1,65 @@
//! Test for #150464: as of #138499, trying to evaluate const blocks during constant promotion will
//! result in a query cycle, so we shouldn't do it. Evaluation can happen when trying to promote
//! integer division and array indexing, where it's necessary for the operation to succeed to be
//! able to use it in a promoted constant.
//@ revisions: pass fail
//@[pass] check-pass
use std::mem::offset_of;
struct Thing(i32);
fn main() {
// For a temporary involving array indexing to be promoted, we evaluate the index to make sure
// it's in-bounds. As of #150557 we treat inline constants as maybe-out-of-bounds to avoid the
// query cycle from evaluating them. That allows this to compile:
let x = &([0][const { 0 }] & 0);
// Likewise, integer divisors must be nonzero. Avoiding the query cycle allows this to compile:
let y = &(1 / const { 1 });
// Likewise, signed integer dividends can't be the integer minimum when the divisor is -1.
let z = &(const { 1 } / -1);
// These temporaries are all lifetime-extended, so they don't need to be promoted for references
// to them to be live later in the block. Generally, code with const blocks in these positions
// should compile as long as being promoted isn't necessary for borrow-checking to succeed.
(x, y, z);
// A reduced example from real code (#150464): this can't be promoted since the array is a local
// variable, but it still resulted in a query cycle because the index was evaluated for the
// bounds-check before checking that. By not evaluating the const block, we avoid the cycle.
// Since this doesn't rely on promotion, it should borrow-check successfully.
let temp = [0u8];
let _ = &(temp[const { 0usize }] & 0u8);
// #150464 was reported because `offset_of!` started desugaring to a const block in #148151.
let _ = &(temp[offset_of!(Thing, 0)] & 0u8);
// Similarly, at the time #150464 was reported, the index here was evaluated before checking
// that the indexed expression is an array. As above, this can't be promoted, but still resulted
// in a query cycle. By not evaluating the const block, we avoid the cycle. Since this doesn't
// rely on promotion, it should borrow-check successfully.
let temp: &[u8] = &[0u8];
let _ = &(temp[const { 0usize }] & 0u8);
// By no longer promoting these temporaries, they're dropped at the ends of their respective
// statements, so we can't refer to them thereafter. This code no longer query-cycles, but it
// fails to borrow-check instead.
#[cfg(fail)]
{
let (x, y, z);
x = &([0][const { 0 }] & 0);
//[fail]~^ ERROR: temporary value dropped while borrowed
y = &(1 / const { 1 });
//[fail]~^ ERROR: temporary value dropped while borrowed
z = &(const { 1 } / -1);
//[fail]~^ ERROR: temporary value dropped while borrowed
(x, y, z);
}
// Sanity check: those temporaries do promote if the const blocks are removed.
// If constant promotion is changed so that these are no longer implicitly promoted, the
// comments on this test file should be reworded to reflect that.
let (x, y, z);
x = &([0][0] & 0);
y = &(1 / 1);
z = &(1 / -1);
(x, y, z);
}