[libc-commits] [libc] [libc] add support for function level attributes (PR #79891)

Schrodinger ZHU Yifan via libc-commits libc-commits at lists.llvm.org
Mon Feb 5 06:32:42 PST 2024


https://github.com/SchrodingerZhu updated https://github.com/llvm/llvm-project/pull/79891

>From 20da6728bb49ee92e6dcab890ef7ca84dcf654f5 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Mon, 29 Jan 2024 14:21:25 -0500
Subject: [PATCH 1/8] [libc][RFC] add support for function level attributes

---
 libc/spec/spec.td                      | 21 ++++++-
 libc/utils/HdrGen/PublicAPICommand.cpp | 77 ++++++++++++++++++++++++--
 libc/utils/HdrGen/PublicAPICommand.h   |  2 +
 3 files changed, 95 insertions(+), 5 deletions(-)

diff --git a/libc/spec/spec.td b/libc/spec/spec.td
index 0b557c807a546..b20362855c630 100644
--- a/libc/spec/spec.td
+++ b/libc/spec/spec.td
@@ -170,10 +170,29 @@ class ArgSpec<Type type, list<Annotation> annotations = [], string name = ""> {
   string Name = name;
 }
 
-class FunctionSpec<string name, RetValSpec return, list<ArgSpec> args> {
+class FunctionAttr {}
+class GnuFunctionAttr<string attr> : FunctionAttr {
+  string Attr = attr;
+  string Style = "gnu";
+}
+class C23FunctionAttr<string attr, string namespace> : FunctionAttr {
+  string Attr = attr;
+  string Namespace = namespace;
+  string Style = "c23";
+}
+class DeclspecFunctionAttr<string attr> : FunctionAttr {
+  string Attr = attr;
+  string Style = "declspec";
+}
+class FunctionAttrSpec<list<FunctionAttr> instances> {
+  list<FunctionAttr> Instances = instances;
+}
+
+class FunctionSpec<string name, RetValSpec return, list<ArgSpec> args, list<FunctionAttrSpec> attrs = []> {
   string Name = name;
   RetValSpec Return = return;
   list<ArgSpec> Args = args;
+  list<FunctionAttrSpec> Attributes = attrs;
 }
 
 class ObjectSpec<string name, string type> {
diff --git a/libc/utils/HdrGen/PublicAPICommand.cpp b/libc/utils/HdrGen/PublicAPICommand.cpp
index b1c7a072658ff..26eb14c758920 100644
--- a/libc/utils/HdrGen/PublicAPICommand.cpp
+++ b/libc/utils/HdrGen/PublicAPICommand.cpp
@@ -15,6 +15,7 @@
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/SourceMgr.h"
 #include "llvm/TableGen/Record.h"
+#include <llvm/ADT/STLExtras.h>
 
 // Text blocks for macro definitions and type decls can be indented to
 // suit the surrounding tablegen listing. We need to dedent such blocks
@@ -49,7 +50,7 @@ namespace llvm_libc {
 
 void writeAPIFromIndex(APIIndexer &G,
                        std::vector<std::string> EntrypointNameList,
-                       llvm::raw_ostream &OS) {
+                       llvm::raw_ostream &OS, AttributeStyle PreferedStyle) {
   for (auto &Pair : G.MacroDefsMap) {
     const std::string &Name = Pair.first;
     if (G.MacroSpecMap.find(Name) == G.MacroSpecMap.end())
@@ -102,6 +103,62 @@ void writeAPIFromIndex(APIIndexer &G,
     llvm::Record *RetValSpec = FunctionSpec->getValueAsDef("Return");
     llvm::Record *ReturnType = RetValSpec->getValueAsDef("ReturnType");
 
+    auto GetStyle = [](llvm::Record *Instance) {
+      auto Style = Instance->getValueAsString("Style");
+      if (Style == "gnu")
+        return AttributeStyle::Gnu;
+      if (Style == "c23")
+        return AttributeStyle::C23;
+      if (Style == "declspec")
+        return AttributeStyle::Declspec;
+      return AttributeStyle::None;
+    };
+
+    if (PreferedStyle != AttributeStyle::None) {
+      auto Attributes = FunctionSpec->getValueAsListOfDefs("Attributes");
+      llvm::SmallVector<llvm::Record *> Attrs;
+      for (auto *Attr : Attributes) {
+        auto Instances = Attr->getValueAsListOfDefs("Instances");
+        for (auto *Instance : Instances) {
+          if (GetStyle(Instance) == PreferedStyle) {
+            Attrs.push_back(Instance);
+          }
+        }
+      }
+
+      if (Attrs.size() != 0) {
+        if (PreferedStyle == AttributeStyle::Gnu) {
+          OS << "__attribute__((";
+          llvm::interleaveComma(Attrs, OS, [&](llvm::Record *Instance) {
+            OS << Instance->getValueAsString("Attr");
+          });
+          OS << ")) ";
+        }
+
+        if (PreferedStyle == AttributeStyle::C23) {
+          OS << "__attribute__((";
+          llvm::interleaveComma(Attrs, OS, [&](llvm::Record *Instance) {
+            auto Namespace = Instance->getValueAsString("Namespace");
+            if (Namespace != "")
+              OS << Namespace << "::";
+            OS << Instance->getValueAsString("Attr");
+          });
+          OS << ")) ";
+        }
+
+        if (PreferedStyle == AttributeStyle::Declspec) {
+          OS << "__declspec(";
+          llvm::interleave(
+              Attrs.begin(), Attrs.end(),
+              [&](llvm::Record *Instance) {
+                OS << Instance->getValueAsString("Attr");
+              },
+              [&]() { OS << ' '; });
+          OS << ") ";
+        }
+      }
+    }
+
     OS << G.getTypeAsString(ReturnType) << " " << Name << "(";
 
     auto ArgsList = FunctionSpec->getValueAsListOfDefs("Args");
@@ -134,12 +191,24 @@ void PublicAPICommand::run(llvm::raw_ostream &OS, const ArgVector &Args,
                            llvm::StringRef StdHeader,
                            llvm::RecordKeeper &Records,
                            const Command::ErrorReporter &Reporter) const {
-  if (Args.size() != 0) {
-    Reporter.printFatalError("public_api command does not take any arguments.");
+  if (Args.size() > 1) {
+    Reporter.printFatalError(
+        "public_api command does not take more than one arguments.");
+  }
+
+  AttributeStyle PreferedStyle = AttributeStyle::Gnu;
+
+  for (auto &arg : Args) {
+    if (arg == "prefer-c23-attributes") {
+      PreferedStyle = AttributeStyle::C23;
+    }
+    if (arg == "prefer-no-attributes") {
+      PreferedStyle = AttributeStyle::None;
+    }
   }
 
   APIIndexer G(StdHeader, Records);
-  writeAPIFromIndex(G, EntrypointNameList, OS);
+  writeAPIFromIndex(G, EntrypointNameList, OS, PreferedStyle);
 }
 
 } // namespace llvm_libc
diff --git a/libc/utils/HdrGen/PublicAPICommand.h b/libc/utils/HdrGen/PublicAPICommand.h
index fb0a7a81cf277..035e1c71f4f12 100644
--- a/libc/utils/HdrGen/PublicAPICommand.h
+++ b/libc/utils/HdrGen/PublicAPICommand.h
@@ -25,6 +25,8 @@ class RecordKeeper;
 
 namespace llvm_libc {
 
+enum class AttributeStyle { None, Gnu, C23, Declspec };
+
 class PublicAPICommand : public Command {
 private:
   const std::vector<std::string> &EntrypointNameList;

>From f233699191529fccff7a80619700b62234c6aa62 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Mon, 29 Jan 2024 15:05:04 -0500
Subject: [PATCH 2/8] [libc][RFC] add support for function level attributes

---
 libc/spec/spec.td                      |   7 +-
 libc/spec/stdc.td                      |   7 +-
 libc/utils/HdrGen/PublicAPICommand.cpp | 152 +++++++++++++------------
 libc/utils/HdrGen/PublicAPICommand.h   |   2 +-
 4 files changed, 92 insertions(+), 76 deletions(-)

diff --git a/libc/spec/spec.td b/libc/spec/spec.td
index b20362855c630..a14668bd15e53 100644
--- a/libc/spec/spec.td
+++ b/libc/spec/spec.td
@@ -175,17 +175,18 @@ class GnuFunctionAttr<string attr> : FunctionAttr {
   string Attr = attr;
   string Style = "gnu";
 }
-class C23FunctionAttr<string attr, string namespace> : FunctionAttr {
+class Cxx11FunctionAttr<string attr, string namespace> : FunctionAttr {
   string Attr = attr;
   string Namespace = namespace;
-  string Style = "c23";
+  string Style = "cxx11";
 }
 class DeclspecFunctionAttr<string attr> : FunctionAttr {
   string Attr = attr;
   string Style = "declspec";
 }
-class FunctionAttrSpec<list<FunctionAttr> instances> {
+class FunctionAttrSpec<string macro, list<FunctionAttr> instances> {
   list<FunctionAttr> Instances = instances;
+  string Macro = macro;
 }
 
 class FunctionSpec<string name, RetValSpec return, list<ArgSpec> args, list<FunctionAttrSpec> attrs = []> {
diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td
index 607dda7c9041f..3552ed2cc6ca4 100644
--- a/libc/spec/stdc.td
+++ b/libc/spec/stdc.td
@@ -26,6 +26,11 @@ def StdC : StandardSpec<"stdc"> {
       []
   >;
 
+  FunctionAttrSpec ConstAttr = FunctionAttrSpec<"__LIBC_CONST_ATTR", [
+    Cxx11FunctionAttr<"const", "gnu">,
+    GnuFunctionAttr<"const">,
+  ]>;
+
   HeaderSpec CType = HeaderSpec<
       "ctype.h",
       [], // Macros
@@ -366,7 +371,7 @@ def StdC : StandardSpec<"stdc"> {
           FunctionSpec<"ceill", RetValSpec<LongDoubleType>, [ArgSpec<LongDoubleType>]>,
           FunctionSpec<"ceilf128", RetValSpec<Float128Type>, [ArgSpec<Float128Type>]>,
 
-          FunctionSpec<"fabs", RetValSpec<DoubleType>, [ArgSpec<DoubleType>]>,
+          FunctionSpec<"fabs", RetValSpec<DoubleType>, [ArgSpec<DoubleType>], [ConstAttr]>,
           FunctionSpec<"fabsf", RetValSpec<FloatType>, [ArgSpec<FloatType>]>,
           FunctionSpec<"fabsl", RetValSpec<LongDoubleType>, [ArgSpec<LongDoubleType>]>,
           FunctionSpec<"fabsf128", RetValSpec<Float128Type>, [ArgSpec<Float128Type>]>,
diff --git a/libc/utils/HdrGen/PublicAPICommand.cpp b/libc/utils/HdrGen/PublicAPICommand.cpp
index 26eb14c758920..d24637db0f788 100644
--- a/libc/utils/HdrGen/PublicAPICommand.cpp
+++ b/libc/utils/HdrGen/PublicAPICommand.cpp
@@ -15,6 +15,7 @@
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/SourceMgr.h"
 #include "llvm/TableGen/Record.h"
+#include <algorithm>
 #include <llvm/ADT/STLExtras.h>
 
 // Text blocks for macro definitions and type decls can be indented to
@@ -50,7 +51,7 @@ namespace llvm_libc {
 
 void writeAPIFromIndex(APIIndexer &G,
                        std::vector<std::string> EntrypointNameList,
-                       llvm::raw_ostream &OS, AttributeStyle PreferedStyle) {
+                       llvm::raw_ostream &OS) {
   for (auto &Pair : G.MacroDefsMap) {
     const std::string &Name = Pair.first;
     if (G.MacroSpecMap.find(Name) == G.MacroSpecMap.end())
@@ -88,6 +89,71 @@ void writeAPIFromIndex(APIIndexer &G,
   if (G.Enumerations.size() != 0)
     OS << "};\n\n";
 
+  // declare macros for attributes
+  llvm::DenseMap<llvm::StringRef, llvm::Record *> MacroAttr;
+  for (auto &Name : EntrypointNameList) {
+    if (G.FunctionSpecMap.find(Name) == G.FunctionSpecMap.end()) {
+      continue;
+    }
+    llvm::Record *FunctionSpec = G.FunctionSpecMap[Name];
+    auto Attributes = FunctionSpec->getValueAsListOfDefs("Attributes");
+    for (auto *Attr : Attributes) {
+      MacroAttr[Attr->getValueAsString("Macro")] = Attr;
+    }
+  }
+
+  auto GetStyle = [](llvm::Record *Instance) {
+    auto Style = Instance->getValueAsString("Style");
+    if (Style == "cxx11")
+      return AttributeStyle::Cxx11;
+    if (Style == "gnu")
+      return AttributeStyle::Gnu;
+    return AttributeStyle::Declspec;
+  };
+
+  for (auto &[Macro, Attr] : MacroAttr) {
+    auto Instances = Attr->getValueAsListOfDefs("Instances");
+    llvm::SmallVector<std::pair<AttributeStyle, llvm::Record *>> Styles;
+    std::transform(Instances.begin(), Instances.end(),
+                   std::back_inserter(Styles),
+                   [&](llvm::Record *Instance)
+                       -> std::pair<AttributeStyle, llvm::Record *> {
+                     auto Style = GetStyle(Instance);
+                     return {Style, Instance};
+                   });
+    // Effectively sort on the first field
+    std::sort(Styles.begin(), Styles.end());
+    for (auto &[Style, Instance] : Styles) {
+      if (Style == AttributeStyle::Cxx11) {
+        OS << "#if !defined(" << Macro << ") && defined(__cplusplus)\n";
+        OS << "#define " << Macro << " [[";
+        auto Namespace = Instance->getValueAsString("Namespace");
+        if (Namespace != "")
+          OS << Namespace << "::";
+        OS << Instance->getValueAsString("Attr") << "]]\n";
+        OS << "#endif\n";
+      }
+      if (Style == AttributeStyle::Gnu) {
+        OS << "#if !defined(" << Macro << ") && defined(__GNUC__)\n";
+        OS << "#define " << Macro << " __attribute__((";
+        OS << Instance->getValueAsString("Attr") << "))\n";
+        OS << "#endif\n";
+      }
+      if (Style == AttributeStyle::Declspec) {
+        OS << "#if !defined(" << Macro << ") && defined(_MSC_VER)\n";
+        OS << "#define " << Macro << " __declspec(";
+        OS << Instance->getValueAsString("Attr") << ")\n";
+        OS << "#endif\n";
+      }
+    }
+    OS << "#if !defined(" << Macro << ")\n";
+    OS << "#define " << Macro << '\n';
+    OS << "#endif\n";
+  }
+
+  if (!MacroAttr.empty())
+    OS << '\n';
+
   OS << "__BEGIN_C_DECLS\n\n";
   for (auto &Name : EntrypointNameList) {
     if (G.FunctionSpecMap.find(Name) == G.FunctionSpecMap.end()) {
@@ -103,61 +169,13 @@ void writeAPIFromIndex(APIIndexer &G,
     llvm::Record *RetValSpec = FunctionSpec->getValueAsDef("Return");
     llvm::Record *ReturnType = RetValSpec->getValueAsDef("ReturnType");
 
-    auto GetStyle = [](llvm::Record *Instance) {
-      auto Style = Instance->getValueAsString("Style");
-      if (Style == "gnu")
-        return AttributeStyle::Gnu;
-      if (Style == "c23")
-        return AttributeStyle::C23;
-      if (Style == "declspec")
-        return AttributeStyle::Declspec;
-      return AttributeStyle::None;
-    };
-
-    if (PreferedStyle != AttributeStyle::None) {
-      auto Attributes = FunctionSpec->getValueAsListOfDefs("Attributes");
-      llvm::SmallVector<llvm::Record *> Attrs;
-      for (auto *Attr : Attributes) {
-        auto Instances = Attr->getValueAsListOfDefs("Instances");
-        for (auto *Instance : Instances) {
-          if (GetStyle(Instance) == PreferedStyle) {
-            Attrs.push_back(Instance);
-          }
-        }
-      }
-
-      if (Attrs.size() != 0) {
-        if (PreferedStyle == AttributeStyle::Gnu) {
-          OS << "__attribute__((";
-          llvm::interleaveComma(Attrs, OS, [&](llvm::Record *Instance) {
-            OS << Instance->getValueAsString("Attr");
-          });
-          OS << ")) ";
-        }
-
-        if (PreferedStyle == AttributeStyle::C23) {
-          OS << "__attribute__((";
-          llvm::interleaveComma(Attrs, OS, [&](llvm::Record *Instance) {
-            auto Namespace = Instance->getValueAsString("Namespace");
-            if (Namespace != "")
-              OS << Namespace << "::";
-            OS << Instance->getValueAsString("Attr");
-          });
-          OS << ")) ";
-        }
-
-        if (PreferedStyle == AttributeStyle::Declspec) {
-          OS << "__declspec(";
-          llvm::interleave(
-              Attrs.begin(), Attrs.end(),
-              [&](llvm::Record *Instance) {
-                OS << Instance->getValueAsString("Attr");
-              },
-              [&]() { OS << ' '; });
-          OS << ") ";
-        }
-      }
-    }
+    auto Attributes = FunctionSpec->getValueAsListOfDefs("Attributes");
+    llvm::interleave(
+        Attributes.begin(), Attributes.end(),
+        [&](llvm::Record *Attr) { OS << Attr->getValueAsString("Macro"); },
+        [&]() { OS << ' '; });
+    if (!Attributes.empty())
+      OS << ' ';
 
     OS << G.getTypeAsString(ReturnType) << " " << Name << "(";
 
@@ -181,6 +199,10 @@ void writeAPIFromIndex(APIIndexer &G,
     OS << "extern " << Type << " " << Name << ";\n";
   }
   OS << "__END_C_DECLS\n";
+
+  // undef the macros
+  for (auto &[Macro, Attr] : MacroAttr)
+    OS << "\n#undef " << Macro << '\n';
 }
 
 void writePublicAPI(llvm::raw_ostream &OS, llvm::RecordKeeper &Records) {}
@@ -191,24 +213,12 @@ void PublicAPICommand::run(llvm::raw_ostream &OS, const ArgVector &Args,
                            llvm::StringRef StdHeader,
                            llvm::RecordKeeper &Records,
                            const Command::ErrorReporter &Reporter) const {
-  if (Args.size() > 1) {
-    Reporter.printFatalError(
-        "public_api command does not take more than one arguments.");
-  }
-
-  AttributeStyle PreferedStyle = AttributeStyle::Gnu;
-
-  for (auto &arg : Args) {
-    if (arg == "prefer-c23-attributes") {
-      PreferedStyle = AttributeStyle::C23;
-    }
-    if (arg == "prefer-no-attributes") {
-      PreferedStyle = AttributeStyle::None;
-    }
+  if (Args.size() != 0) {
+    Reporter.printFatalError("public_api command does not take any arguments.");
   }
 
   APIIndexer G(StdHeader, Records);
-  writeAPIFromIndex(G, EntrypointNameList, OS, PreferedStyle);
+  writeAPIFromIndex(G, EntrypointNameList, OS);
 }
 
 } // namespace llvm_libc
diff --git a/libc/utils/HdrGen/PublicAPICommand.h b/libc/utils/HdrGen/PublicAPICommand.h
index 035e1c71f4f12..72401cfe068e3 100644
--- a/libc/utils/HdrGen/PublicAPICommand.h
+++ b/libc/utils/HdrGen/PublicAPICommand.h
@@ -25,7 +25,7 @@ class RecordKeeper;
 
 namespace llvm_libc {
 
-enum class AttributeStyle { None, Gnu, C23, Declspec };
+enum class AttributeStyle { Cxx11 = 0, Gnu = 1, Declspec = 2 };
 
 class PublicAPICommand : public Command {
 private:

>From 61812478bc205e3566ae19971467ae4cee398202 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Tue, 30 Jan 2024 09:38:49 -0500
Subject: [PATCH 3/8] add checks for namespaces

---
 libc/utils/HdrGen/PublicAPICommand.cpp | 33 ++++++++++++++++++++++----
 libc/utils/HdrGen/PublicAPICommand.h   |  1 +
 2 files changed, 29 insertions(+), 5 deletions(-)

diff --git a/libc/utils/HdrGen/PublicAPICommand.cpp b/libc/utils/HdrGen/PublicAPICommand.cpp
index d24637db0f788..8ddb2ea5eaf90 100644
--- a/libc/utils/HdrGen/PublicAPICommand.cpp
+++ b/libc/utils/HdrGen/PublicAPICommand.cpp
@@ -111,6 +111,17 @@ void writeAPIFromIndex(APIIndexer &G,
     return AttributeStyle::Declspec;
   };
 
+  auto GetNamespace = [](llvm::Record *Instance) {
+    auto Namespace = Instance->getValueAsString("Namespace");
+    // Empty namespace is likely to be most standard-compliant.
+    if (Namespace == "")
+      return AttributeNamespace::None;
+    // Dispatch clang version before gnu version.
+    if (Namespace == "clang")
+      return AttributeNamespace::Clang;
+    return AttributeNamespace::Gnu;
+  };
+
   for (auto &[Macro, Attr] : MacroAttr) {
     auto Instances = Attr->getValueAsListOfDefs("Instances");
     llvm::SmallVector<std::pair<AttributeStyle, llvm::Record *>> Styles;
@@ -122,14 +133,26 @@ void writeAPIFromIndex(APIIndexer &G,
                      return {Style, Instance};
                    });
     // Effectively sort on the first field
-    std::sort(Styles.begin(), Styles.end());
+    std::sort(Styles.begin(), Styles.end(), [&](auto &a, auto &b) {
+      if (a.first == AttributeStyle::Cxx11 && b.first == AttributeStyle::Cxx11)
+        return GetNamespace(a.second) < GetNamespace(b.second);
+      return a.first < b.first;
+    });
     for (auto &[Style, Instance] : Styles) {
       if (Style == AttributeStyle::Cxx11) {
-        OS << "#if !defined(" << Macro << ") && defined(__cplusplus)\n";
+        OS << "#if !defined(" << Macro << ") && defined(__cplusplus)";
+        auto Namespace = GetNamespace(Instance);
+        if (Namespace == AttributeNamespace::Clang)
+          OS << " && defined(__clang__)\n";
+        else if (Namespace == AttributeNamespace::Gnu)
+          OS << " && defined(__GNUC__)\n";
+        else
+          OS << '\n';
         OS << "#define " << Macro << " [[";
-        auto Namespace = Instance->getValueAsString("Namespace");
-        if (Namespace != "")
-          OS << Namespace << "::";
+        if (Namespace == AttributeNamespace::Clang)
+          OS << "clang::";
+        else if (Namespace == AttributeNamespace::Gnu)
+          OS << "gnu::";
         OS << Instance->getValueAsString("Attr") << "]]\n";
         OS << "#endif\n";
       }
diff --git a/libc/utils/HdrGen/PublicAPICommand.h b/libc/utils/HdrGen/PublicAPICommand.h
index 72401cfe068e3..41aa53e51c326 100644
--- a/libc/utils/HdrGen/PublicAPICommand.h
+++ b/libc/utils/HdrGen/PublicAPICommand.h
@@ -26,6 +26,7 @@ class RecordKeeper;
 namespace llvm_libc {
 
 enum class AttributeStyle { Cxx11 = 0, Gnu = 1, Declspec = 2 };
+enum class AttributeNamespace { None = 0, Clang = 1, Gnu = 2 };
 
 class PublicAPICommand : public Command {
 private:

>From 0ba87950b15a38bd24073868d67a998166d86bd5 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Tue, 30 Jan 2024 09:40:07 -0500
Subject: [PATCH 4/8] use empty() instead of literal

---
 libc/utils/HdrGen/PublicAPICommand.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libc/utils/HdrGen/PublicAPICommand.cpp b/libc/utils/HdrGen/PublicAPICommand.cpp
index 8ddb2ea5eaf90..799be24ee0d8c 100644
--- a/libc/utils/HdrGen/PublicAPICommand.cpp
+++ b/libc/utils/HdrGen/PublicAPICommand.cpp
@@ -114,7 +114,7 @@ void writeAPIFromIndex(APIIndexer &G,
   auto GetNamespace = [](llvm::Record *Instance) {
     auto Namespace = Instance->getValueAsString("Namespace");
     // Empty namespace is likely to be most standard-compliant.
-    if (Namespace == "")
+    if (Namespace.empty())
       return AttributeNamespace::None;
     // Dispatch clang version before gnu version.
     if (Namespace == "clang")

>From 69771d89bc9f4f3c78f70dbb1124d75523ae86a0 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Wed, 31 Jan 2024 09:28:33 -0500
Subject: [PATCH 5/8] adjust include style

---
 libc/utils/HdrGen/PublicAPICommand.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libc/utils/HdrGen/PublicAPICommand.cpp b/libc/utils/HdrGen/PublicAPICommand.cpp
index 799be24ee0d8c..4b915a900969c 100644
--- a/libc/utils/HdrGen/PublicAPICommand.cpp
+++ b/libc/utils/HdrGen/PublicAPICommand.cpp
@@ -10,13 +10,13 @@
 
 #include "utils/LibcTableGenUtil/APIIndexer.h"
 
+#include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/SourceMgr.h"
 #include "llvm/TableGen/Record.h"
 #include <algorithm>
-#include <llvm/ADT/STLExtras.h>
 
 // Text blocks for macro definitions and type decls can be indented to
 // suit the surrounding tablegen listing. We need to dedent such blocks

>From fba67bd12d34acc47b1fd4879d80803a25e2e873 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Wed, 31 Jan 2024 09:42:50 -0500
Subject: [PATCH 6/8] add comments

---
 libc/spec/spec.td | 25 ++++++++++++++-----------
 1 file changed, 14 insertions(+), 11 deletions(-)

diff --git a/libc/spec/spec.td b/libc/spec/spec.td
index a14668bd15e53..10b7f41d4b7bb 100644
--- a/libc/spec/spec.td
+++ b/libc/spec/spec.td
@@ -170,20 +170,23 @@ class ArgSpec<Type type, list<Annotation> annotations = [], string name = ""> {
   string Name = name;
 }
 
-class FunctionAttr {}
-class GnuFunctionAttr<string attr> : FunctionAttr {
-  string Attr = attr;
-  string Style = "gnu";
-}
-class Cxx11FunctionAttr<string attr, string namespace> : FunctionAttr {
+// The following classes are used to describe function attributes.
+// In the future, we may consider supporting parameter attributes as well.
+// https://clang.llvm.org/docs/AttributeReference.html
+class FunctionAttr<string style, string attr> {
   string Attr = attr;
+  // The style of the attribute, e.g. "gnu", "cxx11", "declspec".
+  // - "gnu" is for GNU-style attributes: __attribute__((...))
+  // - "cxx11" is for C++11-style attributes: [[...]]
+  // - "declspec" is for Microsoft-style attributes: __declspec(...)
+  string Style = style;
+}
+class GnuFunctionAttr<string attr> : FunctionAttr<"gnu", attr> {}
+class Cxx11FunctionAttr<string attr, string namespace> : FunctionAttr<"cxx11", attr> {
+  // The namespace of the attribute, e.g. "gnu" or "clang".
   string Namespace = namespace;
-  string Style = "cxx11";
-}
-class DeclspecFunctionAttr<string attr> : FunctionAttr {
-  string Attr = attr;
-  string Style = "declspec";
 }
+class DeclspecFunctionAttr<string attr> : FunctionAttr<"declspec", attr> {}
 class FunctionAttrSpec<string macro, list<FunctionAttr> instances> {
   list<FunctionAttr> Instances = instances;
   string Macro = macro;

>From ff22577bb32c98d923f8a6134ba92422221ac05c Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Wed, 31 Jan 2024 10:22:53 -0500
Subject: [PATCH 7/8] refactor

---
 libc/utils/HdrGen/PublicAPICommand.cpp | 198 +++++++++++++++----------
 1 file changed, 117 insertions(+), 81 deletions(-)

diff --git a/libc/utils/HdrGen/PublicAPICommand.cpp b/libc/utils/HdrGen/PublicAPICommand.cpp
index 4b915a900969c..192c941d965ea 100644
--- a/libc/utils/HdrGen/PublicAPICommand.cpp
+++ b/libc/utils/HdrGen/PublicAPICommand.cpp
@@ -14,9 +14,11 @@
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSwitch.h"
 #include "llvm/Support/SourceMgr.h"
 #include "llvm/TableGen/Record.h"
 #include <algorithm>
+#include <vector>
 
 // Text blocks for macro definitions and type decls can be indented to
 // suit the surrounding tablegen listing. We need to dedent such blocks
@@ -49,99 +51,74 @@ static std::string getTypeHdrName(const std::string &Name) {
 
 namespace llvm_libc {
 
-void writeAPIFromIndex(APIIndexer &G,
-                       std::vector<std::string> EntrypointNameList,
-                       llvm::raw_ostream &OS) {
-  for (auto &Pair : G.MacroDefsMap) {
-    const std::string &Name = Pair.first;
-    if (G.MacroSpecMap.find(Name) == G.MacroSpecMap.end())
-      llvm::PrintFatalError(Name + " not found in any standard spec.\n");
-
-    llvm::Record *MacroDef = Pair.second;
-    dedentAndWrite(MacroDef->getValueAsString("Defn"), OS);
-
-    OS << '\n';
-  }
-
-  for (auto &TypeName : G.RequiredTypes) {
-    if (G.TypeSpecMap.find(TypeName) == G.TypeSpecMap.end())
-      llvm::PrintFatalError(TypeName + " not found in any standard spec.\n");
-    OS << "#include <llvm-libc-types/" << getTypeHdrName(TypeName) << ".h>\n";
-  }
-  OS << '\n';
+static AttributeStyle getAttributeStyle(llvm::Record *Instance) {
+  llvm::StringRef Style = Instance->getValueAsString("Style");
+  return llvm::StringSwitch<AttributeStyle>(Style)
+      .Case("cxx11", AttributeStyle::Cxx11)
+      .Case("gnu", AttributeStyle::Gnu)
+      .Case("declspec", AttributeStyle::Declspec)
+      .Default(AttributeStyle::Gnu);
+}
 
-  if (G.Enumerations.size() != 0)
-    OS << "enum {" << '\n';
-  for (const auto &Name : G.Enumerations) {
-    if (G.EnumerationSpecMap.find(Name) == G.EnumerationSpecMap.end())
-      llvm::PrintFatalError(
-          Name + " is not listed as an enumeration in any standard spec.\n");
+static AttributeNamespace getAttributeNamespace(llvm::Record *Instance) {
+  llvm::StringRef Namespace = Instance->getValueAsString("Namespace");
+  return llvm::StringSwitch<AttributeNamespace>(Namespace)
+      .Case("clang", AttributeNamespace::Clang)
+      .Case("gnu", AttributeNamespace::Gnu)
+      .Default(AttributeNamespace::None);
+}
 
-    llvm::Record *EnumerationSpec = G.EnumerationSpecMap[Name];
-    OS << "  " << EnumerationSpec->getValueAsString("Name");
-    auto Value = EnumerationSpec->getValueAsString("Value");
-    if (Value == "__default__") {
-      OS << ",\n";
-    } else {
-      OS << " = " << Value << ",\n";
-    }
-  }
-  if (G.Enumerations.size() != 0)
-    OS << "};\n\n";
+using AttributeMap = llvm::DenseMap<llvm::StringRef, llvm::Record *>;
 
-  // declare macros for attributes
+template <class SpecMap, class FuncList>
+static AttributeMap collectAttributeMacros(const SpecMap &Spec,
+                                           const FuncList &Funcs) {
   llvm::DenseMap<llvm::StringRef, llvm::Record *> MacroAttr;
-  for (auto &Name : EntrypointNameList) {
-    if (G.FunctionSpecMap.find(Name) == G.FunctionSpecMap.end()) {
+  for (const auto &Name : Funcs) {
+    if (!Spec.count(Name))
       continue;
-    }
-    llvm::Record *FunctionSpec = G.FunctionSpecMap[Name];
-    auto Attributes = FunctionSpec->getValueAsListOfDefs("Attributes");
-    for (auto *Attr : Attributes) {
+
+    llvm::Record *FunctionSpec = Spec.at(Name);
+    std::vector<llvm::Record *> Attributes =
+        FunctionSpec->getValueAsListOfDefs("Attributes");
+    for (llvm::Record *Attr : Attributes)
       MacroAttr[Attr->getValueAsString("Macro")] = Attr;
-    }
   }
+  return MacroAttr;
+}
 
-  auto GetStyle = [](llvm::Record *Instance) {
-    auto Style = Instance->getValueAsString("Style");
-    if (Style == "cxx11")
-      return AttributeStyle::Cxx11;
-    if (Style == "gnu")
-      return AttributeStyle::Gnu;
-    return AttributeStyle::Declspec;
-  };
-
-  auto GetNamespace = [](llvm::Record *Instance) {
-    auto Namespace = Instance->getValueAsString("Namespace");
-    // Empty namespace is likely to be most standard-compliant.
-    if (Namespace.empty())
-      return AttributeNamespace::None;
-    // Dispatch clang version before gnu version.
-    if (Namespace == "clang")
-      return AttributeNamespace::Clang;
-    return AttributeNamespace::Gnu;
-  };
-
+static void emitAttributeMacroDecls(const AttributeMap &MacroAttr,
+                                    llvm::raw_ostream &OS) {
   for (auto &[Macro, Attr] : MacroAttr) {
-    auto Instances = Attr->getValueAsListOfDefs("Instances");
+    std::vector<llvm::Record *> Instances =
+        Attr->getValueAsListOfDefs("Instances");
     llvm::SmallVector<std::pair<AttributeStyle, llvm::Record *>> Styles;
     std::transform(Instances.begin(), Instances.end(),
                    std::back_inserter(Styles),
                    [&](llvm::Record *Instance)
                        -> std::pair<AttributeStyle, llvm::Record *> {
-                     auto Style = GetStyle(Instance);
+                     auto Style = getAttributeStyle(Instance);
                      return {Style, Instance};
                    });
-    // Effectively sort on the first field
+    // 1. If __cplusplus is defined and cxx11 style is provided, define the
+    // macro using cxx11 version with the following priority:
+    //    1a. If the attribute is a clang attribute, check for __clang__.
+    //    2b. If the attribute is a gnu attribute, check for __GNUC__.
+    // 2. Otherwise, if __GNUC__ is defined and gnu
+    // style is provided, define the macro using gnu version;
+    // 3. Otherwise, if _MSC_VER is defined and __declspec is provided, define
+    // the macro using __declspec version;
+    // 4. Fallback to empty macro.
     std::sort(Styles.begin(), Styles.end(), [&](auto &a, auto &b) {
       if (a.first == AttributeStyle::Cxx11 && b.first == AttributeStyle::Cxx11)
-        return GetNamespace(a.second) < GetNamespace(b.second);
+        return getAttributeNamespace(a.second) <
+               getAttributeNamespace(b.second);
       return a.first < b.first;
     });
     for (auto &[Style, Instance] : Styles) {
       if (Style == AttributeStyle::Cxx11) {
         OS << "#if !defined(" << Macro << ") && defined(__cplusplus)";
-        auto Namespace = GetNamespace(Instance);
+        AttributeNamespace Namespace = getAttributeNamespace(Instance);
         if (Namespace == AttributeNamespace::Clang)
           OS << " && defined(__clang__)\n";
         else if (Namespace == AttributeNamespace::Gnu)
@@ -176,6 +153,72 @@ void writeAPIFromIndex(APIIndexer &G,
 
   if (!MacroAttr.empty())
     OS << '\n';
+}
+
+static void emitAttributeMacroForFunction(const llvm::Record *FunctionSpec,
+                                          llvm::raw_ostream &OS) {
+  std::vector<llvm::Record *> Attributes =
+      FunctionSpec->getValueAsListOfDefs("Attributes");
+  llvm::interleave(
+      Attributes.begin(), Attributes.end(),
+      [&](llvm::Record *Attr) { OS << Attr->getValueAsString("Macro"); },
+      [&]() { OS << ' '; });
+  if (!Attributes.empty())
+    OS << ' ';
+}
+
+static void emitUndefsForAttributeMacros(const AttributeMap &MacroAttr,
+                                         llvm::raw_ostream &OS) {
+  if (!MacroAttr.empty())
+    OS << '\n';
+  for (auto &[Macro, Attr] : MacroAttr)
+    OS << "#undef " << Macro << '\n';
+}
+
+static void writeAPIFromIndex(APIIndexer &G,
+                              std::vector<std::string> EntrypointNameList,
+                              llvm::raw_ostream &OS) {
+  for (auto &Pair : G.MacroDefsMap) {
+    const std::string &Name = Pair.first;
+    if (G.MacroSpecMap.find(Name) == G.MacroSpecMap.end())
+      llvm::PrintFatalError(Name + " not found in any standard spec.\n");
+
+    llvm::Record *MacroDef = Pair.second;
+    dedentAndWrite(MacroDef->getValueAsString("Defn"), OS);
+
+    OS << '\n';
+  }
+
+  for (auto &TypeName : G.RequiredTypes) {
+    if (G.TypeSpecMap.find(TypeName) == G.TypeSpecMap.end())
+      llvm::PrintFatalError(TypeName + " not found in any standard spec.\n");
+    OS << "#include <llvm-libc-types/" << getTypeHdrName(TypeName) << ".h>\n";
+  }
+  OS << '\n';
+
+  if (G.Enumerations.size() != 0)
+    OS << "enum {" << '\n';
+  for (const auto &Name : G.Enumerations) {
+    if (!G.EnumerationSpecMap.count(Name))
+      llvm::PrintFatalError(
+          Name + " is not listed as an enumeration in any standard spec.\n");
+
+    llvm::Record *EnumerationSpec = G.EnumerationSpecMap[Name];
+    OS << "  " << EnumerationSpec->getValueAsString("Name");
+    auto Value = EnumerationSpec->getValueAsString("Value");
+    if (Value == "__default__") {
+      OS << ",\n";
+    } else {
+      OS << " = " << Value << ",\n";
+    }
+  }
+  if (G.Enumerations.size() != 0)
+    OS << "};\n\n";
+
+  // Collect and declare macros for attributes
+  AttributeMap MacroAttr =
+      collectAttributeMacros(G.FunctionSpecMap, EntrypointNameList);
+  emitAttributeMacroDecls(MacroAttr, OS);
 
   OS << "__BEGIN_C_DECLS\n\n";
   for (auto &Name : EntrypointNameList) {
@@ -192,14 +235,8 @@ void writeAPIFromIndex(APIIndexer &G,
     llvm::Record *RetValSpec = FunctionSpec->getValueAsDef("Return");
     llvm::Record *ReturnType = RetValSpec->getValueAsDef("ReturnType");
 
-    auto Attributes = FunctionSpec->getValueAsListOfDefs("Attributes");
-    llvm::interleave(
-        Attributes.begin(), Attributes.end(),
-        [&](llvm::Record *Attr) { OS << Attr->getValueAsString("Macro"); },
-        [&]() { OS << ' '; });
-    if (!Attributes.empty())
-      OS << ' ';
-
+    // Emit attribute macros for the function. Space is automatically added.
+    emitAttributeMacroForFunction(FunctionSpec, OS);
     OS << G.getTypeAsString(ReturnType) << " " << Name << "(";
 
     auto ArgsList = FunctionSpec->getValueAsListOfDefs("Args");
@@ -223,9 +260,8 @@ void writeAPIFromIndex(APIIndexer &G,
   }
   OS << "__END_C_DECLS\n";
 
-  // undef the macros
-  for (auto &[Macro, Attr] : MacroAttr)
-    OS << "\n#undef " << Macro << '\n';
+  // Undef file-level attribute macros.
+  emitUndefsForAttributeMacros(MacroAttr, OS);
 }
 
 void writePublicAPI(llvm::raw_ostream &OS, llvm::RecordKeeper &Records) {}

>From c3138d0b1a66f92c47f4a83789d35fd36aa74f9a Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Mon, 5 Feb 2024 09:32:15 -0500
Subject: [PATCH 8/8] more clean up

---
 libc/utils/HdrGen/PublicAPICommand.cpp | 37 ++++++++++++++------------
 1 file changed, 20 insertions(+), 17 deletions(-)

diff --git a/libc/utils/HdrGen/PublicAPICommand.cpp b/libc/utils/HdrGen/PublicAPICommand.cpp
index 192c941d965ea..4a49d715b0583 100644
--- a/libc/utils/HdrGen/PublicAPICommand.cpp
+++ b/libc/utils/HdrGen/PublicAPICommand.cpp
@@ -75,10 +75,11 @@ static AttributeMap collectAttributeMacros(const SpecMap &Spec,
                                            const FuncList &Funcs) {
   llvm::DenseMap<llvm::StringRef, llvm::Record *> MacroAttr;
   for (const auto &Name : Funcs) {
-    if (!Spec.count(Name))
+    auto Iter = Spec.find(Name);
+    if (Iter == Spec.end())
       continue;
 
-    llvm::Record *FunctionSpec = Spec.at(Name);
+    llvm::Record *FunctionSpec = Iter->second;
     std::vector<llvm::Record *> Attributes =
         FunctionSpec->getValueAsListOfDefs("Attributes");
     for (llvm::Record *Attr : Attributes)
@@ -180,7 +181,7 @@ static void writeAPIFromIndex(APIIndexer &G,
                               llvm::raw_ostream &OS) {
   for (auto &Pair : G.MacroDefsMap) {
     const std::string &Name = Pair.first;
-    if (G.MacroSpecMap.find(Name) == G.MacroSpecMap.end())
+    if (!G.MacroSpecMap.count(Name))
       llvm::PrintFatalError(Name + " not found in any standard spec.\n");
 
     llvm::Record *MacroDef = Pair.second;
@@ -190,7 +191,7 @@ static void writeAPIFromIndex(APIIndexer &G,
   }
 
   for (auto &TypeName : G.RequiredTypes) {
-    if (G.TypeSpecMap.find(TypeName) == G.TypeSpecMap.end())
+    if (!G.TypeSpecMap.count(TypeName))
       llvm::PrintFatalError(TypeName + " not found in any standard spec.\n");
     OS << "#include <llvm-libc-types/" << getTypeHdrName(TypeName) << ".h>\n";
   }
@@ -222,16 +223,18 @@ static void writeAPIFromIndex(APIIndexer &G,
 
   OS << "__BEGIN_C_DECLS\n\n";
   for (auto &Name : EntrypointNameList) {
-    if (G.FunctionSpecMap.find(Name) == G.FunctionSpecMap.end()) {
-      continue; // Functions that aren't in this header file are skipped as
-                // opposed to erroring out because the list of functions being
-                // iterated over is the complete list of functions with
-                // entrypoints. Thus this is filtering out the functions that
-                // don't go to this header file, whereas the other, similar
-                // conditionals above are more of a sanity check.
-    }
+    auto Iter = G.FunctionSpecMap.find(Name);
+
+    // Functions that aren't in this header file are skipped as
+    // opposed to erroring out because the list of functions being
+    // iterated over is the complete list of functions with
+    // entrypoints. Thus this is filtering out the functions that
+    // don't go to this header file, whereas the other, similar
+    // conditionals above are more of a sanity check.
+    if (Iter == G.FunctionSpecMap.end())
+      continue;
 
-    llvm::Record *FunctionSpec = G.FunctionSpecMap[Name];
+    llvm::Record *FunctionSpec = Iter->second;
     llvm::Record *RetValSpec = FunctionSpec->getValueAsDef("Return");
     llvm::Record *ReturnType = RetValSpec->getValueAsDef("ReturnType");
 
@@ -252,9 +255,10 @@ static void writeAPIFromIndex(APIIndexer &G,
 
   // Make another pass over entrypoints to emit object declarations.
   for (const auto &Name : EntrypointNameList) {
-    if (G.ObjectSpecMap.find(Name) == G.ObjectSpecMap.end())
+    auto Iter = G.ObjectSpecMap.find(Name);
+    if (Iter == G.ObjectSpecMap.end())
       continue;
-    llvm::Record *ObjectSpec = G.ObjectSpecMap[Name];
+    llvm::Record *ObjectSpec = Iter->second;
     auto Type = ObjectSpec->getValueAsString("Type");
     OS << "extern " << Type << " " << Name << ";\n";
   }
@@ -272,9 +276,8 @@ void PublicAPICommand::run(llvm::raw_ostream &OS, const ArgVector &Args,
                            llvm::StringRef StdHeader,
                            llvm::RecordKeeper &Records,
                            const Command::ErrorReporter &Reporter) const {
-  if (Args.size() != 0) {
+  if (Args.size() != 0)
     Reporter.printFatalError("public_api command does not take any arguments.");
-  }
 
   APIIndexer G(StdHeader, Records);
   writeAPIFromIndex(G, EntrypointNameList, OS);



More information about the libc-commits mailing list