-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[Offload] Generate OffloadTypedGetInfo.inc
#168615
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Various offload APIs `olGet*Info` are essentially untyped because they "return" value via `void *PropValue` output parameter. However, for C++ consumers (e.g., SYCL in llvm#166927) it would be beneficial if we could recover that type information. Before this PR it was only encoded in the comments near corresponding information info descriptors, e.g., ```c++ /////////////////////////////////////////////////////////////////////////////// /// @brief Supported event info. typedef enum ol_event_info_t { /// [ol_queue_handle_t] The handle of the queue associated with the device. OL_EVENT_INFO_QUEUE = 0, /// [bool] True if and only if the event is complete. OL_EVENT_INFO_IS_COMPLETE = 1, /// @cond OL_EVENT_INFO_LAST = 2, OL_EVENT_INFO_FORCE_UINT32 = 0x7fffffff /// @endcond } ol_event_info_t; ``` and not accessible programmatically.
|
@llvm/pr-subscribers-offload Author: Andrei Elovikov (aelovikov-intel) ChangesVarious offload APIs ///////////////////////////////////////////////////////////////////////////////
/// @<!-- -->brief Supported event info.
typedef enum ol_event_info_t {
/// [ol_queue_handle_t] The handle of the queue associated with the device.
OL_EVENT_INFO_QUEUE = 0,
/// [bool] True if and only if the event is complete.
OL_EVENT_INFO_IS_COMPLETE = 1,
/// @<!-- -->cond
OL_EVENT_INFO_LAST = 2,
OL_EVENT_INFO_FORCE_UINT32 = 0x7fffffff
/// @<!-- -->endcond
} ol_event_info_t;and not accessible programmatically. Full diff: https://github.com/llvm/llvm-project/pull/168615.diff 6 Files Affected:
diff --git a/offload/liboffload/API/CMakeLists.txt b/offload/liboffload/API/CMakeLists.txt
index e4baa4772a1ef..daa8f382197df 100644
--- a/offload/liboffload/API/CMakeLists.txt
+++ b/offload/liboffload/API/CMakeLists.txt
@@ -19,6 +19,7 @@ offload_tablegen(OffloadEntryPoints.inc -gen-entry-points)
offload_tablegen(OffloadFuncs.inc -gen-func-names)
offload_tablegen(OffloadImplFuncDecls.inc -gen-impl-func-decls)
offload_tablegen(OffloadPrint.hpp -gen-print-header)
+offload_tablegen(OffloadTypedGetInfo.inc -gen-get-info-wrappers)
add_public_tablegen_target(OffloadGenerate)
diff --git a/offload/test/tools/offload-tblgen/get_info_wrappers.td b/offload/test/tools/offload-tblgen/get_info_wrappers.td
new file mode 100644
index 0000000000000..4483022c94fbc
--- /dev/null
+++ b/offload/test/tools/offload-tblgen/get_info_wrappers.td
@@ -0,0 +1,57 @@
+// RUN: %offload-tblgen -gen-get-info-wrappers -I %S/../../../liboffload/API %s | %fcheck-generic
+
+include "APIDefs.td"
+
+def ol_foo_handle_t : Handle {
+}
+
+def ol_foo_info_t : Enum {
+ let is_typed = 1;
+ let etors = [
+ TaggedEtor<"INT", "int", "">,
+ TaggedEtor<"STRING", "char[]", "">,
+ TaggedEtor<"ARRAY", "int[]", "">,
+ ];
+}
+
+def olGetFooInfo : Function {
+ let params = [
+ Param<"ol_foo_handle_t", "Foo", "", PARAM_IN>,
+ Param<"ol_foo_info_t", "PropName", "", PARAM_IN>,
+ Param<"size_t", "PropSize", "", PARAM_IN>,
+ TypeTaggedParam<"void*", "PropValue", "array of bytes holding the info.", PARAM_OUT,
+ TypeInfo<"PropName", "PropSize">>
+ ];
+ let returns = [
+ Return<"OL_FOO_INVALID">
+ ];
+}
+
+// CHECK-LABEL: template <ol_foo_info_t Desc> inline auto get_info(ol_foo_handle_t Foo);
+// CHECK-NEXT: template<> inline auto get_info<OL_FOO_INFO_INT>(ol_foo_handle_t Foo) {
+// CHECK-NEXT: int Result;
+// CHECK-NEXT: if (auto Err = olGetFooInfo(Foo, OL_FOO_INFO_INT, 1, &Result))
+// CHECK-NEXT: return std::variant<int, ol_result_t>{Err};
+// CHECK-NEXT: else
+// CHECK-NEXT: return std::variant<int, ol_result_t>{Result};
+// CHECK-NEXT: }
+// CHECK-NEXT: template<> inline auto get_info<OL_FOO_INFO_STRING>(ol_foo_handle_t Foo) {
+// CHECK-NEXT: std::string Result;
+// CHECK-NEXT: size_t ResultSize = 0; if (auto Err = olGetFooInfoSize(Foo, OL_FOO_INFO_STRING, &ResultSize))
+// CHECK-NEXT: return std::variant<std::string, ol_result_t>{Err};
+// CHECK-NEXT: Result.resize(ResultSize);
+// CHECK-NEXT: if (auto Err = olGetFooInfo(Foo, OL_FOO_INFO_STRING, ResultSize, Result.data()))
+// CHECK-NEXT: return std::variant<std::string, ol_result_t>{Err};
+// CHECK-NEXT: else
+// CHECK-NEXT: return std::variant<std::string, ol_result_t>{Result};
+// CHECK-NEXT: }
+// CHECK-NEXT: template<> inline auto get_info<OL_FOO_INFO_ARRAY>(ol_foo_handle_t Foo) {
+// CHECK-NEXT: std::vector<int> Result;
+// CHECK-NEXT: size_t ResultSize = 0; if (auto Err = olGetFooInfoSize(Foo, OL_FOO_INFO_ARRAY, &ResultSize))
+// CHECK-NEXT: return std::variant<std::vector<int>, ol_result_t>{Err};
+// CHECK-NEXT: Result.resize(ResultSize);
+// CHECK-NEXT: if (auto Err = olGetFooInfo(Foo, OL_FOO_INFO_ARRAY, ResultSize, Result.data()))
+// CHECK-NEXT: return std::variant<std::vector<int>, ol_result_t>{Err};
+// CHECK-NEXT: else
+// CHECK-NEXT: return std::variant<std::vector<int>, ol_result_t>{Result};
+// CHECK-NEXT: }
diff --git a/offload/tools/offload-tblgen/CMakeLists.txt b/offload/tools/offload-tblgen/CMakeLists.txt
index a5ae1c3757fbf..bc3c4fa5b6ef7 100644
--- a/offload/tools/offload-tblgen/CMakeLists.txt
+++ b/offload/tools/offload-tblgen/CMakeLists.txt
@@ -20,6 +20,7 @@ add_tablegen(offload-tblgen OFFLOAD
offload-tblgen.cpp
PrintGen.cpp
RecordTypes.hpp
+ TypedGetInfoWrappers.cpp
)
# Make sure that C++ headers are available, if libcxx is built at the same
diff --git a/offload/tools/offload-tblgen/Generators.hpp b/offload/tools/offload-tblgen/Generators.hpp
index fda63f8b198e5..84e14ea0c16e9 100644
--- a/offload/tools/offload-tblgen/Generators.hpp
+++ b/offload/tools/offload-tblgen/Generators.hpp
@@ -25,3 +25,5 @@ void EmitOffloadExports(const llvm::RecordKeeper &Records,
void EmitOffloadErrcodes(const llvm::RecordKeeper &Records,
llvm::raw_ostream &OS);
void EmitOffloadInfo(const llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
+void EmitTypedGetInfoWrappers(const llvm::RecordKeeper &Records,
+ llvm::raw_ostream &OS);
diff --git a/offload/tools/offload-tblgen/TypedGetInfoWrappers.cpp b/offload/tools/offload-tblgen/TypedGetInfoWrappers.cpp
new file mode 100644
index 0000000000000..5c1ef15ce3aff
--- /dev/null
+++ b/offload/tools/offload-tblgen/TypedGetInfoWrappers.cpp
@@ -0,0 +1,84 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 is a Tablegen backend that produces typed C++ inline wrappers for
+// various `olGet*Info interfaces.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/TableGen/Record.h"
+#include "llvm/TableGen/TableGenBackend.h"
+
+#include "GenCommon.hpp"
+#include "RecordTypes.hpp"
+
+using namespace llvm;
+using namespace offload::tblgen;
+
+void EmitTypedGetInfoWrappers(const llvm::RecordKeeper &Records,
+ llvm::raw_ostream &OS) {
+ OS << GenericHeader;
+ for (auto *R : Records.getAllDerivedDefinitions("Function")) {
+ auto Name = R->getName();
+ if (!Name.starts_with("olGet") || !Name.ends_with("Info"))
+ continue;
+ auto F = FunctionRec{R};
+ auto Params = F.getParams();
+ assert(Params.size() == 4);
+ auto Object = Params[0];
+ auto InfoDesc = Params[1];
+
+ OS << formatv("template <{} Desc> inline auto get_info({} {});\n",
+ InfoDesc.getType(), Object.getType(), Object.getName());
+
+ EnumRec E{Records.getDef(InfoDesc.getType())};
+ for (auto &V : E.getValues()) {
+ auto Desc = E.getEnumValNamePrefix() + "_" + V.getName();
+ auto TaggedType = V.getTaggedType();
+ auto ResultType = [TaggedType]() -> std::string {
+ if (!TaggedType.ends_with("[]"))
+ return TaggedType.str();
+ if (TaggedType == "char[]")
+ return "std::string";
+
+ return ("std::vector<" + TaggedType.drop_back(2) + ">").str();
+ }();
+ auto ReturnType =
+ "std::variant<" + ResultType + ", " + PrefixLower + "_result_t>";
+ OS << formatv("template<> inline auto get_info<{}>({} {}) {{\n", Desc,
+ Object.getType(), Object.getName());
+ if (TaggedType.ends_with("[]")) {
+ OS << TAB_1 << formatv("{0} Result;\n", ResultType);
+ OS << TAB_1 << "size_t ResultSize = 0;";
+ OS << TAB_1
+ << formatv("if (auto Err = {}Size({}, {}, &ResultSize))\n",
+ F.getName(), Object.getName(), Desc);
+ OS << TAB_2 << formatv("return {}{{Err};\n", ReturnType);
+ OS << TAB_1 << "Result.resize(ResultSize);\n"; // TODO: Or "-1"?
+ OS << TAB_1
+ << formatv("if (auto Err = {}({}, {}, ResultSize, Result.data()))\n",
+ F.getName(), Object.getName(), Desc);
+ OS << TAB_2 << formatv("return {0}{{Err};\n", ReturnType);
+ OS << TAB_1 << "else\n";
+ OS << TAB_2 << formatv("return {0}{{Result};\n", ReturnType);
+ } else {
+ OS << TAB_1 << formatv("{0} Result;\n", TaggedType);
+ OS << TAB_1
+ << formatv("if (auto Err = {}({}, {}, 1, &Result))\n", F.getName(),
+ Object.getName(), Desc);
+ OS << TAB_2 << formatv("return {0}{{Err};\n", ReturnType);
+ OS << TAB_1 << "else\n";
+ OS << TAB_2 << formatv("return {0}{{Result};\n", ReturnType);
+ }
+ OS << "}\n";
+ }
+ OS << "\n";
+ }
+}
diff --git a/offload/tools/offload-tblgen/offload-tblgen.cpp b/offload/tools/offload-tblgen/offload-tblgen.cpp
index 18aaf9e00f08a..3a0f56acdc460 100644
--- a/offload/tools/offload-tblgen/offload-tblgen.cpp
+++ b/offload/tools/offload-tblgen/offload-tblgen.cpp
@@ -34,6 +34,7 @@ enum ActionType {
GenExports,
GenErrcodes,
GenInfo,
+ GenTypedGetInfoWrappers,
};
namespace {
@@ -60,7 +61,10 @@ cl::opt<ActionType> Action(
"Generate export file for the Offload library"),
clEnumValN(GenErrcodes, "gen-errcodes",
"Generate Offload Error Code enum"),
- clEnumValN(GenInfo, "gen-info", "Generate Offload Info enum")));
+ clEnumValN(GenInfo, "gen-info", "Generate Offload Info enum"),
+ clEnumValN(GenTypedGetInfoWrappers, "gen-get-info-wrappers",
+ "Generate typed C++ wrappers around various olGet*Info "
+ "interfaces")));
}
static bool OffloadTableGenMain(raw_ostream &OS, const RecordKeeper &Records) {
@@ -98,6 +102,8 @@ static bool OffloadTableGenMain(raw_ostream &OS, const RecordKeeper &Records) {
case GenInfo:
EmitOffloadInfo(Records, OS);
break;
+ case GenTypedGetInfoWrappers:
+ EmitTypedGetInfoWrappers(Records, OS);
}
return false;
|
|
The intention of the offloading runtime is to be a C API. Are we providing these as additional wrappers? |
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
Yes, additional and only for in-tree C++ consumers, i.e., I'm not adding llvm-project/offload/liboffload/CMakeLists.txt Lines 42 to 49 in 5bba4fd
Expected usage will be something like #166927 (comment) |
|
I've been thinking if there is a smaller/cheaper alternative.
// get_info is provided/tblgen'ed by liboffload:
auto result = get_info<OL_DEVICE_INFO_SMTH>(device_handle);but it mandates the mapping of how C types get translated into C++ type/error handling (e.g., choosing
// Technically, we can outline that helper in the "full" approach inside generated code as well.
// On the consumer side, pseudocode:
namespace detail {
template <auto InfoKind>
auto get_info(auto &InfoGetter, auto &InfoSizeGetter, auto Handle) {
// ol_ret_type is provided/tblgen'ed by liboffload
using ret_ty = typename ol_ret_type<decltype(InfoKind), InfoKind>::type;
// generic implementation using InfoGetter, InfoSizeGetter, ret_ty
}
} // namespace detail
...
auto result = detail::get_info<OL_DEVICE_INFO_SMTH>(olGetDeviceInfo, olGetDeviceInfoSize, device_handle);consumer can choose what types to use and how to handler errors. The actual logic is implemented in a single template instead of being duplicated in multiple template specializations. Minimal amount of tblgen'ed code inside liboffload. Maybe we can even put that into ///////////////////////////////////////////////////////////////////////////////
/// @brief Variant of olWaitEvents that also sets source code location
/// information
/// @details See also ::olWaitEvents
OL_APIEXPORT ol_result_t OL_APICALL
olWaitEventsWithCodeLoc(ol_queue_handle_t Queue, ol_event_handle_t *Events,
size_t NumEvents, ol_code_location_t *CodeLocation);
#if defined(__cplusplus)
} // extern "C"
#endif
#if defined(__cplusplus)
template <typename, auto> struct ol_info_ret_type;
template <> struct ol_info_ret_type<ol_device_info_t, OL_DEVICE_INFO_TYPE> {
using type = ol_device_type_t;
};
template <> struct ol_info_ret_type<ol_device_info_t, OL_DEVICE_INFO_PLATFORM> {
using type = ol_platform_handle_t;
};
...
#endif
template <typename, auto> struct info_traits;
template <> struct info_traits<ol_device_info_t, OL_DEVICE_INFO_SMTH> {
using return_type = <smth>;
// Maybe split these into a separate trait with just typename template param:
static constexpr auto InfoGetter = &olGetDeviceInfo;
static constexpr auto InfoSizeGetter = &olGetDeviceInfoSize;
};@jhuber6 any preference between those three? |
|
Realistically all we need is a way to query the size from the runtime and then just make a template helper that passes a sufficient amount of data and casts it to the requested type. Do we have any runtime calls similar to that? |
Sorry, I don't understand that. The problem is that liboffload might be returning an |
Various offload APIs
olGet*Infoare essentially untyped because they "return" value viavoid *PropValueoutput parameter. However, for C++ consumers (e.g., SYCL in #166927) it would be beneficial if we could recover that type information. Before this PR it was only encoded in the comments near corresponding information info descriptors, e.g.,and not accessible programmatically.