Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 47 additions & 53 deletions src/tools/miri/src/intrinsics/x86/avx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, EmulateItemResult> {
let this = self.eval_context_mut();
this.expect_target_feature_for_intrinsic(link_name, "avx")?;
let ecx = self.eval_context_mut();
ecx.expect_target_feature_for_intrinsic(link_name, "avx")?;
// Prefix should have already been checked.
let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.avx.").unwrap();

Expand All @@ -27,90 +27,90 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// matches the IEEE min/max operations, while x86 has different
// semantics.
"min.ps.256" | "max.ps.256" => {
let [left, right] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
let [left, right] = ecx.check_shim_sig_llvm_intrinsic(link_name, args)?;

let which = match unprefixed_name {
"min.ps.256" => FloatBinOp::Min,
"max.ps.256" => FloatBinOp::Max,
_ => unreachable!(),
};

bin_op_simd_float_all::<Single>(this, which, left, right, dest)?;
bin_op_simd_float_all::<Single>(ecx, which, left, right, dest)?;
}
// Used to implement _mm256_min_pd and _mm256_max_pd functions.
"min.pd.256" | "max.pd.256" => {
let [left, right] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
let [left, right] = ecx.check_shim_sig_llvm_intrinsic(link_name, args)?;

let which = match unprefixed_name {
"min.pd.256" => FloatBinOp::Min,
"max.pd.256" => FloatBinOp::Max,
_ => unreachable!(),
};

bin_op_simd_float_all::<Double>(this, which, left, right, dest)?;
bin_op_simd_float_all::<Double>(ecx, which, left, right, dest)?;
}
// Used to implement the _mm256_round_ps function.
// Rounds the elements of `op` according to `rounding`.
"round.ps.256" => {
let [op, rounding] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
let [op, rounding] = ecx.check_shim_sig_llvm_intrinsic(link_name, args)?;

round_all::<rustc_apfloat::ieee::Single>(this, op, rounding, dest)?;
round_all::<rustc_apfloat::ieee::Single>(ecx, op, rounding, dest)?;
}
// Used to implement the _mm256_round_pd function.
// Rounds the elements of `op` according to `rounding`.
"round.pd.256" => {
let [op, rounding] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
let [op, rounding] = ecx.check_shim_sig_llvm_intrinsic(link_name, args)?;

round_all::<rustc_apfloat::ieee::Double>(this, op, rounding, dest)?;
round_all::<rustc_apfloat::ieee::Double>(ecx, op, rounding, dest)?;
}
// Used to implement _mm256_{rcp,rsqrt}_ps functions.
// Performs the operations on all components of `op`.
"rcp.ps.256" | "rsqrt.ps.256" => {
let [op] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
let [op] = ecx.check_shim_sig_llvm_intrinsic(link_name, args)?;

let which = match unprefixed_name {
"rcp.ps.256" => FloatUnaryOp::Rcp,
"rsqrt.ps.256" => FloatUnaryOp::Rsqrt,
_ => unreachable!(),
};

unary_op_ps(this, which, op, dest)?;
unary_op_ps(ecx, which, op, dest)?;
}
// Used to implement the _mm256_dp_ps function.
"dp.ps.256" => {
let [left, right, imm] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
let [left, right, imm] = ecx.check_shim_sig_llvm_intrinsic(link_name, args)?;

conditional_dot_product(this, left, right, imm, dest)?;
conditional_dot_product(ecx, left, right, imm, dest)?;
}
// Used to implement the _mm256_cmp_ps function.
// Performs a comparison operation on each component of `left`
// and `right`. For each component, returns 0 if false or u32::MAX
// if true.
"cmp.ps.256" => {
let [left, right, imm] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
let [left, right, imm] = ecx.check_shim_sig_llvm_intrinsic(link_name, args)?;

let which =
FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
FloatBinOp::cmp_from_imm(ecx, ecx.read_scalar(imm)?.to_i8()?, link_name)?;

bin_op_simd_float_all::<Single>(this, which, left, right, dest)?;
bin_op_simd_float_all::<Single>(ecx, which, left, right, dest)?;
}
// Used to implement the _mm256_cmp_pd function.
// Performs a comparison operation on each component of `left`
// and `right`. For each component, returns 0 if false or u64::MAX
// if true.
"cmp.pd.256" => {
let [left, right, imm] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
let [left, right, imm] = ecx.check_shim_sig_llvm_intrinsic(link_name, args)?;

let which =
FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
FloatBinOp::cmp_from_imm(ecx, ecx.read_scalar(imm)?.to_i8()?, link_name)?;

bin_op_simd_float_all::<Double>(this, which, left, right, dest)?;
bin_op_simd_float_all::<Double>(ecx, which, left, right, dest)?;
}
// Used to implement the _mm256_cvtps_epi32, _mm256_cvttps_epi32, _mm256_cvtpd_epi32
// and _mm256_cvttpd_epi32 functions.
// Converts packed f32/f64 to packed i32.
"cvt.ps2dq.256" | "cvtt.ps2dq.256" | "cvt.pd2dq.256" | "cvtt.pd2dq.256" => {
let [op] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
let [op] = ecx.check_shim_sig_llvm_intrinsic(link_name, args)?;

let rnd = match unprefixed_name {
// "current SSE rounding mode", assume nearest
Expand All @@ -120,38 +120,35 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
_ => unreachable!(),
};

convert_float_to_int(this, op, rnd, dest)?;
convert_float_to_int(ecx, op, rnd, dest)?;
}
// Used to implement the _mm_permutevar_ps and _mm256_permutevar_ps functions.
// Shuffles 32-bit floats from `data` using `control` as control. Each 128-bit
// chunk is shuffled independently: this means that we view the vector as a
// sequence of 4-element arrays, and we shuffle each of these arrays, where
// `control` determines which element of the current `data` array is written.
"vpermilvar.ps" | "vpermilvar.ps.256" => {
let [data, control] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
let [data, control] = ecx.check_shim_sig_llvm_intrinsic(link_name, args)?;

let (data, data_len) = this.project_to_simd(data)?;
let (control, control_len) = this.project_to_simd(control)?;
let (dest, dest_len) = this.project_to_simd(dest)?;
let (data, data_len) = ecx.project_to_simd(data)?;
let (control, control_len) = ecx.project_to_simd(control)?;
let (dest, dest_len) = ecx.project_to_simd(dest)?;

assert_eq!(dest_len, data_len);
assert_eq!(dest_len, control_len);

for i in 0..dest_len {
let control = this.project_index(&control, i)?;
let control = ecx.project_index(&control, i)?;

// Each 128-bit chunk is shuffled independently. Since each chunk contains
// four 32-bit elements, only two bits from `control` are used. To read the
// value from the current chunk, add the destination index truncated to a multiple
// of 4.
let chunk_base = i & !0b11;
let src_i = u64::from(this.read_scalar(&control)?.to_u32()? & 0b11)
let src_i = u64::from(ecx.read_scalar(&control)?.to_u32()? & 0b11)
.strict_add(chunk_base);

this.copy_op(
&this.project_index(&data, src_i)?,
&this.project_index(&dest, i)?,
)?;
ecx.copy_op(&ecx.project_index(&data, src_i)?, &ecx.project_index(&dest, i)?)?;
}
}
// Used to implement the _mm_permutevar_pd and _mm256_permutevar_pd functions.
Expand All @@ -161,30 +158,27 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// where `right` determines which element of the current `left` array is
// written.
"vpermilvar.pd" | "vpermilvar.pd.256" => {
let [data, control] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
let [data, control] = ecx.check_shim_sig_llvm_intrinsic(link_name, args)?;

let (data, data_len) = this.project_to_simd(data)?;
let (control, control_len) = this.project_to_simd(control)?;
let (dest, dest_len) = this.project_to_simd(dest)?;
let (data, data_len) = ecx.project_to_simd(data)?;
let (control, control_len) = ecx.project_to_simd(control)?;
let (dest, dest_len) = ecx.project_to_simd(dest)?;

assert_eq!(dest_len, data_len);
assert_eq!(dest_len, control_len);

for i in 0..dest_len {
let control = this.project_index(&control, i)?;
let control = ecx.project_index(&control, i)?;

// Each 128-bit chunk is shuffled independently. Since each chunk contains
// two 64-bit elements, only the second bit from `control` is used (yes, the
// second instead of the first, ask Intel). To read the value from the current
// chunk, add the destination index truncated to a multiple of 2.
let chunk_base = i & !1;
let src_i =
((this.read_scalar(&control)?.to_u64()? >> 1) & 1).strict_add(chunk_base);
((ecx.read_scalar(&control)?.to_u64()? >> 1) & 1).strict_add(chunk_base);

this.copy_op(
&this.project_index(&data, src_i)?,
&this.project_index(&dest, i)?,
)?;
ecx.copy_op(&ecx.project_index(&data, src_i)?, &ecx.project_index(&dest, i)?)?;
}
}
// Used to implement the _mm256_lddqu_si256 function.
Expand All @@ -193,22 +187,22 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// the data crosses a cache line, but for Miri this is just a regular
// unaligned read.
"ldu.dq.256" => {
let [src_ptr] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
let src_ptr = this.read_pointer(src_ptr)?;
let dest = dest.force_mplace(this)?;
let [src_ptr] = ecx.check_shim_sig_llvm_intrinsic(link_name, args)?;
let src_ptr = ecx.read_pointer(src_ptr)?;
let dest = dest.force_mplace(ecx)?;

// Unaligned copy, which is what we want.
this.mem_copy(src_ptr, dest.ptr(), dest.layout.size, /*nonoverlapping*/ true)?;
ecx.mem_copy(src_ptr, dest.ptr(), dest.layout.size, /*nonoverlapping*/ true)?;
}
// Used to implement the _mm256_testnzc_si256 function.
// Tests `op & mask != 0 && op & mask != mask`
"ptestnzc.256" => {
let [op, mask] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
let [op, mask] = ecx.check_shim_sig_llvm_intrinsic(link_name, args)?;

let (all_zero, masked_set) = test_bits_masked(this, op, mask)?;
let (all_zero, masked_set) = test_bits_masked(ecx, op, mask)?;
let res = !all_zero && !masked_set;

this.write_scalar(Scalar::from_i32(res.into()), dest)?;
ecx.write_scalar(Scalar::from_i32(res.into()), dest)?;
}
// Used to implement the _mm256_testz_pd, _mm256_testc_pd, _mm256_testnzc_pd
// _mm_testnzc_pd, _mm256_testz_ps, _mm256_testc_ps, _mm256_testnzc_ps and
Expand All @@ -219,9 +213,9 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Return `direct` (testz), `negated` (testc) or `!direct & !negated` (testnzc)
"vtestz.pd.256" | "vtestc.pd.256" | "vtestnzc.pd.256" | "vtestnzc.pd"
| "vtestz.ps.256" | "vtestc.ps.256" | "vtestnzc.ps.256" | "vtestnzc.ps" => {
let [op, mask] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
let [op, mask] = ecx.check_shim_sig_llvm_intrinsic(link_name, args)?;

let (direct, negated) = test_high_bits_masked(this, op, mask)?;
let (direct, negated) = test_high_bits_masked(ecx, op, mask)?;
let res = match unprefixed_name {
"vtestz.pd.256" | "vtestz.ps.256" => direct,
"vtestc.pd.256" | "vtestc.ps.256" => negated,
Expand All @@ -230,7 +224,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
_ => unreachable!(),
};

this.write_scalar(Scalar::from_i32(res.into()), dest)?;
ecx.write_scalar(Scalar::from_i32(res.into()), dest)?;
}
// Used to implement the `_mm256_zeroupper` and `_mm256_zeroall` functions.
// These function clear out the upper 128 bits of all avx registers or
Expand All @@ -241,7 +235,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// compiler, making these functions no-ops.

// The only thing that needs to be ensured is the correct calling convention.
let [] = this.check_shim_sig_llvm_intrinsic(link_name, args)?;
let [] = ecx.check_shim_sig_llvm_intrinsic(link_name, args)?;
}
_ => return interp_ok(EmulateItemResult::NotSupported),
}
Expand Down
Loading
Loading