1 //===-- Implementation of the main header generation class ----------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
11 #include "IncludeFileCommand.h"
12 #include "PublicAPICommand.h"
13 #include "utils/LibcTableGenUtil/APIIndexer.h"
15 #include "llvm/ADT/StringRef.h"
16 #include "llvm/Support/MemoryBuffer.h"
17 #include "llvm/Support/SourceMgr.h"
18 #include "llvm/Support/raw_ostream.h"
23 static const char CommandPrefix
[] = "%%";
24 static const size_t CommandPrefixSize
= llvm::StringRef(CommandPrefix
).size();
26 static const char CommentPrefix
[] = "<!>";
28 static const char ParamNamePrefix
[] = "${";
29 static const size_t ParamNamePrefixSize
=
30 llvm::StringRef(ParamNamePrefix
).size();
31 static const char ParamNameSuffix
[] = "}";
32 static const size_t ParamNameSuffixSize
=
33 llvm::StringRef(ParamNameSuffix
).size();
37 Command
*Generator::getCommandHandler(llvm::StringRef CommandName
) {
38 if (CommandName
== IncludeFileCommand::Name
) {
40 IncludeFileCmd
= std::make_unique
<IncludeFileCommand
>();
41 return IncludeFileCmd
.get();
42 } else if (CommandName
== PublicAPICommand::Name
) {
44 PublicAPICmd
= std::make_unique
<PublicAPICommand
>(EntrypointNameList
);
45 return PublicAPICmd
.get();
51 void Generator::parseCommandArgs(llvm::StringRef ArgStr
, ArgVector
&Args
) {
52 if (!ArgStr
.contains(',') && ArgStr
.trim(' ').trim('\t').size() == 0) {
53 // If it is just space between the parenthesis
57 ArgStr
.split(Args
, ",");
58 for (llvm::StringRef
&A
: Args
) {
60 if (A
.startswith(ParamNamePrefix
) && A
.endswith(ParamNameSuffix
)) {
61 A
= A
.drop_front(ParamNamePrefixSize
).drop_back(ParamNameSuffixSize
);
62 A
= ArgMap
[std::string(A
)];
67 void Generator::generate(llvm::raw_ostream
&OS
, llvm::RecordKeeper
&Records
) {
68 auto DefFileBuffer
= llvm::MemoryBuffer::getFile(HeaderDefFile
);
70 llvm::errs() << "Unable to open " << HeaderDefFile
<< ".\n";
73 llvm::SourceMgr SrcMgr
;
74 unsigned DefFileID
= SrcMgr
.AddNewSourceBuffer(
75 std::move(DefFileBuffer
.get()), llvm::SMLoc::getFromPointer(nullptr));
77 llvm::StringRef Content
= SrcMgr
.getMemoryBuffer(DefFileID
)->getBuffer();
79 std::pair
<llvm::StringRef
, llvm::StringRef
> P
= Content
.split('\n');
82 llvm::StringRef Line
= P
.first
.trim(' ');
83 if (Line
.startswith(CommandPrefix
)) {
84 Line
= Line
.drop_front(CommandPrefixSize
);
87 if (P
.second
.empty() || P
.second
[P
.second
.size() - 1] != ')') {
88 SrcMgr
.PrintMessage(llvm::SMLoc::getFromPointer(P
.second
.data()),
89 llvm::SourceMgr::DK_Error
,
90 "Command argument list should begin with '(' "
94 llvm::StringRef CommandName
= P
.first
;
95 Command
*Cmd
= getCommandHandler(CommandName
);
97 SrcMgr
.PrintMessage(llvm::SMLoc::getFromPointer(CommandName
.data()),
98 llvm::SourceMgr::DK_Error
,
99 "Unknown command '%%" + CommandName
+ "'.");
103 llvm::StringRef ArgStr
= P
.second
.drop_back(1);
105 parseCommandArgs(ArgStr
, Args
);
107 Command::ErrorReporter
Reporter(
108 llvm::SMLoc::getFromPointer(CommandName
.data()), SrcMgr
);
109 Cmd
->run(OS
, Args
, StdHeader
, Records
, Reporter
);
110 } else if (!Line
.startswith(CommentPrefix
)) {
111 // There is no comment or command on this line so we just write it as is.
112 OS
<< P
.first
<< "\n";
115 if (P
.second
.empty())
120 void Generator::generateDecls(llvm::raw_ostream
&OS
,
121 llvm::RecordKeeper
&Records
) {
123 OS
<< "//===-- C standard declarations for " << StdHeader
<< " "
124 << std::string(80 - (42 + StdHeader
.size()), '-') << "===//\n"
126 << "// Part of the LLVM Project, under the Apache License v2.0 with LLVM "
128 << "// See https://llvm.org/LICENSE.txt for license information.\n"
129 << "// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n"
132 "===-------------------------------------------------------------------"
135 std::string
HeaderGuard(StdHeader
.size(), '\0');
136 llvm::transform(StdHeader
, HeaderGuard
.begin(), [](const char C
) -> char {
137 return !isalnum(C
) ? '_' : llvm::toUpper(C
);
139 OS
<< "#ifndef __LLVM_LIBC_DECLARATIONS_" << HeaderGuard
<< "\n"
140 << "#define __LLVM_LIBC_DECLARATIONS_" << HeaderGuard
<< "\n\n";
142 OS
<< "#ifndef __LIBC_ATTRS\n"
143 << "#define __LIBC_ATTRS\n"
146 OS
<< "#ifdef __cplusplus\n"
147 << "extern \"C\" {\n"
150 APIIndexer
G(StdHeader
, Records
);
151 for (auto &Name
: EntrypointNameList
) {
152 // Filter out functions not exported by this header.
153 if (G
.FunctionSpecMap
.find(Name
) == G
.FunctionSpecMap
.end())
156 llvm::Record
*FunctionSpec
= G
.FunctionSpecMap
[Name
];
157 llvm::Record
*RetValSpec
= FunctionSpec
->getValueAsDef("Return");
158 llvm::Record
*ReturnType
= RetValSpec
->getValueAsDef("ReturnType");
160 OS
<< G
.getTypeAsString(ReturnType
) << " " << Name
<< "(";
162 auto ArgsList
= FunctionSpec
->getValueAsListOfDefs("Args");
163 for (size_t i
= 0; i
< ArgsList
.size(); ++i
) {
164 llvm::Record
*ArgType
= ArgsList
[i
]->getValueAsDef("ArgType");
165 OS
<< G
.getTypeAsString(ArgType
);
166 if (i
< ArgsList
.size() - 1)
170 OS
<< ") __LIBC_ATTRS;\n\n";
173 // Make another pass over entrypoints to emit object declarations.
174 for (const auto &Name
: EntrypointNameList
) {
175 if (G
.ObjectSpecMap
.find(Name
) == G
.ObjectSpecMap
.end())
177 llvm::Record
*ObjectSpec
= G
.ObjectSpecMap
[Name
];
178 auto Type
= ObjectSpec
->getValueAsString("Type");
179 OS
<< "extern " << Type
<< " " << Name
<< " __LIBC_ATTRS;\n";
182 // Emit a final newline if we emitted any object declarations.
183 if (llvm::any_of(EntrypointNameList
, [&](const std::string
&Name
) {
184 return G
.ObjectSpecMap
.find(Name
) != G
.ObjectSpecMap
.end();
188 OS
<< "#ifdef __cplusplus\n"
194 } // namespace llvm_libc