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

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