1//===-- Implementation of the main header generation class ----------------===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8
9#include "Generator.h"
10
11#include "IncludeFileCommand.h"
12#include "PublicAPICommand.h"
13#include "utils/LibcTableGenUtil/APIIndexer.h"
14
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"
19
20#include <cstdlib>
21#include <memory>
22
23static const char CommandPrefix[] = "%%";
24static const size_t CommandPrefixSize = llvm::StringRef(CommandPrefix).size();
25
26static const char CommentPrefix[] = "<!>";
27
28static const char ParamNamePrefix[] = "${";
29static const size_t ParamNamePrefixSize =
30 llvm::StringRef(ParamNamePrefix).size();
31static const char ParamNameSuffix[] = "}";
32static const size_t ParamNameSuffixSize =
33 llvm::StringRef(ParamNameSuffix).size();
34
35namespace llvm_libc {
36
37Command *Generator::getCommandHandler(llvm::StringRef CommandName) {
38 if (CommandName == IncludeFileCommand::Name) {
39 if (!IncludeFileCmd)
40 IncludeFileCmd = std::make_unique<IncludeFileCommand>();
41 return IncludeFileCmd.get();
42 } else if (CommandName == PublicAPICommand::Name) {
43 if (!PublicAPICmd)
44 PublicAPICmd = std::make_unique<PublicAPICommand>(EntrypointNameList);
45 return PublicAPICmd.get();
46 } else {
47 return nullptr;
48 }
49}
50
51void 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
54 return;
55 }
56
57 ArgStr.split(Args, ",");
58 for (llvm::StringRef &A : Args) {
59 A = A.trim(' ');
60 if (A.starts_with(ParamNamePrefix) && A.ends_with(ParamNameSuffix)) {
61 A = A.drop_front(ParamNamePrefixSize).drop_back(ParamNameSuffixSize);
62 A = ArgMap[std::string(A)];
63 }
64 }
65}
66
67void Generator::generate(llvm::raw_ostream &OS, llvm::RecordKeeper &Records) {
68 auto DefFileBuffer = llvm::MemoryBuffer::getFile(HeaderDefFile);
69 if (!DefFileBuffer) {
70 llvm::errs() << "Unable to open " << HeaderDefFile << ".\n";
71 std::exit(status: 1);
72 }
73 llvm::SourceMgr SrcMgr;
74 unsigned DefFileID = SrcMgr.AddNewSourceBuffer(
75 std::move(DefFileBuffer.get()), llvm::SMLoc::getFromPointer(nullptr));
76
77 llvm::StringRef Content = SrcMgr.getMemoryBuffer(DefFileID)->getBuffer();
78 while (true) {
79 std::pair<llvm::StringRef, llvm::StringRef> P = Content.split('\n');
80 Content = P.second;
81
82 llvm::StringRef Line = P.first.trim(' ');
83 if (Line.starts_with(CommandPrefix)) {
84 Line = Line.drop_front(CommandPrefixSize);
85
86 P = Line.split("(");
87 // It's possible that we have windows line endings, so strip off the extra
88 // CR.
89 P.second = P.second.trim();
90 if (P.second.empty() || P.second[P.second.size() - 1] != ')') {
91 SrcMgr.PrintMessage(llvm::SMLoc::getFromPointer(P.second.data()),
92 llvm::SourceMgr::DK_Error,
93 "Command argument list should begin with '(' "
94 "and end with ')'.");
95 SrcMgr.PrintMessage(llvm::SMLoc::getFromPointer(P.second.data()),
96 llvm::SourceMgr::DK_Error, P.second.data());
97 SrcMgr.PrintMessage(llvm::SMLoc::getFromPointer(P.second.data()),
98 llvm::SourceMgr::DK_Error,
99 std::to_string(P.second.size()));
100 std::exit(status: 1);
101 }
102 llvm::StringRef CommandName = P.first;
103 Command *Cmd = getCommandHandler(CommandName);
104 if (Cmd == nullptr) {
105 SrcMgr.PrintMessage(llvm::SMLoc::getFromPointer(CommandName.data()),
106 llvm::SourceMgr::DK_Error,
107 "Unknown command '%%" + CommandName + "'.");
108 std::exit(status: 1);
109 }
110
111 llvm::StringRef ArgStr = P.second.drop_back(1);
112 ArgVector Args;
113 parseCommandArgs(ArgStr, Args);
114
115 Command::ErrorReporter Reporter(
116 llvm::SMLoc::getFromPointer(CommandName.data()), SrcMgr);
117 Cmd->run(OS, Args, StdHeader, Records, Reporter);
118 } else if (!Line.starts_with(CommentPrefix)) {
119 // There is no comment or command on this line so we just write it as is.
120 OS << P.first << "\n";
121 }
122
123 if (P.second.empty())
124 break;
125 }
126}
127
128void Generator::generateDecls(llvm::raw_ostream &OS,
129 llvm::RecordKeeper &Records) {
130
131 OS << "//===-- C standard declarations for " << StdHeader << " "
132 << std::string(80 - (42 + StdHeader.size()), '-') << "===//\n"
133 << "//\n"
134 << "// Part of the LLVM Project, under the Apache License v2.0 with LLVM "
135 "Exceptions.\n"
136 << "// See https://llvm.org/LICENSE.txt for license information.\n"
137 << "// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n"
138 << "//\n"
139 << "//"
140 "===-------------------------------------------------------------------"
141 "---===//\n\n";
142
143 std::string HeaderGuard(StdHeader.size(), '\0');
144 llvm::transform(StdHeader, HeaderGuard.begin(), [](const char C) -> char {
145 return !isalnum(C) ? '_' : llvm::toUpper(C);
146 });
147 OS << "#ifndef __LLVM_LIBC_DECLARATIONS_" << HeaderGuard << "\n"
148 << "#define __LLVM_LIBC_DECLARATIONS_" << HeaderGuard << "\n\n";
149
150 OS << "#ifndef __LIBC_ATTRS\n"
151 << "#define __LIBC_ATTRS\n"
152 << "#endif\n\n";
153
154 OS << "#ifdef __cplusplus\n"
155 << "extern \"C\" {\n"
156 << "#endif\n\n";
157
158 APIIndexer G(StdHeader, Records);
159 for (auto &Name : EntrypointNameList) {
160 // Filter out functions not exported by this header.
161 if (G.FunctionSpecMap.find(Name) == G.FunctionSpecMap.end())
162 continue;
163
164 llvm::Record *FunctionSpec = G.FunctionSpecMap[Name];
165 llvm::Record *RetValSpec = FunctionSpec->getValueAsDef("Return");
166 llvm::Record *ReturnType = RetValSpec->getValueAsDef("ReturnType");
167
168 OS << G.getTypeAsString(ReturnType) << " " << Name << "(";
169
170 auto ArgsList = FunctionSpec->getValueAsListOfDefs("Args");
171 for (size_t i = 0; i < ArgsList.size(); ++i) {
172 llvm::Record *ArgType = ArgsList[i]->getValueAsDef("ArgType");
173 OS << G.getTypeAsString(ArgType);
174 if (i < ArgsList.size() - 1)
175 OS << ", ";
176 }
177
178 OS << ") __LIBC_ATTRS;\n\n";
179 }
180
181 // Make another pass over entrypoints to emit object declarations.
182 for (const auto &Name : EntrypointNameList) {
183 if (G.ObjectSpecMap.find(Name) == G.ObjectSpecMap.end())
184 continue;
185 llvm::Record *ObjectSpec = G.ObjectSpecMap[Name];
186 auto Type = ObjectSpec->getValueAsString("Type");
187 OS << "extern " << Type << " " << Name << " __LIBC_ATTRS;\n";
188 }
189
190 // Emit a final newline if we emitted any object declarations.
191 if (llvm::any_of(EntrypointNameList, [&](const std::string &Name) {
192 return G.ObjectSpecMap.find(Name) != G.ObjectSpecMap.end();
193 }))
194 OS << "\n";
195
196 OS << "#ifdef __cplusplus\n"
197 << "}\n"
198 << "#endif\n\n";
199 OS << "#endif\n";
200}
201
202} // namespace llvm_libc
203

source code of libc/utils/HdrGen/Generator.cpp