| //===-- Implementation of the main header generation class ----------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "Generator.h" |
| |
| #include "IncludeFileCommand.h" |
| #include "PublicAPICommand.h" |
| #include "utils/LibcTableGenUtil/APIIndexer.h" |
| |
| #include "llvm/ADT/StringRef.h" |
| #include "llvm/Support/MemoryBuffer.h" |
| #include "llvm/Support/SourceMgr.h" |
| #include "llvm/Support/raw_ostream.h" |
| |
| #include <cstdlib> |
| #include <memory> |
| |
| static const char CommandPrefix[] = "%%"; |
| static const size_t CommandPrefixSize = llvm::StringRef(CommandPrefix).size(); |
| |
| static const char CommentPrefix[] = "<!>"; |
| |
| static const char ParamNamePrefix[] = "${"; |
| static const size_t ParamNamePrefixSize = |
| llvm::StringRef(ParamNamePrefix).size(); |
| static const char ParamNameSuffix[] = "}"; |
| static const size_t ParamNameSuffixSize = |
| llvm::StringRef(ParamNameSuffix).size(); |
| |
| namespace llvm_libc { |
| |
| Command *Generator::getCommandHandler(llvm::StringRef CommandName) { |
| if (CommandName == IncludeFileCommand::Name) { |
| if (!IncludeFileCmd) |
| IncludeFileCmd = std::make_unique<IncludeFileCommand>(); |
| return IncludeFileCmd.get(); |
| } else if (CommandName == PublicAPICommand::Name) { |
| if (!PublicAPICmd) |
| PublicAPICmd = std::make_unique<PublicAPICommand>(EntrypointNameList); |
| return PublicAPICmd.get(); |
| } else { |
| return nullptr; |
| } |
| } |
| |
| void Generator::parseCommandArgs(llvm::StringRef ArgStr, ArgVector &Args) { |
| if (!ArgStr.contains(',') && ArgStr.trim(' ').trim('\t').size() == 0) { |
| // If it is just space between the parenthesis |
| return; |
| } |
| |
| ArgStr.split(Args, ","); |
| for (llvm::StringRef &A : Args) { |
| A = A.trim(' '); |
| if (A.starts_with(ParamNamePrefix) && A.ends_with(ParamNameSuffix)) { |
| A = A.drop_front(ParamNamePrefixSize).drop_back(ParamNameSuffixSize); |
| A = ArgMap[std::string(A)]; |
| } |
| } |
| } |
| |
| void Generator::generate(llvm::raw_ostream &OS, llvm::RecordKeeper &Records) { |
| auto DefFileBuffer = llvm::MemoryBuffer::getFile(HeaderDefFile); |
| if (!DefFileBuffer) { |
| llvm::errs() << "Unable to open " << HeaderDefFile << ".\n"; |
| std::exit(1); |
| } |
| llvm::SourceMgr SrcMgr; |
| unsigned DefFileID = SrcMgr.AddNewSourceBuffer( |
| std::move(DefFileBuffer.get()), llvm::SMLoc::getFromPointer(nullptr)); |
| |
| llvm::StringRef Content = SrcMgr.getMemoryBuffer(DefFileID)->getBuffer(); |
| while (true) { |
| std::pair<llvm::StringRef, llvm::StringRef> P = Content.split('\n'); |
| Content = P.second; |
| |
| llvm::StringRef Line = P.first.trim(' '); |
| if (Line.starts_with(CommandPrefix)) { |
| Line = Line.drop_front(CommandPrefixSize); |
| |
| P = Line.split("("); |
| // It's possible that we have windows line endings, so strip off the extra |
| // CR. |
| P.second = P.second.trim(); |
| if (P.second.empty() || P.second[P.second.size() - 1] != ')') { |
| SrcMgr.PrintMessage(llvm::SMLoc::getFromPointer(P.second.data()), |
| llvm::SourceMgr::DK_Error, |
| "Command argument list should begin with '(' " |
| "and end with ')'."); |
| SrcMgr.PrintMessage(llvm::SMLoc::getFromPointer(P.second.data()), |
| llvm::SourceMgr::DK_Error, P.second.data()); |
| SrcMgr.PrintMessage(llvm::SMLoc::getFromPointer(P.second.data()), |
| llvm::SourceMgr::DK_Error, |
| std::to_string(P.second.size())); |
| std::exit(1); |
| } |
| llvm::StringRef CommandName = P.first; |
| Command *Cmd = getCommandHandler(CommandName); |
| if (Cmd == nullptr) { |
| SrcMgr.PrintMessage(llvm::SMLoc::getFromPointer(CommandName.data()), |
| llvm::SourceMgr::DK_Error, |
| "Unknown command '%%" + CommandName + "'."); |
| std::exit(1); |
| } |
| |
| llvm::StringRef ArgStr = P.second.drop_back(1); |
| ArgVector Args; |
| parseCommandArgs(ArgStr, Args); |
| |
| Command::ErrorReporter Reporter( |
| llvm::SMLoc::getFromPointer(CommandName.data()), SrcMgr); |
| Cmd->run(OS, Args, StdHeader, Records, Reporter); |
| } else if (!Line.starts_with(CommentPrefix)) { |
| // There is no comment or command on this line so we just write it as is. |
| OS << P.first << "\n"; |
| } |
| |
| if (P.second.empty()) |
| break; |
| } |
| } |
| |
| void Generator::generateDecls(llvm::raw_ostream &OS, |
| llvm::RecordKeeper &Records) { |
| |
| OS << "//===-- C standard declarations for " << StdHeader << " " |
| << std::string(80 - (42 + StdHeader.size()), '-') << "===//\n" |
| << "//\n" |
| << "// Part of the LLVM Project, under the Apache License v2.0 with LLVM " |
| "Exceptions.\n" |
| << "// See https://llvm.org/LICENSE.txt for license information.\n" |
| << "// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n" |
| << "//\n" |
| << "//" |
| "===-------------------------------------------------------------------" |
| "---===//\n\n"; |
| |
| std::string HeaderGuard(StdHeader.size(), '\0'); |
| llvm::transform(StdHeader, HeaderGuard.begin(), [](const char C) -> char { |
| return !isalnum(C) ? '_' : llvm::toUpper(C); |
| }); |
| OS << "#ifndef __LLVM_LIBC_DECLARATIONS_" << HeaderGuard << "\n" |
| << "#define __LLVM_LIBC_DECLARATIONS_" << HeaderGuard << "\n\n"; |
| |
| OS << "#ifndef __LIBC_ATTRS\n" |
| << "#define __LIBC_ATTRS\n" |
| << "#endif\n\n"; |
| |
| OS << "#ifdef __cplusplus\n" |
| << "extern \"C\" {\n" |
| << "#endif\n\n"; |
| |
| APIIndexer G(StdHeader, Records); |
| for (auto &Name : EntrypointNameList) { |
| // Filter out functions not exported by this header. |
| if (G.FunctionSpecMap.find(Name) == G.FunctionSpecMap.end()) |
| continue; |
| |
| llvm::Record *FunctionSpec = G.FunctionSpecMap[Name]; |
| llvm::Record *RetValSpec = FunctionSpec->getValueAsDef("Return"); |
| llvm::Record *ReturnType = RetValSpec->getValueAsDef("ReturnType"); |
| |
| OS << G.getTypeAsString(ReturnType) << " " << Name << "("; |
| |
| auto ArgsList = FunctionSpec->getValueAsListOfDefs("Args"); |
| for (size_t i = 0; i < ArgsList.size(); ++i) { |
| llvm::Record *ArgType = ArgsList[i]->getValueAsDef("ArgType"); |
| OS << G.getTypeAsString(ArgType); |
| if (i < ArgsList.size() - 1) |
| OS << ", "; |
| } |
| |
| OS << ") __LIBC_ATTRS;\n\n"; |
| } |
| |
| // Make another pass over entrypoints to emit object declarations. |
| for (const auto &Name : EntrypointNameList) { |
| if (G.ObjectSpecMap.find(Name) == G.ObjectSpecMap.end()) |
| continue; |
| llvm::Record *ObjectSpec = G.ObjectSpecMap[Name]; |
| auto Type = ObjectSpec->getValueAsString("Type"); |
| OS << "extern " << Type << " " << Name << " __LIBC_ATTRS;\n"; |
| } |
| |
| // Emit a final newline if we emitted any object declarations. |
| if (llvm::any_of(EntrypointNameList, [&](const std::string &Name) { |
| return G.ObjectSpecMap.find(Name) != G.ObjectSpecMap.end(); |
| })) |
| OS << "\n"; |
| |
| OS << "#ifdef __cplusplus\n" |
| << "}\n" |
| << "#endif\n\n"; |
| OS << "#endif\n"; |
| } |
| |
| } // namespace llvm_libc |