miri: reduce code duplication in SSE2 pavg.b and pavg.w

This commit is contained in:
Eduardo Sánchez Muñoz 2023-09-13 21:51:40 +02:00
parent 4bb8a8b804
commit 8d590f2fa8
2 changed files with 53 additions and 39 deletions

View file

@ -14,7 +14,7 @@ use rustc_middle::mir;
use rustc_middle::ty::{
self,
layout::{IntegerExt as _, LayoutOf, TyAndLayout},
Ty, TyCtxt,
IntTy, Ty, TyCtxt, UintTy,
};
use rustc_span::{def_id::CrateNum, sym, Span, Symbol};
use rustc_target::abi::{Align, FieldIdx, FieldsShape, Integer, Size, Variants};
@ -1067,6 +1067,24 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
),
}
}
/// Returns an integer type that is twice wide as `ty`
fn get_twice_wide_int_ty(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
let this = self.eval_context_ref();
match ty.kind() {
// Unsigned
ty::Uint(UintTy::U8) => this.tcx.types.u16,
ty::Uint(UintTy::U16) => this.tcx.types.u32,
ty::Uint(UintTy::U32) => this.tcx.types.u64,
ty::Uint(UintTy::U64) => this.tcx.types.u128,
// Signed
ty::Int(IntTy::I8) => this.tcx.types.i16,
ty::Int(IntTy::I16) => this.tcx.types.i32,
ty::Int(IntTy::I32) => this.tcx.types.i64,
ty::Int(IntTy::I64) => this.tcx.types.i128,
_ => span_bug!(this.cur_span(), "unexpected type: {ty:?}"),
}
}
}
impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {

View file

@ -2,6 +2,7 @@ use rustc_apfloat::{
ieee::{Double, Single},
Float as _,
};
use rustc_middle::mir;
use rustc_middle::ty::layout::LayoutOf as _;
use rustc_middle::ty::Ty;
use rustc_span::Symbol;
@ -36,9 +37,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
// Intrinsincs sufixed with "epiX" or "epuX" operate with X-bit signed or unsigned
// vectors.
match unprefixed_name {
// Used to implement the _mm_avg_epu8 function.
// Averages packed unsigned 8-bit integers in `left` and `right`.
"pavg.b" => {
// Used to implement the _mm_avg_epu8 and _mm_avg_epu16 functions.
// Averages packed unsigned 8/16-bit integers in `left` and `right`.
"pavg.b" | "pavg.w" => {
let [left, right] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
@ -50,46 +51,41 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
assert_eq!(dest_len, right_len);
for i in 0..dest_len {
let left = this.read_scalar(&this.project_index(&left, i)?)?.to_u8()?;
let right = this.read_scalar(&this.project_index(&right, i)?)?.to_u8()?;
let left = this.read_immediate(&this.project_index(&left, i)?)?;
let right = this.read_immediate(&this.project_index(&right, i)?)?;
let dest = this.project_index(&dest, i)?;
// Values are expanded from u8 to u16, so adds cannot overflow.
let res = u16::from(left)
.checked_add(u16::from(right))
.unwrap()
.checked_add(1)
.unwrap()
/ 2;
this.write_scalar(Scalar::from_u8(res.try_into().unwrap()), &dest)?;
}
}
// Used to implement the _mm_avg_epu16 function.
// Averages packed unsigned 16-bit integers in `left` and `right`.
"pavg.w" => {
let [left, right] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
// Widen the operands to avoid overflow
let twice_wide_ty = this.get_twice_wide_int_ty(left.layout.ty);
let twice_wide_layout = this.layout_of(twice_wide_ty)?;
let left = this.int_to_int_or_float(&left, twice_wide_ty)?;
let right = this.int_to_int_or_float(&right, twice_wide_ty)?;
let (left, left_len) = this.operand_to_simd(left)?;
let (right, right_len) = this.operand_to_simd(right)?;
let (dest, dest_len) = this.place_to_simd(dest)?;
// Calculate left + right + 1
let (added, _overflow, _ty) = this.overflowing_binary_op(
mir::BinOp::Add,
&ImmTy::from_immediate(left, twice_wide_layout),
&ImmTy::from_immediate(right, twice_wide_layout),
)?;
let (added, _overflow, _ty) = this.overflowing_binary_op(
mir::BinOp::Add,
&ImmTy::from_scalar(added, twice_wide_layout),
&ImmTy::from_uint(1u32, twice_wide_layout),
)?;
assert_eq!(dest_len, left_len);
assert_eq!(dest_len, right_len);
// Calculate (left + right + 1) / 2
let (divided, _overflow, _ty) = this.overflowing_binary_op(
mir::BinOp::Div,
&ImmTy::from_scalar(added, twice_wide_layout),
&ImmTy::from_uint(2u32, twice_wide_layout),
)?;
for i in 0..dest_len {
let left = this.read_scalar(&this.project_index(&left, i)?)?.to_u16()?;
let right = this.read_scalar(&this.project_index(&right, i)?)?.to_u16()?;
let dest = this.project_index(&dest, i)?;
// Values are expanded from u16 to u32, so adds cannot overflow.
let res = u32::from(left)
.checked_add(u32::from(right))
.unwrap()
.checked_add(1)
.unwrap()
/ 2;
this.write_scalar(Scalar::from_u16(res.try_into().unwrap()), &dest)?;
// Narrow back to the original type
let res = this.int_to_int_or_float(
&ImmTy::from_scalar(divided, twice_wide_layout),
dest.layout.ty,
)?;
this.write_immediate(res, &dest)?;
}
}
// Used to implement the _mm_mulhi_epi16 function.