[compiler-rt] 442f2d7 - [ORC-RT] Add OrcRTCWrapperFunctionResult.

Lang Hames via llvm-commits llvm-commits at lists.llvm.org
Sun May 30 11:44:48 PDT 2021


Author: Lang Hames
Date: 2021-05-30T11:20:57-07:00
New Revision: 442f2d7bc0bc4f19ce056018231fbed9166c9b08

URL: https://github.com/llvm/llvm-project/commit/442f2d7bc0bc4f19ce056018231fbed9166c9b08
DIFF: https://github.com/llvm/llvm-project/commit/442f2d7bc0bc4f19ce056018231fbed9166c9b08.diff

LOG: [ORC-RT] Add OrcRTCWrapperFunctionResult.

OrcRTCWrapperFunctionResult is a C struct that can be used to return serialized
results from "wrapper functions" -- functions that deserialize an argument
buffer, call through to an actual implementation function, then serialize and
return the result of that function. Wrapper functions allow calls between ORC
and the ORC Runtime to be written using a single signature,
WrapperFunctionResult(const char *ArgData, size_t ArgSize), and without coupling
either side to a particular transport mechanism (in-memory, TCP, IPC, ... the
actual mechanism will be determined by the TargetProcessControl implementation).

OrcRTCWrapperFunctionResult is designed to allow small serialized buffers to
be returned by value, with larger serialized results stored on the heap. They
also provide an error state to report failures in serialization/deserialization.

Added: 
    compiler-rt/lib/orc/c_api.h
    compiler-rt/lib/orc/unittests/c_api_test.cpp

Modified: 
    compiler-rt/lib/orc/CMakeLists.txt
    compiler-rt/lib/orc/unittests/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/compiler-rt/lib/orc/CMakeLists.txt b/compiler-rt/lib/orc/CMakeLists.txt
index 37ca504b92c17..fb45f28f40768 100644
--- a/compiler-rt/lib/orc/CMakeLists.txt
+++ b/compiler-rt/lib/orc/CMakeLists.txt
@@ -13,6 +13,7 @@ set(x86_64_SOURCES
 set(ORC_IMPL_HEADERS
 # Implementation headers will go here.
   adt.h
+  c_api.h
   compiler.h
   endian.h
   error.h

diff  --git a/compiler-rt/lib/orc/c_api.h b/compiler-rt/lib/orc/c_api.h
new file mode 100644
index 0000000000000..ac8d3442e7bb7
--- /dev/null
+++ b/compiler-rt/lib/orc/c_api.h
@@ -0,0 +1,207 @@
+/*===- c_api.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                            *|
+|*                                                                            *|
+|* NOTE: The OrtRTWrapperFunctionResult type must be kept in sync with the    *|
+|* definition in llvm/include/llvm-c/OrcShared.h.                             *|
+|*                                                                            *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef ORC_RT_C_API_H
+#define ORC_RT_C_API_H
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.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
+
+ORC_RT_C_EXTERN_C_BEGIN
+
+typedef union {
+  const char *ValuePtr;
+  char Value[sizeof(ValuePtr)];
+} OrcRTCWrapperFunctionResultDataUnion;
+
+/**
+ * OrcRTCWrapperFunctionResult 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(OrcRTCWrapperFunctionResultData) then the value is in the
+ * 'small' state and the content is held in the first Size bytes of Data.Value.
+ *
+ * If Size > sizeof(OrtRTCWrapperFunctionResultData) 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 {
+  OrcRTCWrapperFunctionResultDataUnion Data;
+  size_t Size;
+} OrcRTCWrapperFunctionResult;
+
+typedef struct OrcRTCSharedOpaqueJITProcessControl
+    *OrcRTSharedJITProcessControlRef;
+
+/**
+ * Zero-initialize an OrcRTCWrapperFunctionResult.
+ */
+static inline void
+OrcRTCWrapperFunctionResultInit(OrcRTCWrapperFunctionResult *R) {
+  R->Size = 0;
+  R->Data.ValuePtr = 0;
+}
+
+/**
+ * Create an OrcRTCWrapperFunctionResult with an uninitialized buffer of size
+ * Size. The buffer is returned via the DataPtr argument.
+ */
+static inline char *
+OrcRTCWrapperFunctionResultAllocate(OrcRTCWrapperFunctionResult *R,
+                                    size_t Size) {
+  char *DataPtr;
+  R->Size = Size;
+  if (Size > sizeof(R->Data.Value)) {
+    DataPtr = (char *)malloc(Size);
+    R->Data.ValuePtr = DataPtr;
+  } else
+    DataPtr = R->Data.Value;
+  return DataPtr;
+}
+
+/**
+ * Create an OrcRTWrapperFunctionResult from the given data range.
+ */
+static inline OrcRTCWrapperFunctionResult
+OrcRTCreateCWrapperFunctionResultFromRange(const char *Data, size_t Size) {
+  OrcRTCWrapperFunctionResult 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 OrcRTCWrapperFunctionResult 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 OrcRTCWrapperFunctionResult
+OrcRTCreateCWrapperFunctionResultFromString(const char *Source) {
+  return OrcRTCreateCWrapperFunctionResultFromRange(Source, strlen(Source) + 1);
+}
+
+/**
+ * Create an OrcRTCWrapperFunctionResult representing an out-of-band
+ * error.
+ *
+ * This function takes ownership of the string argument which must have been
+ * allocated with malloc.
+ */
+static inline OrcRTCWrapperFunctionResult
+OrcRTCreateCWrapperFunctionResultFromOutOfBandError(const char *ErrMsg) {
+  OrcRTCWrapperFunctionResult 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 OrcRTCWrapperFunctionResult values
+ * regardless of their state.
+ */
+static inline void
+OrcRTDisposeCWrapperFunctionResult(OrcRTCWrapperFunctionResult *R) {
+  if (R->Size > sizeof(R->Data.Value) ||
+      (R->Size == 0 && R->Data.ValuePtr))
+    free((void *)R->Data.ValuePtr);
+}
+
+/**
+ * Get a pointer to the data contained in the given
+ * OrcRTCWrapperFunctionResult.
+ */
+static inline const char *
+OrcRTCWrapperFunctionResultData(const OrcRTCWrapperFunctionResult *R) {
+  assert((R->Size != 0 || R->Data.ValuePtr == nullptr) &&
+         "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 OrcRTCWrapperFunctionResult.
+ *
+ * Asserts that we're not trying to access the size of an error value.
+ */
+static inline size_t
+OrcRTCWrapperFunctionResultSize(const OrcRTCWrapperFunctionResult *R) {
+  assert((R->Size != 0 || R->Data.ValuePtr == nullptr) &&
+         "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
+ * OrcRTCWrapperFunctionResultInit, 0 otherwise.
+ */
+static inline size_t
+OrcRTCWrapperFunctionResultEmpty(const OrcRTCWrapperFunctionResult *R) {
+  return R->Size == 0 && R->Data.ValuePtr == 0;
+}
+
+/**
+ * Returns a pointer to the out-of-band error string for this
+ * OrcRTCWrapperFunctionResult, or null if there is no error.
+ *
+ * The OrcRTCWrapperFunctionResult retains ownership of the error
+ * string, so it should be copied if the caller wishes to preserve it.
+ */
+static inline const char *OrcRTCWrapperFunctionResultGetOutOfBandError(
+    const OrcRTCWrapperFunctionResult *R) {
+  return R->Size == 0 ? R->Data.ValuePtr : 0;
+}
+
+ORC_RT_C_EXTERN_C_END
+
+#endif /* ORC_RT_C_API_H */

diff  --git a/compiler-rt/lib/orc/unittests/CMakeLists.txt b/compiler-rt/lib/orc/unittests/CMakeLists.txt
index 6ed0f309d68b1..b784f22b37e88 100644
--- a/compiler-rt/lib/orc/unittests/CMakeLists.txt
+++ b/compiler-rt/lib/orc/unittests/CMakeLists.txt
@@ -81,6 +81,7 @@ endmacro()
 
 set(UNITTEST_SOURCES
   adt_test.cpp
+  c_api_test.cpp
   endian_test.cpp
   error_test.cpp
   extensible_rtti_test.cpp

diff  --git a/compiler-rt/lib/orc/unittests/c_api_test.cpp b/compiler-rt/lib/orc/unittests/c_api_test.cpp
new file mode 100644
index 0000000000000..14009818218c5
--- /dev/null
+++ b/compiler-rt/lib/orc/unittests/c_api_test.cpp
@@ -0,0 +1,200 @@
+//===-- c_api_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 "c_api.h"
+#include "gtest/gtest.h"
+
+TEST(CAPITest, CWrapperFunctionResultInit) {
+  OrcRTCWrapperFunctionResult R;
+  OrcRTCWrapperFunctionResultInit(&R);
+
+  EXPECT_EQ(R.Size, 0U);
+  EXPECT_EQ(R.Data.ValuePtr, nullptr);
+
+  // Check that this value isn't treated as an out-of-band error.
+  EXPECT_EQ(OrcRTCWrapperFunctionResultGetOutOfBandError(&R), nullptr);
+
+  // Check that we can dispose of the value.
+  OrcRTDisposeCWrapperFunctionResult(&R);
+}
+
+TEST(CAPITest, CWrapperFunctionResultAllocSmall) {
+  constexpr size_t SmallAllocSize = sizeof(const char *);
+
+  OrcRTCWrapperFunctionResult R;
+  char *DataPtr = OrcRTCWrapperFunctionResultAllocate(&R, SmallAllocSize);
+
+  for (size_t I = 0; I != SmallAllocSize; ++I)
+    DataPtr[I] = 0x55 + I;
+
+  // Check that the inline storage in R.Data.Value contains the expected
+  // sequence.
+  EXPECT_EQ(R.Size, SmallAllocSize);
+  for (size_t I = 0; I != SmallAllocSize; ++I)
+    EXPECT_EQ(R.Data.Value[I], (char)(0x55 + I))
+        << "Unexpected value at index " << I;
+
+  // Check that this value isn't treated as an out-of-band error.
+  EXPECT_EQ(OrcRTCWrapperFunctionResultGetOutOfBandError(&R), nullptr);
+
+  // Check that OrcRTCWrapperFunctionResult(Data|Result|Size) and
+  // OrcRTCWrapperFunctionResultGetOutOfBandError behave as expected.
+  EXPECT_EQ(OrcRTCWrapperFunctionResultData(&R), R.Data.Value);
+  EXPECT_EQ(OrcRTCWrapperFunctionResultSize(&R), SmallAllocSize);
+  EXPECT_FALSE(OrcRTCWrapperFunctionResultEmpty(&R));
+  EXPECT_EQ(OrcRTCWrapperFunctionResultGetOutOfBandError(&R), nullptr);
+
+  // Check that we can dispose of the value.
+  OrcRTDisposeCWrapperFunctionResult(&R);
+}
+
+TEST(CAPITest, CWrapperFunctionResultAllocLarge) {
+  constexpr size_t LargeAllocSize = sizeof(const char *) + 1;
+
+  OrcRTCWrapperFunctionResult R;
+  char *DataPtr = OrcRTCWrapperFunctionResultAllocate(&R, LargeAllocSize);
+
+  for (size_t I = 0; I != LargeAllocSize; ++I)
+    DataPtr[I] = 0x55 + I;
+
+  // Check that the inline storage in R.Data.Value contains the expected
+  // sequence.
+  EXPECT_EQ(R.Size, LargeAllocSize);
+  EXPECT_EQ(R.Data.ValuePtr, DataPtr);
+  for (size_t I = 0; I != LargeAllocSize; ++I)
+    EXPECT_EQ(R.Data.ValuePtr[I], (char)(0x55 + I))
+        << "Unexpected value at index " << I;
+
+  // Check that this value isn't treated as an out-of-band error.
+  EXPECT_EQ(OrcRTCWrapperFunctionResultGetOutOfBandError(&R), nullptr);
+
+  // Check that OrcRTCWrapperFunctionResult(Data|Result|Size) and
+  // OrcRTCWrapperFunctionResultGetOutOfBandError behave as expected.
+  EXPECT_EQ(OrcRTCWrapperFunctionResultData(&R), R.Data.ValuePtr);
+  EXPECT_EQ(OrcRTCWrapperFunctionResultSize(&R), LargeAllocSize);
+  EXPECT_FALSE(OrcRTCWrapperFunctionResultEmpty(&R));
+  EXPECT_EQ(OrcRTCWrapperFunctionResultGetOutOfBandError(&R), nullptr);
+
+  // Check that we can dispose of the value.
+  OrcRTDisposeCWrapperFunctionResult(&R);
+}
+
+TEST(CAPITest, CWrapperFunctionResultFromRangeSmall) {
+  constexpr size_t SmallAllocSize = sizeof(const char *);
+
+  char Source[SmallAllocSize];
+  for (size_t I = 0; I != SmallAllocSize; ++I)
+    Source[I] = 0x55 + I;
+
+  OrcRTCWrapperFunctionResult R =
+      OrcRTCreateCWrapperFunctionResultFromRange(Source, SmallAllocSize);
+
+  // Check that the inline storage in R.Data.Value contains the expected
+  // sequence.
+  EXPECT_EQ(R.Size, SmallAllocSize);
+  for (size_t I = 0; I != SmallAllocSize; ++I)
+    EXPECT_EQ(R.Data.Value[I], (char)(0x55 + I))
+        << "Unexpected value at index " << I;
+
+  // Check that we can dispose of the value.
+  OrcRTDisposeCWrapperFunctionResult(&R);
+}
+
+TEST(CAPITest, CWrapperFunctionResultFromRangeLarge) {
+  constexpr size_t LargeAllocSize = sizeof(const char *) + 1;
+
+  char Source[LargeAllocSize];
+  for (size_t I = 0; I != LargeAllocSize; ++I)
+    Source[I] = 0x55 + I;
+
+  OrcRTCWrapperFunctionResult R =
+      OrcRTCreateCWrapperFunctionResultFromRange(Source, LargeAllocSize);
+
+  // Check that the inline storage in R.Data.Value contains the expected
+  // sequence.
+  EXPECT_EQ(R.Size, LargeAllocSize);
+  for (size_t I = 0; I != LargeAllocSize; ++I)
+    EXPECT_EQ(R.Data.ValuePtr[I], (char)(0x55 + I))
+        << "Unexpected value at index " << I;
+
+  // Check that we can dispose of the value.
+  OrcRTDisposeCWrapperFunctionResult(&R);
+}
+
+TEST(CAPITest, CWrapperFunctionResultFromStringSmall) {
+  constexpr size_t SmallAllocSize = sizeof(const char *);
+
+  char Source[SmallAllocSize];
+  for (size_t I = 0; I != SmallAllocSize - 1; ++I)
+    Source[I] = 'a' + I;
+  Source[SmallAllocSize - 1] = '\0';
+
+  OrcRTCWrapperFunctionResult R =
+      OrcRTCreateCWrapperFunctionResultFromString(Source);
+
+  // Check that the inline storage in R.Data.Value contains the expected
+  // sequence.
+  EXPECT_EQ(R.Size, SmallAllocSize);
+  for (size_t I = 0; I != SmallAllocSize - 1; ++I)
+    EXPECT_EQ(R.Data.Value[I], (char)('a' + I))
+        << "Unexpected value at index " << I;
+  EXPECT_EQ(R.Data.Value[SmallAllocSize - 1], '\0')
+      << "Unexpected value at index " << (SmallAllocSize - 1);
+
+  // Check that we can dispose of the value.
+  OrcRTDisposeCWrapperFunctionResult(&R);
+}
+
+TEST(CAPITest, CWrapperFunctionResultFromStringLarge) {
+  constexpr size_t LargeAllocSize = sizeof(const char *) + 1;
+
+  char Source[LargeAllocSize];
+  for (size_t I = 0; I != LargeAllocSize - 1; ++I)
+    Source[I] = 'a' + I;
+  Source[LargeAllocSize - 1] = '\0';
+
+  OrcRTCWrapperFunctionResult R =
+      OrcRTCreateCWrapperFunctionResultFromString(Source);
+
+  // Check that the inline storage in R.Data.Value contains the expected
+  // sequence.
+  EXPECT_EQ(R.Size, LargeAllocSize);
+  for (size_t I = 0; I != LargeAllocSize - 1; ++I)
+    EXPECT_EQ(R.Data.ValuePtr[I], (char)('a' + I))
+        << "Unexpected value at index " << I;
+  EXPECT_EQ(R.Data.ValuePtr[LargeAllocSize - 1], '\0')
+      << "Unexpected value at index " << (LargeAllocSize - 1);
+
+  // Check that we can dispose of the value.
+  OrcRTDisposeCWrapperFunctionResult(&R);
+}
+
+TEST(CAPITest, CWrapperFunctionResultFromOutOfBandError) {
+  constexpr const char *ErrMsg = "test error message";
+  OrcRTCWrapperFunctionResult R =
+      OrcRTCreateCWrapperFunctionResultFromOutOfBandError(ErrMsg);
+
+#ifndef NDEBUG
+  EXPECT_DEATH({ OrcRTCWrapperFunctionResultData(&R); },
+               "Cannot get data for out-of-band error value");
+  EXPECT_DEATH({ OrcRTCWrapperFunctionResultSize(&R); },
+               "Cannot get size for out-of-band error value");
+#endif
+
+  EXPECT_FALSE(OrcRTCWrapperFunctionResultEmpty(&R));
+  const char *OOBErrMsg = OrcRTCWrapperFunctionResultGetOutOfBandError(&R);
+  EXPECT_NE(OOBErrMsg, nullptr);
+  EXPECT_NE(OOBErrMsg, ErrMsg);
+  EXPECT_TRUE(strcmp(OOBErrMsg, ErrMsg) == 0);
+
+  OrcRTDisposeCWrapperFunctionResult(&R);
+}


        


More information about the llvm-commits mailing list