[llvm] [orc-rt] Add C and C++ APIs for WrapperFunctionResult. (PR #154927)
Lang Hames via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 22 04:04:18 PDT 2025
https://github.com/lhames created https://github.com/llvm/llvm-project/pull/154927
orc_rt_WrapperFunctionResult is a byte-buffer with inline storage and a builtin error state. It is intended as a general purpose return type for functions that return a serialized result (e.g. for communication across ABIs or via IPC/RPC).
orc_rt_WrapperFunctionResult contains a small amount of inline storage, allowing it to avoid heap-allocation for small return types (e.g. bools, chars, pointers).
>From a7b115dc214f72007e3638e80d7d55d73fb101b9 Mon Sep 17 00:00:00 2001
From: Lang Hames <lhames at gmail.com>
Date: Fri, 22 Aug 2025 15:29:09 +1000
Subject: [PATCH] [orc-rt] Add C and C++ APIs for WrapperFunctionResult.
orc_rt_WrapperFunctionResult is a byte-buffer with inline storage and a builtin
error state. It is intended as a general purpose return type for functions that
return a serialized result (e.g. for communication across ABIs or via IPC/RPC).
orc_rt_WrapperFunctionResult contains a small amount of inline storage, allowing
it to avoid heap-allocation for small return types (e.g. bools, chars,
pointers).
---
orc-rt/include/CMakeLists.txt | 3 +
orc-rt/include/orc-rt-c/ExternC.h | 41 ++++
.../include/orc-rt-c/WrapperFunctionResult.h | 178 ++++++++++++++++++
orc-rt/include/orc-rt/WrapperFunctionResult.h | 106 +++++++++++
orc-rt/unittests/CMakeLists.txt | 1 +
.../unittests/WrapperFunctionResultTest.cpp | 60 ++++++
6 files changed, 389 insertions(+)
create mode 100644 orc-rt/include/orc-rt-c/ExternC.h
create mode 100644 orc-rt/include/orc-rt-c/WrapperFunctionResult.h
create mode 100644 orc-rt/include/orc-rt/WrapperFunctionResult.h
create mode 100644 orc-rt/unittests/WrapperFunctionResultTest.cpp
diff --git a/orc-rt/include/CMakeLists.txt b/orc-rt/include/CMakeLists.txt
index ed90a7a45e2fa..e58a70f74dd6c 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 0000000000000..dd839037adf1e
--- /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 0000000000000..d8dec28680f1b
--- /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 <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+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 0000000000000..d3bc132070e86
--- /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 <utility>
+
+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 6a1f5d6f8bce2..2abbdd7bc634b 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 0000000000000..9ecc513ba50fa
--- /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);
+}
More information about the llvm-commits
mailing list