| 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 | |
| 22 | using namespace llvm; |
| 23 | using namespace offload::tblgen; |
| 24 | |
| 25 | // Produce a possibly multi-line comment from the input string |
| 26 | static std::string (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 | |
| 43 | static 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 | |
| 56 | static 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 | |
| 62 | static 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 | |
| 80 | static 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 | |
| 128 | static 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 | |
| 155 | static 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 | |
| 168 | static 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 | |
| 181 | static 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; |
| 190 | typedef 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 | |
| 201 | static 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} |
| 208 | OL_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 | |
| 219 | void 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 | |