================
@@ -605,3 +607,188 @@ void clang::EmitClangBuiltins(const RecordKeeper 
&Records, raw_ostream &OS) {
 #undef ATOMIC_BUILTIN
 )c++";
 }
+
+//===----------------------------------------------------------------------===//
+// Builtin documentation emitter
+//===----------------------------------------------------------------------===//
+
+/// Holds the data needed to emit documentation for a single builtin.
+namespace {
+struct BuiltinDocData {
+  const Record *Documentation = nullptr;
+  const Record *BuiltinRecord = nullptr;
+  std::string Heading;
+};
+} // namespace
+
+static void writeCategoryHeader(const Record *Category, raw_ostream &OS) {
+  StringRef CategoryName = Category->getValueAsString("Name");
+  OS << "\n" << CategoryName << "\n";
+  for (size_t I = 0, E = CategoryName.size(); I < E; ++I)
+    OS << "=";
+  OS << "\n\n";
+
+  StringRef CategoryContent = Category->getValueAsString("Content");
+  if (!CategoryContent.trim().empty())
+    OS << CategoryContent.trim() << "\n\n";
+}
+
+/// Split a parameter list string into individual parameter type strings,
+/// respecting nested angle brackets (e.g. address_space<4>, _ExtVector<4,
+/// float>).
+static SmallVector<StringRef> splitParams(StringRef Params) {
+  SmallVector<StringRef> Result;
+  if (Params.empty())
+    return Result;
+
+  int Depth = 0;
+  size_t Start = 0;
+  for (size_t I = 0, E = Params.size(); I < E; ++I) {
+    if (Params[I] == '<') {
+      ++Depth;
+    } else if (Params[I] == '>') {
+      --Depth;
+    } else if (Params[I] == ',' && Depth == 0) {
+      Result.push_back(Params.substr(Start, I - Start).trim());
+      Start = I + 1;
+    }
+  }
+  // Add the last parameter.
+  StringRef Last = Params.substr(Start).trim();
+  if (!Last.empty())
+    Result.push_back(Last);
+  return Result;
+}
+
+static void writeBuiltinDocumentation(const BuiltinDocData &Doc,
+                                      raw_ostream &OS) {
+  // Write heading with '-' underline (subsection).
+  std::string HeadingText = "``" + Doc.Heading + "``";
+  OS << HeadingText << "\n";
+  for (size_t I = 0, E = HeadingText.size(); I < E; ++I)
+    OS << "-";
+  OS << "\n\n";
+
+  // Write prototype as a code block.
+  StringRef Prototype = Doc.BuiltinRecord->getValueAsString("Prototype");
+  if (!Prototype.empty()) {
+    std::vector<StringRef> Spellings =
+        Doc.BuiltinRecord->getValueAsListOfStrings("Spellings");
+    StringRef Name =
+        Spellings.empty() ? Doc.BuiltinRecord->getName() : Spellings[0];
+
+    // Split prototype into return type and params at the first '('.
+    size_t ParenPos = Prototype.find('(');
+    if (ParenPos != StringRef::npos) {
+      StringRef RetType = Prototype.substr(0, ParenPos).rtrim();
+      StringRef ParamStr =
+          Prototype.substr(ParenPos + 1, Prototype.size() - ParenPos - 2);
+
+      OS << "**Prototype:**\n\n";
+      OS << ".. code-block:: c\n\n";
+      OS << "  " << RetType << " " << Name << "(";
+
+      std::vector<StringRef> ArgNames =
+          Doc.BuiltinRecord->getValueAsListOfStrings("ArgNames");
+      if (!ArgNames.empty()) {
+        SmallVector<StringRef> ParamTypes = splitParams(ParamStr);
+        bool IsVariadic = !ParamTypes.empty() && ParamTypes.back() == "...";
+        size_t NamedParams = ParamTypes.size() - (IsVariadic ? 1 : 0);
+        if (ArgNames.size() != NamedParams)
+          PrintFatalError(Doc.BuiltinRecord->getLoc(),
+                          "number of ArgNames (" + Twine(ArgNames.size()) +
+                              ") does not match number of prototype "
+                              "parameters (" +
+                              Twine(NamedParams) + ")");
+        for (size_t I = 0, E = ParamTypes.size(); I < E; ++I) {
+          if (I > 0)
+            OS << ", ";
+          if (I < NamedParams)
+            OS << ParamTypes[I] << " " << ArgNames[I];
+          else
+            OS << ParamTypes[I];
+        }
+      } else {
+        OS << ParamStr;
+      }
+
+      OS << ")\n\n";
+    }
+  }
+
+  // Write target features if this is a TargetBuiltin with features.
+  if (Doc.BuiltinRecord->isSubClassOf("TargetBuiltin")) {
+    StringRef Features = Doc.BuiltinRecord->getValueAsString("Features");
+    if (!Features.empty())
+      OS << "**Target Features:** " << Features << "\n\n";
+  }
+
+  // Write documentation content.
+  StringRef Content = Doc.Documentation->getValueAsString("Content");
+  OS << Content.trim() << "\n\n\n";
+}
+
+void clang::EmitClangBuiltinDocs(const RecordKeeper &Records, raw_ostream &OS) 
{
+  // Get the documentation introduction paragraph.
+  const Record *Doc = Records.getDef("GlobalDocumentation");
+  if (!Doc) {
+    PrintFatalError("The GlobalDocumentation top-level definition is missing, "
+                    "no documentation will be generated.");
+    return;
+  }
+
+  OS << Doc->getValueAsString("Intro") << "\n";
+
+  // Gather documentation from each builtin, grouped by category.
+  llvm::MapVector<const Record *, std::vector<BuiltinDocData>> SplitDocs;
+
+  for (const Record *B : Records.getAllDerivedDefinitions("Builtin")) {
+    for (const Record *D : B->getValueAsListOfDefs("Documentation")) {
+      const Record *Category = D->getValueAsDef("Category");
+      StringRef Cat = Category->getValueAsString("Name");
+
+      // Skip builtins that are explicitly internal-only.
+      if (Cat == "InternalOnly")
+        continue;
+
+      BuiltinDocData Data;
+      Data.Documentation = D;
+      Data.BuiltinRecord = B;
+
+      // Use the Heading field if set, otherwise use the builtin's first
+      // spelling.
+      StringRef Heading = D->getValueAsString("Heading");
----------------
jurahul wrote:

Q: Can this move into the BuiltinDocData constructor (that accepts 2 records) ? 
And then do:

SplitDocs[Category].emplace_back(D, B);

https://github.com/llvm/llvm-project/pull/181573
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to