FLANG

unsigned-const-division.h
1 //===-- include/flang/Common/unsigned-const-division.h ----------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #ifndef FORTRAN_COMMON_UNSIGNED_CONST_DIVISION_H_
10 #define FORTRAN_COMMON_UNSIGNED_CONST_DIVISION_H_
11
12 // Work around unoptimized implementations of unsigned integer division
13 // by constant values in some compilers (looking at YOU, clang 7!) by
14 // explicitly implementing integer division by constant divisors as
15 // multiplication by a fixed-point reciprocal and a right shift.
16
17 #include "bit-population-count.h"
18 #include "leading-zero-bit-count.h"
19 #include "uint128.h"
20 #include <cinttypes>
21 #include <type_traits>
22
23 namespace Fortran::common {
24
25 template <typename UINT> class FixedPointReciprocal {
26 public:
27 using type = UINT;
28
29 private:
30 static_assert(std::is_unsigned_v<type>);
31 static const int bits{static_cast<int>(8 * sizeof(type))};
32 static_assert(bits <= 64);
33 using Big = HostUnsignedIntType<bits * 2>;
34
35 public:
36 static constexpr FixedPointReciprocal For(type n) {
37 if (n == 0) {
38 return {0, 0};
39 } else if ((n & (n - 1)) == 0) { // n is a power of two
40 return {TrailingZeroBitCount(n), 1};
41 } else {
42 int shift{bits - 1 + BitsNeededFor(n)};
43 return {shift, static_cast<type>(((Big{1} << shift) + n - 1) / n)};
44 }
45 }
46
47 constexpr type Divide(type n) const {
48 return static_cast<type>((static_cast<Big>(reciprocal_) * n) >> shift_);
49 }
50
51 private:
52 constexpr FixedPointReciprocal(int s, type r) : shift_{s}, reciprocal_{r} {}
53
54 int shift_;
55 type reciprocal_;
56 };
57
58 static_assert(FixedPointReciprocal<std::uint32_t>::For(5).Divide(2000000000u) ==
59 400000000u);
60 static_assert(FixedPointReciprocal<std::uint64_t>::For(10).Divide(
61 10000000000000000u) == 1000000000000000u);
62
63 template <typename UINT, std::uint64_t DENOM>
64 inline constexpr UINT DivideUnsignedBy(UINT n) {
65 if constexpr (std::is_same_v<UINT, uint128_t>) {
66 return n / static_cast<UINT>(DENOM);
67 } else {
68 // G++ can recognize that the reciprocal is a compile-time
69 // constant when For() is called inline, but clang requires
70 // a constexpr variable definition to force compile-time
71 // evaluation of the reciprocal.
72 constexpr auto recip{FixedPointReciprocal<UINT>::For(DENOM)};
73 return recip.Divide(n);
74 }
75 }
76 } // namespace Fortran::common
77 #endif
Fortran::common::FixedPointReciprocal
Definition: unsigned-const-division.h:25