[llvm] [Offload][Conformance] Add `RandomGenerator` for large input spaces (PR #154252)

Leandro Lacerda via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 19 04:47:03 PDT 2025


https://github.com/leandrolcampos updated https://github.com/llvm/llvm-project/pull/154252

>From 2c6904b209c3a6ff3b2995258058d7954db41449 Mon Sep 17 00:00:00 2001
From: Leandro Augusto Lacerda Campos <leandrolcampos at yahoo.com.br>
Date: Mon, 18 Aug 2025 22:50:54 -0300
Subject: [PATCH 1/5] Add `RandomGenerator` for large input spaces

---
 .../Conformance/device_code/CUDAMath.cpp      |   5 +
 .../Conformance/device_code/DeviceAPIs.hpp    |   2 +
 .../Conformance/device_code/HIPMath.cpp       |   5 +
 .../Conformance/device_code/LLVMLibm.cpp      |   5 +
 .../include/mathtest/ExhaustiveGenerator.hpp  | 103 +++++------------
 .../include/mathtest/RandomGenerator.hpp      |  88 +++++++++++++++
 .../include/mathtest/RandomState.hpp          |  49 +++++++++
 .../include/mathtest/RangeBasedGenerator.hpp  | 104 ++++++++++++++++++
 .../Conformance/tests/CMakeLists.txt          |   1 +
 .../unittests/Conformance/tests/LogTest.cpp   |  66 +++++++++++
 10 files changed, 353 insertions(+), 75 deletions(-)
 create mode 100644 offload/unittests/Conformance/include/mathtest/RandomGenerator.hpp
 create mode 100644 offload/unittests/Conformance/include/mathtest/RandomState.hpp
 create mode 100644 offload/unittests/Conformance/include/mathtest/RangeBasedGenerator.hpp
 create mode 100644 offload/unittests/Conformance/tests/LogTest.cpp

diff --git a/offload/unittests/Conformance/device_code/CUDAMath.cpp b/offload/unittests/Conformance/device_code/CUDAMath.cpp
index a351e924b8f89..86c5d698d80af 100644
--- a/offload/unittests/Conformance/device_code/CUDAMath.cpp
+++ b/offload/unittests/Conformance/device_code/CUDAMath.cpp
@@ -119,6 +119,11 @@ __gpu_kernel void expm1fKernel(const float *X, float *Out,
   runKernelBody<__nv_expm1f>(NumElements, Out, X);
 }
 
+__gpu_kernel void logKernel(const double *X, double *Out,
+                            size_t NumElements) noexcept {
+  runKernelBody<__nv_log>(NumElements, Out, X);
+}
+
 __gpu_kernel void logfKernel(const float *X, float *Out,
                              size_t NumElements) noexcept {
   runKernelBody<__nv_logf>(NumElements, Out, X);
diff --git a/offload/unittests/Conformance/device_code/DeviceAPIs.hpp b/offload/unittests/Conformance/device_code/DeviceAPIs.hpp
index 8476dcbeff0c9..7941a05010cc7 100644
--- a/offload/unittests/Conformance/device_code/DeviceAPIs.hpp
+++ b/offload/unittests/Conformance/device_code/DeviceAPIs.hpp
@@ -63,6 +63,7 @@ float __nv_expf(float);
 float __nv_exp10f(float);
 float __nv_exp2f(float);
 float __nv_expm1f(float);
+double __nv_log(double);
 float __nv_logf(float);
 float __nv_log10f(float);
 float __nv_log1pf(float);
@@ -96,6 +97,7 @@ float __ocml_exp_f32(float);
 float __ocml_exp10_f32(float);
 float __ocml_exp2_f32(float);
 float __ocml_expm1_f32(float);
+double __ocml_log_f64(double);
 float __ocml_log_f32(float);
 float __ocml_log10_f32(float);
 float __ocml_log1p_f32(float);
diff --git a/offload/unittests/Conformance/device_code/HIPMath.cpp b/offload/unittests/Conformance/device_code/HIPMath.cpp
index 36efe6b2696ab..55f67669872c5 100644
--- a/offload/unittests/Conformance/device_code/HIPMath.cpp
+++ b/offload/unittests/Conformance/device_code/HIPMath.cpp
@@ -119,6 +119,11 @@ __gpu_kernel void expm1fKernel(const float *X, float *Out,
   runKernelBody<__ocml_expm1_f32>(NumElements, Out, X);
 }
 
+__gpu_kernel void logKernel(const double *X, double *Out,
+                            size_t NumElements) noexcept {
+  runKernelBody<__ocml_log_f64>(NumElements, Out, X);
+}
+
 __gpu_kernel void logfKernel(const float *X, float *Out,
                              size_t NumElements) noexcept {
   runKernelBody<__ocml_log_f32>(NumElements, Out, X);
diff --git a/offload/unittests/Conformance/device_code/LLVMLibm.cpp b/offload/unittests/Conformance/device_code/LLVMLibm.cpp
index 8869d87017486..cf33e0a86e94c 100644
--- a/offload/unittests/Conformance/device_code/LLVMLibm.cpp
+++ b/offload/unittests/Conformance/device_code/LLVMLibm.cpp
@@ -123,6 +123,11 @@ __gpu_kernel void hypotf16Kernel(const float16 *X, float16 *Y, float16 *Out,
   runKernelBody<hypotf16>(NumElements, Out, X, Y);
 }
 
+__gpu_kernel void logKernel(const double *X, double *Out,
+                            size_t NumElements) noexcept {
+  runKernelBody<log>(NumElements, Out, X);
+}
+
 __gpu_kernel void logfKernel(const float *X, float *Out,
                              size_t NumElements) noexcept {
   runKernelBody<logf>(NumElements, Out, X);
diff --git a/offload/unittests/Conformance/include/mathtest/ExhaustiveGenerator.hpp b/offload/unittests/Conformance/include/mathtest/ExhaustiveGenerator.hpp
index 6f7f7a9b665d0..1bed91251a041 100644
--- a/offload/unittests/Conformance/include/mathtest/ExhaustiveGenerator.hpp
+++ b/offload/unittests/Conformance/include/mathtest/ExhaustiveGenerator.hpp
@@ -8,8 +8,8 @@
 ///
 /// \file
 /// This file contains the definition of the ExhaustiveGenerator class, a
-/// concrete input generator that exhaustively creates inputs from a given
-/// sequence of ranges.
+/// concrete range-based generator that exhaustively creates inputs from a
+/// given sequence of ranges.
 ///
 //===----------------------------------------------------------------------===//
 
@@ -17,14 +17,9 @@
 #define MATHTEST_EXHAUSTIVEGENERATOR_HPP
 
 #include "mathtest/IndexedRange.hpp"
-#include "mathtest/InputGenerator.hpp"
+#include "mathtest/RangeBasedGenerator.hpp"
 
-#include "llvm/ADT/ArrayRef.h"
-#include "llvm/Support/Parallel.h"
-
-#include <algorithm>
 #include <array>
-#include <cassert>
 #include <cstddef>
 #include <cstdint>
 #include <tuple>
@@ -33,73 +28,44 @@ namespace mathtest {
 
 template <typename... InTypes>
 class [[nodiscard]] ExhaustiveGenerator final
-    : public InputGenerator<InTypes...> {
-  static constexpr std::size_t NumInputs = sizeof...(InTypes);
-  static_assert(NumInputs > 0, "The number of inputs must be at least 1");
+    : public RangeBasedGenerator<ExhaustiveGenerator<InTypes...>, InTypes...> {
+
+  friend class RangeBasedGenerator<ExhaustiveGenerator<InTypes...>, InTypes...>;
+
+  using Base = RangeBasedGenerator<ExhaustiveGenerator<InTypes...>, InTypes...>;
+  using IndexArrayType = std::array<uint64_t, Base::NumInputs>;
+
+  using Base::InputSpaceSize;
+  using Base::RangesTuple;
+  using Base::SizeToGenerate;
 
 public:
   explicit constexpr ExhaustiveGenerator(
       const IndexedRange<InTypes> &...Ranges) noexcept
-      : RangesTuple(Ranges...) {
-    bool Overflowed = getSizeWithOverflow(Ranges..., Size);
-
-    assert(!Overflowed && "The input space size is too large");
-    assert((Size > 0) && "The input space size must be at least 1");
+      : Base(Ranges...) {
+    SizeToGenerate = InputSpaceSize;
 
     IndexArrayType DimSizes = {};
     std::size_t DimIndex = 0;
     ((DimSizes[DimIndex++] = Ranges.getSize()), ...);
 
-    Strides[NumInputs - 1] = 1;
-    if constexpr (NumInputs > 1)
-      for (int Index = static_cast<int>(NumInputs) - 2; Index >= 0; --Index)
+    Strides[Base::NumInputs - 1] = 1;
+    if constexpr (Base::NumInputs > 1)
+      for (int Index = static_cast<int>(Base::NumInputs) - 2; Index >= 0;
+           --Index)
         Strides[Index] = Strides[Index + 1] * DimSizes[Index + 1];
   }
 
-  void reset() noexcept override { NextFlatIndex = 0; }
-
-  [[nodiscard]] std::size_t
-  fill(llvm::MutableArrayRef<InTypes>... Buffers) noexcept override {
-    const std::array<std::size_t, NumInputs> BufferSizes = {Buffers.size()...};
-    const std::size_t BufferSize = BufferSizes[0];
-    assert((BufferSize != 0) && "Buffer size cannot be zero");
-    assert(std::all_of(BufferSizes.begin(), BufferSizes.end(),
-                       [&](std::size_t Size) { return Size == BufferSize; }) &&
-           "All input buffers must have the same size");
-
-    if (NextFlatIndex >= Size)
-      return 0;
-
-    const auto BatchSize = std::min<uint64_t>(BufferSize, Size - NextFlatIndex);
-    const auto CurrentFlatIndex = NextFlatIndex;
-    NextFlatIndex += BatchSize;
-
-    auto BufferPtrsTuple = std::make_tuple(Buffers.data()...);
-
-    llvm::parallelFor(0, BatchSize, [&](std::size_t Offset) {
-      writeInputs(CurrentFlatIndex, Offset, BufferPtrsTuple);
-    });
-
-    return static_cast<std::size_t>(BatchSize);
-  }
-
 private:
-  using RangesTupleType = std::tuple<IndexedRange<InTypes>...>;
-  using IndexArrayType = std::array<uint64_t, NumInputs>;
-
-  static bool getSizeWithOverflow(const IndexedRange<InTypes> &...Ranges,
-                                  uint64_t &Size) noexcept {
-    Size = 1;
-    bool Overflowed = false;
-
-    auto Multiplier = [&](const uint64_t RangeSize) {
-      if (!Overflowed)
-        Overflowed = __builtin_mul_overflow(Size, RangeSize, &Size);
-    };
+  constexpr IndexArrayType getNDIndex(uint64_t FlatIndex) const noexcept {
+    IndexArrayType NDIndex;
 
-    (Multiplier(Ranges.getSize()), ...);
+    for (std::size_t Index = 0; Index < Base::NumInputs; ++Index) {
+      NDIndex[Index] = FlatIndex / Strides[Index];
+      FlatIndex -= NDIndex[Index] * Strides[Index];
+    }
 
-    return Overflowed;
+    return NDIndex;
   }
 
   template <typename BufferPtrsTupleType>
@@ -109,31 +75,18 @@ class [[nodiscard]] ExhaustiveGenerator final
     writeInputsImpl<0>(NDIndex, Offset, BufferPtrsTuple);
   }
 
-  constexpr IndexArrayType getNDIndex(uint64_t FlatIndex) const noexcept {
-    IndexArrayType NDIndex;
-
-    for (std::size_t Index = 0; Index < NumInputs; ++Index) {
-      NDIndex[Index] = FlatIndex / Strides[Index];
-      FlatIndex -= NDIndex[Index] * Strides[Index];
-    }
-
-    return NDIndex;
-  }
-
   template <std::size_t Index, typename BufferPtrsTupleType>
   void writeInputsImpl(IndexArrayType NDIndex, uint64_t Offset,
                        BufferPtrsTupleType BufferPtrsTuple) const noexcept {
-    if constexpr (Index < NumInputs) {
+    if constexpr (Index < Base::NumInputs) {
       const auto &Range = std::get<Index>(RangesTuple);
       std::get<Index>(BufferPtrsTuple)[Offset] = Range[NDIndex[Index]];
+
       writeInputsImpl<Index + 1>(NDIndex, Offset, BufferPtrsTuple);
     }
   }
 
-  uint64_t Size = 1;
-  RangesTupleType RangesTuple;
   IndexArrayType Strides = {};
-  uint64_t NextFlatIndex = 0;
 };
 } // namespace mathtest
 
diff --git a/offload/unittests/Conformance/include/mathtest/RandomGenerator.hpp b/offload/unittests/Conformance/include/mathtest/RandomGenerator.hpp
new file mode 100644
index 0000000000000..0c62370f17c7a
--- /dev/null
+++ b/offload/unittests/Conformance/include/mathtest/RandomGenerator.hpp
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the definition of the RandomGenerator class, a concrete
+/// range-based generator that randomly creates inputs from a given sequence of
+/// ranges.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef MATHTEST_RANDOMGENERATOR_HPP
+#define MATHTEST_RANDOMGENERATOR_HPP
+
+#include "mathtest/IndexedRange.hpp"
+#include "mathtest/RandomState.hpp"
+#include "mathtest/RangeBasedGenerator.hpp"
+
+#include <cstddef>
+#include <cstdint>
+#include <tuple>
+
+namespace mathtest {
+
+template <typename... InTypes>
+class [[nodiscard]] RandomGenerator final
+    : public RangeBasedGenerator<RandomGenerator<InTypes...>, InTypes...> {
+
+  friend class RangeBasedGenerator<RandomGenerator<InTypes...>, InTypes...>;
+
+  using Base = RangeBasedGenerator<RandomGenerator<InTypes...>, InTypes...>;
+
+  using Base::RangesTuple;
+  using Base::SizeToGenerate;
+
+public:
+  explicit constexpr RandomGenerator(
+      SeedTy BaseSeed, uint64_t Size,
+      const IndexedRange<InTypes> &...Ranges) noexcept
+      : Base(Ranges...), BaseSeed(BaseSeed) {
+    SizeToGenerate = Size;
+  }
+
+private:
+  static uint64_t getRandomIndex(RandomState &RNG,
+                                 uint64_t RangeSize) noexcept {
+    if (RangeSize == 0)
+      return 0;
+
+    const uint64_t Threshold = (-RangeSize) % RangeSize;
+
+    uint64_t RandomNumber;
+    do {
+      RandomNumber = RNG.next();
+    } while (RandomNumber < Threshold);
+
+    return RandomNumber % RangeSize;
+  }
+
+  template <typename BufferPtrsTupleType>
+  void writeInputs(uint64_t CurrentFlatIndex, uint64_t Offset,
+                   BufferPtrsTupleType BufferPtrsTuple) const noexcept {
+
+    RandomState RNG(SeedTy{BaseSeed.Value ^ (CurrentFlatIndex + Offset)});
+    writeInputsImpl<0>(RNG, Offset, BufferPtrsTuple);
+  }
+
+  template <std::size_t Index, typename BufferPtrsTupleType>
+  void writeInputsImpl(RandomState &RNG, uint64_t Offset,
+                       BufferPtrsTupleType BufferPtrsTuple) const noexcept {
+    if constexpr (Index < Base::NumInputs) {
+      const auto &Range = std::get<Index>(RangesTuple);
+      const auto RandomIndex = getRandomIndex(RNG, Range.getSize());
+      std::get<Index>(BufferPtrsTuple)[Offset] = Range[RandomIndex];
+
+      writeInputsImpl<Index + 1>(RNG, Offset, BufferPtrsTuple);
+    }
+  }
+
+  SeedTy BaseSeed;
+};
+} // namespace mathtest
+
+#endif // MATHTEST_RANDOMGENERATOR_HPP
diff --git a/offload/unittests/Conformance/include/mathtest/RandomState.hpp b/offload/unittests/Conformance/include/mathtest/RandomState.hpp
new file mode 100644
index 0000000000000..a09cc18c57142
--- /dev/null
+++ b/offload/unittests/Conformance/include/mathtest/RandomState.hpp
@@ -0,0 +1,49 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the definition of the RandomState class, a fast and
+/// lightweight pseudo-random number generator.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef MATHTEST_RANDOMSTATE_HPP
+#define MATHTEST_RANDOMSTATE_HPP
+
+#include <cstdint>
+
+struct SeedTy {
+  uint64_t Value;
+};
+
+class [[nodiscard]] RandomState {
+  uint64_t State;
+
+  [[nodiscard]] static uint64_t splitMix64(uint64_t X) noexcept {
+    X += 0x9E3779B97F4A7C15ULL;
+    X = (X ^ (X >> 30)) * 0xBF58476D1CE4E5B9ULL;
+    X = (X ^ (X >> 27)) * 0x94D049BB133111EBULL;
+    X = (X ^ (X >> 31));
+    return X ? X : 0x9E3779B97F4A7C15ULL;
+  }
+
+public:
+  explicit inline RandomState(SeedTy Seed) noexcept
+      : State(splitMix64(Seed.Value)) {}
+
+  [[nodiscard]] inline uint64_t next() noexcept {
+    uint64_t X = State;
+    X ^= X >> 12;
+    X ^= X << 25;
+    X ^= X >> 27;
+    State = X;
+    return X * 0x2545F4914F6CDD1DULL;
+  }
+};
+
+#endif // MATHTEST_RANDOMSTATE_HPP
diff --git a/offload/unittests/Conformance/include/mathtest/RangeBasedGenerator.hpp b/offload/unittests/Conformance/include/mathtest/RangeBasedGenerator.hpp
new file mode 100644
index 0000000000000..7e8b0889b9625
--- /dev/null
+++ b/offload/unittests/Conformance/include/mathtest/RangeBasedGenerator.hpp
@@ -0,0 +1,104 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the definition of the RangeBasedGenerator class, a base
+/// class for input generators that operate on a sequence of ranges.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef MATHTEST_RANGEBASEDGENERATOR_HPP
+#define MATHTEST_RANGEBASEDGENERATOR_HPP
+
+#include "mathtest/IndexedRange.hpp"
+#include "mathtest/InputGenerator.hpp"
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/Support/Parallel.h"
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <tuple>
+
+namespace mathtest {
+
+template <typename Derived, typename... InTypes>
+class [[nodiscard]] RangeBasedGenerator : public InputGenerator<InTypes...> {
+public:
+  void reset() noexcept override { NextFlatIndex = 0; }
+
+  [[nodiscard]] std::size_t
+  fill(llvm::MutableArrayRef<InTypes>... Buffers) noexcept override {
+    const std::array<std::size_t, NumInputs> BufferSizes = {Buffers.size()...};
+    const std::size_t BufferSize = BufferSizes[0];
+    assert((BufferSize != 0) && "Buffer size cannot be zero");
+    assert(std::all_of(BufferSizes.begin(), BufferSizes.end(),
+                       [&](std::size_t Size) { return Size == BufferSize; }) &&
+           "All input buffers must have the same size");
+
+    if (NextFlatIndex >= SizeToGenerate)
+      return 0;
+
+    const auto BatchSize =
+        std::min<uint64_t>(BufferSize, SizeToGenerate - NextFlatIndex);
+    const auto CurrentFlatIndex = NextFlatIndex;
+    NextFlatIndex += BatchSize;
+
+    auto BufferPtrsTuple = std::make_tuple(Buffers.data()...);
+
+    llvm::parallelFor(0, BatchSize, [&](std::size_t Offset) {
+      static_cast<Derived *>(this)->writeInputs(CurrentFlatIndex, Offset,
+                                                BufferPtrsTuple);
+    });
+
+    return static_cast<std::size_t>(BatchSize);
+  }
+
+protected:
+  using RangesTupleType = std::tuple<IndexedRange<InTypes>...>;
+
+  static constexpr std::size_t NumInputs = sizeof...(InTypes);
+  static_assert(NumInputs > 0, "The number of inputs must be at least 1");
+
+  explicit constexpr RangeBasedGenerator(
+      const IndexedRange<InTypes> &...Ranges) noexcept
+      : RangesTuple(Ranges...) {
+    bool Overflowed = getSizeWithOverflow(Ranges..., InputSpaceSize);
+
+    assert(!Overflowed && "The input space size is too large");
+    assert((InputSpaceSize > 0) && "The input space size must be at least 1");
+  }
+
+  uint64_t SizeToGenerate = 0;
+  uint64_t InputSpaceSize = 1;
+  RangesTupleType RangesTuple;
+
+private:
+  static bool getSizeWithOverflow(const IndexedRange<InTypes> &...Ranges,
+                                  uint64_t &Size) noexcept {
+    Size = 1;
+    bool Overflowed = false;
+
+    auto Multiplier = [&](const uint64_t RangeSize) {
+      if (!Overflowed)
+        Overflowed = __builtin_mul_overflow(Size, RangeSize, &Size);
+    };
+
+    (Multiplier(Ranges.getSize()), ...);
+
+    return Overflowed;
+  }
+
+  uint64_t NextFlatIndex = 0;
+};
+} // namespace mathtest
+
+#endif // MATHTEST_RANGEBASEDGENERATOR_HPP
diff --git a/offload/unittests/Conformance/tests/CMakeLists.txt b/offload/unittests/Conformance/tests/CMakeLists.txt
index 8c0109ba62ce3..7d45e7a8a5865 100644
--- a/offload/unittests/Conformance/tests/CMakeLists.txt
+++ b/offload/unittests/Conformance/tests/CMakeLists.txt
@@ -19,6 +19,7 @@ add_conformance_test(exp10f Exp10fTest.cpp)
 add_conformance_test(exp2f Exp2fTest.cpp)
 add_conformance_test(expm1f Expm1fTest.cpp)
 add_conformance_test(hypotf16 Hypotf16Test.cpp)
+add_conformance_test(log LogTest.cpp)
 add_conformance_test(logf LogfTest.cpp)
 add_conformance_test(log10f Log10fTest.cpp)
 add_conformance_test(log1pf Log1pfTest.cpp)
diff --git a/offload/unittests/Conformance/tests/LogTest.cpp b/offload/unittests/Conformance/tests/LogTest.cpp
new file mode 100644
index 0000000000000..ae568e2c47404
--- /dev/null
+++ b/offload/unittests/Conformance/tests/LogTest.cpp
@@ -0,0 +1,66 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file contains the conformance test of the log function.
+///
+//===----------------------------------------------------------------------===//
+
+#include "mathtest/CommandLineExtras.hpp"
+#include "mathtest/IndexedRange.hpp"
+#include "mathtest/RandomGenerator.hpp"
+#include "mathtest/RandomState.hpp"
+#include "mathtest/TestConfig.hpp"
+#include "mathtest/TestRunner.hpp"
+
+#include "llvm/ADT/StringRef.h"
+
+#include <cstdlib>
+#include <limits>
+#include <math.h>
+
+namespace {
+
+// Disambiguate the overloaded 'log' function to select the double version
+constexpr auto logd // NOLINT(readability-identifier-naming)
+    = static_cast<double (*)(double)>(log);
+} // namespace
+
+namespace mathtest {
+
+template <> struct FunctionConfig<logd> {
+  static constexpr llvm::StringRef Name = "log";
+  static constexpr llvm::StringRef KernelName = "logKernel";
+
+  // Source: The Khronos Group, The OpenCL C Specification v3.0.19, Sec. 7.4,
+  //         Table 68, Khronos Registry [July 10, 2025].
+  static constexpr uint64_t UlpTolerance = 3;
+};
+} // namespace mathtest
+
+int main(int argc, const char **argv) {
+  llvm::cl::ParseCommandLineOptions(argc, argv,
+                                    "Conformance test of the log function");
+
+  using namespace mathtest;
+
+  uint64_t Seed = 42;
+  uint64_t Size = 1ULL << 32;
+  IndexedRange<double> Range(/*Begin=*/0.0,
+                             /*End=*/std::numeric_limits<double>::infinity(),
+                             /*Inclusive=*/true);
+  RandomGenerator<double> Generator(SeedTy{Seed}, Size, Range);
+
+  const auto Configs = cl::getTestConfigs();
+  const llvm::StringRef DeviceBinaryDir = DEVICE_BINARY_DIR;
+  const bool IsVerbose = cl::IsVerbose;
+
+  bool Passed = runTests<logd>(Generator, Configs, DeviceBinaryDir, IsVerbose);
+
+  return Passed ? EXIT_SUCCESS : EXIT_FAILURE;
+}

>From 2e71a0a3ad9acfff3d5ed51a0c94c2520d260b18 Mon Sep 17 00:00:00 2001
From: Leandro Augusto Lacerda Campos <leandrolcampos at yahoo.com.br>
Date: Tue, 19 Aug 2025 00:22:39 -0300
Subject: [PATCH 2/5] Apply `[[nodiscard]]` and `constexpr` consistently

---
 .../include/mathtest/ExhaustiveGenerator.hpp  |  3 ++-
 .../include/mathtest/RandomGenerator.hpp      |  4 ++--
 .../include/mathtest/RandomState.hpp          |  4 ++--
 .../include/mathtest/RangeBasedGenerator.hpp  | 22 +++++++++++++------
 4 files changed, 21 insertions(+), 12 deletions(-)

diff --git a/offload/unittests/Conformance/include/mathtest/ExhaustiveGenerator.hpp b/offload/unittests/Conformance/include/mathtest/ExhaustiveGenerator.hpp
index 1bed91251a041..df87e0fcfa756 100644
--- a/offload/unittests/Conformance/include/mathtest/ExhaustiveGenerator.hpp
+++ b/offload/unittests/Conformance/include/mathtest/ExhaustiveGenerator.hpp
@@ -57,7 +57,8 @@ class [[nodiscard]] ExhaustiveGenerator final
   }
 
 private:
-  constexpr IndexArrayType getNDIndex(uint64_t FlatIndex) const noexcept {
+  [[nodiscard]] constexpr IndexArrayType
+  getNDIndex(uint64_t FlatIndex) const noexcept {
     IndexArrayType NDIndex;
 
     for (std::size_t Index = 0; Index < Base::NumInputs; ++Index) {
diff --git a/offload/unittests/Conformance/include/mathtest/RandomGenerator.hpp b/offload/unittests/Conformance/include/mathtest/RandomGenerator.hpp
index 0c62370f17c7a..395775fac609c 100644
--- a/offload/unittests/Conformance/include/mathtest/RandomGenerator.hpp
+++ b/offload/unittests/Conformance/include/mathtest/RandomGenerator.hpp
@@ -46,8 +46,8 @@ class [[nodiscard]] RandomGenerator final
   }
 
 private:
-  static uint64_t getRandomIndex(RandomState &RNG,
-                                 uint64_t RangeSize) noexcept {
+  [[nodiscard]] static uint64_t getRandomIndex(RandomState &RNG,
+                                               uint64_t RangeSize) noexcept {
     if (RangeSize == 0)
       return 0;
 
diff --git a/offload/unittests/Conformance/include/mathtest/RandomState.hpp b/offload/unittests/Conformance/include/mathtest/RandomState.hpp
index a09cc18c57142..012573d664a3b 100644
--- a/offload/unittests/Conformance/include/mathtest/RandomState.hpp
+++ b/offload/unittests/Conformance/include/mathtest/RandomState.hpp
@@ -24,7 +24,7 @@ struct SeedTy {
 class [[nodiscard]] RandomState {
   uint64_t State;
 
-  [[nodiscard]] static uint64_t splitMix64(uint64_t X) noexcept {
+  [[nodiscard]] static constexpr uint64_t splitMix64(uint64_t X) noexcept {
     X += 0x9E3779B97F4A7C15ULL;
     X = (X ^ (X >> 30)) * 0xBF58476D1CE4E5B9ULL;
     X = (X ^ (X >> 27)) * 0x94D049BB133111EBULL;
@@ -33,7 +33,7 @@ class [[nodiscard]] RandomState {
   }
 
 public:
-  explicit inline RandomState(SeedTy Seed) noexcept
+  explicit constexpr RandomState(SeedTy Seed) noexcept
       : State(splitMix64(Seed.Value)) {}
 
   [[nodiscard]] inline uint64_t next() noexcept {
diff --git a/offload/unittests/Conformance/include/mathtest/RangeBasedGenerator.hpp b/offload/unittests/Conformance/include/mathtest/RangeBasedGenerator.hpp
index 7e8b0889b9625..9ba1d26e18971 100644
--- a/offload/unittests/Conformance/include/mathtest/RangeBasedGenerator.hpp
+++ b/offload/unittests/Conformance/include/mathtest/RangeBasedGenerator.hpp
@@ -26,6 +26,7 @@
 #include <cassert>
 #include <cstddef>
 #include <cstdint>
+#include <optional>
 #include <tuple>
 
 namespace mathtest {
@@ -71,9 +72,12 @@ class [[nodiscard]] RangeBasedGenerator : public InputGenerator<InTypes...> {
   explicit constexpr RangeBasedGenerator(
       const IndexedRange<InTypes> &...Ranges) noexcept
       : RangesTuple(Ranges...) {
-    bool Overflowed = getSizeWithOverflow(Ranges..., InputSpaceSize);
+    const auto MaybeInputSpaceSize = getInputSpaceSize(Ranges...);
+
+    assert(MaybeInputSpaceSize.has_value() &&
+           "The input space size is too large");
+    InputSpaceSize = *MaybeInputSpaceSize;
 
-    assert(!Overflowed && "The input space size is too large");
     assert((InputSpaceSize > 0) && "The input space size must be at least 1");
   }
 
@@ -82,19 +86,23 @@ class [[nodiscard]] RangeBasedGenerator : public InputGenerator<InTypes...> {
   RangesTupleType RangesTuple;
 
 private:
-  static bool getSizeWithOverflow(const IndexedRange<InTypes> &...Ranges,
-                                  uint64_t &Size) noexcept {
-    Size = 1;
+  [[nodiscard]] static constexpr std::optional<uint64_t>
+  getInputSpaceSize(const IndexedRange<InTypes> &...Ranges) noexcept {
+    uint64_t InputSpaceSize = 1;
     bool Overflowed = false;
 
     auto Multiplier = [&](const uint64_t RangeSize) {
       if (!Overflowed)
-        Overflowed = __builtin_mul_overflow(Size, RangeSize, &Size);
+        Overflowed =
+            __builtin_mul_overflow(InputSpaceSize, RangeSize, &InputSpaceSize);
     };
 
     (Multiplier(Ranges.getSize()), ...);
 
-    return Overflowed;
+    if (Overflowed)
+      return std::nullopt;
+
+    return InputSpaceSize;
   }
 
   uint64_t NextFlatIndex = 0;

>From 590170b7ebaf5adedce3b1133b44eea6c183e99e Mon Sep 17 00:00:00 2001
From: Leandro Augusto Lacerda Campos <leandrolcampos at yahoo.com.br>
Date: Tue, 19 Aug 2025 08:05:51 -0300
Subject: [PATCH 3/5] Add reference for the PRNG algorithm

---
 .../unittests/Conformance/include/mathtest/RandomState.hpp    | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/offload/unittests/Conformance/include/mathtest/RandomState.hpp b/offload/unittests/Conformance/include/mathtest/RandomState.hpp
index 012573d664a3b..2651b02bbbd10 100644
--- a/offload/unittests/Conformance/include/mathtest/RandomState.hpp
+++ b/offload/unittests/Conformance/include/mathtest/RandomState.hpp
@@ -10,6 +10,10 @@
 /// This file contains the definition of the RandomState class, a fast and
 /// lightweight pseudo-random number generator.
 ///
+/// The implementation is based on the xorshift* generator, seeded using the
+/// SplitMix64 generator for robust initialization. For more details on the
+/// algorithm, see: https://en.wikipedia.org/wiki/Xorshift
+///
 //===----------------------------------------------------------------------===//
 
 #ifndef MATHTEST_RANDOMSTATE_HPP

>From 64be5acd57d18f6de71a0325b53c7b012f285f95 Mon Sep 17 00:00:00 2001
From: Leandro Augusto Lacerda Campos <leandrolcampos at yahoo.com.br>
Date: Tue, 19 Aug 2025 08:09:41 -0300
Subject: [PATCH 4/5] Remove `[[nodiscard]]` from the `next()` method of
 `RandomState`

---
 offload/unittests/Conformance/include/mathtest/RandomState.hpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/offload/unittests/Conformance/include/mathtest/RandomState.hpp b/offload/unittests/Conformance/include/mathtest/RandomState.hpp
index 2651b02bbbd10..322d53175236f 100644
--- a/offload/unittests/Conformance/include/mathtest/RandomState.hpp
+++ b/offload/unittests/Conformance/include/mathtest/RandomState.hpp
@@ -40,7 +40,7 @@ class [[nodiscard]] RandomState {
   explicit constexpr RandomState(SeedTy Seed) noexcept
       : State(splitMix64(Seed.Value)) {}
 
-  [[nodiscard]] inline uint64_t next() noexcept {
+  inline uint64_t next() noexcept {
     uint64_t X = State;
     X ^= X >> 12;
     X ^= X << 25;

>From 4f22449dd2e401ef47bd0be98f4f6b2a1ab89b01 Mon Sep 17 00:00:00 2001
From: Leandro Augusto Lacerda Campos <leandrolcampos at yahoo.com.br>
Date: Tue, 19 Aug 2025 08:46:41 -0300
Subject: [PATCH 5/5] Move `getInputSpaceSize` to `ExhaustiveGenerator`

---
 .../include/mathtest/ExhaustiveGenerator.hpp  | 31 ++++++++++++--
 .../include/mathtest/RandomGenerator.hpp      |  6 +--
 .../include/mathtest/RangeBasedGenerator.hpp  | 40 ++++---------------
 3 files changed, 37 insertions(+), 40 deletions(-)

diff --git a/offload/unittests/Conformance/include/mathtest/ExhaustiveGenerator.hpp b/offload/unittests/Conformance/include/mathtest/ExhaustiveGenerator.hpp
index df87e0fcfa756..39c6838eecf7e 100644
--- a/offload/unittests/Conformance/include/mathtest/ExhaustiveGenerator.hpp
+++ b/offload/unittests/Conformance/include/mathtest/ExhaustiveGenerator.hpp
@@ -20,8 +20,10 @@
 #include "mathtest/RangeBasedGenerator.hpp"
 
 #include <array>
+#include <cassert>
 #include <cstddef>
 #include <cstdint>
+#include <optional>
 #include <tuple>
 
 namespace mathtest {
@@ -35,15 +37,19 @@ class [[nodiscard]] ExhaustiveGenerator final
   using Base = RangeBasedGenerator<ExhaustiveGenerator<InTypes...>, InTypes...>;
   using IndexArrayType = std::array<uint64_t, Base::NumInputs>;
 
-  using Base::InputSpaceSize;
   using Base::RangesTuple;
-  using Base::SizeToGenerate;
+  using Base::Size;
 
 public:
   explicit constexpr ExhaustiveGenerator(
       const IndexedRange<InTypes> &...Ranges) noexcept
       : Base(Ranges...) {
-    SizeToGenerate = InputSpaceSize;
+    const auto MaybeSize = getInputSpaceSize(Ranges...);
+
+    assert(MaybeSize.has_value() && "The size is too large");
+    Size = *MaybeSize;
+
+    assert((Size > 0) && "The size must be at least 1");
 
     IndexArrayType DimSizes = {};
     std::size_t DimIndex = 0;
@@ -87,6 +93,25 @@ class [[nodiscard]] ExhaustiveGenerator final
     }
   }
 
+  [[nodiscard]] static constexpr std::optional<uint64_t>
+  getInputSpaceSize(const IndexedRange<InTypes> &...Ranges) noexcept {
+    uint64_t InputSpaceSize = 1;
+    bool Overflowed = false;
+
+    auto Multiplier = [&](const uint64_t RangeSize) {
+      if (!Overflowed)
+        Overflowed =
+            __builtin_mul_overflow(InputSpaceSize, RangeSize, &InputSpaceSize);
+    };
+
+    (Multiplier(Ranges.getSize()), ...);
+
+    if (Overflowed)
+      return std::nullopt;
+
+    return InputSpaceSize;
+  }
+
   IndexArrayType Strides = {};
 };
 } // namespace mathtest
diff --git a/offload/unittests/Conformance/include/mathtest/RandomGenerator.hpp b/offload/unittests/Conformance/include/mathtest/RandomGenerator.hpp
index 395775fac609c..436cd05f0a3d3 100644
--- a/offload/unittests/Conformance/include/mathtest/RandomGenerator.hpp
+++ b/offload/unittests/Conformance/include/mathtest/RandomGenerator.hpp
@@ -35,15 +35,13 @@ class [[nodiscard]] RandomGenerator final
   using Base = RangeBasedGenerator<RandomGenerator<InTypes...>, InTypes...>;
 
   using Base::RangesTuple;
-  using Base::SizeToGenerate;
+  using Base::Size;
 
 public:
   explicit constexpr RandomGenerator(
       SeedTy BaseSeed, uint64_t Size,
       const IndexedRange<InTypes> &...Ranges) noexcept
-      : Base(Ranges...), BaseSeed(BaseSeed) {
-    SizeToGenerate = Size;
-  }
+      : Base(Size, Ranges...), BaseSeed(BaseSeed) {}
 
 private:
   [[nodiscard]] static uint64_t getRandomIndex(RandomState &RNG,
diff --git a/offload/unittests/Conformance/include/mathtest/RangeBasedGenerator.hpp b/offload/unittests/Conformance/include/mathtest/RangeBasedGenerator.hpp
index 9ba1d26e18971..5e1e1139aba96 100644
--- a/offload/unittests/Conformance/include/mathtest/RangeBasedGenerator.hpp
+++ b/offload/unittests/Conformance/include/mathtest/RangeBasedGenerator.hpp
@@ -26,7 +26,6 @@
 #include <cassert>
 #include <cstddef>
 #include <cstdint>
-#include <optional>
 #include <tuple>
 
 namespace mathtest {
@@ -45,11 +44,10 @@ class [[nodiscard]] RangeBasedGenerator : public InputGenerator<InTypes...> {
                        [&](std::size_t Size) { return Size == BufferSize; }) &&
            "All input buffers must have the same size");
 
-    if (NextFlatIndex >= SizeToGenerate)
+    if (NextFlatIndex >= Size)
       return 0;
 
-    const auto BatchSize =
-        std::min<uint64_t>(BufferSize, SizeToGenerate - NextFlatIndex);
+    const auto BatchSize = std::min<uint64_t>(BufferSize, Size - NextFlatIndex);
     const auto CurrentFlatIndex = NextFlatIndex;
     NextFlatIndex += BatchSize;
 
@@ -71,40 +69,16 @@ class [[nodiscard]] RangeBasedGenerator : public InputGenerator<InTypes...> {
 
   explicit constexpr RangeBasedGenerator(
       const IndexedRange<InTypes> &...Ranges) noexcept
-      : RangesTuple(Ranges...) {
-    const auto MaybeInputSpaceSize = getInputSpaceSize(Ranges...);
+      : RangesTuple(Ranges...) {}
 
-    assert(MaybeInputSpaceSize.has_value() &&
-           "The input space size is too large");
-    InputSpaceSize = *MaybeInputSpaceSize;
-
-    assert((InputSpaceSize > 0) && "The input space size must be at least 1");
-  }
+  explicit constexpr RangeBasedGenerator(
+      uint64_t Size, const IndexedRange<InTypes> &...Ranges) noexcept
+      : RangesTuple(Ranges...), Size(Size) {}
 
-  uint64_t SizeToGenerate = 0;
-  uint64_t InputSpaceSize = 1;
   RangesTupleType RangesTuple;
+  uint64_t Size = 0;
 
 private:
-  [[nodiscard]] static constexpr std::optional<uint64_t>
-  getInputSpaceSize(const IndexedRange<InTypes> &...Ranges) noexcept {
-    uint64_t InputSpaceSize = 1;
-    bool Overflowed = false;
-
-    auto Multiplier = [&](const uint64_t RangeSize) {
-      if (!Overflowed)
-        Overflowed =
-            __builtin_mul_overflow(InputSpaceSize, RangeSize, &InputSpaceSize);
-    };
-
-    (Multiplier(Ranges.getSize()), ...);
-
-    if (Overflowed)
-      return std::nullopt;
-
-    return InputSpaceSize;
-  }
-
   uint64_t NextFlatIndex = 0;
 };
 } // namespace mathtest



More information about the llvm-commits mailing list