[libc-commits] [libc] [libc] add support for function level attributes (PR #79891)
Schrodinger ZHU Yifan via libc-commits
libc-commits at lists.llvm.org
Sun Feb 11 20:09:10 PST 2024
https://github.com/SchrodingerZhu updated https://github.com/llvm/llvm-project/pull/79891
>From 6c2ab9cc9f19d129bbeb29bf1a74d2a538cebf33 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] [libc][RFC] add support for function level attributes
---
libc/spec/spec.td | 25 +++-
libc/spec/stdc.td | 7 +-
libc/utils/HdrGen/PublicAPICommand.cpp | 179 ++++++++++++++++++++++---
libc/utils/HdrGen/PublicAPICommand.h | 3 +
4 files changed, 193 insertions(+), 21 deletions(-)
diff --git a/libc/spec/spec.td b/libc/spec/spec.td
index aebf4955382862..faac4446e1b0b3 100644
--- a/libc/spec/spec.td
+++ b/libc/spec/spec.td
@@ -170,10 +170,33 @@ class ArgSpec<Type type, list<Annotation> annotations = [], string name = ""> {
string Name = name;
}
-class FunctionSpec<string name, RetValSpec return, list<ArgSpec> args> {
+// 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;
+}
+class DeclspecFunctionAttr<string attr> : FunctionAttr<"declspec", attr> {}
+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 = []> {
string Name = name;
RetValSpec Return = return;
list<ArgSpec> Args = args;
+ list<FunctionAttrSpec> Attributes = attrs;
}
class GuardedFunctionSpec<string name, RetValSpec return, list<ArgSpec> args, string guard_macro> : FunctionSpec<name, return, args> {
diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td
index 011abbfa496533..66edd6df41c644 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>]>,
GuardedFunctionSpec<"ceilf128", RetValSpec<Float128Type>, [ArgSpec<Float128Type>], "LIBC_COMPILER_HAS_FLOAT128">,
- 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>]>,
GuardedFunctionSpec<"fabsf128", RetValSpec<Float128Type>, [ArgSpec<Float128Type>], "LIBC_COMPILER_HAS_FLOAT128">,
diff --git a/libc/utils/HdrGen/PublicAPICommand.cpp b/libc/utils/HdrGen/PublicAPICommand.cpp
index cf6984be74d1d4..01ec059748c5db 100644
--- a/libc/utils/HdrGen/PublicAPICommand.cpp
+++ b/libc/utils/HdrGen/PublicAPICommand.cpp
@@ -10,11 +10,15 @@
#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/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
@@ -47,12 +51,137 @@ static std::string getTypeHdrName(const std::string &Name) {
namespace llvm_libc {
-void writeAPIFromIndex(APIIndexer &G,
- std::vector<std::string> EntrypointNameList,
- llvm::raw_ostream &OS) {
+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);
+}
+
+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);
+}
+
+using AttributeMap = llvm::DenseMap<llvm::StringRef, llvm::Record *>;
+
+template <class SpecMap, class FuncList>
+static AttributeMap collectAttributeMacros(const SpecMap &Spec,
+ const FuncList &Funcs) {
+ llvm::DenseMap<llvm::StringRef, llvm::Record *> MacroAttr;
+ for (const auto &Name : Funcs) {
+ auto Iter = Spec.find(Name);
+ if (Iter == Spec.end())
+ continue;
+
+ llvm::Record *FunctionSpec = Iter->second;
+ std::vector<llvm::Record *> Attributes =
+ FunctionSpec->getValueAsListOfDefs("Attributes");
+ for (llvm::Record *Attr : Attributes)
+ MacroAttr[Attr->getValueAsString("Macro")] = Attr;
+ }
+ return MacroAttr;
+}
+
+static void emitAttributeMacroDecls(const AttributeMap &MacroAttr,
+ llvm::raw_ostream &OS) {
+ for (auto &[Macro, Attr] : MacroAttr) {
+ 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 = getAttributeStyle(Instance);
+ return {Style, Instance};
+ });
+ // 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 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)";
+ AttributeNamespace Namespace = getAttributeNamespace(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";
+ }
+
+ 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())
+ if (!G.MacroSpecMap.count(Name))
llvm::PrintFatalError(Name + " not found in any standard spec.\n");
llvm::Record *MacroDef = Pair.second;
@@ -62,7 +191,7 @@ 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";
}
@@ -71,7 +200,7 @@ void writeAPIFromIndex(APIIndexer &G,
if (G.Enumerations.size() != 0)
OS << "enum {" << '\n';
for (const auto &Name : G.Enumerations) {
- if (G.EnumerationSpecMap.find(Name) == G.EnumerationSpecMap.end())
+ if (!G.EnumerationSpecMap.count(Name))
llvm::PrintFatalError(
Name + " is not listed as an enumeration in any standard spec.\n");
@@ -87,18 +216,25 @@ void writeAPIFromIndex(APIIndexer &G,
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) {
- 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");
@@ -110,6 +246,8 @@ void writeAPIFromIndex(APIIndexer &G,
if (Guarded)
OS << "#ifdef " << FunctionSpec->getValueAsString("Guard") << "\n";
+ // Emit attribute macros for the function. Space is automatically added.
+ emitAttributeMacroForFunction(FunctionSpec, OS);
OS << G.getTypeAsString(ReturnType) << " " << Name << "(";
auto ArgsList = FunctionSpec->getValueAsListOfDefs("Args");
@@ -130,13 +268,17 @@ 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";
}
OS << "__END_C_DECLS\n";
+
+ // Undef file-level attribute macros.
+ emitUndefsForAttributeMacros(MacroAttr, OS);
}
void writePublicAPI(llvm::raw_ostream &OS, llvm::RecordKeeper &Records) {}
@@ -147,9 +289,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);
diff --git a/libc/utils/HdrGen/PublicAPICommand.h b/libc/utils/HdrGen/PublicAPICommand.h
index fb0a7a81cf277a..41aa53e51c3269 100644
--- a/libc/utils/HdrGen/PublicAPICommand.h
+++ b/libc/utils/HdrGen/PublicAPICommand.h
@@ -25,6 +25,9 @@ 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:
const std::vector<std::string> &EntrypointNameList;
More information about the libc-commits
mailing list