[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