From b07b7bed0d00ec4e9666fa9992dd31f85e530c56 Mon Sep 17 00:00:00 2001 From: "Kenneth Benzie (Benie)" 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/DocGen.cpp | 195 ++++++++++++++++++ offload/tools/offload-tblgen/GenCommon.hpp | 5 + offload/tools/offload-tblgen/Generators.hpp | 1 + .../tools/offload-tblgen/offload-tblgen.cpp | 6 + 6 files changed, 209 insertions(+), 1 deletion(-) create mode 100644 offload/tools/offload-tblgen/DocGen.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..15525dc44ea60 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 + DocGen.cpp EntryPointGen.cpp MiscGen.cpp GenCommon.hpp diff --git a/offload/tools/offload-tblgen/DocGen.cpp b/offload/tools/offload-tblgen/DocGen.cpp new file mode 100644 index 0000000000000..29d00cb3b5098 --- /dev/null +++ b/offload/tools/offload-tblgen/DocGen.cpp @@ -0,0 +1,195 @@ +//===- offload-tblgen/DocGen.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 reStructuredText 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 Params) { + std::string S; + raw_string_ostream OS{S}; + OS << RetTy << " " << Name << "("; + for (const ParamRec &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 (const ParamRec &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 (const EnumValueRec 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 (const StructMemberRec &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 (StringRef D : F.getDetails()) { + OS << " " << D << "\n"; + } + if (!F.getDetails().empty()) { + OS << "\n"; + } + + for (const ParamRec &P : F.getParams()) { + OS << formatv(" :param {0}: {1}\n", P.getName(), P.getDesc()); + } + + for (const ReturnRec &R : F.getReturns()) { + OS << formatv(" :retval {0}:\n", R.getValue()); + for (StringRef 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 EmitOffloadDoc(const RecordKeeper &Records, raw_ostream &OS) { + OS << "Offload API\n"; + OS << "===========\n\n"; + + ArrayRef Macros = Records.getAllDerivedDefinitions("Macro"); + if (!Macros.empty()) { + OS << "Macros\n"; + OS << "------\n\n"; + for (const Record *M : Macros) { + processMacro(MacroRec{M}, OS); + } + } + + ArrayRef Handles = Records.getAllDerivedDefinitions("Handle"); + ArrayRef Typedefs = + Records.getAllDerivedDefinitions("Typedef"); + ArrayRef FptrTypedefs = + Records.getAllDerivedDefinitions("FptrTypedef"); + if (!Handles.empty() || !Typedefs.empty() || !FptrTypedefs.empty()) { + OS << "Type Definitions\n"; + OS << "----------------\n\n"; + for (const Record *H : Handles) { + processHandle(HandleRec{H}, OS); + } + for (const Record *T : Typedefs) { + processTypedef(TypedefRec{T}, OS); + } + for (const Record *F : FptrTypedefs) { + processFptrTypedef(FptrTypedefRec{F}, OS); + } + } + + ArrayRef Enums = Records.getAllDerivedDefinitions("Enum"); + OS << "Enums\n"; + OS << "-----\n\n"; + if (!Enums.empty()) { + for (const Record *E : Enums) { + processEnum(EnumRec{E}, OS); + } + } + + ArrayRef Structs = Records.getAllDerivedDefinitions("Struct"); + if (!Structs.empty()) { + OS << "Structs\n"; + OS << "-------\n\n"; + for (const Record *S : Structs) { + processStruct(StructRec{S}, OS); + } + } + + ArrayRef Functions = + Records.getAllDerivedDefinitions("Function"); + if (!Functions.empty()) { + OS << "Functions\n"; + OS << "---------\n\n"; + for (const Record *F : Functions) { + processFunction(FunctionRec{F}, OS); + } + } +} 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..fda63f8b198e5 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 EmitOffloadDoc(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/offload-tblgen.cpp b/offload/tools/offload-tblgen/offload-tblgen.cpp index ee7f6d4d79bff..18aaf9e00f08a 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, + GenDoc, GenFuncNames, GenImplFuncDecls, GenEntryPoints, @@ -44,6 +45,8 @@ cl::opt Action( clEnumValN(DumpJSON, "dump-json", "Dump all records as machine-readable JSON"), clEnumValN(GenAPI, "gen-api", "Generate Offload API header contents"), + clEnumValN(GenDoc, "gen-doc", + "Generate Offload API documentation 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 GenDoc: + EmitOffloadDoc(Records, OS); + break; case GenFuncNames: EmitOffloadFuncNames(Records, OS); break; From 439de860901adafa2eef392e5c84ba10d2a8dd29 Mon Sep 17 00:00:00 2001 From: "Kenneth Benzie (Benie)" 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..9c5db87b5ab4e --- /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-doc + 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 + ) + add_dependencies(OffloadDocsCopy OffloadDocsGenerate) + + # Generate the HTML documentation, the docs-offload-html target. + add_sphinx_target(html offload) + add_dependencies(docs-offload-html 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";