Skip to content

Commit f564229

Browse files
committed
implement carryless_mul
1 parent 55407b8 commit f564229

12 files changed

Lines changed: 163 additions & 4 deletions

File tree

compiler/rustc_codegen_llvm/src/intrinsic.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
396396
| sym::bitreverse
397397
| sym::saturating_add
398398
| sym::saturating_sub
399+
| sym::carryless_mul
399400
| sym::unchecked_funnel_shl
400401
| sym::unchecked_funnel_shr => {
401402
let ty = args[0].layout.ty;
@@ -438,6 +439,11 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
438439
sym::bitreverse => {
439440
self.call_intrinsic("llvm.bitreverse", &[llty], &[args[0].immediate()])
440441
}
442+
sym::carryless_mul => {
443+
let lhs = args[0].immediate();
444+
let rhs = args[1].immediate();
445+
self.call_intrinsic("llvm.clmul", &[llty], &[lhs, rhs])
446+
}
441447
sym::unchecked_funnel_shl | sym::unchecked_funnel_shr => {
442448
let is_left = name == sym::unchecked_funnel_shl;
443449
let lhs = args[0].immediate();
@@ -2787,6 +2793,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
27872793
sym::simd_cttz => "llvm.cttz",
27882794
sym::simd_funnel_shl => "llvm.fshl",
27892795
sym::simd_funnel_shr => "llvm.fshr",
2796+
sym::simd_carryless_mul => "llvm.clmul",
27902797
_ => unreachable!(),
27912798
};
27922799
let int_size = in_elem.int_size_and_signed(bx.tcx()).0.bits();
@@ -2812,6 +2819,11 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
28122819
&[vec_ty],
28132820
&[args[0].immediate(), args[1].immediate(), args[2].immediate()],
28142821
)),
2822+
sym::simd_carryless_mul => Ok(bx.call_intrinsic(
2823+
llvm_intrinsic,
2824+
&[vec_ty],
2825+
&[args[0].immediate(), args[1].immediate()],
2826+
)),
28152827
_ => unreachable!(),
28162828
};
28172829
}

compiler/rustc_hir_analysis/src/check/intrinsic.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi
8282
| sym::bswap
8383
| sym::caller_location
8484
| sym::carrying_mul_add
85+
| sym::carryless_mul
8586
| sym::ceilf16
8687
| sym::ceilf32
8788
| sym::ceilf64
@@ -564,6 +565,7 @@ pub(crate) fn check_intrinsic_type(
564565
(1, 0, vec![param(0), param(0)], param(0))
565566
}
566567
sym::saturating_add | sym::saturating_sub => (1, 0, vec![param(0), param(0)], param(0)),
568+
sym::carryless_mul => (1, 0, vec![param(0), param(0)], param(0)),
567569
sym::fadd_fast | sym::fsub_fast | sym::fmul_fast | sym::fdiv_fast | sym::frem_fast => {
568570
(1, 0, vec![param(0), param(0)], param(0))
569571
}
@@ -711,7 +713,8 @@ pub(crate) fn check_intrinsic_type(
711713
| sym::simd_fmin
712714
| sym::simd_fmax
713715
| sym::simd_saturating_add
714-
| sym::simd_saturating_sub => (1, 0, vec![param(0), param(0)], param(0)),
716+
| sym::simd_saturating_sub
717+
| sym::simd_carryless_mul => (1, 0, vec![param(0), param(0)], param(0)),
715718
sym::simd_arith_offset => (2, 0, vec![param(0), param(1)], param(0)),
716719
sym::simd_neg
717720
| sym::simd_bswap

compiler/rustc_span/src/symbol.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,7 @@ symbols! {
642642
caller_location,
643643
capture_disjoint_fields,
644644
carrying_mul_add,
645+
carryless_mul,
645646
catch_unwind,
646647
cause,
647648
cdylib,
@@ -2083,6 +2084,7 @@ symbols! {
20832084
simd_bitmask,
20842085
simd_bitreverse,
20852086
simd_bswap,
2087+
simd_carryless_mul,
20862088
simd_cast,
20872089
simd_cast_ptr,
20882090
simd_ceil,

library/core/src/intrinsics/fallback.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,3 +218,57 @@ macro_rules! impl_funnel_shifts {
218218
impl_funnel_shifts! {
219219
u8, u16, u32, u64, u128, usize
220220
}
221+
222+
#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")]
223+
pub const trait CarrylessMul: Copy + 'static {
224+
/// See [`super::carryless_mul`]; we just need the trait indirection to handle
225+
/// different types since calling intrinsics with generics doesn't work.
226+
fn carryless_mul(self, rhs: Self) -> Self;
227+
}
228+
229+
macro_rules! impl_carryless_mul{
230+
($($type:ident),*) => {$(
231+
/// This approach uses a bitmask of the form `0b100010001...0001` to avoid carry spilling.
232+
/// When carries do occur, they wind up in a "hole" of zeros and are subsequently masked
233+
/// out of the result.
234+
#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")]
235+
impl const CarrylessMul for $type {
236+
#[inline]
237+
fn carryless_mul(self, rhs: Self) -> Self {
238+
use crate::num::Wrapping;
239+
240+
// i.e. 0b100010001...0001 in binary.
241+
const MASK: u128 = 0x1111_1111_1111_1111_1111_1111_1111_1111;
242+
243+
let m0 = MASK as $type;
244+
let x = self;
245+
let y = rhs;
246+
247+
let m1 = m0 << 1;
248+
let m2 = m1 << 1;
249+
let m3 = m2 << 1;
250+
251+
let x0 = Wrapping(x & m0);
252+
let x1 = Wrapping(x & m1);
253+
let x2 = Wrapping(x & m2);
254+
let x3 = Wrapping(x & m3);
255+
256+
let y0 = Wrapping(y & m0);
257+
let y1 = Wrapping(y & m1);
258+
let y2 = Wrapping(y & m2);
259+
let y3 = Wrapping(y & m3);
260+
261+
let z0 = (x0 * y0) ^ (x1 * y3) ^ (x2 * y2) ^ (x3 * y1);
262+
let z1 = (x0 * y1) ^ (x1 * y0) ^ (x2 * y3) ^ (x3 * y2);
263+
let z2 = (x0 * y2) ^ (x1 * y1) ^ (x2 * y0) ^ (x3 * y3);
264+
let z3 = (x0 * y3) ^ (x1 * y2) ^ (x2 * y1) ^ (x3 * y0);
265+
266+
(z0.0 & m0) | (z1.0 & m1) | (z2.0 & m2) | (z3.0 & m3)
267+
}
268+
}
269+
)*};
270+
}
271+
272+
impl_carryless_mul! {
273+
u8, u16, u32, u64, u128, usize
274+
}

library/core/src/intrinsics/mod.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2179,6 +2179,19 @@ pub const unsafe fn unchecked_funnel_shr<T: [const] fallback::FunnelShift>(
21792179
unsafe { a.unchecked_funnel_shr(b, shift) }
21802180
}
21812181

2182+
/// Carryless multiply.
2183+
///
2184+
/// Safe versions of this intrinsic are available on the integer primitives
2185+
/// via the `carryless_mul` method. For example, [`u32::carryless_mul`].
2186+
#[rustc_intrinsic]
2187+
#[rustc_nounwind]
2188+
#[rustc_const_unstable(feature = "uint_carryless_mul", issue = "152080")]
2189+
#[unstable(feature = "uint_carryless_mul", issue = "152080")]
2190+
#[miri::intrinsic_fallback_is_spec]
2191+
pub const fn carryless_mul<T: [const] fallback::CarrylessMul>(a: T, b: T) -> T {
2192+
a.carryless_mul(b)
2193+
}
2194+
21822195
/// This is an implementation detail of [`crate::ptr::read`] and should
21832196
/// not be used anywhere else. See its comments for why this exists.
21842197
///

library/core/src/intrinsics/simd.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,18 @@ pub const unsafe fn simd_funnel_shl<T>(a: T, b: T, shift: T) -> T;
162162
#[rustc_nounwind]
163163
pub const unsafe fn simd_funnel_shr<T>(a: T, b: T, shift: T) -> T;
164164

165+
/// Compute the carry-less product.
166+
///
167+
/// This is similar to long multiplication except that the carry is discarded.
168+
///
169+
/// This operation can be used to model multiplication in `GF(2)[X]`, the polynomial
170+
/// ring over `GF(2)`.
171+
///
172+
/// `T` must be a vector of integers.
173+
#[rustc_intrinsic]
174+
#[rustc_nounwind]
175+
pub unsafe fn simd_carryless_mul<T>(a: T, b: T) -> T;
176+
165177
/// "And"s vectors elementwise.
166178
///
167179
/// `T` must be a vector of integers.

library/core/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@
188188
#![feature(trait_alias)]
189189
#![feature(transparent_unions)]
190190
#![feature(try_blocks)]
191+
#![feature(uint_carryless_mul)]
191192
#![feature(unboxed_closures)]
192193
#![feature(unsized_fn_params)]
193194
#![feature(with_negative_coherence)]

library/core/src/num/mod.rs

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,9 @@ impl u8 {
458458
fsh_op = "0x36",
459459
fshl_result = "0x8",
460460
fshr_result = "0x8d",
461+
clmul_lhs = "0x12",
462+
clmul_rhs = "0x34",
463+
clmul_result = "0x28",
461464
swap_op = "0x12",
462465
swapped = "0x12",
463466
reversed = "0x48",
@@ -1095,6 +1098,9 @@ impl u16 {
10951098
fsh_op = "0x2de",
10961099
fshl_result = "0x30",
10971100
fshr_result = "0x302d",
1101+
clmul_lhs = "0x9012",
1102+
clmul_rhs = "0xcd34",
1103+
clmul_result = "0x928",
10981104
swap_op = "0x1234",
10991105
swapped = "0x3412",
11001106
reversed = "0x2c48",
@@ -1145,6 +1151,9 @@ impl u32 {
11451151
fsh_op = "0x2fe78e45",
11461152
fshl_result = "0xb32f",
11471153
fshr_result = "0xb32fe78e",
1154+
clmul_lhs = "0x56789012",
1155+
clmul_rhs = "0xf52ecd34",
1156+
clmul_result = "0x9b980928",
11481157
swap_op = "0x12345678",
11491158
swapped = "0x78563412",
11501159
reversed = "0x1e6a2c48",
@@ -1171,6 +1180,9 @@ impl u64 {
11711180
fsh_op = "0x2fe78e45983acd98",
11721181
fshl_result = "0x6e12fe",
11731182
fshr_result = "0x6e12fe78e45983ac",
1183+
clmul_lhs = "0x7890123456789012",
1184+
clmul_rhs = "0xdd358416f52ecd34",
1185+
clmul_result = "0xa6299579b980928",
11741186
swap_op = "0x1234567890123456",
11751187
swapped = "0x5634129078563412",
11761188
reversed = "0x6a2c48091e6a2c48",
@@ -1197,6 +1209,9 @@ impl u128 {
11971209
fsh_op = "0x2fe78e45983acd98039000008736273",
11981210
fshl_result = "0x4f7602fe",
11991211
fshr_result = "0x4f7602fe78e45983acd9803900000873",
1212+
clmul_lhs = "0x12345678901234567890123456789012",
1213+
clmul_rhs = "0x4317e40ab4ddcf05dd358416f52ecd34",
1214+
clmul_result = "0xb9cf660de35d0c170a6299579b980928",
12001215
swap_op = "0x12345678901234567890123456789012",
12011216
swapped = "0x12907856341290785634129078563412",
12021217
reversed = "0x48091e6a2c48091e6a2c48091e6a2c48",
@@ -1223,9 +1238,12 @@ impl usize {
12231238
rot = 4,
12241239
rot_op = "0xa003",
12251240
rot_result = "0x3a",
1226-
fsh_op = "0x2fe78e45983acd98039000008736273",
1227-
fshl_result = "0x4f7602fe",
1228-
fshr_result = "0x4f7602fe78e45983acd9803900000873",
1241+
fsh_op = "0x2de",
1242+
fshl_result = "0x30",
1243+
fshr_result = "0x302d",
1244+
clmul_lhs = "0x9012",
1245+
clmul_rhs = "0xcd34",
1246+
clmul_result = "0x928",
12291247
swap_op = "0x1234",
12301248
swapped = "0x3412",
12311249
reversed = "0x2c48",
@@ -1253,6 +1271,9 @@ impl usize {
12531271
fsh_op = "0x2fe78e45",
12541272
fshl_result = "0xb32f",
12551273
fshr_result = "0xb32fe78e",
1274+
clmul_lhs = "0x56789012",
1275+
clmul_rhs = "0xf52ecd34",
1276+
clmul_result = "0x9b980928",
12561277
swap_op = "0x12345678",
12571278
swapped = "0x78563412",
12581279
reversed = "0x1e6a2c48",
@@ -1280,6 +1301,9 @@ impl usize {
12801301
fsh_op = "0x2fe78e45983acd98",
12811302
fshl_result = "0x6e12fe",
12821303
fshr_result = "0x6e12fe78e45983ac",
1304+
clmul_lhs = "0x7890123456789012",
1305+
clmul_rhs = "0xdd358416f52ecd34",
1306+
clmul_result = "0xa6299579b980928",
12831307
swap_op = "0x1234567890123456",
12841308
swapped = "0x5634129078563412",
12851309
reversed = "0x6a2c48091e6a2c48",

library/core/src/num/uint_macros.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ macro_rules! uint_impl {
1717
fsh_op = $fsh_op:literal,
1818
fshl_result = $fshl_result:literal,
1919
fshr_result = $fshr_result:literal,
20+
clmul_lhs = $clmul_rhs:literal,
21+
clmul_rhs = $clmul_lhs:literal,
22+
clmul_result = $clmul_result:literal,
2023
swap_op = $swap_op:literal,
2124
swapped = $swapped:literal,
2225
reversed = $reversed:literal,
@@ -482,6 +485,32 @@ macro_rules! uint_impl {
482485
unsafe { intrinsics::unchecked_funnel_shr(self, rhs, n) }
483486
}
484487

488+
/// Performs a carry-less multiplication.
489+
///
490+
/// This is similar to long multiplication except that the carry is discarded.
491+
/// This function wraps, so only the low bits are returned.
492+
///
493+
/// This operation can be used to model multiplication in `GF(2)[X]`, the polynomial
494+
/// ring over `GF(2)`.
495+
///
496+
/// ```
497+
/// #![feature(uint_carryless_mul)]
498+
///
499+
#[doc = concat!("let a = ", $clmul_lhs, stringify!($SelfT), ";")]
500+
#[doc = concat!("let b = ", $clmul_rhs, stringify!($SelfT), ";")]
501+
///
502+
#[doc = concat!("assert_eq!(a.carryless_mul(b), ", $clmul_result, ");")]
503+
/// ```
504+
#[rustc_const_unstable(feature = "uint_carryless_mul", issue = "152080")]
505+
#[doc(alias = "clmul")]
506+
#[unstable(feature = "uint_carryless_mul", issue = "152080")]
507+
#[must_use = "this returns the result of the operation, \
508+
without modifying the original"]
509+
#[inline(always)]
510+
pub const fn carryless_mul(self, rhs: Self) -> Self {
511+
intrinsics::carryless_mul(self, rhs)
512+
}
513+
485514
/// Reverses the byte order of the integer.
486515
///
487516
/// # Examples

library/coretests/tests/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@
120120
#![feature(try_trait_v2)]
121121
#![feature(type_info)]
122122
#![feature(uint_bit_width)]
123+
#![feature(uint_carryless_mul)]
123124
#![feature(uint_gather_scatter_bits)]
124125
#![feature(unsize)]
125126
#![feature(unwrap_infallible)]

0 commit comments

Comments
 (0)