From d702705674b9c433d61b6169f2a88b2f539a8e14 Mon Sep 17 00:00:00 2001 From: Lang Hames Date: Wed, 27 Aug 2025 15:52:29 +1000 Subject: [PATCH] [orc-rt] Add bind_front, a pre-c++-20 std::bind_front substitute. This can be used until the ORC runtime is able to move to c++-20. Also adds a CommonTestUtils header with a utility class, OpCounter, that counts the number of default constructions, copy constructions and assignments, move constructions and assignments, and destructions. This is used to test that orc_rt::bind_front doesn't introduce unnecessary copies / moves. --- orc-rt/include/CMakeLists.txt | 1 + orc-rt/include/orc-rt/bind.h | 56 ++++++++++++++++++ orc-rt/unittests/CMakeLists.txt | 2 + orc-rt/unittests/CommonTestUtils.cpp | 20 +++++++ orc-rt/unittests/CommonTestUtils.h | 60 ++++++++++++++++++++ orc-rt/unittests/bind-test.cpp | 85 ++++++++++++++++++++++++++++ 6 files changed, 224 insertions(+) create mode 100644 orc-rt/include/orc-rt/bind.h create mode 100644 orc-rt/unittests/CommonTestUtils.cpp create mode 100644 orc-rt/unittests/CommonTestUtils.h create mode 100644 orc-rt/unittests/bind-test.cpp diff --git a/orc-rt/include/CMakeLists.txt b/orc-rt/include/CMakeLists.txt index 61834f765fc8c..a270fd7c06c73 100644 --- a/orc-rt/include/CMakeLists.txt +++ b/orc-rt/include/CMakeLists.txt @@ -12,6 +12,7 @@ set(ORC_RT_HEADERS orc-rt/RTTI.h orc-rt/WrapperFunctionResult.h orc-rt/SimplePackedSerialization.h + orc-rt/bind.h orc-rt/bit.h orc-rt/move_only_function.h orc-rt/span.h diff --git a/orc-rt/include/orc-rt/bind.h b/orc-rt/include/orc-rt/bind.h new file mode 100644 index 0000000000000..ca1f20d1b5b3d --- /dev/null +++ b/orc-rt/include/orc-rt/bind.h @@ -0,0 +1,56 @@ +//===---- bind.h - Substitute for future STL bind_front APIs ----*- 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 +// +//===----------------------------------------------------------------------===// +// +// Substitute for STL bind* APIs that aren't available to the ORC runtime yet. +// +// TODO: Replace all uses once the respective APIs are available. +// +//===----------------------------------------------------------------------===// + +#ifndef ORC_RT_BIND_H +#define ORC_RT_BIND_H + +#include +#include + +namespace orc_rt { +namespace detail { + +template class BoundFn { +private: + template + auto callExpandingBound(std::index_sequence, ArgTs &&...Args) { + return F(std::get(BoundArgs)..., std::move(Args)...); + } + +public: + BoundFn(Fn &&F, BoundArgTs &&...BoundArgs) + : F(std::move(F)), BoundArgs(std::forward(BoundArgs)...) {} + + template auto operator()(ArgTs &&...Args) { + return callExpandingBound(std::index_sequence_for(), + std::forward(Args)...); + } + +private: + std::decay_t F; + std::tuple...> BoundArgs; +}; + +} // namespace detail + +template +detail::BoundFn bind_front(Fn &&F, + BoundArgTs &&...BoundArgs) { + return detail::BoundFn( + std::forward(F), std::forward(BoundArgs)...); +} + +} // namespace orc_rt + +#endif // ORC_RT_BIND_H diff --git a/orc-rt/unittests/CMakeLists.txt b/orc-rt/unittests/CMakeLists.txt index 0fd0d09d10aa5..39d9223f5b0eb 100644 --- a/orc-rt/unittests/CMakeLists.txt +++ b/orc-rt/unittests/CMakeLists.txt @@ -13,6 +13,7 @@ endfunction() add_orc_rt_unittest(CoreTests BitmaskEnumTest.cpp + CommonTestUtils.cpp ErrorTest.cpp ExecutorAddressTest.cpp IntervalMapTest.cpp @@ -21,6 +22,7 @@ add_orc_rt_unittest(CoreTests RTTITest.cpp SimplePackedSerializationTest.cpp WrapperFunctionResultTest.cpp + bind-test.cpp bit-test.cpp move_only_function-test.cpp span-test.cpp diff --git a/orc-rt/unittests/CommonTestUtils.cpp b/orc-rt/unittests/CommonTestUtils.cpp new file mode 100644 index 0000000000000..d9f9433d167fd --- /dev/null +++ b/orc-rt/unittests/CommonTestUtils.cpp @@ -0,0 +1,20 @@ +//===- CommonTestUtils.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 +// +//===----------------------------------------------------------------------===// +// +// Common test utilities. +// +//===----------------------------------------------------------------------===// + +#include "CommonTestUtils.h" + +size_t OpCounter::DefaultConstructions = 0; +size_t OpCounter::CopyConstructions = 0; +size_t OpCounter::CopyAssignments = 0; +size_t OpCounter::MoveConstructions = 0; +size_t OpCounter::MoveAssignments = 0; +size_t OpCounter::Destructions = 0; diff --git a/orc-rt/unittests/CommonTestUtils.h b/orc-rt/unittests/CommonTestUtils.h new file mode 100644 index 0000000000000..5ff2c8e6e8989 --- /dev/null +++ b/orc-rt/unittests/CommonTestUtils.h @@ -0,0 +1,60 @@ +//===- CommonTestUtils.h --------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef ORC_RT_UNITTEST_COMMONTESTUTILS_H +#define ORC_RT_UNITTEST_COMMONTESTUTILS_H + +#include + +class OpCounter { +public: + OpCounter() { ++DefaultConstructions; } + OpCounter(const OpCounter &Other) { ++CopyConstructions; } + OpCounter &operator=(const OpCounter &Other) { + ++CopyAssignments; + return *this; + } + OpCounter(OpCounter &&Other) { ++MoveConstructions; } + OpCounter &operator=(OpCounter &&Other) { + ++MoveAssignments; + return *this; + } + ~OpCounter() { ++Destructions; } + + static size_t defaultConstructions() { return DefaultConstructions; } + static size_t copyConstructions() { return CopyConstructions; } + static size_t copyAssignments() { return CopyAssignments; } + static size_t copies() { return copyConstructions() + copyAssignments(); } + static size_t moveConstructions() { return MoveConstructions; } + static size_t moveAssignments() { return MoveAssignments; } + static size_t moves() { return moveConstructions() + moveAssignments(); } + static size_t destructions() { return Destructions; } + + static bool destructionsMatch() { + return destructions() == defaultConstructions() + copies() + moves(); + } + + static void reset() { + DefaultConstructions = 0; + CopyConstructions = 0; + CopyAssignments = 0; + MoveConstructions = 0; + MoveAssignments = 0; + Destructions = 0; + } + +private: + static size_t DefaultConstructions; + static size_t CopyConstructions; + static size_t CopyAssignments; + static size_t MoveConstructions; + static size_t MoveAssignments; + static size_t Destructions; +}; + +#endif // ORC_RT_UNITTEST_COMMONTESTUTILS_H diff --git a/orc-rt/unittests/bind-test.cpp b/orc-rt/unittests/bind-test.cpp new file mode 100644 index 0000000000000..26b868aaef77b --- /dev/null +++ b/orc-rt/unittests/bind-test.cpp @@ -0,0 +1,85 @@ +//===- bind-test.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 bind-test.h APIs. +// +//===----------------------------------------------------------------------===// + +#include "CommonTestUtils.h" +#include "orc-rt/bind.h" +#include "orc-rt/move_only_function.h" +#include "gtest/gtest.h" + +using namespace orc_rt; + +static void voidVoid(void) {} + +TEST(BindTest, VoidVoid) { + auto B = bind_front(voidVoid); + B(); +} + +static int addInts(int X, int Y) { return X + Y; } + +TEST(BindTest, SimpleBind) { + auto Add1 = bind_front(addInts, 1); + EXPECT_EQ(Add1(2), 3); +} + +TEST(BindTest, NoBoundArguments) { + auto Add = bind_front(addInts); + EXPECT_EQ(Add(1, 2), 3); +} + +TEST(BindTest, NoFreeArguments) { + auto Add1And2 = bind_front(addInts, 1, 2); + EXPECT_EQ(Add1And2(), 3); +} + +TEST(BindTest, LambdaCapture) { + auto Add1 = bind_front([](int X, int Y) { return X + Y; }, 1); + EXPECT_EQ(Add1(2), 3); +} + +TEST(BindTest, MinimalMoves) { + OpCounter::reset(); + { + auto B = bind_front([](OpCounter &O, int) {}, OpCounter()); + B(0); + } + EXPECT_EQ(OpCounter::defaultConstructions(), 1U); + EXPECT_EQ(OpCounter::copies(), 0U); + EXPECT_EQ(OpCounter::moves(), 1U); + EXPECT_EQ(OpCounter::destructions(), 2U); +} + +TEST(BindTest, MinimalCopies) { + OpCounter::reset(); + { + OpCounter O; + auto B = bind_front([](OpCounter &O, int) {}, O); + B(0); + } + EXPECT_EQ(OpCounter::defaultConstructions(), 1U); + EXPECT_EQ(OpCounter::copies(), 1U); + EXPECT_EQ(OpCounter::moves(), 0U); + EXPECT_EQ(OpCounter::destructions(), 2U); +} + +static int increment(int N) { return N + 1; } + +TEST(BindTest, BindFunction) { + auto Op = bind_front([](int op(int), int arg) { return op(arg); }, increment); + EXPECT_EQ(Op(1), 2); +} + +TEST(BindTest, BindTo_move_only_function) { + move_only_function Add = [](int X, int Y) { return X + Y; }; + auto Add1 = bind_front(std::move(Add), 1); + EXPECT_EQ(Add1(2), 3); +}