Skip to content

Commit 4c02465

Browse files
committed
[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.
1 parent 92fbfc2 commit 4c02465

File tree

6 files changed

+209
-1
lines changed

6 files changed

+209
-1
lines changed

offload/tools/offload-tblgen/APIGen.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ static void ProcessHandle(const HandleRec &H, raw_ostream &OS) {
4848
exit(1);
4949
}
5050

51-
auto ImplName = H.getName().substr(0, H.getName().size() - 9) + "_impl_t";
51+
auto ImplName = getHandleImplName(H);
5252
OS << CommentsHeader;
5353
OS << formatv("/// @brief {0}\n", H.getDesc());
5454
OS << formatv("typedef struct {0} *{1};\n", ImplName, H.getName());

offload/tools/offload-tblgen/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ set(LLVM_LINK_COMPONENTS Support)
1212
add_tablegen(offload-tblgen OFFLOAD
1313
EXPORT OFFLOAD
1414
APIGen.cpp
15+
SpecGen.cpp
1516
EntryPointGen.cpp
1617
MiscGen.cpp
1718
GenCommon.hpp

offload/tools/offload-tblgen/GenCommon.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,8 @@ MakeParamComment(const llvm::offload::tblgen::ParamRec &Param) {
6565
(Param.isOut() ? "[out]" : ""),
6666
(Param.isOpt() ? "[optional]" : ""), Param.getDesc());
6767
}
68+
69+
inline std::string
70+
getHandleImplName(const llvm::offload::tblgen::HandleRec &H) {
71+
return (H.getName().substr(0, H.getName().size() - 9) + "_impl_t").str();
72+
}

offload/tools/offload-tblgen/Generators.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "llvm/TableGen/Record.h"
1212

1313
void EmitOffloadAPI(const llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
14+
void EmitOffloadSpec(const llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
1415
void EmitOffloadFuncNames(const llvm::RecordKeeper &Records,
1516
llvm::raw_ostream &OS);
1617
void EmitOffloadImplFuncDecls(const llvm::RecordKeeper &Records,
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
//===- offload-tblgen/APIGen.cpp - Tablegen backend for Offload header ----===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This is a Tablegen backend that produces the contents of the Offload API
10+
// specification. The generated reStructuredText is Sphinx compatible, see
11+
// https://www.sphinx-doc.org/en/master/usage/domains/c.html for further
12+
// details on the C language domain.
13+
//
14+
//===----------------------------------------------------------------------===//
15+
16+
#include "llvm/ADT/StringExtras.h"
17+
#include "llvm/Support/FormatVariadic.h"
18+
#include "llvm/TableGen/Record.h"
19+
#include "llvm/TableGen/TableGenBackend.h"
20+
21+
#include "GenCommon.hpp"
22+
#include "RecordTypes.hpp"
23+
24+
using namespace llvm;
25+
using namespace offload::tblgen;
26+
27+
namespace {
28+
std::string makeFunctionSignature(StringRef RetTy, StringRef Name,
29+
ArrayRef<ParamRec> Params) {
30+
std::string S;
31+
raw_string_ostream OS{S};
32+
OS << RetTy << " " << Name << "(";
33+
for (const ParamRec &Param : Params) {
34+
OS << Param.getType() << " " << Param.getName();
35+
if (Param != Params.back()) {
36+
OS << ", ";
37+
}
38+
}
39+
OS << ")";
40+
return S;
41+
}
42+
43+
std::string makeDoubleBackticks(StringRef R) {
44+
std::string S;
45+
for (char C : R) {
46+
if (C == '`') {
47+
S.push_back('`');
48+
}
49+
S.push_back(C);
50+
}
51+
return S;
52+
}
53+
54+
void processMacro(const MacroRec &M, raw_ostream &OS) {
55+
OS << formatv(".. c:macro:: {0}\n\n", M.getNameWithArgs());
56+
OS << " " << M.getDesc() << "\n\n";
57+
}
58+
59+
void processTypedef(const TypedefRec &T, raw_ostream &OS) {
60+
OS << formatv(".. c:type:: {0} {1}\n\n", T.getValue(), T.getName());
61+
OS << " " << T.getDesc() << "\n\n";
62+
}
63+
64+
void processHandle(const HandleRec &H, raw_ostream &OS) {
65+
66+
OS << formatv(".. c:type:: struct {0} *{1}\n\n", getHandleImplName(H),
67+
H.getName());
68+
OS << " " << H.getDesc() << "\n\n";
69+
}
70+
71+
void processFptrTypedef(const FptrTypedefRec &F, raw_ostream &OS) {
72+
OS << ".. c:type:: "
73+
<< makeFunctionSignature(F.getReturn(),
74+
StringRef{formatv("(*{0})", F.getName())},
75+
F.getParams())
76+
<< "\n\n";
77+
for (const ParamRec &P : F.getParams()) {
78+
OS << formatv(" :param {0}: {1}\n", P.getName(), P.getDesc());
79+
}
80+
OS << "\n";
81+
}
82+
83+
void processEnum(const EnumRec &E, raw_ostream &OS) {
84+
OS << formatv(".. c:enum:: {0}\n\n", E.getName());
85+
OS << " " << E.getDesc() << "\n\n";
86+
for (const EnumValueRec Etor : E.getValues()) {
87+
OS << formatv(" .. c:enumerator:: {0}_{1}\n\n", E.getEnumValNamePrefix(),
88+
Etor.getName());
89+
OS << " " << Etor.getDesc() << "\n\n";
90+
}
91+
}
92+
93+
void processStruct(const StructRec &S, raw_ostream &OS) {
94+
OS << formatv(".. c:struct:: {0}\n\n", S.getName());
95+
OS << " " << S.getDesc() << "\n\n";
96+
for (const StructMemberRec &M : S.getMembers()) {
97+
OS << formatv(" .. c:member:: {0} {1}\n\n", M.getType(), M.getName());
98+
OS << " " << M.getDesc() << "\n\n";
99+
}
100+
}
101+
102+
void processFunction(const FunctionRec &F, raw_ostream &OS) {
103+
OS << ".. c:function:: "
104+
<< makeFunctionSignature({formatv("{0}_result_t", PrefixLower)},
105+
F.getName(), F.getParams())
106+
<< "\n\n";
107+
108+
OS << " " << F.getDesc() << "\n\n";
109+
for (StringRef D : F.getDetails()) {
110+
OS << " " << D << "\n";
111+
}
112+
if (!F.getDetails().empty()) {
113+
OS << "\n";
114+
}
115+
116+
for (const ParamRec &P : F.getParams()) {
117+
OS << formatv(" :param {0}: {1}\n", P.getName(), P.getDesc());
118+
}
119+
120+
for (const ReturnRec &R : F.getReturns()) {
121+
OS << formatv(" :retval {0}:\n", R.getValue());
122+
for (StringRef C : R.getConditions()) {
123+
OS << " * ";
124+
if (C.starts_with("`") && C.ends_with("`")) {
125+
OS << ":c:expr:" << C;
126+
} else {
127+
OS << makeDoubleBackticks(C);
128+
}
129+
OS << "\n";
130+
}
131+
}
132+
OS << "\n";
133+
}
134+
} // namespace
135+
136+
void EmitOffloadSpec(const RecordKeeper &Records, raw_ostream &OS) {
137+
OS << "Offload API\n";
138+
OS << "===========\n\n";
139+
140+
ArrayRef<const Record *> Macros = Records.getAllDerivedDefinitions("Macro");
141+
if (!Macros.empty()) {
142+
OS << "Macros\n";
143+
OS << "------\n\n";
144+
for (const Record *M : Macros) {
145+
processMacro(MacroRec{M}, OS);
146+
}
147+
}
148+
149+
ArrayRef<const Record *> Handles = Records.getAllDerivedDefinitions("Handle");
150+
ArrayRef<const Record *> Typedefs =
151+
Records.getAllDerivedDefinitions("Typedef");
152+
ArrayRef<const Record *> FptrTypedefs =
153+
Records.getAllDerivedDefinitions("FptrTypedef");
154+
if (!Handles.empty() || !Typedefs.empty() || !FptrTypedefs.empty()) {
155+
OS << "Type Definitions\n";
156+
OS << "----------------\n\n";
157+
for (const Record *H : Handles) {
158+
processHandle(HandleRec{H}, OS);
159+
}
160+
for (const Record *T : Typedefs) {
161+
processTypedef(TypedefRec{T}, OS);
162+
}
163+
for (const Record *F : FptrTypedefs) {
164+
processFptrTypedef(FptrTypedefRec{F}, OS);
165+
}
166+
}
167+
168+
ArrayRef<const Record *> Enums = Records.getAllDerivedDefinitions("Enum");
169+
OS << "Enums\n";
170+
OS << "-----\n\n";
171+
if (!Enums.empty()) {
172+
for (const Record *E : Enums) {
173+
processEnum(EnumRec{E}, OS);
174+
}
175+
}
176+
177+
ArrayRef<const Record *> Structs = Records.getAllDerivedDefinitions("Struct");
178+
if (!Structs.empty()) {
179+
OS << "Structs\n";
180+
OS << "-------\n\n";
181+
for (const Record *S : Structs) {
182+
processStruct(StructRec{S}, OS);
183+
}
184+
}
185+
186+
ArrayRef<const Record *> Functions =
187+
Records.getAllDerivedDefinitions("Function");
188+
if (!Functions.empty()) {
189+
OS << "Functions\n";
190+
OS << "---------\n\n";
191+
for (const Record *F : Functions) {
192+
processFunction(FunctionRec{F}, OS);
193+
}
194+
}
195+
}

offload/tools/offload-tblgen/offload-tblgen.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ enum ActionType {
2626
PrintRecords,
2727
DumpJSON,
2828
GenAPI,
29+
GenSpec,
2930
GenFuncNames,
3031
GenImplFuncDecls,
3132
GenEntryPoints,
@@ -44,6 +45,8 @@ cl::opt<ActionType> Action(
4445
clEnumValN(DumpJSON, "dump-json",
4546
"Dump all records as machine-readable JSON"),
4647
clEnumValN(GenAPI, "gen-api", "Generate Offload API header contents"),
48+
clEnumValN(GenSpec, "gen-spec",
49+
"Generate Offload API specification contents"),
4750
clEnumValN(GenFuncNames, "gen-func-names",
4851
"Generate a list of all Offload API function names"),
4952
clEnumValN(
@@ -71,6 +74,9 @@ static bool OffloadTableGenMain(raw_ostream &OS, const RecordKeeper &Records) {
7174
case GenAPI:
7275
EmitOffloadAPI(Records, OS);
7376
break;
77+
case GenSpec:
78+
EmitOffloadSpec(Records, OS);
79+
break;
7480
case GenFuncNames:
7581
EmitOffloadFuncNames(Records, OS);
7682
break;

0 commit comments

Comments
 (0)