Skip to content

Commit 20e64bd

Browse files
committed
Use is_val_statically_known to optimize pow
In the dynamic exponent case, it's preferred to not increase code size, so use solely the loop-based implementation there. This shows about 4% penalty in the variable exponent benchmarks on x86_64.
1 parent 05ee322 commit 20e64bd

File tree

3 files changed

+125
-92
lines changed

3 files changed

+125
-92
lines changed

Diff for: core/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@
170170
#![feature(internal_impls_macro)]
171171
#![feature(ip)]
172172
#![feature(is_ascii_octdigit)]
173+
#![feature(is_val_statically_known)]
173174
#![feature(isqrt)]
174175
#![feature(link_cfg)]
175176
#![feature(offset_of_enum)]

Diff for: core/src/num/int_macros.rs

+62-46
Original file line numberDiff line numberDiff line change
@@ -2172,35 +2172,43 @@ macro_rules! int_impl {
21722172
#[must_use = "this returns the result of the operation, \
21732173
without modifying the original"]
21742174
#[inline]
2175+
#[rustc_allow_const_fn_unstable(is_val_statically_known)]
21752176
pub const fn wrapping_pow(self, mut exp: u32) -> Self {
21762177
let mut base = self;
21772178

2178-
// Unroll multiplications for small exponent values.
2179-
// This gives the optimizer a way to efficiently inline call sites
2180-
// for the most common use cases with constant exponents.
2181-
// Currently, LLVM is unable to unroll the loop below.
2182-
match exp {
2183-
0 => return 1,
2184-
1 => return base,
2185-
2 => return base.wrapping_mul(base),
2186-
3 => {
2187-
let squared = base.wrapping_mul(base);
2188-
return squared.wrapping_mul(base);
2189-
}
2190-
4 => {
2191-
let squared = base.wrapping_mul(base);
2192-
return squared.wrapping_mul(squared);
2193-
}
2194-
5 => {
2195-
let squared = base.wrapping_mul(base);
2196-
return squared.wrapping_mul(squared).wrapping_mul(base);
2179+
if intrinsics::is_val_statically_known(exp) {
2180+
// Unroll multiplications for small exponent values.
2181+
// This gives the optimizer a way to efficiently inline call sites
2182+
// for the most common use cases with constant exponents.
2183+
// Currently, LLVM is unable to unroll the loop below.
2184+
match exp {
2185+
0 => return 1,
2186+
1 => return base,
2187+
2 => return base.wrapping_mul(base),
2188+
3 => {
2189+
let squared = base.wrapping_mul(base);
2190+
return squared.wrapping_mul(base);
2191+
}
2192+
4 => {
2193+
let squared = base.wrapping_mul(base);
2194+
return squared.wrapping_mul(squared);
2195+
}
2196+
5 => {
2197+
let squared = base.wrapping_mul(base);
2198+
return squared.wrapping_mul(squared).wrapping_mul(base);
2199+
}
2200+
6 => {
2201+
let cubed = base.wrapping_mul(base).wrapping_mul(base);
2202+
return cubed.wrapping_mul(cubed);
2203+
}
2204+
_ => {}
21972205
}
2198-
6 => {
2199-
let cubed = base.wrapping_mul(base).wrapping_mul(base);
2200-
return cubed.wrapping_mul(cubed);
2206+
} else {
2207+
if exp == 0 {
2208+
return 1;
22012209
}
2202-
_ => {}
22032210
}
2211+
debug_assert!(exp != 0);
22042212

22052213
let mut acc: Self = 1;
22062214

@@ -2743,35 +2751,43 @@ macro_rules! int_impl {
27432751
without modifying the original"]
27442752
#[inline]
27452753
#[rustc_inherit_overflow_checks]
2754+
#[rustc_allow_const_fn_unstable(is_val_statically_known)]
27462755
pub const fn pow(self, mut exp: u32) -> Self {
27472756
let mut base = self;
27482757

2749-
// Unroll multiplications for small exponent values.
2750-
// This gives the optimizer a way to efficiently inline call sites
2751-
// for the most common use cases with constant exponents.
2752-
// Currently, LLVM is unable to unroll the loop below.
2753-
match exp {
2754-
0 => return 1,
2755-
1 => return base,
2756-
2 => return base * base,
2757-
3 => {
2758-
let squared = base * base;
2759-
return squared * base;
2760-
}
2761-
4 => {
2762-
let squared = base * base;
2763-
return squared * squared;
2764-
}
2765-
5 => {
2766-
let squared = base * base;
2767-
return squared * squared * base;
2758+
if intrinsics::is_val_statically_known(exp) {
2759+
// Unroll multiplications for small exponent values.
2760+
// This gives the optimizer a way to efficiently inline call sites
2761+
// for the most common use cases with constant exponents.
2762+
// Currently, LLVM is unable to unroll the loop below.
2763+
match exp {
2764+
0 => return 1,
2765+
1 => return base,
2766+
2 => return base * base,
2767+
3 => {
2768+
let squared = base * base;
2769+
return squared * base;
2770+
}
2771+
4 => {
2772+
let squared = base * base;
2773+
return squared * squared;
2774+
}
2775+
5 => {
2776+
let squared = base * base;
2777+
return squared * squared * base;
2778+
}
2779+
6 => {
2780+
let cubed = base * base * base;
2781+
return cubed * cubed;
2782+
}
2783+
_ => {}
27682784
}
2769-
6 => {
2770-
let cubed = base * base * base;
2771-
return cubed * cubed;
2785+
} else {
2786+
if exp == 0 {
2787+
return 1;
27722788
}
2773-
_ => {}
27742789
}
2790+
debug_assert!(exp != 0);
27752791

27762792
let mut acc = 1;
27772793

Diff for: core/src/num/uint_macros.rs

+62-46
Original file line numberDiff line numberDiff line change
@@ -2048,35 +2048,43 @@ macro_rules! uint_impl {
20482048
#[must_use = "this returns the result of the operation, \
20492049
without modifying the original"]
20502050
#[inline]
2051+
#[rustc_allow_const_fn_unstable(is_val_statically_known)]
20512052
pub const fn wrapping_pow(self, mut exp: u32) -> Self {
20522053
let mut base = self;
20532054

2054-
// Unroll multiplications for small exponent values.
2055-
// This gives the optimizer a way to efficiently inline call sites
2056-
// for the most common use cases with constant exponents.
2057-
// Currently, LLVM is unable to unroll the loop below.
2058-
match exp {
2059-
0 => return 1,
2060-
1 => return base,
2061-
2 => return base.wrapping_mul(base),
2062-
3 => {
2063-
let squared = base.wrapping_mul(base);
2064-
return squared.wrapping_mul(base);
2065-
}
2066-
4 => {
2067-
let squared = base.wrapping_mul(base);
2068-
return squared.wrapping_mul(squared);
2069-
}
2070-
5 => {
2071-
let squared = base.wrapping_mul(base);
2072-
return squared.wrapping_mul(squared).wrapping_mul(base);
2055+
if intrinsics::is_val_statically_known(exp) {
2056+
// Unroll multiplications for small exponent values.
2057+
// This gives the optimizer a way to efficiently inline call sites
2058+
// for the most common use cases with constant exponents.
2059+
// Currently, LLVM is unable to unroll the loop below.
2060+
match exp {
2061+
0 => return 1,
2062+
1 => return base,
2063+
2 => return base.wrapping_mul(base),
2064+
3 => {
2065+
let squared = base.wrapping_mul(base);
2066+
return squared.wrapping_mul(base);
2067+
}
2068+
4 => {
2069+
let squared = base.wrapping_mul(base);
2070+
return squared.wrapping_mul(squared);
2071+
}
2072+
5 => {
2073+
let squared = base.wrapping_mul(base);
2074+
return squared.wrapping_mul(squared).wrapping_mul(base);
2075+
}
2076+
6 => {
2077+
let cubed = base.wrapping_mul(base).wrapping_mul(base);
2078+
return cubed.wrapping_mul(cubed);
2079+
}
2080+
_ => {}
20732081
}
2074-
6 => {
2075-
let cubed = base.wrapping_mul(base).wrapping_mul(base);
2076-
return cubed.wrapping_mul(cubed);
2082+
} else {
2083+
if exp == 0 {
2084+
return 1;
20772085
}
2078-
_ => {}
20792086
}
2087+
debug_assert!(exp != 0);
20802088

20812089
let mut acc: Self = 1;
20822090

@@ -2568,35 +2576,43 @@ macro_rules! uint_impl {
25682576
without modifying the original"]
25692577
#[inline]
25702578
#[rustc_inherit_overflow_checks]
2579+
#[rustc_allow_const_fn_unstable(is_val_statically_known)]
25712580
pub const fn pow(self, mut exp: u32) -> Self {
25722581
let mut base = self;
25732582

2574-
// Unroll multiplications for small exponent values.
2575-
// This gives the optimizer a way to efficiently inline call sites
2576-
// for the most common use cases with constant exponents.
2577-
// Currently, LLVM is unable to unroll the loop below.
2578-
match exp {
2579-
0 => return 1,
2580-
1 => return base,
2581-
2 => return base * base,
2582-
3 => {
2583-
let squared = base * base;
2584-
return squared * base;
2585-
}
2586-
4 => {
2587-
let squared = base * base;
2588-
return squared * squared;
2589-
}
2590-
5 => {
2591-
let squared = base * base;
2592-
return squared * squared * base;
2583+
if intrinsics::is_val_statically_known(exp) {
2584+
// Unroll multiplications for small exponent values.
2585+
// This gives the optimizer a way to efficiently inline call sites
2586+
// for the most common use cases with constant exponents.
2587+
// Currently, LLVM is unable to unroll the loop below.
2588+
match exp {
2589+
0 => return 1,
2590+
1 => return base,
2591+
2 => return base * base,
2592+
3 => {
2593+
let squared = base * base;
2594+
return squared * base;
2595+
}
2596+
4 => {
2597+
let squared = base * base;
2598+
return squared * squared;
2599+
}
2600+
5 => {
2601+
let squared = base * base;
2602+
return squared * squared * base;
2603+
}
2604+
6 => {
2605+
let cubed = base * base * base;
2606+
return cubed * cubed;
2607+
}
2608+
_ => {}
25932609
}
2594-
6 => {
2595-
let cubed = base * base * base;
2596-
return cubed * cubed;
2610+
} else {
2611+
if exp == 0 {
2612+
return 1;
25972613
}
2598-
_ => {}
25992614
}
2615+
debug_assert!(exp != 0);
26002616

26012617
let mut acc = 1;
26022618

0 commit comments

Comments
 (0)