[llvm] [orc-rt] Replace Math.h in favor of bit.h. (PR #219390)

Lang Hames via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 27 23:46:15 PDT 2026


https://github.com/lhames created https://github.com/llvm/llvm-project/pull/219390

Replace isPowerOf2 with has_single_bit, and nextPowerOf2 with bit_width (plus necessary arithmetic at the call sites, and a guard on the long from sysconf in detectPageSize, which only excluded -1).

Bitmask enums must now have an unsigned underlying type (STL <bit> APIs require it, and "bit.h" matches the STL).

>From 86992625827f0dfc06eb3c0c7beefd45e395bbff Mon Sep 17 00:00:00 2001
From: Lang Hames <lhames at gmail.com>
Date: Fri, 28 Aug 2026 16:25:00 +1000
Subject: [PATCH] [orc-rt] Replace Math.h in favor of bit.h.

Replace isPowerOf2 with has_single_bit, and nextPowerOf2 with bit_width
(plus necessary arithmetic at the call sites, and a guard on the long
from sysconf in detectPageSize, which only excluded -1).

Bitmask enums must now have an unsigned underlying type (STL <bit> APIs
require it, and "bit.h" matches the STL).
---
 orc-rt/include/CMakeLists.txt                 |  1 -
 orc-rt/include/orc-rt/support/BitmaskEnum.h   | 16 +++-
 orc-rt/include/orc-rt/support/Math.h          | 35 ---------
 orc-rt/lib/bedrock/ExecutorProcessInfo.cpp    |  6 +-
 orc-rt/test/unit/CMakeLists.txt               |  1 -
 .../unit/bedrock/ExecutorProcessInfoTest.cpp  |  4 +-
 orc-rt/test/unit/support/BitmaskEnumTest.cpp  | 22 +++++-
 orc-rt/test/unit/support/MathTest.cpp         | 78 -------------------
 orc-rt/test/unit/support/bit-test.cpp         | 13 ++++
 9 files changed, 50 insertions(+), 126 deletions(-)
 delete mode 100644 orc-rt/include/orc-rt/support/Math.h
 delete mode 100644 orc-rt/test/unit/support/MathTest.cpp

diff --git a/orc-rt/include/CMakeLists.txt b/orc-rt/include/CMakeLists.txt
index 8cbce12d9c2f3..376ac26be83d6 100644
--- a/orc-rt/include/CMakeLists.txt
+++ b/orc-rt/include/CMakeLists.txt
@@ -41,7 +41,6 @@ set(ORC_RT_HEADERS
     orc-rt/support/IntervalSet.h
     orc-rt/support/LockedAccess.h
     orc-rt/support/MacroUtils.h
-    orc-rt/support/Math.h
     orc-rt/support/MemoryFlags.h
     orc-rt/support/Proxy.h
     orc-rt/support/RTTI.h
diff --git a/orc-rt/include/orc-rt/support/BitmaskEnum.h b/orc-rt/include/orc-rt/support/BitmaskEnum.h
index 3594084709d7d..85a863f99d6c2 100644
--- a/orc-rt/include/orc-rt/support/BitmaskEnum.h
+++ b/orc-rt/include/orc-rt/support/BitmaskEnum.h
@@ -16,10 +16,10 @@
 #ifndef ORC_RT_SUPPORT_BITMASKENUM_H
 #define ORC_RT_SUPPORT_BITMASKENUM_H
 
-#include "orc-rt/support/Math.h"
 #include "orc-rt/support/bit.h"
 
 #include <cassert>
+#include <limits>
 #include <type_traits>
 
 namespace orc_rt {
@@ -97,19 +97,29 @@ template <typename E>
 struct largest_bitmask_enum_bit<
     E, std::enable_if_t<sizeof(E::ORC_RT_BITMASK_LARGEST_ENUMERATOR) >= 0>> {
   using UnderlyingTy = std::underlying_type_t<E>;
+  static_assert(std::is_unsigned_v<UnderlyingTy>,
+                "Bitmask enums must have an unsigned underlying type. Declare "
+                "the enum with an explicit unsigned base type, e.g. "
+                "'enum class E : unsigned'; a scoped enum without one has an "
+                "underlying type of int.");
   static constexpr UnderlyingTy value =
       static_cast<UnderlyingTy>(E::ORC_RT_BITMASK_LARGEST_ENUMERATOR);
 };
 
 template <typename E>
 constexpr std::underlying_type_t<E> bitmask_enum_mask() noexcept {
-  return nextPowerOf2(largest_bitmask_enum_bit<E>::value) - 1;
+  using UnderlyingTy = std::underlying_type_t<E>;
+  constexpr int Width = bit_width(largest_bitmask_enum_bit<E>::value);
+  // Shifting by the full width of the type would be undefined, so handle a
+  // largest bit in the top position separately: the mask is then every bit.
+  return Width == std::numeric_limits<UnderlyingTy>::digits
+             ? static_cast<UnderlyingTy>(~UnderlyingTy(0))
+             : static_cast<UnderlyingTy>((UnderlyingTy(1) << Width) - 1);
 }
 
 template <typename E>
 constexpr std::underlying_type_t<E> bitmask_enum_to_underlying(E Val) noexcept {
   auto U = static_cast<std::underlying_type_t<E>>(Val);
-  assert(U >= 0 && "Negative enum values are not allowed");
   assert(U <= bitmask_enum_mask<E>() &&
          "Enum value too large (or langest val too small");
   return U;
diff --git a/orc-rt/include/orc-rt/support/Math.h b/orc-rt/include/orc-rt/support/Math.h
deleted file mode 100644
index ee7b38e081255..0000000000000
--- a/orc-rt/include/orc-rt/support/Math.h
+++ /dev/null
@@ -1,35 +0,0 @@
-//===--------- Math.h - Math helpers for the ORC runtime --------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-//
-// Math helper functions for the ORC runtime.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef ORC_RT_SUPPORT_MATH_H
-#define ORC_RT_SUPPORT_MATH_H
-
-#include <cstdint>
-#include <limits>
-
-namespace orc_rt {
-
-/// Test whether the given value is a power of 2.
-template <typename T> [[nodiscard]] constexpr bool isPowerOf2(T Val) noexcept {
-  return Val != 0 && (Val & (Val - 1)) == 0;
-}
-
-/// Calculates the next power of 2.
-template <typename T> [[nodiscard]] constexpr T nextPowerOf2(T Val) noexcept {
-  for (std::size_t I = 1; I < std::numeric_limits<T>::digits; I <<= 1)
-    Val |= (Val >> I);
-  return Val + 1;
-}
-
-} // namespace orc_rt
-
-#endif // ORC_RT_SUPPORT_MATH_H
diff --git a/orc-rt/lib/bedrock/ExecutorProcessInfo.cpp b/orc-rt/lib/bedrock/ExecutorProcessInfo.cpp
index 6f302e8961444..bff9d93953968 100644
--- a/orc-rt/lib/bedrock/ExecutorProcessInfo.cpp
+++ b/orc-rt/lib/bedrock/ExecutorProcessInfo.cpp
@@ -13,7 +13,7 @@
 
 #include "orc-rt/bedrock/ExecutorProcessInfo.h"
 #include "orc-rt-internal/support/StringExtras.h"
-#include "orc-rt/support/Math.h"
+#include "orc-rt/support/bit.h"
 
 #include <cassert>
 #include <cstring>
@@ -26,7 +26,7 @@ ExecutorProcessInfo::ExecutorProcessInfo(std::string Triple, size_t PageSize,
     : Triple(std::move(Triple)), PageSize(PageSize),
       CPUFeatures(std::move(CPUFeatures)) {
   assert(!this->Triple.empty() && "triple cannot be empty");
-  assert(isPowerOf2(this->PageSize) && "page-size is not a power of two");
+  assert(has_single_bit(this->PageSize) && "page-size is not a power of two");
 }
 
 /// Create an ExecutorProcessInfo, auto-detecting property values.
@@ -75,7 +75,7 @@ Expected<size_t> ExecutorProcessInfo::detectPageSize() noexcept {
   long PageSize = sysconf(_SC_PAGESIZE);
   if (PageSize == -1)
     return make_error<StringError>(strerror(errno));
-  if (!isPowerOf2(PageSize))
+  if (PageSize <= 0 || !has_single_bit(static_cast<size_t>(PageSize)))
     return make_error<StringError>((StringOutputStream()
                                     << "reported page size " << PageSize
                                     << " is not a power of two")
diff --git a/orc-rt/test/unit/CMakeLists.txt b/orc-rt/test/unit/CMakeLists.txt
index f58dac2d86e7f..8233e5affd27d 100644
--- a/orc-rt/test/unit/CMakeLists.txt
+++ b/orc-rt/test/unit/CMakeLists.txt
@@ -27,7 +27,6 @@ add_orc_rt_unittest(CoreTests
   support/LockedAccessTest.cpp
   support/LoggingTest.cpp
   support/MacroUtilsTest.cpp
-  support/MathTest.cpp
   support/MemoryFlagsTest.cpp
   support/ProxyTest.cpp
   support/RTTITest.cpp
diff --git a/orc-rt/test/unit/bedrock/ExecutorProcessInfoTest.cpp b/orc-rt/test/unit/bedrock/ExecutorProcessInfoTest.cpp
index 1b5adab0cc75e..cfdd0a41a4eca 100644
--- a/orc-rt/test/unit/bedrock/ExecutorProcessInfoTest.cpp
+++ b/orc-rt/test/unit/bedrock/ExecutorProcessInfoTest.cpp
@@ -12,7 +12,7 @@
 
 #include "orc-rt/bedrock/ExecutorProcessInfo.h"
 #include "orc-rt-internal/bedrock/TargetDetails.h"
-#include "orc-rt/support/Math.h"
+#include "orc-rt/support/bit.h"
 #include "gtest/gtest.h"
 
 #include <algorithm>
@@ -49,7 +49,7 @@ TEST(ExecutorProcessInfoTest, DetectSucceeds) {
 TEST(ExecutorProcessInfoTest, DetectPageSizeIsPowerOfTwo) {
   auto EPI = cantFail(ExecutorProcessInfo::Detect());
   EXPECT_GT(EPI.pageSize(), 0U);
-  EXPECT_TRUE(isPowerOf2(EPI.pageSize()));
+  EXPECT_TRUE(has_single_bit(EPI.pageSize()));
 }
 
 TEST(ExecutorProcessInfoTest, DetectPageSizeAtLeast4096) {
diff --git a/orc-rt/test/unit/support/BitmaskEnumTest.cpp b/orc-rt/test/unit/support/BitmaskEnumTest.cpp
index 3cd169c20b9da..54fd14f9345eb 100644
--- a/orc-rt/test/unit/support/BitmaskEnumTest.cpp
+++ b/orc-rt/test/unit/support/BitmaskEnumTest.cpp
@@ -19,7 +19,7 @@ using namespace orc_rt;
 
 namespace {
 
-enum Flags { F0 = 0, F1 = 1, F2 = 2, F3 = 4, F4 = 8 };
+enum Flags : unsigned { F0 = 0, F1 = 1, F2 = 2, F3 = 4, F4 = 8 };
 
 } // namespace
 
@@ -107,7 +107,7 @@ TEST(BitmaskEnumTest, BitwiseNot) {
   EXPECT_EQ(15, ~F0);
 }
 
-enum class FlagsClass {
+enum class FlagsClass : unsigned {
   F0 = 0,
   F1 = 1,
   F2 = 2,
@@ -122,7 +122,7 @@ TEST(BitmaskEnumTest, ScopedEnum) {
 }
 
 struct Container {
-  enum Flags {
+  enum Flags : unsigned {
     F0 = 0,
     F1 = 1,
     F2 = 2,
@@ -139,4 +139,20 @@ struct Container {
 
 TEST(BitmaskEnumTest, EnumInStruct) { EXPECT_EQ(3, Container::getFlags()); }
 
+// A largest bit in the top position of the underlying type: the mask is every
+// bit, and computing it must not shift by the full width of the type.
+enum class TopBitFlags : uint32_t {
+  Low = 1,
+  High = 0x80000000,
+  ORC_RT_MARK_AS_BITMASK_ENUM(High)
+};
+
+TEST(BitmaskEnumTest, LargestBitInTopPosition) {
+  EXPECT_EQ(0xffffffffU, bitmask_enum_mask<TopBitFlags>());
+  EXPECT_EQ(32, bitmask_enum_num_bits_v<TopBitFlags>);
+  EXPECT_EQ(0x80000001U,
+            static_cast<uint32_t>(TopBitFlags::Low | TopBitFlags::High));
+  EXPECT_EQ(0xfffffffeU, static_cast<uint32_t>(~TopBitFlags::Low));
+}
+
 } // namespace
diff --git a/orc-rt/test/unit/support/MathTest.cpp b/orc-rt/test/unit/support/MathTest.cpp
deleted file mode 100644
index 35740a266413c..0000000000000
--- a/orc-rt/test/unit/support/MathTest.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-//===- MathTest.cpp -------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-//
-// Tests for orc-rt's Math.h APIs.
-//
-//===----------------------------------------------------------------------===//
-
-#include "orc-rt/support/Math.h"
-#include "gtest/gtest.h"
-
-using namespace orc_rt;
-
-TEST(MathTest, isPowerOf2) {
-  // Test [0..16]
-  EXPECT_FALSE(isPowerOf2(0x00));
-  EXPECT_TRUE(isPowerOf2(0x01));
-  EXPECT_TRUE(isPowerOf2(0x02));
-  EXPECT_FALSE(isPowerOf2(0x03));
-  EXPECT_TRUE(isPowerOf2(0x04));
-  EXPECT_FALSE(isPowerOf2(0x05));
-  EXPECT_FALSE(isPowerOf2(0x06));
-  EXPECT_FALSE(isPowerOf2(0x07));
-  EXPECT_TRUE(isPowerOf2(0x08));
-  EXPECT_FALSE(isPowerOf2(0x09));
-  EXPECT_FALSE(isPowerOf2(0x0A));
-  EXPECT_FALSE(isPowerOf2(0x0B));
-  EXPECT_FALSE(isPowerOf2(0x0C));
-  EXPECT_FALSE(isPowerOf2(0x0D));
-  EXPECT_FALSE(isPowerOf2(0x0E));
-  EXPECT_FALSE(isPowerOf2(0x0F));
-  EXPECT_TRUE(isPowerOf2(0x10));
-
-  // Test some higher powers of two and their adjacent values.
-  EXPECT_FALSE(isPowerOf2(0x1F));
-  EXPECT_TRUE(isPowerOf2(0x20));
-  EXPECT_FALSE(isPowerOf2(0x21));
-
-  EXPECT_FALSE(isPowerOf2(0x3F));
-  EXPECT_TRUE(isPowerOf2(0x40));
-  EXPECT_FALSE(isPowerOf2(0x41));
-
-  EXPECT_FALSE(isPowerOf2(0x7F));
-  EXPECT_TRUE(isPowerOf2(0x80));
-  EXPECT_FALSE(isPowerOf2(0x81));
-
-  // Test larger values.
-  EXPECT_FALSE(isPowerOf2(0x3fffffff));
-  EXPECT_TRUE(isPowerOf2(0x40000000));
-  EXPECT_FALSE(isPowerOf2(0x40000001));
-
-  // Test negatives.
-  EXPECT_FALSE(isPowerOf2(-1));
-}
-
-TEST(MathTest, nextPowerOf2) {
-  EXPECT_EQ(nextPowerOf2(0x00), (1 << 0));
-  EXPECT_EQ(nextPowerOf2(0x01), (1 << 1));
-  EXPECT_EQ(nextPowerOf2(0x02), (1 << 2));
-  EXPECT_EQ(nextPowerOf2(0x03), (1 << 2));
-  EXPECT_EQ(nextPowerOf2(0x04), (1 << 3));
-  EXPECT_EQ(nextPowerOf2(0x05), (1 << 3));
-  EXPECT_EQ(nextPowerOf2(0x06), (1 << 3));
-  EXPECT_EQ(nextPowerOf2(0x07), (1 << 3));
-  EXPECT_EQ(nextPowerOf2(0x08), (1 << 4));
-  EXPECT_EQ(nextPowerOf2(0x09), (1 << 4));
-  EXPECT_EQ(nextPowerOf2(0x0a), (1 << 4));
-  EXPECT_EQ(nextPowerOf2(0x0b), (1 << 4));
-  EXPECT_EQ(nextPowerOf2(0x0c), (1 << 4));
-  EXPECT_EQ(nextPowerOf2(0x0d), (1 << 4));
-  EXPECT_EQ(nextPowerOf2(0x0e), (1 << 4));
-  EXPECT_EQ(nextPowerOf2(0x0f), (1 << 4));
-  EXPECT_EQ(nextPowerOf2(0x10), (1 << 5));
-}
diff --git a/orc-rt/test/unit/support/bit-test.cpp b/orc-rt/test/unit/support/bit-test.cpp
index 53ecd850950c0..f2ca32b89edc9 100644
--- a/orc-rt/test/unit/support/bit-test.cpp
+++ b/orc-rt/test/unit/support/bit-test.cpp
@@ -167,6 +167,19 @@ TEST(BitTest, HasSingleBit) {
 
   static const int16_t kValueS16 = -32768;
   EXPECT_TRUE(orc_rt::has_single_bit(static_cast<uint16_t>(kValueS16)));
+
+  // Dense sweep and power-of-two boundaries.
+  for (unsigned V : {0x00U, 0x03U, 0x05U, 0x06U, 0x07U, 0x09U, 0x0aU, 0x0bU,
+                     0x0cU, 0x0dU, 0x0eU, 0x0fU})
+    EXPECT_FALSE(orc_rt::has_single_bit(V)) << "V = " << V;
+  for (unsigned V : {0x01U, 0x02U, 0x04U, 0x08U, 0x10U})
+    EXPECT_TRUE(orc_rt::has_single_bit(V)) << "V = " << V;
+
+  for (unsigned V : {0x20U, 0x40U, 0x80U, 0x40000000U}) {
+    EXPECT_FALSE(orc_rt::has_single_bit(V - 1)) << "V = " << V;
+    EXPECT_TRUE(orc_rt::has_single_bit(V)) << "V = " << V;
+    EXPECT_FALSE(orc_rt::has_single_bit(V + 1)) << "V = " << V;
+  }
 }
 
 TEST(BitTest, Rotl) {



More information about the llvm-commits mailing list