Auto merge of #137594 - RalfJung:miri-sync, r=RalfJung
Miri subtree update r? `@ghost` try-job: x86_64-gnu-aux
This commit is contained in:
commit
cb06d12710
38 changed files with 718 additions and 301 deletions
|
@ -34,6 +34,7 @@ fn checked_ilog() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)] // FIXME test is broken on Miri: https://github.com/rust-lang/rust/issues/137591
|
||||
fn checked_ilog2() {
|
||||
assert_eq!(5u32.checked_ilog2(), Some(2));
|
||||
assert_eq!(0u64.checked_ilog2(), None);
|
||||
|
|
|
@ -154,7 +154,6 @@ pub fn get_closest_merge_commit(
|
|||
"rev-list",
|
||||
&format!("--author={}", config.git_merge_commit_email),
|
||||
"-n1",
|
||||
"--first-parent",
|
||||
&merge_base,
|
||||
]);
|
||||
|
||||
|
|
2
src/tools/miri/.github/workflows/ci.yml
vendored
2
src/tools/miri/.github/workflows/ci.yml
vendored
|
@ -30,6 +30,8 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/workflows/setup
|
||||
with:
|
||||
toolchain_flags: "--host ${{ matrix.host_target }}"
|
||||
|
||||
# The `style` job only runs on Linux; this makes sure the Windows-host-specific
|
||||
# code is also covered by clippy.
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
name: "Miri CI setup"
|
||||
description: "Sets up Miri CI"
|
||||
inputs:
|
||||
toolchain_flags:
|
||||
required: false
|
||||
default: ''
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
|
@ -45,7 +49,7 @@ runs:
|
|||
echo "Building against latest rustc git version"
|
||||
git ls-remote https://github.com/rust-lang/rust/ HEAD | cut -f 1 > rust-version
|
||||
fi
|
||||
./miri toolchain
|
||||
./miri toolchain ${{ inputs.toolchain_flags }}
|
||||
shell: bash
|
||||
|
||||
- name: Show Rust version (miri toolchain)
|
||||
|
|
|
@ -67,6 +67,10 @@ default = ["stack-cache"]
|
|||
stack-cache = []
|
||||
stack-cache-consistency-check = ["stack-cache"]
|
||||
|
||||
[lints.rust.unexpected_cfgs]
|
||||
level = "warn"
|
||||
check-cfg = ['cfg(bootstrap)']
|
||||
|
||||
# Be aware that this file is inside a workspace when used via the
|
||||
# submodule in the rustc repo. That means there are many cargo features
|
||||
# we cannot use, such as profiles.
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
fn main() {
|
||||
// Don't rebuild miri when nothing changed.
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
// Re-export the TARGET environment variable so it can be accessed by miri. Needed to know the
|
||||
// "host" triple inside Miri.
|
||||
let target = std::env::var("TARGET").unwrap();
|
||||
println!("cargo:rustc-env=TARGET={target}");
|
||||
// Allow some cfgs.
|
||||
println!("cargo::rustc-check-cfg=cfg(bootstrap)");
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
set -eu
|
||||
|
||||
function begingroup {
|
||||
echo "::group::$@"
|
||||
|
@ -11,6 +11,17 @@ function endgroup {
|
|||
echo "::endgroup"
|
||||
}
|
||||
|
||||
begingroup "Sanity-check environment"
|
||||
|
||||
# Ensure the HOST_TARGET is what it should be.
|
||||
if ! rustc -vV | grep -q "^host: $HOST_TARGET\$"; then
|
||||
echo "This runner should be using host target $HOST_TARGET but rustc disagrees:"
|
||||
rustc -vV
|
||||
exit 1
|
||||
fi
|
||||
|
||||
endgroup
|
||||
|
||||
begingroup "Building Miri"
|
||||
|
||||
# Global configuration
|
||||
|
|
|
@ -1 +1 @@
|
|||
6dd75f0d6802f56564f5f9c947a85ded286d3986
|
||||
f5729cfed3c45e061e8a443677fc1d5ef9277df7
|
||||
|
|
|
@ -29,8 +29,8 @@ use std::num::NonZero;
|
|||
use std::ops::Range;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, Once};
|
||||
use std::sync::atomic::{AtomicI32, AtomicU32, Ordering};
|
||||
use std::sync::{Arc, Once};
|
||||
|
||||
use miri::{
|
||||
BacktraceStyle, BorrowTrackerMethod, MiriConfig, MiriEntryFnType, ProvenanceMode, RetagFields,
|
||||
|
@ -720,8 +720,8 @@ fn main() {
|
|||
|
||||
// Ensure we have parallelism for many-seeds mode.
|
||||
if many_seeds.is_some() && !rustc_args.iter().any(|arg| arg.starts_with("-Zthreads=")) {
|
||||
// Clamp to 10 threads; things get a lot less efficient beyond that due to lock contention.
|
||||
let threads = std::thread::available_parallelism().map_or(1, |n| n.get()).min(10);
|
||||
// Clamp to 20 threads; things get a less efficient beyond that due to lock contention.
|
||||
let threads = std::thread::available_parallelism().map_or(1, |n| n.get()).min(20);
|
||||
rustc_args.push(format!("-Zthreads={threads}"));
|
||||
}
|
||||
let many_seeds =
|
||||
|
|
|
@ -999,7 +999,20 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
&'a [OpTy<'tcx>; N]: TryFrom<&'a [OpTy<'tcx>]>,
|
||||
{
|
||||
self.check_abi_and_shim_symbol_clash(abi, exp_abi, link_name)?;
|
||||
check_arg_count(args)
|
||||
|
||||
if abi.c_variadic {
|
||||
throw_ub_format!(
|
||||
"calling a non-variadic function with a variadic caller-side signature"
|
||||
);
|
||||
}
|
||||
if let Ok(ops) = args.try_into() {
|
||||
return interp_ok(ops);
|
||||
}
|
||||
throw_ub_format!(
|
||||
"incorrect number of arguments for `{link_name}`: got {}, expected {}",
|
||||
args.len(),
|
||||
N
|
||||
)
|
||||
}
|
||||
|
||||
/// Check shim for variadic function.
|
||||
|
@ -1015,7 +1028,23 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
&'a [OpTy<'tcx>; N]: TryFrom<&'a [OpTy<'tcx>]>,
|
||||
{
|
||||
self.check_abi_and_shim_symbol_clash(abi, exp_abi, link_name)?;
|
||||
check_vargarg_fixed_arg_count(link_name, abi, args)
|
||||
|
||||
if !abi.c_variadic {
|
||||
throw_ub_format!(
|
||||
"calling a variadic function with a non-variadic caller-side signature"
|
||||
);
|
||||
}
|
||||
if abi.fixed_count != u32::try_from(N).unwrap() {
|
||||
throw_ub_format!(
|
||||
"incorrect number of fixed arguments for variadic function `{}`: got {}, expected {N}",
|
||||
link_name.as_str(),
|
||||
abi.fixed_count
|
||||
)
|
||||
}
|
||||
if let Some(args) = args.split_first_chunk() {
|
||||
return interp_ok(args);
|
||||
}
|
||||
panic!("mismatch between signature and `args` slice");
|
||||
}
|
||||
|
||||
/// Mark a machine allocation that was just created as immutable.
|
||||
|
@ -1199,7 +1228,7 @@ impl<'tcx> MiriMachine<'tcx> {
|
|||
}
|
||||
|
||||
/// Check that the number of args is what we expect.
|
||||
pub fn check_arg_count<'a, 'tcx, const N: usize>(
|
||||
pub fn check_intrinsic_arg_count<'a, 'tcx, const N: usize>(
|
||||
args: &'a [OpTy<'tcx>],
|
||||
) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]>
|
||||
where
|
||||
|
@ -1208,7 +1237,11 @@ where
|
|||
if let Ok(ops) = args.try_into() {
|
||||
return interp_ok(ops);
|
||||
}
|
||||
throw_ub_format!("incorrect number of arguments: got {}, expected {}", args.len(), N)
|
||||
throw_ub_format!(
|
||||
"incorrect number of arguments for intrinsic: got {}, expected {}",
|
||||
args.len(),
|
||||
N
|
||||
)
|
||||
}
|
||||
|
||||
/// Check that the number of varargs is at least the minimum what we expect.
|
||||
|
@ -1228,34 +1261,6 @@ pub fn check_min_vararg_count<'a, 'tcx, const N: usize>(
|
|||
)
|
||||
}
|
||||
|
||||
/// Check the number of fixed args of a vararg function.
|
||||
/// Returns a tuple that consisting of an array of fixed args, and a slice of varargs.
|
||||
fn check_vargarg_fixed_arg_count<'a, 'tcx, const N: usize>(
|
||||
link_name: Symbol,
|
||||
abi: &FnAbi<'tcx, Ty<'tcx>>,
|
||||
args: &'a [OpTy<'tcx>],
|
||||
) -> InterpResult<'tcx, (&'a [OpTy<'tcx>; N], &'a [OpTy<'tcx>])> {
|
||||
if !abi.c_variadic {
|
||||
throw_ub_format!("calling a variadic function with a non-variadic caller-side signature");
|
||||
}
|
||||
if abi.fixed_count != u32::try_from(N).unwrap() {
|
||||
throw_ub_format!(
|
||||
"incorrect number of fixed arguments for variadic function `{}`: got {}, expected {N}",
|
||||
link_name.as_str(),
|
||||
abi.fixed_count
|
||||
)
|
||||
}
|
||||
if let Some(args) = args.split_first_chunk() {
|
||||
return interp_ok(args);
|
||||
}
|
||||
throw_ub_format!(
|
||||
"incorrect number of arguments for `{}`: got {}, expected at least {}",
|
||||
link_name.as_str(),
|
||||
args.len(),
|
||||
N
|
||||
)
|
||||
}
|
||||
|
||||
pub fn isolation_abort_error<'tcx>(name: &str) -> InterpResult<'tcx> {
|
||||
throw_machine_stop!(TerminationInfo::UnsupportedInIsolation(format!(
|
||||
"{name} not available when isolation is enabled",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use rustc_middle::mir::BinOp;
|
||||
use rustc_middle::{mir, ty};
|
||||
|
||||
use self::helpers::check_arg_count;
|
||||
use self::helpers::check_intrinsic_arg_count;
|
||||
use crate::*;
|
||||
|
||||
pub enum AtomicOp {
|
||||
|
@ -131,7 +131,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
|
|||
) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
let [place] = check_arg_count(args)?;
|
||||
let [place] = check_intrinsic_arg_count(args)?;
|
||||
let place = this.deref_pointer(place)?;
|
||||
|
||||
// Perform atomic load.
|
||||
|
@ -144,7 +144,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
|
|||
fn atomic_store(&mut self, args: &[OpTy<'tcx>], atomic: AtomicWriteOrd) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
let [place, val] = check_arg_count(args)?;
|
||||
let [place, val] = check_intrinsic_arg_count(args)?;
|
||||
let place = this.deref_pointer(place)?;
|
||||
|
||||
// Perform regular load.
|
||||
|
@ -159,7 +159,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
|
|||
args: &[OpTy<'tcx>],
|
||||
atomic: AtomicFenceOrd,
|
||||
) -> InterpResult<'tcx> {
|
||||
let [] = check_arg_count(args)?;
|
||||
let [] = check_intrinsic_arg_count(args)?;
|
||||
let _ = atomic;
|
||||
//FIXME: compiler fences are currently ignored
|
||||
interp_ok(())
|
||||
|
@ -171,7 +171,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
|
|||
atomic: AtomicFenceOrd,
|
||||
) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
let [] = check_arg_count(args)?;
|
||||
let [] = check_intrinsic_arg_count(args)?;
|
||||
this.atomic_fence(atomic)?;
|
||||
interp_ok(())
|
||||
}
|
||||
|
@ -185,7 +185,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
|
|||
) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
let [place, rhs] = check_arg_count(args)?;
|
||||
let [place, rhs] = check_intrinsic_arg_count(args)?;
|
||||
let place = this.deref_pointer(place)?;
|
||||
let rhs = this.read_immediate(rhs)?;
|
||||
|
||||
|
@ -226,7 +226,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
|
|||
) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
let [place, new] = check_arg_count(args)?;
|
||||
let [place, new] = check_intrinsic_arg_count(args)?;
|
||||
let place = this.deref_pointer(place)?;
|
||||
let new = this.read_scalar(new)?;
|
||||
|
||||
|
@ -245,7 +245,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
|
|||
) -> InterpResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
let [place, expect_old, new] = check_arg_count(args)?;
|
||||
let [place, expect_old, new] = check_intrinsic_arg_count(args)?;
|
||||
let place = this.deref_pointer(place)?;
|
||||
let expect_old = this.read_immediate(expect_old)?; // read as immediate for the sake of `binary_op()`
|
||||
let new = this.read_scalar(new)?;
|
||||
|
|
|
@ -7,12 +7,13 @@ use rand::Rng;
|
|||
use rustc_abi::Size;
|
||||
use rustc_apfloat::{Float, Round};
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::ty::{self, FloatTy};
|
||||
use rustc_middle::ty::{self, FloatTy, ScalarInt};
|
||||
use rustc_span::{Symbol, sym};
|
||||
|
||||
use self::atomic::EvalContextExt as _;
|
||||
use self::helpers::{ToHost, ToSoft, check_arg_count};
|
||||
use self::helpers::{ToHost, ToSoft, check_intrinsic_arg_count};
|
||||
use self::simd::EvalContextExt as _;
|
||||
use crate::math::apply_random_float_error_ulp;
|
||||
use crate::*;
|
||||
|
||||
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
|
||||
|
@ -104,24 +105,24 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
|
||||
// Raw memory accesses
|
||||
"volatile_load" => {
|
||||
let [place] = check_arg_count(args)?;
|
||||
let [place] = check_intrinsic_arg_count(args)?;
|
||||
let place = this.deref_pointer(place)?;
|
||||
this.copy_op(&place, dest)?;
|
||||
}
|
||||
"volatile_store" => {
|
||||
let [place, dest] = check_arg_count(args)?;
|
||||
let [place, dest] = check_intrinsic_arg_count(args)?;
|
||||
let place = this.deref_pointer(place)?;
|
||||
this.copy_op(dest, &place)?;
|
||||
}
|
||||
|
||||
"volatile_set_memory" => {
|
||||
let [ptr, val_byte, count] = check_arg_count(args)?;
|
||||
let [ptr, val_byte, count] = check_intrinsic_arg_count(args)?;
|
||||
this.write_bytes_intrinsic(ptr, val_byte, count, "volatile_set_memory")?;
|
||||
}
|
||||
|
||||
// Memory model / provenance manipulation
|
||||
"ptr_mask" => {
|
||||
let [ptr, mask] = check_arg_count(args)?;
|
||||
let [ptr, mask] = check_intrinsic_arg_count(args)?;
|
||||
|
||||
let ptr = this.read_pointer(ptr)?;
|
||||
let mask = this.read_target_usize(mask)?;
|
||||
|
@ -137,7 +138,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
// ```
|
||||
// Would not be considered UB, or the other way around (`is_val_statically_known(0)`).
|
||||
"is_val_statically_known" => {
|
||||
let [_arg] = check_arg_count(args)?;
|
||||
let [_arg] = check_intrinsic_arg_count(args)?;
|
||||
// FIXME: should we check for validity here? It's tricky because we do not have a
|
||||
// place. Codegen does not seem to set any attributes like `noundef` for intrinsic
|
||||
// calls, so we don't *have* to do anything.
|
||||
|
@ -146,7 +147,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
}
|
||||
|
||||
"floorf16" | "ceilf16" | "truncf16" | "roundf16" | "round_ties_even_f16" => {
|
||||
let [f] = check_arg_count(args)?;
|
||||
let [f] = check_intrinsic_arg_count(args)?;
|
||||
let f = this.read_scalar(f)?.to_f16()?;
|
||||
let mode = match intrinsic_name {
|
||||
"floorf16" => Round::TowardNegative,
|
||||
|
@ -161,7 +162,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
"floorf32" | "ceilf32" | "truncf32" | "roundf32" | "round_ties_even_f32" => {
|
||||
let [f] = check_arg_count(args)?;
|
||||
let [f] = check_intrinsic_arg_count(args)?;
|
||||
let f = this.read_scalar(f)?.to_f32()?;
|
||||
let mode = match intrinsic_name {
|
||||
"floorf32" => Round::TowardNegative,
|
||||
|
@ -176,7 +177,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
"floorf64" | "ceilf64" | "truncf64" | "roundf64" | "round_ties_even_f64" => {
|
||||
let [f] = check_arg_count(args)?;
|
||||
let [f] = check_intrinsic_arg_count(args)?;
|
||||
let f = this.read_scalar(f)?.to_f64()?;
|
||||
let mode = match intrinsic_name {
|
||||
"floorf64" => Round::TowardNegative,
|
||||
|
@ -191,7 +192,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
"floorf128" | "ceilf128" | "truncf128" | "roundf128" | "round_ties_even_f128" => {
|
||||
let [f] = check_arg_count(args)?;
|
||||
let [f] = check_intrinsic_arg_count(args)?;
|
||||
let f = this.read_scalar(f)?.to_f128()?;
|
||||
let mode = match intrinsic_name {
|
||||
"floorf128" => Round::TowardNegative,
|
||||
|
@ -206,65 +207,98 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
|
||||
"sqrtf32" => {
|
||||
let [f] = check_intrinsic_arg_count(args)?;
|
||||
let f = this.read_scalar(f)?.to_f32()?;
|
||||
// Sqrt is specified to be fully precise.
|
||||
let res = math::sqrt(f);
|
||||
let res = this.adjust_nan(res, &[f]);
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
"sqrtf64" => {
|
||||
let [f] = check_intrinsic_arg_count(args)?;
|
||||
let f = this.read_scalar(f)?.to_f64()?;
|
||||
// Sqrt is specified to be fully precise.
|
||||
let res = math::sqrt(f);
|
||||
let res = this.adjust_nan(res, &[f]);
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
| "sinf32"
|
||||
| "cosf32"
|
||||
| "sqrtf32"
|
||||
| "expf32"
|
||||
| "exp2f32"
|
||||
| "logf32"
|
||||
| "log10f32"
|
||||
| "log2f32"
|
||||
=> {
|
||||
let [f] = check_arg_count(args)?;
|
||||
let [f] = check_intrinsic_arg_count(args)?;
|
||||
let f = this.read_scalar(f)?.to_f32()?;
|
||||
// Using host floats except for sqrt (but it's fine, these operations do not have
|
||||
// Using host floats (but it's fine, these operations do not have
|
||||
// guaranteed precision).
|
||||
let host = f.to_host();
|
||||
let res = match intrinsic_name {
|
||||
"sinf32" => f.to_host().sin().to_soft(),
|
||||
"cosf32" => f.to_host().cos().to_soft(),
|
||||
"sqrtf32" => math::sqrt(f),
|
||||
"expf32" => f.to_host().exp().to_soft(),
|
||||
"exp2f32" => f.to_host().exp2().to_soft(),
|
||||
"logf32" => f.to_host().ln().to_soft(),
|
||||
"log10f32" => f.to_host().log10().to_soft(),
|
||||
"log2f32" => f.to_host().log2().to_soft(),
|
||||
"sinf32" => host.sin(),
|
||||
"cosf32" => host.cos(),
|
||||
"expf32" => host.exp(),
|
||||
"exp2f32" => host.exp2(),
|
||||
"logf32" => host.ln(),
|
||||
"log10f32" => host.log10(),
|
||||
"log2f32" => host.log2(),
|
||||
_ => bug!(),
|
||||
};
|
||||
let res = res.to_soft();
|
||||
// Apply a relative error of 16ULP to introduce some non-determinism
|
||||
// simulating imprecise implementations and optimizations.
|
||||
// FIXME: temporarily disabled as it breaks std tests.
|
||||
// let res = apply_random_float_error_ulp(
|
||||
// this,
|
||||
// res,
|
||||
// 4, // log2(16)
|
||||
// );
|
||||
let res = this.adjust_nan(res, &[f]);
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
#[rustfmt::skip]
|
||||
| "sinf64"
|
||||
| "cosf64"
|
||||
| "sqrtf64"
|
||||
| "expf64"
|
||||
| "exp2f64"
|
||||
| "logf64"
|
||||
| "log10f64"
|
||||
| "log2f64"
|
||||
=> {
|
||||
let [f] = check_arg_count(args)?;
|
||||
let [f] = check_intrinsic_arg_count(args)?;
|
||||
let f = this.read_scalar(f)?.to_f64()?;
|
||||
// Using host floats except for sqrt (but it's fine, these operations do not have
|
||||
// Using host floats (but it's fine, these operations do not have
|
||||
// guaranteed precision).
|
||||
let host = f.to_host();
|
||||
let res = match intrinsic_name {
|
||||
"sinf64" => f.to_host().sin().to_soft(),
|
||||
"cosf64" => f.to_host().cos().to_soft(),
|
||||
"sqrtf64" => math::sqrt(f),
|
||||
"expf64" => f.to_host().exp().to_soft(),
|
||||
"exp2f64" => f.to_host().exp2().to_soft(),
|
||||
"logf64" => f.to_host().ln().to_soft(),
|
||||
"log10f64" => f.to_host().log10().to_soft(),
|
||||
"log2f64" => f.to_host().log2().to_soft(),
|
||||
"sinf64" => host.sin(),
|
||||
"cosf64" => host.cos(),
|
||||
"expf64" => host.exp(),
|
||||
"exp2f64" => host.exp2(),
|
||||
"logf64" => host.ln(),
|
||||
"log10f64" => host.log10(),
|
||||
"log2f64" => host.log2(),
|
||||
_ => bug!(),
|
||||
};
|
||||
let res = res.to_soft();
|
||||
// Apply a relative error of 16ULP to introduce some non-determinism
|
||||
// simulating imprecise implementations and optimizations.
|
||||
// FIXME: temporarily disabled as it breaks std tests.
|
||||
// let res = apply_random_float_error_ulp(
|
||||
// this,
|
||||
// res,
|
||||
// 4, // log2(16)
|
||||
// );
|
||||
let res = this.adjust_nan(res, &[f]);
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
|
||||
"fmaf32" => {
|
||||
let [a, b, c] = check_arg_count(args)?;
|
||||
let [a, b, c] = check_intrinsic_arg_count(args)?;
|
||||
let a = this.read_scalar(a)?.to_f32()?;
|
||||
let b = this.read_scalar(b)?.to_f32()?;
|
||||
let c = this.read_scalar(c)?.to_f32()?;
|
||||
|
@ -274,7 +308,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
"fmaf64" => {
|
||||
let [a, b, c] = check_arg_count(args)?;
|
||||
let [a, b, c] = check_intrinsic_arg_count(args)?;
|
||||
let a = this.read_scalar(a)?.to_f64()?;
|
||||
let b = this.read_scalar(b)?.to_f64()?;
|
||||
let c = this.read_scalar(c)?.to_f64()?;
|
||||
|
@ -285,7 +319,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
}
|
||||
|
||||
"fmuladdf32" => {
|
||||
let [a, b, c] = check_arg_count(args)?;
|
||||
let [a, b, c] = check_intrinsic_arg_count(args)?;
|
||||
let a = this.read_scalar(a)?.to_f32()?;
|
||||
let b = this.read_scalar(b)?.to_f32()?;
|
||||
let c = this.read_scalar(c)?.to_f32()?;
|
||||
|
@ -300,7 +334,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
"fmuladdf64" => {
|
||||
let [a, b, c] = check_arg_count(args)?;
|
||||
let [a, b, c] = check_intrinsic_arg_count(args)?;
|
||||
let a = this.read_scalar(a)?.to_f64()?;
|
||||
let b = this.read_scalar(b)?.to_f64()?;
|
||||
let c = this.read_scalar(c)?.to_f64()?;
|
||||
|
@ -316,7 +350,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
}
|
||||
|
||||
"powf32" => {
|
||||
let [f1, f2] = check_arg_count(args)?;
|
||||
// FIXME: apply random relative error but without altering behaviour of powf
|
||||
let [f1, f2] = check_intrinsic_arg_count(args)?;
|
||||
let f1 = this.read_scalar(f1)?.to_f32()?;
|
||||
let f2 = this.read_scalar(f2)?.to_f32()?;
|
||||
// Using host floats (but it's fine, this operation does not have guaranteed precision).
|
||||
|
@ -325,7 +360,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
"powf64" => {
|
||||
let [f1, f2] = check_arg_count(args)?;
|
||||
// FIXME: apply random relative error but without altering behaviour of powf
|
||||
let [f1, f2] = check_intrinsic_arg_count(args)?;
|
||||
let f1 = this.read_scalar(f1)?.to_f64()?;
|
||||
let f2 = this.read_scalar(f2)?.to_f64()?;
|
||||
// Using host floats (but it's fine, this operation does not have guaranteed precision).
|
||||
|
@ -335,7 +371,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
}
|
||||
|
||||
"powif32" => {
|
||||
let [f, i] = check_arg_count(args)?;
|
||||
// FIXME: apply random relative error but without altering behaviour of powi
|
||||
let [f, i] = check_intrinsic_arg_count(args)?;
|
||||
let f = this.read_scalar(f)?.to_f32()?;
|
||||
let i = this.read_scalar(i)?.to_i32()?;
|
||||
// Using host floats (but it's fine, this operation does not have guaranteed precision).
|
||||
|
@ -344,7 +381,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
"powif64" => {
|
||||
let [f, i] = check_arg_count(args)?;
|
||||
// FIXME: apply random relative error but without altering behaviour of powi
|
||||
let [f, i] = check_intrinsic_arg_count(args)?;
|
||||
let f = this.read_scalar(f)?.to_f64()?;
|
||||
let i = this.read_scalar(i)?.to_i32()?;
|
||||
// Using host floats (but it's fine, this operation does not have guaranteed precision).
|
||||
|
@ -360,7 +398,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
| "fdiv_algebraic"
|
||||
| "frem_algebraic"
|
||||
=> {
|
||||
let [a, b] = check_arg_count(args)?;
|
||||
let [a, b] = check_intrinsic_arg_count(args)?;
|
||||
let a = this.read_immediate(a)?;
|
||||
let b = this.read_immediate(b)?;
|
||||
let op = match intrinsic_name {
|
||||
|
@ -372,7 +410,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
_ => bug!(),
|
||||
};
|
||||
let res = this.binary_op(op, &a, &b)?;
|
||||
// `binary_op` already called `generate_nan` if necessary.
|
||||
// `binary_op` already called `generate_nan` if needed.
|
||||
// Apply a relative error of 16ULP to simulate non-deterministic precision loss
|
||||
// due to optimizations.
|
||||
let res = apply_random_float_error_to_imm(this, res, 4 /* log2(16) */)?;
|
||||
this.write_immediate(*res, dest)?;
|
||||
}
|
||||
|
||||
|
@ -383,7 +424,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
| "fdiv_fast"
|
||||
| "frem_fast"
|
||||
=> {
|
||||
let [a, b] = check_arg_count(args)?;
|
||||
let [a, b] = check_intrinsic_arg_count(args)?;
|
||||
let a = this.read_immediate(a)?;
|
||||
let b = this.read_immediate(b)?;
|
||||
let op = match intrinsic_name {
|
||||
|
@ -418,16 +459,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
_ => {}
|
||||
}
|
||||
let res = this.binary_op(op, &a, &b)?;
|
||||
// This cannot be a NaN so we also don't have to apply any non-determinism.
|
||||
// (Also, `binary_op` already called `generate_nan` if needed.)
|
||||
if !float_finite(&res)? {
|
||||
throw_ub_format!("`{intrinsic_name}` intrinsic produced non-finite value as result");
|
||||
}
|
||||
// This cannot be a NaN so we also don't have to apply any non-determinism.
|
||||
// (Also, `binary_op` already called `generate_nan` if needed.)
|
||||
// Apply a relative error of 16ULP to simulate non-deterministic precision loss
|
||||
// due to optimizations.
|
||||
let res = apply_random_float_error_to_imm(this, res, 4 /* log2(16) */)?;
|
||||
this.write_immediate(*res, dest)?;
|
||||
}
|
||||
|
||||
"float_to_int_unchecked" => {
|
||||
let [val] = check_arg_count(args)?;
|
||||
let [val] = check_intrinsic_arg_count(args)?;
|
||||
let val = this.read_immediate(val)?;
|
||||
|
||||
let res = this
|
||||
|
@ -444,7 +488,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
|
||||
// Other
|
||||
"breakpoint" => {
|
||||
let [] = check_arg_count(args)?;
|
||||
let [] = check_intrinsic_arg_count(args)?;
|
||||
// normally this would raise a SIGTRAP, which aborts if no debugger is connected
|
||||
throw_machine_stop!(TerminationInfo::Abort(format!("trace/breakpoint trap")))
|
||||
}
|
||||
|
@ -455,3 +499,26 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
interp_ok(EmulateItemResult::NeedsReturn)
|
||||
}
|
||||
}
|
||||
|
||||
/// Applies a random 16ULP floating point error to `val` and returns the new value.
|
||||
/// Will fail if `val` is not a floating point number.
|
||||
fn apply_random_float_error_to_imm<'tcx>(
|
||||
ecx: &mut MiriInterpCx<'tcx>,
|
||||
val: ImmTy<'tcx>,
|
||||
ulp_exponent: u32,
|
||||
) -> InterpResult<'tcx, ImmTy<'tcx>> {
|
||||
let scalar = val.to_scalar_int()?;
|
||||
let res: ScalarInt = match val.layout.ty.kind() {
|
||||
ty::Float(FloatTy::F16) =>
|
||||
apply_random_float_error_ulp(ecx, scalar.to_f16(), ulp_exponent).into(),
|
||||
ty::Float(FloatTy::F32) =>
|
||||
apply_random_float_error_ulp(ecx, scalar.to_f32(), ulp_exponent).into(),
|
||||
ty::Float(FloatTy::F64) =>
|
||||
apply_random_float_error_ulp(ecx, scalar.to_f64(), ulp_exponent).into(),
|
||||
ty::Float(FloatTy::F128) =>
|
||||
apply_random_float_error_ulp(ecx, scalar.to_f128(), ulp_exponent).into(),
|
||||
_ => bug!("intrinsic called with non-float input type"),
|
||||
};
|
||||
|
||||
interp_ok(ImmTy::from_scalar_int(res, val.layout))
|
||||
}
|
||||
|
|
|
@ -7,7 +7,9 @@ use rustc_middle::ty::layout::LayoutOf;
|
|||
use rustc_middle::{mir, ty};
|
||||
use rustc_span::{Symbol, sym};
|
||||
|
||||
use crate::helpers::{ToHost, ToSoft, bool_to_simd_element, check_arg_count, simd_element_to_bool};
|
||||
use crate::helpers::{
|
||||
ToHost, ToSoft, bool_to_simd_element, check_intrinsic_arg_count, simd_element_to_bool,
|
||||
};
|
||||
use crate::*;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
|
@ -50,7 +52,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
| "bswap"
|
||||
| "bitreverse"
|
||||
=> {
|
||||
let [op] = check_arg_count(args)?;
|
||||
let [op] = check_intrinsic_arg_count(args)?;
|
||||
let (op, op_len) = this.project_to_simd(op)?;
|
||||
let (dest, dest_len) = this.project_to_simd(dest)?;
|
||||
|
||||
|
@ -197,7 +199,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
=> {
|
||||
use mir::BinOp;
|
||||
|
||||
let [left, right] = check_arg_count(args)?;
|
||||
let [left, right] = check_intrinsic_arg_count(args)?;
|
||||
let (left, left_len) = this.project_to_simd(left)?;
|
||||
let (right, right_len) = this.project_to_simd(right)?;
|
||||
let (dest, dest_len) = this.project_to_simd(dest)?;
|
||||
|
@ -288,7 +290,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
}
|
||||
}
|
||||
"fma" | "relaxed_fma" => {
|
||||
let [a, b, c] = check_arg_count(args)?;
|
||||
let [a, b, c] = check_intrinsic_arg_count(args)?;
|
||||
let (a, a_len) = this.project_to_simd(a)?;
|
||||
let (b, b_len) = this.project_to_simd(b)?;
|
||||
let (c, c_len) = this.project_to_simd(c)?;
|
||||
|
@ -352,7 +354,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
| "reduce_min" => {
|
||||
use mir::BinOp;
|
||||
|
||||
let [op] = check_arg_count(args)?;
|
||||
let [op] = check_intrinsic_arg_count(args)?;
|
||||
let (op, op_len) = this.project_to_simd(op)?;
|
||||
|
||||
let imm_from_bool =
|
||||
|
@ -415,7 +417,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
| "reduce_mul_ordered" => {
|
||||
use mir::BinOp;
|
||||
|
||||
let [op, init] = check_arg_count(args)?;
|
||||
let [op, init] = check_intrinsic_arg_count(args)?;
|
||||
let (op, op_len) = this.project_to_simd(op)?;
|
||||
let init = this.read_immediate(init)?;
|
||||
|
||||
|
@ -433,7 +435,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
this.write_immediate(*res, dest)?;
|
||||
}
|
||||
"select" => {
|
||||
let [mask, yes, no] = check_arg_count(args)?;
|
||||
let [mask, yes, no] = check_intrinsic_arg_count(args)?;
|
||||
let (mask, mask_len) = this.project_to_simd(mask)?;
|
||||
let (yes, yes_len) = this.project_to_simd(yes)?;
|
||||
let (no, no_len) = this.project_to_simd(no)?;
|
||||
|
@ -455,7 +457,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
}
|
||||
// Variant of `select` that takes a bitmask rather than a "vector of bool".
|
||||
"select_bitmask" => {
|
||||
let [mask, yes, no] = check_arg_count(args)?;
|
||||
let [mask, yes, no] = check_intrinsic_arg_count(args)?;
|
||||
let (yes, yes_len) = this.project_to_simd(yes)?;
|
||||
let (no, no_len) = this.project_to_simd(no)?;
|
||||
let (dest, dest_len) = this.project_to_simd(dest)?;
|
||||
|
@ -529,7 +531,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
}
|
||||
// Converts a "vector of bool" into a bitmask.
|
||||
"bitmask" => {
|
||||
let [op] = check_arg_count(args)?;
|
||||
let [op] = check_intrinsic_arg_count(args)?;
|
||||
let (op, op_len) = this.project_to_simd(op)?;
|
||||
let bitmask_len = op_len.next_multiple_of(8);
|
||||
if bitmask_len > 64 {
|
||||
|
@ -577,7 +579,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
}
|
||||
}
|
||||
"cast" | "as" | "cast_ptr" | "expose_provenance" | "with_exposed_provenance" => {
|
||||
let [op] = check_arg_count(args)?;
|
||||
let [op] = check_intrinsic_arg_count(args)?;
|
||||
let (op, op_len) = this.project_to_simd(op)?;
|
||||
let (dest, dest_len) = this.project_to_simd(dest)?;
|
||||
|
||||
|
@ -634,7 +636,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
}
|
||||
}
|
||||
"shuffle_const_generic" => {
|
||||
let [left, right] = check_arg_count(args)?;
|
||||
let [left, right] = check_intrinsic_arg_count(args)?;
|
||||
let (left, left_len) = this.project_to_simd(left)?;
|
||||
let (right, right_len) = this.project_to_simd(right)?;
|
||||
let (dest, dest_len) = this.project_to_simd(dest)?;
|
||||
|
@ -664,7 +666,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
}
|
||||
}
|
||||
"shuffle" => {
|
||||
let [left, right, index] = check_arg_count(args)?;
|
||||
let [left, right, index] = check_intrinsic_arg_count(args)?;
|
||||
let (left, left_len) = this.project_to_simd(left)?;
|
||||
let (right, right_len) = this.project_to_simd(right)?;
|
||||
let (index, index_len) = this.project_to_simd(index)?;
|
||||
|
@ -695,7 +697,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
}
|
||||
}
|
||||
"gather" => {
|
||||
let [passthru, ptrs, mask] = check_arg_count(args)?;
|
||||
let [passthru, ptrs, mask] = check_intrinsic_arg_count(args)?;
|
||||
let (passthru, passthru_len) = this.project_to_simd(passthru)?;
|
||||
let (ptrs, ptrs_len) = this.project_to_simd(ptrs)?;
|
||||
let (mask, mask_len) = this.project_to_simd(mask)?;
|
||||
|
@ -721,7 +723,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
}
|
||||
}
|
||||
"scatter" => {
|
||||
let [value, ptrs, mask] = check_arg_count(args)?;
|
||||
let [value, ptrs, mask] = check_intrinsic_arg_count(args)?;
|
||||
let (value, value_len) = this.project_to_simd(value)?;
|
||||
let (ptrs, ptrs_len) = this.project_to_simd(ptrs)?;
|
||||
let (mask, mask_len) = this.project_to_simd(mask)?;
|
||||
|
@ -741,7 +743,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
}
|
||||
}
|
||||
"masked_load" => {
|
||||
let [mask, ptr, default] = check_arg_count(args)?;
|
||||
let [mask, ptr, default] = check_intrinsic_arg_count(args)?;
|
||||
let (mask, mask_len) = this.project_to_simd(mask)?;
|
||||
let ptr = this.read_pointer(ptr)?;
|
||||
let (default, default_len) = this.project_to_simd(default)?;
|
||||
|
@ -767,7 +769,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
}
|
||||
}
|
||||
"masked_store" => {
|
||||
let [mask, ptr, vals] = check_arg_count(args)?;
|
||||
let [mask, ptr, vals] = check_intrinsic_arg_count(args)?;
|
||||
let (mask, mask_len) = this.project_to_simd(mask)?;
|
||||
let ptr = this.read_pointer(ptr)?;
|
||||
let (vals, vals_len) = this.project_to_simd(vals)?;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#![feature(rustc_private)]
|
||||
#![feature(cfg_match)]
|
||||
#![feature(cell_update)]
|
||||
#![feature(float_gamma)]
|
||||
#![feature(float_erf)]
|
||||
|
|
|
@ -713,12 +713,13 @@ impl<'tcx> MiriMachine<'tcx> {
|
|||
clock: Clock::new(config.isolated_op == IsolatedOp::Allow),
|
||||
#[cfg(unix)]
|
||||
native_lib: config.native_lib.as_ref().map(|lib_file_path| {
|
||||
let host_triple = rustc_session::config::host_tuple();
|
||||
let target_triple = tcx.sess.opts.target_triple.tuple();
|
||||
// Check if host target == the session target.
|
||||
if env!("TARGET") != target_triple {
|
||||
if host_triple != target_triple {
|
||||
panic!(
|
||||
"calling external C functions in linked .so file requires host and target to be the same: host={}, target={}",
|
||||
env!("TARGET"),
|
||||
host_triple,
|
||||
target_triple,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -27,6 +27,22 @@ pub(crate) fn apply_random_float_error<F: rustc_apfloat::Float>(
|
|||
(val * (F::from_u128(1).value + err).value).value
|
||||
}
|
||||
|
||||
/// [`apply_random_float_error`] gives instructions to apply a 2^N ULP error.
|
||||
/// This function implements these instructions such that applying a 2^N ULP error is less error prone.
|
||||
/// So for a 2^N ULP error, you would pass N as the `ulp_exponent` argument.
|
||||
pub(crate) fn apply_random_float_error_ulp<F: rustc_apfloat::Float>(
|
||||
ecx: &mut crate::MiriInterpCx<'_>,
|
||||
val: F,
|
||||
ulp_exponent: u32,
|
||||
) -> F {
|
||||
let n = i32::try_from(ulp_exponent)
|
||||
.expect("`err_scale_for_ulp`: exponent is too large to create an error scale");
|
||||
// we know this fits
|
||||
let prec = i32::try_from(F::PRECISION).unwrap();
|
||||
let err_scale = -(prec - n - 1);
|
||||
apply_random_float_error(ecx, val, err_scale)
|
||||
}
|
||||
|
||||
pub(crate) fn sqrt<S: rustc_apfloat::ieee::Semantics>(x: IeeeFloat<S>) -> IeeeFloat<S> {
|
||||
match x.category() {
|
||||
// preserve zero sign
|
||||
|
|
78
src/tools/miri/src/shims/aarch64.rs
Normal file
78
src/tools/miri/src/shims/aarch64.rs
Normal file
|
@ -0,0 +1,78 @@
|
|||
use rustc_middle::mir::BinOp;
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_span::Symbol;
|
||||
use rustc_target::callconv::{Conv, FnAbi};
|
||||
|
||||
use crate::*;
|
||||
|
||||
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
|
||||
pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||
fn emulate_aarch64_intrinsic(
|
||||
&mut self,
|
||||
link_name: Symbol,
|
||||
abi: &FnAbi<'tcx, Ty<'tcx>>,
|
||||
args: &[OpTy<'tcx>],
|
||||
dest: &MPlaceTy<'tcx>,
|
||||
) -> InterpResult<'tcx, EmulateItemResult> {
|
||||
let this = self.eval_context_mut();
|
||||
// Prefix should have already been checked.
|
||||
let unprefixed_name = link_name.as_str().strip_prefix("llvm.aarch64.").unwrap();
|
||||
match unprefixed_name {
|
||||
"isb" => {
|
||||
let [arg] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let arg = this.read_scalar(arg)?.to_i32()?;
|
||||
match arg {
|
||||
// SY ("full system scope")
|
||||
15 => {
|
||||
this.yield_active_thread();
|
||||
}
|
||||
_ => {
|
||||
throw_unsup_format!("unsupported llvm.aarch64.isb argument {}", arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Used to implement the vpmaxq_u8 function.
|
||||
// Computes the maximum of adjacent pairs; the first half of the output is produced from the
|
||||
// `left` input, the second half of the output from the `right` input.
|
||||
// https://developer.arm.com/architectures/instruction-sets/intrinsics/vpmaxq_u8
|
||||
"neon.umaxp.v16i8" => {
|
||||
let [left, right] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
|
||||
let (left, left_len) = this.project_to_simd(left)?;
|
||||
let (right, right_len) = this.project_to_simd(right)?;
|
||||
let (dest, lane_count) = this.project_to_simd(dest)?;
|
||||
assert_eq!(left_len, right_len);
|
||||
assert_eq!(lane_count, left_len);
|
||||
|
||||
for lane_idx in 0..lane_count {
|
||||
let src = if lane_idx < (lane_count / 2) { &left } else { &right };
|
||||
let src_idx = lane_idx.strict_rem(lane_count / 2);
|
||||
|
||||
let lhs_lane =
|
||||
this.read_immediate(&this.project_index(src, src_idx.strict_mul(2))?)?;
|
||||
let rhs_lane = this.read_immediate(
|
||||
&this.project_index(src, src_idx.strict_mul(2).strict_add(1))?,
|
||||
)?;
|
||||
|
||||
// Compute `if lhs > rhs { lhs } else { rhs }`, i.e., `max`.
|
||||
let res_lane = if this
|
||||
.binary_op(BinOp::Gt, &lhs_lane, &rhs_lane)?
|
||||
.to_scalar()
|
||||
.to_bool()?
|
||||
{
|
||||
lhs_lane
|
||||
} else {
|
||||
rhs_lane
|
||||
};
|
||||
|
||||
let dest = this.project_index(&dest, lane_idx)?;
|
||||
this.write_immediate(*res_lane, &dest)?;
|
||||
}
|
||||
}
|
||||
|
||||
_ => return interp_ok(EmulateItemResult::NotSupported),
|
||||
}
|
||||
interp_ok(EmulateItemResult::NeedsReturn)
|
||||
}
|
||||
}
|
|
@ -766,6 +766,14 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
_ => bug!(),
|
||||
};
|
||||
let res = res.to_soft();
|
||||
// Apply a relative error of 16ULP to introduce some non-determinism
|
||||
// simulating imprecise implementations and optimizations.
|
||||
// FIXME: temporarily disabled as it breaks std tests.
|
||||
// let res = math::apply_random_float_error_ulp(
|
||||
// this,
|
||||
// res,
|
||||
// 4, // log2(16)
|
||||
// );
|
||||
let res = this.adjust_nan(res, &[f]);
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
|
@ -788,6 +796,14 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
"fdimf" => f1.to_host().abs_sub(f2.to_host()).to_soft(),
|
||||
_ => bug!(),
|
||||
};
|
||||
// Apply a relative error of 16ULP to introduce some non-determinism
|
||||
// simulating imprecise implementations and optimizations.
|
||||
// FIXME: temporarily disabled as it breaks std tests.
|
||||
// let res = math::apply_random_float_error_ulp(
|
||||
// this,
|
||||
// res,
|
||||
// 4, // log2(16)
|
||||
// );
|
||||
let res = this.adjust_nan(res, &[f1, f2]);
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
|
@ -827,6 +843,14 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
_ => bug!(),
|
||||
};
|
||||
let res = res.to_soft();
|
||||
// Apply a relative error of 16ULP to introduce some non-determinism
|
||||
// simulating imprecise implementations and optimizations.
|
||||
// FIXME: temporarily disabled as it breaks std tests.
|
||||
// let res = math::apply_random_float_error_ulp(
|
||||
// this,
|
||||
// res.to_soft(),
|
||||
// 4, // log2(16)
|
||||
// );
|
||||
let res = this.adjust_nan(res, &[f]);
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
|
@ -849,6 +873,14 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
"fdim" => f1.to_host().abs_sub(f2.to_host()).to_soft(),
|
||||
_ => bug!(),
|
||||
};
|
||||
// Apply a relative error of 16ULP to introduce some non-determinism
|
||||
// simulating imprecise implementations and optimizations.
|
||||
// FIXME: temporarily disabled as it breaks std tests.
|
||||
// let res = math::apply_random_float_error_ulp(
|
||||
// this,
|
||||
// res,
|
||||
// 4, // log2(16)
|
||||
// );
|
||||
let res = this.adjust_nan(res, &[f1, f2]);
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
|
@ -874,7 +906,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
// Using host floats (but it's fine, these operations do not have guaranteed precision).
|
||||
let (res, sign) = x.to_host().ln_gamma();
|
||||
this.write_int(sign, &signp)?;
|
||||
let res = this.adjust_nan(res.to_soft(), &[x]);
|
||||
let res = res.to_soft();
|
||||
// Apply a relative error of 16ULP to introduce some non-determinism
|
||||
// simulating imprecise implementations and optimizations.
|
||||
// FIXME: temporarily disabled as it breaks std tests.
|
||||
// let res = math::apply_random_float_error_ulp(this, res, 4 /* log2(16) */);
|
||||
let res = this.adjust_nan(res, &[x]);
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
"lgamma_r" => {
|
||||
|
@ -885,7 +922,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
// Using host floats (but it's fine, these operations do not have guaranteed precision).
|
||||
let (res, sign) = x.to_host().ln_gamma();
|
||||
this.write_int(sign, &signp)?;
|
||||
let res = this.adjust_nan(res.to_soft(), &[x]);
|
||||
let res = res.to_soft();
|
||||
// Apply a relative error of 16ULP to introduce some non-determinism
|
||||
// simulating imprecise implementations and optimizations.
|
||||
// FIXME: temporarily disabled as it breaks std tests.
|
||||
// let res = math::apply_random_float_error_ulp(this, res, 4 /* log2(16) */);
|
||||
let res = this.adjust_nan(res, &[x]);
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
|
||||
|
@ -947,20 +989,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
this, link_name, abi, args, dest,
|
||||
);
|
||||
}
|
||||
// FIXME: Move these to an `arm` submodule.
|
||||
"llvm.aarch64.isb" if this.tcx.sess.target.arch == "aarch64" => {
|
||||
let [arg] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let arg = this.read_scalar(arg)?.to_i32()?;
|
||||
match arg {
|
||||
// SY ("full system scope")
|
||||
15 => {
|
||||
this.yield_active_thread();
|
||||
}
|
||||
_ => {
|
||||
throw_unsup_format!("unsupported llvm.aarch64.isb argument {}", arg);
|
||||
}
|
||||
}
|
||||
name if name.starts_with("llvm.aarch64.") && this.tcx.sess.target.arch == "aarch64" => {
|
||||
return shims::aarch64::EvalContextExt::emulate_aarch64_intrinsic(
|
||||
this, link_name, abi, args, dest,
|
||||
);
|
||||
}
|
||||
// FIXME: Move this to an `arm` submodule.
|
||||
"llvm.arm.hint" if this.tcx.sess.target.arch == "arm" => {
|
||||
let [arg] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let arg = this.read_scalar(arg)?.to_i32()?;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#![warn(clippy::arithmetic_side_effects)]
|
||||
|
||||
mod aarch64;
|
||||
mod alloc;
|
||||
mod backtrace;
|
||||
mod files;
|
||||
|
|
|
@ -15,7 +15,7 @@ use rustc_abi::ExternAbi;
|
|||
use rustc_middle::{mir, ty};
|
||||
use rustc_target::spec::PanicStrategy;
|
||||
|
||||
use self::helpers::check_arg_count;
|
||||
use self::helpers::check_intrinsic_arg_count;
|
||||
use crate::*;
|
||||
|
||||
/// Holds all of the relevant data for when unwinding hits a `try` frame.
|
||||
|
@ -77,7 +77,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
// a pointer to `Box<dyn Any + Send + 'static>`.
|
||||
|
||||
// Get all the arguments.
|
||||
let [try_fn, data, catch_fn] = check_arg_count(args)?;
|
||||
let [try_fn, data, catch_fn] = check_intrinsic_arg_count(args)?;
|
||||
let try_fn = this.read_pointer(try_fn)?;
|
||||
let data = this.read_immediate(data)?;
|
||||
let catch_fn = this.read_pointer(catch_fn)?;
|
||||
|
|
|
@ -815,7 +815,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
this.handle_miri_start_unwind(payload)?;
|
||||
return interp_ok(EmulateItemResult::NeedsUnwind);
|
||||
}
|
||||
"getuid" => {
|
||||
"getuid" | "geteuid" => {
|
||||
let [] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
// For now, just pretend we always have this fixed UID.
|
||||
this.write_int(UID, dest)?;
|
||||
|
|
|
@ -178,92 +178,91 @@ impl UnixFileDescription for FileHandle {
|
|||
op: FlockOp,
|
||||
) -> InterpResult<'tcx, io::Result<()>> {
|
||||
assert!(communicate_allowed, "isolation should have prevented even opening a file");
|
||||
#[cfg(target_family = "unix")]
|
||||
{
|
||||
use std::os::fd::AsRawFd;
|
||||
cfg_match! {
|
||||
all(target_family = "unix", not(target_os = "solaris")) => {
|
||||
use std::os::fd::AsRawFd;
|
||||
|
||||
use FlockOp::*;
|
||||
// We always use non-blocking call to prevent interpreter from being blocked
|
||||
let (host_op, lock_nb) = match op {
|
||||
SharedLock { nonblocking } => (libc::LOCK_SH | libc::LOCK_NB, nonblocking),
|
||||
ExclusiveLock { nonblocking } => (libc::LOCK_EX | libc::LOCK_NB, nonblocking),
|
||||
Unlock => (libc::LOCK_UN, false),
|
||||
};
|
||||
use FlockOp::*;
|
||||
// We always use non-blocking call to prevent interpreter from being blocked
|
||||
let (host_op, lock_nb) = match op {
|
||||
SharedLock { nonblocking } => (libc::LOCK_SH | libc::LOCK_NB, nonblocking),
|
||||
ExclusiveLock { nonblocking } => (libc::LOCK_EX | libc::LOCK_NB, nonblocking),
|
||||
Unlock => (libc::LOCK_UN, false),
|
||||
};
|
||||
|
||||
let fd = self.file.as_raw_fd();
|
||||
let ret = unsafe { libc::flock(fd, host_op) };
|
||||
let res = match ret {
|
||||
0 => Ok(()),
|
||||
-1 => {
|
||||
let err = io::Error::last_os_error();
|
||||
if !lock_nb && err.kind() == io::ErrorKind::WouldBlock {
|
||||
throw_unsup_format!("blocking `flock` is not currently supported");
|
||||
}
|
||||
Err(err)
|
||||
}
|
||||
ret => panic!("Unexpected return value from flock: {ret}"),
|
||||
};
|
||||
interp_ok(res)
|
||||
}
|
||||
|
||||
#[cfg(target_family = "windows")]
|
||||
{
|
||||
use std::os::windows::io::AsRawHandle;
|
||||
|
||||
use windows_sys::Win32::Foundation::{
|
||||
ERROR_IO_PENDING, ERROR_LOCK_VIOLATION, FALSE, HANDLE, TRUE,
|
||||
};
|
||||
use windows_sys::Win32::Storage::FileSystem::{
|
||||
LOCKFILE_EXCLUSIVE_LOCK, LOCKFILE_FAIL_IMMEDIATELY, LockFileEx, UnlockFile,
|
||||
};
|
||||
|
||||
let fh = self.file.as_raw_handle() as HANDLE;
|
||||
|
||||
use FlockOp::*;
|
||||
let (ret, lock_nb) = match op {
|
||||
SharedLock { nonblocking } | ExclusiveLock { nonblocking } => {
|
||||
// We always use non-blocking call to prevent interpreter from being blocked
|
||||
let mut flags = LOCKFILE_FAIL_IMMEDIATELY;
|
||||
if matches!(op, ExclusiveLock { .. }) {
|
||||
flags |= LOCKFILE_EXCLUSIVE_LOCK;
|
||||
}
|
||||
let ret = unsafe { LockFileEx(fh, flags, 0, !0, !0, &mut std::mem::zeroed()) };
|
||||
(ret, nonblocking)
|
||||
}
|
||||
Unlock => {
|
||||
let ret = unsafe { UnlockFile(fh, 0, 0, !0, !0) };
|
||||
(ret, false)
|
||||
}
|
||||
};
|
||||
|
||||
let res = match ret {
|
||||
TRUE => Ok(()),
|
||||
FALSE => {
|
||||
let mut err = io::Error::last_os_error();
|
||||
// This only runs on Windows hosts so we can use `raw_os_error`.
|
||||
// We have to be careful not to forward that error code to target code.
|
||||
let code: u32 = err.raw_os_error().unwrap().try_into().unwrap();
|
||||
if matches!(code, ERROR_IO_PENDING | ERROR_LOCK_VIOLATION) {
|
||||
if lock_nb {
|
||||
// The io error mapping does not know about these error codes,
|
||||
// so we translate it to `WouldBlock` manually.
|
||||
let desc = format!("LockFileEx wouldblock error: {err}");
|
||||
err = io::Error::new(io::ErrorKind::WouldBlock, desc);
|
||||
} else {
|
||||
let fd = self.file.as_raw_fd();
|
||||
let ret = unsafe { libc::flock(fd, host_op) };
|
||||
let res = match ret {
|
||||
0 => Ok(()),
|
||||
-1 => {
|
||||
let err = io::Error::last_os_error();
|
||||
if !lock_nb && err.kind() == io::ErrorKind::WouldBlock {
|
||||
throw_unsup_format!("blocking `flock` is not currently supported");
|
||||
}
|
||||
Err(err)
|
||||
}
|
||||
Err(err)
|
||||
}
|
||||
_ => panic!("Unexpected return value: {ret}"),
|
||||
};
|
||||
interp_ok(res)
|
||||
}
|
||||
ret => panic!("Unexpected return value from flock: {ret}"),
|
||||
};
|
||||
interp_ok(res)
|
||||
}
|
||||
target_family = "windows" => {
|
||||
use std::os::windows::io::AsRawHandle;
|
||||
|
||||
#[cfg(not(any(target_family = "unix", target_family = "windows")))]
|
||||
{
|
||||
let _ = op;
|
||||
compile_error!("flock is supported only on UNIX and Windows hosts");
|
||||
use windows_sys::Win32::Foundation::{
|
||||
ERROR_IO_PENDING, ERROR_LOCK_VIOLATION, FALSE, HANDLE, TRUE,
|
||||
};
|
||||
use windows_sys::Win32::Storage::FileSystem::{
|
||||
LOCKFILE_EXCLUSIVE_LOCK, LOCKFILE_FAIL_IMMEDIATELY, LockFileEx, UnlockFile,
|
||||
};
|
||||
|
||||
let fh = self.file.as_raw_handle() as HANDLE;
|
||||
|
||||
use FlockOp::*;
|
||||
let (ret, lock_nb) = match op {
|
||||
SharedLock { nonblocking } | ExclusiveLock { nonblocking } => {
|
||||
// We always use non-blocking call to prevent interpreter from being blocked
|
||||
let mut flags = LOCKFILE_FAIL_IMMEDIATELY;
|
||||
if matches!(op, ExclusiveLock { .. }) {
|
||||
flags |= LOCKFILE_EXCLUSIVE_LOCK;
|
||||
}
|
||||
let ret = unsafe { LockFileEx(fh, flags, 0, !0, !0, &mut std::mem::zeroed()) };
|
||||
(ret, nonblocking)
|
||||
}
|
||||
Unlock => {
|
||||
let ret = unsafe { UnlockFile(fh, 0, 0, !0, !0) };
|
||||
(ret, false)
|
||||
}
|
||||
};
|
||||
|
||||
let res = match ret {
|
||||
TRUE => Ok(()),
|
||||
FALSE => {
|
||||
let mut err = io::Error::last_os_error();
|
||||
// This only runs on Windows hosts so we can use `raw_os_error`.
|
||||
// We have to be careful not to forward that error code to target code.
|
||||
let code: u32 = err.raw_os_error().unwrap().try_into().unwrap();
|
||||
if matches!(code, ERROR_IO_PENDING | ERROR_LOCK_VIOLATION) {
|
||||
if lock_nb {
|
||||
// The io error mapping does not know about these error codes,
|
||||
// so we translate it to `WouldBlock` manually.
|
||||
let desc = format!("LockFileEx wouldblock error: {err}");
|
||||
err = io::Error::new(io::ErrorKind::WouldBlock, desc);
|
||||
} else {
|
||||
throw_unsup_format!("blocking `flock` is not currently supported");
|
||||
}
|
||||
}
|
||||
Err(err)
|
||||
}
|
||||
_ => panic!("Unexpected return value: {ret}"),
|
||||
};
|
||||
interp_ok(res)
|
||||
}
|
||||
_ => {
|
||||
let _ = op;
|
||||
throw_unsup_format!(
|
||||
"flock is supported only on UNIX (except Solaris) and Windows hosts"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -133,8 +133,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
this.write_scalar(ptr, dest)?;
|
||||
}
|
||||
"mremap" => {
|
||||
let [old_address, old_size, new_size, flags] =
|
||||
this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let ([old_address, old_size, new_size, flags], _) =
|
||||
this.check_shim_variadic(abi, Conv::C, link_name, args)?;
|
||||
let ptr = this.mremap(old_address, old_size, new_size, flags)?;
|
||||
this.write_scalar(ptr, dest)?;
|
||||
}
|
||||
|
|
|
@ -13,14 +13,6 @@ pub fn futex<'tcx>(
|
|||
varargs: &[OpTy<'tcx>],
|
||||
dest: &MPlaceTy<'tcx>,
|
||||
) -> InterpResult<'tcx> {
|
||||
// The amount of arguments used depends on the type of futex operation.
|
||||
// The full futex syscall takes six arguments (excluding the syscall
|
||||
// number), which is also the maximum amount of arguments a linux syscall
|
||||
// can take on most architectures.
|
||||
// However, not all futex operations use all six arguments. The unused ones
|
||||
// may or may not be left out from the `syscall()` call.
|
||||
// Therefore we don't use `check_arg_count` here, but only check for the
|
||||
// number of arguments to fall within a range.
|
||||
let [addr, op, val] = check_min_vararg_count("`syscall(SYS_futex, ...)`", varargs)?;
|
||||
|
||||
// The first three arguments (after the syscall number itself) are the same to all futex operations:
|
||||
|
|
|
@ -5,6 +5,6 @@ fn main() {
|
|||
|
||||
unsafe {
|
||||
abort(1);
|
||||
//~^ ERROR: Undefined Behavior: incorrect number of arguments: got 1, expected 0
|
||||
//~^ ERROR: Undefined Behavior: incorrect number of arguments for `abort`: got 1, expected 0
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
error: Undefined Behavior: incorrect number of arguments: got 1, expected 0
|
||||
error: Undefined Behavior: incorrect number of arguments for `abort`: got 1, expected 0
|
||||
--> tests/fail/function_calls/check_arg_count_abort.rs:LL:CC
|
||||
|
|
||||
LL | abort(1);
|
||||
| ^^^^^^^^ incorrect number of arguments: got 1, expected 0
|
||||
| ^^^^^^^^ incorrect number of arguments for `abort`: got 1, expected 0
|
||||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
|
|
|
@ -4,6 +4,6 @@ fn main() {
|
|||
}
|
||||
|
||||
unsafe {
|
||||
let _ = malloc(); //~ ERROR: Undefined Behavior: incorrect number of arguments: got 0, expected 1
|
||||
let _ = malloc(); //~ ERROR: Undefined Behavior: incorrect number of arguments for `malloc`: got 0, expected 1
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
error: Undefined Behavior: incorrect number of arguments: got 0, expected 1
|
||||
error: Undefined Behavior: incorrect number of arguments for `malloc`: got 0, expected 1
|
||||
--> tests/fail/function_calls/check_arg_count_too_few_args.rs:LL:CC
|
||||
|
|
||||
LL | let _ = malloc();
|
||||
| ^^^^^^^^ incorrect number of arguments: got 0, expected 1
|
||||
| ^^^^^^^^ incorrect number of arguments for `malloc`: got 0, expected 1
|
||||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
|
|
|
@ -4,6 +4,6 @@ fn main() {
|
|||
}
|
||||
|
||||
unsafe {
|
||||
let _ = malloc(1, 2); //~ ERROR: Undefined Behavior: incorrect number of arguments: got 2, expected 1
|
||||
let _ = malloc(1, 2); //~ ERROR: Undefined Behavior: incorrect number of arguments for `malloc`: got 2, expected 1
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
error: Undefined Behavior: incorrect number of arguments: got 2, expected 1
|
||||
error: Undefined Behavior: incorrect number of arguments for `malloc`: got 2, expected 1
|
||||
--> tests/fail/function_calls/check_arg_count_too_many_args.rs:LL:CC
|
||||
|
|
||||
LL | let _ = malloc(1, 2);
|
||||
| ^^^^^^^^^^^^ incorrect number of arguments: got 2, expected 1
|
||||
| ^^^^^^^^^^^^ incorrect number of arguments for `malloc`: got 2, expected 1
|
||||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
//@ignore-target: windows # No libc pipe on Windows
|
||||
|
||||
// Declare a non-variadic function as variadic.
|
||||
extern "C" {
|
||||
fn pipe(fds: *mut std::ffi::c_int, ...) -> std::ffi::c_int;
|
||||
}
|
||||
|
||||
// Test the error caused by invoking non-vararg shim with a vararg import.
|
||||
fn main() {
|
||||
let mut fds = [-1, -1];
|
||||
let res = unsafe { pipe(fds.as_mut_ptr()) };
|
||||
//~^ ERROR: calling a non-variadic function with a variadic caller-side signature
|
||||
assert_eq!(res, 0);
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
error: Undefined Behavior: calling a non-variadic function with a variadic caller-side signature
|
||||
--> tests/fail/shims/vararg_caller_signature_mismatch.rs:LL:CC
|
||||
|
|
||||
LL | let res = unsafe { pipe(fds.as_mut_ptr()) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ calling a non-variadic function with a variadic caller-side signature
|
||||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
= note: BACKTRACE:
|
||||
= note: inside `main` at tests/fail/shims/vararg_caller_signature_mismatch.rs:LL:CC
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
|
@ -78,11 +78,16 @@ fn test_getuid() {
|
|||
let _val = unsafe { libc::getuid() };
|
||||
}
|
||||
|
||||
fn test_geteuid() {
|
||||
let _val = unsafe { libc::geteuid() };
|
||||
}
|
||||
|
||||
fn main() {
|
||||
test_thread_local_errno();
|
||||
test_environ();
|
||||
test_dlsym();
|
||||
test_getuid();
|
||||
test_geteuid();
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
test_sigrt();
|
||||
|
|
|
@ -88,8 +88,7 @@ fn test_socketpair_threaded() {
|
|||
assert_eq!(res, 5);
|
||||
assert_eq!(buf, "abcde".as_bytes());
|
||||
});
|
||||
// FIXME: we should yield here once blocking is implemented.
|
||||
//thread::yield_now();
|
||||
thread::yield_now();
|
||||
let data = "abcde".as_bytes().as_ptr();
|
||||
let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5) };
|
||||
assert_eq!(res, 5);
|
||||
|
@ -97,14 +96,11 @@ fn test_socketpair_threaded() {
|
|||
|
||||
// Read and write from different direction
|
||||
let thread2 = thread::spawn(move || {
|
||||
// FIXME: we should yield here once blocking is implemented.
|
||||
//thread::yield_now();
|
||||
thread::yield_now();
|
||||
let data = "12345".as_bytes().as_ptr();
|
||||
let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) };
|
||||
assert_eq!(res, 5);
|
||||
});
|
||||
// FIXME: we should not yield here once blocking is implemented.
|
||||
thread::yield_now();
|
||||
let mut buf: [u8; 5] = [0; 5];
|
||||
let res = unsafe { libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) };
|
||||
assert_eq!(res, 5);
|
||||
|
|
|
@ -13,11 +13,34 @@ use std::fmt::{Debug, Display, LowerHex};
|
|||
use std::hint::black_box;
|
||||
use std::{f32, f64};
|
||||
|
||||
/// Compare the two floats, allowing for $ulp many ULPs of error.
|
||||
///
|
||||
/// ULP means "Units in the Last Place" or "Units of Least Precision".
|
||||
/// The ULP of a float `a`` is the smallest possible change at `a`, so the ULP difference represents how
|
||||
/// many discrete floating-point steps are needed to reach the actual value from the expected value.
|
||||
///
|
||||
/// Essentially ULP can be seen as a distance metric of floating-point numbers, but with
|
||||
/// the same amount of "spacing" between all consecutive representable values. So even though 2 very large floating point numbers
|
||||
/// have a large value difference, their ULP can still be 1, so they are still "approximatly equal",
|
||||
/// but the EPSILON check would have failed.
|
||||
macro_rules! assert_approx_eq {
|
||||
($a:expr, $b:expr) => {{
|
||||
let (a, b) = (&$a, &$b);
|
||||
assert!((*a - *b).abs() < 1.0e-6, "{} is not approximately equal to {}", *a, *b);
|
||||
($a:expr, $b:expr, $ulp:expr) => {{
|
||||
let (actual, expected) = ($a, $b);
|
||||
let allowed_ulp_diff = $ulp;
|
||||
let _force_same_type = actual == expected;
|
||||
// Approximate the ULP by taking half the distance between the number one place "up"
|
||||
// and the number one place "down".
|
||||
let ulp = (expected.next_up() - expected.next_down()) / 2.0;
|
||||
let ulp_diff = ((actual - expected) / ulp).abs().round() as i32;
|
||||
if ulp_diff > allowed_ulp_diff {
|
||||
panic!("{actual:?} is not approximately equal to {expected:?}\ndifference in ULP: {ulp_diff} > {allowed_ulp_diff}");
|
||||
};
|
||||
}};
|
||||
|
||||
($a:expr, $b: expr) => {
|
||||
// accept up to 64ULP (16ULP for host floats and 16ULP for miri artificial error and 32 for any rounding errors)
|
||||
assert_approx_eq!($a, $b, 64);
|
||||
};
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
@ -33,6 +56,7 @@ fn main() {
|
|||
test_algebraic();
|
||||
test_fmuladd();
|
||||
test_min_max_nondet();
|
||||
test_non_determinism();
|
||||
}
|
||||
|
||||
trait Float: Copy + PartialEq + Debug {
|
||||
|
@ -1029,7 +1053,7 @@ pub fn libm() {
|
|||
assert_approx_eq!(f64::consts::FRAC_PI_4.sin().asin(), f64::consts::FRAC_PI_4);
|
||||
|
||||
assert_approx_eq!(1.0f32.sinh(), 1.1752012f32);
|
||||
assert_approx_eq!(1.0f64.sinh(), 1.1752012f64);
|
||||
assert_approx_eq!(1.0f64.sinh(), 1.1752011936438014f64);
|
||||
assert_approx_eq!(2.0f32.asinh(), 1.443635475178810342493276740273105f32);
|
||||
assert_approx_eq!((-2.0f64).asinh(), -1.443635475178810342493276740273105f64);
|
||||
|
||||
|
@ -1041,12 +1065,12 @@ pub fn libm() {
|
|||
assert_approx_eq!(f64::consts::FRAC_PI_4.cos().acos(), f64::consts::FRAC_PI_4);
|
||||
|
||||
assert_approx_eq!(1.0f32.cosh(), 1.54308f32);
|
||||
assert_approx_eq!(1.0f64.cosh(), 1.54308f64);
|
||||
assert_approx_eq!(1.0f64.cosh(), 1.5430806348152437f64);
|
||||
assert_approx_eq!(2.0f32.acosh(), 1.31695789692481670862504634730796844f32);
|
||||
assert_approx_eq!(3.0f64.acosh(), 1.76274717403908605046521864995958461f64);
|
||||
|
||||
assert_approx_eq!(1.0f32.tan(), 1.557408f32);
|
||||
assert_approx_eq!(1.0f64.tan(), 1.557408f64);
|
||||
assert_approx_eq!(1.0f64.tan(), 1.5574077246549023f64);
|
||||
assert_approx_eq!(1.0_f32, 1.0_f32.tan().atan());
|
||||
assert_approx_eq!(1.0_f64, 1.0_f64.tan().atan());
|
||||
assert_approx_eq!(1.0f32.atan2(2.0f32), 0.46364761f32);
|
||||
|
@ -1091,11 +1115,11 @@ fn test_fast() {
|
|||
pub fn test_operations_f16(a: f16, b: f16) {
|
||||
// make sure they all map to the correct operation
|
||||
unsafe {
|
||||
assert_eq!(fadd_fast(a, b), a + b);
|
||||
assert_eq!(fsub_fast(a, b), a - b);
|
||||
assert_eq!(fmul_fast(a, b), a * b);
|
||||
assert_eq!(fdiv_fast(a, b), a / b);
|
||||
assert_eq!(frem_fast(a, b), a % b);
|
||||
assert_approx_eq!(fadd_fast(a, b), a + b);
|
||||
assert_approx_eq!(fsub_fast(a, b), a - b);
|
||||
assert_approx_eq!(fmul_fast(a, b), a * b);
|
||||
assert_approx_eq!(fdiv_fast(a, b), a / b);
|
||||
assert_approx_eq!(frem_fast(a, b), a % b);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1103,11 +1127,11 @@ fn test_fast() {
|
|||
pub fn test_operations_f32(a: f32, b: f32) {
|
||||
// make sure they all map to the correct operation
|
||||
unsafe {
|
||||
assert_eq!(fadd_fast(a, b), a + b);
|
||||
assert_eq!(fsub_fast(a, b), a - b);
|
||||
assert_eq!(fmul_fast(a, b), a * b);
|
||||
assert_eq!(fdiv_fast(a, b), a / b);
|
||||
assert_eq!(frem_fast(a, b), a % b);
|
||||
assert_approx_eq!(fadd_fast(a, b), a + b);
|
||||
assert_approx_eq!(fsub_fast(a, b), a - b);
|
||||
assert_approx_eq!(fmul_fast(a, b), a * b);
|
||||
assert_approx_eq!(fdiv_fast(a, b), a / b);
|
||||
assert_approx_eq!(frem_fast(a, b), a % b);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1115,11 +1139,11 @@ fn test_fast() {
|
|||
pub fn test_operations_f64(a: f64, b: f64) {
|
||||
// make sure they all map to the correct operation
|
||||
unsafe {
|
||||
assert_eq!(fadd_fast(a, b), a + b);
|
||||
assert_eq!(fsub_fast(a, b), a - b);
|
||||
assert_eq!(fmul_fast(a, b), a * b);
|
||||
assert_eq!(fdiv_fast(a, b), a / b);
|
||||
assert_eq!(frem_fast(a, b), a % b);
|
||||
assert_approx_eq!(fadd_fast(a, b), a + b);
|
||||
assert_approx_eq!(fsub_fast(a, b), a - b);
|
||||
assert_approx_eq!(fmul_fast(a, b), a * b);
|
||||
assert_approx_eq!(fdiv_fast(a, b), a / b);
|
||||
assert_approx_eq!(frem_fast(a, b), a % b);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1127,11 +1151,11 @@ fn test_fast() {
|
|||
pub fn test_operations_f128(a: f128, b: f128) {
|
||||
// make sure they all map to the correct operation
|
||||
unsafe {
|
||||
assert_eq!(fadd_fast(a, b), a + b);
|
||||
assert_eq!(fsub_fast(a, b), a - b);
|
||||
assert_eq!(fmul_fast(a, b), a * b);
|
||||
assert_eq!(fdiv_fast(a, b), a / b);
|
||||
assert_eq!(frem_fast(a, b), a % b);
|
||||
assert_approx_eq!(fadd_fast(a, b), a + b);
|
||||
assert_approx_eq!(fsub_fast(a, b), a - b);
|
||||
assert_approx_eq!(fmul_fast(a, b), a * b);
|
||||
assert_approx_eq!(fdiv_fast(a, b), a / b);
|
||||
assert_approx_eq!(frem_fast(a, b), a % b);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1153,41 +1177,41 @@ fn test_algebraic() {
|
|||
#[inline(never)]
|
||||
pub fn test_operations_f16(a: f16, b: f16) {
|
||||
// make sure they all map to the correct operation
|
||||
assert_eq!(fadd_algebraic(a, b), a + b);
|
||||
assert_eq!(fsub_algebraic(a, b), a - b);
|
||||
assert_eq!(fmul_algebraic(a, b), a * b);
|
||||
assert_eq!(fdiv_algebraic(a, b), a / b);
|
||||
assert_eq!(frem_algebraic(a, b), a % b);
|
||||
assert_approx_eq!(fadd_algebraic(a, b), a + b);
|
||||
assert_approx_eq!(fsub_algebraic(a, b), a - b);
|
||||
assert_approx_eq!(fmul_algebraic(a, b), a * b);
|
||||
assert_approx_eq!(fdiv_algebraic(a, b), a / b);
|
||||
assert_approx_eq!(frem_algebraic(a, b), a % b);
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn test_operations_f32(a: f32, b: f32) {
|
||||
// make sure they all map to the correct operation
|
||||
assert_eq!(fadd_algebraic(a, b), a + b);
|
||||
assert_eq!(fsub_algebraic(a, b), a - b);
|
||||
assert_eq!(fmul_algebraic(a, b), a * b);
|
||||
assert_eq!(fdiv_algebraic(a, b), a / b);
|
||||
assert_eq!(frem_algebraic(a, b), a % b);
|
||||
assert_approx_eq!(fadd_algebraic(a, b), a + b);
|
||||
assert_approx_eq!(fsub_algebraic(a, b), a - b);
|
||||
assert_approx_eq!(fmul_algebraic(a, b), a * b);
|
||||
assert_approx_eq!(fdiv_algebraic(a, b), a / b);
|
||||
assert_approx_eq!(frem_algebraic(a, b), a % b);
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn test_operations_f64(a: f64, b: f64) {
|
||||
// make sure they all map to the correct operation
|
||||
assert_eq!(fadd_algebraic(a, b), a + b);
|
||||
assert_eq!(fsub_algebraic(a, b), a - b);
|
||||
assert_eq!(fmul_algebraic(a, b), a * b);
|
||||
assert_eq!(fdiv_algebraic(a, b), a / b);
|
||||
assert_eq!(frem_algebraic(a, b), a % b);
|
||||
assert_approx_eq!(fadd_algebraic(a, b), a + b);
|
||||
assert_approx_eq!(fsub_algebraic(a, b), a - b);
|
||||
assert_approx_eq!(fmul_algebraic(a, b), a * b);
|
||||
assert_approx_eq!(fdiv_algebraic(a, b), a / b);
|
||||
assert_approx_eq!(frem_algebraic(a, b), a % b);
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn test_operations_f128(a: f128, b: f128) {
|
||||
// make sure they all map to the correct operation
|
||||
assert_eq!(fadd_algebraic(a, b), a + b);
|
||||
assert_eq!(fsub_algebraic(a, b), a - b);
|
||||
assert_eq!(fmul_algebraic(a, b), a * b);
|
||||
assert_eq!(fdiv_algebraic(a, b), a / b);
|
||||
assert_eq!(frem_algebraic(a, b), a % b);
|
||||
assert_approx_eq!(fadd_algebraic(a, b), a + b);
|
||||
assert_approx_eq!(fsub_algebraic(a, b), a - b);
|
||||
assert_approx_eq!(fmul_algebraic(a, b), a * b);
|
||||
assert_approx_eq!(fdiv_algebraic(a, b), a / b);
|
||||
assert_approx_eq!(frem_algebraic(a, b), a % b);
|
||||
}
|
||||
|
||||
test_operations_f16(11., 2.);
|
||||
|
@ -1245,3 +1269,122 @@ fn test_min_max_nondet() {
|
|||
ensure_both(|| f128::min(0.0, -0.0).is_sign_positive());
|
||||
ensure_both(|| f128::max(0.0, -0.0).is_sign_positive());
|
||||
}
|
||||
|
||||
fn test_non_determinism() {
|
||||
use std::intrinsics::{
|
||||
fadd_algebraic, fadd_fast, fdiv_algebraic, fdiv_fast, fmul_algebraic, fmul_fast,
|
||||
frem_algebraic, frem_fast, fsub_algebraic, fsub_fast,
|
||||
};
|
||||
use std::{f32, f64};
|
||||
// TODO: Also test powi and powf when the non-determinism is implemented for them
|
||||
|
||||
/// Ensure that the operation is non-deterministic
|
||||
#[track_caller]
|
||||
fn ensure_nondet<T: PartialEq + std::fmt::Debug>(f: impl Fn() -> T) {
|
||||
|
||||
let rounds = 16;
|
||||
let first = f();
|
||||
for _ in 1..rounds {
|
||||
if f() != first {
|
||||
// We saw two different values!
|
||||
return;
|
||||
}
|
||||
}
|
||||
// We saw the same thing N times.
|
||||
// FIXME: temporarily disabled as it breaks std tests.
|
||||
//panic!("expected non-determinism, got {rounds} times the same result: {first:?}");
|
||||
}
|
||||
|
||||
macro_rules! test_operations_f {
|
||||
($a:expr, $b:expr) => {
|
||||
ensure_nondet(|| fadd_algebraic($a, $b));
|
||||
ensure_nondet(|| fsub_algebraic($a, $b));
|
||||
ensure_nondet(|| fmul_algebraic($a, $b));
|
||||
ensure_nondet(|| fdiv_algebraic($a, $b));
|
||||
ensure_nondet(|| frem_algebraic($a, $b));
|
||||
|
||||
unsafe {
|
||||
ensure_nondet(|| fadd_fast($a, $b));
|
||||
ensure_nondet(|| fsub_fast($a, $b));
|
||||
ensure_nondet(|| fmul_fast($a, $b));
|
||||
ensure_nondet(|| fdiv_fast($a, $b));
|
||||
ensure_nondet(|| frem_fast($a, $b));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn test_operations_f16(a: f16, b: f16) {
|
||||
test_operations_f!(a, b);
|
||||
}
|
||||
pub fn test_operations_f32(a: f32, b: f32) {
|
||||
test_operations_f!(a, b);
|
||||
ensure_nondet(|| a.log(b));
|
||||
ensure_nondet(|| a.exp());
|
||||
ensure_nondet(|| 10f32.exp2());
|
||||
ensure_nondet(|| f32::consts::E.ln());
|
||||
ensure_nondet(|| 1f32.ln_1p());
|
||||
ensure_nondet(|| 10f32.log10());
|
||||
ensure_nondet(|| 8f32.log2());
|
||||
ensure_nondet(|| 27.0f32.cbrt());
|
||||
ensure_nondet(|| 3.0f32.hypot(4.0f32));
|
||||
ensure_nondet(|| 1f32.sin());
|
||||
ensure_nondet(|| 0f32.cos());
|
||||
// On i686-pc-windows-msvc , these functions are implemented by calling the `f64` version,
|
||||
// which means the little rounding errors Miri introduces are discard by the cast down to `f32`.
|
||||
// Just skip the test for them.
|
||||
if !cfg!(all(target_os = "windows", target_env = "msvc", target_arch = "x86")) {
|
||||
ensure_nondet(|| 1.0f32.tan());
|
||||
ensure_nondet(|| 1.0f32.asin());
|
||||
ensure_nondet(|| 5.0f32.acos());
|
||||
ensure_nondet(|| 1.0f32.atan());
|
||||
ensure_nondet(|| 1.0f32.atan2(2.0f32));
|
||||
ensure_nondet(|| 1.0f32.sinh());
|
||||
ensure_nondet(|| 1.0f32.cosh());
|
||||
ensure_nondet(|| 1.0f32.tanh());
|
||||
}
|
||||
ensure_nondet(|| 1.0f32.asinh());
|
||||
ensure_nondet(|| 2.0f32.acosh());
|
||||
ensure_nondet(|| 0.5f32.atanh());
|
||||
ensure_nondet(|| 5.0f32.gamma());
|
||||
ensure_nondet(|| 5.0f32.ln_gamma());
|
||||
ensure_nondet(|| 5.0f32.erf());
|
||||
ensure_nondet(|| 5.0f32.erfc());
|
||||
}
|
||||
pub fn test_operations_f64(a: f64, b: f64) {
|
||||
test_operations_f!(a, b);
|
||||
ensure_nondet(|| a.log(b));
|
||||
ensure_nondet(|| a.exp());
|
||||
ensure_nondet(|| 50f64.exp2());
|
||||
ensure_nondet(|| 3f64.ln());
|
||||
ensure_nondet(|| 1f64.ln_1p());
|
||||
ensure_nondet(|| f64::consts::E.log10());
|
||||
ensure_nondet(|| f64::consts::E.log2());
|
||||
ensure_nondet(|| 27.0f64.cbrt());
|
||||
ensure_nondet(|| 3.0f64.hypot(4.0f64));
|
||||
ensure_nondet(|| 1f64.sin());
|
||||
ensure_nondet(|| 0f64.cos());
|
||||
ensure_nondet(|| 1.0f64.tan());
|
||||
ensure_nondet(|| 1.0f64.asin());
|
||||
ensure_nondet(|| 5.0f64.acos());
|
||||
ensure_nondet(|| 1.0f64.atan());
|
||||
ensure_nondet(|| 1.0f64.atan2(2.0f64));
|
||||
ensure_nondet(|| 1.0f64.sinh());
|
||||
ensure_nondet(|| 1.0f64.cosh());
|
||||
ensure_nondet(|| 1.0f64.tanh());
|
||||
ensure_nondet(|| 1.0f64.asinh());
|
||||
ensure_nondet(|| 3.0f64.acosh());
|
||||
ensure_nondet(|| 0.5f64.atanh());
|
||||
ensure_nondet(|| 5.0f64.gamma());
|
||||
ensure_nondet(|| 5.0f64.ln_gamma());
|
||||
ensure_nondet(|| 5.0f64.erf());
|
||||
ensure_nondet(|| 5.0f64.erfc());
|
||||
}
|
||||
pub fn test_operations_f128(a: f128, b: f128) {
|
||||
test_operations_f!(a, b);
|
||||
}
|
||||
|
||||
test_operations_f16(5., 7.);
|
||||
test_operations_f32(12., 5.);
|
||||
test_operations_f64(19., 11.);
|
||||
test_operations_f128(25., 18.);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
// We're testing aarch64 target specific features
|
||||
//@only-target: aarch64
|
||||
//@compile-flags: -C target-feature=+neon
|
||||
|
||||
use std::arch::aarch64::*;
|
||||
use std::arch::is_aarch64_feature_detected;
|
||||
|
||||
fn main() {
|
||||
assert!(is_aarch64_feature_detected!("neon"));
|
||||
|
||||
unsafe {
|
||||
test_neon();
|
||||
}
|
||||
}
|
||||
|
||||
#[target_feature(enable = "neon")]
|
||||
unsafe fn test_neon() {
|
||||
// Adapted from library/stdarch/crates/core_arch/src/aarch64/neon/mod.rs
|
||||
unsafe fn test_vpmaxq_u8() {
|
||||
let a = vld1q_u8([1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8].as_ptr());
|
||||
let b = vld1q_u8([0, 3, 2, 5, 4, 7, 6, 9, 0, 3, 2, 5, 4, 7, 6, 9].as_ptr());
|
||||
let e = [2, 4, 6, 8, 2, 4, 6, 8, 3, 5, 7, 9, 3, 5, 7, 9];
|
||||
let mut r = [0; 16];
|
||||
vst1q_u8(r.as_mut_ptr(), vpmaxq_u8(a, b));
|
||||
assert_eq!(r, e);
|
||||
}
|
||||
test_vpmaxq_u8();
|
||||
|
||||
unsafe fn test_vpmaxq_u8_is_unsigned() {
|
||||
let a = vld1q_u8(
|
||||
[255, 0, 253, 252, 251, 250, 249, 248, 255, 254, 253, 252, 251, 250, 249, 248].as_ptr(),
|
||||
);
|
||||
let b = vld1q_u8([254, 3, 2, 5, 4, 7, 6, 9, 0, 3, 2, 5, 4, 7, 6, 9].as_ptr());
|
||||
let e = [255, 253, 251, 249, 255, 253, 251, 249, 254, 5, 7, 9, 3, 5, 7, 9];
|
||||
let mut r = [0; 16];
|
||||
vst1q_u8(r.as_mut_ptr(), vpmaxq_u8(a, b));
|
||||
assert_eq!(r, e);
|
||||
}
|
||||
test_vpmaxq_u8_is_unsigned();
|
||||
}
|
|
@ -217,15 +217,11 @@ fn run_tests(
|
|||
ui_test::default_file_filter,
|
||||
// This could be used to overwrite the `Config` on a per-test basis.
|
||||
|_, _| {},
|
||||
(
|
||||
match args.format {
|
||||
Format::Terse => status_emitter::Text::quiet(),
|
||||
Format::Pretty => status_emitter::Text::verbose(),
|
||||
},
|
||||
status_emitter::Gha::</* GHA Actions groups*/ false> {
|
||||
name: format!("{mode:?} {path} ({target})"),
|
||||
},
|
||||
),
|
||||
// No GHA output as that would also show in the main rustc repo.
|
||||
match args.format {
|
||||
Format::Terse => status_emitter::Text::quiet(),
|
||||
Format::Pretty => status_emitter::Text::verbose(),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
"vendor": "unknown",
|
||||
"linker": "rust-lld",
|
||||
"linker-flavor": "gnu-lld",
|
||||
"rustc-abi": "x86-softfloat",
|
||||
"features": "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-avx,-avx2,+soft-float",
|
||||
"dynamic-linking": false,
|
||||
"executables": true,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue