[llvm] [Offload] Add Offload API Sphinx documentation (PR #147323)

Kenneth Benzie via llvm-commits llvm-commits at lists.llvm.org
Wed Jul 9 06:24:17 PDT 2025


https://github.com/kbenzie updated https://github.com/llvm/llvm-project/pull/147323

>From 6eb509b85f0c67a1b9d2c92681f14ceda0b2a61e Mon Sep 17 00:00:00 2001
From: "Kenneth Benzie (Benie)" <k.benzie83 at gmail.com>
Date: Mon, 7 Jul 2025 16:13:30 +0100
Subject: [PATCH 1/2] [Offload] Add spec generation to offload-tblgen tool

This patch adds generation of Sphinx compatible reStructuedText
utilizing the C domain to document the Offload API directly from the
spec definition `.td` files.
---
 offload/tools/offload-tblgen/APIGen.cpp       |   2 +-
 offload/tools/offload-tblgen/CMakeLists.txt   |   1 +
 offload/tools/offload-tblgen/GenCommon.hpp    |   5 +
 offload/tools/offload-tblgen/Generators.hpp   |   1 +
 offload/tools/offload-tblgen/SpecGen.cpp      | 192 ++++++++++++++++++
 .../tools/offload-tblgen/offload-tblgen.cpp   |   6 +
 6 files changed, 206 insertions(+), 1 deletion(-)
 create mode 100644 offload/tools/offload-tblgen/SpecGen.cpp

diff --git a/offload/tools/offload-tblgen/APIGen.cpp b/offload/tools/offload-tblgen/APIGen.cpp
index c52642592e934..8c61d1f12de7a 100644
--- a/offload/tools/offload-tblgen/APIGen.cpp
+++ b/offload/tools/offload-tblgen/APIGen.cpp
@@ -48,7 +48,7 @@ static void ProcessHandle(const HandleRec &H, raw_ostream &OS) {
     exit(1);
   }
 
-  auto ImplName = H.getName().substr(0, H.getName().size() - 9) + "_impl_t";
+  auto ImplName = getHandleImplName(H);
   OS << CommentsHeader;
   OS << formatv("/// @brief {0}\n", H.getDesc());
   OS << formatv("typedef struct {0} *{1};\n", ImplName, H.getName());
diff --git a/offload/tools/offload-tblgen/CMakeLists.txt b/offload/tools/offload-tblgen/CMakeLists.txt
index 613b166d62b4d..01ef87941e733 100644
--- a/offload/tools/offload-tblgen/CMakeLists.txt
+++ b/offload/tools/offload-tblgen/CMakeLists.txt
@@ -12,6 +12,7 @@ set(LLVM_LINK_COMPONENTS Support)
 add_tablegen(offload-tblgen OFFLOAD
   EXPORT OFFLOAD
   APIGen.cpp
+  SpecGen.cpp
   EntryPointGen.cpp
   MiscGen.cpp
   GenCommon.hpp
diff --git a/offload/tools/offload-tblgen/GenCommon.hpp b/offload/tools/offload-tblgen/GenCommon.hpp
index db432e9958b5d..b57f96ad0c456 100644
--- a/offload/tools/offload-tblgen/GenCommon.hpp
+++ b/offload/tools/offload-tblgen/GenCommon.hpp
@@ -65,3 +65,8 @@ MakeParamComment(const llvm::offload::tblgen::ParamRec &Param) {
                        (Param.isOut() ? "[out]" : ""),
                        (Param.isOpt() ? "[optional]" : ""), Param.getDesc());
 }
+
+inline std::string
+getHandleImplName(const llvm::offload::tblgen::HandleRec &H) {
+  return (H.getName().substr(0, H.getName().size() - 9) + "_impl_t").str();
+}
diff --git a/offload/tools/offload-tblgen/Generators.hpp b/offload/tools/offload-tblgen/Generators.hpp
index 3f94fc47c6b21..5caeb5e734eb2 100644
--- a/offload/tools/offload-tblgen/Generators.hpp
+++ b/offload/tools/offload-tblgen/Generators.hpp
@@ -11,6 +11,7 @@
 #include "llvm/TableGen/Record.h"
 
 void EmitOffloadAPI(const llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
+void EmitOffloadSpec(const llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
 void EmitOffloadFuncNames(const llvm::RecordKeeper &Records,
                           llvm::raw_ostream &OS);
 void EmitOffloadImplFuncDecls(const llvm::RecordKeeper &Records,
diff --git a/offload/tools/offload-tblgen/SpecGen.cpp b/offload/tools/offload-tblgen/SpecGen.cpp
new file mode 100644
index 0000000000000..fe13cf8abe8e7
--- /dev/null
+++ b/offload/tools/offload-tblgen/SpecGen.cpp
@@ -0,0 +1,192 @@
+//===- offload-tblgen/APIGen.cpp - Tablegen backend for Offload header ----===//
+//
+// 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 the contents of the Offload API
+// specification. The generated reStructureText is Sphinx compatible, see
+// https://www.sphinx-doc.org/en/master/usage/domains/c.html for further
+// details on the C language domain.
+//
+//===----------------------------------------------------------------------===//
+
+#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;
+
+namespace {
+std::string makeFunctionSignature(StringRef RetTy, StringRef Name,
+                                  ArrayRef<ParamRec> Params) {
+  std::string S;
+  raw_string_ostream OS{S};
+  OS << RetTy << " " << Name << "(";
+  for (const auto &Param : Params) {
+    OS << Param.getType() << " " << Param.getName();
+    if (Param != Params.back()) {
+      OS << ", ";
+    }
+  }
+  OS << ")";
+  return S;
+}
+
+std::string makeDoubleBackticks(StringRef R) {
+  std::string S;
+  for (char C : R) {
+    if (C == '`') {
+      S.push_back('`');
+    }
+    S.push_back(C);
+  }
+  return S;
+}
+
+void processMacro(const MacroRec &M, raw_ostream &OS) {
+  OS << formatv(".. c:macro:: {0}\n\n", M.getNameWithArgs());
+  OS << "  " << M.getDesc() << "\n\n";
+}
+
+void processTypedef(const TypedefRec &T, raw_ostream &OS) {
+  OS << formatv(".. c:type:: {0} {1}\n\n", T.getValue(), T.getName());
+  OS << "  " << T.getDesc() << "\n\n";
+}
+
+void processHandle(const HandleRec &H, raw_ostream &OS) {
+
+  OS << formatv(".. c:type:: struct {0} *{1}\n\n", getHandleImplName(H),
+                H.getName());
+  OS << "  " << H.getDesc() << "\n\n";
+}
+
+void processFptrTypedef(const FptrTypedefRec &F, raw_ostream &OS) {
+  OS << ".. c:type:: "
+     << makeFunctionSignature(F.getReturn(),
+                              StringRef{formatv("(*{0})", F.getName())},
+                              F.getParams())
+     << "\n\n";
+  for (auto &P : F.getParams()) {
+    OS << formatv("  :param {0}: {1}\n", P.getName(), P.getDesc());
+  }
+  OS << "\n";
+}
+
+void processEnum(const EnumRec &E, raw_ostream &OS) {
+  OS << formatv(".. c:enum:: {0}\n\n", E.getName());
+  OS << "  " << E.getDesc() << "\n\n";
+  for (auto Etor : E.getValues()) {
+    OS << formatv("  .. c:enumerator:: {0}_{1}\n\n", E.getEnumValNamePrefix(),
+                  Etor.getName());
+    OS << "    " << Etor.getDesc() << "\n\n";
+  }
+}
+
+void processStruct(const StructRec &S, raw_ostream &OS) {
+  OS << formatv(".. c:struct:: {0}\n\n", S.getName());
+  OS << "  " << S.getDesc() << "\n\n";
+  for (auto &M : S.getMembers()) {
+    OS << formatv("  .. c:member:: {0} {1}\n\n", M.getType(), M.getName());
+    OS << "    " << M.getDesc() << "\n\n";
+  }
+}
+
+void processFunction(const FunctionRec &F, raw_ostream &OS) {
+  OS << ".. c:function:: "
+     << makeFunctionSignature({formatv("{0}_result_t", PrefixLower)},
+                              F.getName(), F.getParams())
+     << "\n\n";
+
+  OS << "  " << F.getDesc() << "\n\n";
+  for (auto D : F.getDetails()) {
+    OS << "  " << D << "\n";
+  }
+  if (!F.getDetails().empty()) {
+    OS << "\n";
+  }
+
+  for (auto &P : F.getParams()) {
+    OS << formatv("  :param {0}: {1}\n", P.getName(), P.getDesc());
+  }
+
+  for (auto &R : F.getReturns()) {
+    OS << formatv("  :retval {0}:\n", R.getValue());
+    for (auto &C : R.getConditions()) {
+      OS << "    * ";
+      if (C.starts_with("`") && C.ends_with("`")) {
+        OS << ":c:expr:" << C;
+      } else {
+        OS << makeDoubleBackticks(C);
+      }
+      OS << "\n";
+    }
+  }
+  OS << "\n";
+}
+} // namespace
+
+void EmitOffloadSpec(const RecordKeeper &Records, raw_ostream &OS) {
+  OS << "Offload API\n";
+  OS << "===========\n\n";
+
+  auto Macros = Records.getAllDerivedDefinitions("Macro");
+  if (!Macros.empty()) {
+    OS << "Macros\n";
+    OS << "------\n\n";
+    for (auto *M : Macros) {
+      processMacro(MacroRec{M}, OS);
+    }
+  }
+
+  auto Handles = Records.getAllDerivedDefinitions("Handle");
+  auto Typedefs = Records.getAllDerivedDefinitions("Typedef");
+  auto FptrTypedefs = Records.getAllDerivedDefinitions("FptrTypedef");
+  if (!Handles.empty() || !Typedefs.empty() || !FptrTypedefs.empty()) {
+    OS << "Type Definitions\n";
+    OS << "----------------\n\n";
+    for (auto *H : Handles) {
+      processHandle(HandleRec{H}, OS);
+    }
+    for (auto *T : Typedefs) {
+      processTypedef(TypedefRec{T}, OS);
+    }
+    for (auto *F : FptrTypedefs) {
+      processFptrTypedef(FptrTypedefRec{F}, OS);
+    }
+  }
+
+  auto Enums = Records.getAllDerivedDefinitions("Enum");
+  OS << "Enums\n";
+  OS << "-----\n\n";
+  if (!Enums.empty()) {
+    for (auto *E : Enums) {
+      processEnum(EnumRec{E}, OS);
+    }
+  }
+
+  auto Structs = Records.getAllDerivedDefinitions("Struct");
+  if (!Structs.empty()) {
+    OS << "Structs\n";
+    OS << "-------\n\n";
+    for (auto *S : Structs) {
+      processStruct(StructRec{S}, OS);
+    }
+  }
+
+  auto Functions = Records.getAllDerivedDefinitions("Function");
+  if (!Functions.empty()) {
+    OS << "Functions\n";
+    OS << "---------\n\n";
+    for (auto *F : Functions) {
+      processFunction(FunctionRec{F}, OS);
+    }
+  }
+}
diff --git a/offload/tools/offload-tblgen/offload-tblgen.cpp b/offload/tools/offload-tblgen/offload-tblgen.cpp
index ee7f6d4d79bff..b2320d9dbc7f0 100644
--- a/offload/tools/offload-tblgen/offload-tblgen.cpp
+++ b/offload/tools/offload-tblgen/offload-tblgen.cpp
@@ -26,6 +26,7 @@ enum ActionType {
   PrintRecords,
   DumpJSON,
   GenAPI,
+  GenSpec,
   GenFuncNames,
   GenImplFuncDecls,
   GenEntryPoints,
@@ -44,6 +45,8 @@ cl::opt<ActionType> Action(
         clEnumValN(DumpJSON, "dump-json",
                    "Dump all records as machine-readable JSON"),
         clEnumValN(GenAPI, "gen-api", "Generate Offload API header contents"),
+        clEnumValN(GenSpec, "gen-spec",
+                   "Generate Offload API specification contents"),
         clEnumValN(GenFuncNames, "gen-func-names",
                    "Generate a list of all Offload API function names"),
         clEnumValN(
@@ -71,6 +74,9 @@ static bool OffloadTableGenMain(raw_ostream &OS, const RecordKeeper &Records) {
   case GenAPI:
     EmitOffloadAPI(Records, OS);
     break;
+  case GenSpec:
+    EmitOffloadSpec(Records, OS);
+    break;
   case GenFuncNames:
     EmitOffloadFuncNames(Records, OS);
     break;

>From c185ab53b916b6ffc1913ebf73b793e9a6be96af Mon Sep 17 00:00:00 2001
From: "Kenneth Benzie (Benie)" <k.benzie83 at gmail.com>
Date: Mon, 7 Jul 2025 16:16:37 +0100
Subject: [PATCH 2/2] [Offload] Add Sphinx HTML documentation target

Introduces the `docs-offload-html` target when CMake is configured with
`LLVM_ENABLE_SPHINX=ON` and `SPHINX_OUTPUT_HTML=ON`. Utilized
`offload-tblgen -gen-spen` to generate Offload API specification docs.
---
 offload/CMakeLists.txt           |  3 +++
 offload/docs/.gitignore          |  3 +++
 offload/docs/CMakeLists.txt      | 37 ++++++++++++++++++++++++++++++++
 offload/docs/conf.py             | 32 +++++++++++++++++++++++++++
 offload/docs/index.rst           | 21 ++++++++++++++++++
 offload/liboffload/API/Common.td | 15 -------------
 6 files changed, 96 insertions(+), 15 deletions(-)
 create mode 100644 offload/docs/.gitignore
 create mode 100644 offload/docs/CMakeLists.txt
 create mode 100644 offload/docs/conf.py
 create mode 100644 offload/docs/index.rst

diff --git a/offload/CMakeLists.txt b/offload/CMakeLists.txt
index d49069f6eb420..38fa77e41bb53 100644
--- a/offload/CMakeLists.txt
+++ b/offload/CMakeLists.txt
@@ -23,6 +23,8 @@ elseif(NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
   return()
 endif()
 
+set(OFFLOAD_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
+
 if(OPENMP_STANDALONE_BUILD)
   set(OFFLOAD_LIBDIR_SUFFIX "" CACHE STRING
     "Suffix of lib installation directory, e.g. 64 => lib64")
@@ -371,6 +373,7 @@ add_subdirectory(tools/offload-tblgen)
 add_subdirectory(plugins-nextgen)
 add_subdirectory(DeviceRTL)
 add_subdirectory(tools)
+add_subdirectory(docs)
 
 # Build target agnostic offloading library.
 add_subdirectory(libomptarget)
diff --git a/offload/docs/.gitignore b/offload/docs/.gitignore
new file mode 100644
index 0000000000000..5b7d6815f8188
--- /dev/null
+++ b/offload/docs/.gitignore
@@ -0,0 +1,3 @@
+_static/
+_themes/
+offload-api.rst
diff --git a/offload/docs/CMakeLists.txt b/offload/docs/CMakeLists.txt
new file mode 100644
index 0000000000000..687712b7dfa5f
--- /dev/null
+++ b/offload/docs/CMakeLists.txt
@@ -0,0 +1,37 @@
+if(LLVM_ENABLE_SPHINX)
+  include(AddSphinxTarget)
+  if(SPHINX_FOUND AND SPHINX_OUTPUT_HTML)
+    # Generate offload-api.rst from OffloadAPI.td
+    set(LLVM_TARGET_DEFINITIONS
+      ${OFFLOAD_SOURCE_DIR}/liboffload/API/OffloadAPI.td)
+    tablegen(OFFLOAD source/offload-api.rst -gen-spec
+      EXTRA_INCLUDES ${OFFLOAD_SOURCE_DIR}/liboffload/API)
+    add_public_tablegen_target(OffloadDocsGenerate)
+
+    # Due to Sphinx only allowing a single source direcotry and the fact we
+    # only generate a single file, copy offload-api.rst to the source directory
+    # to be included in the generated documentation.
+    # Additionally, copy the llvm-theme into the Sphinx source directory.
+    # A .gitignore file ensures the copied files will not be added to the
+    # repository.
+    add_custom_target(OffloadDocsCopy
+      COMMAND ${CMAKE_COMMAND} -E copy
+        ${CMAKE_CURRENT_BINARY_DIR}/source/offload-api.rst
+        ${CMAKE_CURRENT_SOURCE_DIR}/offload-api.rst
+      COMMAND ${CMAKE_COMMAND} -E copy
+        ${OFFLOAD_SOURCE_DIR}/../clang/www/favicon.ico
+        ${CMAKE_CURRENT_SOURCE_DIR}/_static/favicon.ico
+      COMMAND ${CMAKE_COMMAND} -E copy
+        ${OFFLOAD_SOURCE_DIR}/../llvm/docs/_static/llvm.css
+        ${CMAKE_CURRENT_SOURCE_DIR}/_static/llvm.css
+      COMMAND ${CMAKE_COMMAND} -E copy_directory
+        ${OFFLOAD_SOURCE_DIR}/../llvm/docs/_themes
+        ${CMAKE_CURRENT_SOURCE_DIR}/_themes
+      )
+
+    # Generate the HTML documentation, invoked wt the docs-offload-html target.
+    add_sphinx_target(html offload
+      SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
+    add_dependencies(docs-offload-html OffloadDocsGenerate OffloadDocsCopy)
+  endif()
+endif()
diff --git a/offload/docs/conf.py b/offload/docs/conf.py
new file mode 100644
index 0000000000000..08a991a7d5ad5
--- /dev/null
+++ b/offload/docs/conf.py
@@ -0,0 +1,32 @@
+# Configuration file for the Sphinx documentation builder.
+#
+# For the full list of built-in configuration values, see the documentation:
+# https://www.sphinx-doc.org/en/master/usage/configuration.html
+
+# -- Project information -----------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
+
+project = "Offload"
+copyright = "2025, LLVM project"
+author = "LLVM project"
+
+# -- General configuration ---------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
+
+extensions = []
+
+templates_path = ["_templates"]
+exclude_patterns = []
+
+# -- C domain configuration --------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#c-config
+
+c_maximum_signature_line_length = 60
+
+# -- Options for HTML output -------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
+
+html_theme = "llvm-theme"
+html_theme_path = ["_themes"]
+html_static_path = ["_static"]
+html_favicon = "_static/favicon.ico"
diff --git a/offload/docs/index.rst b/offload/docs/index.rst
new file mode 100644
index 0000000000000..481d1f7ddd8b8
--- /dev/null
+++ b/offload/docs/index.rst
@@ -0,0 +1,21 @@
+.. Offload documentation master file, created by
+   sphinx-quickstart on Fri Jul  4 14:59:13 2025.
+   You can adapt this file completely to your liking, but it should at least
+   contain the root `toctree` directive.
+
+Welcome to Offload's documentation!
+===================================
+
+.. toctree::
+   :maxdepth: 2
+   :caption: Contents:
+
+   offload-api
+
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/offload/liboffload/API/Common.td b/offload/liboffload/API/Common.td
index 850a01d06759e..a621de081a0c6 100644
--- a/offload/liboffload/API/Common.td
+++ b/offload/liboffload/API/Common.td
@@ -44,21 +44,6 @@ def : Macro {
   let alt_value = "";
 }
 
-def : Macro {
-  let name = "OL_DLLEXPORT";
-  let desc = "Microsoft-specific dllexport storage-class attribute";
-  let condition = "defined(_WIN32)";
-  let value = "__declspec(dllexport)";
-}
-
-def : Macro {
-  let name = "OL_DLLEXPORT";
-  let desc = "GCC-specific dllexport storage-class attribute";
-  let condition = "__GNUC__ >= 4";
-  let value = "__attribute__ ((visibility (\"default\")))";
-  let alt_value = "";
-}
-
 def : Handle {
   let name = "ol_platform_handle_t";
   let desc = "Handle of a platform instance";



More information about the llvm-commits mailing list