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

Nick Desaulniers via libc-commits libc-commits at lists.llvm.org
Tue Jan 30 08:48:53 PST 2024


================
@@ -87,6 +89,94 @@ 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;
+  };
+
+  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;
+  };
+
+  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(), [&](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)";
+        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 << " [[";
+        if (Namespace == AttributeNamespace::Clang)
+          OS << "clang::";
+        else if (Namespace == AttributeNamespace::Gnu)
+          OS << "gnu::";
+        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";
----------------
nickdesaulniers wrote:

I'd be curious to see examples of the resulting output.

I love function attributes, but using them portably is somewhat a PITA.

`__has_attribute` helps a lot, if your compiler supports it.  If not, you have to rely on compiler version checks (or omit the attributes or explicitly not support those compiler versions).

So if we expose these on the client facing headers, we need to ensure that the result works with a wide range of compiler versions.  Perhaps wider than we require to build llvm libc.  I don't think we make the distinction in our docs, but we probably should.

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


More information about the libc-commits mailing list