//===- 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) { OS << CommentsHeader; OS << formatv("/// @brief {0}\n", H.getDesc()); OS << formatv("typedef struct {0}_ *{0};\n", 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 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); } } // 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; }