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// header. The generated comments are Doxygen compatible.
11//
12//===----------------------------------------------------------------------===//
13
14#include "llvm/ADT/StringExtras.h"
15#include "llvm/Support/FormatVariadic.h"
16#include "llvm/TableGen/Record.h"
17#include "llvm/TableGen/TableGenBackend.h"
18
19#include "GenCommon.hpp"
20#include "RecordTypes.hpp"
21
22using namespace llvm;
23using namespace offload::tblgen;
24
25// Produce a possibly multi-line comment from the input string
26static std::string MakeComment(StringRef in) {
27 std::string out = "";
28 size_t LineStart = 0;
29 size_t LineBreak = 0;
30 while (LineBreak < in.size()) {
31 LineBreak = in.find_first_of(Chars: "\n", From: LineStart);
32 if (LineBreak - LineStart <= 1) {
33 break;
34 }
35 out += std::string("/// ") +
36 in.substr(Start: LineStart, N: LineBreak - LineStart).str() + "\n";
37 LineStart = LineBreak + 1;
38 }
39
40 return out;
41}
42
43static void ProcessHandle(const HandleRec &H, raw_ostream &OS) {
44 if (!H.getName().ends_with(Suffix: "_handle_t")) {
45 errs() << "Handle type name (" << H.getName()
46 << ") must end with '_handle_t'!\n";
47 exit(status: 1);
48 }
49
50 auto ImplName = H.getName().substr(Start: 0, N: H.getName().size() - 9) + "_impl_t";
51 OS << CommentsHeader;
52 OS << formatv(Fmt: "/// @brief {0}\n", Vals: H.getDesc());
53 OS << formatv(Fmt: "typedef struct {0} *{1};\n", Vals&: ImplName, Vals: H.getName());
54}
55
56static void ProcessTypedef(const TypedefRec &T, raw_ostream &OS) {
57 OS << CommentsHeader;
58 OS << formatv(Fmt: "/// @brief {0}\n", Vals: T.getDesc());
59 OS << formatv(Fmt: "typedef {0} {1};\n", Vals: T.getValue(), Vals: T.getName());
60}
61
62static void ProcessMacro(const MacroRec &M, raw_ostream &OS) {
63 OS << CommentsHeader;
64 OS << formatv(Fmt: "#ifndef {0}\n", Vals: M.getName());
65 if (auto Condition = M.getCondition()) {
66 OS << formatv(Fmt: "#if {0}\n", Vals&: *Condition);
67 }
68 OS << "/// @brief " << M.getDesc() << "\n";
69 OS << formatv(Fmt: "#define {0} {1}\n", Vals: M.getNameWithArgs(), Vals: M.getValue());
70 if (auto AltValue = M.getAltValue()) {
71 OS << "#else\n";
72 OS << formatv(Fmt: "#define {0} {1}\n", Vals: M.getNameWithArgs(), Vals&: *AltValue);
73 }
74 if (auto Condition = M.getCondition()) {
75 OS << formatv(Fmt: "#endif // {0}\n", Vals&: *Condition);
76 }
77 OS << formatv(Fmt: "#endif // {0}\n", Vals: M.getName());
78}
79
80static void ProcessFunction(const FunctionRec &F, raw_ostream &OS) {
81 OS << CommentsHeader;
82 OS << formatv(Fmt: "/// @brief {0}\n", Vals: F.getDesc());
83 OS << CommentsBreak;
84
85 OS << "/// @details\n";
86 for (auto &Detail : F.getDetails()) {
87 OS << formatv(Fmt: "/// - {0}\n", Vals&: Detail);
88 }
89 OS << CommentsBreak;
90
91 // Emit analogue remarks
92 auto Analogues = F.getAnalogues();
93 if (!Analogues.empty()) {
94 OS << "/// @remarks\n/// _Analogues_\n";
95 for (auto &Analogue : Analogues) {
96 OS << formatv(Fmt: "/// - **{0}**\n", Vals&: Analogue);
97 }
98 OS << CommentsBreak;
99 }
100
101 OS << "/// @returns\n";
102 auto Returns = F.getReturns();
103 for (auto &Ret : Returns) {
104 OS << formatv(Fmt: "/// - ::{0}\n", Vals: Ret.getValue());
105 auto RetConditions = Ret.getConditions();
106 for (auto &RetCondition : RetConditions) {
107 OS << formatv(Fmt: "/// + {0}\n", Vals&: RetCondition);
108 }
109 }
110
111 OS << formatv(Fmt: "{0}_APIEXPORT {1}_result_t {0}_APICALL ", Vals: PrefixUpper,
112 Vals: PrefixLower);
113 OS << F.getName();
114 OS << "(\n";
115 auto Params = F.getParams();
116 for (auto &Param : Params) {
117 OS << MakeParamComment(Param) << "\n";
118 OS << " " << Param.getType() << " " << Param.getName();
119 if (Param != Params.back()) {
120 OS << ",\n";
121 } else {
122 OS << "\n";
123 }
124 }
125 OS << ");\n\n";
126}
127
128static void ProcessEnum(const EnumRec &Enum, raw_ostream &OS) {
129 OS << CommentsHeader;
130 OS << formatv(Fmt: "/// @brief {0}\n", Vals: Enum.getDesc());
131 OS << formatv(Fmt: "typedef enum {0} {{\n", Vals: Enum.getName());
132
133 uint32_t EtorVal = 0;
134 for (const auto &EnumVal : Enum.getValues()) {
135 if (Enum.isTyped()) {
136 OS << MakeComment(
137 in: formatv(Fmt: "[{0}] {1}", Vals: EnumVal.getTaggedType(), Vals: EnumVal.getDesc())
138 .str());
139 } else {
140 OS << MakeComment(in: EnumVal.getDesc());
141 }
142 OS << formatv(TAB_1 "{0}_{1} = {2},\n", Vals: Enum.getEnumValNamePrefix(),
143 Vals: EnumVal.getName(), Vals: EtorVal++);
144 }
145
146 // Add force uint32 val
147 OS << formatv(TAB_1 "/// @cond\n" TAB_1
148 "{0}_FORCE_UINT32 = 0x7fffffff\n" TAB_1
149 "/// @endcond\n\n",
150 Vals: Enum.getEnumValNamePrefix());
151
152 OS << formatv(Fmt: "} {0};\n", Vals: Enum.getName());
153}
154
155static void ProcessStruct(const StructRec &Struct, raw_ostream &OS) {
156 OS << CommentsHeader;
157 OS << formatv(Fmt: "/// @brief {0}\n", Vals: Struct.getDesc());
158 OS << formatv(Fmt: "typedef struct {0} {{\n", Vals: Struct.getName());
159
160 for (const auto &Member : Struct.getMembers()) {
161 OS << formatv(TAB_1 "{0} {1}; {2}", Vals: Member.getType(), Vals: Member.getName(),
162 Vals: MakeComment(in: Member.getDesc()));
163 }
164
165 OS << formatv(Fmt: "} {0};\n\n", Vals: Struct.getName());
166}
167
168static void ProcessFptrTypedef(const FptrTypedefRec &F, raw_ostream &OS) {
169 OS << CommentsHeader;
170 OS << formatv(Fmt: "/// @brief {0}\n", Vals: F.getDesc());
171 OS << formatv(Fmt: "typedef {0} (*{1})(", Vals: F.getReturn(), Vals: F.getName());
172 for (const auto &Param : F.getParams()) {
173 OS << formatv(Fmt: "\n // {0}\n {1} {2}", Vals: Param.getDesc(), Vals: Param.getType(),
174 Vals: Param.getName());
175 if (Param != F.getParams().back())
176 OS << ",";
177 }
178 OS << ");\n";
179}
180
181static void ProcessFuncParamStruct(const FunctionRec &Func, raw_ostream &OS) {
182 if (Func.getParams().size() == 0) {
183 return;
184 }
185
186 auto FuncParamStructBegin = R"(
187///////////////////////////////////////////////////////////////////////////////
188/// @brief Function parameters for {0}
189/// @details Each entry is a pointer to the parameter passed to the function;
190typedef struct {1} {{
191)";
192
193 OS << formatv(Fmt: FuncParamStructBegin, Vals: Func.getName(),
194 Vals: Func.getParamStructName());
195 for (const auto &Param : Func.getParams()) {
196 OS << TAB_1 << Param.getType() << "* p" << Param.getName() << ";\n";
197 }
198 OS << formatv(Fmt: "} {0};\n", Vals: Func.getParamStructName());
199}
200
201static void ProcessFuncWithCodeLocVariant(const FunctionRec &Func,
202 raw_ostream &OS) {
203
204 auto FuncWithCodeLocBegin = R"(
205///////////////////////////////////////////////////////////////////////////////
206/// @brief Variant of {0} that also sets source code location information
207/// @details See also ::{0}
208OL_APIEXPORT ol_result_t OL_APICALL {0}WithCodeLoc(
209)";
210 OS << formatv(Fmt: FuncWithCodeLocBegin, Vals: Func.getName());
211 auto Params = Func.getParams();
212 for (auto &Param : Params) {
213 OS << " " << Param.getType() << " " << Param.getName();
214 OS << ",\n";
215 }
216 OS << "ol_code_location_t *CodeLocation);\n\n";
217}
218
219void EmitOffloadAPI(const RecordKeeper &Records, raw_ostream &OS) {
220 OS << GenericHeader;
221 OS << FileHeader;
222 // Generate main API definitions
223 for (auto *R : Records.getAllDerivedDefinitions(ClassName: "APIObject")) {
224 if (R->isSubClassOf(Name: "Macro")) {
225 ProcessMacro(M: MacroRec{R}, OS);
226 } else if (R->isSubClassOf(Name: "Typedef")) {
227 ProcessTypedef(T: TypedefRec{R}, OS);
228 } else if (R->isSubClassOf(Name: "Handle")) {
229 ProcessHandle(H: HandleRec{R}, OS);
230 } else if (R->isSubClassOf(Name: "Function")) {
231 ProcessFunction(F: FunctionRec{R}, OS);
232 } else if (R->isSubClassOf(Name: "Enum")) {
233 ProcessEnum(Enum: EnumRec{R}, OS);
234 } else if (R->isSubClassOf(Name: "Struct")) {
235 ProcessStruct(Struct: StructRec{R}, OS);
236 } else if (R->isSubClassOf(Name: "FptrTypedef")) {
237 ProcessFptrTypedef(F: FptrTypedefRec{R}, OS);
238 }
239 }
240
241 // Generate auxiliary definitions (func param structs etc)
242 for (auto *R : Records.getAllDerivedDefinitions(ClassName: "Function")) {
243 ProcessFuncParamStruct(Func: FunctionRec{R}, OS);
244 }
245
246 for (auto *R : Records.getAllDerivedDefinitions(ClassName: "Function")) {
247 ProcessFuncWithCodeLocVariant(Func: FunctionRec{R}, OS);
248 }
249
250 OS << FileFooter;
251}
252

source code of offload/tools/offload-tblgen/APIGen.cpp