diff --git a/orc-rt/include/CMakeLists.txt b/orc-rt/include/CMakeLists.txt index ed90a7a45e2f..e58a70f74dd6 100644 --- a/orc-rt/include/CMakeLists.txt +++ b/orc-rt/include/CMakeLists.txt @@ -1,10 +1,13 @@ set(ORC_RT_HEADERS + orc-rt-c/ExternC.h + orc-rt-c/WrapperFunctionResult.h orc-rt-c/orc-rt.h orc-rt/BitmaskEnum.h orc-rt/Compiler.h orc-rt/Error.h orc-rt/Math.h orc-rt/RTTI.h + orc-rt/WrapperFunctionResult.h orc-rt/move_only_function.h orc-rt/span.h ) diff --git a/orc-rt/include/orc-rt-c/ExternC.h b/orc-rt/include/orc-rt-c/ExternC.h new file mode 100644 index 000000000000..dd839037adf1 --- /dev/null +++ b/orc-rt/include/orc-rt-c/ExternC.h @@ -0,0 +1,41 @@ +/*===- ExternC.h - C API 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 *| +|* *| +|*===----------------------------------------------------------------------===*| +|* *| +|* This file defines the C API for the ORC runtime *| +|* *| +\*===----------------------------------------------------------------------===*/ + +#ifndef ORC_RT_C_EXTERNC_H +#define ORC_RT_C_EXTERNC_H + +/* Helper to suppress strict prototype warnings. */ +#ifdef __clang__ +#define ORC_RT_C_STRICT_PROTOTYPES_BEGIN \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic error \"-Wstrict-prototypes\"") +#define ORC_RT_C_STRICT_PROTOTYPES_END _Pragma("clang diagnostic pop") +#else +#define ORC_RT_C_STRICT_PROTOTYPES_BEGIN +#define ORC_RT_C_STRICT_PROTOTYPES_END +#endif + +/* Helper to wrap C code for C++ */ +#ifdef __cplusplus +#define ORC_RT_C_EXTERN_C_BEGIN \ + extern "C" { \ + ORC_RT_C_STRICT_PROTOTYPES_BEGIN +#define ORC_RT_C_EXTERN_C_END \ + ORC_RT_C_STRICT_PROTOTYPES_END \ + } +#else +#define ORC_RT_C_EXTERN_C_BEGIN ORC_RT_C_STRICT_PROTOTYPES_BEGIN +#define ORC_RT_C_EXTERN_C_END ORC_RT_C_STRICT_PROTOTYPES_END +#endif + +#endif /* ORC_RT_C_EXTERNC_H */ diff --git a/orc-rt/include/orc-rt-c/WrapperFunctionResult.h b/orc-rt/include/orc-rt-c/WrapperFunctionResult.h new file mode 100644 index 000000000000..d8dec28680f1 --- /dev/null +++ b/orc-rt/include/orc-rt-c/WrapperFunctionResult.h @@ -0,0 +1,178 @@ +/*===----- WrapperFunctionResult.h - blob-of-bytes container -----*- 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 *| +|* *| +|*===----------------------------------------------------------------------===*| +|* *| +|* Defines orc_rt_WrapperFunctionResult and related APIs. *| +|* *| +\*===----------------------------------------------------------------------===*/ + +#ifndef ORC_RT_C_WRAPPERFUNCTIONRESULT_H +#define ORC_RT_C_WRAPPERFUNCTIONRESULT_H + +#include "orc-rt-c/ExternC.h" + +#include +#include +#include + +ORC_RT_C_EXTERN_C_BEGIN + +typedef union { + char *ValuePtr; + char Value[sizeof(char *)]; +} orc_rt_WrapperFunctionResultDataUnion; + +/** + * orc_rt_WrapperFunctionResult is a kind of C-SmallVector with an + * out-of-band error state. + * + * If Size == 0 and Data.ValuePtr is non-zero then the value is in the + * 'out-of-band error' state, and Data.ValuePtr points at a malloc-allocated, + * null-terminated string error message. + * + * If Size <= sizeof(orc_rt_WrapperFunctionResultData) then the value is in + * the 'small' state and the content is held in the first Size bytes of + * Data.Value. + * + * If Size > sizeof(orc_rt_WrapperFunctionResultData) then the value is in the + * 'large' state and the content is held in the first Size bytes of the + * memory pointed to by Data.ValuePtr. This memory must have been allocated by + * malloc, and will be freed with free when this value is destroyed. + */ +typedef struct { + orc_rt_WrapperFunctionResultDataUnion Data; + size_t Size; +} orc_rt_WrapperFunctionResult; + +/** + * Zero-initialize an orc_rt_WrapperFunctionResult. + */ +static inline void +orc_rt_WrapperFunctionResultInit(orc_rt_WrapperFunctionResult *R) { + R->Size = 0; + R->Data.ValuePtr = 0; +} + +/** + * Create an orc_rt_WrapperFunctionResult with an uninitialized buffer of + * size Size. The buffer is returned via the DataPtr argument. + */ +static inline orc_rt_WrapperFunctionResult +orc_rt_WrapperFunctionResultAllocate(size_t Size) { + orc_rt_WrapperFunctionResult R; + R.Size = Size; + // If Size is 0 ValuePtr must be 0 or it is considered an out-of-band error. + R.Data.ValuePtr = 0; + if (Size > sizeof(R.Data.Value)) + R.Data.ValuePtr = (char *)malloc(Size); + return R; +} + +/** + * Create an orc_rt_WrapperFunctionResult from the given data range. + */ +static inline orc_rt_WrapperFunctionResult +orc_rt_CreateWrapperFunctionResultFromRange(const char *Data, size_t Size) { + orc_rt_WrapperFunctionResult R; + R.Size = Size; + if (R.Size > sizeof(R.Data.Value)) { + char *Tmp = (char *)malloc(Size); + memcpy(Tmp, Data, Size); + R.Data.ValuePtr = Tmp; + } else + memcpy(R.Data.Value, Data, Size); + return R; +} + +/** + * Create an orc_rt_WrapperFunctionResult by copying the given string, + * including the null-terminator. + * + * This function copies the input string. The client is responsible for freeing + * the ErrMsg arg. + */ +static inline orc_rt_WrapperFunctionResult +orc_rt_CreateWrapperFunctionResultFromString(const char *Source) { + return orc_rt_CreateWrapperFunctionResultFromRange(Source, + strlen(Source) + 1); +} + +/** + * Create an orc_rt_WrapperFunctionResult representing an out-of-band + * error. + * + * This function copies the input string. The client is responsible for freeing + * the ErrMsg arg. + */ +static inline orc_rt_WrapperFunctionResult +orc_rt_CreateWrapperFunctionResultFromOutOfBandError(const char *ErrMsg) { + orc_rt_WrapperFunctionResult R; + R.Size = 0; + char *Tmp = (char *)malloc(strlen(ErrMsg) + 1); + strcpy(Tmp, ErrMsg); + R.Data.ValuePtr = Tmp; + return R; +} + +/** + * This should be called to destroy orc_rt_WrapperFunctionResult values + * regardless of their state. + */ +static inline void +orc_rt_DisposeWrapperFunctionResult(orc_rt_WrapperFunctionResult *R) { + if (R->Size > sizeof(R->Data.Value) || (R->Size == 0 && R->Data.ValuePtr)) + free(R->Data.ValuePtr); +} + +/** + * Get a pointer to the data contained in the given + * orc_rt_WrapperFunctionResult. + */ +static inline char * +orc_rt_WrapperFunctionResultData(orc_rt_WrapperFunctionResult *R) { + assert((R->Size != 0 || R->Data.ValuePtr == NULL) && + "Cannot get data for out-of-band error value"); + return R->Size > sizeof(R->Data.Value) ? R->Data.ValuePtr : R->Data.Value; +} + +/** + * Safely get the size of the given orc_rt_WrapperFunctionResult. + * + * Asserts that we're not trying to access the size of an error value. + */ +static inline size_t +orc_rt_WrapperFunctionResultSize(const orc_rt_WrapperFunctionResult *R) { + assert((R->Size != 0 || R->Data.ValuePtr == NULL) && + "Cannot get size for out-of-band error value"); + return R->Size; +} + +/** + * Returns 1 if this value is equivalent to a value just initialized by + * orc_rt_WrapperFunctionResultInit, 0 otherwise. + */ +static inline size_t +orc_rt_WrapperFunctionResultEmpty(const orc_rt_WrapperFunctionResult *R) { + return R->Size == 0 && R->Data.ValuePtr == 0; +} + +/** + * Returns a pointer to the out-of-band error string for this + * orc_rt_WrapperFunctionResult, or null if there is no error. + * + * The orc_rt_WrapperFunctionResult retains ownership of the error + * string, so it should be copied if the caller wishes to preserve it. + */ +static inline const char *orc_rt_WrapperFunctionResultGetOutOfBandError( + const orc_rt_WrapperFunctionResult *R) { + return R->Size == 0 ? R->Data.ValuePtr : 0; +} + +ORC_RT_C_EXTERN_C_END + +#endif /* ORC_RT_WRAPPERFUNCTIONRESULT_H */ diff --git a/orc-rt/include/orc-rt/WrapperFunctionResult.h b/orc-rt/include/orc-rt/WrapperFunctionResult.h new file mode 100644 index 000000000000..d3bc132070e8 --- /dev/null +++ b/orc-rt/include/orc-rt/WrapperFunctionResult.h @@ -0,0 +1,106 @@ +//===---- WrapperFunctionResult.h -- blob-of-bytes container ----*- 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 +// +//===----------------------------------------------------------------------===// +// +// Defines WrapperFunctionResult and related APIs. +// +//===----------------------------------------------------------------------===// + +#ifndef ORC_RT_WRAPPERFUNCTIONRESULT_H +#define ORC_RT_WRAPPERFUNCTIONRESULT_H + +#include "orc-rt-c/WrapperFunctionResult.h" + +#include + +namespace orc_rt { + +/// A C++ convenience wrapper for orc_rt_WrapperFunctionResult. Auto-disposes +/// the contained result on destruction. +class WrapperFunctionResult { +public: + /// Create a default WrapperFunctionResult. + WrapperFunctionResult() { orc_rt_WrapperFunctionResultInit(&R); } + + /// Create a WrapperFunctionResult from a WrapperFunctionResult. This + /// instance takes ownership of the result object and will automatically + /// call dispose on the result upon destruction. + WrapperFunctionResult(orc_rt_WrapperFunctionResult R) : R(R) {} + + WrapperFunctionResult(const WrapperFunctionResult &) = delete; + WrapperFunctionResult &operator=(const WrapperFunctionResult &) = delete; + + WrapperFunctionResult(WrapperFunctionResult &&Other) { + orc_rt_WrapperFunctionResultInit(&R); + std::swap(R, Other.R); + } + + WrapperFunctionResult &operator=(WrapperFunctionResult &&Other) { + orc_rt_WrapperFunctionResult Tmp; + orc_rt_WrapperFunctionResultInit(&Tmp); + std::swap(Tmp, Other.R); + std::swap(R, Tmp); + return *this; + } + + ~WrapperFunctionResult() { orc_rt_DisposeWrapperFunctionResult(&R); } + + /// Relinquish ownership of and return the + /// orc_rt_WrapperFunctionResult. + orc_rt_WrapperFunctionResult release() { + orc_rt_WrapperFunctionResult Tmp; + orc_rt_WrapperFunctionResultInit(&Tmp); + std::swap(R, Tmp); + return Tmp; + } + + /// Get a pointer to the data contained in this instance. + char *data() { return orc_rt_WrapperFunctionResultData(&R); } + + /// Returns the size of the data contained in this instance. + size_t size() const { return orc_rt_WrapperFunctionResultSize(&R); } + + /// Returns true if this value is equivalent to a default-constructed + /// WrapperFunctionResult. + bool empty() const { return orc_rt_WrapperFunctionResultEmpty(&R); } + + /// Create a WrapperFunctionResult with the given size and return a pointer + /// to the underlying memory. + static WrapperFunctionResult allocate(size_t Size) { + WrapperFunctionResult R; + R.R = orc_rt_WrapperFunctionResultAllocate(Size); + return R; + } + + /// Copy from the given char range. + static WrapperFunctionResult copyFrom(const char *Source, size_t Size) { + return orc_rt_CreateWrapperFunctionResultFromRange(Source, Size); + } + + /// Copy from the given null-terminated string (includes the null-terminator). + static WrapperFunctionResult copyFrom(const char *Source) { + return orc_rt_CreateWrapperFunctionResultFromString(Source); + } + + /// Create an out-of-band error by copying the given string. + static WrapperFunctionResult createOutOfBandError(const char *Msg) { + return orc_rt_CreateWrapperFunctionResultFromOutOfBandError(Msg); + } + + /// If this value is an out-of-band error then this returns the error message, + /// otherwise returns nullptr. + const char *getOutOfBandError() const { + return orc_rt_WrapperFunctionResultGetOutOfBandError(&R); + } + +private: + orc_rt_WrapperFunctionResult R; +}; + +} // namespace orc_rt + +#endif // ORC_RT_WRAPPERFUNCTIONRESULT_H diff --git a/orc-rt/unittests/CMakeLists.txt b/orc-rt/unittests/CMakeLists.txt index 6a1f5d6f8bce..2abbdd7bc634 100644 --- a/orc-rt/unittests/CMakeLists.txt +++ b/orc-rt/unittests/CMakeLists.txt @@ -16,6 +16,7 @@ add_orc_rt_unittest(CoreTests ErrorTest.cpp MathTest.cpp RTTITest.cpp + WrapperFunctionResultTest.cpp move_only_function-test.cpp span-test.cpp DISABLE_LLVM_LINK_LLVM_DYLIB diff --git a/orc-rt/unittests/WrapperFunctionResultTest.cpp b/orc-rt/unittests/WrapperFunctionResultTest.cpp new file mode 100644 index 000000000000..9ecc513ba50f --- /dev/null +++ b/orc-rt/unittests/WrapperFunctionResultTest.cpp @@ -0,0 +1,60 @@ +//===-- wrapper_function_utils_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 +// +//===----------------------------------------------------------------------===// +// +// This file is a part of the ORC runtime. +// +//===----------------------------------------------------------------------===// + +#include "orc-rt-c/WrapperFunctionResult.h" +#include "orc-rt/WrapperFunctionResult.h" +#include "gtest/gtest.h" + +using namespace orc_rt; + +namespace { +constexpr const char *TestString = "test string"; +} // end anonymous namespace + +TEST(WrapperFunctionUtilsTest, DefaultWrapperFunctionResult) { + WrapperFunctionResult R; + EXPECT_TRUE(R.empty()); + EXPECT_EQ(R.size(), 0U); + EXPECT_EQ(R.getOutOfBandError(), nullptr); +} + +TEST(WrapperFunctionUtilsTest, WrapperFunctionResultFromCStruct) { + orc_rt_WrapperFunctionResult CR = + orc_rt_CreateWrapperFunctionResultFromString(TestString); + WrapperFunctionResult R(CR); + EXPECT_EQ(R.size(), strlen(TestString) + 1); + EXPECT_TRUE(strcmp(R.data(), TestString) == 0); + EXPECT_FALSE(R.empty()); + EXPECT_EQ(R.getOutOfBandError(), nullptr); +} + +TEST(WrapperFunctionUtilsTest, WrapperFunctionResultFromRange) { + auto R = WrapperFunctionResult::copyFrom(TestString, strlen(TestString) + 1); + EXPECT_EQ(R.size(), strlen(TestString) + 1); + EXPECT_TRUE(strcmp(R.data(), TestString) == 0); + EXPECT_FALSE(R.empty()); + EXPECT_EQ(R.getOutOfBandError(), nullptr); +} + +TEST(WrapperFunctionUtilsTest, WrapperFunctionResultFromCString) { + auto R = WrapperFunctionResult::copyFrom(TestString); + EXPECT_EQ(R.size(), strlen(TestString) + 1); + EXPECT_TRUE(strcmp(R.data(), TestString) == 0); + EXPECT_FALSE(R.empty()); + EXPECT_EQ(R.getOutOfBandError(), nullptr); +} + +TEST(WrapperFunctionUtilsTest, WrapperFunctionResultFromOutOfBandError) { + auto R = WrapperFunctionResult::createOutOfBandError(TestString); + EXPECT_FALSE(R.empty()); + EXPECT_TRUE(strcmp(R.getOutOfBandError(), TestString) == 0); +}