//===-- 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 #include 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(); return IncludeFileCmd.get(); } else if (CommandName == PublicAPICommand::Name) { if (!PublicAPICmd) PublicAPICmd = std::make_unique(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 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("("); 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 ')'."); 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