@@ -105,6 +105,13 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
105105 }
106106 }
107107
108+ "pclmulqdq" => {
109+ let [ left, right, imm] =
110+ this. check_shim ( abi, Abi :: C { unwind : false } , link_name, args) ?;
111+
112+ pclmulqdq ( this, left, right, imm, dest) ?;
113+ }
114+
108115 name if name. starts_with ( "sse." ) => {
109116 return sse:: EvalContextExt :: emulate_x86_sse_intrinsic (
110117 this, link_name, abi, args, dest,
@@ -1133,6 +1140,68 @@ fn pmulhrsw<'tcx>(
11331140 Ok ( ( ) )
11341141}
11351142
1143+ /// Perform a carry-less multiplication of two 64-bit integers, selected from `left` and `right` according to `imm8`,
1144+ /// and store the results in `dst`.
1145+ ///
1146+ /// `left` and `right` are both vectors of type 2 x i64. Only bits 0 and 4 of `imm8` matter;
1147+ /// they select the element of `left` and `right`, respectively.
1148+ ///
1149+ /// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_clmulepi64_si128>
1150+ fn pclmulqdq < ' tcx > (
1151+ this : & mut MiriInterpCx < ' tcx > ,
1152+ left : & OpTy < ' tcx > ,
1153+ right : & OpTy < ' tcx > ,
1154+ imm8 : & OpTy < ' tcx > ,
1155+ dest : & MPlaceTy < ' tcx > ,
1156+ ) -> InterpResult < ' tcx , ( ) > {
1157+ assert_eq ! ( left. layout, right. layout) ;
1158+ assert_eq ! ( left. layout. size, dest. layout. size) ;
1159+
1160+ // Transmute to `[u64; 2]`
1161+
1162+ let array_layout = this. layout_of ( Ty :: new_array ( this. tcx . tcx , this. tcx . types . u64 , 2 ) ) ?;
1163+ let left = left. transmute ( array_layout, this) ?;
1164+ let right = right. transmute ( array_layout, this) ?;
1165+ let dest = dest. transmute ( array_layout, this) ?;
1166+
1167+ let imm8 = this. read_scalar ( imm8) ?. to_u8 ( ) ?;
1168+
1169+ // select the 64-bit integer from left that the user specified (low or high)
1170+ let index = if ( imm8 & 0x01 ) == 0 { 0 } else { 1 } ;
1171+ let left = this. read_scalar ( & this. project_index ( & left, index) ?) ?. to_u64 ( ) ?;
1172+
1173+ // select the 64-bit integer from right that the user specified (low or high)
1174+ let index = if ( imm8 & 0x10 ) == 0 { 0 } else { 1 } ;
1175+ let right = this. read_scalar ( & this. project_index ( & right, index) ?) ?. to_u64 ( ) ?;
1176+
1177+ // Perform carry-less multiplication
1178+ //
1179+ // This operation is like long multiplication, but ignores all carries.
1180+ // That idea corresponds to the xor operator, which is used in the implementation.
1181+ //
1182+ // Wikipedia has an example https://en.wikipedia.org/wiki/Carry-less_product#Example
1183+ let mut result: u128 = 0 ;
1184+
1185+ for i in 0 ..64 {
1186+ // if the i-th bit in right is set
1187+ if ( right & ( 1 << i) ) != 0 {
1188+ // xor result with `left` shifted to the left by i positions
1189+ result ^= ( left as u128 ) << i;
1190+ }
1191+ }
1192+
1193+ let result_low = ( result & 0xFFFF_FFFF_FFFF_FFFF ) as u64 ;
1194+ let result_high = ( result >> 64 ) as u64 ;
1195+
1196+ let dest_low = this. project_index ( & dest, 0 ) ?;
1197+ this. write_scalar ( Scalar :: from_u64 ( result_low) , & dest_low) ?;
1198+
1199+ let dest_high = this. project_index ( & dest, 1 ) ?;
1200+ this. write_scalar ( Scalar :: from_u64 ( result_high) , & dest_high) ?;
1201+
1202+ Ok ( ( ) )
1203+ }
1204+
11361205/// Packs two N-bit integer vectors to a single N/2-bit integers.
11371206///
11381207/// The conversion from N-bit to N/2-bit should be provided by `f`.
0 commit comments