@@ -105,6 +105,13 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
105
105
}
106
106
}
107
107
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
+
108
115
name if name. starts_with ( "sse." ) => {
109
116
return sse:: EvalContextExt :: emulate_x86_sse_intrinsic (
110
117
this, link_name, abi, args, dest,
@@ -1133,6 +1140,68 @@ fn pmulhrsw<'tcx>(
1133
1140
Ok ( ( ) )
1134
1141
}
1135
1142
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
+
1136
1205
/// Packs two N-bit integer vectors to a single N/2-bit integers.
1137
1206
///
1138
1207
/// The conversion from N-bit to N/2-bit should be provided by `f`.
0 commit comments