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 | |
23 | static const char CommandPrefix[] = "%%" ; |
24 | static const size_t CommandPrefixSize = llvm::StringRef(CommandPrefix).size(); |
25 | |
26 | static const char [] = "<!>" ; |
27 | |
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(); |
34 | |
35 | namespace llvm_libc { |
36 | |
37 | Command *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 | |
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 |
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 | |
67 | void 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 | |
128 | void 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 (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 | |