Auto merge of #148446 - GuillaumeGomez:rollup-lxwlqol, r=GuillaumeGomez
Rollup of 8 pull requests Successful merges: - rust-lang/rust#135099 (Add FileCheck annotations to mir-opt/copy-prop) - rust-lang/rust#145903 (Give correct suggestion for a typo in raw pointers) - rust-lang/rust#147520 (Port the remaining SIMD intrinsics to const-eval) - rust-lang/rust#148068 (rustdoc: Use configured target modifiers when collecting doctests) - rust-lang/rust#148099 (Prepare to move debugger discovery from compiletest to bootstrap) - rust-lang/rust#148268 (rustdoc: fix `--emit=dep-info` on scraped examples) - rust-lang/rust#148306 (Remove double check when decoding ExpnId to avoid races) - rust-lang/rust#148378 (Fix documentation for std::panic::update_hook) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
1f880d9a1f
50 changed files with 1334 additions and 370 deletions
|
|
@ -25,6 +25,35 @@ use super::{
|
|||
};
|
||||
use crate::fluent_generated as fluent;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
enum MulAddType {
|
||||
/// Used with `fma` and `simd_fma`, always uses fused-multiply-add
|
||||
Fused,
|
||||
/// Used with `fmuladd` and `simd_relaxed_fma`, nondeterministically determines whether to use
|
||||
/// fma or simple multiply-add
|
||||
Nondeterministic,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub(crate) enum MinMax {
|
||||
/// The IEEE `Minimum` operation - see `f32::minimum` etc
|
||||
/// In particular, `-0.0` is considered smaller than `+0.0` and
|
||||
/// if either input is NaN, the result is NaN.
|
||||
Minimum,
|
||||
/// The IEEE `MinNum` operation - see `f32::min` etc
|
||||
/// In particular, if the inputs are `-0.0` and `+0.0`, the result is non-deterministic,
|
||||
/// and is one argument is NaN, the other one is returned.
|
||||
MinNum,
|
||||
/// The IEEE `Maximum` operation - see `f32::maximum` etc
|
||||
/// In particular, `-0.0` is considered smaller than `+0.0` and
|
||||
/// if either input is NaN, the result is NaN.
|
||||
Maximum,
|
||||
/// The IEEE `MaxNum` operation - see `f32::max` etc
|
||||
/// In particular, if the inputs are `-0.0` and `+0.0`, the result is non-deterministic,
|
||||
/// and is one argument is NaN, the other one is returned.
|
||||
MaxNum,
|
||||
}
|
||||
|
||||
/// Directly returns an `Allocation` containing an absolute path representation of the given type.
|
||||
pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> (AllocId, u64) {
|
||||
let path = crate::util::type_name(tcx, ty);
|
||||
|
|
@ -504,25 +533,33 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
self.write_scalar(Scalar::from_target_usize(align.bytes(), self), dest)?;
|
||||
}
|
||||
|
||||
sym::minnumf16 => self.float_min_intrinsic::<Half>(args, dest)?,
|
||||
sym::minnumf32 => self.float_min_intrinsic::<Single>(args, dest)?,
|
||||
sym::minnumf64 => self.float_min_intrinsic::<Double>(args, dest)?,
|
||||
sym::minnumf128 => self.float_min_intrinsic::<Quad>(args, dest)?,
|
||||
sym::minnumf16 => self.float_minmax_intrinsic::<Half>(args, MinMax::MinNum, dest)?,
|
||||
sym::minnumf32 => self.float_minmax_intrinsic::<Single>(args, MinMax::MinNum, dest)?,
|
||||
sym::minnumf64 => self.float_minmax_intrinsic::<Double>(args, MinMax::MinNum, dest)?,
|
||||
sym::minnumf128 => self.float_minmax_intrinsic::<Quad>(args, MinMax::MinNum, dest)?,
|
||||
|
||||
sym::minimumf16 => self.float_minimum_intrinsic::<Half>(args, dest)?,
|
||||
sym::minimumf32 => self.float_minimum_intrinsic::<Single>(args, dest)?,
|
||||
sym::minimumf64 => self.float_minimum_intrinsic::<Double>(args, dest)?,
|
||||
sym::minimumf128 => self.float_minimum_intrinsic::<Quad>(args, dest)?,
|
||||
sym::minimumf16 => self.float_minmax_intrinsic::<Half>(args, MinMax::Minimum, dest)?,
|
||||
sym::minimumf32 => {
|
||||
self.float_minmax_intrinsic::<Single>(args, MinMax::Minimum, dest)?
|
||||
}
|
||||
sym::minimumf64 => {
|
||||
self.float_minmax_intrinsic::<Double>(args, MinMax::Minimum, dest)?
|
||||
}
|
||||
sym::minimumf128 => self.float_minmax_intrinsic::<Quad>(args, MinMax::Minimum, dest)?,
|
||||
|
||||
sym::maxnumf16 => self.float_max_intrinsic::<Half>(args, dest)?,
|
||||
sym::maxnumf32 => self.float_max_intrinsic::<Single>(args, dest)?,
|
||||
sym::maxnumf64 => self.float_max_intrinsic::<Double>(args, dest)?,
|
||||
sym::maxnumf128 => self.float_max_intrinsic::<Quad>(args, dest)?,
|
||||
sym::maxnumf16 => self.float_minmax_intrinsic::<Half>(args, MinMax::MaxNum, dest)?,
|
||||
sym::maxnumf32 => self.float_minmax_intrinsic::<Single>(args, MinMax::MaxNum, dest)?,
|
||||
sym::maxnumf64 => self.float_minmax_intrinsic::<Double>(args, MinMax::MaxNum, dest)?,
|
||||
sym::maxnumf128 => self.float_minmax_intrinsic::<Quad>(args, MinMax::MaxNum, dest)?,
|
||||
|
||||
sym::maximumf16 => self.float_maximum_intrinsic::<Half>(args, dest)?,
|
||||
sym::maximumf32 => self.float_maximum_intrinsic::<Single>(args, dest)?,
|
||||
sym::maximumf64 => self.float_maximum_intrinsic::<Double>(args, dest)?,
|
||||
sym::maximumf128 => self.float_maximum_intrinsic::<Quad>(args, dest)?,
|
||||
sym::maximumf16 => self.float_minmax_intrinsic::<Half>(args, MinMax::Maximum, dest)?,
|
||||
sym::maximumf32 => {
|
||||
self.float_minmax_intrinsic::<Single>(args, MinMax::Maximum, dest)?
|
||||
}
|
||||
sym::maximumf64 => {
|
||||
self.float_minmax_intrinsic::<Double>(args, MinMax::Maximum, dest)?
|
||||
}
|
||||
sym::maximumf128 => self.float_minmax_intrinsic::<Quad>(args, MinMax::Maximum, dest)?,
|
||||
|
||||
sym::copysignf16 => self.float_copysign_intrinsic::<Half>(args, dest)?,
|
||||
sym::copysignf32 => self.float_copysign_intrinsic::<Single>(args, dest)?,
|
||||
|
|
@ -630,14 +667,22 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
dest,
|
||||
rustc_apfloat::Round::NearestTiesToEven,
|
||||
)?,
|
||||
sym::fmaf16 => self.fma_intrinsic::<Half>(args, dest)?,
|
||||
sym::fmaf32 => self.fma_intrinsic::<Single>(args, dest)?,
|
||||
sym::fmaf64 => self.fma_intrinsic::<Double>(args, dest)?,
|
||||
sym::fmaf128 => self.fma_intrinsic::<Quad>(args, dest)?,
|
||||
sym::fmuladdf16 => self.float_muladd_intrinsic::<Half>(args, dest)?,
|
||||
sym::fmuladdf32 => self.float_muladd_intrinsic::<Single>(args, dest)?,
|
||||
sym::fmuladdf64 => self.float_muladd_intrinsic::<Double>(args, dest)?,
|
||||
sym::fmuladdf128 => self.float_muladd_intrinsic::<Quad>(args, dest)?,
|
||||
sym::fmaf16 => self.float_muladd_intrinsic::<Half>(args, dest, MulAddType::Fused)?,
|
||||
sym::fmaf32 => self.float_muladd_intrinsic::<Single>(args, dest, MulAddType::Fused)?,
|
||||
sym::fmaf64 => self.float_muladd_intrinsic::<Double>(args, dest, MulAddType::Fused)?,
|
||||
sym::fmaf128 => self.float_muladd_intrinsic::<Quad>(args, dest, MulAddType::Fused)?,
|
||||
sym::fmuladdf16 => {
|
||||
self.float_muladd_intrinsic::<Half>(args, dest, MulAddType::Nondeterministic)?
|
||||
}
|
||||
sym::fmuladdf32 => {
|
||||
self.float_muladd_intrinsic::<Single>(args, dest, MulAddType::Nondeterministic)?
|
||||
}
|
||||
sym::fmuladdf64 => {
|
||||
self.float_muladd_intrinsic::<Double>(args, dest, MulAddType::Nondeterministic)?
|
||||
}
|
||||
sym::fmuladdf128 => {
|
||||
self.float_muladd_intrinsic::<Quad>(args, dest, MulAddType::Nondeterministic)?
|
||||
}
|
||||
|
||||
// Unsupported intrinsic: skip the return_to_block below.
|
||||
_ => return interp_ok(false),
|
||||
|
|
@ -919,76 +964,45 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
interp_ok(Scalar::from_bool(lhs_bytes == rhs_bytes))
|
||||
}
|
||||
|
||||
fn float_min_intrinsic<F>(
|
||||
&mut self,
|
||||
args: &[OpTy<'tcx, M::Provenance>],
|
||||
dest: &PlaceTy<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, ()>
|
||||
fn float_minmax<F>(
|
||||
&self,
|
||||
a: Scalar<M::Provenance>,
|
||||
b: Scalar<M::Provenance>,
|
||||
op: MinMax,
|
||||
) -> InterpResult<'tcx, Scalar<M::Provenance>>
|
||||
where
|
||||
F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>,
|
||||
{
|
||||
let a: F = self.read_scalar(&args[0])?.to_float()?;
|
||||
let b: F = self.read_scalar(&args[1])?.to_float()?;
|
||||
let res = if a == b {
|
||||
let a: F = a.to_float()?;
|
||||
let b: F = b.to_float()?;
|
||||
let res = if matches!(op, MinMax::MinNum | MinMax::MaxNum) && a == b {
|
||||
// They are definitely not NaN (those are never equal), but they could be `+0` and `-0`.
|
||||
// Let the machine decide which one to return.
|
||||
M::equal_float_min_max(self, a, b)
|
||||
} else {
|
||||
self.adjust_nan(a.min(b), &[a, b])
|
||||
let result = match op {
|
||||
MinMax::Minimum => a.minimum(b),
|
||||
MinMax::MinNum => a.min(b),
|
||||
MinMax::Maximum => a.maximum(b),
|
||||
MinMax::MaxNum => a.max(b),
|
||||
};
|
||||
self.adjust_nan(result, &[a, b])
|
||||
};
|
||||
self.write_scalar(res, dest)?;
|
||||
interp_ok(())
|
||||
|
||||
interp_ok(res.into())
|
||||
}
|
||||
|
||||
fn float_max_intrinsic<F>(
|
||||
fn float_minmax_intrinsic<F>(
|
||||
&mut self,
|
||||
args: &[OpTy<'tcx, M::Provenance>],
|
||||
op: MinMax,
|
||||
dest: &PlaceTy<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, ()>
|
||||
where
|
||||
F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>,
|
||||
{
|
||||
let a: F = self.read_scalar(&args[0])?.to_float()?;
|
||||
let b: F = self.read_scalar(&args[1])?.to_float()?;
|
||||
let res = if a == b {
|
||||
// They are definitely not NaN (those are never equal), but they could be `+0` and `-0`.
|
||||
// Let the machine decide which one to return.
|
||||
M::equal_float_min_max(self, a, b)
|
||||
} else {
|
||||
self.adjust_nan(a.max(b), &[a, b])
|
||||
};
|
||||
self.write_scalar(res, dest)?;
|
||||
interp_ok(())
|
||||
}
|
||||
|
||||
fn float_minimum_intrinsic<F>(
|
||||
&mut self,
|
||||
args: &[OpTy<'tcx, M::Provenance>],
|
||||
dest: &PlaceTy<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, ()>
|
||||
where
|
||||
F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>,
|
||||
{
|
||||
let a: F = self.read_scalar(&args[0])?.to_float()?;
|
||||
let b: F = self.read_scalar(&args[1])?.to_float()?;
|
||||
let res = a.minimum(b);
|
||||
let res = self.adjust_nan(res, &[a, b]);
|
||||
self.write_scalar(res, dest)?;
|
||||
interp_ok(())
|
||||
}
|
||||
|
||||
fn float_maximum_intrinsic<F>(
|
||||
&mut self,
|
||||
args: &[OpTy<'tcx, M::Provenance>],
|
||||
dest: &PlaceTy<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, ()>
|
||||
where
|
||||
F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>,
|
||||
{
|
||||
let a: F = self.read_scalar(&args[0])?.to_float()?;
|
||||
let b: F = self.read_scalar(&args[1])?.to_float()?;
|
||||
let res = a.maximum(b);
|
||||
let res = self.adjust_nan(res, &[a, b]);
|
||||
let res =
|
||||
self.float_minmax::<F>(self.read_scalar(&args[0])?, self.read_scalar(&args[1])?, op)?;
|
||||
self.write_scalar(res, dest)?;
|
||||
interp_ok(())
|
||||
}
|
||||
|
|
@ -1022,6 +1036,20 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
interp_ok(())
|
||||
}
|
||||
|
||||
fn float_round<F>(
|
||||
&mut self,
|
||||
x: Scalar<M::Provenance>,
|
||||
mode: rustc_apfloat::Round,
|
||||
) -> InterpResult<'tcx, Scalar<M::Provenance>>
|
||||
where
|
||||
F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>,
|
||||
{
|
||||
let x: F = x.to_float()?;
|
||||
let res = x.round_to_integral(mode).value;
|
||||
let res = self.adjust_nan(res, &[x]);
|
||||
interp_ok(res.into())
|
||||
}
|
||||
|
||||
fn float_round_intrinsic<F>(
|
||||
&mut self,
|
||||
args: &[OpTy<'tcx, M::Provenance>],
|
||||
|
|
@ -1031,47 +1059,46 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
where
|
||||
F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>,
|
||||
{
|
||||
let x: F = self.read_scalar(&args[0])?.to_float()?;
|
||||
let res = x.round_to_integral(mode).value;
|
||||
let res = self.adjust_nan(res, &[x]);
|
||||
let res = self.float_round::<F>(self.read_scalar(&args[0])?, mode)?;
|
||||
self.write_scalar(res, dest)?;
|
||||
interp_ok(())
|
||||
}
|
||||
|
||||
fn fma_intrinsic<F>(
|
||||
&mut self,
|
||||
args: &[OpTy<'tcx, M::Provenance>],
|
||||
dest: &PlaceTy<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, ()>
|
||||
fn float_muladd<F>(
|
||||
&self,
|
||||
a: Scalar<M::Provenance>,
|
||||
b: Scalar<M::Provenance>,
|
||||
c: Scalar<M::Provenance>,
|
||||
typ: MulAddType,
|
||||
) -> InterpResult<'tcx, Scalar<M::Provenance>>
|
||||
where
|
||||
F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>,
|
||||
{
|
||||
let a: F = self.read_scalar(&args[0])?.to_float()?;
|
||||
let b: F = self.read_scalar(&args[1])?.to_float()?;
|
||||
let c: F = self.read_scalar(&args[2])?.to_float()?;
|
||||
let a: F = a.to_float()?;
|
||||
let b: F = b.to_float()?;
|
||||
let c: F = c.to_float()?;
|
||||
|
||||
let res = a.mul_add(b, c).value;
|
||||
let fuse = typ == MulAddType::Fused || M::float_fuse_mul_add(self);
|
||||
|
||||
let res = if fuse { a.mul_add(b, c).value } else { ((a * b).value + c).value };
|
||||
let res = self.adjust_nan(res, &[a, b, c]);
|
||||
self.write_scalar(res, dest)?;
|
||||
interp_ok(())
|
||||
interp_ok(res.into())
|
||||
}
|
||||
|
||||
fn float_muladd_intrinsic<F>(
|
||||
&mut self,
|
||||
args: &[OpTy<'tcx, M::Provenance>],
|
||||
dest: &PlaceTy<'tcx, M::Provenance>,
|
||||
typ: MulAddType,
|
||||
) -> InterpResult<'tcx, ()>
|
||||
where
|
||||
F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>,
|
||||
{
|
||||
let a: F = self.read_scalar(&args[0])?.to_float()?;
|
||||
let b: F = self.read_scalar(&args[1])?.to_float()?;
|
||||
let c: F = self.read_scalar(&args[2])?.to_float()?;
|
||||
let a = self.read_scalar(&args[0])?;
|
||||
let b = self.read_scalar(&args[1])?;
|
||||
let c = self.read_scalar(&args[2])?;
|
||||
|
||||
let fuse = M::float_fuse_mul_add(self);
|
||||
|
||||
let res = if fuse { a.mul_add(b, c).value } else { ((a * b).value + c).value };
|
||||
let res = self.adjust_nan(res, &[a, b, c]);
|
||||
let res = self.float_muladd::<F>(a, b, c, typ)?;
|
||||
self.write_scalar(res, dest)?;
|
||||
interp_ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use either::Either;
|
||||
use rustc_abi::Endian;
|
||||
use rustc_apfloat::ieee::{Double, Half, Quad, Single};
|
||||
use rustc_apfloat::{Float, Round};
|
||||
use rustc_middle::mir::interpret::{InterpErrorKind, UndefinedBehaviorInfo};
|
||||
use rustc_middle::ty::FloatTy;
|
||||
|
|
@ -8,17 +9,11 @@ use rustc_span::{Symbol, sym};
|
|||
use tracing::trace;
|
||||
|
||||
use super::{
|
||||
ImmTy, InterpCx, InterpResult, Machine, OpTy, PlaceTy, Provenance, Scalar, Size, interp_ok,
|
||||
throw_ub_format,
|
||||
ImmTy, InterpCx, InterpResult, Machine, MinMax, MulAddType, OpTy, PlaceTy, Provenance, Scalar,
|
||||
Size, interp_ok, throw_ub_format,
|
||||
};
|
||||
use crate::interpret::Writeable;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub(crate) enum MinMax {
|
||||
Min,
|
||||
Max,
|
||||
}
|
||||
|
||||
impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||
/// Returns `true` if emulation happened.
|
||||
/// Here we implement the intrinsics that are common to all CTFE instances; individual machines can add their own
|
||||
|
|
@ -125,10 +120,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
let op = op.to_scalar();
|
||||
// "Bitwise" operation, no NaN adjustments
|
||||
match float_ty {
|
||||
FloatTy::F16 => unimplemented!("f16_f128"),
|
||||
FloatTy::F16 => Scalar::from_f16(op.to_f16()?.abs()),
|
||||
FloatTy::F32 => Scalar::from_f32(op.to_f32()?.abs()),
|
||||
FloatTy::F64 => Scalar::from_f64(op.to_f64()?.abs()),
|
||||
FloatTy::F128 => unimplemented!("f16_f128"),
|
||||
FloatTy::F128 => Scalar::from_f128(op.to_f128()?.abs()),
|
||||
}
|
||||
}
|
||||
Op::Round(rounding) => {
|
||||
|
|
@ -139,21 +134,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
intrinsic_name
|
||||
)
|
||||
};
|
||||
let op = op.to_scalar();
|
||||
match float_ty {
|
||||
FloatTy::F16 => unimplemented!("f16_f128"),
|
||||
FloatTy::F32 => {
|
||||
let f = op.to_scalar().to_f32()?;
|
||||
let res = f.round_to_integral(rounding).value;
|
||||
let res = self.adjust_nan(res, &[f]);
|
||||
Scalar::from_f32(res)
|
||||
}
|
||||
FloatTy::F64 => {
|
||||
let f = op.to_scalar().to_f64()?;
|
||||
let res = f.round_to_integral(rounding).value;
|
||||
let res = self.adjust_nan(res, &[f]);
|
||||
Scalar::from_f64(res)
|
||||
}
|
||||
FloatTy::F128 => unimplemented!("f16_f128"),
|
||||
FloatTy::F16 => self.float_round::<Half>(op, rounding)?,
|
||||
FloatTy::F32 => self.float_round::<Single>(op, rounding)?,
|
||||
FloatTy::F64 => self.float_round::<Double>(op, rounding)?,
|
||||
FloatTy::F128 => self.float_round::<Quad>(op, rounding)?,
|
||||
}
|
||||
}
|
||||
Op::Numeric(name) => {
|
||||
|
|
@ -216,8 +202,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
sym::simd_le => Op::MirOp(BinOp::Le),
|
||||
sym::simd_gt => Op::MirOp(BinOp::Gt),
|
||||
sym::simd_ge => Op::MirOp(BinOp::Ge),
|
||||
sym::simd_fmax => Op::FMinMax(MinMax::Max),
|
||||
sym::simd_fmin => Op::FMinMax(MinMax::Min),
|
||||
sym::simd_fmax => Op::FMinMax(MinMax::MaxNum),
|
||||
sym::simd_fmin => Op::FMinMax(MinMax::MinNum),
|
||||
sym::simd_saturating_add => Op::SaturatingOp(BinOp::Add),
|
||||
sym::simd_saturating_sub => Op::SaturatingOp(BinOp::Sub),
|
||||
sym::simd_arith_offset => Op::WrappingOffset,
|
||||
|
|
@ -309,8 +295,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
sym::simd_reduce_xor => Op::MirOp(BinOp::BitXor),
|
||||
sym::simd_reduce_any => Op::MirOpBool(BinOp::BitOr),
|
||||
sym::simd_reduce_all => Op::MirOpBool(BinOp::BitAnd),
|
||||
sym::simd_reduce_max => Op::MinMax(MinMax::Max),
|
||||
sym::simd_reduce_min => Op::MinMax(MinMax::Min),
|
||||
sym::simd_reduce_max => Op::MinMax(MinMax::MaxNum),
|
||||
sym::simd_reduce_min => Op::MinMax(MinMax::MinNum),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
|
|
@ -332,10 +318,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
if matches!(res.layout.ty.kind(), ty::Float(_)) {
|
||||
ImmTy::from_scalar(self.fminmax_op(mmop, &res, &op)?, res.layout)
|
||||
} else {
|
||||
// Just boring integers, so NaNs to worry about
|
||||
// Just boring integers, no NaNs to worry about.
|
||||
let mirop = match mmop {
|
||||
MinMax::Min => BinOp::Le,
|
||||
MinMax::Max => BinOp::Ge,
|
||||
MinMax::MinNum | MinMax::Minimum => BinOp::Le,
|
||||
MinMax::MaxNum | MinMax::Maximum => BinOp::Ge,
|
||||
};
|
||||
if self.binary_op(mirop, &res, &op)?.to_scalar().to_bool()? {
|
||||
res
|
||||
|
|
@ -701,6 +687,43 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
};
|
||||
}
|
||||
}
|
||||
sym::simd_fma | sym::simd_relaxed_fma => {
|
||||
// `simd_fma` should always deterministically use `mul_add`, whereas `relaxed_fma`
|
||||
// is non-deterministic, and can use either `mul_add` or `a * b + c`
|
||||
let typ = match intrinsic_name {
|
||||
sym::simd_fma => MulAddType::Fused,
|
||||
sym::simd_relaxed_fma => MulAddType::Nondeterministic,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let (a, a_len) = self.project_to_simd(&args[0])?;
|
||||
let (b, b_len) = self.project_to_simd(&args[1])?;
|
||||
let (c, c_len) = self.project_to_simd(&args[2])?;
|
||||
let (dest, dest_len) = self.project_to_simd(&dest)?;
|
||||
|
||||
assert_eq!(dest_len, a_len);
|
||||
assert_eq!(dest_len, b_len);
|
||||
assert_eq!(dest_len, c_len);
|
||||
|
||||
for i in 0..dest_len {
|
||||
let a = self.read_scalar(&self.project_index(&a, i)?)?;
|
||||
let b = self.read_scalar(&self.project_index(&b, i)?)?;
|
||||
let c = self.read_scalar(&self.project_index(&c, i)?)?;
|
||||
let dest = self.project_index(&dest, i)?;
|
||||
|
||||
let ty::Float(float_ty) = dest.layout.ty.kind() else {
|
||||
span_bug!(self.cur_span(), "{} operand is not a float", intrinsic_name)
|
||||
};
|
||||
|
||||
let val = match float_ty {
|
||||
FloatTy::F16 => self.float_muladd::<Half>(a, b, c, typ)?,
|
||||
FloatTy::F32 => self.float_muladd::<Single>(a, b, c, typ)?,
|
||||
FloatTy::F64 => self.float_muladd::<Double>(a, b, c, typ)?,
|
||||
FloatTy::F128 => self.float_muladd::<Quad>(a, b, c, typ)?,
|
||||
};
|
||||
self.write_scalar(val, &dest)?;
|
||||
}
|
||||
}
|
||||
|
||||
// Unsupported intrinsic: skip the return_to_block below.
|
||||
_ => return interp_ok(false),
|
||||
|
|
@ -711,12 +734,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
interp_ok(true)
|
||||
}
|
||||
|
||||
fn fminmax_op<Prov: Provenance>(
|
||||
fn fminmax_op(
|
||||
&self,
|
||||
op: MinMax,
|
||||
left: &ImmTy<'tcx, Prov>,
|
||||
right: &ImmTy<'tcx, Prov>,
|
||||
) -> InterpResult<'tcx, Scalar<Prov>> {
|
||||
left: &ImmTy<'tcx, M::Provenance>,
|
||||
right: &ImmTy<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, Scalar<M::Provenance>> {
|
||||
assert_eq!(left.layout.ty, right.layout.ty);
|
||||
let ty::Float(float_ty) = left.layout.ty.kind() else {
|
||||
bug!("fmax operand is not a float")
|
||||
|
|
@ -724,28 +747,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
let left = left.to_scalar();
|
||||
let right = right.to_scalar();
|
||||
interp_ok(match float_ty {
|
||||
FloatTy::F16 => unimplemented!("f16_f128"),
|
||||
FloatTy::F32 => {
|
||||
let left = left.to_f32()?;
|
||||
let right = right.to_f32()?;
|
||||
let res = match op {
|
||||
MinMax::Min => left.min(right),
|
||||
MinMax::Max => left.max(right),
|
||||
};
|
||||
let res = self.adjust_nan(res, &[left, right]);
|
||||
Scalar::from_f32(res)
|
||||
}
|
||||
FloatTy::F64 => {
|
||||
let left = left.to_f64()?;
|
||||
let right = right.to_f64()?;
|
||||
let res = match op {
|
||||
MinMax::Min => left.min(right),
|
||||
MinMax::Max => left.max(right),
|
||||
};
|
||||
let res = self.adjust_nan(res, &[left, right]);
|
||||
Scalar::from_f64(res)
|
||||
}
|
||||
FloatTy::F128 => unimplemented!("f16_f128"),
|
||||
FloatTy::F16 => self.float_minmax::<Half>(left, right, op)?,
|
||||
FloatTy::F32 => self.float_minmax::<Single>(left, right, op)?,
|
||||
FloatTy::F64 => self.float_minmax::<Double>(left, right, op)?,
|
||||
FloatTy::F128 => self.float_minmax::<Quad>(left, right, op)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -290,7 +290,7 @@ pub trait Machine<'tcx>: Sized {
|
|||
}
|
||||
|
||||
/// Determines whether the `fmuladd` intrinsics fuse the multiply-add or use separate operations.
|
||||
fn float_fuse_mul_add(_ecx: &mut InterpCx<'tcx, Self>) -> bool;
|
||||
fn float_fuse_mul_add(_ecx: &InterpCx<'tcx, Self>) -> bool;
|
||||
|
||||
/// Called before a basic block terminator is executed.
|
||||
#[inline]
|
||||
|
|
@ -676,7 +676,7 @@ pub macro compile_time_machine(<$tcx: lifetime>) {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn float_fuse_mul_add(_ecx: &mut InterpCx<$tcx, Self>) -> bool {
|
||||
fn float_fuse_mul_add(_ecx: &InterpCx<$tcx, Self>) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1560,7 +1560,6 @@ impl<'a> CrateMetadataRef<'a> {
|
|||
}
|
||||
|
||||
fn expn_hash_to_expn_id(self, sess: &Session, index_guess: u32, hash: ExpnHash) -> ExpnId {
|
||||
debug_assert_eq!(ExpnId::from_hash(hash), None);
|
||||
let index_guess = ExpnIndex::from_u32(index_guess);
|
||||
let old_hash = self.root.expn_hashes.get(self, index_guess).map(|lazy| lazy.decode(self));
|
||||
|
||||
|
|
|
|||
|
|
@ -407,6 +407,10 @@ impl<'a> Parser<'a> {
|
|||
// Qualified path
|
||||
let (qself, path) = self.parse_qpath(PathStyle::Type)?;
|
||||
TyKind::Path(Some(qself), path)
|
||||
} else if (self.token.is_keyword(kw::Const) || self.token.is_keyword(kw::Mut))
|
||||
&& self.look_ahead(1, |t| *t == token::Star)
|
||||
{
|
||||
self.parse_ty_c_style_pointer()?
|
||||
} else if self.check_path() {
|
||||
self.parse_path_start_ty(lo, allow_plus, ty_generics)?
|
||||
} else if self.can_begin_bound() {
|
||||
|
|
@ -588,6 +592,41 @@ impl<'a> Parser<'a> {
|
|||
Ok(TyKind::TraitObject(bounds, TraitObjectSyntax::None))
|
||||
}
|
||||
|
||||
/// Parses a raw pointer with a C-style typo
|
||||
fn parse_ty_c_style_pointer(&mut self) -> PResult<'a, TyKind> {
|
||||
let kw_span = self.token.span;
|
||||
let mutbl = self.parse_const_or_mut();
|
||||
|
||||
if let Some(mutbl) = mutbl
|
||||
&& self.eat(exp!(Star))
|
||||
{
|
||||
let star_span = self.prev_token.span;
|
||||
|
||||
let mutability = match mutbl {
|
||||
Mutability::Not => "const",
|
||||
Mutability::Mut => "mut",
|
||||
};
|
||||
|
||||
let ty = self.parse_ty_no_question_mark_recover()?;
|
||||
|
||||
self.dcx()
|
||||
.struct_span_err(
|
||||
kw_span,
|
||||
format!("raw pointer types must be written as `*{mutability} T`"),
|
||||
)
|
||||
.with_multipart_suggestion(
|
||||
format!("put the `*` before `{mutability}`"),
|
||||
vec![(star_span, String::new()), (kw_span.shrink_to_lo(), "*".to_string())],
|
||||
Applicability::MachineApplicable,
|
||||
)
|
||||
.emit();
|
||||
|
||||
return Ok(TyKind::Ptr(MutTy { ty, mutbl }));
|
||||
}
|
||||
// This is unreachable because we always get into if above and return from it
|
||||
unreachable!("this could never happen")
|
||||
}
|
||||
|
||||
/// Parses a raw pointer type: `*[const | mut] $type`.
|
||||
fn parse_ty_ptr(&mut self) -> PResult<'a, TyKind> {
|
||||
let mutbl = self.parse_const_or_mut().unwrap_or_else(|| {
|
||||
|
|
|
|||
|
|
@ -215,10 +215,10 @@ pub fn take_hook() -> Box<dyn Fn(&PanicHookInfo<'_>) + 'static + Sync + Send> {
|
|||
///
|
||||
/// // Equivalent to
|
||||
/// // let prev = panic::take_hook();
|
||||
/// // panic::set_hook(move |info| {
|
||||
/// // panic::set_hook(Box::new(move |info| {
|
||||
/// // println!("...");
|
||||
/// // prev(info);
|
||||
/// // );
|
||||
/// // }));
|
||||
/// panic::update_hook(move |prev, info| {
|
||||
/// println!("Print custom message and execute panic handler as usual");
|
||||
/// prev(info);
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ use crate::core::builder::{
|
|||
};
|
||||
use crate::core::config::TargetSelection;
|
||||
use crate::core::config::flags::{Subcommand, get_completion, top_level_help};
|
||||
use crate::core::debuggers;
|
||||
use crate::utils::build_stamp::{self, BuildStamp};
|
||||
use crate::utils::exec::{BootstrapCommand, command};
|
||||
use crate::utils::helpers::{
|
||||
|
|
@ -38,8 +39,6 @@ use crate::utils::helpers::{
|
|||
use crate::utils::render_tests::{add_flags_and_try_run_tests, try_run_tests};
|
||||
use crate::{CLang, CodegenBackendKind, DocTests, GitRepo, Mode, PathSet, envify};
|
||||
|
||||
const ADB_TEST_DIR: &str = "/data/local/tmp/work";
|
||||
|
||||
/// Runs `cargo test` on various internal tools used by bootstrap.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct CrateBootstrap {
|
||||
|
|
@ -2078,27 +2077,28 @@ Please disable assertions with `rust.debug-assertions = false`.
|
|||
|
||||
cmd.arg("--python").arg(builder.python());
|
||||
|
||||
if let Some(ref gdb) = builder.config.gdb {
|
||||
cmd.arg("--gdb").arg(gdb);
|
||||
// FIXME(#148099): Currently we set these Android-related flags in all
|
||||
// modes, even though they should only be needed in "debuginfo" mode,
|
||||
// because the GDB-discovery code in compiletest currently assumes that
|
||||
// `--android-cross-path` is always set for Android targets.
|
||||
if let Some(debuggers::Android { adb_path, adb_test_dir, android_cross_path }) =
|
||||
debuggers::discover_android(builder, target)
|
||||
{
|
||||
cmd.arg("--adb-path").arg(adb_path);
|
||||
cmd.arg("--adb-test-dir").arg(adb_test_dir);
|
||||
cmd.arg("--android-cross-path").arg(android_cross_path);
|
||||
}
|
||||
|
||||
let lldb_exe = builder.config.lldb.clone().unwrap_or_else(|| PathBuf::from("lldb"));
|
||||
let lldb_version = command(&lldb_exe)
|
||||
.allow_failure()
|
||||
.arg("--version")
|
||||
.run_capture(builder)
|
||||
.stdout_if_ok()
|
||||
.and_then(|v| if v.trim().is_empty() { None } else { Some(v) });
|
||||
if let Some(ref vers) = lldb_version {
|
||||
cmd.arg("--lldb-version").arg(vers);
|
||||
let lldb_python_dir = command(&lldb_exe)
|
||||
.allow_failure()
|
||||
.arg("-P")
|
||||
.run_capture_stdout(builder)
|
||||
.stdout_if_ok()
|
||||
.map(|p| p.lines().next().expect("lldb Python dir not found").to_string());
|
||||
if let Some(ref dir) = lldb_python_dir {
|
||||
cmd.arg("--lldb-python-dir").arg(dir);
|
||||
if mode == "debuginfo" {
|
||||
if let Some(debuggers::Gdb { gdb }) = debuggers::discover_gdb(builder) {
|
||||
cmd.arg("--gdb").arg(gdb);
|
||||
}
|
||||
|
||||
if let Some(debuggers::Lldb { lldb_version, lldb_python_dir }) =
|
||||
debuggers::discover_lldb(builder)
|
||||
{
|
||||
cmd.arg("--lldb-version").arg(lldb_version);
|
||||
cmd.arg("--lldb-python-dir").arg(lldb_python_dir);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2332,16 +2332,6 @@ Please disable assertions with `rust.debug-assertions = false`.
|
|||
|
||||
cmd.env("RUST_TEST_TMPDIR", builder.tempdir());
|
||||
|
||||
cmd.arg("--adb-path").arg("adb");
|
||||
cmd.arg("--adb-test-dir").arg(ADB_TEST_DIR);
|
||||
if target.contains("android") && !builder.config.dry_run() {
|
||||
// Assume that cc for this target comes from the android sysroot
|
||||
cmd.arg("--android-cross-path")
|
||||
.arg(builder.cc(target).parent().unwrap().parent().unwrap());
|
||||
} else {
|
||||
cmd.arg("--android-cross-path").arg("");
|
||||
}
|
||||
|
||||
if builder.config.cmd.rustfix_coverage() {
|
||||
cmd.arg("--rustfix-coverage");
|
||||
}
|
||||
|
|
|
|||
24
src/bootstrap/src/core/debuggers/android.rs
Normal file
24
src/bootstrap/src/core/debuggers/android.rs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use crate::core::builder::Builder;
|
||||
use crate::core::config::TargetSelection;
|
||||
|
||||
pub(crate) struct Android {
|
||||
pub(crate) adb_path: &'static str,
|
||||
pub(crate) adb_test_dir: &'static str,
|
||||
pub(crate) android_cross_path: PathBuf,
|
||||
}
|
||||
|
||||
pub(crate) fn discover_android(builder: &Builder<'_>, target: TargetSelection) -> Option<Android> {
|
||||
let adb_path = "adb";
|
||||
// See <https://github.com/rust-lang/rust/pull/102755>.
|
||||
let adb_test_dir = "/data/local/tmp/work";
|
||||
|
||||
let android_cross_path = if target.contains("android") && !builder.config.dry_run() {
|
||||
builder.cc(target).parent().unwrap().parent().unwrap().to_owned()
|
||||
} else {
|
||||
PathBuf::new()
|
||||
};
|
||||
|
||||
Some(Android { adb_path, adb_test_dir, android_cross_path })
|
||||
}
|
||||
13
src/bootstrap/src/core/debuggers/gdb.rs
Normal file
13
src/bootstrap/src/core/debuggers/gdb.rs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
use std::path::Path;
|
||||
|
||||
use crate::core::builder::Builder;
|
||||
|
||||
pub(crate) struct Gdb<'a> {
|
||||
pub(crate) gdb: &'a Path,
|
||||
}
|
||||
|
||||
pub(crate) fn discover_gdb<'a>(builder: &'a Builder<'_>) -> Option<Gdb<'a>> {
|
||||
let gdb = builder.config.gdb.as_deref()?;
|
||||
|
||||
Some(Gdb { gdb })
|
||||
}
|
||||
32
src/bootstrap/src/core/debuggers/lldb.rs
Normal file
32
src/bootstrap/src/core/debuggers/lldb.rs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use crate::core::builder::Builder;
|
||||
use crate::utils::exec::command;
|
||||
|
||||
pub(crate) struct Lldb {
|
||||
pub(crate) lldb_version: String,
|
||||
pub(crate) lldb_python_dir: String,
|
||||
}
|
||||
|
||||
pub(crate) fn discover_lldb(builder: &Builder<'_>) -> Option<Lldb> {
|
||||
// FIXME(#148361): We probably should not be picking up whatever arbitrary
|
||||
// lldb happens to be in the user's path, and instead require some kind of
|
||||
// explicit opt-in or configuration.
|
||||
let lldb_exe = builder.config.lldb.clone().unwrap_or_else(|| PathBuf::from("lldb"));
|
||||
|
||||
let lldb_version = command(&lldb_exe)
|
||||
.allow_failure()
|
||||
.arg("--version")
|
||||
.run_capture(builder)
|
||||
.stdout_if_ok()
|
||||
.and_then(|v| if v.trim().is_empty() { None } else { Some(v) })?;
|
||||
|
||||
let lldb_python_dir = command(&lldb_exe)
|
||||
.allow_failure()
|
||||
.arg("-P")
|
||||
.run_capture_stdout(builder)
|
||||
.stdout_if_ok()
|
||||
.map(|p| p.lines().next().expect("lldb Python dir not found").to_string())?;
|
||||
|
||||
Some(Lldb { lldb_version, lldb_python_dir })
|
||||
}
|
||||
10
src/bootstrap/src/core/debuggers/mod.rs
Normal file
10
src/bootstrap/src/core/debuggers/mod.rs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
//! Code for discovering debuggers and debugger-related configuration, so that
|
||||
//! it can be passed to compiletest when running debuginfo tests.
|
||||
|
||||
pub(crate) use self::android::{Android, discover_android};
|
||||
pub(crate) use self::gdb::{Gdb, discover_gdb};
|
||||
pub(crate) use self::lldb::{Lldb, discover_lldb};
|
||||
|
||||
mod android;
|
||||
mod gdb;
|
||||
mod lldb;
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
pub(crate) mod build_steps;
|
||||
pub(crate) mod builder;
|
||||
pub(crate) mod config;
|
||||
pub(crate) mod debuggers;
|
||||
pub(crate) mod download;
|
||||
pub(crate) mod metadata;
|
||||
pub(crate) mod sanity;
|
||||
|
|
|
|||
|
|
@ -166,6 +166,7 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions
|
|||
remap_path_prefix: options.remap_path_prefix.clone(),
|
||||
unstable_opts: options.unstable_opts.clone(),
|
||||
error_format: options.error_format.clone(),
|
||||
target_modifiers: options.target_modifiers.clone(),
|
||||
..config::Options::default()
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -896,7 +896,7 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) {
|
|||
// Register the loaded external files in the source map so they show up in depinfo.
|
||||
// We can't load them via the source map because it gets created after we process the options.
|
||||
for external_path in &loaded_paths {
|
||||
let _ = sess.source_map().load_file(external_path);
|
||||
let _ = sess.source_map().load_binary_file(external_path);
|
||||
}
|
||||
|
||||
if sess.opts.describe_lints {
|
||||
|
|
|
|||
|
|
@ -273,6 +273,7 @@ pub(crate) fn run(
|
|||
bin_crate: bool,
|
||||
) {
|
||||
let inner = move || -> Result<(), String> {
|
||||
let emit_dep_info = renderopts.dep_info().is_some();
|
||||
// Generates source files for examples
|
||||
renderopts.no_emit_shared = true;
|
||||
let (cx, _) = Context::init(krate, renderopts, cache, tcx, Default::default())
|
||||
|
|
@ -320,6 +321,10 @@ pub(crate) fn run(
|
|||
calls.encode(&mut encoder);
|
||||
encoder.finish().map_err(|(_path, e)| e.to_string())?;
|
||||
|
||||
if emit_dep_info {
|
||||
rustc_interface::passes::write_dep_info(tcx);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -103,22 +103,19 @@ fn find_cdb(target: &str) -> Option<Utf8PathBuf> {
|
|||
}
|
||||
|
||||
/// Returns Path to CDB
|
||||
pub(crate) fn analyze_cdb(
|
||||
cdb: Option<String>,
|
||||
target: &str,
|
||||
) -> (Option<Utf8PathBuf>, Option<[u16; 4]>) {
|
||||
pub(crate) fn discover_cdb(cdb: Option<String>, target: &str) -> Option<Utf8PathBuf> {
|
||||
let cdb = cdb.map(Utf8PathBuf::from).or_else(|| find_cdb(target));
|
||||
cdb
|
||||
}
|
||||
|
||||
pub(crate) fn query_cdb_version(cdb: &Utf8Path) -> Option<[u16; 4]> {
|
||||
let mut version = None;
|
||||
if let Some(cdb) = cdb.as_ref() {
|
||||
if let Ok(output) = Command::new(cdb).arg("/version").output() {
|
||||
if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() {
|
||||
version = extract_cdb_version(&first_line);
|
||||
}
|
||||
if let Ok(output) = Command::new(cdb).arg("/version").output() {
|
||||
if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() {
|
||||
version = extract_cdb_version(&first_line);
|
||||
}
|
||||
}
|
||||
|
||||
(cdb, version)
|
||||
version
|
||||
}
|
||||
|
||||
pub(crate) fn extract_cdb_version(full_version_line: &str) -> Option<[u16; 4]> {
|
||||
|
|
@ -132,12 +129,11 @@ pub(crate) fn extract_cdb_version(full_version_line: &str) -> Option<[u16; 4]> {
|
|||
Some([major, minor, patch, build])
|
||||
}
|
||||
|
||||
/// Returns (Path to GDB, GDB Version)
|
||||
pub(crate) fn analyze_gdb(
|
||||
pub(crate) fn discover_gdb(
|
||||
gdb: Option<String>,
|
||||
target: &str,
|
||||
android_cross_path: &Utf8Path,
|
||||
) -> (Option<String>, Option<u32>) {
|
||||
) -> Option<String> {
|
||||
#[cfg(not(windows))]
|
||||
const GDB_FALLBACK: &str = "gdb";
|
||||
#[cfg(windows)]
|
||||
|
|
@ -159,6 +155,10 @@ pub(crate) fn analyze_gdb(
|
|||
Some(ref s) => s.to_owned(),
|
||||
};
|
||||
|
||||
Some(gdb)
|
||||
}
|
||||
|
||||
pub(crate) fn query_gdb_version(gdb: &str) -> Option<u32> {
|
||||
let mut version_line = None;
|
||||
if let Ok(output) = Command::new(&gdb).arg("--version").output() {
|
||||
if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() {
|
||||
|
|
@ -168,10 +168,10 @@ pub(crate) fn analyze_gdb(
|
|||
|
||||
let version = match version_line {
|
||||
Some(line) => extract_gdb_version(&line),
|
||||
None => return (None, None),
|
||||
None => return None,
|
||||
};
|
||||
|
||||
(Some(gdb), version)
|
||||
version
|
||||
}
|
||||
|
||||
pub(crate) fn extract_gdb_version(full_version_line: &str) -> Option<u32> {
|
||||
|
|
|
|||
|
|
@ -258,10 +258,11 @@ fn parse_config(args: Vec<String>) -> Config {
|
|||
let target = opt_str2(matches.opt_str("target"));
|
||||
let android_cross_path = opt_path(matches, "android-cross-path");
|
||||
// FIXME: `cdb_version` is *derived* from cdb, but it's *not* technically a config!
|
||||
let (cdb, cdb_version) = debuggers::analyze_cdb(matches.opt_str("cdb"), &target);
|
||||
let cdb = debuggers::discover_cdb(matches.opt_str("cdb"), &target);
|
||||
let cdb_version = cdb.as_deref().and_then(debuggers::query_cdb_version);
|
||||
// FIXME: `gdb_version` is *derived* from gdb, but it's *not* technically a config!
|
||||
let (gdb, gdb_version) =
|
||||
debuggers::analyze_gdb(matches.opt_str("gdb"), &target, &android_cross_path);
|
||||
let gdb = debuggers::discover_gdb(matches.opt_str("gdb"), &target, &android_cross_path);
|
||||
let gdb_version = gdb.as_deref().and_then(debuggers::query_gdb_version);
|
||||
// FIXME: `lldb_version` is *derived* from lldb, but it's *not* technically a config!
|
||||
let lldb_version =
|
||||
matches.opt_str("lldb-version").as_deref().and_then(debuggers::extract_lldb_version);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
use rand::Rng;
|
||||
use rustc_apfloat::Float;
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::FloatTy;
|
||||
|
||||
|
|
@ -83,62 +81,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
this.write_scalar(val, &dest)?;
|
||||
}
|
||||
}
|
||||
"fma" | "relaxed_fma" => {
|
||||
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)?;
|
||||
let (dest, dest_len) = this.project_to_simd(dest)?;
|
||||
|
||||
assert_eq!(dest_len, a_len);
|
||||
assert_eq!(dest_len, b_len);
|
||||
assert_eq!(dest_len, c_len);
|
||||
|
||||
for i in 0..dest_len {
|
||||
let a = this.read_scalar(&this.project_index(&a, i)?)?;
|
||||
let b = this.read_scalar(&this.project_index(&b, i)?)?;
|
||||
let c = this.read_scalar(&this.project_index(&c, i)?)?;
|
||||
let dest = this.project_index(&dest, i)?;
|
||||
|
||||
let fuse: bool = intrinsic_name == "fma"
|
||||
|| (this.machine.float_nondet && this.machine.rng.get_mut().random());
|
||||
|
||||
// Works for f32 and f64.
|
||||
// FIXME: using host floats to work around https://github.com/rust-lang/miri/issues/2468.
|
||||
let ty::Float(float_ty) = dest.layout.ty.kind() else {
|
||||
span_bug!(this.cur_span(), "{} operand is not a float", intrinsic_name)
|
||||
};
|
||||
let val = match float_ty {
|
||||
FloatTy::F16 => unimplemented!("f16_f128"),
|
||||
FloatTy::F32 => {
|
||||
let a = a.to_f32()?;
|
||||
let b = b.to_f32()?;
|
||||
let c = c.to_f32()?;
|
||||
let res = if fuse {
|
||||
a.mul_add(b, c).value
|
||||
} else {
|
||||
((a * b).value + c).value
|
||||
};
|
||||
let res = this.adjust_nan(res, &[a, b, c]);
|
||||
Scalar::from(res)
|
||||
}
|
||||
FloatTy::F64 => {
|
||||
let a = a.to_f64()?;
|
||||
let b = b.to_f64()?;
|
||||
let c = c.to_f64()?;
|
||||
let res = if fuse {
|
||||
a.mul_add(b, c).value
|
||||
} else {
|
||||
((a * b).value + c).value
|
||||
};
|
||||
let res = this.adjust_nan(res, &[a, b, c]);
|
||||
Scalar::from(res)
|
||||
}
|
||||
FloatTy::F128 => unimplemented!("f16_f128"),
|
||||
};
|
||||
this.write_scalar(val, &dest)?;
|
||||
}
|
||||
}
|
||||
"expose_provenance" => {
|
||||
let [op] = check_intrinsic_arg_count(args)?;
|
||||
let (op, op_len) = this.project_to_simd(op)?;
|
||||
|
|
|
|||
|
|
@ -1347,8 +1347,8 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn float_fuse_mul_add(ecx: &mut InterpCx<'tcx, Self>) -> bool {
|
||||
ecx.machine.float_nondet && ecx.machine.rng.get_mut().random()
|
||||
fn float_fuse_mul_add(ecx: &InterpCx<'tcx, Self>) -> bool {
|
||||
ecx.machine.float_nondet && ecx.machine.rng.borrow_mut().random()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
|
|
|||
|
|
@ -6,18 +6,143 @@
|
|||
rustc_attrs,
|
||||
intrinsics,
|
||||
core_intrinsics,
|
||||
repr_simd
|
||||
repr_simd,
|
||||
f16,
|
||||
f128
|
||||
)]
|
||||
#![allow(incomplete_features, internal_features)]
|
||||
#![allow(incomplete_features, internal_features, non_camel_case_types)]
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
use std::intrinsics::simd as intrinsics;
|
||||
use std::ptr;
|
||||
use std::simd::StdFloat;
|
||||
use std::simd::prelude::*;
|
||||
|
||||
#[repr(simd, packed)]
|
||||
#[derive(Copy)]
|
||||
struct PackedSimd<T, const N: usize>([T; N]);
|
||||
|
||||
impl<T: Copy, const N: usize> Clone for PackedSimd<T, N> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialEq + Copy, const N: usize> PartialEq for PackedSimd<T, N> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.into_array() == other.into_array()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Debug + Copy, const N: usize> Debug for PackedSimd<T, N> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
Debug::fmt(&self.into_array(), f)
|
||||
}
|
||||
}
|
||||
|
||||
type f16x2 = PackedSimd<f16, 2>;
|
||||
type f16x4 = PackedSimd<f16, 4>;
|
||||
|
||||
type f128x2 = PackedSimd<f128, 2>;
|
||||
type f128x4 = PackedSimd<f128, 4>;
|
||||
|
||||
impl<T: Copy, const N: usize> PackedSimd<T, N> {
|
||||
fn splat(x: T) -> Self {
|
||||
Self([x; N])
|
||||
}
|
||||
fn from_array(a: [T; N]) -> Self {
|
||||
Self(a)
|
||||
}
|
||||
fn into_array(self) -> [T; N] {
|
||||
// as we have `repr(packed)`, there shouldn't be any padding bytes
|
||||
unsafe { std::mem::transmute_copy(&self) }
|
||||
}
|
||||
}
|
||||
|
||||
#[rustc_intrinsic]
|
||||
#[rustc_nounwind]
|
||||
pub unsafe fn simd_shuffle_const_generic<T, U, const IDX: &'static [u32]>(x: T, y: T) -> U;
|
||||
|
||||
pub fn simd_ops_f16() {
|
||||
use intrinsics::*;
|
||||
|
||||
// small hack to make type inference better
|
||||
macro_rules! assert_eq {
|
||||
($a:expr, $b:expr $(,$t:tt)*) => {{
|
||||
let a = $a;
|
||||
let b = $b;
|
||||
if false { let _inference = b == a; }
|
||||
::std::assert_eq!(a, b, $(,$t)*)
|
||||
}}
|
||||
}
|
||||
|
||||
let a = f16x4::splat(10.0);
|
||||
let b = f16x4::from_array([1.0, 2.0, 3.0, -4.0]);
|
||||
|
||||
unsafe {
|
||||
assert_eq!(simd_neg(b), f16x4::from_array([-1.0, -2.0, -3.0, 4.0]));
|
||||
assert_eq!(simd_add(a, b), f16x4::from_array([11.0, 12.0, 13.0, 6.0]));
|
||||
assert_eq!(simd_sub(a, b), f16x4::from_array([9.0, 8.0, 7.0, 14.0]));
|
||||
assert_eq!(simd_mul(a, b), f16x4::from_array([10.0, 20.0, 30.0, -40.0]));
|
||||
assert_eq!(simd_div(b, a), f16x4::from_array([0.1, 0.2, 0.3, -0.4]));
|
||||
assert_eq!(simd_div(a, f16x4::splat(2.0)), f16x4::splat(5.0));
|
||||
assert_eq!(simd_rem(a, b), f16x4::from_array([0.0, 0.0, 1.0, 2.0]));
|
||||
assert_eq!(simd_fabs(b), f16x4::from_array([1.0, 2.0, 3.0, 4.0]));
|
||||
assert_eq!(
|
||||
simd_fmax(a, simd_mul(b, f16x4::splat(4.0))),
|
||||
f16x4::from_array([10.0, 10.0, 12.0, 10.0])
|
||||
);
|
||||
assert_eq!(
|
||||
simd_fmin(a, simd_mul(b, f16x4::splat(4.0))),
|
||||
f16x4::from_array([4.0, 8.0, 10.0, -16.0])
|
||||
);
|
||||
|
||||
assert_eq!(simd_fma(a, b, a), simd_add(simd_mul(a, b), a));
|
||||
assert_eq!(simd_fma(b, b, a), simd_add(simd_mul(b, b), a));
|
||||
assert_eq!(simd_fma(a, b, b), simd_add(simd_mul(a, b), b));
|
||||
assert_eq!(
|
||||
simd_fma(f16x4::splat(-3.2), b, f16x4::splat(f16::NEG_INFINITY)),
|
||||
f16x4::splat(f16::NEG_INFINITY)
|
||||
);
|
||||
|
||||
assert_eq!(simd_relaxed_fma(a, b, a), simd_add(simd_mul(a, b), a));
|
||||
assert_eq!(simd_relaxed_fma(b, b, a), simd_add(simd_mul(b, b), a));
|
||||
assert_eq!(simd_relaxed_fma(a, b, b), simd_add(simd_mul(a, b), b));
|
||||
assert_eq!(
|
||||
simd_relaxed_fma(f16x4::splat(-3.2), b, f16x4::splat(f16::NEG_INFINITY)),
|
||||
f16x4::splat(f16::NEG_INFINITY)
|
||||
);
|
||||
|
||||
assert_eq!(simd_eq(a, simd_mul(f16x4::splat(5.0), b)), i32x4::from_array([0, !0, 0, 0]));
|
||||
assert_eq!(simd_ne(a, simd_mul(f16x4::splat(5.0), b)), i32x4::from_array([!0, 0, !0, !0]));
|
||||
assert_eq!(simd_le(a, simd_mul(f16x4::splat(5.0), b)), i32x4::from_array([0, !0, !0, 0]));
|
||||
assert_eq!(simd_lt(a, simd_mul(f16x4::splat(5.0), b)), i32x4::from_array([0, 0, !0, 0]));
|
||||
assert_eq!(simd_ge(a, simd_mul(f16x4::splat(5.0), b)), i32x4::from_array([!0, !0, 0, !0]));
|
||||
assert_eq!(simd_gt(a, simd_mul(f16x4::splat(5.0), b)), i32x4::from_array([!0, 0, 0, !0]));
|
||||
|
||||
assert_eq!(simd_reduce_add_ordered(a, 0.0), 40.0f16);
|
||||
assert_eq!(simd_reduce_add_ordered(b, 0.0), 2.0f16);
|
||||
assert_eq!(simd_reduce_mul_ordered(a, 1.0), 10000.0f16);
|
||||
assert_eq!(simd_reduce_mul_ordered(b, 1.0), -24.0f16);
|
||||
assert_eq!(simd_reduce_max(a), 10.0f16);
|
||||
assert_eq!(simd_reduce_max(b), 3.0f16);
|
||||
assert_eq!(simd_reduce_min(a), 10.0f16);
|
||||
assert_eq!(simd_reduce_min(b), -4.0f16);
|
||||
|
||||
assert_eq!(
|
||||
simd_fmax(f16x2::from_array([0.0, f16::NAN]), f16x2::from_array([f16::NAN, 0.0])),
|
||||
f16x2::from_array([0.0, 0.0])
|
||||
);
|
||||
assert_eq!(simd_reduce_max(f16x2::from_array([0.0, f16::NAN])), 0.0f16);
|
||||
assert_eq!(simd_reduce_max(f16x2::from_array([f16::NAN, 0.0])), 0.0f16);
|
||||
assert_eq!(
|
||||
simd_fmin(f16x2::from_array([0.0, f16::NAN]), f16x2::from_array([f16::NAN, 0.0])),
|
||||
f16x2::from_array([0.0, 0.0])
|
||||
);
|
||||
assert_eq!(simd_reduce_min(f16x2::from_array([0.0, f16::NAN])), 0.0f16);
|
||||
assert_eq!(simd_reduce_min(f16x2::from_array([f16::NAN, 0.0])), 0.0f16);
|
||||
}
|
||||
}
|
||||
|
||||
fn simd_ops_f32() {
|
||||
let a = f32x4::splat(10.0);
|
||||
let b = f32x4::from_array([1.0, 2.0, 3.0, -4.0]);
|
||||
|
|
@ -148,6 +273,87 @@ fn simd_ops_f64() {
|
|||
assert_eq!(f64x2::from_array([f64::NAN, 0.0]).reduce_min(), 0.0);
|
||||
}
|
||||
|
||||
pub fn simd_ops_f128() {
|
||||
use intrinsics::*;
|
||||
|
||||
// small hack to make type inference better
|
||||
macro_rules! assert_eq {
|
||||
($a:expr, $b:expr $(,$t:tt)*) => {{
|
||||
let a = $a;
|
||||
let b = $b;
|
||||
if false { let _inference = b == a; }
|
||||
::std::assert_eq!(a, b, $(,$t)*)
|
||||
}}
|
||||
}
|
||||
|
||||
let a = f128x4::splat(10.0);
|
||||
let b = f128x4::from_array([1.0, 2.0, 3.0, -4.0]);
|
||||
|
||||
unsafe {
|
||||
assert_eq!(simd_neg(b), f128x4::from_array([-1.0, -2.0, -3.0, 4.0]));
|
||||
assert_eq!(simd_add(a, b), f128x4::from_array([11.0, 12.0, 13.0, 6.0]));
|
||||
assert_eq!(simd_sub(a, b), f128x4::from_array([9.0, 8.0, 7.0, 14.0]));
|
||||
assert_eq!(simd_mul(a, b), f128x4::from_array([10.0, 20.0, 30.0, -40.0]));
|
||||
assert_eq!(simd_div(b, a), f128x4::from_array([0.1, 0.2, 0.3, -0.4]));
|
||||
assert_eq!(simd_div(a, f128x4::splat(2.0)), f128x4::splat(5.0));
|
||||
assert_eq!(simd_rem(a, b), f128x4::from_array([0.0, 0.0, 1.0, 2.0]));
|
||||
assert_eq!(simd_fabs(b), f128x4::from_array([1.0, 2.0, 3.0, 4.0]));
|
||||
assert_eq!(
|
||||
simd_fmax(a, simd_mul(b, f128x4::splat(4.0))),
|
||||
f128x4::from_array([10.0, 10.0, 12.0, 10.0])
|
||||
);
|
||||
assert_eq!(
|
||||
simd_fmin(a, simd_mul(b, f128x4::splat(4.0))),
|
||||
f128x4::from_array([4.0, 8.0, 10.0, -16.0])
|
||||
);
|
||||
|
||||
assert_eq!(simd_fma(a, b, a), simd_add(simd_mul(a, b), a));
|
||||
assert_eq!(simd_fma(b, b, a), simd_add(simd_mul(b, b), a));
|
||||
assert_eq!(simd_fma(a, b, b), simd_add(simd_mul(a, b), b));
|
||||
assert_eq!(
|
||||
simd_fma(f128x4::splat(-3.2), b, f128x4::splat(f128::NEG_INFINITY)),
|
||||
f128x4::splat(f128::NEG_INFINITY)
|
||||
);
|
||||
|
||||
assert_eq!(simd_relaxed_fma(a, b, a), simd_add(simd_mul(a, b), a));
|
||||
assert_eq!(simd_relaxed_fma(b, b, a), simd_add(simd_mul(b, b), a));
|
||||
assert_eq!(simd_relaxed_fma(a, b, b), simd_add(simd_mul(a, b), b));
|
||||
assert_eq!(
|
||||
simd_relaxed_fma(f128x4::splat(-3.2), b, f128x4::splat(f128::NEG_INFINITY)),
|
||||
f128x4::splat(f128::NEG_INFINITY)
|
||||
);
|
||||
|
||||
assert_eq!(simd_eq(a, simd_mul(f128x4::splat(5.0), b)), i32x4::from_array([0, !0, 0, 0]));
|
||||
assert_eq!(simd_ne(a, simd_mul(f128x4::splat(5.0), b)), i32x4::from_array([!0, 0, !0, !0]));
|
||||
assert_eq!(simd_le(a, simd_mul(f128x4::splat(5.0), b)), i32x4::from_array([0, !0, !0, 0]));
|
||||
assert_eq!(simd_lt(a, simd_mul(f128x4::splat(5.0), b)), i32x4::from_array([0, 0, !0, 0]));
|
||||
assert_eq!(simd_ge(a, simd_mul(f128x4::splat(5.0), b)), i32x4::from_array([!0, !0, 0, !0]));
|
||||
assert_eq!(simd_gt(a, simd_mul(f128x4::splat(5.0), b)), i32x4::from_array([!0, 0, 0, !0]));
|
||||
|
||||
assert_eq!(simd_reduce_add_ordered(a, 0.0), 40.0f128);
|
||||
assert_eq!(simd_reduce_add_ordered(b, 0.0), 2.0f128);
|
||||
assert_eq!(simd_reduce_mul_ordered(a, 1.0), 10000.0f128);
|
||||
assert_eq!(simd_reduce_mul_ordered(b, 1.0), -24.0f128);
|
||||
assert_eq!(simd_reduce_max(a), 10.0f128);
|
||||
assert_eq!(simd_reduce_max(b), 3.0f128);
|
||||
assert_eq!(simd_reduce_min(a), 10.0f128);
|
||||
assert_eq!(simd_reduce_min(b), -4.0f128);
|
||||
|
||||
assert_eq!(
|
||||
simd_fmax(f128x2::from_array([0.0, f128::NAN]), f128x2::from_array([f128::NAN, 0.0])),
|
||||
f128x2::from_array([0.0, 0.0])
|
||||
);
|
||||
assert_eq!(simd_reduce_max(f128x2::from_array([0.0, f128::NAN])), 0.0f128);
|
||||
assert_eq!(simd_reduce_max(f128x2::from_array([f128::NAN, 0.0])), 0.0f128);
|
||||
assert_eq!(
|
||||
simd_fmin(f128x2::from_array([0.0, f128::NAN]), f128x2::from_array([f128::NAN, 0.0])),
|
||||
f128x2::from_array([0.0, 0.0])
|
||||
);
|
||||
assert_eq!(simd_reduce_min(f128x2::from_array([0.0, f128::NAN])), 0.0f128);
|
||||
assert_eq!(simd_reduce_min(f128x2::from_array([f128::NAN, 0.0])), 0.0f128);
|
||||
}
|
||||
}
|
||||
|
||||
fn simd_ops_i32() {
|
||||
let a = i32x4::splat(10);
|
||||
let b = i32x4::from_array([1, 2, 3, -4]);
|
||||
|
|
@ -563,6 +769,31 @@ fn simd_gather_scatter() {
|
|||
}
|
||||
|
||||
fn simd_round() {
|
||||
unsafe {
|
||||
use intrinsics::*;
|
||||
|
||||
assert_eq!(
|
||||
simd_ceil(f16x4::from_array([0.9, 1.001, 2.0, -4.5])),
|
||||
f16x4::from_array([1.0, 2.0, 2.0, -4.0])
|
||||
);
|
||||
assert_eq!(
|
||||
simd_floor(f16x4::from_array([0.9, 1.001, 2.0, -4.5])),
|
||||
f16x4::from_array([0.0, 1.0, 2.0, -5.0])
|
||||
);
|
||||
assert_eq!(
|
||||
simd_round(f16x4::from_array([0.9, 1.001, 2.0, -4.5])),
|
||||
f16x4::from_array([1.0, 1.0, 2.0, -5.0])
|
||||
);
|
||||
assert_eq!(
|
||||
simd_round_ties_even(f16x4::from_array([0.9, 1.001, 2.0, -4.5])),
|
||||
f16x4::from_array([1.0, 1.0, 2.0, -4.0])
|
||||
);
|
||||
assert_eq!(
|
||||
simd_trunc(f16x4::from_array([0.9, 1.001, 2.0, -4.5])),
|
||||
f16x4::from_array([0.0, 1.0, 2.0, -4.0])
|
||||
);
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
f32x4::from_array([0.9, 1.001, 2.0, -4.5]).ceil(),
|
||||
f32x4::from_array([1.0, 2.0, 2.0, -4.0])
|
||||
|
|
@ -604,6 +835,31 @@ fn simd_round() {
|
|||
f64x4::from_array([0.9, 1.001, 2.0, -4.5]).trunc(),
|
||||
f64x4::from_array([0.0, 1.0, 2.0, -4.0])
|
||||
);
|
||||
|
||||
unsafe {
|
||||
use intrinsics::*;
|
||||
|
||||
assert_eq!(
|
||||
simd_ceil(f128x4::from_array([0.9, 1.001, 2.0, -4.5])),
|
||||
f128x4::from_array([1.0, 2.0, 2.0, -4.0])
|
||||
);
|
||||
assert_eq!(
|
||||
simd_floor(f128x4::from_array([0.9, 1.001, 2.0, -4.5])),
|
||||
f128x4::from_array([0.0, 1.0, 2.0, -5.0])
|
||||
);
|
||||
assert_eq!(
|
||||
simd_round(f128x4::from_array([0.9, 1.001, 2.0, -4.5])),
|
||||
f128x4::from_array([1.0, 1.0, 2.0, -5.0])
|
||||
);
|
||||
assert_eq!(
|
||||
simd_round_ties_even(f128x4::from_array([0.9, 1.001, 2.0, -4.5])),
|
||||
f128x4::from_array([1.0, 1.0, 2.0, -4.0])
|
||||
);
|
||||
assert_eq!(
|
||||
simd_trunc(f128x4::from_array([0.9, 1.001, 2.0, -4.5])),
|
||||
f128x4::from_array([0.0, 1.0, 2.0, -4.0])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn simd_intrinsics() {
|
||||
|
|
@ -724,8 +980,10 @@ fn simd_ops_non_pow2() {
|
|||
|
||||
fn main() {
|
||||
simd_mask();
|
||||
simd_ops_f16();
|
||||
simd_ops_f32();
|
||||
simd_ops_f64();
|
||||
simd_ops_f128();
|
||||
simd_ops_i32();
|
||||
simd_ops_non_pow2();
|
||||
simd_cast();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
// skip-filecheck
|
||||
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
|
||||
//! Tests that we bail out when there are multiple assignments to the same local.
|
||||
//@ test-mir-pass: CopyProp
|
||||
|
|
@ -12,6 +11,14 @@ fn cond() -> bool {
|
|||
|
||||
// EMIT_MIR branch.foo.CopyProp.diff
|
||||
fn foo() -> i32 {
|
||||
// CHECK-LABEL: fn foo(
|
||||
// CHECK: debug x => [[x:_.*]];
|
||||
// CHECK: debug y => [[y:_.*]];
|
||||
// CHECK: bb3: {
|
||||
// CHECK: [[y]] = copy [[x]];
|
||||
// CHECK: bb5: {
|
||||
// CHECK: [[y]] = copy [[x]];
|
||||
// CHECK: _0 = copy [[y]];
|
||||
let x = val();
|
||||
|
||||
let y = if cond() {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
// skip-filecheck
|
||||
// Check that CopyProp does propagate return values of call terminators.
|
||||
//@ test-mir-pass: CopyProp
|
||||
//@ needs-unwind
|
||||
|
|
@ -13,6 +12,13 @@ fn dummy(x: u8) -> u8 {
|
|||
|
||||
// EMIT_MIR calls.nrvo.CopyProp.diff
|
||||
fn nrvo() -> u8 {
|
||||
// CHECK-LABEL: fn nrvo(
|
||||
// CHECK: debug y => _0;
|
||||
// CHECK-NOT: StorageLive(_1);
|
||||
// CHECK-NOT: _1 = dummy(const 5_u8)
|
||||
// CHECK: _0 = dummy(const 5_u8)
|
||||
// CHECK-NOT: _0 = copy _1;
|
||||
// CHECK-NOT: StorageDead(_1);
|
||||
let y = dummy(5); // this should get NRVO
|
||||
y
|
||||
}
|
||||
|
|
@ -20,6 +26,11 @@ fn nrvo() -> u8 {
|
|||
// EMIT_MIR calls.multiple_edges.CopyProp.diff
|
||||
#[custom_mir(dialect = "runtime", phase = "initial")]
|
||||
fn multiple_edges(t: bool) -> u8 {
|
||||
// CHECK-LABEL: fn multiple_edges(
|
||||
// CHECK: bb1: {
|
||||
// CHECK: _2 = dummy(const 13_u8)
|
||||
// CHECK: bb2: {
|
||||
// CHECK: _0 = copy _2;
|
||||
mir! {
|
||||
let x: u8;
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
// skip-filecheck
|
||||
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
|
||||
// Check that CopyProp does not propagate an assignment to a function argument
|
||||
// (doing so can break usages of the original argument value)
|
||||
|
|
@ -9,25 +8,46 @@ fn dummy(x: u8) -> u8 {
|
|||
|
||||
// EMIT_MIR copy_propagation_arg.foo.CopyProp.diff
|
||||
fn foo(mut x: u8) {
|
||||
// CHECK-LABEL: fn foo(
|
||||
// CHECK: debug x => [[x:_.*]];
|
||||
// CHECK: [[three:_.*]] = copy [[x]];
|
||||
// CHECK: [[two:_.*]] = dummy(move [[three]])
|
||||
// CHECK: [[x]] = move [[two]];
|
||||
// calling `dummy` to make a use of `x` that copyprop cannot eliminate
|
||||
x = dummy(x); // this will assign a local to `x`
|
||||
}
|
||||
|
||||
// EMIT_MIR copy_propagation_arg.bar.CopyProp.diff
|
||||
fn bar(mut x: u8) {
|
||||
// CHECK-LABEL: fn bar(
|
||||
// CHECK: debug x => [[x:_.*]];
|
||||
// CHECK: [[three:_.*]] = copy [[x]];
|
||||
// CHECK: dummy(move [[three]])
|
||||
// CHECK: [[x]] = const 5_u8;
|
||||
dummy(x);
|
||||
x = 5;
|
||||
}
|
||||
|
||||
// EMIT_MIR copy_propagation_arg.baz.CopyProp.diff
|
||||
fn baz(mut x: i32) -> i32 {
|
||||
// self-assignment to a function argument should be eliminated
|
||||
// CHECK-LABEL: fn baz(
|
||||
// CHECK: debug x => [[x:_.*]];
|
||||
// CHECK: [[x2:_.*]] = copy [[x]];
|
||||
// CHECK: [[x]] = move [[x2]];
|
||||
// CHECK: _0 = copy [[x]];
|
||||
// In the original case for DestProp, the self-assignment to a function argument is eliminated,
|
||||
// but in CopyProp it is not eliminated.
|
||||
x = x;
|
||||
x
|
||||
}
|
||||
|
||||
// EMIT_MIR copy_propagation_arg.arg_src.CopyProp.diff
|
||||
fn arg_src(mut x: i32) -> i32 {
|
||||
// CHECK-LABEL: fn arg_src(
|
||||
// CHECK: debug x => [[x:_.*]];
|
||||
// CHECK: debug y => [[y:_.*]];
|
||||
// CHECK: [[y]] = copy [[x]];
|
||||
// CHECK: [[x]] = const 123_i32;
|
||||
let y = x;
|
||||
x = 123; // Don't propagate this assignment to `y`
|
||||
y
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
// skip-filecheck
|
||||
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
|
||||
//@ test-mir-pass: CopyProp
|
||||
|
||||
|
|
@ -12,6 +11,13 @@ struct NotCopy(bool);
|
|||
// EMIT_MIR custom_move_arg.f.CopyProp.diff
|
||||
#[custom_mir(dialect = "runtime")]
|
||||
fn f(_1: NotCopy) {
|
||||
// CHECK-LABEL: fn f(
|
||||
// CHECK: bb0: {
|
||||
// CHECK-NOT: _2 = copy _1;
|
||||
// CHECK: _0 = opaque::<NotCopy>(copy _1)
|
||||
// CHECK: bb1: {
|
||||
// CHECK-NOT: _3 = move _2;
|
||||
// CHECK: _0 = opaque::<NotCopy>(copy _1)
|
||||
mir! {
|
||||
{
|
||||
let _2 = _1;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
// skip-filecheck
|
||||
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
|
||||
//! Tests that cyclic assignments don't hang CopyProp, and result in reasonable code.
|
||||
//@ test-mir-pass: CopyProp
|
||||
|
|
@ -8,6 +7,18 @@ fn val() -> i32 {
|
|||
|
||||
// EMIT_MIR cycle.main.CopyProp.diff
|
||||
fn main() {
|
||||
// CHECK-LABEL: fn main(
|
||||
// CHECK: debug x => [[x:_.*]];
|
||||
// CHECK: debug y => [[y:_.*]];
|
||||
// CHECK: debug z => [[y]];
|
||||
// CHECK-NOT: StorageLive([[y]]);
|
||||
// CHECK: [[y]] = copy [[x]];
|
||||
// CHECK-NOT: StorageLive(_3);
|
||||
// CHECK-NOT: _3 = copy [[y]];
|
||||
// CHECK-NOT: StorageLive(_4);
|
||||
// CHECK-NOT: _4 = copy _3;
|
||||
// CHECK-NOT: _1 = move _4;
|
||||
// CHECK: [[x]] = copy [[y]];
|
||||
let mut x = val();
|
||||
let y = x;
|
||||
let z = y;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
// skip-filecheck
|
||||
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
|
||||
//@ test-mir-pass: CopyProp
|
||||
|
||||
|
|
@ -8,6 +7,14 @@ fn id<T>(x: T) -> T {
|
|||
|
||||
// EMIT_MIR dead_stores_79191.f.CopyProp.after.mir
|
||||
fn f(mut a: usize) -> usize {
|
||||
// CHECK-LABEL: fn f(
|
||||
// CHECK: debug a => [[a:_.*]];
|
||||
// CHECK: debug b => [[b:_.*]];
|
||||
// CHECK: [[b]] = copy [[a]];
|
||||
// CHECK: [[a]] = const 5_usize;
|
||||
// CHECK: [[a]] = copy [[b]];
|
||||
// CHECK: [[c:_.*]] = copy [[a]]
|
||||
// CHECK: id::<usize>(move [[c]])
|
||||
let b = a;
|
||||
a = 5;
|
||||
a = b;
|
||||
|
|
|
|||
|
|
@ -1,26 +0,0 @@
|
|||
// MIR for `f` after CopyProp
|
||||
|
||||
fn f(_1: usize) -> usize {
|
||||
debug a => _1;
|
||||
let mut _0: usize;
|
||||
let _2: usize;
|
||||
let mut _3: usize;
|
||||
let mut _4: usize;
|
||||
scope 1 {
|
||||
debug b => _2;
|
||||
}
|
||||
|
||||
bb0: {
|
||||
_2 = copy _1;
|
||||
_1 = const 5_usize;
|
||||
_1 = copy _2;
|
||||
StorageLive(_4);
|
||||
_4 = copy _1;
|
||||
_0 = id::<usize>(move _4) -> [return: bb1, unwind unreachable];
|
||||
}
|
||||
|
||||
bb1: {
|
||||
StorageDead(_4);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
// MIR for `f` after CopyProp
|
||||
|
||||
fn f(_1: usize) -> usize {
|
||||
debug a => _1;
|
||||
let mut _0: usize;
|
||||
let _2: usize;
|
||||
let mut _3: usize;
|
||||
let mut _4: usize;
|
||||
scope 1 {
|
||||
debug b => _2;
|
||||
}
|
||||
|
||||
bb0: {
|
||||
_2 = copy _1;
|
||||
_1 = const 5_usize;
|
||||
_1 = copy _2;
|
||||
StorageLive(_4);
|
||||
_4 = copy _1;
|
||||
_0 = id::<usize>(move _4) -> [return: bb1, unwind continue];
|
||||
}
|
||||
|
||||
bb1: {
|
||||
StorageDead(_4);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
// skip-filecheck
|
||||
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
|
||||
// This is a copy of the `dead_stores_79191` test, except that we turn on DSE. This demonstrates
|
||||
// that that pass enables this one to do more optimizations.
|
||||
|
||||
//@ test-mir-pass: CopyProp
|
||||
//@ compile-flags: -Zmir-enable-passes=+DeadStoreElimination
|
||||
|
||||
fn id<T>(x: T) -> T {
|
||||
x
|
||||
}
|
||||
|
||||
// EMIT_MIR dead_stores_better.f.CopyProp.after.mir
|
||||
pub fn f(mut a: usize) -> usize {
|
||||
let b = a;
|
||||
a = 5;
|
||||
a = b;
|
||||
id(a)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
f(0);
|
||||
}
|
||||
|
|
@ -1,9 +1,12 @@
|
|||
// skip-filecheck
|
||||
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
|
||||
//@ test-mir-pass: CopyProp
|
||||
|
||||
// EMIT_MIR issue_107511.main.CopyProp.diff
|
||||
fn main() {
|
||||
// CHECK-LABEL: fn main(
|
||||
// CHECK: debug i => [[i:_.*]];
|
||||
// CHECK-NOT: StorageLive([[i]]);
|
||||
// CHECK-NOT: StorageDead([[i]]);
|
||||
let mut sum = 0;
|
||||
let a = [0, 10, 20, 30];
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
// skip-filecheck
|
||||
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
|
||||
// Test that we do not move multiple times from the same local.
|
||||
//@ test-mir-pass: CopyProp
|
||||
|
||||
// EMIT_MIR move_arg.f.CopyProp.diff
|
||||
pub fn f<T: Copy>(a: T) {
|
||||
// CHECK-LABEL: fn f(
|
||||
// CHECK: debug a => [[a:_.*]];
|
||||
// CHECK: debug b => [[a]];
|
||||
// CHECK: g::<T>(copy [[a]], copy [[a]])
|
||||
let b = a;
|
||||
g(a, b);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
// skip-filecheck
|
||||
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
|
||||
//@ test-mir-pass: CopyProp
|
||||
|
||||
|
|
@ -15,6 +14,15 @@ struct Foo(u8);
|
|||
|
||||
#[custom_mir(dialect = "runtime")]
|
||||
fn f(a: Foo) -> bool {
|
||||
// CHECK-LABEL: fn f(
|
||||
// CHECK-SAME: [[a:_.*]]: Foo)
|
||||
// CHECK: bb0: {
|
||||
// CHECK-NOT: _2 = copy [[a]];
|
||||
// CHECK-NOT: _3 = move (_2.0: u8);
|
||||
// CHECK: [[c:_.*]] = copy ([[a]].0: u8);
|
||||
// CHECK: _0 = opaque::<Foo>(copy [[a]])
|
||||
// CHECK: bb1: {
|
||||
// CHECK: _0 = opaque::<u8>(move [[c]])
|
||||
mir! {
|
||||
{
|
||||
let b = a;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
// skip-filecheck
|
||||
//@ test-mir-pass: CopyProp
|
||||
//
|
||||
// This attempts to mutate `a` via a pointer derived from `addr_of!(a)`. That is UB
|
||||
|
|
@ -18,6 +17,10 @@ use core::intrinsics::mir::*;
|
|||
|
||||
#[custom_mir(dialect = "analysis", phase = "post-cleanup")]
|
||||
fn f(c: bool) -> bool {
|
||||
// CHECK-LABEL: fn f(
|
||||
// CHECK: _2 = copy _1;
|
||||
// CHECK-NOT: _3 = &raw const _1;
|
||||
// CHECK: _3 = &raw const _2;
|
||||
mir! {
|
||||
{
|
||||
let a = c;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
// skip-filecheck
|
||||
//@ test-mir-pass: CopyProp
|
||||
|
||||
#![feature(custom_mir, core_intrinsics)]
|
||||
|
|
@ -8,6 +7,11 @@ use core::intrinsics::mir::*;
|
|||
|
||||
#[custom_mir(dialect = "analysis", phase = "post-cleanup")]
|
||||
fn f(c: bool) -> bool {
|
||||
// CHECK-LABEL: fn f(
|
||||
// CHECK: bb2: {
|
||||
// CHECK: _2 = copy _3;
|
||||
// CHECK: bb3: {
|
||||
// CHECK: _0 = copy _2;
|
||||
mir! {
|
||||
let a: bool;
|
||||
let b: bool;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
// skip-filecheck
|
||||
//@ test-mir-pass: CopyProp
|
||||
// Verify that we do not ICE on partial initializations.
|
||||
|
||||
|
|
@ -9,6 +8,9 @@ use core::intrinsics::mir::*;
|
|||
// EMIT_MIR partial_init.main.CopyProp.diff
|
||||
#[custom_mir(dialect = "runtime", phase = "post-cleanup")]
|
||||
pub fn main() {
|
||||
// CHECK-LABEL: fn main(
|
||||
// CHECK: let mut [[x:_.*]]: (isize,);
|
||||
// CHECK: ([[x]].0: isize) = const 1_isize;
|
||||
mir! (
|
||||
let x: (isize, );
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
// skip-filecheck
|
||||
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
|
||||
// Check that CopyProp considers reborrows as not mutating the pointer.
|
||||
//@ test-mir-pass: CopyProp
|
||||
|
|
@ -8,6 +7,9 @@ fn opaque(_: impl Sized) {}
|
|||
|
||||
// EMIT_MIR reborrow.remut.CopyProp.diff
|
||||
fn remut(mut x: u8) {
|
||||
// CHECK-LABEL: fn remut(
|
||||
// CHECK: debug a => [[a:_.*]];
|
||||
// CHECK: debug c => [[a]];
|
||||
let a = &mut x;
|
||||
let b = &mut *a; //< this cannot mutate a.
|
||||
let c = a; //< so `c` and `a` can be merged.
|
||||
|
|
@ -16,6 +18,9 @@ fn remut(mut x: u8) {
|
|||
|
||||
// EMIT_MIR reborrow.reraw.CopyProp.diff
|
||||
fn reraw(mut x: u8) {
|
||||
// CHECK-LABEL: fn reraw(
|
||||
// CHECK: debug a => [[a:_.*]];
|
||||
// CHECK: debug c => [[a]];
|
||||
let a = &mut x;
|
||||
let b = &raw mut *a; //< this cannot mutate a.
|
||||
let c = a; //< so `c` and `a` can be merged.
|
||||
|
|
@ -24,6 +29,9 @@ fn reraw(mut x: u8) {
|
|||
|
||||
// EMIT_MIR reborrow.miraw.CopyProp.diff
|
||||
fn miraw(mut x: u8) {
|
||||
// CHECK-LABEL: fn miraw(
|
||||
// CHECK: debug a => [[a:_.*]];
|
||||
// CHECK: debug c => [[a]];
|
||||
let a = &raw mut x;
|
||||
let b = unsafe { &raw mut *a }; //< this cannot mutate a.
|
||||
let c = a; //< so `c` and `a` can be merged.
|
||||
|
|
@ -32,6 +40,9 @@ fn miraw(mut x: u8) {
|
|||
|
||||
// EMIT_MIR reborrow.demiraw.CopyProp.diff
|
||||
fn demiraw(mut x: u8) {
|
||||
// CHECK-LABEL: fn demiraw(
|
||||
// CHECK: debug a => [[a:_.*]];
|
||||
// CHECK: debug c => [[a]];
|
||||
let a = &raw mut x;
|
||||
let b = unsafe { &mut *a }; //< this cannot mutate a.
|
||||
let c = a; //< so `c` and `a` can be merged.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
fn main() {}
|
||||
|
||||
#[test]
|
||||
fn a_test() {
|
||||
foobar::ok();
|
||||
}
|
||||
19
tests/run-make/rustdoc-scrape-examples-dep-info/rmake.rs
Normal file
19
tests/run-make/rustdoc-scrape-examples-dep-info/rmake.rs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
//@ needs-target-std
|
||||
use run_make_support::{assert_contains, rfs};
|
||||
|
||||
#[path = "../rustdoc-scrape-examples-remap/scrape.rs"]
|
||||
mod scrape;
|
||||
|
||||
fn main() {
|
||||
scrape::scrape(
|
||||
&["--scrape-tests", "--emit=dep-info"],
|
||||
&["--emit=dep-info,invocation-specific"],
|
||||
);
|
||||
|
||||
let content = rfs::read_to_string("foobar.d").replace(r"\", "/");
|
||||
assert_contains(&content, "lib.rs:");
|
||||
assert_contains(&content, "rustdoc/ex.calls:");
|
||||
|
||||
let content = rfs::read_to_string("ex.d").replace(r"\", "/");
|
||||
assert_contains(&content, "examples/ex.rs:");
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
//@ has foobar/fn.ok.html '//*[@class="docblock scraped-example-list"]' ''
|
||||
|
||||
pub fn ok() {}
|
||||
|
|
@ -3,5 +3,5 @@
|
|||
mod scrape;
|
||||
|
||||
fn main() {
|
||||
scrape::scrape(&[]);
|
||||
scrape::scrape(&[], &[]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,5 +3,5 @@
|
|||
mod scrape;
|
||||
|
||||
fn main() {
|
||||
scrape::scrape(&[]);
|
||||
scrape::scrape(&[], &[]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,5 +3,5 @@
|
|||
mod scrape;
|
||||
|
||||
fn main() {
|
||||
scrape::scrape(&[]);
|
||||
scrape::scrape(&[], &[]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,5 +2,5 @@
|
|||
mod scrape;
|
||||
|
||||
fn main() {
|
||||
scrape::scrape(&[]);
|
||||
scrape::scrape(&[], &[]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use std::path::Path;
|
|||
|
||||
use run_make_support::{htmldocck, rfs, rustc, rustdoc};
|
||||
|
||||
pub fn scrape(extra_args: &[&str]) {
|
||||
pub fn scrape(extra_args_scrape: &[&str], extra_args_doc: &[&str]) {
|
||||
let out_dir = Path::new("rustdoc");
|
||||
let crate_name = "foobar";
|
||||
let deps = rfs::read_dir("examples")
|
||||
|
|
@ -27,7 +27,7 @@ pub fn scrape(extra_args: &[&str]) {
|
|||
.arg(&out_example)
|
||||
.arg("--scrape-examples-target-crate")
|
||||
.arg(crate_name)
|
||||
.args(extra_args)
|
||||
.args(extra_args_scrape)
|
||||
.run();
|
||||
out_deps.push(out_example);
|
||||
}
|
||||
|
|
@ -42,6 +42,7 @@ pub fn scrape(extra_args: &[&str]) {
|
|||
for dep in out_deps {
|
||||
rustdoc.arg("--with-examples").arg(dep);
|
||||
}
|
||||
rustdoc.args(extra_args_doc);
|
||||
rustdoc.run();
|
||||
|
||||
htmldocck().arg(out_dir).arg("src/lib.rs").run();
|
||||
|
|
|
|||
|
|
@ -3,5 +3,5 @@
|
|||
mod scrape;
|
||||
|
||||
fn main() {
|
||||
scrape::scrape(&["--scrape-tests"]);
|
||||
scrape::scrape(&["--scrape-tests"], &[]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,5 +3,5 @@
|
|||
mod scrape;
|
||||
|
||||
fn main() {
|
||||
scrape::scrape(&[]);
|
||||
scrape::scrape(&[], &[]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,4 +25,43 @@ fn main() {
|
|||
.target("aarch64-unknown-none-softfloat")
|
||||
.arg("-Zfixed-x18")
|
||||
.run();
|
||||
|
||||
rustdoc()
|
||||
.input("c.rs")
|
||||
.crate_type("rlib")
|
||||
.extern_("d", "libd.rmeta")
|
||||
.target("aarch64-unknown-none-softfloat")
|
||||
.arg("-Zfixed-x18")
|
||||
.arg("--test")
|
||||
.run();
|
||||
|
||||
rustdoc()
|
||||
.input("c.rs")
|
||||
.edition("2024")
|
||||
.crate_type("rlib")
|
||||
.extern_("d", "libd.rmeta")
|
||||
.target("aarch64-unknown-none-softfloat")
|
||||
.arg("-Zfixed-x18")
|
||||
.arg("--test")
|
||||
.run();
|
||||
|
||||
// rustdoc --test detects ABI mismatch
|
||||
rustdoc()
|
||||
.input("c.rs")
|
||||
.crate_type("rlib")
|
||||
.extern_("d", "libd.rmeta")
|
||||
.target("aarch64-unknown-none-softfloat")
|
||||
.arg("--test")
|
||||
.run_fail()
|
||||
.assert_stderr_contains("mixing `-Zfixed-x18` will cause an ABI mismatch");
|
||||
|
||||
// rustdoc --test -Cunsafe-allow-abi-mismatch=... ignores the mismatch
|
||||
rustdoc()
|
||||
.input("c.rs")
|
||||
.crate_type("rlib")
|
||||
.extern_("d", "libd.rmeta")
|
||||
.target("aarch64-unknown-none-softfloat")
|
||||
.arg("--test")
|
||||
.arg("-Cunsafe-allow-abi-mismatch=fixed-x18")
|
||||
.run();
|
||||
}
|
||||
|
|
|
|||
113
tests/ui/did_you_mean/c-style-pointer-types.fixed
Normal file
113
tests/ui/did_you_mean/c-style-pointer-types.fixed
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
//@ run-rustfix
|
||||
|
||||
#![allow(unused)]
|
||||
|
||||
pub const P1: *const u8 = 0 as _;
|
||||
//~^ ERROR: raw pointer types must be written as `*const T`
|
||||
//~| HELP: put the `*` before `const`
|
||||
|
||||
pub const P2: *mut u8 = 1 as _;
|
||||
//~^ ERROR: raw pointer types must be written as `*mut T`
|
||||
//~| HELP: put the `*` before `mut`
|
||||
|
||||
pub const P3: *const i32 = std::ptr::null();
|
||||
//~^ ERROR: raw pointer types must be written as `*const T`
|
||||
//~| HELP: put the `*` before `const`
|
||||
|
||||
pub const P4: *const i32 = std::ptr::null();
|
||||
//~^ ERROR: raw pointer types must be written as `*const T`
|
||||
//~| HELP: put the `*` before `const`
|
||||
|
||||
pub const P5: *mut i32 = std::ptr::null_mut();
|
||||
//~^ ERROR: raw pointer types must be written as `*mut T`
|
||||
//~| HELP: put the `*` before `mut`
|
||||
|
||||
pub const P6: *mut i32 = std::ptr::null_mut();
|
||||
//~^ ERROR: raw pointer types must be written as `*mut T`
|
||||
//~| HELP: put the `*` before `mut`
|
||||
|
||||
pub const P7: *const Vec<u8> = std::ptr::null();
|
||||
//~^ ERROR: raw pointer types must be written as `*const T`
|
||||
//~| HELP: put the `*` before `const`
|
||||
|
||||
pub const P8: *const std::collections::HashMap<String, i32> = std::ptr::null();
|
||||
//~^ ERROR: raw pointer types must be written as `*const T`
|
||||
//~| HELP: put the `*` before `const`
|
||||
|
||||
fn func1(p: *const u8) {}
|
||||
//~^ ERROR: raw pointer types must be written as `*const T`
|
||||
//~| HELP: put the `*` before `const`
|
||||
|
||||
fn func2(p: *mut u8) {}
|
||||
//~^ ERROR: raw pointer types must be written as `*mut T`
|
||||
//~| HELP: put the `*` before `mut`
|
||||
|
||||
fn func3() -> *const u8 { std::ptr::null() }
|
||||
//~^ ERROR: raw pointer types must be written as `*const T`
|
||||
//~| HELP: put the `*` before `const`
|
||||
|
||||
fn func4() -> *mut u8 { std::ptr::null_mut() }
|
||||
//~^ ERROR: raw pointer types must be written as `*mut T`
|
||||
//~| HELP: put the `*` before `mut`
|
||||
|
||||
struct S1 {
|
||||
field: *const u8,
|
||||
//~^ ERROR: raw pointer types must be written as `*const T`
|
||||
//~| HELP: put the `*` before `const`
|
||||
}
|
||||
|
||||
struct S2 {
|
||||
field: *mut u8,
|
||||
//~^ ERROR: raw pointer types must be written as `*mut T`
|
||||
//~| HELP: put the `*` before `mut`
|
||||
}
|
||||
|
||||
type Tuple1 = (*const u8, i32);
|
||||
//~^ ERROR: raw pointer types must be written as `*const T`
|
||||
//~| HELP: put the `*` before `const`
|
||||
|
||||
type Tuple2 = (*mut u8, i32);
|
||||
//~^ ERROR: raw pointer types must be written as `*mut T`
|
||||
//~| HELP: put the `*` before `mut`
|
||||
|
||||
type Array1 = [*const u8; 10];
|
||||
//~^ ERROR: raw pointer types must be written as `*const T`
|
||||
//~| HELP: put the `*` before `const`
|
||||
|
||||
type Array2 = [*mut u8; 10];
|
||||
//~^ ERROR: raw pointer types must be written as `*mut T`
|
||||
//~| HELP: put the `*` before `mut`
|
||||
|
||||
type Alias1 = *const u8;
|
||||
//~^ ERROR: raw pointer types must be written as `*const T`
|
||||
//~| HELP: put the `*` before `const`
|
||||
|
||||
type Alias2 = *mut u8;
|
||||
//~^ ERROR: raw pointer types must be written as `*mut T`
|
||||
//~| HELP: put the `*` before `mut`
|
||||
|
||||
pub const P9: *const u8 = std::ptr::null();
|
||||
//~^ ERROR: raw pointer types must be written as `*const T`
|
||||
//~| HELP: put the `*` before `const`
|
||||
|
||||
pub const P10: *const u8 = std::ptr::null();
|
||||
//~^ ERROR: raw pointer types must be written as `*const T`
|
||||
//~| HELP: put the `*` before `const`
|
||||
|
||||
impl S1 {
|
||||
fn method(self, size: *const u32) {}
|
||||
//~^ ERROR: raw pointer types must be written as `*const T`
|
||||
//~| HELP: put the `*` before `const`
|
||||
}
|
||||
|
||||
trait Trait1 {
|
||||
fn method(p: *const u8);
|
||||
//~^ ERROR: raw pointer types must be written as `*const T`
|
||||
//~| HELP: put the `*` before `const`
|
||||
}
|
||||
|
||||
fn generic_func<T>() -> *const T { std::ptr::null() }
|
||||
//~^ ERROR: raw pointer types must be written as `*const T`
|
||||
//~| HELP: put the `*` before `const`
|
||||
|
||||
fn main() {}
|
||||
113
tests/ui/did_you_mean/c-style-pointer-types.rs
Normal file
113
tests/ui/did_you_mean/c-style-pointer-types.rs
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
//@ run-rustfix
|
||||
|
||||
#![allow(unused)]
|
||||
|
||||
pub const P1: const* u8 = 0 as _;
|
||||
//~^ ERROR: raw pointer types must be written as `*const T`
|
||||
//~| HELP: put the `*` before `const`
|
||||
|
||||
pub const P2: mut* u8 = 1 as _;
|
||||
//~^ ERROR: raw pointer types must be written as `*mut T`
|
||||
//~| HELP: put the `*` before `mut`
|
||||
|
||||
pub const P3: const* i32 = std::ptr::null();
|
||||
//~^ ERROR: raw pointer types must be written as `*const T`
|
||||
//~| HELP: put the `*` before `const`
|
||||
|
||||
pub const P4: const* i32 = std::ptr::null();
|
||||
//~^ ERROR: raw pointer types must be written as `*const T`
|
||||
//~| HELP: put the `*` before `const`
|
||||
|
||||
pub const P5: mut* i32 = std::ptr::null_mut();
|
||||
//~^ ERROR: raw pointer types must be written as `*mut T`
|
||||
//~| HELP: put the `*` before `mut`
|
||||
|
||||
pub const P6: mut* i32 = std::ptr::null_mut();
|
||||
//~^ ERROR: raw pointer types must be written as `*mut T`
|
||||
//~| HELP: put the `*` before `mut`
|
||||
|
||||
pub const P7: const* Vec<u8> = std::ptr::null();
|
||||
//~^ ERROR: raw pointer types must be written as `*const T`
|
||||
//~| HELP: put the `*` before `const`
|
||||
|
||||
pub const P8: const* std::collections::HashMap<String, i32> = std::ptr::null();
|
||||
//~^ ERROR: raw pointer types must be written as `*const T`
|
||||
//~| HELP: put the `*` before `const`
|
||||
|
||||
fn func1(p: const* u8) {}
|
||||
//~^ ERROR: raw pointer types must be written as `*const T`
|
||||
//~| HELP: put the `*` before `const`
|
||||
|
||||
fn func2(p: mut* u8) {}
|
||||
//~^ ERROR: raw pointer types must be written as `*mut T`
|
||||
//~| HELP: put the `*` before `mut`
|
||||
|
||||
fn func3() -> const* u8 { std::ptr::null() }
|
||||
//~^ ERROR: raw pointer types must be written as `*const T`
|
||||
//~| HELP: put the `*` before `const`
|
||||
|
||||
fn func4() -> mut* u8 { std::ptr::null_mut() }
|
||||
//~^ ERROR: raw pointer types must be written as `*mut T`
|
||||
//~| HELP: put the `*` before `mut`
|
||||
|
||||
struct S1 {
|
||||
field: const* u8,
|
||||
//~^ ERROR: raw pointer types must be written as `*const T`
|
||||
//~| HELP: put the `*` before `const`
|
||||
}
|
||||
|
||||
struct S2 {
|
||||
field: mut* u8,
|
||||
//~^ ERROR: raw pointer types must be written as `*mut T`
|
||||
//~| HELP: put the `*` before `mut`
|
||||
}
|
||||
|
||||
type Tuple1 = (const* u8, i32);
|
||||
//~^ ERROR: raw pointer types must be written as `*const T`
|
||||
//~| HELP: put the `*` before `const`
|
||||
|
||||
type Tuple2 = (mut* u8, i32);
|
||||
//~^ ERROR: raw pointer types must be written as `*mut T`
|
||||
//~| HELP: put the `*` before `mut`
|
||||
|
||||
type Array1 = [const* u8; 10];
|
||||
//~^ ERROR: raw pointer types must be written as `*const T`
|
||||
//~| HELP: put the `*` before `const`
|
||||
|
||||
type Array2 = [mut* u8; 10];
|
||||
//~^ ERROR: raw pointer types must be written as `*mut T`
|
||||
//~| HELP: put the `*` before `mut`
|
||||
|
||||
type Alias1 = const* u8;
|
||||
//~^ ERROR: raw pointer types must be written as `*const T`
|
||||
//~| HELP: put the `*` before `const`
|
||||
|
||||
type Alias2 = mut* u8;
|
||||
//~^ ERROR: raw pointer types must be written as `*mut T`
|
||||
//~| HELP: put the `*` before `mut`
|
||||
|
||||
pub const P9: const *u8 = std::ptr::null();
|
||||
//~^ ERROR: raw pointer types must be written as `*const T`
|
||||
//~| HELP: put the `*` before `const`
|
||||
|
||||
pub const P10: const * u8 = std::ptr::null();
|
||||
//~^ ERROR: raw pointer types must be written as `*const T`
|
||||
//~| HELP: put the `*` before `const`
|
||||
|
||||
impl S1 {
|
||||
fn method(self, size: const* u32) {}
|
||||
//~^ ERROR: raw pointer types must be written as `*const T`
|
||||
//~| HELP: put the `*` before `const`
|
||||
}
|
||||
|
||||
trait Trait1 {
|
||||
fn method(p: const* u8);
|
||||
//~^ ERROR: raw pointer types must be written as `*const T`
|
||||
//~| HELP: put the `*` before `const`
|
||||
}
|
||||
|
||||
fn generic_func<T>() -> const* T { std::ptr::null() }
|
||||
//~^ ERROR: raw pointer types must be written as `*const T`
|
||||
//~| HELP: put the `*` before `const`
|
||||
|
||||
fn main() {}
|
||||
302
tests/ui/did_you_mean/c-style-pointer-types.stderr
Normal file
302
tests/ui/did_you_mean/c-style-pointer-types.stderr
Normal file
|
|
@ -0,0 +1,302 @@
|
|||
error: raw pointer types must be written as `*const T`
|
||||
--> $DIR/c-style-pointer-types.rs:5:15
|
||||
|
|
||||
LL | pub const P1: const* u8 = 0 as _;
|
||||
| ^^^^^
|
||||
|
|
||||
help: put the `*` before `const`
|
||||
|
|
||||
LL - pub const P1: const* u8 = 0 as _;
|
||||
LL + pub const P1: *const u8 = 0 as _;
|
||||
|
|
||||
|
||||
error: raw pointer types must be written as `*mut T`
|
||||
--> $DIR/c-style-pointer-types.rs:9:15
|
||||
|
|
||||
LL | pub const P2: mut* u8 = 1 as _;
|
||||
| ^^^
|
||||
|
|
||||
help: put the `*` before `mut`
|
||||
|
|
||||
LL - pub const P2: mut* u8 = 1 as _;
|
||||
LL + pub const P2: *mut u8 = 1 as _;
|
||||
|
|
||||
|
||||
error: raw pointer types must be written as `*const T`
|
||||
--> $DIR/c-style-pointer-types.rs:13:15
|
||||
|
|
||||
LL | pub const P3: const* i32 = std::ptr::null();
|
||||
| ^^^^^
|
||||
|
|
||||
help: put the `*` before `const`
|
||||
|
|
||||
LL - pub const P3: const* i32 = std::ptr::null();
|
||||
LL + pub const P3: *const i32 = std::ptr::null();
|
||||
|
|
||||
|
||||
error: raw pointer types must be written as `*const T`
|
||||
--> $DIR/c-style-pointer-types.rs:17:15
|
||||
|
|
||||
LL | pub const P4: const* i32 = std::ptr::null();
|
||||
| ^^^^^
|
||||
|
|
||||
help: put the `*` before `const`
|
||||
|
|
||||
LL - pub const P4: const* i32 = std::ptr::null();
|
||||
LL + pub const P4: *const i32 = std::ptr::null();
|
||||
|
|
||||
|
||||
error: raw pointer types must be written as `*mut T`
|
||||
--> $DIR/c-style-pointer-types.rs:21:15
|
||||
|
|
||||
LL | pub const P5: mut* i32 = std::ptr::null_mut();
|
||||
| ^^^
|
||||
|
|
||||
help: put the `*` before `mut`
|
||||
|
|
||||
LL - pub const P5: mut* i32 = std::ptr::null_mut();
|
||||
LL + pub const P5: *mut i32 = std::ptr::null_mut();
|
||||
|
|
||||
|
||||
error: raw pointer types must be written as `*mut T`
|
||||
--> $DIR/c-style-pointer-types.rs:25:15
|
||||
|
|
||||
LL | pub const P6: mut* i32 = std::ptr::null_mut();
|
||||
| ^^^
|
||||
|
|
||||
help: put the `*` before `mut`
|
||||
|
|
||||
LL - pub const P6: mut* i32 = std::ptr::null_mut();
|
||||
LL + pub const P6: *mut i32 = std::ptr::null_mut();
|
||||
|
|
||||
|
||||
error: raw pointer types must be written as `*const T`
|
||||
--> $DIR/c-style-pointer-types.rs:29:15
|
||||
|
|
||||
LL | pub const P7: const* Vec<u8> = std::ptr::null();
|
||||
| ^^^^^
|
||||
|
|
||||
help: put the `*` before `const`
|
||||
|
|
||||
LL - pub const P7: const* Vec<u8> = std::ptr::null();
|
||||
LL + pub const P7: *const Vec<u8> = std::ptr::null();
|
||||
|
|
||||
|
||||
error: raw pointer types must be written as `*const T`
|
||||
--> $DIR/c-style-pointer-types.rs:33:15
|
||||
|
|
||||
LL | pub const P8: const* std::collections::HashMap<String, i32> = std::ptr::null();
|
||||
| ^^^^^
|
||||
|
|
||||
help: put the `*` before `const`
|
||||
|
|
||||
LL - pub const P8: const* std::collections::HashMap<String, i32> = std::ptr::null();
|
||||
LL + pub const P8: *const std::collections::HashMap<String, i32> = std::ptr::null();
|
||||
|
|
||||
|
||||
error: raw pointer types must be written as `*const T`
|
||||
--> $DIR/c-style-pointer-types.rs:37:13
|
||||
|
|
||||
LL | fn func1(p: const* u8) {}
|
||||
| ^^^^^
|
||||
|
|
||||
help: put the `*` before `const`
|
||||
|
|
||||
LL - fn func1(p: const* u8) {}
|
||||
LL + fn func1(p: *const u8) {}
|
||||
|
|
||||
|
||||
error: raw pointer types must be written as `*mut T`
|
||||
--> $DIR/c-style-pointer-types.rs:41:13
|
||||
|
|
||||
LL | fn func2(p: mut* u8) {}
|
||||
| ^^^
|
||||
|
|
||||
help: put the `*` before `mut`
|
||||
|
|
||||
LL - fn func2(p: mut* u8) {}
|
||||
LL + fn func2(p: *mut u8) {}
|
||||
|
|
||||
|
||||
error: raw pointer types must be written as `*const T`
|
||||
--> $DIR/c-style-pointer-types.rs:45:15
|
||||
|
|
||||
LL | fn func3() -> const* u8 { std::ptr::null() }
|
||||
| ^^^^^
|
||||
|
|
||||
help: put the `*` before `const`
|
||||
|
|
||||
LL - fn func3() -> const* u8 { std::ptr::null() }
|
||||
LL + fn func3() -> *const u8 { std::ptr::null() }
|
||||
|
|
||||
|
||||
error: raw pointer types must be written as `*mut T`
|
||||
--> $DIR/c-style-pointer-types.rs:49:15
|
||||
|
|
||||
LL | fn func4() -> mut* u8 { std::ptr::null_mut() }
|
||||
| ^^^
|
||||
|
|
||||
help: put the `*` before `mut`
|
||||
|
|
||||
LL - fn func4() -> mut* u8 { std::ptr::null_mut() }
|
||||
LL + fn func4() -> *mut u8 { std::ptr::null_mut() }
|
||||
|
|
||||
|
||||
error: raw pointer types must be written as `*const T`
|
||||
--> $DIR/c-style-pointer-types.rs:54:12
|
||||
|
|
||||
LL | field: const* u8,
|
||||
| ^^^^^
|
||||
|
|
||||
help: put the `*` before `const`
|
||||
|
|
||||
LL - field: const* u8,
|
||||
LL + field: *const u8,
|
||||
|
|
||||
|
||||
error: raw pointer types must be written as `*mut T`
|
||||
--> $DIR/c-style-pointer-types.rs:60:12
|
||||
|
|
||||
LL | field: mut* u8,
|
||||
| ^^^
|
||||
|
|
||||
help: put the `*` before `mut`
|
||||
|
|
||||
LL - field: mut* u8,
|
||||
LL + field: *mut u8,
|
||||
|
|
||||
|
||||
error: raw pointer types must be written as `*const T`
|
||||
--> $DIR/c-style-pointer-types.rs:65:16
|
||||
|
|
||||
LL | type Tuple1 = (const* u8, i32);
|
||||
| ^^^^^
|
||||
|
|
||||
help: put the `*` before `const`
|
||||
|
|
||||
LL - type Tuple1 = (const* u8, i32);
|
||||
LL + type Tuple1 = (*const u8, i32);
|
||||
|
|
||||
|
||||
error: raw pointer types must be written as `*mut T`
|
||||
--> $DIR/c-style-pointer-types.rs:69:16
|
||||
|
|
||||
LL | type Tuple2 = (mut* u8, i32);
|
||||
| ^^^
|
||||
|
|
||||
help: put the `*` before `mut`
|
||||
|
|
||||
LL - type Tuple2 = (mut* u8, i32);
|
||||
LL + type Tuple2 = (*mut u8, i32);
|
||||
|
|
||||
|
||||
error: raw pointer types must be written as `*const T`
|
||||
--> $DIR/c-style-pointer-types.rs:73:16
|
||||
|
|
||||
LL | type Array1 = [const* u8; 10];
|
||||
| ^^^^^
|
||||
|
|
||||
help: put the `*` before `const`
|
||||
|
|
||||
LL - type Array1 = [const* u8; 10];
|
||||
LL + type Array1 = [*const u8; 10];
|
||||
|
|
||||
|
||||
error: raw pointer types must be written as `*mut T`
|
||||
--> $DIR/c-style-pointer-types.rs:77:16
|
||||
|
|
||||
LL | type Array2 = [mut* u8; 10];
|
||||
| ^^^
|
||||
|
|
||||
help: put the `*` before `mut`
|
||||
|
|
||||
LL - type Array2 = [mut* u8; 10];
|
||||
LL + type Array2 = [*mut u8; 10];
|
||||
|
|
||||
|
||||
error: raw pointer types must be written as `*const T`
|
||||
--> $DIR/c-style-pointer-types.rs:81:15
|
||||
|
|
||||
LL | type Alias1 = const* u8;
|
||||
| ^^^^^
|
||||
|
|
||||
help: put the `*` before `const`
|
||||
|
|
||||
LL - type Alias1 = const* u8;
|
||||
LL + type Alias1 = *const u8;
|
||||
|
|
||||
|
||||
error: raw pointer types must be written as `*mut T`
|
||||
--> $DIR/c-style-pointer-types.rs:85:15
|
||||
|
|
||||
LL | type Alias2 = mut* u8;
|
||||
| ^^^
|
||||
|
|
||||
help: put the `*` before `mut`
|
||||
|
|
||||
LL - type Alias2 = mut* u8;
|
||||
LL + type Alias2 = *mut u8;
|
||||
|
|
||||
|
||||
error: raw pointer types must be written as `*const T`
|
||||
--> $DIR/c-style-pointer-types.rs:89:15
|
||||
|
|
||||
LL | pub const P9: const *u8 = std::ptr::null();
|
||||
| ^^^^^
|
||||
|
|
||||
help: put the `*` before `const`
|
||||
|
|
||||
LL - pub const P9: const *u8 = std::ptr::null();
|
||||
LL + pub const P9: *const u8 = std::ptr::null();
|
||||
|
|
||||
|
||||
error: raw pointer types must be written as `*const T`
|
||||
--> $DIR/c-style-pointer-types.rs:93:16
|
||||
|
|
||||
LL | pub const P10: const * u8 = std::ptr::null();
|
||||
| ^^^^^
|
||||
|
|
||||
help: put the `*` before `const`
|
||||
|
|
||||
LL - pub const P10: const * u8 = std::ptr::null();
|
||||
LL + pub const P10: *const u8 = std::ptr::null();
|
||||
|
|
||||
|
||||
error: raw pointer types must be written as `*const T`
|
||||
--> $DIR/c-style-pointer-types.rs:98:27
|
||||
|
|
||||
LL | fn method(self, size: const* u32) {}
|
||||
| ^^^^^
|
||||
|
|
||||
help: put the `*` before `const`
|
||||
|
|
||||
LL - fn method(self, size: const* u32) {}
|
||||
LL + fn method(self, size: *const u32) {}
|
||||
|
|
||||
|
||||
error: raw pointer types must be written as `*const T`
|
||||
--> $DIR/c-style-pointer-types.rs:104:18
|
||||
|
|
||||
LL | fn method(p: const* u8);
|
||||
| ^^^^^
|
||||
|
|
||||
help: put the `*` before `const`
|
||||
|
|
||||
LL - fn method(p: const* u8);
|
||||
LL + fn method(p: *const u8);
|
||||
|
|
||||
|
||||
error: raw pointer types must be written as `*const T`
|
||||
--> $DIR/c-style-pointer-types.rs:109:25
|
||||
|
|
||||
LL | fn generic_func<T>() -> const* T { std::ptr::null() }
|
||||
| ^^^^^
|
||||
|
|
||||
help: put the `*` before `const`
|
||||
|
|
||||
LL - fn generic_func<T>() -> const* T { std::ptr::null() }
|
||||
LL + fn generic_func<T>() -> *const T { std::ptr::null() }
|
||||
|
|
||||
|
||||
error: aborting due to 25 previous errors
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue