_ZN5Botan10ct_is_zeroITkNSt3__117unsigned_integralEmEET_S2_:
   37|  7.28k|BOTAN_FORCE_INLINE constexpr T ct_is_zero(T x) {
   38|  7.28k|   return ct_expand_top_bit<T>(~x & (x - 1));
   39|  7.28k|}
_ZN5Botan17ct_expand_top_bitITkNSt3__117unsigned_integralEmEET_S2_:
   28|  28.7k|BOTAN_FORCE_INLINE constexpr T ct_expand_top_bit(T a) {
   29|  28.7k|   const T top = CT::value_barrier<T>(a >> (sizeof(T) * 8 - 1));
   30|  28.7k|   return static_cast<T>(0) - top;
   31|  28.7k|}
_ZN5Botan6chooseITkNSt3__117unsigned_integralEmEET_S2_S2_S2_:
  217|  4.08k|BOTAN_FORCE_INLINE constexpr T choose(T mask, T a, T b) {
  218|       |   //return (mask & a) | (~mask & b);
  219|  4.08k|   return (b ^ (mask & (a ^ b)));
  220|  4.08k|}
_ZN5Botan8high_bitITkNSt3__117unsigned_integralEmEEmT_:
   73|    234|BOTAN_FORCE_INLINE constexpr size_t high_bit(T n) {
   74|    234|   size_t hb = 0;
   75|       |
   76|  1.63k|   for(size_t s = 8 * sizeof(T) / 2; s > 0; s /= 2) {
  ------------------
  |  Branch (76:38): [True: 1.40k, False: 234]
  ------------------
   77|       |      // Equivalent to: ((n >> s) == 0) ? 0 : s;
   78|  1.40k|      const size_t z = s - ct_if_is_zero_ret<T>(n >> s, s);
   79|  1.40k|      hb += z;
   80|  1.40k|      n >>= z;
   81|  1.40k|   }
   82|       |
   83|    234|   hb += static_cast<size_t>(n);
   84|       |
   85|    234|   return hb;
   86|    234|}
_ZN5Botan17ct_if_is_zero_retITkNSt3__117unsigned_integralEmEEmT_m:
   45|  1.40k|BOTAN_FORCE_INLINE constexpr size_t ct_if_is_zero_ret(T x, size_t s) {
   46|       |   /*
   47|       |   Similar to `return ct_is_zero(x) & s` but has to account for possibility that
   48|       |   sizeof(T) is smaller than sizeof(size_t) which would lead to incomplete masking
   49|       |   */
   50|  1.40k|   const T a = ~x & (x - 1);
   51|  1.40k|   const size_t a_top = static_cast<size_t>(CT::value_barrier<T>(a >> (sizeof(T) * 8 - 1)));
   52|  1.40k|   const size_t mask = static_cast<size_t>(0) - a_top;
   53|  1.40k|   return mask & s;
   54|  1.40k|}
_ZN5Botan13ceil_divisionITkNSt3__117unsigned_integralEmEET_S2_S2_:
  167|    122|BOTAN_FORCE_INLINE constexpr T ceil_division(T a, T b) {
  168|       |   // Same as `(a + b - 1) / b` but avoids possible overflow
  169|    122|   return a / b + ((a % b != 0) ? 1 : 0);
  ------------------
  |  Branch (169:20): [True: 0, False: 122]
  ------------------
  170|    122|}
_ZN5Botan10ct_is_zeroITkNSt3__117unsigned_integralEhEET_S2_:
   37|    118|BOTAN_FORCE_INLINE constexpr T ct_is_zero(T x) {
   38|    118|   return ct_expand_top_bit<T>(~x & (x - 1));
   39|    118|}
_ZN5Botan17ct_expand_top_bitITkNSt3__117unsigned_integralEhEET_S2_:
   28|    118|BOTAN_FORCE_INLINE constexpr T ct_expand_top_bit(T a) {
   29|    118|   const T top = CT::value_barrier<T>(a >> (sizeof(T) * 8 - 1));
   30|    118|   return static_cast<T>(0) - top;
   31|    118|}
_ZN5Botan6chooseITkNSt3__117unsigned_integralEhEET_S2_S2_S2_:
  217|    118|BOTAN_FORCE_INLINE constexpr T choose(T mask, T a, T b) {
  218|       |   //return (mask & a) | (~mask & b);
  219|    118|   return (b ^ (mask & (a ^ b)));
  220|    118|}

_ZN5Botan13reverse_bytesITkNSt3__117unsigned_integralEmQooooooeqstT_Li1EeqstS2_Li2EeqstS2_Li4EeqstS2_Li8EEES2_S2_:
   25|  1.93k|inline constexpr T reverse_bytes(T x) {
   26|       |   if constexpr(sizeof(T) == 1) {
   27|       |      return x;
   28|       |   } else if constexpr(sizeof(T) == 2) {
   29|       |#if BOTAN_COMPILER_HAS_BUILTIN(__builtin_bswap16)
   30|       |      return static_cast<T>(__builtin_bswap16(x));
   31|       |#else
   32|       |      return static_cast<T>((x << 8) | (x >> 8));
   33|       |#endif
   34|       |   } else if constexpr(sizeof(T) == 4) {
   35|       |#if BOTAN_COMPILER_HAS_BUILTIN(__builtin_bswap32)
   36|       |      return static_cast<T>(__builtin_bswap32(x));
   37|       |#else
   38|       |      // MSVC at least recognizes this as a bswap
   39|       |      return static_cast<T>(((x & 0x000000FF) << 24) | ((x & 0x0000FF00) << 8) | ((x & 0x00FF0000) >> 8) |
   40|       |                            ((x & 0xFF000000) >> 24));
   41|       |#endif
   42|  1.93k|   } else if constexpr(sizeof(T) == 8) {
   43|  1.93k|#if BOTAN_COMPILER_HAS_BUILTIN(__builtin_bswap64)
   44|  1.93k|      return static_cast<T>(__builtin_bswap64(x));
   45|       |#else
   46|       |      uint32_t hi = static_cast<uint32_t>(x >> 32);
   47|       |      uint32_t lo = static_cast<uint32_t>(x);
   48|       |
   49|       |      hi = reverse_bytes(hi);
   50|       |      lo = reverse_bytes(lo);
   51|       |
   52|       |      return (static_cast<T>(lo) << 32) | hi;
   53|       |#endif
   54|  1.93k|   }
   55|  1.93k|}

_ZN5Botan5CPUID3hasENS_10CPUFeatureES1_:
   98|      1|      static bool has(CPUID::Feature feat1, CPUID::Feature feat2) {
   99|      1|         return state().has_bit(feat1.as_u32() | feat2.as_u32());
  100|      1|      }
_ZN5Botan5CPUID5stateEv:
  155|      1|      static CPUID_Data& state() {
  156|      1|         static CPUID::CPUID_Data g_cpuid;
  157|      1|         return g_cpuid;
  158|      1|      }
_ZNK5Botan5CPUID10CPUID_Data7has_bitEj:
  143|      1|            bool has_bit(uint32_t bit) const { return (m_processor_features & bit) == bit; }
_ZN5Botan5CPUID6is_setEjNS_10CPUFeatureE:
  126|      5|      static inline bool is_set(uint32_t allowed, CPUID::Feature bit) {
  127|      5|         const uint32_t feat_bit = bit.as_u32();
  128|      5|         return ((allowed & feat_bit) == feat_bit);
  129|      5|      }
cpuid_x86.cpp:_ZN5Botan5CPUID6if_setIZNS0_10CPUID_Data19detect_cpu_featuresEjE16x86_CPUID_1_bitsEEjmT_NS_10CPUFeatureEj:
  116|      7|      static inline uint32_t if_set(uint64_t cpuid, T flag, CPUID::Feature bit, uint32_t allowed) {
  117|      7|         const uint64_t flag64 = static_cast<uint64_t>(flag);
  118|      7|         if((cpuid & flag64) == flag64) {
  ------------------
  |  Branch (118:13): [True: 7, False: 0]
  ------------------
  119|      7|            return (bit.as_u32() & allowed);
  120|      7|         } else {
  121|      0|            return 0;
  122|      0|         }
  123|      7|      }
cpuid_x86.cpp:_ZN5Botan5CPUID6if_setIZNS0_10CPUID_Data19detect_cpu_featuresEjE16x86_CPUID_7_bitsEEjmT_NS_10CPUFeatureEj:
  116|      8|      static inline uint32_t if_set(uint64_t cpuid, T flag, CPUID::Feature bit, uint32_t allowed) {
  117|      8|         const uint64_t flag64 = static_cast<uint64_t>(flag);
  118|      8|         if((cpuid & flag64) == flag64) {
  ------------------
  |  Branch (118:13): [True: 5, False: 3]
  ------------------
  119|      5|            return (bit.as_u32() & allowed);
  120|      5|         } else {
  121|      3|            return 0;
  122|      3|         }
  123|      8|      }
cpuid_x86.cpp:_ZN5Botan5CPUID6if_setIZNS0_10CPUID_Data19detect_cpu_featuresEjE18x86_CPUID_7_1_bitsEEjmT_NS_10CPUFeatureEj:
  116|      3|      static inline uint32_t if_set(uint64_t cpuid, T flag, CPUID::Feature bit, uint32_t allowed) {
  117|      3|         const uint64_t flag64 = static_cast<uint64_t>(flag);
  118|      3|         if((cpuid & flag64) == flag64) {
  ------------------
  |  Branch (118:13): [True: 0, False: 3]
  ------------------
  119|      0|            return (bit.as_u32() & allowed);
  120|      3|         } else {
  121|      3|            return 0;
  122|      3|         }
  123|      3|      }

_ZNK5Botan10CPUFeature6as_u32Ev:
   56|     19|      uint32_t as_u32() const { return static_cast<uint32_t>(m_bit); }
_ZN5Botan10CPUFeatureC2ENS0_3BitE:
   54|     25|      CPUFeature(Bit b) : m_bit(b) {}  // NOLINT(*-explicit-conversions,*-explicit-constructor)

_ZN5Botan2CT22conditional_assign_memImEENS0_4MaskIT_EES3_PS3_PKS3_m:
  749|    120|constexpr inline Mask<T> conditional_assign_mem(T cnd, T* dest, const T* src, size_t elems) {
  750|    120|   const auto mask = CT::Mask<T>::expand(cnd);
  751|    120|   mask.select_n(dest, src, dest, elems);
  752|    120|   return mask;
  753|    120|}
_ZN5Botan2CT4MaskImE6expandEm:
  392|    921|      static constexpr Mask<T> expand(T v) { return ~Mask<T>::is_zero(value_barrier<T>(v)); }
_ZN5Botan2CT4MaskImE7is_zeroEm:
  437|  5.80k|      static constexpr Mask<T> is_zero(T x) { return Mask<T>(ct_is_zero<T>(value_barrier<T>(x))); }
_ZN5Botan2CT4MaskImEC2Em:
  637|  49.1k|      constexpr explicit Mask(T m) : m_mask(m) {}
_ZNK5Botan2CT4MaskImEcoEv:
  533|  8.97k|      constexpr Mask<T> operator~() const { return Mask<T>(~value()); }
_ZNK5Botan2CT4MaskImE5valueEv:
  630|  59.6k|      constexpr T value() const { return value_barrier<T>(m_mask); }
_ZNK5Botan2CT4MaskImE8select_nEPmPKmS5_m:
  565|    120|      constexpr void select_n(T output[], const T x[], const T y[], size_t len) const {
  566|    120|         const T mask = value();
  567|    600|         for(size_t i = 0; i != len; ++i) {
  ------------------
  |  Branch (567:28): [True: 480, False: 120]
  ------------------
  568|    480|            output[i] = choose(mask, x[i], y[i]);
  569|    480|         }
  570|    120|      }
_ZNK5Botan2CT4MaskImE6selectEmm:
  548|  3.60k|      constexpr T select(T x, T y) const { return choose(value(), x, y); }
_ZN5Botan2CT8unpoisonImEEvPKT_m:
   67|  1.31k|constexpr inline void unpoison(const T* p, size_t n) {
   68|       |#if defined(BOTAN_HAS_VALGRIND)
   69|       |   if(!std::is_constant_evaluated()) {
   70|       |      VALGRIND_MAKE_MEM_DEFINED(p, n * sizeof(T));
   71|       |   }
   72|       |#endif
   73|       |
   74|  1.31k|   BOTAN_UNUSED(p, n);
  ------------------
  |  |  151|  1.31k|#define BOTAN_UNUSED Botan::ignore_params
  ------------------
   75|  1.31k|}
_ZN5Botan2CT8unpoisonITkNSt3__18integralEmEEvRKT_:
  112|    485|constexpr void unpoison(const T& p) {
  113|    485|   unpoison(&p, 1);
  114|    485|}
_ZNK5Botan2CT4MaskImE16unpoisoned_valueEv:
  598|    120|      constexpr T unpoisoned_value() const {
  599|    120|         T r = value();
  600|    120|         CT::unpoison(r);
  601|    120|         return r;
  602|    120|      }
_ZN5Botan2CT4MaskImE8is_equalEmm:
  442|  4.75k|      static constexpr Mask<T> is_equal(T x, T y) {
  443|  4.75k|         const T diff = value_barrier(x) ^ value_barrier(y);
  444|  4.75k|         return Mask<T>::is_zero(diff);
  445|  4.75k|      }
_ZN5Botan2CT4MaskImE5is_ltEmm:
  450|  13.9k|      static constexpr Mask<T> is_lt(T x, T y) {
  451|  13.9k|         T u = x ^ ((x ^ y) | ((x - y) ^ x));
  452|  13.9k|         return Mask<T>::expand_top_bit(u);
  453|  13.9k|      }
_ZN5Botan2CT4MaskImE14expand_top_bitEm:
  415|  21.4k|      static constexpr Mask<T> expand_top_bit(T v) { return Mask<T>(ct_expand_top_bit<T>(v)); }
_ZNK5Botan2CT4MaskImE13if_set_returnEm:
  538|  20.3k|      constexpr T if_set_return(T x) const { return value() & x; }
_ZN5Botan2CT4MaskImE5is_gtEmm:
  458|  3.94k|      static constexpr Mask<T> is_gt(T x, T y) { return Mask<T>::is_lt(y, x); }
_ZN5Botan2CT4MaskImE6is_gteEmm:
  468|  8.05k|      static constexpr Mask<T> is_gte(T x, T y) { return ~Mask<T>::is_lt(x, y); }
_ZNK5Botan2CT4MaskImE7as_boolEv:
  614|    120|      constexpr bool as_bool() const { return unpoisoned_value() != 0; }
_ZNK5Botan2CT4MaskImE11select_maskES2_S2_:
  559|    960|      Mask<T> select_mask(Mask<T> x, Mask<T> y) const { return Mask<T>(select(x.value(), y.value())); }
_ZN5Botan2CT4MaskIhE11expand_boolEb:
  397|    118|      static constexpr Mask<T> expand_bool(bool v) { return Mask<T>::expand(static_cast<T>(v)); }
_ZN5Botan2CT4MaskIhE6expandEh:
  392|    118|      static constexpr Mask<T> expand(T v) { return ~Mask<T>::is_zero(value_barrier<T>(v)); }
_ZN5Botan2CT4MaskIhE7is_zeroEh:
  437|    118|      static constexpr Mask<T> is_zero(T x) { return Mask<T>(ct_is_zero<T>(value_barrier<T>(x))); }
_ZN5Botan2CT4MaskIhEC2Eh:
  637|    236|      constexpr explicit Mask(T m) : m_mask(m) {}
_ZNK5Botan2CT4MaskIhEcoEv:
  533|    118|      constexpr Mask<T> operator~() const { return Mask<T>(~value()); }
_ZNK5Botan2CT4MaskIhE5valueEv:
  630|    236|      constexpr T value() const { return value_barrier<T>(m_mask); }
_ZNK5Botan2CT4MaskIhE6selectEhh:
  548|    118|      constexpr T select(T x, T y) const { return choose(value(), x, y); }
_ZN5Botan2CTorENS0_4MaskImEES2_:
  528|  9.74k|      friend constexpr Mask<T> operator|(Mask<T> x, Mask<T> y) { return Mask<T>(x.value() | y.value()); }
_ZN5Botan2CTanENS0_4MaskImEES2_:
  518|  2.25k|      friend constexpr Mask<T> operator&(Mask<T> x, Mask<T> y) { return Mask<T>(x.value() & y.value()); }

_ZN5Botan11checked_mulITkNSt3__117unsigned_integralEmEENS1_8optionalIT_EES3_S3_:
   46|    480|constexpr inline std::optional<T> checked_mul(T a, T b) {
   47|       |   // Multiplication by 1U is a hack to work around C's insane
   48|       |   // integer promotion rules.
   49|       |   // https://stackoverflow.com/questions/24795651
   50|    480|   const T r = (1U * a) * b;
   51|       |   // If a == 0 then the multiply certainly did not overflow
   52|       |   // Otherwise r / a == b unless overflow occurred
   53|    480|   if(a != 0 && r / a != b) {
  ------------------
  |  Branch (53:7): [True: 480, False: 0]
  |  Branch (53:17): [True: 0, False: 480]
  ------------------
   54|      0|      return {};
   55|      0|   }
   56|    480|   return r;
   57|    480|}

_ZN5Botan7load_beImJNSt3__14spanIKhLm18446744073709551615EEEEEEDaDpOT0_:
  504|    960|inline constexpr auto load_be(ParamTs&&... params) {
  505|    960|   return detail::load_any<std::endian::big, OutT>(std::forward<ParamTs>(params)...);
  506|    960|}
_ZN5Botan6detail8load_anyILNSt3__16endianE64206ETkNS0_20unsigned_integralishEmTkNS_6ranges16contiguous_rangeIhEENS2_4spanIKhLm18446744073709551615EEEQnt15custom_loadableINS0_19wrapped_type_helperIu14__remove_cvrefIT0_EE4typeEEEESA_OT1_:
  278|    960|inline constexpr WrappedOutT load_any(InR&& in_range) {
  279|    960|   using OutT = detail::wrapped_type<WrappedOutT>;
  280|    960|   ranges::assert_exact_byte_length<sizeof(OutT)>(in_range);
  281|       |
  282|    960|   return detail::wrap_strong_type_or_enum<WrappedOutT>([&]() -> OutT {
  283|       |      // At compile time we cannot use `typecast_copy` as it uses `std::memcpy`
  284|       |      // internally to copy ranges on a byte-by-byte basis, which is not allowed
  285|       |      // in a `constexpr` context.
  286|    960|      if(std::is_constant_evaluated()) /* TODO: C++23: if consteval {} */ {
  287|    960|         return fallback_load_any<endianness, OutT>(std::forward<InR>(in_range));
  288|    960|      } else {
  289|    960|         const std::span in{in_range};
  290|    960|         if constexpr(sizeof(OutT) == 1) {
  291|    960|            return static_cast<OutT>(in[0]);
  292|    960|         } else if constexpr(endianness == std::endian::native) {
  293|    960|            return typecast_copy<OutT>(in);
  294|    960|         } else {
  295|    960|            static_assert(opposite(endianness) == std::endian::native);
  296|    960|            return reverse_bytes(typecast_copy<OutT>(in));
  297|    960|         }
  298|    960|      }
  299|    960|   }());
  300|    960|}
_ZN5Botan6detail24wrap_strong_type_or_enumITkNS0_20unsigned_integralishEmTkNSt3__117unsigned_integralEmEEDaT0_:
  200|  1.93k|constexpr auto wrap_strong_type_or_enum(T t) {
  201|       |   if constexpr(std::is_enum_v<OutT>) {
  202|       |      return static_cast<OutT>(t);
  203|  1.93k|   } else {
  204|  1.93k|      return Botan::wrap_strong_type<OutT>(t);
  205|  1.93k|   }
  206|  1.93k|}
_ZZN5Botan6detail8load_anyILNSt3__16endianE64206ETkNS0_20unsigned_integralishEmTkNS_6ranges16contiguous_rangeIhEENS2_4spanIKhLm18446744073709551615EEEQnt15custom_loadableINS0_19wrapped_type_helperIu14__remove_cvrefIT0_EE4typeEEEESA_OT1_ENKUlvE_clEv:
  282|    960|   return detail::wrap_strong_type_or_enum<WrappedOutT>([&]() -> OutT {
  283|       |      // At compile time we cannot use `typecast_copy` as it uses `std::memcpy`
  284|       |      // internally to copy ranges on a byte-by-byte basis, which is not allowed
  285|       |      // in a `constexpr` context.
  286|    960|      if(std::is_constant_evaluated()) /* TODO: C++23: if consteval {} */ {
  ------------------
  |  Branch (286:10): [Folded, False: 960]
  ------------------
  287|      0|         return fallback_load_any<endianness, OutT>(std::forward<InR>(in_range));
  288|    960|      } else {
  289|    960|         const std::span in{in_range};
  290|       |         if constexpr(sizeof(OutT) == 1) {
  291|       |            return static_cast<OutT>(in[0]);
  292|       |         } else if constexpr(endianness == std::endian::native) {
  293|       |            return typecast_copy<OutT>(in);
  294|    960|         } else {
  295|    960|            static_assert(opposite(endianness) == std::endian::native);
  296|    960|            return reverse_bytes(typecast_copy<OutT>(in));
  297|    960|         }
  298|    960|      }
  299|    960|   }());
_ZN5Botan7load_beImJNSt3__14spanIKhLm8EEEEEEDaDpOT0_:
  504|    976|inline constexpr auto load_be(ParamTs&&... params) {
  505|    976|   return detail::load_any<std::endian::big, OutT>(std::forward<ParamTs>(params)...);
  506|    976|}
_ZN5Botan6detail8load_anyILNSt3__16endianE64206ETkNS0_20unsigned_integralishEmTkNS_6ranges16contiguous_rangeIhEENS2_4spanIKhLm8EEEQnt15custom_loadableINS0_19wrapped_type_helperIu14__remove_cvrefIT0_EE4typeEEEESA_OT1_:
  278|    976|inline constexpr WrappedOutT load_any(InR&& in_range) {
  279|    976|   using OutT = detail::wrapped_type<WrappedOutT>;
  280|    976|   ranges::assert_exact_byte_length<sizeof(OutT)>(in_range);
  281|       |
  282|    976|   return detail::wrap_strong_type_or_enum<WrappedOutT>([&]() -> OutT {
  283|       |      // At compile time we cannot use `typecast_copy` as it uses `std::memcpy`
  284|       |      // internally to copy ranges on a byte-by-byte basis, which is not allowed
  285|       |      // in a `constexpr` context.
  286|    976|      if(std::is_constant_evaluated()) /* TODO: C++23: if consteval {} */ {
  287|    976|         return fallback_load_any<endianness, OutT>(std::forward<InR>(in_range));
  288|    976|      } else {
  289|    976|         const std::span in{in_range};
  290|    976|         if constexpr(sizeof(OutT) == 1) {
  291|    976|            return static_cast<OutT>(in[0]);
  292|    976|         } else if constexpr(endianness == std::endian::native) {
  293|    976|            return typecast_copy<OutT>(in);
  294|    976|         } else {
  295|    976|            static_assert(opposite(endianness) == std::endian::native);
  296|    976|            return reverse_bytes(typecast_copy<OutT>(in));
  297|    976|         }
  298|    976|      }
  299|    976|   }());
  300|    976|}
_ZZN5Botan6detail8load_anyILNSt3__16endianE64206ETkNS0_20unsigned_integralishEmTkNS_6ranges16contiguous_rangeIhEENS2_4spanIKhLm8EEEQnt15custom_loadableINS0_19wrapped_type_helperIu14__remove_cvrefIT0_EE4typeEEEESA_OT1_ENKUlvE_clEv:
  282|    976|   return detail::wrap_strong_type_or_enum<WrappedOutT>([&]() -> OutT {
  283|       |      // At compile time we cannot use `typecast_copy` as it uses `std::memcpy`
  284|       |      // internally to copy ranges on a byte-by-byte basis, which is not allowed
  285|       |      // in a `constexpr` context.
  286|    976|      if(std::is_constant_evaluated()) /* TODO: C++23: if consteval {} */ {
  ------------------
  |  Branch (286:10): [Folded, False: 976]
  ------------------
  287|      0|         return fallback_load_any<endianness, OutT>(std::forward<InR>(in_range));
  288|    976|      } else {
  289|    976|         const std::span in{in_range};
  290|       |         if constexpr(sizeof(OutT) == 1) {
  291|       |            return static_cast<OutT>(in[0]);
  292|       |         } else if constexpr(endianness == std::endian::native) {
  293|       |            return typecast_copy<OutT>(in);
  294|    976|         } else {
  295|    976|            static_assert(opposite(endianness) == std::endian::native);
  296|    976|            return reverse_bytes(typecast_copy<OutT>(in));
  297|    976|         }
  298|    976|      }
  299|    976|   }());

_ZN5Botan14zeroize_bufferITkNSt3__117unsigned_integralEmEEvPT_m:
   37|      1|inline void zeroize_buffer(T buf[], size_t n) {
   38|      1|   if(n > 0) {
  ------------------
  |  Branch (38:7): [True: 1, False: 0]
  ------------------
   39|      1|      std::memset(buf, 0, sizeof(T) * n);
   40|      1|   }
   41|      1|}

_ZN5Botan10word_madd3ITkNS_8WordTypeEmEET_S1_S1_S1_PS1_:
  137|    480|inline constexpr auto word_madd3(W a, W b, W c, W* d) -> W {
  138|    480|#if defined(BOTAN_MP_USE_X86_64_ASM)
  139|    480|   if constexpr(std::same_as<W, uint64_t>) {
  140|    480|      if(!std::is_constant_evaluated()) {
  ------------------
  |  Branch (140:10): [True: 480, Folded]
  ------------------
  141|    480|         asm(R"(
  142|    480|            mulq %[b]
  143|    480|
  144|    480|            addq %[c],%[a]
  145|    480|            adcq $0,%[carry]
  146|    480|
  147|    480|            addq %[d],%[a]
  148|    480|            adcq $0,%[carry]
  149|    480|            )"
  150|    480|             : [a] "=a"(a), [b] "=rm"(b), [carry] "=&d"(*d)
  151|    480|             : "0"(a), "1"(b), [c] "g"(c), [d] "g"(*d)
  152|    480|             : "cc");
  153|       |
  154|    480|         return a;
  155|    480|      }
  156|    480|   }
  157|       |#elif defined(BOTAN_MP_USE_AARCH64_ASM)
  158|       |   if constexpr(std::same_as<W, uint64_t>) {
  159|       |      if(!std::is_constant_evaluated()) {
  160|       |         W lo = 0;
  161|       |         W hi = 0;
  162|       |         asm(R"(
  163|       |            mul  %[lo], %[a], %[b]
  164|       |            umulh %[hi], %[a], %[b]
  165|       |            adds %[lo], %[lo], %[c]
  166|       |            adc  %[hi], %[hi], xzr
  167|       |            adds %[lo], %[lo], %[d]
  168|       |            adc  %[hi], %[hi], xzr
  169|       |            )"
  170|       |             : [lo] "=&r"(lo), [hi] "=&r"(hi)
  171|       |             : [a] "r"(a), [b] "r"(b), [c] "r"(c), [d] "r"(*d)
  172|       |             : "cc");
  173|       |
  174|       |         *d = hi;
  175|       |         return lo;
  176|       |      }
  177|       |   }
  178|       |#endif
  179|       |
  180|      0|   typedef typename WordInfo<W>::dword dword;
  181|    480|   const dword s = dword(a) * b + c + *d;
  182|    480|   *d = static_cast<W>(s >> WordInfo<W>::bits);
  183|    480|   return static_cast<W>(s);
  184|    480|}
_ZN5Botan10word_madd2ITkNS_8WordTypeEmEET_S1_S1_PS1_:
   90|  5.19k|inline constexpr auto word_madd2(W a, W b, W* c) -> W {
   91|  5.19k|#if defined(BOTAN_MP_USE_X86_64_ASM)
   92|  5.19k|   if constexpr(std::same_as<W, uint64_t>) {
   93|  5.19k|      if(!std::is_constant_evaluated()) {
  ------------------
  |  Branch (93:10): [True: 5.19k, Folded]
  ------------------
   94|  5.19k|         asm(R"(
   95|  5.19k|            mulq %[b]
   96|  5.19k|            addq %[c],%[a]
   97|  5.19k|            adcq $0,%[carry]
   98|  5.19k|            )"
   99|  5.19k|             : [a] "=a"(a), [b] "=rm"(b), [carry] "=&d"(*c)
  100|  5.19k|             : "0"(a), "1"(b), [c] "g"(*c)
  101|  5.19k|             : "cc");
  102|       |
  103|  5.19k|         return a;
  104|  5.19k|      }
  105|  5.19k|   }
  106|       |#elif defined(BOTAN_MP_USE_AARCH64_ASM)
  107|       |   if constexpr(std::same_as<W, uint64_t>) {
  108|       |      if(!std::is_constant_evaluated()) {
  109|       |         W lo = 0;
  110|       |         W hi = 0;
  111|       |         asm(R"(
  112|       |            mul  %[lo], %[a], %[b]
  113|       |            umulh %[hi], %[a], %[b]
  114|       |            adds %[lo], %[lo], %[c]
  115|       |            adc  %[hi], %[hi], xzr
  116|       |            )"
  117|       |             : [lo] "=&r"(lo), [hi] "=&r"(hi)
  118|       |             : [a] "r"(a), [b] "r"(b), [c] "r"(*c)
  119|       |             : "cc");
  120|       |
  121|       |         *c = hi;
  122|       |         return lo;
  123|       |      }
  124|       |   }
  125|       |#endif
  126|       |
  127|      0|   typedef typename WordInfo<W>::dword dword;
  128|  5.19k|   const dword s = dword(a) * b + *c;
  129|  5.19k|   *c = static_cast<W>(s >> WordInfo<W>::bits);
  130|  5.19k|   return static_cast<W>(s);
  131|  5.19k|}
_ZN5Botan8word_addITkNS_8WordTypeEmEET_S1_S1_PS1_:
  239|  2.73k|inline constexpr auto word_add(W x, W y, W* carry) -> W {
  240|  2.73k|#if BOTAN_COMPILER_HAS_BUILTIN(__builtin_addc)
  241|  2.73k|   if(!std::is_constant_evaluated()) {
  ------------------
  |  Branch (241:7): [True: 2.73k, Folded]
  ------------------
  242|       |      if constexpr(std::same_as<W, unsigned int>) {
  243|       |         return __builtin_addc(x, y, *carry & 1, carry);
  244|  2.73k|      } else if constexpr(std::same_as<W, unsigned long>) {
  245|  2.73k|         return __builtin_addcl(x, y, *carry & 1, carry);
  246|       |      } else if constexpr(std::same_as<W, unsigned long long>) {
  247|       |         return __builtin_addcll(x, y, *carry & 1, carry);
  248|       |      }
  249|  2.73k|   }
  250|      0|#endif
  251|       |
  252|       |   if constexpr(WordInfo<W>::dword_is_native && use_dword_for_word_add) {
  253|       |      /*
  254|       |      TODO(Botan4) this is largely a performance hack for GCCs that don't
  255|       |      support __builtin_addc, if we increase the minimum supported version of
  256|       |      GCC to GCC 14 then we can remove this and not worry about it
  257|       |      */
  258|       |      const W cb = *carry & 1;
  259|       |      const auto s = typename WordInfo<W>::dword(x) + y + cb;
  260|       |      *carry = static_cast<W>(s >> WordInfo<W>::bits);
  261|       |      return static_cast<W>(s);
  262|  2.73k|   } else {
  263|  2.73k|      const W cb = *carry & 1;
  264|  2.73k|      W z = x + y;
  265|  2.73k|      W c1 = (z < x);
  266|  2.73k|      z += cb;
  267|  2.73k|      *carry = c1 | (z < cb);
  268|  2.73k|      return z;
  269|  2.73k|   }
  270|  2.73k|}
_ZN5Botan8word_subITkNS_8WordTypeEmEET_S1_S1_PS1_:
  332|  3.30k|inline constexpr auto word_sub(W x, W y, W* carry) -> W {
  333|  3.30k|#if BOTAN_COMPILER_HAS_BUILTIN(__builtin_subc)
  334|  3.30k|   if(!std::is_constant_evaluated()) {
  ------------------
  |  Branch (334:7): [True: 3.30k, Folded]
  ------------------
  335|       |      if constexpr(std::same_as<W, unsigned int>) {
  336|       |         return __builtin_subc(x, y, *carry & 1, carry);
  337|  3.30k|      } else if constexpr(std::same_as<W, unsigned long>) {
  338|  3.30k|         return __builtin_subcl(x, y, *carry & 1, carry);
  339|       |      } else if constexpr(std::same_as<W, unsigned long long>) {
  340|       |         return __builtin_subcll(x, y, *carry & 1, carry);
  341|       |      }
  342|  3.30k|   }
  343|      0|#endif
  344|       |
  345|      0|   const W cb = *carry & 1;
  346|  3.30k|   W t0 = x - y;
  347|  3.30k|   W c1 = (t0 > x);
  348|  3.30k|   W z = t0 - cb;
  349|  3.30k|   *carry = c1 | (z > t0);
  350|  3.30k|   return z;
  351|  3.30k|}

_ZN5Botan13redc_crandallITkNS_8WordTypeEmLm4ETnT_Lm4294968273EEENSt3__15arrayIS1_XT0_EEENS2_4spanIKS1_XmlLi2ET0_EEE:
 1167|    120|constexpr std::array<W, N> redc_crandall(std::span<const W, 2 * N> z) {
 1168|    120|   static_assert(N >= 2);
 1169|       |
 1170|    120|   std::array<W, N> hi = {};
 1171|       |
 1172|       |   // hi = hi * c + lo
 1173|       |
 1174|    120|   W carry = 0;
 1175|    600|   for(size_t i = 0; i != N; ++i) {
  ------------------
  |  Branch (1175:22): [True: 480, False: 120]
  ------------------
 1176|    480|      hi[i] = word_madd3(z[i + N], C, z[i], &carry);
 1177|    480|   }
 1178|       |
 1179|       |   // hi += carry * C
 1180|    120|   W carry_c[2] = {0};
 1181|    120|   carry_c[0] = word_madd2(carry, C, &carry_c[1]);
 1182|       |
 1183|    120|   carry = bigint_add2(hi.data(), N, carry_c, 2);
 1184|       |
 1185|    120|   constexpr W P0 = WordInfo<W>::max - (C - 1);
 1186|       |
 1187|    120|   std::array<W, N> r = {};
 1188|       |
 1189|    120|   W borrow = 0;
 1190|       |
 1191|       |   /*
 1192|       |   * For undetermined reasons, on GCC (only) removing this asm block causes
 1193|       |   * massive (up to 20%) performance regressions in secp256k1.
 1194|       |   *
 1195|       |   * The generated code without the asm seems quite reasonable, and timing
 1196|       |   * repeated calls to redc_crandall with the cycle counter show that GCC
 1197|       |   * computes it in about the same number of cycles with or without the asm.
 1198|       |   *
 1199|       |   * So the cause of the regression is unclear. But it is reproducible across
 1200|       |   * machines and GCC versions.
 1201|       |   */
 1202|       |#if defined(BOTAN_MP_USE_X86_64_ASM) && defined(__GNUC__) && !defined(__clang__)
 1203|       |   if constexpr(N == 4 && std::same_as<W, uint64_t>) {
 1204|       |      if(!std::is_constant_evaluated()) {
 1205|       |         asm volatile(R"(
 1206|       |                      movq 0(%[x]), %[borrow]
 1207|       |                      subq %[p0], %[borrow]
 1208|       |                      movq %[borrow], 0(%[r])
 1209|       |                      movq 8(%[x]), %[borrow]
 1210|       |                      sbbq $-1, %[borrow]
 1211|       |                      movq %[borrow], 8(%[r])
 1212|       |                      movq 16(%[x]), %[borrow]
 1213|       |                      sbbq $-1, %[borrow]
 1214|       |                      movq %[borrow], 16(%[r])
 1215|       |                      movq 24(%[x]), %[borrow]
 1216|       |                      sbbq $-1, %[borrow]
 1217|       |                      movq %[borrow], 24(%[r])
 1218|       |                      sbbq %[borrow],%[borrow]
 1219|       |                      negq %[borrow]
 1220|       |                      )"
 1221|       |                      : [borrow] "=r"(borrow)
 1222|       |                      : [x] "r"(hi.data()), [p0] "r"(P0), [r] "r"(r.data()), "0"(borrow)
 1223|       |                      : "cc", "memory");
 1224|       |
 1225|       |         borrow = (carry - borrow) > carry;
 1226|       |         CT::conditional_assign_mem(borrow, r.data(), hi.data(), N);
 1227|       |         return r;
 1228|       |      }
 1229|       |   }
 1230|       |#endif
 1231|       |
 1232|    120|   r[0] = word_sub(hi[0], P0, &borrow);
 1233|    480|   for(size_t i = 1; i != N; ++i) {
  ------------------
  |  Branch (1233:22): [True: 360, False: 120]
  ------------------
 1234|    360|      r[i] = word_sub(hi[i], WordInfo<W>::max, &borrow);
 1235|    360|   }
 1236|       |
 1237|    120|   borrow = (carry - borrow) > carry;
 1238|       |
 1239|    120|   CT::conditional_assign_mem(borrow, r.data(), hi.data(), N);
 1240|       |
 1241|    120|   return r;
 1242|    120|}
_ZN5Botan11bigint_add2ITkNS_8WordTypeEmEET_PS1_mPKS1_m:
   97|    120|inline constexpr auto bigint_add2(W x[], size_t x_size, const W y[], size_t y_size) -> W {
   98|    120|   W carry = 0;
   99|       |
  100|    120|   BOTAN_ASSERT(x_size >= y_size, "Expected sizes");
  ------------------
  |  |   71|    120|   do {                                                                                 \
  |  |   72|    120|      /* NOLINTNEXTLINE(*-simplify-boolean-expr) */                                     \
  |  |   73|    120|      if(!(expr)) {                                                                     \
  |  |  ------------------
  |  |  |  Branch (73:10): [True: 0, False: 120]
  |  |  ------------------
  |  |   74|      0|         /* NOLINTNEXTLINE(bugprone-lambda-function-name) */                            \
  |  |   75|      0|         Botan::assertion_failure(#expr, assertion_made, __func__, __FILE__, __LINE__); \
  |  |   76|      0|      }                                                                                 \
  |  |   77|    120|   } while(0)
  |  |  ------------------
  |  |  |  Branch (77:12): [Folded, False: 120]
  |  |  ------------------
  ------------------
  101|       |
  102|    120|   const size_t blocks = y_size - (y_size % 8);
  103|       |
  104|    120|   for(size_t i = 0; i != blocks; i += 8) {
  ------------------
  |  Branch (104:22): [True: 0, False: 120]
  ------------------
  105|      0|      carry = word8_add2(x + i, y + i, carry);
  106|      0|   }
  107|       |
  108|    360|   for(size_t i = blocks; i != y_size; ++i) {
  ------------------
  |  Branch (108:27): [True: 240, False: 120]
  ------------------
  109|    240|      x[i] = word_add(x[i], y[i], &carry);
  110|    240|   }
  111|       |
  112|    360|   for(size_t i = y_size; i != x_size; ++i) {
  ------------------
  |  Branch (112:27): [True: 240, False: 120]
  ------------------
  113|    240|      x[i] = word_add(x[i], static_cast<W>(0), &carry);
  114|    240|   }
  115|       |
  116|    120|   return carry;
  117|    120|}
_ZN5Botan10bigint_cmpITkNS_8WordTypeEmEEiPKT_mS3_m:
  462|    123|inline constexpr int32_t bigint_cmp(const W x[], size_t x_size, const W y[], size_t y_size) {
  463|    123|   static_assert(sizeof(W) >= sizeof(uint32_t), "Size assumption");
  464|       |
  465|    123|   const W LT = static_cast<W>(-1);
  466|    123|   const W EQ = 0;
  467|    123|   const W GT = 1;
  468|       |
  469|    123|   const size_t common_elems = std::min(x_size, y_size);
  470|       |
  471|    123|   W result = EQ;  // until found otherwise
  472|       |
  473|  1.10k|   for(size_t i = 0; i != common_elems; i++) {
  ------------------
  |  Branch (473:22): [True: 977, False: 123]
  ------------------
  474|    977|      const auto is_eq = CT::Mask<W>::is_equal(x[i], y[i]);
  475|    977|      const auto is_lt = CT::Mask<W>::is_lt(x[i], y[i]);
  476|       |
  477|    977|      result = is_eq.select(result, is_lt.select(LT, GT));
  478|    977|   }
  479|       |
  480|    123|   if(x_size < y_size) {
  ------------------
  |  Branch (480:7): [True: 122, False: 1]
  ------------------
  481|    122|      W mask = 0;
  482|  2.07k|      for(size_t i = x_size; i != y_size; i++) {
  ------------------
  |  Branch (482:30): [True: 1.95k, False: 122]
  ------------------
  483|  1.95k|         mask |= y[i];
  484|  1.95k|      }
  485|       |
  486|       |      // If any bits were set in high part of y, then x < y
  487|    122|      result = CT::Mask<W>::is_zero(mask).select(result, LT);
  488|    122|   } else if(y_size < x_size) {
  ------------------
  |  Branch (488:14): [True: 1, False: 0]
  ------------------
  489|      1|      W mask = 0;
  490|      5|      for(size_t i = y_size; i != x_size; i++) {
  ------------------
  |  Branch (490:30): [True: 4, False: 1]
  ------------------
  491|      4|         mask |= x[i];
  492|      4|      }
  493|       |
  494|       |      // If any bits were set in high part of x, then x > y
  495|      1|      result = CT::Mask<W>::is_zero(mask).select(result, GT);
  496|      1|   }
  497|       |
  498|    123|   CT::unpoison(result);
  499|    123|   BOTAN_DEBUG_ASSERT(result == LT || result == GT || result == EQ);
  ------------------
  |  |  137|    123|      do { /* NOLINT(*-avoid-do-while) */ \
  |  |  138|    123|      } while(0)
  |  |  ------------------
  |  |  |  Branch (138:15): [Folded, False: 123]
  |  |  ------------------
  ------------------
  500|    123|   return static_cast<int32_t>(result);
  501|    123|}
_ZN5Botan11bigint_sub3ITkNS_8WordTypeEmEET_PS1_PKS1_mS4_m:
  195|      1|inline constexpr auto bigint_sub3(W z[], const W x[], size_t x_size, const W y[], size_t y_size) -> W {
  196|      1|   W borrow = 0;
  197|       |
  198|      1|   BOTAN_ASSERT(x_size >= y_size, "Expected sizes");
  ------------------
  |  |   71|      1|   do {                                                                                 \
  |  |   72|      1|      /* NOLINTNEXTLINE(*-simplify-boolean-expr) */                                     \
  |  |   73|      1|      if(!(expr)) {                                                                     \
  |  |  ------------------
  |  |  |  Branch (73:10): [True: 0, False: 1]
  |  |  ------------------
  |  |   74|      0|         /* NOLINTNEXTLINE(bugprone-lambda-function-name) */                            \
  |  |   75|      0|         Botan::assertion_failure(#expr, assertion_made, __func__, __FILE__, __LINE__); \
  |  |   76|      0|      }                                                                                 \
  |  |   77|      1|   } while(0)
  |  |  ------------------
  |  |  |  Branch (77:12): [Folded, False: 1]
  |  |  ------------------
  ------------------
  199|       |
  200|      1|   const size_t blocks = y_size - (y_size % 8);
  201|       |
  202|      1|   for(size_t i = 0; i != blocks; i += 8) {
  ------------------
  |  Branch (202:22): [True: 0, False: 1]
  ------------------
  203|      0|      borrow = word8_sub3(z + i, x + i, y + i, borrow);
  204|      0|   }
  205|       |
  206|      2|   for(size_t i = blocks; i != y_size; ++i) {
  ------------------
  |  Branch (206:27): [True: 1, False: 1]
  ------------------
  207|      1|      z[i] = word_sub(x[i], y[i], &borrow);
  208|      1|   }
  209|       |
  210|      5|   for(size_t i = y_size; i != x_size; ++i) {
  ------------------
  |  Branch (210:27): [True: 4, False: 1]
  ------------------
  211|      4|      z[i] = word_sub(x[i], static_cast<W>(0), &borrow);
  212|      4|   }
  213|       |
  214|      1|   return borrow;
  215|      1|}
_ZN5Botan14divide_precompImEC2Emmmm:
  771|    117|            m_divisor(divisor), m_shift(shift), m_norm_divisor(norm_divisor), m_reciprocal(reciprocal) {}
_ZNK5Botan14divide_precompImE14divmod_2to1_ctEmm:
  731|    564|      inline constexpr std::pair<W, W> divmod_2to1_ct(W n1, W n0) const {
  732|       |         /*
  733|       |         * Scale the numerator to match the normalized divisor; the scaling cancels
  734|       |         * in the quotient and is removed from the remainder by the final shift.
  735|       |         */
  736|    564|         n1 <<= m_shift;
  737|    564|         const size_t rshift = WordInfo<W>::bits - m_shift;
  738|       |         /*
  739|       |         * We can't just `n0 >> rshift` because m_shift may be 0 (high bit divisor)
  740|       |         * in which case rshift will be the maximum bit length, and >> by the maximum
  741|       |         * bitlength is undefined behavior despite having an entirely obvious meaning.
  742|       |         *
  743|       |         * However we know m_shift is not equal to the bit length of the word, because
  744|       |         * that would imply that the divisor is 0. Thus rshift must be >= 1. So split
  745|       |         * the shift into two phases (first rshift-1, then 1)
  746|       |         */
  747|    564|         n1 |= (n0 >> 1) >> (rshift - 1);
  748|    564|         n0 <<= m_shift;
  749|       |
  750|    564|         const auto [q, scaled_r] = div2by1_preinv(n1, n0, m_norm_divisor, m_reciprocal);
  751|    564|         const W r = scaled_r >> m_shift;
  752|    564|         return std::make_pair(q, r);
  753|    564|      }
_ZN5Botan14divide_precompImE14div2by1_preinvEmmmm:
  782|    564|      static constexpr std::pair<W, W> div2by1_preinv(W u1, W u0, W D, W v) {
  783|       |         // Steps 1-3: <q1,q0> = v*u1; <q1,q0> += <u1,u0>; q1 += 1
  784|    564|         W q1 = u0;
  785|    564|         const W q0 = word_madd2(u1, v, &q1);  // <q1,q0> = u1*v + u0
  786|    564|         q1 += u1 + 1;
  787|       |
  788|       |         // Step 4
  789|    564|         W r = u0 - q1 * D;
  790|       |
  791|       |         // Steps 5-7: If r >= q0 { q1 -= 1; r += d; }
  792|    564|         const auto fix_down = CT::Mask<W>::is_gt(r, q0);
  793|    564|         q1 -= fix_down.if_set_return(1);
  794|    564|         r += fix_down.if_set_return(D);
  795|       |
  796|       |         // Steps 8-10: if r >= D { q1 += 1; r -= D; }
  797|    564|         const auto fix_up = CT::Mask<W>::is_gte(r, D);
  798|    564|         q1 += fix_up.if_set_return(1);
  799|    564|         r -= fix_up.if_set_return(D);
  800|       |
  801|       |         // Step 11
  802|    564|         return std::make_pair(q1, r);
  803|    564|      }
_ZN5Botan15bigint_ct_is_ltITkNS_8WordTypeEmEENS_2CT4MaskIT_EEPKS3_mS6_mb:
  510|    120|   -> CT::Mask<W> {
  511|    120|   const size_t common_elems = std::min(x_size, y_size);
  512|       |
  513|    120|   auto is_lt = CT::Mask<W>::expand(lt_or_equal);
  514|       |
  515|  1.08k|   for(size_t i = 0; i != common_elems; i++) {
  ------------------
  |  Branch (515:22): [True: 960, False: 120]
  ------------------
  516|    960|      const auto eq = CT::Mask<W>::is_equal(x[i], y[i]);
  517|    960|      const auto lt = CT::Mask<W>::is_lt(x[i], y[i]);
  518|    960|      is_lt = eq.select_mask(is_lt, lt);
  519|    960|   }
  520|       |
  521|    120|   if(x_size < y_size) {
  ------------------
  |  Branch (521:7): [True: 0, False: 120]
  ------------------
  522|      0|      W mask = 0;
  523|      0|      for(size_t i = x_size; i != y_size; i++) {
  ------------------
  |  Branch (523:30): [True: 0, False: 0]
  ------------------
  524|      0|         mask |= y[i];
  525|      0|      }
  526|       |      // If any bits were set in high part of y, then is_lt should be forced true
  527|      0|      is_lt |= CT::Mask<W>::expand(mask);
  528|    120|   } else if(y_size < x_size) {
  ------------------
  |  Branch (528:14): [True: 0, False: 120]
  ------------------
  529|      0|      W mask = 0;
  530|      0|      for(size_t i = y_size; i != x_size; i++) {
  ------------------
  |  Branch (530:30): [True: 0, False: 0]
  ------------------
  531|      0|         mask |= x[i];
  532|      0|      }
  533|       |
  534|       |      // If any bits were set in high part of x, then is_lt should be false
  535|      0|      is_lt &= CT::Mask<W>::is_zero(mask);
  536|      0|   }
  537|       |
  538|    120|   return is_lt;
  539|    120|}
_ZN5Botan14divide_precompImE5setupEm:
  694|    117|      static constexpr divide_precomp setup(W divisor) {
  695|    117|         const size_t shift = WordInfo<W>::bits - high_bit(divisor);
  696|    117|         const W norm_divisor = divisor << shift;
  697|    117|         const W reciprocal = reciprocal_word_ct(norm_divisor);
  698|    117|         return divide_precomp(divisor, shift, norm_divisor, reciprocal);
  699|    117|      }
_ZN5Botan18reciprocal_word_ctITkNS_8WordTypeEmEET_S1_:
  649|    117|constexpr W reciprocal_word_ct(W D) {
  650|    117|   BOTAN_DEBUG_ASSERT((D & WordInfo<W>::top_bit) != 0);
  ------------------
  |  |  137|    117|      do { /* NOLINT(*-avoid-do-while) */ \
  |  |  138|    117|      } while(0)
  |  |  ------------------
  |  |  |  Branch (138:15): [Folded, False: 117]
  |  |  ------------------
  ------------------
  651|       |
  652|       |   // Bit serial long division of ((~D) || (2^b - 1)) / D, with all of the
  653|       |   // conditional logic computed via masks
  654|    117|   W remainder = static_cast<W>(~D);
  655|    117|   W quotient = 0;
  656|       |
  657|  7.60k|   for(size_t i = 0; i != WordInfo<W>::bits; ++i) {
  ------------------
  |  Branch (657:22): [True: 7.48k, False: 117]
  ------------------
  658|  7.48k|      const auto carry = CT::Mask<W>::expand_top_bit(remainder);
  659|  7.48k|      remainder = static_cast<W>((remainder << 1) | 1);
  660|  7.48k|      quotient <<= 1;
  661|       |
  662|  7.48k|      const auto sub = carry | CT::Mask<W>::is_gte(remainder, D);
  663|  7.48k|      remainder -= sub.if_set_return(D);
  664|  7.48k|      quotient |= sub.if_set_return(1);
  665|  7.48k|   }
  666|       |
  667|    117|   return quotient;
  668|    117|}
_ZN5Botan13bigint_submulITkNS_8WordTypeEmEET_PS1_PKS1_mS1_:
  442|    564|inline constexpr auto bigint_submul(W z[], const W y[], size_t N, W q) -> W {
  443|    564|   W mul_carry = 0;
  444|    564|   W borrow = 0;
  445|       |
  446|  2.82k|   for(size_t i = 0; i != N; ++i) {
  ------------------
  |  Branch (446:22): [True: 2.25k, False: 564]
  ------------------
  447|  2.25k|      const W t = word_madd2(y[i], q, &mul_carry);
  448|  2.25k|      z[i] = word_sub(z[i], t, &borrow);
  449|  2.25k|   }
  450|       |
  451|       |   // Both mul_carry <= W_max - 1 and borrow <= 1, so this cannot overflow
  452|    564|   return mul_carry + borrow;
  453|    564|}
_ZN5Botan14bigint_cnd_addITkNS_8WordTypeEmEET_S1_PS1_PKS1_m:
   48|    564|inline constexpr W bigint_cnd_add(W cnd, W x[], const W y[], size_t size) {
   49|    564|   const auto mask = CT::Mask<W>::expand(cnd).value();
   50|       |
   51|    564|   W carry = 0;
   52|       |
   53|  2.82k|   for(size_t i = 0; i != size; ++i) {
  ------------------
  |  Branch (53:22): [True: 2.25k, False: 564]
  ------------------
   54|  2.25k|      x[i] = word_add(x[i], y[i] & mask, &carry);
   55|  2.25k|   }
   56|       |
   57|    564|   return (mask & carry);
   58|    564|}

_ZN5Botan2CT13value_barrierITkNSt3__117unsigned_integralEmQntsr3stdE7same_asIbT_EEES3_S3_:
   43|   106k|constexpr inline T value_barrier(T x) {
   44|   106k|   if(std::is_constant_evaluated()) {
  ------------------
  |  Branch (44:7): [Folded, False: 106k]
  ------------------
   45|      0|      return x;
   46|   106k|   } else {
   47|   106k|#if defined(BOTAN_CT_VALUE_BARRIER_USE_ASM)
   48|       |      /*
   49|       |      * We may want a "stronger" statement such as
   50|       |      *     asm volatile("" : "+r,m"(x) : : "memory);
   51|       |      * (see https://theunixzoo.co.uk/blog/2021-10-14-preventing-optimisations.html)
   52|       |      * however the current approach seems sufficient with current compilers,
   53|       |      * and is minimally damaging with regards to degrading code generation.
   54|       |      */
   55|   106k|      asm("" : "+r"(x) : /* no input */);  // NOLINT(*-no-assembler)
   56|   106k|      return x;
   57|       |#elif defined(BOTAN_CT_VALUE_BARRIER_USE_VOLATILE)
   58|       |      volatile T vx = x;
   59|       |      return vx;
   60|       |#else
   61|       |      return x;
   62|       |#endif
   63|   106k|   }
   64|   106k|}
_ZN5Botan2CT13value_barrierITkNSt3__117unsigned_integralEhQntsr3stdE7same_asIbT_EEES3_S3_:
   43|    590|constexpr inline T value_barrier(T x) {
   44|    590|   if(std::is_constant_evaluated()) {
  ------------------
  |  Branch (44:7): [Folded, False: 590]
  ------------------
   45|      0|      return x;
   46|    590|   } else {
   47|    590|#if defined(BOTAN_CT_VALUE_BARRIER_USE_ASM)
   48|       |      /*
   49|       |      * We may want a "stronger" statement such as
   50|       |      *     asm volatile("" : "+r,m"(x) : : "memory);
   51|       |      * (see https://theunixzoo.co.uk/blog/2021-10-14-preventing-optimisations.html)
   52|       |      * however the current approach seems sufficient with current compilers,
   53|       |      * and is minimally damaging with regards to degrading code generation.
   54|       |      */
   55|    590|      asm("" : "+r"(x) : /* no input */);  // NOLINT(*-no-assembler)
   56|    590|      return x;
   57|       |#elif defined(BOTAN_CT_VALUE_BARRIER_USE_VOLATILE)
   58|       |      volatile T vx = x;
   59|       |      return vx;
   60|       |#else
   61|       |      return x;
   62|       |#endif
   63|    590|   }
   64|    590|}

_ZN5Botan13ignore_paramsIJPKmmEEEvDpRKT_:
  149|  1.31k|constexpr void ignore_params([[maybe_unused]] const T&... args) {}

_ZN5BotanmiERKNS_6BigIntEm:
 1229|      1|inline BigInt operator-(const BigInt& x, word y) {
 1230|      1|   return BigInt::add2(x, &y, 1, BigInt::Negative);
 1231|      1|}
_ZN5Botan6BigInt10power_of_2Em:
  906|      1|      static BigInt power_of_2(size_t n) {
  907|      1|         BigInt b;
  908|      1|         b.set_bit(n);
  909|      1|         return b;
  910|      1|      }
_ZN5Botan6BigIntC2Ev:
   45|    827|      BigInt() = default;
_ZN5Botan6BigInt7set_bitEm:
  516|      1|      void set_bit(size_t n) { conditionally_set_bit(n, true); }
_ZN5Botan6BigInt21conditionally_set_bitEmb:
  526|      1|      void conditionally_set_bit(size_t n, bool set_it) {
  527|      1|         const size_t which = n / (sizeof(word) * 8);
  528|      1|         const word mask = static_cast<word>(set_it) << (n % (sizeof(word) * 8));
  529|      1|         m_data.set_word_at(which, word_at(which) | mask);
  530|      1|      }
_ZN5Botan6BigInt4Data11set_word_atEmm:
 1106|      1|            void set_word_at(size_t i, word w) {
 1107|      1|               invalidate_sig_words();
 1108|      1|               if(i >= m_reg.size()) {
  ------------------
  |  Branch (1108:19): [True: 1, False: 0]
  ------------------
 1109|      1|                  if(w == 0) {
  ------------------
  |  Branch (1109:22): [True: 0, False: 1]
  ------------------
 1110|      0|                     return;
 1111|      0|                  }
 1112|      1|                  grow_to(i + 1);
 1113|      1|               }
 1114|      1|               m_reg[i] = w;
 1115|      1|            }
_ZNK5Botan6BigInt4Data20invalidate_sig_wordsEv:
 1161|    359|            void invalidate_sig_words() const noexcept { m_sig_words = sig_words_npos; }
_ZNK5Botan6BigInt4Data7grow_toEm:
 1126|      3|            void grow_to(size_t n) const {
 1127|      3|               if(n > size()) {
  ------------------
  |  Branch (1127:19): [True: 3, False: 0]
  ------------------
 1128|      3|                  if(n <= m_reg.capacity()) {
  ------------------
  |  Branch (1128:22): [True: 0, False: 3]
  ------------------
 1129|      0|                     m_reg.resize(n);
 1130|      3|                  } else {
 1131|      3|                     m_reg.resize(n + (8 - (n % 8)));
 1132|      3|                  }
 1133|      3|               }
 1134|      3|            }
_ZNK5Botan6BigInt4Data4sizeEv:
 1136|  1.32k|            size_t size() const { return m_reg.size(); }
_ZNK5Botan6BigInt7word_atEm:
  601|      1|      word word_at(size_t n) const { return m_data.get_word_at(n); }
_ZNK5Botan6BigInt4Data11get_word_atEm:
 1099|      1|            word get_word_at(size_t n) const {
 1100|      1|               if(n < m_reg.size()) {
  ------------------
  |  Branch (1100:19): [True: 0, False: 1]
  ------------------
 1101|      0|                  return m_reg[n];
 1102|      0|               }
 1103|      1|               return 0;
 1104|      1|            }
_ZN5Botan6BigIntD2Ev:
  185|    830|      ~BigInt() { _const_time_unpoison(); }
_ZN5BotangeERKNS_6BigIntES2_:
 1343|    122|inline bool operator>=(const BigInt& a, const BigInt& b) {
 1344|    122|   return (a.cmp(b) >= 0);
 1345|    122|}
_ZNK5Botan6BigInt5_dataEv:
 1033|  2.10k|      const word* _data() const { return m_data.const_data(); }
_ZNK5Botan6BigInt4Data10const_dataEv:
 1088|  2.93k|            const word* const_data() const { return m_reg.data(); }
_ZNK5Botan6BigInt9sig_wordsEv:
  687|  2.15k|      size_t sig_words() const { return m_data.sig_words(); }
_ZNK5Botan6BigInt4Data9sig_wordsEv:
 1163|  2.15k|            size_t sig_words() const {
 1164|  2.15k|               if(m_sig_words == sig_words_npos) {
  ------------------
  |  Branch (1164:19): [True: 242, False: 1.91k]
  ------------------
 1165|    242|                  m_sig_words = calc_sig_words();
 1166|    242|               }
 1167|  2.15k|               return m_sig_words;
 1168|  2.15k|            }
_ZN5Botan6BigIntaSEOS0_:
  190|    468|      BigInt& operator=(BigInt&& other) noexcept {
  191|    468|         if(this != &other) {
  ------------------
  |  Branch (191:13): [True: 468, False: 0]
  ------------------
  192|    468|            this->swap(other);
  193|    468|         }
  194|       |
  195|    468|         return (*this);
  196|    468|      }
_ZN5Botan6BigInt4swapERS0_:
  207|    468|      void swap(BigInt& other) noexcept {
  208|    468|         m_data.swap(other.m_data);
  209|    468|         std::swap(m_signedness, other.m_signedness);
  210|    468|      }
_ZN5Botan6BigInt5clearEv:
  441|    122|      void clear() {
  442|    122|         m_data.set_to_zero();
  443|    122|         m_signedness = Positive;
  444|    122|      }
_ZNK5Botan6BigInt6signumEv:
  493|  1.44k|      int signum() const {
  494|  1.44k|         if(sig_words() == 0) {
  ------------------
  |  Branch (494:13): [True: 4, False: 1.44k]
  ------------------
  495|      4|            return 0;
  496|      4|         }
  497|  1.44k|         return (sign() == Negative) ? -1 : 1;
  ------------------
  |  Branch (497:17): [True: 0, False: 1.44k]
  ------------------
  498|  1.44k|      }
_ZNK5Botan6BigInt7is_zeroEv:
  510|    237|      bool is_zero() const { return sig_words() == 0; }
_ZNK5Botan6BigInt4signEv:
  641|  1.79k|      Sign sign() const { return (m_signedness); }
_ZN5Botan6BigInt8set_signENS0_4SignE:
  663|    119|      void set_sign(Sign sign) {
  664|    119|         if(sign == Negative && is_zero()) {
  ------------------
  |  Branch (664:13): [True: 0, False: 119]
  |  Branch (664:33): [True: 0, False: 0]
  ------------------
  665|      0|            sign = Positive;
  666|      0|         }
  667|       |
  668|    119|         m_signedness = sign;
  669|    119|      }
_ZNK5Botan6BigInt4sizeEv:
  681|    490|      size_t size() const { return m_data.size(); }
_ZN5Botan6BigInt12mutable_dataEv:
  712|      2|      BOTAN_DEPRECATED("Deprecated no replacement") word* mutable_data() { return m_data.mutable_data(); }
_ZNK5Botan6BigInt4dataEv:
  718|      2|      BOTAN_DEPRECATED("Deprecated no replacement") const word* data() const { return m_data.const_data(); }
_ZNK5Botan6BigInt7grow_toEm:
  738|      2|      BOTAN_DEPRECATED("Deprecated no replacement") void grow_to(size_t n) const { m_data.grow_to(n); }
_ZN5Botan6BigInt11_from_wordsERNSt3__16vectorImNS_16secure_allocatorImEEEE:
 1052|    234|      static BigInt _from_words(secure_vector<word>& words) {
 1053|    234|         BigInt bn;
 1054|    234|         bn.m_data.swap(words);
 1055|    234|         return bn;
 1056|    234|      }
_ZN5Botan6BigInt4Data12mutable_dataEv:
 1083|      2|            word* mutable_data() {
 1084|      2|               invalidate_sig_words();
 1085|      2|               return m_reg.data();
 1086|      2|            }
_ZN5Botan6BigInt4Data4swapERS1_:
 1151|    468|            void swap(Data& other) noexcept {
 1152|    468|               m_reg.swap(other.m_reg);
 1153|    468|               std::swap(m_sig_words, other.m_sig_words);
 1154|    468|            }
_ZN5Botan6BigInt4Data4swapERNSt3__16vectorImNS_16secure_allocatorImEEEE:
 1156|    356|            void swap(secure_vector<word>& reg) noexcept {
 1157|    356|               m_reg.swap(reg);
 1158|    356|               invalidate_sig_words();
 1159|    356|            }
_ZN5BotanltERKNS_6BigIntES2_:
 1353|    120|inline bool operator<(const BigInt& a, const BigInt& b) {
 1354|    120|   return a.is_less_than(b);
 1355|    120|}
_ZN5Botan6BigIntC2ERKS0_:
   88|      3|      BigInt(const BigInt& other) = default;

_ZN5Botan13typecast_copyImTkNS_6ranges16contiguous_rangeENSt3__14spanIKhLm18446744073709551615EEEQaaaasr3stdE26is_default_constructible_vIT_Esr3stdE23is_trivially_copyable_vIS6_Esr3stdE23is_trivially_copyable_vINS2_11conditionalIXsr21__is_primary_templateINS2_15iterator_traitsIu14__remove_cvrefIDTclL_ZNS2_6ranges5__cpo5beginEEclsr3stdE7declvalIRT0_EEEEEEEEE5valueENS2_26indirectly_readable_traitsISE_EESF_E4type10value_typeEEEES6_RKSB_:
  210|    960|inline constexpr ToT typecast_copy(const FromR& src) {
  211|    960|   ToT dst;  // NOLINT(*-member-init)
  212|    960|   typecast_copy(dst, src);
  213|    960|   return dst;
  214|    960|}
_ZN5Botan13typecast_copyImTkNS_6ranges16contiguous_rangeENSt3__14spanIKhLm18446744073709551615EEEQaaaasr3stdE23is_trivially_copyable_vINS2_11conditionalIXsr21__is_primary_templateINS2_15iterator_traitsIu14__remove_cvrefIDTclL_ZNS2_6ranges5__cpo5beginEEclsr3stdE7declvalIRT0_EEEEEEEEE5valueENS2_26indirectly_readable_traitsISD_EESE_E4type10value_typeEEsr3stdE23is_trivially_copyable_vIT_Entsr3std6rangesE5rangeISK_EEEvRSK_RKSA_:
  188|    960|inline constexpr void typecast_copy(ToT& out, const FromR& in) {
  189|    960|   typecast_copy(std::span<ToT, 1>(&out, 1), in);
  190|    960|}
_ZN5Botan13typecast_copyITkNS_6ranges23contiguous_output_rangeENSt3__14spanImLm1EEETkNS1_16contiguous_rangeENS3_IKhLm18446744073709551615EEEQaasr3stdE23is_trivially_copyable_vINS2_11conditionalIXsr21__is_primary_templateINS2_15iterator_traitsIu14__remove_cvrefIDTclL_ZNS2_6ranges5__cpo5beginEEclsr3stdE7declvalIRT0_EEEEEEEEE5valueENS2_26indirectly_readable_traitsISE_EESF_E4type10value_typeEEsr3stdE23is_trivially_copyable_vINS7_IXsr21__is_primary_templateINS8_Iu14__remove_cvrefIDTclL_ZNSA_5beginEEclsr3stdE7declvalIRT_EEEEEEEEE5valueENSG_ISO_EESP_E4type10value_typeEEEEvOSL_RKSB_:
  176|    960|inline constexpr void typecast_copy(ToR&& out /* NOLINT(*-std-forward) */, const FromR& in) {
  177|    960|   ranges::assert_equal_byte_lengths(out, in);
  178|    960|   std::memcpy(std::ranges::data(out), std::ranges::data(in), ranges::size_bytes(out));
  179|    960|}
_ZN5Botan11clear_bytesEPvm:
  101|    128|inline constexpr void clear_bytes(void* ptr, size_t bytes) {
  102|    128|   if(bytes > 0) {
  ------------------
  |  Branch (102:7): [True: 6, False: 122]
  ------------------
  103|      6|      std::memset(ptr, 0, bytes);
  104|      6|   }
  105|    128|}
_ZN5Botan9clear_memImEEvPT_m:
  118|    122|inline constexpr void clear_mem(T* ptr, size_t n) {
  119|    122|   clear_bytes(ptr, sizeof(T) * n);
  120|    122|}
_ZN5Botan13typecast_copyImTkNS_6ranges16contiguous_rangeENSt3__14spanIKhLm8EEEQaaaasr3stdE26is_default_constructible_vIT_Esr3stdE23is_trivially_copyable_vIS6_Esr3stdE23is_trivially_copyable_vINS2_11conditionalIXsr21__is_primary_templateINS2_15iterator_traitsIu14__remove_cvrefIDTclL_ZNS2_6ranges5__cpo5beginEEclsr3stdE7declvalIRT0_EEEEEEEEE5valueENS2_26indirectly_readable_traitsISE_EESF_E4type10value_typeEEEES6_RKSB_:
  210|    976|inline constexpr ToT typecast_copy(const FromR& src) {
  211|    976|   ToT dst;  // NOLINT(*-member-init)
  212|    976|   typecast_copy(dst, src);
  213|    976|   return dst;
  214|    976|}
_ZN5Botan13typecast_copyImTkNS_6ranges16contiguous_rangeENSt3__14spanIKhLm8EEEQaaaasr3stdE23is_trivially_copyable_vINS2_11conditionalIXsr21__is_primary_templateINS2_15iterator_traitsIu14__remove_cvrefIDTclL_ZNS2_6ranges5__cpo5beginEEclsr3stdE7declvalIRT0_EEEEEEEEE5valueENS2_26indirectly_readable_traitsISD_EESE_E4type10value_typeEEsr3stdE23is_trivially_copyable_vIT_Entsr3std6rangesE5rangeISK_EEEvRSK_RKSA_:
  188|    976|inline constexpr void typecast_copy(ToT& out, const FromR& in) {
  189|    976|   typecast_copy(std::span<ToT, 1>(&out, 1), in);
  190|    976|}
_ZN5Botan13typecast_copyITkNS_6ranges23contiguous_output_rangeENSt3__14spanImLm1EEETkNS1_16contiguous_rangeENS3_IKhLm8EEEQaasr3stdE23is_trivially_copyable_vINS2_11conditionalIXsr21__is_primary_templateINS2_15iterator_traitsIu14__remove_cvrefIDTclL_ZNS2_6ranges5__cpo5beginEEclsr3stdE7declvalIRT0_EEEEEEEEE5valueENS2_26indirectly_readable_traitsISE_EESF_E4type10value_typeEEsr3stdE23is_trivially_copyable_vINS7_IXsr21__is_primary_templateINS8_Iu14__remove_cvrefIDTclL_ZNSA_5beginEEclsr3stdE7declvalIRT_EEEEEEEEE5valueENSG_ISO_EESP_E4type10value_typeEEEEvOSL_RKSB_:
  176|    976|inline constexpr void typecast_copy(ToR&& out /* NOLINT(*-std-forward) */, const FromR& in) {
  177|    976|   ranges::assert_equal_byte_lengths(out, in);
  178|    976|   std::memcpy(std::ranges::data(out), std::ranges::data(in), ranges::size_bytes(out));
  179|    976|}
_ZN5Botan9clear_memIjEEvPT_m:
  118|      6|inline constexpr void clear_mem(T* ptr, size_t n) {
  119|      6|   clear_bytes(ptr, sizeof(T) * n);
  120|      6|}

_ZN5Botan6ranges24assert_exact_byte_lengthILm8ETkNS0_14spanable_rangeENSt3__14spanIKhLm18446744073709551615EEEEEvRKT0_:
   77|  1.92k|inline constexpr void assert_exact_byte_length(const R& r) {
   78|  1.92k|   const std::span s{r};
   79|       |   if constexpr(statically_spanable_range<R>) {
   80|       |      static_assert(s.size_bytes() == expected, "memory region does not have expected byte lengths");
   81|  1.92k|   } else {
   82|  1.92k|      if(s.size_bytes() != expected) {
  ------------------
  |  Branch (82:10): [True: 0, False: 1.92k]
  ------------------
   83|      0|         memory_region_size_violation();
   84|      0|      }
   85|  1.92k|   }
   86|  1.92k|}
_ZN5Botan6ranges25assert_equal_byte_lengthsITkNS0_14spanable_rangeENSt3__14spanImLm1EEETpTkNS0_14spanable_rangeEJNS3_IKhLm18446744073709551615EEEEEEvRKT_DpRKT0_QgtsZT0_Li0E:
  100|    960|{
  101|    960|   const std::span s0{r0};
  102|       |
  103|    960|   if constexpr(statically_spanable_range<R0>) {
  104|    960|      constexpr size_t expected_size = s0.size_bytes();
  105|    960|      (assert_exact_byte_length<expected_size>(rs), ...);
  106|       |   } else {
  107|       |      const size_t expected_size = s0.size_bytes();
  108|       |      const bool correct_size =
  109|       |         ((std::span<const std::ranges::range_value_t<Rs>>{rs}.size_bytes() == expected_size) && ...);
  110|       |
  111|       |      if(!correct_size) {
  112|       |         memory_region_size_violation();
  113|       |      }
  114|       |   }
  115|    960|}
_ZN5Botan6ranges10size_bytesITkNS0_14spanable_rangeENSt3__14spanImLm1EEEEEmRKT_:
   59|  1.93k|inline constexpr size_t size_bytes(const spanable_range auto& r) {
   60|  1.93k|   return std::span{r}.size_bytes();
   61|  1.93k|}
_ZN5Botan6ranges24assert_exact_byte_lengthILm8ETkNS0_14spanable_rangeENSt3__14spanIKhLm8EEEEEvRKT0_:
   77|  1.95k|inline constexpr void assert_exact_byte_length(const R& r) {
   78|  1.95k|   const std::span s{r};
   79|  1.95k|   if constexpr(statically_spanable_range<R>) {
   80|  1.95k|      static_assert(s.size_bytes() == expected, "memory region does not have expected byte lengths");
   81|       |   } else {
   82|       |      if(s.size_bytes() != expected) {
   83|       |         memory_region_size_violation();
   84|       |      }
   85|       |   }
   86|  1.95k|}
_ZN5Botan6ranges25assert_equal_byte_lengthsITkNS0_14spanable_rangeENSt3__14spanImLm1EEETpTkNS0_14spanable_rangeEJNS3_IKhLm8EEEEEEvRKT_DpRKT0_QgtsZT0_Li0E:
  100|    976|{
  101|    976|   const std::span s0{r0};
  102|       |
  103|    976|   if constexpr(statically_spanable_range<R0>) {
  104|    976|      constexpr size_t expected_size = s0.size_bytes();
  105|    976|      (assert_exact_byte_length<expected_size>(rs), ...);
  106|       |   } else {
  107|       |      const size_t expected_size = s0.size_bytes();
  108|       |      const bool correct_size =
  109|       |         ((std::span<const std::ranges::range_value_t<Rs>>{rs}.size_bytes() == expected_size) && ...);
  110|       |
  111|       |      if(!correct_size) {
  112|       |         memory_region_size_violation();
  113|       |      }
  114|       |   }
  115|    976|}

_ZN5Botan16secure_allocatorImE8allocateEm:
   95|    480|      T* allocate(std::size_t n) { return static_cast<T*>(allocate_memory(n, sizeof(T))); }
_ZN5Botan16secure_allocatorImE10deallocateEPmm:
  102|    480|      void deallocate(T* p, std::size_t n) { deallocate_memory(p, n, sizeof(T)); }

_ZN5Botan16wrap_strong_typeImRmQoosr3stdE18constructible_fromIT_T0_Eaasr8conceptsE11strong_typeIS2_Esr3stdE18constructible_fromINS2_12wrapped_typeES3_EEEDcOS3_:
  144|  1.93k|[[nodiscard]] constexpr decltype(auto) wrap_strong_type(ParamT&& t) {
  145|  1.93k|   if constexpr(std::same_as<std::remove_cvref_t<ParamT>, T>) {
  146|       |      // Noop, if the parameter type already is the desired return type.
  147|  1.93k|      return std::forward<ParamT>(t);
  148|       |   } else if constexpr(std::constructible_from<T, ParamT>) {
  149|       |      // Implicit conversion from the parameter type to the return type.
  150|       |      return T{std::forward<ParamT>(t)};
  151|       |   } else {
  152|       |      // Explicitly calling the wrapped type's constructor to support
  153|       |      // implicit conversions on types that mark their constructors as explicit.
  154|       |      static_assert(concepts::strong_type<T> && std::constructible_from<typename T::wrapped_type, ParamT>);
  155|       |      return T{typename T::wrapped_type{std::forward<ParamT>(t)}};
  156|       |   }
  157|  1.93k|}

LLVMFuzzerInitialize:
   28|      2|extern "C" int LLVMFuzzerInitialize(int* /*argc*/, char*** /*argv*/) {
   29|       |   /*
   30|       |   * This disables the mlock pool, as overwrites within the pool are
   31|       |   * opaque to ASan or other instrumentation.
   32|       |   */
   33|      2|   ::setenv("BOTAN_MLOCK_POOL_SIZE", "0", 1);
   34|      2|   return 0;
   35|      2|}
LLVMFuzzerTestOneInput:
   39|    172|extern "C" int LLVMFuzzerTestOneInput(const uint8_t in[], size_t len) {
   40|    172|   if(len <= max_fuzzer_input_size) {
  ------------------
  |  Branch (40:7): [True: 162, False: 10]
  ------------------
   41|    162|      try {
   42|    162|         fuzz(std::span<const uint8_t>(in, len));
   43|    162|      } catch(const std::exception& e) {
   44|      0|         std::cerr << "Uncaught exception from fuzzer driver " << e.what() << "\n";
   45|      0|         abort();
   46|      0|      } catch(...) {
   47|      0|         std::cerr << "Uncaught exception from fuzzer driver (unknown type)\n";
   48|      0|         abort();
   49|      0|      }
   50|    162|   }
   51|    172|   return 0;
   52|    172|}

_Z16compare_word_vecPKmmS0_mPKc:
   37|    120|inline void compare_word_vec(const word x[], size_t x_len, const word y[], size_t y_len, const char* comparing) {
   38|    120|   const size_t common_words = std::min(x_len, y_len);
   39|       |
   40|    583|   for(size_t i = 0; i != common_words; ++i) {
  ------------------
  |  Branch (40:22): [True: 463, False: 120]
  ------------------
   41|    463|      if(x[i] != y[i]) {
  ------------------
  |  Branch (41:10): [True: 0, False: 463]
  ------------------
   42|      0|         dump_word_vec("x", x, x_len);
   43|      0|         dump_word_vec("y", y, y_len);
   44|      0|         FUZZER_WRITE_AND_CRASH("Comparison failed " << comparing);
  ------------------
  |  |   70|      0|   do {                                                                                                       \
  |  |   71|      0|      std::cerr << expr << " @ Line " << __LINE__ << " in " << __FILE__ << "\n"; /* NOLINT(*-macro-paren*) */ \
  |  |   72|      0|      abort();                                                                                                \
  |  |   73|      0|   } while(0)
  |  |  ------------------
  |  |  |  Branch (73:12): [Folded, False: 0]
  |  |  ------------------
  ------------------
   45|      0|      }
   46|    463|   }
   47|       |
   48|       |   // all other words must be zero
   49|    137|   for(size_t i = common_words; i != x_len; ++i) {
  ------------------
  |  Branch (49:33): [True: 17, False: 120]
  ------------------
   50|     17|      if(x[i] != 0) {
  ------------------
  |  Branch (50:10): [True: 0, False: 17]
  ------------------
   51|      0|         dump_word_vec("x", x, x_len);
   52|      0|         dump_word_vec("y", y, y_len);
   53|      0|         FUZZER_WRITE_AND_CRASH("Unexpected non-zero in high words of x " << comparing);
  ------------------
  |  |   70|      0|   do {                                                                                                       \
  |  |   71|      0|      std::cerr << expr << " @ Line " << __LINE__ << " in " << __FILE__ << "\n"; /* NOLINT(*-macro-paren*) */ \
  |  |   72|      0|      abort();                                                                                                \
  |  |   73|      0|   } while(0)
  |  |  ------------------
  |  |  |  Branch (73:12): [Folded, False: 0]
  |  |  ------------------
  ------------------
   54|      0|      }
   55|     17|   }
   56|    120|   for(size_t i = common_words; i != y_len; ++i) {
  ------------------
  |  Branch (56:33): [True: 0, False: 120]
  ------------------
   57|      0|      if(y[i] != 0) {
  ------------------
  |  Branch (57:10): [True: 0, False: 0]
  ------------------
   58|      0|         dump_word_vec("x", x, x_len);
   59|      0|         dump_word_vec("y", y, y_len);
   60|      0|         FUZZER_WRITE_AND_CRASH("Unexpected non-zero in high words of y " << comparing);
  ------------------
  |  |   70|      0|   do {                                                                                                       \
  |  |   71|      0|      std::cerr << expr << " @ Line " << __LINE__ << " in " << __FILE__ << "\n"; /* NOLINT(*-macro-paren*) */ \
  |  |   72|      0|      abort();                                                                                                \
  |  |   73|      0|   } while(0)
  |  |  ------------------
  |  |  |  Branch (73:12): [Folded, False: 0]
  |  |  ------------------
  ------------------
   61|      0|      }
   62|      0|   }
   63|    120|}

_Z4fuzzNSt3__14spanIKhLm18446744073709551615EEE:
   26|    162|void fuzz(std::span<const uint8_t> in) {
   27|    162|   if(in.size() != 8 * sizeof(word)) {
  ------------------
  |  Branch (27:7): [True: 40, False: 122]
  ------------------
   28|     40|      return;
   29|     40|   }
   30|       |
   31|    122|   constexpr word C = crandall_C();
   32|       |
   33|    122|   static const Botan::BigInt refp = Botan::BigInt::power_of_2(4 * 8 * sizeof(C)) - C;
   34|    122|   static const Botan::BigInt refp2 = refp * refp;
   35|       |
   36|    122|   const auto refz = Botan::BigInt::from_bytes(in);
   37|       |
   38|    122|   if(refz >= refp2) {
  ------------------
  |  Branch (38:7): [True: 2, False: 120]
  ------------------
   39|      2|      return;
   40|      2|   }
   41|       |
   42|    120|   const auto refc = refz % refp;
   43|       |
   44|    120|   std::array<word, 8> z = {};
   45|  1.08k|   for(size_t i = 0; i != 8; ++i) {
  ------------------
  |  Branch (45:22): [True: 960, False: 120]
  ------------------
   46|    960|      z[7 - i] = Botan::load_be<word>(in.subspan(sizeof(word) * i, sizeof(word)));
   47|    960|   }
   48|       |
   49|    120|   const auto rc = Botan::redc_crandall<word, 4, C>(z);
   50|       |
   51|    120|   compare_word_vec(rc.data(), 4, refc._data(), refc.sig_words(), "Crandall reduction");
   52|    120|}

_ZN5Botan6BigInt4add2ERKS0_PKmmNS0_4SignE:
   20|      1|BigInt BigInt::add2(const BigInt& x, const word y[], size_t y_size, BigInt::Sign y_sign) {
   21|      1|   const size_t x_sw = x.sig_words();
   22|       |
   23|      1|   BigInt z = BigInt::with_capacity(std::max(x_sw, y_size) + 1);
   24|       |
   25|      1|   if(x.sign() == y_sign) {
  ------------------
  |  Branch (25:7): [True: 0, False: 1]
  ------------------
   26|      0|      const word carry = bigint_add3(z.mutable_data(), x._data(), x_sw, y, y_size);
   27|      0|      z.mutable_data()[std::max(x_sw, y_size)] += carry;
   28|      0|      z.set_sign(x.sign());
   29|      1|   } else {
   30|      1|      const int32_t relative_size = bigint_cmp(x.data(), x_sw, y, y_size);
   31|       |
   32|      1|      if(relative_size < 0) {
  ------------------
  |  Branch (32:10): [True: 0, False: 1]
  ------------------
   33|       |         // x < y so z = abs(y - x)
   34|       |         // NOLINTNEXTLINE(*-suspicious-call-argument) intentionally swapping x and y here
   35|      0|         bigint_sub3(z.mutable_data(), y, y_size, x.data(), x_sw);
   36|      0|         z.set_sign(y_sign);
   37|      1|      } else if(relative_size == 0) {
  ------------------
  |  Branch (37:17): [True: 0, False: 1]
  ------------------
   38|       |         // Positive zero (nothing to do in this case)
   39|      1|      } else {
   40|       |         /*
   41|       |         * We know at this point that x >= y so if y_size is larger than
   42|       |         * x_sw, we are guaranteed they are just leading zeros which can
   43|       |         * be ignored
   44|       |         */
   45|      1|         y_size = std::min(x_sw, y_size);
   46|      1|         bigint_sub3(z.mutable_data(), x.data(), x_sw, y, y_size);
   47|      1|         z.set_sign(x.sign());
   48|      1|      }
   49|      1|   }
   50|       |
   51|      1|   return z;
   52|      1|}
_ZN5BotanmlERKNS_6BigIntES2_:
   57|      1|BigInt operator*(const BigInt& x, const BigInt& y) {
   58|      1|   const size_t x_sw = x.sig_words();
   59|      1|   const size_t y_sw = y.sig_words();
   60|       |
   61|      1|   BigInt z = BigInt::with_capacity(x.size() + y.size());
   62|       |
   63|      1|   if(x_sw == 1 && y_sw > 0) {
  ------------------
  |  Branch (63:7): [True: 0, False: 1]
  |  Branch (63:20): [True: 0, False: 0]
  ------------------
   64|      0|      bigint_linmul3(z.mutable_data(), y._data(), y_sw, x.word_at(0));
   65|      1|   } else if(y_sw == 1 && x_sw > 0) {
  ------------------
  |  Branch (65:14): [True: 0, False: 1]
  |  Branch (65:27): [True: 0, False: 0]
  ------------------
   66|      0|      bigint_linmul3(z.mutable_data(), x._data(), x_sw, y.word_at(0));
   67|      1|   } else if(x_sw > 0 && y_sw > 0) {
  ------------------
  |  Branch (67:14): [True: 1, False: 0]
  |  Branch (67:26): [True: 1, False: 0]
  ------------------
   68|      1|      secure_vector<word> workspace(z.size());
   69|       |
   70|      1|      bigint_mul(z.mutable_data(),
   71|      1|                 z.size(),
   72|      1|                 x._data(),
   73|      1|                 x.size(),
   74|      1|                 x_sw,
   75|      1|                 y._data(),
   76|      1|                 y.size(),
   77|      1|                 y_sw,
   78|      1|                 workspace.data(),
   79|      1|                 workspace.size());
   80|      1|   }
   81|       |
   82|      1|   z.cond_flip_sign(x_sw > 0 && y_sw > 0 && x.sign() != y.sign());
  ------------------
  |  Branch (82:21): [True: 1, False: 0]
  |  Branch (82:33): [True: 1, False: 0]
  |  Branch (82:45): [True: 0, False: 1]
  ------------------
   83|       |
   84|      1|   return z;
   85|      1|}
_ZN5BotanrmERKNS_6BigIntES2_:
  134|    120|BigInt operator%(const BigInt& n, const BigInt& mod) {
  135|    120|   if(mod.is_zero()) {
  ------------------
  |  Branch (135:7): [True: 0, False: 120]
  ------------------
  136|      0|      throw Invalid_Argument("BigInt::operator% divide by zero");
  137|      0|   }
  138|    120|   if(mod.signum() < 0) {
  ------------------
  |  Branch (138:7): [True: 0, False: 120]
  ------------------
  139|      0|      throw Invalid_Argument("BigInt::operator% modulus must be > 0");
  140|      0|   }
  141|    120|   if(n.signum() >= 0 && mod.signum() >= 0 && n < mod) {
  ------------------
  |  Branch (141:7): [True: 120, False: 0]
  |  Branch (141:26): [True: 120, False: 0]
  |  Branch (141:47): [True: 3, False: 117]
  ------------------
  142|      3|      return n;
  143|      3|   }
  144|       |
  145|    117|   if(mod.sig_words() == 1) {
  ------------------
  |  Branch (145:7): [True: 0, False: 117]
  ------------------
  146|      0|      return BigInt::from_word(n % mod.word_at(0));
  147|      0|   }
  148|       |
  149|    117|   BigInt q;
  150|    117|   BigInt r;
  151|    117|   ct_divide(n, mod, q, r);
  152|    117|   return r;
  153|    117|}

_ZN5Botan6BigInt13with_capacityEm:
   49|      2|BigInt BigInt::with_capacity(size_t size) {
   50|      2|   BigInt bn;
   51|      2|   bn.grow_to(size);
   52|      2|   return bn;
   53|      2|}
_ZN5Botan6BigInt10from_bytesENSt3__14spanIKhLm18446744073709551615EEE:
   81|    122|BigInt BigInt::from_bytes(std::span<const uint8_t> input) {
   82|    122|   BigInt r;
   83|    122|   r.assign_from_bytes(input);
   84|    122|   return r;
   85|    122|}
_ZNK5Botan6BigInt3cmpERKS0_b:
  136|    122|int32_t BigInt::cmp(const BigInt& other, bool check_signs) const {
  137|    122|   if(check_signs) {
  ------------------
  |  Branch (137:7): [True: 122, False: 0]
  ------------------
  138|    122|      if(other.signum() >= 0 && this->signum() < 0) {
  ------------------
  |  Branch (138:10): [True: 122, False: 0]
  |  Branch (138:33): [True: 0, False: 122]
  ------------------
  139|      0|         return -1;
  140|      0|      }
  141|       |
  142|    122|      if(other.signum() < 0 && this->signum() >= 0) {
  ------------------
  |  Branch (142:10): [True: 0, False: 122]
  |  Branch (142:32): [True: 0, False: 0]
  ------------------
  143|      0|         return 1;
  144|      0|      }
  145|       |
  146|    122|      if(other.signum() < 0 && this->signum() < 0) {
  ------------------
  |  Branch (146:10): [True: 0, False: 122]
  |  Branch (146:32): [True: 0, False: 0]
  ------------------
  147|      0|         return (-bigint_cmp(this->_data(), this->size(), other._data(), other.size()));
  148|      0|      }
  149|    122|   }
  150|       |
  151|    122|   return bigint_cmp(this->_data(), this->size(), other._data(), other.size());
  152|    122|}
_ZNK5Botan6BigInt12is_less_thanERKS0_:
  162|    120|bool BigInt::is_less_than(const BigInt& other) const {
  163|    120|   if(this->signum() < 0 && other.signum() >= 0) {
  ------------------
  |  Branch (163:7): [True: 0, False: 120]
  |  Branch (163:29): [True: 0, False: 0]
  ------------------
  164|      0|      return true;
  165|      0|   }
  166|       |
  167|    120|   if(this->signum() >= 0 && other.signum() < 0) {
  ------------------
  |  Branch (167:7): [True: 120, False: 0]
  |  Branch (167:30): [True: 0, False: 120]
  ------------------
  168|      0|      return false;
  169|      0|   }
  170|       |
  171|    120|   if(other.signum() < 0 && this->signum() < 0) {
  ------------------
  |  Branch (171:7): [True: 0, False: 120]
  |  Branch (171:29): [True: 0, False: 0]
  ------------------
  172|      0|      return bigint_ct_is_lt(other._data(), other.size(), this->_data(), this->size()).as_bool();
  173|      0|   }
  174|       |
  175|    120|   return bigint_ct_is_lt(this->_data(), this->size(), other._data(), other.size()).as_bool();
  176|    120|}
_ZN5Botan6BigInt4Data11set_to_zeroEv:
  189|    122|void BigInt::Data::set_to_zero() {
  190|    122|   m_reg.resize(m_reg.capacity());
  191|    122|   clear_mem(m_reg.data(), m_reg.size());
  192|    122|   m_sig_words = 0;
  193|    122|}
_ZNK5Botan6BigInt4Data14calc_sig_wordsEv:
  213|    242|size_t BigInt::Data::calc_sig_words() const {
  214|    242|   const size_t sz = m_reg.size();
  215|    242|   size_t sig = sz;
  216|       |
  217|    242|   word sub = 1;
  218|       |
  219|  1.72k|   for(size_t i = 0; i != sz; ++i) {
  ------------------
  |  Branch (219:22): [True: 1.48k, False: 242]
  ------------------
  220|  1.48k|      const word w = m_reg[sz - i - 1];
  221|  1.48k|      sub &= ct_is_zero(w);
  222|  1.48k|      sig -= sub;
  223|  1.48k|   }
  224|       |
  225|       |   /*
  226|       |   * This depends on the data so is poisoned, but unpoison it here as
  227|       |   * later conditionals are made on the size.
  228|       |   */
  229|    242|   CT::unpoison(sig);
  230|       |
  231|    242|   return sig;
  232|    242|}
_ZN5Botan6BigInt17assign_from_bytesENSt3__14spanIKhLm18446744073709551615EEE:
  423|    122|void BigInt::assign_from_bytes(std::span<const uint8_t> bytes) {
  424|    122|   clear();
  425|       |
  426|    122|   const size_t length = bytes.size();
  427|    122|   const size_t full_words = length / sizeof(word);
  428|    122|   const size_t extra_bytes = length % sizeof(word);
  429|       |
  430|    122|   const size_t total_words = full_words + (extra_bytes > 0 ? 1 : 0);
  ------------------
  |  Branch (430:45): [True: 0, False: 122]
  ------------------
  431|       |
  432|    122|   secure_vector<word> reg(8 * ceil_division<size_t>(total_words, 8));
  433|       |
  434|  1.09k|   for(size_t i = 0; i != full_words; ++i) {
  ------------------
  |  Branch (434:22): [True: 976, False: 122]
  ------------------
  435|    976|      reg[i] = load_be<word>(bytes.last<sizeof(word)>());
  436|    976|      bytes = bytes.first(bytes.size() - sizeof(word));
  437|    976|   }
  438|       |
  439|    122|   if(!bytes.empty()) {
  ------------------
  |  Branch (439:7): [True: 0, False: 122]
  ------------------
  440|      0|      BOTAN_ASSERT_NOMSG(extra_bytes == bytes.size());
  ------------------
  |  |   84|      0|   do {                                                                     \
  |  |   85|      0|      /* NOLINTNEXTLINE(*-simplify-boolean-expr) */                         \
  |  |   86|      0|      if(!(expr)) {                                                         \
  |  |  ------------------
  |  |  |  Branch (86:10): [True: 0, False: 0]
  |  |  ------------------
  |  |   87|      0|         /* NOLINTNEXTLINE(bugprone-lambda-function-name) */                \
  |  |   88|      0|         Botan::assertion_failure(#expr, "", __func__, __FILE__, __LINE__); \
  |  |   89|      0|      }                                                                     \
  |  |   90|      0|   } while(0)
  |  |  ------------------
  |  |  |  Branch (90:12): [Folded, False: 0]
  |  |  ------------------
  ------------------
  441|      0|      std::array<uint8_t, sizeof(word)> last_partial_word = {0};
  442|      0|      copy_mem(std::span{last_partial_word}.last(extra_bytes), bytes);
  443|      0|      reg[full_words] = load_be<word>(last_partial_word);
  444|      0|   }
  445|       |
  446|    122|   m_data.swap(reg);
  447|    122|}
_ZN5Botan6BigInt14cond_flip_signEb:
  521|    118|void BigInt::cond_flip_sign(bool predicate) {
  522|       |   // This code is assuming Negative == 0, Positive == 1
  523|       |
  524|    118|   const auto mask = CT::Mask<uint8_t>::expand_bool(predicate);
  525|       |
  526|    118|   const uint8_t current_sign = static_cast<uint8_t>(sign());
  527|       |
  528|    118|   const uint8_t new_sign = mask.select(current_sign ^ 1, current_sign);
  529|       |
  530|    118|   set_sign(static_cast<Sign>(new_sign));
  531|    118|}
_ZNK5Botan6BigInt20_const_time_unpoisonEv:
  559|    830|void BigInt::_const_time_unpoison() const {
  560|    830|   CT::unpoison(m_data.const_data(), m_data.size());
  561|    830|}

_ZN5Botan9ct_divideERKNS_6BigIntES2_RS0_S3_:
  180|    117|void ct_divide(const BigInt& x, const BigInt& y, BigInt& q_out, BigInt& r_out) {
  181|    117|   if(y.is_zero()) {
  ------------------
  |  Branch (181:7): [True: 0, False: 117]
  ------------------
  182|      0|      throw Invalid_Argument("ct_divide: cannot divide by zero");
  183|      0|   }
  184|       |
  185|    117|   BigInt q;
  186|    117|   BigInt r;
  187|    117|   ct_divide_impl(q, r, x, y);
  188|       |
  189|    117|   sign_fixup(x, y, q, r);
  190|    117|   r_out = std::move(r);
  191|    117|   q_out = std::move(q);
  192|    117|}
divide.cpp:_ZN5Botan12_GLOBAL__N_114ct_divide_implERNS_6BigIntES2_RKS1_S4_:
   66|    117|void ct_divide_impl(BigInt& q_out, BigInt& r_out, const BigInt& x, const BigInt& y) {
   67|    117|   constexpr size_t WB = WordInfo<word>::bits;
   68|       |
   69|    117|   const size_t x_words = x.sig_words();
   70|    117|   const size_t y_words = y.sig_words();
   71|    117|   BOTAN_ASSERT_NOMSG(y_words > 0);
  ------------------
  |  |   84|    117|   do {                                                                     \
  |  |   85|    117|      /* NOLINTNEXTLINE(*-simplify-boolean-expr) */                         \
  |  |   86|    117|      if(!(expr)) {                                                         \
  |  |  ------------------
  |  |  |  Branch (86:10): [True: 0, False: 117]
  |  |  ------------------
  |  |   87|      0|         /* NOLINTNEXTLINE(bugprone-lambda-function-name) */                \
  |  |   88|      0|         Botan::assertion_failure(#expr, "", __func__, __FILE__, __LINE__); \
  |  |   89|      0|      }                                                                     \
  |  |   90|    117|   } while(0)
  |  |  ------------------
  |  |  |  Branch (90:12): [Folded, False: 117]
  |  |  ------------------
  ------------------
   72|       |
   73|    117|   if(x_words < y_words) {
  ------------------
  |  Branch (73:7): [True: 0, False: 117]
  ------------------
   74|       |      // Here |x| < |y| so q = 0 and r = |x|
   75|      0|      r_out = x.abs();
   76|      0|      q_out = BigInt::zero();
   77|      0|      return;
   78|      0|   }
   79|       |
   80|    117|   const size_t r_words = x_words + 1;
   81|    117|   const size_t q_words = x_words - y_words + 1;
   82|       |
   83|       |   // The number of leading zero bits of y, in [0,WB)
   84|    117|   const word s = static_cast<word>(WB) - static_cast<word>(high_bit(CT::value_barrier(y._data()[y_words - 1])));
   85|    117|   const auto s_mask = CT::Mask<word>::expand(s);
   86|    117|   const word s_comp = s_mask.if_set_return(static_cast<word>(WB) - s);
   87|       |
   88|    117|   secure_vector<word> yn(y_words);
   89|    117|   secure_vector<word> rw(r_words);
   90|    117|   secure_vector<word> qw(q_words);
   91|       |
   92|       |   // Normalize: yn = |y| << s (which sets the top bit of yn) and rw = |x| << s;
   93|       |   // shifting y stays within y_words words, shifting x adds at most one word
   94|    117|   word carry = 0;
   95|    585|   for(size_t i = 0; i != y_words; ++i) {
  ------------------
  |  Branch (95:22): [True: 468, False: 117]
  ------------------
   96|    468|      const word w = y._data()[i];
   97|    468|      yn[i] = (w << s) | carry;
   98|    468|      carry = s_mask.if_set_return(w >> s_comp);
   99|    468|   }
  100|       |
  101|    117|   carry = 0;
  102|  1.03k|   for(size_t i = 0; i != x_words; ++i) {
  ------------------
  |  Branch (102:22): [True: 915, False: 117]
  ------------------
  103|    915|      const word w = x._data()[i];
  104|    915|      rw[i] = (w << s) | carry;
  105|    915|      carry = s_mask.if_set_return(w >> s_comp);
  106|    915|   }
  107|    117|   rw[x_words] = carry;
  108|       |
  109|    117|   const auto div_by_yn = divide_precomp<word>::setup(CT::value_barrier(yn[y_words - 1]));
  110|       |
  111|    117|   if(y_words == 1) {
  ------------------
  |  Branch (111:7): [True: 0, False: 117]
  ------------------
  112|       |      /*
  113|       |      * 2/1 word-serial division. The initial remainder is the shift-out
  114|       |      * carry, which is less than 2^s and so less than the divisor.
  115|       |      */
  116|      0|      word r = rw[x_words];
  117|      0|      for(size_t i = x_words; i > 0; --i) {
  ------------------
  |  Branch (117:31): [True: 0, False: 0]
  ------------------
  118|      0|         const auto [qi, ri] = div_by_yn.divmod_2to1_ct(r, rw[i - 1]);
  119|      0|         qw[i - 1] = qi;
  120|      0|         r = ri;
  121|      0|      }
  122|       |
  123|      0|      rw[0] = r >> s;
  124|    117|   } else {
  125|    117|      const word yn1 = yn[y_words - 1];
  126|    117|      const word yn2 = yn[y_words - 2];
  127|       |
  128|    681|      for(size_t i = q_words; i > 0; --i) {
  ------------------
  |  Branch (128:31): [True: 564, False: 117]
  ------------------
  129|    564|         const size_t j = i - 1;  // current window is rw[j ... j + y_words]
  130|       |
  131|    564|         const word u2 = rw[j + y_words];
  132|    564|         const word u1 = rw[j + y_words - 1];
  133|    564|         const word u0 = rw[j + y_words - 2];
  134|       |
  135|       |         /*
  136|       |         * Estimate the quotient digit from the top two words of the window.
  137|       |         * The window invariant gives u2 <= yn1; if u2 == yn1 the 2/1
  138|       |         * division precondition does not hold, and instead the digit
  139|       |         * estimate is the maximum word value.
  140|       |         */
  141|    564|         const auto top_eq = CT::Mask<word>::is_equal(u2, yn1);
  142|    564|         const word q_est = div_by_yn.divmod_2to1_ct(u2, u1).first;
  143|    564|         word qhat = top_eq.select(WordInfo<word>::max, q_est);
  144|       |
  145|       |         // Per HAC 14.23, this correction is required at most twice
  146|    564|         qhat -= division_check_ct(qhat, yn1, yn2, u2, u1, u0).if_set_return(1);
  147|    564|         qhat -= division_check_ct(qhat, yn1, yn2, u2, u1, u0).if_set_return(1);
  148|       |
  149|       |         // Subtract qhat * yn from the window
  150|    564|         const word bw = bigint_submul(&rw[j], yn.data(), y_words, qhat);
  151|    564|         word borrow = 0;
  152|    564|         rw[j + y_words] = word_sub(u2, bw, &borrow);
  153|       |
  154|       |         // If the digit was still one too large, the subtraction
  155|       |         // underflowed; add back and decrement the digit
  156|    564|         const word cb = bigint_cnd_add(borrow, &rw[j], yn.data(), y_words);
  157|    564|         rw[j + y_words] += cb;
  158|    564|         qhat -= borrow;
  159|       |
  160|    564|         qw[j] = qhat;
  161|    564|      }
  162|       |
  163|       |      // The remainder is in rw[0:y_words], still shifted left by s, and
  164|       |      // rw[y_words] is zero
  165|    585|      for(size_t i = 0; i != y_words; ++i) {
  ------------------
  |  Branch (165:25): [True: 468, False: 117]
  ------------------
  166|    468|         rw[i] = (rw[i] >> s) | s_mask.if_set_return(CT::value_barrier(rw[i + 1]) << s_comp);
  167|    468|      }
  168|    117|   }
  169|       |
  170|    117|   BigInt q = BigInt::_from_words(qw);
  171|    117|   rw.resize(y_words);
  172|    117|   BigInt r = BigInt::_from_words(rw);
  173|       |
  174|    117|   r_out = std::move(r);
  175|    117|   q_out = std::move(q);
  176|    117|}
divide.cpp:_ZN5Botan12_GLOBAL__N_117division_check_ctEmmmmmm:
   39|  1.12k|inline CT::Mask<word> division_check_ct(word q, word y2, word y1, word x3, word x2, word x1) {
   40|  1.12k|   word y3 = 0;
   41|  1.12k|   y1 = word_madd2(q, y1, &y3);
   42|  1.12k|   y2 = word_madd2(q, y2, &y3);
   43|       |
   44|  1.12k|   const auto gt3 = CT::Mask<word>::is_gt(y3, x3);
   45|  1.12k|   const auto eq3 = CT::Mask<word>::is_equal(y3, x3);
   46|  1.12k|   const auto gt2 = CT::Mask<word>::is_gt(y2, x2);
   47|  1.12k|   const auto eq2 = CT::Mask<word>::is_equal(y2, x2);
   48|  1.12k|   const auto gt1 = CT::Mask<word>::is_gt(y1, x1);
   49|       |
   50|  1.12k|   return gt3 | (eq3 & (gt2 | (eq2 & gt1)));
   51|  1.12k|}
divide.cpp:_ZN5Botan12_GLOBAL__N_110sign_fixupERKNS_6BigIntES3_RS1_S4_:
   22|    117|void sign_fixup(const BigInt& x, const BigInt& y, BigInt& q, BigInt& r) {
   23|    117|   q.cond_flip_sign(x.sign() != y.sign());
   24|       |
   25|    117|   if(x.signum() < 0 && r.signum() != 0) {
  ------------------
  |  Branch (25:7): [True: 0, False: 117]
  |  Branch (25:25): [True: 0, False: 0]
  ------------------
   26|      0|      if(y.signum() > 0) {
  ------------------
  |  Branch (26:10): [True: 0, False: 0]
  ------------------
   27|      0|         q -= 1;
   28|      0|      } else {
   29|      0|         q += 1;
   30|      0|      }
   31|      0|      r = y.abs() - r;
   32|      0|   }
   33|    117|}

_ZN5Botan17bigint_comba_mul4EPmPKmS2_:
   54|      1|void bigint_comba_mul4(word z[8], const word x[4], const word y[4]) {
   55|      1|#if defined(BOTAN_HAS_MP_MULX)
   56|      1|   if(CPUID::has(CPUID::Feature::BMI, CPUID::Feature::ADX)) {
  ------------------
  |  Branch (56:7): [True: 1, False: 0]
  ------------------
   57|      1|      return bigint_mulx_mul4(z, x, y);
   58|      1|   }
   59|      0|#endif
   60|       |
   61|      0|   word3<word> accum;
   62|       |
   63|      0|   accum.mul(x[0], y[0]);
   64|      0|   z[0] = accum.extract();
   65|      0|   accum.mul(x[0], y[1]);
   66|      0|   accum.mul(x[1], y[0]);
   67|      0|   z[1] = accum.extract();
   68|      0|   accum.mul(x[0], y[2]);
   69|      0|   accum.mul(x[1], y[1]);
   70|      0|   accum.mul(x[2], y[0]);
   71|      0|   z[2] = accum.extract();
   72|      0|   accum.mul(x[0], y[3]);
   73|      0|   accum.mul(x[1], y[2]);
   74|      0|   accum.mul(x[2], y[1]);
   75|      0|   accum.mul(x[3], y[0]);
   76|      0|   z[3] = accum.extract();
   77|      0|   accum.mul(x[1], y[3]);
   78|      0|   accum.mul(x[2], y[2]);
   79|      0|   accum.mul(x[3], y[1]);
   80|      0|   z[4] = accum.extract();
   81|      0|   accum.mul(x[2], y[3]);
   82|      0|   accum.mul(x[3], y[2]);
   83|      0|   z[5] = accum.extract();
   84|      0|   accum.mul(x[3], y[3]);
   85|      0|   z[6] = accum.extract();
   86|      0|   z[7] = accum.extract();
   87|      0|}

_ZN5Botan10bigint_mulEPmmPKmmmS2_mmS0_m:
  292|      1|                size_t ws_size) {
  293|      1|   zeroize_buffer(z, z_size);
  294|       |
  295|      1|   if(x_sw == 1) {
  ------------------
  |  Branch (295:7): [True: 0, False: 1]
  ------------------
  296|      0|      bigint_linmul3(z, y, y_sw, x[0]);
  297|      1|   } else if(y_sw == 1) {
  ------------------
  |  Branch (297:14): [True: 0, False: 1]
  ------------------
  298|      0|      bigint_linmul3(z, x, x_sw, y[0]);
  299|      1|   } else if(sized_for_comba_mul<4>(x_sw, x_size, y_sw, y_size, z_size)) {
  ------------------
  |  Branch (299:14): [True: 1, False: 0]
  ------------------
  300|      1|      bigint_comba_mul4(z, x, y);
  301|      1|   } else if(sized_for_comba_mul<6>(x_sw, x_size, y_sw, y_size, z_size)) {
  ------------------
  |  Branch (301:14): [True: 0, False: 0]
  ------------------
  302|      0|      bigint_comba_mul6(z, x, y);
  303|      0|   } else if(sized_for_comba_mul<8>(x_sw, x_size, y_sw, y_size, z_size)) {
  ------------------
  |  Branch (303:14): [True: 0, False: 0]
  ------------------
  304|      0|      bigint_comba_mul8(z, x, y);
  305|      0|   } else if(sized_for_comba_mul<9>(x_sw, x_size, y_sw, y_size, z_size)) {
  ------------------
  |  Branch (305:14): [True: 0, False: 0]
  ------------------
  306|      0|      bigint_comba_mul9(z, x, y);
  307|      0|   } else if(sized_for_comba_mul<16>(x_sw, x_size, y_sw, y_size, z_size)) {
  ------------------
  |  Branch (307:14): [True: 0, False: 0]
  ------------------
  308|      0|      bigint_comba_mul16(z, x, y);
  309|      0|   } else if(sized_for_comba_mul<24>(x_sw, x_size, y_sw, y_size, z_size)) {
  ------------------
  |  Branch (309:14): [True: 0, False: 0]
  ------------------
  310|      0|      bigint_comba_mul24(z, x, y);
  311|      0|   } else if(x_sw < KARATSUBA_MULTIPLY_THRESHOLD || y_sw < KARATSUBA_MULTIPLY_THRESHOLD || workspace == nullptr) {
  ------------------
  |  Branch (311:14): [True: 0, False: 0]
  |  Branch (311:53): [True: 0, False: 0]
  |  Branch (311:92): [True: 0, False: 0]
  ------------------
  312|      0|      basecase_mul(z, z_size, x, x_sw, y, y_sw);
  313|      0|   } else {
  314|      0|      const size_t N = karatsuba_size(z_size, x_size, x_sw, y_size, y_sw);
  315|       |
  316|      0|      if(N > 0 && z_size >= 2 * N && ws_size >= 2 * N) {
  ------------------
  |  Branch (316:10): [True: 0, False: 0]
  |  Branch (316:19): [True: 0, False: 0]
  |  Branch (316:38): [True: 0, False: 0]
  ------------------
  317|      0|         karatsuba_mul(z, x, y, N, workspace);
  318|      0|      } else {
  319|      0|         basecase_mul(z, z_size, x, x_sw, y, y_sw);
  320|      0|      }
  321|      0|   }
  322|      1|}
mp_karat.cpp:_ZN5Botan12_GLOBAL__N_119sized_for_comba_mulILm4EEEbmmmmm:
  272|      1|inline bool sized_for_comba_mul(size_t x_sw, size_t x_size, size_t y_sw, size_t y_size, size_t z_size) {
  273|      1|   return (x_sw <= SZ && x_size >= SZ && y_sw <= SZ && y_size >= SZ && z_size >= 2 * SZ);
  ------------------
  |  Branch (273:12): [True: 1, False: 0]
  |  Branch (273:26): [True: 1, False: 0]
  |  Branch (273:42): [True: 1, False: 0]
  |  Branch (273:56): [True: 1, False: 0]
  |  Branch (273:72): [True: 1, False: 0]
  ------------------
  274|      1|}

_ZN5Botan16bigint_mulx_mul4EPmPKmS2_:
  120|      1|void bigint_mulx_mul4(word z[8], const word x[4], const word y[4]) {
  121|       |   // clang-format off
  122|      1|   asm(
  123|      1|      "xorl %%r8d, %%r8d\n\t"
  124|      1|      "xorl %%r9d, %%r9d\n\t"
  125|      1|      "xorl %%r10d, %%r10d\n\t"
  126|      1|      "xorl %%r11d, %%r11d\n\t"
  127|       |
  128|      1|      "movq 0(%[y]), %%rdx\n\t"  // row 0: x * y[0]
  129|      1|      "xorl %%eax, %%eax\n\t"
  130|      1|      "mulx 0(%[x]), %%rax, %%rcx\n\t"
  131|      1|      "adcx %%rax, %%r8\n\t"
  132|      1|      "movq %%r8, 0(%[z])\n\t"
  133|      1|      "mulx 8(%[x]), %%rax, %%r8\n\t"
  134|      1|      "adox %%rcx, %%rax\n\t"
  135|      1|      "adcx %%rax, %%r9\n\t"
  136|      1|      "mulx 16(%[x]), %%rax, %%rcx\n\t"
  137|      1|      "adox %%r8, %%rax\n\t"
  138|      1|      "adcx %%rax, %%r10\n\t"
  139|      1|      "mulx 24(%[x]), %%rax, %%r8\n\t"
  140|      1|      "adox %%rcx, %%rax\n\t"
  141|      1|      "adcx %%rax, %%r11\n\t"
  142|      1|      "movl $0, %%eax\n\t"
  143|      1|      "adox %%rax, %%r8\n\t"
  144|      1|      "adcx %%rax, %%r8\n\t"
  145|       |
  146|      1|      "movq 8(%[y]), %%rdx\n\t"  // row 1: x * y[1]
  147|      1|      "xorl %%eax, %%eax\n\t"
  148|      1|      "mulx 0(%[x]), %%rax, %%rcx\n\t"
  149|      1|      "adcx %%rax, %%r9\n\t"
  150|      1|      "movq %%r9, 8(%[z])\n\t"
  151|      1|      "mulx 8(%[x]), %%rax, %%r9\n\t"
  152|      1|      "adox %%rcx, %%rax\n\t"
  153|      1|      "adcx %%rax, %%r10\n\t"
  154|      1|      "mulx 16(%[x]), %%rax, %%rcx\n\t"
  155|      1|      "adox %%r9, %%rax\n\t"
  156|      1|      "adcx %%rax, %%r11\n\t"
  157|      1|      "mulx 24(%[x]), %%rax, %%r9\n\t"
  158|      1|      "adox %%rcx, %%rax\n\t"
  159|      1|      "adcx %%rax, %%r8\n\t"
  160|      1|      "movl $0, %%eax\n\t"
  161|      1|      "adox %%rax, %%r9\n\t"
  162|      1|      "adcx %%rax, %%r9\n\t"
  163|       |
  164|      1|      "movq 16(%[y]), %%rdx\n\t"  // row 2: x * y[2]
  165|      1|      "xorl %%eax, %%eax\n\t"
  166|      1|      "mulx 0(%[x]), %%rax, %%rcx\n\t"
  167|      1|      "adcx %%rax, %%r10\n\t"
  168|      1|      "movq %%r10, 16(%[z])\n\t"
  169|      1|      "mulx 8(%[x]), %%rax, %%r10\n\t"
  170|      1|      "adox %%rcx, %%rax\n\t"
  171|      1|      "adcx %%rax, %%r11\n\t"
  172|      1|      "mulx 16(%[x]), %%rax, %%rcx\n\t"
  173|      1|      "adox %%r10, %%rax\n\t"
  174|      1|      "adcx %%rax, %%r8\n\t"
  175|      1|      "mulx 24(%[x]), %%rax, %%r10\n\t"
  176|      1|      "adox %%rcx, %%rax\n\t"
  177|      1|      "adcx %%rax, %%r9\n\t"
  178|      1|      "movl $0, %%eax\n\t"
  179|      1|      "adox %%rax, %%r10\n\t"
  180|      1|      "adcx %%rax, %%r10\n\t"
  181|       |
  182|      1|      "movq 24(%[y]), %%rdx\n\t"  // row 3: x * y[3]
  183|      1|      "xorl %%eax, %%eax\n\t"
  184|      1|      "mulx 0(%[x]), %%rax, %%rcx\n\t"
  185|      1|      "adcx %%rax, %%r11\n\t"
  186|      1|      "movq %%r11, 24(%[z])\n\t"
  187|      1|      "mulx 8(%[x]), %%rax, %%r11\n\t"
  188|      1|      "adox %%rcx, %%rax\n\t"
  189|      1|      "adcx %%rax, %%r8\n\t"
  190|      1|      "mulx 16(%[x]), %%rax, %%rcx\n\t"
  191|      1|      "adox %%r11, %%rax\n\t"
  192|      1|      "adcx %%rax, %%r9\n\t"
  193|      1|      "mulx 24(%[x]), %%rax, %%r11\n\t"
  194|      1|      "adox %%rcx, %%rax\n\t"
  195|      1|      "adcx %%rax, %%r10\n\t"
  196|      1|      "movl $0, %%eax\n\t"
  197|      1|      "adox %%rax, %%r11\n\t"
  198|      1|      "adcx %%rax, %%r11\n\t"
  199|       |
  200|      1|      "movq %%r8, 32(%[z])\n\t"
  201|      1|      "movq %%r9, 40(%[z])\n\t"
  202|      1|      "movq %%r10, 48(%[z])\n\t"
  203|      1|      "movq %%r11, 56(%[z])\n\t"
  204|      1|      :
  205|      1|      : [z] "r"(z), [x] "r"(x), [y] "r"(y)
  206|      1|      : "rax", "rcx", "rdx", "r8", "r9", "r10", "r11", "cc", "memory");
  207|       |   // clang-format on
  208|      1|}

_ZN5Botan15allocate_memoryEmm:
   21|    480|BOTAN_MALLOC_FN void* allocate_memory(size_t elems, size_t elem_size) {
   22|    480|   if(elems == 0 || elem_size == 0) {
  ------------------
  |  Branch (22:7): [True: 0, False: 480]
  |  Branch (22:21): [True: 0, False: 480]
  ------------------
   23|      0|      return nullptr;
   24|      0|   }
   25|       |
   26|       |   // Some calloc implementations do not check for overflow (?!?)
   27|    480|   if(!checked_mul(elems, elem_size).has_value()) {
  ------------------
  |  Branch (27:7): [True: 0, False: 480]
  ------------------
   28|      0|      throw std::bad_alloc();
   29|      0|   }
   30|       |
   31|       |#if defined(BOTAN_HAS_LOCKING_ALLOCATOR)
   32|       |   // NOLINTNEXTLINE(*-const-correctness) bug in clang-tidy
   33|       |   if(void* p = mlock_allocator::instance().allocate(elems, elem_size)) {
   34|       |      return p;
   35|       |   }
   36|       |#endif
   37|       |
   38|       |#if defined(BOTAN_TARGET_OS_HAS_ALLOC_CONCEAL)
   39|       |   void* ptr = ::calloc_conceal(elems, elem_size);
   40|       |#else
   41|       |   // NOLINTNEXTLINE(*-const-correctness) bug in clang-tidy
   42|    480|   void* ptr = std::calloc(elems, elem_size);  // NOLINT(*-no-malloc,*-owning-memory)
   43|    480|#endif
   44|    480|   if(ptr == nullptr) {
  ------------------
  |  Branch (44:7): [True: 0, False: 480]
  ------------------
   45|      0|      [[unlikely]] throw std::bad_alloc();
   46|      0|   }
   47|    480|   return ptr;
   48|    480|}
_ZN5Botan17deallocate_memoryEPvmm:
   50|    480|void deallocate_memory(void* p, size_t elems, size_t elem_size) {
   51|    480|   if(p == nullptr) {
  ------------------
  |  Branch (51:7): [True: 0, False: 480]
  ------------------
   52|      0|      [[unlikely]] return;
   53|      0|   }
   54|       |
   55|    480|   secure_scrub_memory(p, elems * elem_size);
   56|       |
   57|       |#if defined(BOTAN_HAS_LOCKING_ALLOCATOR)
   58|       |   if(mlock_allocator::instance().deallocate(p, elems, elem_size)) {
   59|       |      return;
   60|       |   }
   61|       |#endif
   62|       |
   63|    480|   std::free(p);  // NOLINT(*-no-malloc,*-owning-memory)
   64|    480|}

_ZN5Botan5CPUID10CPUID_DataC2Ev:
   80|      1|CPUID::CPUID_Data::CPUID_Data() {
   81|       |   // NOLINTBEGIN(*-prefer-member-initializer)
   82|      1|#if defined(BOTAN_HAS_CPUID_DETECTION)
   83|      1|   m_processor_features = detect_cpu_features(~cleared_cpuid_bits());
   84|       |#else
   85|       |   m_processor_features = 0;
   86|       |#endif
   87|       |   // NOLINTEND(*-prefer-member-initializer)
   88|      1|}
cpuid.cpp:_ZN5Botan12_GLOBAL__N_118cleared_cpuid_bitsEv:
   59|      1|uint32_t cleared_cpuid_bits() {
   60|      1|   uint32_t cleared = 0;
   61|       |
   62|      1|   #if defined(BOTAN_HAS_OS_UTILS)
   63|      1|   std::string clear_cpuid_env;
   64|      1|   if(OS::read_env_variable(clear_cpuid_env, "BOTAN_CLEAR_CPUID")) {
  ------------------
  |  Branch (64:7): [True: 0, False: 1]
  ------------------
   65|      0|      for(const auto& cpuid : split_on(clear_cpuid_env, ',')) {
  ------------------
  |  Branch (65:29): [True: 0, False: 0]
  ------------------
   66|      0|         if(auto bit = CPUID::bit_from_string(cpuid)) {
  ------------------
  |  Branch (66:18): [True: 0, False: 0]
  ------------------
   67|      0|            cleared |= bit->as_u32();
   68|      0|         }
   69|      0|      }
   70|      0|   }
   71|      1|   #endif
   72|       |
   73|      1|   return cleared;
   74|      1|}

_ZN5Botan5CPUID10CPUID_Data19detect_cpu_featuresEj:
   62|      1|uint32_t CPUID::CPUID_Data::detect_cpu_features(uint32_t allowed) {
   63|      1|   enum class x86_CPUID_1_bits : uint64_t {
   64|      1|      RDTSC = (1ULL << 4),
   65|      1|      SSE2 = (1ULL << 26),
   66|      1|      CLMUL = (1ULL << 33),
   67|      1|      SSSE3 = (1ULL << 41),
   68|      1|      SSE41 = (1ULL << 51),
   69|      1|      POPCNT = (1ULL << 55),
   70|      1|      AESNI = (1ULL << 57),
   71|       |      // AVX + OSXSAVE
   72|      1|      OSXSAVE = (1ULL << 59) | (1ULL << 60),
   73|      1|      RDRAND = (1ULL << 62)
   74|      1|   };
   75|       |
   76|      1|   enum class x86_CPUID_7_bits : uint64_t {
   77|      1|      BMI1 = (1ULL << 3),
   78|      1|      AVX2 = (1ULL << 5),
   79|      1|      BMI2 = (1ULL << 8),
   80|      1|      BMI_1_AND_2 = BMI1 | BMI2,
   81|      1|      AVX512_F = (1ULL << 16),
   82|      1|      AVX512_DQ = (1ULL << 17),
   83|      1|      RDSEED = (1ULL << 18),
   84|      1|      ADX = (1ULL << 19),
   85|      1|      AVX512_IFMA = (1ULL << 21),
   86|      1|      SHA = (1ULL << 29),
   87|      1|      AVX512_BW = (1ULL << 30),
   88|      1|      AVX512_VL = (1ULL << 31),
   89|      1|      AVX512_VBMI = (1ULL << 33),
   90|      1|      AVX512_VBMI2 = (1ULL << 38),
   91|      1|      GFNI = (1ULL << 40),
   92|      1|      AVX512_VAES = (1ULL << 41),
   93|      1|      AVX512_VCLMUL = (1ULL << 42),
   94|      1|      AVX512_VBITALG = (1ULL << 44),
   95|       |
   96|       |      /*
   97|       |      We only enable AVX512 support if all of the below flags are available
   98|       |
   99|       |      This is more than we strictly need for most uses, however it also has
  100|       |      the effect of preventing execution of AVX512 codepaths on cores that
  101|       |      have serious downclocking problems when AVX512 code executes,
  102|       |      especially Intel Skylake.
  103|       |
  104|       |      VBMI2/VBITALG are the key flags here as they restrict us to Intel Ice
  105|       |      Lake/Rocket Lake, or AMD Zen4, all of which do not have penalties for
  106|       |      executing AVX512.
  107|       |
  108|       |      There is nothing stopping some future processor from supporting the
  109|       |      above flags and having AVX512 penalties, but maybe you should not have
  110|       |      bought such a processor.
  111|       |      */
  112|      1|      AVX512_PROFILE =
  113|      1|         AVX512_F | AVX512_DQ | AVX512_IFMA | AVX512_BW | AVX512_VL | AVX512_VBMI | AVX512_VBMI2 | AVX512_VBITALG,
  114|      1|   };
  115|       |
  116|       |   // NOLINTNEXTLINE(performance-enum-size)
  117|      1|   enum class x86_CPUID_7_1_bits : uint64_t {
  118|      1|      SHA512 = (1 << 0),
  119|      1|      SM3 = (1 << 1),
  120|      1|      SM4 = (1 << 2),
  121|      1|   };
  122|       |
  123|      1|   uint32_t feat = 0;
  124|      1|   uint32_t cpuid[4] = {0};
  125|      1|   bool has_sse41 = false;
  126|      1|   bool has_os_ymm_support = false;
  127|      1|   bool has_os_zmm_support = false;
  128|       |
  129|       |   // CPUID 0: vendor identification, max sublevel
  130|      1|   invoke_cpuid(0, cpuid);
  131|       |
  132|      1|   const uint32_t max_supported_sublevel = cpuid[0];
  133|       |
  134|      1|   if(max_supported_sublevel >= 1) {
  ------------------
  |  Branch (134:7): [True: 1, False: 0]
  ------------------
  135|       |      // CPUID 1: feature bits
  136|      1|      invoke_cpuid(1, cpuid);
  137|      1|      const uint64_t flags0 = (static_cast<uint64_t>(cpuid[2]) << 32) | cpuid[3];
  138|       |
  139|      1|      feat |= if_set(flags0, x86_CPUID_1_bits::RDTSC, CPUFeature::Bit::RDTSC, allowed);
  140|       |
  141|      1|      feat |= if_set(flags0, x86_CPUID_1_bits::RDRAND, CPUFeature::Bit::RDRAND, allowed);
  142|       |
  143|      1|      feat |= if_set(flags0, x86_CPUID_1_bits::POPCNT, CPUFeature::Bit::POPCNT, allowed);
  144|       |
  145|      1|      feat |= if_set(flags0, x86_CPUID_1_bits::SSE2, CPUFeature::Bit::SSE2, allowed);
  146|       |
  147|      1|      if(is_set(feat, CPUFeature::Bit::SSE2)) {
  ------------------
  |  Branch (147:10): [True: 1, False: 0]
  ------------------
  148|      1|         feat |= if_set(flags0, x86_CPUID_1_bits::SSSE3, CPUFeature::Bit::SSSE3, allowed);
  149|       |
  150|      1|         if(is_set(feat, CPUFeature::Bit::SSSE3)) {
  ------------------
  |  Branch (150:13): [True: 1, False: 0]
  ------------------
  151|      1|            feat |= if_set(flags0, x86_CPUID_1_bits::CLMUL, CPUFeature::Bit::CLMUL, allowed);
  152|      1|            feat |= if_set(flags0, x86_CPUID_1_bits::AESNI, CPUFeature::Bit::AESNI, allowed);
  153|       |
  154|       |            // SSE4.1 is not tracked as a feature, but is required by the SHA-NI code
  155|      1|            const uint64_t sse41 = static_cast<uint64_t>(x86_CPUID_1_bits::SSE41);
  156|      1|            has_sse41 = (flags0 & sse41) == sse41;
  157|      1|         }
  158|       |
  159|      1|         const uint64_t osxsave64 = static_cast<uint64_t>(x86_CPUID_1_bits::OSXSAVE);
  160|      1|         if((flags0 & osxsave64) == osxsave64) {
  ------------------
  |  Branch (160:13): [True: 1, False: 0]
  ------------------
  161|      1|            const uint64_t xcr_flags = xgetbv();
  162|      1|            if((xcr_flags & 0x6) == 0x6) {
  ------------------
  |  Branch (162:16): [True: 1, False: 0]
  ------------------
  163|      1|               has_os_ymm_support = true;
  164|      1|               has_os_zmm_support = (xcr_flags & 0xE0) == 0xE0;
  165|      1|            }
  166|      1|         }
  167|      1|      }
  168|      1|   }
  169|       |
  170|      1|   if(max_supported_sublevel >= 7) {
  ------------------
  |  Branch (170:7): [True: 1, False: 0]
  ------------------
  171|      1|      clear_mem(cpuid, 4);
  172|      1|      invoke_cpuid_sublevel(7, 0, cpuid);
  173|       |
  174|      1|      const uint64_t flags7 = (static_cast<uint64_t>(cpuid[2]) << 32) | cpuid[1];
  175|       |
  176|      1|      clear_mem(cpuid, 4);
  177|      1|      invoke_cpuid_sublevel(7, 1, cpuid);
  178|      1|      const uint32_t flags7_1 = cpuid[0];
  179|       |
  180|      1|      feat |= if_set(flags7, x86_CPUID_7_bits::RDSEED, CPUFeature::Bit::RDSEED, allowed);
  181|      1|      feat |= if_set(flags7, x86_CPUID_7_bits::ADX, CPUFeature::Bit::ADX, allowed);
  182|       |
  183|       |      /*
  184|       |      We only set the BMI bit if both BMI1 and BMI2 are supported, since
  185|       |      typically we want to use both extensions in the same code.
  186|       |      */
  187|      1|      feat |= if_set(flags7, x86_CPUID_7_bits::BMI_1_AND_2, CPUFeature::Bit::BMI, allowed);
  188|       |
  189|      1|      if(is_set(feat, CPUFeature::Bit::SSSE3)) {
  ------------------
  |  Branch (189:10): [True: 1, False: 0]
  ------------------
  190|       |         // The SHA-NI code uses SSE4.1 instructions in addition to SSSE3
  191|      1|         if(has_sse41) {
  ------------------
  |  Branch (191:13): [True: 1, False: 0]
  ------------------
  192|      1|            feat |= if_set(flags7, x86_CPUID_7_bits::SHA, CPUFeature::Bit::SHA, allowed);
  193|      1|         }
  194|       |
  195|       |         // We only consider AVX2 if SSSE3 is supported
  196|      1|         if(has_os_ymm_support) {
  ------------------
  |  Branch (196:13): [True: 1, False: 0]
  ------------------
  197|      1|            feat |= if_set(flags7, x86_CPUID_7_bits::AVX2, CPUFeature::Bit::AVX2, allowed);
  198|       |
  199|      1|            if(is_set(feat, CPUFeature::Bit::AVX2)) {
  ------------------
  |  Branch (199:16): [True: 1, False: 0]
  ------------------
  200|      1|               feat |= if_set(flags7, x86_CPUID_7_bits::GFNI, CPUFeature::Bit::GFNI, allowed);
  201|      1|               feat |= if_set(flags7, x86_CPUID_7_bits::AVX512_VAES, CPUFeature::Bit::AVX2_AES, allowed);
  202|      1|               feat |= if_set(flags7_1, x86_CPUID_7_1_bits::SHA512, CPUFeature::Bit::SHA512, allowed);
  203|       |               // The SM3/SM4 instructions are VEX encoded and the code using them also uses AVX2
  204|      1|               feat |= if_set(flags7_1, x86_CPUID_7_1_bits::SM3, CPUFeature::Bit::SM3, allowed);
  205|      1|               feat |= if_set(flags7_1, x86_CPUID_7_1_bits::SM4, CPUFeature::Bit::SM4, allowed);
  206|       |
  207|       |               // The VPCLMULQDQ code also uses the legacy 128-bit PCLMULQDQ instruction
  208|      1|               if(is_set(feat, CPUFeature::Bit::CLMUL)) {
  ------------------
  |  Branch (208:19): [True: 1, False: 0]
  ------------------
  209|      1|                  feat |= if_set(flags7, x86_CPUID_7_bits::AVX512_VCLMUL, CPUFeature::Bit::AVX2_CLMUL, allowed);
  210|      1|               }
  211|       |
  212|       |               // Likewise we only consider AVX-512 if AVX2 is supported
  213|      1|               if(has_os_zmm_support) {
  ------------------
  |  Branch (213:19): [True: 0, False: 1]
  ------------------
  214|      0|                  feat |= if_set(flags7, x86_CPUID_7_bits::AVX512_PROFILE, CPUFeature::Bit::AVX512, allowed);
  215|       |
  216|      0|                  if(is_set(feat, CPUFeature::Bit::AVX512)) {
  ------------------
  |  Branch (216:22): [True: 0, False: 0]
  ------------------
  217|      0|                     feat |= if_set(flags7, x86_CPUID_7_bits::AVX512_VAES, CPUFeature::Bit::AVX512_AES, allowed);
  218|      0|                     if(is_set(feat, CPUFeature::Bit::CLMUL)) {
  ------------------
  |  Branch (218:25): [True: 0, False: 0]
  ------------------
  219|      0|                        feat |= if_set(flags7, x86_CPUID_7_bits::AVX512_VCLMUL, CPUFeature::Bit::AVX512_CLMUL, allowed);
  220|      0|                     }
  221|      0|                  }
  222|      0|               }
  223|      1|            }
  224|      1|         }
  225|      1|      }
  226|      1|   }
  227|       |
  228|       |/*
  229|       |   * If we don't have access to CPUID, we can still safely assume that
  230|       |   * any x86-64 processor has SSE2 and RDTSC
  231|       |   */
  232|      1|#if defined(BOTAN_TARGET_ARCH_IS_X86_64)
  233|      1|   if(feat == 0) {
  ------------------
  |  Branch (233:7): [True: 0, False: 1]
  ------------------
  234|      0|      feat |= CPUFeature::Bit::SSE2 & allowed;
  235|      0|      feat |= CPUFeature::Bit::RDTSC & allowed;
  236|      0|   }
  237|      1|#endif
  238|       |
  239|      1|   return feat;
  240|      1|}
cpuid_x86.cpp:_ZN5Botan12_GLOBAL__N_112invoke_cpuidEjPj:
   24|      2|void invoke_cpuid(uint32_t type, uint32_t out[4]) {
   25|      2|   clear_mem(out, 4);
   26|       |
   27|      2|#if defined(BOTAN_USE_GCC_INLINE_ASM)
   28|       |   // NOLINTNEXTLINE(*-no-assembler)
   29|      2|   asm volatile("cpuid\n\t" : "=a"(out[0]), "=b"(out[1]), "=c"(out[2]), "=d"(out[3]) : "0"(type));
   30|       |
   31|       |#elif defined(BOTAN_BUILD_COMPILER_IS_MSVC)
   32|       |   __cpuid((int*)out, type);
   33|       |
   34|       |#else
   35|       |   BOTAN_UNUSED(type);
   36|       |   #warning "No way of calling x86 cpuid instruction for this compiler"
   37|       |#endif
   38|      2|}
cpuid_x86.cpp:_ZN5Botan12_GLOBAL__N_16xgetbvEv:
   56|      1|BOTAN_FUNC_ISA("xsave") uint64_t xgetbv() {
   57|       |   return _xgetbv(0);
   58|      1|}
cpuid_x86.cpp:_ZN5Botan12_GLOBAL__N_121invoke_cpuid_sublevelEjjPj:
   40|      2|void invoke_cpuid_sublevel(uint32_t type, uint32_t level, uint32_t out[4]) {
   41|      2|   clear_mem(out, 4);
   42|       |
   43|      2|#if defined(BOTAN_USE_GCC_INLINE_ASM)
   44|       |   // NOLINTNEXTLINE(*-no-assembler)
   45|      2|   asm volatile("cpuid\n\t" : "=a"(out[0]), "=b"(out[1]), "=c"(out[2]), "=d"(out[3]) : "0"(type), "2"(level));
   46|       |
   47|       |#elif defined(BOTAN_BUILD_COMPILER_IS_MSVC)
   48|       |   __cpuidex((int*)out, type, level);
   49|       |
   50|       |#else
   51|       |   BOTAN_UNUSED(type, level);
   52|       |   #warning "No way of calling x86 cpuid instruction for this compiler"
   53|       |#endif
   54|      2|}

_ZN5Botan19secure_scrub_memoryEPvm:
   24|    480|void secure_scrub_memory(void* ptr, size_t n) {
   25|    480|   return secure_zeroize_buffer(ptr, n);
   26|    480|}
_ZN5Botan21secure_zeroize_bufferEPvm:
   28|    480|void secure_zeroize_buffer(void* ptr, size_t n) {
   29|    480|   if(n == 0) {
  ------------------
  |  Branch (29:7): [True: 0, False: 480]
  ------------------
   30|      0|      return;
   31|      0|   }
   32|       |
   33|       |#if defined(BOTAN_TARGET_OS_HAS_RTLSECUREZEROMEMORY)
   34|       |   ::RtlSecureZeroMemory(ptr, n);
   35|       |
   36|       |#elif defined(BOTAN_TARGET_OS_HAS_EXPLICIT_BZERO)
   37|    480|   ::explicit_bzero(ptr, n);
   38|       |
   39|       |#elif defined(BOTAN_TARGET_OS_HAS_EXPLICIT_MEMSET)
   40|       |   (void)::explicit_memset(ptr, 0, n);
   41|       |
   42|       |#else
   43|       |   /*
   44|       |   * Call memset through a static volatile pointer, which the compiler should
   45|       |   * not elide. This construct should be safe in conforming compilers, but who
   46|       |   * knows. This has been checked to generate the expected code, which saves the
   47|       |   * memset address in the data segment and unconditionally loads and jumps to
   48|       |   * that address, with the following targets:
   49|       |   *
   50|       |   * x86-64: Clang 19, GCC 6, 11, 13, 14
   51|       |   * riscv64: GCC 14
   52|       |   * aarch64: GCC 14
   53|       |   * armv7: GCC 14
   54|       |   *
   55|       |   * Actually all of them generated the expected jump even without marking the
   56|       |   * function pointer as volatile. However this seems worth including as an
   57|       |   * additional precaution.
   58|       |   */
   59|       |   static void* (*const volatile memset_ptr)(void*, int, size_t) = std::memset;
   60|       |   (memset_ptr)(ptr, 0, n);
   61|       |#endif
   62|    480|}

_ZN5Botan2OS17read_env_variableERNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEENS1_17basic_string_viewIcS4_EE:
  471|      1|bool OS::read_env_variable(std::string& value_out, std::string_view name_view) {
  472|      1|   value_out = "";
  473|       |
  474|      1|   if(running_in_privileged_state()) {
  ------------------
  |  Branch (474:7): [True: 0, False: 1]
  ------------------
  475|      0|      return false;
  476|      0|   }
  477|       |
  478|       |#if defined(BOTAN_TARGET_OS_HAS_WIN32) && \
  479|       |   (defined(BOTAN_BUILD_COMPILER_IS_MSVC) || defined(BOTAN_BUILD_COMPILER_IS_CLANGCL))
  480|       |   const std::string name(name_view);
  481|       |
  482|       |   // First query the required buffer size; req_size is 0 if the variable is not set
  483|       |   size_t req_size = 0;
  484|       |   if(getenv_s(&req_size, nullptr, 0, name.c_str()) != 0 || req_size == 0) {
  485|       |      return false;
  486|       |   }
  487|       |
  488|       |   // The reported size includes the terminating \0
  489|       |   std::string val(req_size, '\0');
  490|       |   if(getenv_s(&req_size, val.data(), val.size(), name.c_str()) != 0) {
  491|       |      return false;
  492|       |   }
  493|       |
  494|       |   value_out = std::string(val.c_str());
  495|       |   return true;
  496|       |#else
  497|      1|   const std::string name(name_view);
  498|      1|   if(const char* val = std::getenv(name.c_str())) {
  ------------------
  |  Branch (498:19): [True: 0, False: 1]
  ------------------
  499|      0|      value_out = val;
  500|      0|      return true;
  501|      0|   }
  502|      1|#endif
  503|       |
  504|      1|   return false;
  505|      1|}
os_utils.cpp:_ZN5Botan12_GLOBAL__N_110get_auxvalENSt3__18optionalImEE:
  120|      1|std::optional<unsigned long> get_auxval(std::optional<unsigned long> id) {
  121|      1|   if(id) {
  ------------------
  |  Branch (121:7): [True: 1, False: 0]
  ------------------
  122|      1|#if defined(BOTAN_TARGET_OS_HAS_GETAUXVAL)
  123|      1|      return ::getauxval(*id);
  124|       |#elif defined(BOTAN_TARGET_OS_HAS_ELF_AUX_INFO)
  125|       |      unsigned long auxinfo = 0;
  126|       |      if(::elf_aux_info(static_cast<int>(*id), &auxinfo, sizeof(auxinfo)) == 0) {
  127|       |         return auxinfo;
  128|       |      }
  129|       |#endif
  130|      1|   }
  131|       |
  132|      0|   return {};
  133|      1|}
os_utils.cpp:_ZN5Botan12_GLOBAL__N_127running_in_privileged_stateEv:
  154|      1|bool running_in_privileged_state() {
  155|      1|#if defined(AT_SECURE)
  156|      1|   if(const auto at_secure = get_auxval(AT_SECURE)) {
  ------------------
  |  Branch (156:18): [True: 1, False: 0]
  ------------------
  157|      1|      return at_secure != 0;
  158|      1|   }
  159|      0|#endif
  160|       |
  161|      0|#if defined(BOTAN_TARGET_OS_HAS_POSIX1)
  162|      0|   return (::getuid() != ::geteuid()) || (::getgid() != ::getegid());
  ------------------
  |  Branch (162:11): [True: 0, False: 0]
  |  Branch (162:42): [True: 0, False: 0]
  ------------------
  163|       |#else
  164|       |   return false;
  165|       |#endif
  166|      1|}

