Skip to content

Commit 10db28f

Browse files
committed
Revert to original loop for const pow exponents
Give LLVM the for original, optimizable loop in pow and wrapped_pow functions in the case when the exponent is statically known.
1 parent 2f23534 commit 10db28f

File tree

2 files changed

+110
-160
lines changed

2 files changed

+110
-160
lines changed

library/core/src/num/int_macros.rs

+55-80
Original file line numberDiff line numberDiff line change
@@ -2174,54 +2174,41 @@ macro_rules! int_impl {
21742174
#[inline]
21752175
#[rustc_allow_const_fn_unstable(is_val_statically_known)]
21762176
pub const fn wrapping_pow(self, mut exp: u32) -> Self {
2177+
if exp == 0 {
2178+
return 1;
2179+
}
21772180
let mut base = self;
2181+
let mut acc: Self = 1;
21782182

21792183
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);
2184+
while exp > 1 {
2185+
if (exp & 1) == 1 {
2186+
acc = acc.wrapping_mul(base);
21952187
}
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-
_ => {}
2188+
exp /= 2;
2189+
base = base.wrapping_mul(base);
22052190
}
2191+
2192+
// since exp!=0, finally the exp must be 1.
2193+
// Deal with the final bit of the exponent separately, since
2194+
// squaring the base afterwards is not necessary.
2195+
acc.wrapping_mul(base)
22062196
} else {
2207-
if exp == 0 {
2208-
return 1;
2209-
}
2210-
}
2211-
debug_assert!(exp != 0);
2212-
2213-
let mut acc: Self = 1;
2214-
2215-
loop {
2216-
if (exp & 1) == 1 {
2217-
acc = acc.wrapping_mul(base);
2218-
// since exp!=0, finally the exp must be 1.
2219-
if exp == 1 {
2220-
return acc;
2197+
// This is faster than the above when the exponent is not known
2198+
// at compile time. We can't use the same code for the constant
2199+
// exponent case because LLVM is currently unable to unroll
2200+
// this loop.
2201+
loop {
2202+
if (exp & 1) == 1 {
2203+
acc = acc.wrapping_mul(base);
2204+
// since exp!=0, finally the exp must be 1.
2205+
if exp == 1 {
2206+
return acc;
2207+
}
22212208
}
2209+
exp /= 2;
2210+
base = base.wrapping_mul(base);
22222211
}
2223-
exp /= 2;
2224-
base = base.wrapping_mul(base);
22252212
}
22262213
}
22272214

@@ -2753,54 +2740,42 @@ macro_rules! int_impl {
27532740
#[rustc_inherit_overflow_checks]
27542741
#[rustc_allow_const_fn_unstable(is_val_statically_known)]
27552742
pub const fn pow(self, mut exp: u32) -> Self {
2743+
if exp == 0 {
2744+
return 1;
2745+
}
27562746
let mut base = self;
2747+
let mut acc = 1;
27572748

27582749
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;
2750+
while exp > 1 {
2751+
if (exp & 1) == 1 {
2752+
acc = acc * base;
27782753
}
2779-
6 => {
2780-
let cubed = base * base * base;
2781-
return cubed * cubed;
2782-
}
2783-
_ => {}
2784-
}
2785-
} else {
2786-
if exp == 0 {
2787-
return 1;
2754+
exp /= 2;
2755+
base = base * base;
27882756
}
2789-
}
2790-
debug_assert!(exp != 0);
27912757

2792-
let mut acc = 1;
2793-
2794-
loop {
2795-
if (exp & 1) == 1 {
2796-
acc = acc * base;
2797-
// since exp!=0, finally the exp must be 1.
2798-
if exp == 1 {
2799-
return acc;
2758+
// since exp!=0, finally the exp must be 1.
2759+
// Deal with the final bit of the exponent separately, since
2760+
// squaring the base afterwards is not necessary and may cause a
2761+
// needless overflow.
2762+
acc * base
2763+
} else {
2764+
// This is faster than the above when the exponent is not known
2765+
// at compile time. We can't use the same code for the constant
2766+
// exponent case because LLVM is currently unable to unroll
2767+
// this loop.
2768+
loop {
2769+
if (exp & 1) == 1 {
2770+
acc = acc * base;
2771+
// since exp!=0, finally the exp must be 1.
2772+
if exp == 1 {
2773+
return acc;
2774+
}
28002775
}
2776+
exp /= 2;
2777+
base = base * base;
28012778
}
2802-
exp /= 2;
2803-
base = base * base;
28042779
}
28052780
}
28062781

library/core/src/num/uint_macros.rs

+55-80
Original file line numberDiff line numberDiff line change
@@ -2050,54 +2050,41 @@ macro_rules! uint_impl {
20502050
#[inline]
20512051
#[rustc_allow_const_fn_unstable(is_val_statically_known)]
20522052
pub const fn wrapping_pow(self, mut exp: u32) -> Self {
2053+
if exp == 0 {
2054+
return 1;
2055+
}
20532056
let mut base = self;
2057+
let mut acc: Self = 1;
20542058

20552059
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);
2060+
while exp > 1 {
2061+
if (exp & 1) == 1 {
2062+
acc = acc.wrapping_mul(base);
20712063
}
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-
_ => {}
2064+
exp /= 2;
2065+
base = base.wrapping_mul(base);
20812066
}
2067+
2068+
// since exp!=0, finally the exp must be 1.
2069+
// Deal with the final bit of the exponent separately, since
2070+
// squaring the base afterwards is not necessary.
2071+
acc.wrapping_mul(base)
20822072
} else {
2083-
if exp == 0 {
2084-
return 1;
2085-
}
2086-
}
2087-
debug_assert!(exp != 0);
2088-
2089-
let mut acc: Self = 1;
2090-
2091-
loop {
2092-
if (exp & 1) == 1 {
2093-
acc = acc.wrapping_mul(base);
2094-
// since exp!=0, finally the exp must be 1.
2095-
if exp == 1 {
2096-
return acc;
2073+
// This is faster than the above when the exponent is not known
2074+
// at compile time. We can't use the same code for the constant
2075+
// exponent case because LLVM is currently unable to unroll
2076+
// this loop.
2077+
loop {
2078+
if (exp & 1) == 1 {
2079+
acc = acc.wrapping_mul(base);
2080+
// since exp!=0, finally the exp must be 1.
2081+
if exp == 1 {
2082+
return acc;
2083+
}
20972084
}
2085+
exp /= 2;
2086+
base = base.wrapping_mul(base);
20982087
}
2099-
exp /= 2;
2100-
base = base.wrapping_mul(base);
21012088
}
21022089
}
21032090

@@ -2578,54 +2565,42 @@ macro_rules! uint_impl {
25782565
#[rustc_inherit_overflow_checks]
25792566
#[rustc_allow_const_fn_unstable(is_val_statically_known)]
25802567
pub const fn pow(self, mut exp: u32) -> Self {
2568+
if exp == 0 {
2569+
return 1;
2570+
}
25812571
let mut base = self;
2572+
let mut acc = 1;
25822573

25832574
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;
2575+
while exp > 1 {
2576+
if (exp & 1) == 1 {
2577+
acc = acc * base;
26032578
}
2604-
6 => {
2605-
let cubed = base * base * base;
2606-
return cubed * cubed;
2607-
}
2608-
_ => {}
2609-
}
2610-
} else {
2611-
if exp == 0 {
2612-
return 1;
2579+
exp /= 2;
2580+
base = base * base;
26132581
}
2614-
}
2615-
debug_assert!(exp != 0);
26162582

2617-
let mut acc = 1;
2618-
2619-
loop {
2620-
if (exp & 1) == 1 {
2621-
acc = acc * base;
2622-
// since exp!=0, finally the exp must be 1.
2623-
if exp == 1 {
2624-
return acc;
2583+
// since exp!=0, finally the exp must be 1.
2584+
// Deal with the final bit of the exponent separately, since
2585+
// squaring the base afterwards is not necessary and may cause a
2586+
// needless overflow.
2587+
acc * base
2588+
} else {
2589+
// This is faster than the above when the exponent is not known
2590+
// at compile time. We can't use the same code for the constant
2591+
// exponent case because LLVM is currently unable to unroll
2592+
// this loop.
2593+
loop {
2594+
if (exp & 1) == 1 {
2595+
acc = acc * base;
2596+
// since exp!=0, finally the exp must be 1.
2597+
if exp == 1 {
2598+
return acc;
2599+
}
26252600
}
2601+
exp /= 2;
2602+
base = base * base;
26262603
}
2627-
exp /= 2;
2628-
base = base * base;
26292604
}
26302605
}
26312606

0 commit comments

Comments
 (0)