[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