| //===- offload-tblgen/APIGen.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 |
| // header. The generated comments are Doxygen compatible. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #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; |
| |
| // Produce a possibly multi-line comment from the input string |
| static std::string MakeComment(StringRef in) { |
| std::string out = ""; |
| size_t LineStart = 0; |
| size_t LineBreak = 0; |
| while (LineBreak < in.size()) { |
| LineBreak = in.find_first_of("\n", LineStart); |
| if (LineBreak - LineStart <= 1) { |
| break; |
| } |
| out += std::string("/// ") + |
| in.substr(LineStart, LineBreak - LineStart).str() + "\n"; |
| LineStart = LineBreak + 1; |
| } |
| |
| return out; |
| } |
| |
| static void ProcessHandle(const HandleRec &H, raw_ostream &OS) { |
| if (!H.getName().ends_with("_handle_t")) { |
| errs() << "Handle type name (" << H.getName() |
| << ") must end with '_handle_t'!\n"; |
| exit(1); |
| } |
| |
| auto ImplName = H.getName().substr(0, H.getName().size() - 9) + "_impl_t"; |
| OS << CommentsHeader; |
| OS << formatv("/// @brief {0}\n", H.getDesc()); |
| OS << formatv("typedef struct {0} *{1};\n", ImplName, H.getName()); |
| } |
| |
| static void ProcessTypedef(const TypedefRec &T, raw_ostream &OS) { |
| OS << CommentsHeader; |
| OS << formatv("/// @brief {0}\n", T.getDesc()); |
| OS << formatv("typedef {0} {1};\n", T.getValue(), T.getName()); |
| } |
| |
| static void ProcessMacro(const MacroRec &M, raw_ostream &OS) { |
| OS << CommentsHeader; |
| OS << formatv("#ifndef {0}\n", M.getName()); |
| if (auto Condition = M.getCondition()) { |
| OS << formatv("#if {0}\n", *Condition); |
| } |
| OS << "/// @brief " << M.getDesc() << "\n"; |
| OS << formatv("#define {0} {1}\n", M.getNameWithArgs(), M.getValue()); |
| if (auto AltValue = M.getAltValue()) { |
| OS << "#else\n"; |
| OS << formatv("#define {0} {1}\n", M.getNameWithArgs(), *AltValue); |
| } |
| if (auto Condition = M.getCondition()) { |
| OS << formatv("#endif // {0}\n", *Condition); |
| } |
| OS << formatv("#endif // {0}\n", M.getName()); |
| } |
| |
| static void ProcessFunction(const FunctionRec &F, raw_ostream &OS) { |
| OS << CommentsHeader; |
| OS << formatv("/// @brief {0}\n", F.getDesc()); |
| OS << CommentsBreak; |
| |
| OS << "/// @details\n"; |
| for (auto &Detail : F.getDetails()) { |
| OS << formatv("/// - {0}\n", Detail); |
| } |
| OS << CommentsBreak; |
| |
| // Emit analogue remarks |
| auto Analogues = F.getAnalogues(); |
| if (!Analogues.empty()) { |
| OS << "/// @remarks\n/// _Analogues_\n"; |
| for (auto &Analogue : Analogues) { |
| OS << formatv("/// - **{0}**\n", Analogue); |
| } |
| OS << CommentsBreak; |
| } |
| |
| OS << "/// @returns\n"; |
| auto Returns = F.getReturns(); |
| for (auto &Ret : Returns) { |
| OS << formatv("/// - ::{0}\n", Ret.getValue()); |
| auto RetConditions = Ret.getConditions(); |
| for (auto &RetCondition : RetConditions) { |
| OS << formatv("/// + {0}\n", RetCondition); |
| } |
| } |
| |
| OS << formatv("{0}_APIEXPORT {1}_result_t {0}_APICALL ", PrefixUpper, |
| PrefixLower); |
| OS << F.getName(); |
| OS << "(\n"; |
| auto Params = F.getParams(); |
| for (auto &Param : Params) { |
| OS << MakeParamComment(Param) << "\n"; |
| OS << " " << Param.getType() << " " << Param.getName(); |
| if (Param != Params.back()) { |
| OS << ",\n"; |
| } else { |
| OS << "\n"; |
| } |
| } |
| OS << ");\n\n"; |
| } |
| |
| static void ProcessEnum(const EnumRec &Enum, raw_ostream &OS) { |
| OS << CommentsHeader; |
| OS << formatv("/// @brief {0}\n", Enum.getDesc()); |
| OS << formatv("typedef enum {0} {{\n", Enum.getName()); |
| |
| uint32_t EtorVal = 0; |
| for (const auto &EnumVal : Enum.getValues()) { |
| if (Enum.isTyped()) { |
| OS << MakeComment( |
| formatv("[{0}] {1}", EnumVal.getTaggedType(), EnumVal.getDesc()) |
| .str()); |
| } else { |
| OS << MakeComment(EnumVal.getDesc()); |
| } |
| OS << formatv(TAB_1 "{0}_{1} = {2},\n", Enum.getEnumValNamePrefix(), |
| EnumVal.getName(), EtorVal++); |
| } |
| |
| // Add force uint32 val |
| OS << formatv(TAB_1 "/// @cond\n" TAB_1 |
| "{0}_FORCE_UINT32 = 0x7fffffff\n" TAB_1 |
| "/// @endcond\n\n", |
| Enum.getEnumValNamePrefix()); |
| |
| OS << formatv("} {0};\n", Enum.getName()); |
| } |
| |
| static void ProcessStruct(const StructRec &Struct, raw_ostream &OS) { |
| OS << CommentsHeader; |
| OS << formatv("/// @brief {0}\n", Struct.getDesc()); |
| OS << formatv("typedef struct {0} {{\n", Struct.getName()); |
| |
| for (const auto &Member : Struct.getMembers()) { |
| OS << formatv(TAB_1 "{0} {1}; {2}", Member.getType(), Member.getName(), |
| MakeComment(Member.getDesc())); |
| } |
| |
| OS << formatv("} {0};\n\n", Struct.getName()); |
| } |
| |
| static void ProcessFptrTypedef(const FptrTypedefRec &F, raw_ostream &OS) { |
| OS << CommentsHeader; |
| OS << formatv("/// @brief {0}\n", F.getDesc()); |
| OS << formatv("typedef {0} (*{1})(", F.getReturn(), F.getName()); |
| for (const auto &Param : F.getParams()) { |
| OS << formatv("\n // {0}\n {1} {2}", Param.getDesc(), Param.getType(), |
| Param.getName()); |
| if (Param != F.getParams().back()) |
| OS << ","; |
| } |
| OS << ");\n"; |
| } |
| |
| static void ProcessFuncParamStruct(const FunctionRec &Func, raw_ostream &OS) { |
| if (Func.getParams().size() == 0) { |
| return; |
| } |
| |
| auto FuncParamStructBegin = R"( |
| /////////////////////////////////////////////////////////////////////////////// |
| /// @brief Function parameters for {0} |
| /// @details Each entry is a pointer to the parameter passed to the function; |
| typedef struct {1} {{ |
| )"; |
| |
| OS << formatv(FuncParamStructBegin, Func.getName(), |
| Func.getParamStructName()); |
| for (const auto &Param : Func.getParams()) { |
| OS << TAB_1 << Param.getType() << "* p" << Param.getName() << ";\n"; |
| } |
| OS << formatv("} {0};\n", Func.getParamStructName()); |
| } |
| |
| static void ProcessFuncWithCodeLocVariant(const FunctionRec &Func, |
| raw_ostream &OS) { |
| |
| auto FuncWithCodeLocBegin = R"( |
| /////////////////////////////////////////////////////////////////////////////// |
| /// @brief Variant of {0} that also sets source code location information |
| /// @details See also ::{0} |
| OL_APIEXPORT ol_result_t OL_APICALL {0}WithCodeLoc( |
| )"; |
| OS << formatv(FuncWithCodeLocBegin, Func.getName()); |
| auto Params = Func.getParams(); |
| for (auto &Param : Params) { |
| OS << " " << Param.getType() << " " << Param.getName(); |
| OS << ",\n"; |
| } |
| OS << "ol_code_location_t *CodeLocation);\n\n"; |
| } |
| |
| void EmitOffloadAPI(const RecordKeeper &Records, raw_ostream &OS) { |
| OS << GenericHeader; |
| OS << FileHeader; |
| // Generate main API definitions |
| for (auto *R : Records.getAllDerivedDefinitions("APIObject")) { |
| if (R->isSubClassOf("Macro")) { |
| ProcessMacro(MacroRec{R}, OS); |
| } else if (R->isSubClassOf("Typedef")) { |
| ProcessTypedef(TypedefRec{R}, OS); |
| } else if (R->isSubClassOf("Handle")) { |
| ProcessHandle(HandleRec{R}, OS); |
| } else if (R->isSubClassOf("Function")) { |
| ProcessFunction(FunctionRec{R}, OS); |
| } else if (R->isSubClassOf("Enum")) { |
| ProcessEnum(EnumRec{R}, OS); |
| } else if (R->isSubClassOf("Struct")) { |
| ProcessStruct(StructRec{R}, OS); |
| } else if (R->isSubClassOf("FptrTypedef")) { |
| ProcessFptrTypedef(FptrTypedefRec{R}, OS); |
| } |
| } |
| |
| // Generate auxiliary definitions (func param structs etc) |
| for (auto *R : Records.getAllDerivedDefinitions("Function")) { |
| ProcessFuncParamStruct(FunctionRec{R}, OS); |
| } |
| |
| for (auto *R : Records.getAllDerivedDefinitions("Function")) { |
| ProcessFuncWithCodeLocVariant(FunctionRec{R}, OS); |
| } |
| |
| OS << FileFooter; |
| } |