178 changes: 178 additions & 0 deletions libc/spec/stdc.td
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
def StdC : StandardSpec<"stdc"> {
PtrType VoidPtr = PtrType<VoidType>;
ConstType ConstVoidPtr = ConstType<VoidPtr>;
RestrictedPtrType VoidRestrictedPtr = RestrictedPtrType<VoidType>;
ConstType ConstVoidRestrictedPtr = ConstType<VoidRestrictedPtr>;

PtrType CharPtr = PtrType<CharType>;
ConstType ConstCharPtr = ConstType<CharPtr>;
RestrictedPtrType CharRestrictedPtr = RestrictedPtrType<CharType>;
ConstType ConstCharRestrictedPtr = ConstType<CharRestrictedPtr>;

NamedType SizeTType = NamedType<"size_t">;

HeaderSpec String = HeaderSpec<
"string.h",
[
Macro<"NULL">,
],
[
SizeTType,
],
[
FunctionSpec<
"memcpy",
RetValSpec<VoidPtr>,
[ArgSpec<VoidRestrictedPtr>,
ArgSpec<ConstVoidRestrictedPtr>,
ArgSpec<SizeTType>]
>,
FunctionSpec<
"memmove",
RetValSpec<VoidPtr>,
[ArgSpec<VoidPtr>, ArgSpec<ConstVoidPtr>, ArgSpec<SizeTType>]
>,
FunctionSpec<
"memcmp",
RetValSpec<IntType>,
[ArgSpec<ConstVoidPtr>, ArgSpec<ConstVoidPtr>, ArgSpec<SizeTType>]
>,
FunctionSpec<
"memchr",
RetValSpec<VoidPtr>,
[ArgSpec<ConstVoidPtr>, ArgSpec<IntType>, ArgSpec<SizeTType>]
>,
FunctionSpec<
"memset",
RetValSpec<VoidPtr>,
[ArgSpec<VoidPtr>, ArgSpec<IntType>, ArgSpec<SizeTType>]
>,
FunctionSpec<
"strcpy",
RetValSpec<CharPtr>,
[ArgSpec<CharRestrictedPtr>, ArgSpec<ConstCharRestrictedPtr>]
>,
FunctionSpec<
"strncpy",
RetValSpec<CharPtr>,
[ArgSpec<CharRestrictedPtr>,
ArgSpec<ConstCharRestrictedPtr>,
ArgSpec<SizeTType>]
>,
FunctionSpec<
"strcat",
RetValSpec<CharPtr>,
[ArgSpec<CharRestrictedPtr>, ArgSpec<ConstCharRestrictedPtr>]
>,
FunctionSpec<
"strncat",
RetValSpec<CharPtr>,
[ArgSpec<CharPtr>, ArgSpec<ConstCharPtr>, ArgSpec<SizeTType>]
>,
FunctionSpec<
"strcmp",
RetValSpec<IntType>,
[ArgSpec<ConstCharPtr>, ArgSpec<ConstCharPtr>]
>,
FunctionSpec<
"strcoll",
RetValSpec<IntType>,
[ArgSpec<ConstCharPtr>, ArgSpec<ConstCharPtr>]
>,
FunctionSpec<
"strncmp",
RetValSpec<IntType>,
[ArgSpec<ConstCharPtr>, ArgSpec<ConstCharPtr>, ArgSpec<SizeTType>]
>,
FunctionSpec<
"strxfrm",
RetValSpec<SizeTType>,
[ArgSpec<CharRestrictedPtr>,
ArgSpec<ConstCharRestrictedPtr>,
ArgSpec<SizeTType>]
>,
FunctionSpec<
"strchr",
RetValSpec<CharPtr>,
[ArgSpec<ConstCharPtr>, ArgSpec<IntType>]
>,
FunctionSpec<
"strcspn",
RetValSpec<SizeTType>,
[ArgSpec<ConstCharPtr>, ArgSpec<ConstCharPtr>]
>,
FunctionSpec<
"strpbrk",
RetValSpec<CharPtr>,
[ArgSpec<ConstCharPtr>, ArgSpec<ConstCharPtr>]
>,
FunctionSpec<
"strrchr",
RetValSpec<CharPtr>,
[ArgSpec<ConstCharPtr>, ArgSpec<IntType>]
>,
FunctionSpec<
"strspn",
RetValSpec<SizeTType>,
[ArgSpec<ConstCharPtr>, ArgSpec<ConstCharPtr>]
>,
FunctionSpec<
"strstr",
RetValSpec<CharPtr>,
[ArgSpec<ConstCharPtr>, ArgSpec<ConstCharPtr>]
>,
FunctionSpec<
"strtok",
RetValSpec<CharPtr>,
[ArgSpec<CharRestrictedPtr>, ArgSpec<ConstCharRestrictedPtr>]
>,
FunctionSpec<
"strerror",
RetValSpec<CharPtr>,
[ArgSpec<IntType>]
>,
FunctionSpec<
"strlen",
RetValSpec<SizeTType>,
[ArgSpec<ConstCharPtr>]
>,
]
>;

HeaderSpec Math = HeaderSpec<
"math.h",
[], // Macros
[
NamedType<"float_t">,
NamedType<"double_t">,
],
[
FunctionSpec<"acos", RetValSpec<DoubleType>, [ArgSpec<DoubleType>]>,
FunctionSpec<"acosl", RetValSpec<LongDoubleType>, [ArgSpec<LongDoubleType>]>,
]
>;

HeaderSpec StdIO = HeaderSpec<
"stdio.h",
[], // Macros
[ // Types
SizeTType,
],
[
FunctionSpec<
"snprintf",
RetValSpec<IntType>,
[ArgSpec<CharPtr>,
ArgSpec<SizeTType>,
ArgSpec<ConstCharRestrictedPtr>,
ArgSpec<VarArgType>]
>,
]
>;

let Headers = [
Math,
String,
StdIO,
];
}
1 change: 1 addition & 0 deletions libc/utils/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
add_subdirectory(HdrGen)
10 changes: 10 additions & 0 deletions libc/utils/HdrGen/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
add_tablegen(libc-hdrgen llvm-libc
Command.h
Generator.cpp
Generator.h
IncludeFileCommand.cpp
IncludeFileCommand.h
Main.cpp
PublicAPICommand.cpp
PublicAPICommand.h
)
52 changes: 52 additions & 0 deletions libc/utils/HdrGen/Command.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//===-------- Base class for header generation commands ---------*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_UTILS_HDRGEN_COMMAND_H
#define LLVM_LIBC_UTILS_HDRGEN_COMMAND_H

#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/SourceMgr.h"

#include <cstdlib>

namespace llvm {

class raw_ostream;
class RecordKeeper;

} // namespace llvm

namespace llvm_libc {

typedef llvm::SmallVector<llvm::StringRef, 4> ArgVector;

class Command {
public:
class ErrorReporter {
llvm::SMLoc Loc;
const llvm::SourceMgr &SrcMgr;

public:
ErrorReporter(llvm::SMLoc L, llvm::SourceMgr &SM) : Loc(L), SrcMgr(SM) {}

void printFatalError(llvm::Twine Msg) const {
SrcMgr.PrintMessage(Loc, llvm::SourceMgr::DK_Error, Msg);
std::exit(1);
}
};

virtual void run(llvm::raw_ostream &OS, const ArgVector &Args,
llvm::StringRef StdHeader, llvm::RecordKeeper &Records,
const ErrorReporter &Reporter) const = 0;
};

} // namespace llvm_libc

#endif // LLVM_LIBC_UTILS_HDRGEN_COMMAND_H
119 changes: 119 additions & 0 deletions libc/utils/HdrGen/Generator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
//===---- Implementation of the main header generation class -----*- C++ -*===//
//
// 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 "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>();
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.startswith(ParamNamePrefix) && A.endswith(ParamNameSuffix)) {
A = A.drop_front(ParamNamePrefixSize).drop_back(ParamNameSuffixSize);
A = ArgMap[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.startswith(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.startswith(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;
}
}

} // namespace llvm_libc
56 changes: 56 additions & 0 deletions libc/utils/HdrGen/Generator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//===------------- - The main header generation class -----------*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_UTILS_HDRGEN_GENERATOR_H
#define LLVM_LIBC_UTILS_HDRGEN_GENERATOR_H

#include "Command.h"

#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"

#include <memory>
#include <string>
#include <unordered_map>

namespace llvm {

class raw_ostream;
class RecordKeeper;

} // namespace llvm

namespace llvm_libc {

class Command;

class Generator {
llvm::StringRef HeaderDefFile;
llvm::StringRef StdHeader;
std::unordered_map<std::string, std::string> &ArgMap;

std::unique_ptr<Command> IncludeFileCmd;
std::unique_ptr<Command> PublicAPICmd;

Command *getCommandHandler(llvm::StringRef CommandName);

void parseCommandArgs(llvm::StringRef ArgStr, ArgVector &Args);

void printError(llvm::StringRef Msg);

public:
Generator(const std::string &DefFile, const std::string &Header,
std::unordered_map<std::string, std::string> &Map)
: HeaderDefFile(DefFile), StdHeader(Header), ArgMap(Map) {}

void generate(llvm::raw_ostream &OS, llvm::RecordKeeper &Records);
};

} // namespace llvm_libc

#endif // LLVM_LIBC_UTILS_HDRGEN_GENERATOR_H
50 changes: 50 additions & 0 deletions libc/utils/HdrGen/IncludeFileCommand.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//===----------- Implementation of IncludeFileCommand -----------*- C++ -*-===//
//
// 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 "IncludeFileCommand.h"

#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/SourceMgr.h"

#include <cstdlib>

namespace llvm_libc {

const char IncludeFileCommand::Name[] = "include_file";

void IncludeFileCommand::run(llvm::raw_ostream &OS, const ArgVector &Args,
llvm::StringRef StdHeader,
llvm::RecordKeeper &Records,
const Command::ErrorReporter &Reporter) const {
if (Args.size() != 1) {
Reporter.printFatalError(
"%%include_file command takes exactly 1 argument.");
}

llvm::StringRef IncludeFile = Args[0];
auto Buffer = llvm::MemoryBuffer::getFileAsStream(IncludeFile);
if (!Buffer)
Reporter.printFatalError(llvm::StringRef("Unable to open ") + IncludeFile);

llvm::StringRef Content = Buffer.get()->getBuffer();

// If the included file has %%begin() command listed, then we want to write
// only the content after the begin command.
// TODO: The way the content is split below does not allow space within the
// the parentheses and, before and after the command. This probably is too
// strict and should be relaxed.
auto P = Content.split("\n%%begin()\n");
if (P.second.empty()) {
// There was no %%begin in the content.
OS << P.first;
} else {
OS << P.second;
}
}

} // namespace llvm_libc
32 changes: 32 additions & 0 deletions libc/utils/HdrGen/IncludeFileCommand.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//===-------- Class which implements the %%include_file command -*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_UTILS_HDRGEN_INCLUDE_COMMAND_H
#define LLVM_LIBC_UTILS_HDRGEN_INCLUDE_COMMAND_H

#include "Command.h"

#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"

#include <string>

namespace llvm_libc {

class IncludeFileCommand : public Command {
public:
static const char Name[];

void run(llvm::raw_ostream &OS, const ArgVector &Args,
llvm::StringRef StdHeader, llvm::RecordKeeper &Records,
const Command::ErrorReporter &Reporter) const override;
};

} // namespace llvm_libc

#endif // LLVM_LIBC_UTILS_HDRGEN_INCLUDE_COMMAND_H
56 changes: 56 additions & 0 deletions libc/utils/HdrGen/Main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//===---------------- "main" function of libc-hdrgen ------------*- C++ -*-===//
//
// 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 "llvm/ADT/StringRef.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/TableGen/Main.h"

#include <string>
#include <unordered_map>

namespace {

llvm::cl::opt<std::string>
HeaderDefFile("def", llvm::cl::desc("Path to the .h.def file."),
llvm::cl::value_desc("<filename>"), llvm::cl::Required);
llvm::cl::opt<std::string> StandardHeader(
"header",
llvm::cl::desc("The standard header file which is to be generated."),
llvm::cl::value_desc("<header file>"));
llvm::cl::list<std::string> ReplacementValues(
"args", llvm::cl::desc("Command seperated <argument name>=<value> pairs."),
llvm::cl::value_desc("<name=value>[,name=value]"));

void ParseArgValuePairs(std::unordered_map<std::string, std::string> &Map) {
for (std::string &R : ReplacementValues) {
auto Pair = llvm::StringRef(R).split('=');
Map[Pair.first] = Pair.second;
}
}

} // anonymous namespace

namespace llvm_libc {

bool HeaderGeneratorMain(llvm::raw_ostream &OS, llvm::RecordKeeper &Records) {
std::unordered_map<std::string, std::string> ArgMap;
ParseArgValuePairs(ArgMap);
Generator G(HeaderDefFile, StandardHeader, ArgMap);
G.generate(OS, Records);

return false;
}

} // namespace llvm_libc

int main(int argc, char *argv[]) {
llvm::cl::ParseCommandLineOptions(argc, argv);
return TableGenMain(argv[0], &llvm_libc::HeaderGeneratorMain);
}
255 changes: 255 additions & 0 deletions libc/utils/HdrGen/PublicAPICommand.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
//===--------------- Implementation of PublicAPICommand ----------*-C++ -*-===//
//
// 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 "PublicAPICommand.h"

#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/TableGen/Error.h"
#include "llvm/TableGen/Record.h"

static const char NamedTypeClassName[] = "NamedType";
static const char PtrTypeClassName[] = "PtrType";
static const char RestrictedPtrTypeClassName[] = "RestrictedPtrType";
static const char ConstTypeClassName[] = "ConstType";
static const char StructTypeClassName[] = "Struct";

static const char StandardSpecClassName[] = "StandardSpec";
static const char PublicAPIClassName[] = "PublicAPI";

static bool isa(llvm::Record *Def, llvm::Record *TypeClass) {
llvm::RecordRecTy *RecordType = Def->getType();
llvm::ArrayRef<llvm::Record *> Classes = RecordType->getClasses();
// We want exact types. That is, we don't want the classes listed in
// spec.td to be subclassed. Hence, we do not want the record |Def|
// to be of more than one class type..
if (Classes.size() != 1)
return false;
return Classes[0] == TypeClass;
}

// Text blocks for macro definitions and type decls can be indented to
// suit the surrounding tablegen listing. We need to dedent such blocks
// before writing them out.
static void dedentAndWrite(llvm::StringRef Text, llvm::raw_ostream &OS) {
llvm::SmallVector<llvm::StringRef, 10> Lines;
llvm::SplitString(Text, Lines, "\n");
size_t shortest_indent = 1024;
for (llvm::StringRef L : Lines) {
llvm::StringRef Indent = L.take_while([](char c) { return c == ' '; });
size_t IndentSize = Indent.size();
if (Indent.size() == L.size()) {
// Line is all spaces so no point noting the indent.
continue;
}
if (IndentSize < shortest_indent)
shortest_indent = IndentSize;
}
for (llvm::StringRef L : Lines) {
if (L.size() >= shortest_indent)
OS << L.drop_front(shortest_indent) << '\n';
}
}

class APIGenerator {
llvm::StringRef StdHeader;

// TableGen classes in spec.td.
llvm::Record *NamedTypeClass;
llvm::Record *PtrTypeClass;
llvm::Record *RestrictedPtrTypeClass;
llvm::Record *ConstTypeClass;
llvm::Record *StructClass;
llvm::Record *StandardSpecClass;
llvm::Record *PublicAPIClass;

using NameToRecordMapping = std::unordered_map<std::string, llvm::Record *>;
using NameSet = std::unordered_set<std::string>;

// Mapping from names to records defining them.
NameToRecordMapping MacroSpecMap;
NameToRecordMapping TypeSpecMap;
NameToRecordMapping FunctionSpecMap;
NameToRecordMapping MacroDefsMap;
NameToRecordMapping TypeDeclsMap;

NameSet Structs;
NameSet Functions;

bool isaNamedType(llvm::Record *Def) { return isa(Def, NamedTypeClass); }

bool isaStructType(llvm::Record *Def) { return isa(Def, StructClass); }

bool isaPtrType(llvm::Record *Def) { return isa(Def, PtrTypeClass); }

bool isaConstType(llvm::Record *Def) { return isa(Def, ConstTypeClass); }

bool isaRestrictedPtrType(llvm::Record *Def) {
return isa(Def, RestrictedPtrTypeClass);
}

bool isaStandardSpec(llvm::Record *Def) {
return isa(Def, StandardSpecClass);
}

bool isaPublicAPI(llvm::Record *Def) { return isa(Def, PublicAPIClass); }

std::string getTypeAsString(llvm::Record *TypeRecord) {
if (isaNamedType(TypeRecord) || isaStructType(TypeRecord)) {
return TypeRecord->getValueAsString("Name");
} else if (isaPtrType(TypeRecord)) {
return getTypeAsString(TypeRecord->getValueAsDef("PointeeType")) + " *";
} else if (isaConstType(TypeRecord)) {
return std::string("const ") +
getTypeAsString(TypeRecord->getValueAsDef("UnqualifiedType"));
} else if (isaRestrictedPtrType(TypeRecord)) {
return getTypeAsString(TypeRecord->getValueAsDef("PointeeType")) +
" *__restrict";
} else {
llvm::PrintFatalError(TypeRecord->getLoc(), "Invalid type.\n");
}
}

void indexStandardSpecDef(llvm::Record *StandardSpec) {
auto HeaderSpecList = StandardSpec->getValueAsListOfDefs("Headers");
for (llvm::Record *HeaderSpec : HeaderSpecList) {
if (HeaderSpec->getValueAsString("Name") == StdHeader) {
auto MacroSpecList = HeaderSpec->getValueAsListOfDefs("Macros");
// TODO: Trigger a fatal error on duplicate specs.
for (llvm::Record *MacroSpec : MacroSpecList)
MacroSpecMap[MacroSpec->getValueAsString("Name")] = MacroSpec;

auto TypeSpecList = HeaderSpec->getValueAsListOfDefs("Types");
for (llvm::Record *TypeSpec : TypeSpecList)
TypeSpecMap[TypeSpec->getValueAsString("Name")] = TypeSpec;

auto FunctionSpecList = HeaderSpec->getValueAsListOfDefs("Functions");
for (llvm::Record *FunctionSpec : FunctionSpecList) {
FunctionSpecMap[FunctionSpec->getValueAsString("Name")] =
FunctionSpec;
}
}
}
}

void indexPublicAPIDef(llvm::Record *PublicAPI) {
// While indexing the public API, we do not check if any of the entities
// requested is from an included standard. Such a check is done while
// generating the API.
auto MacroDefList = PublicAPI->getValueAsListOfDefs("Macros");
for (llvm::Record *MacroDef : MacroDefList)
MacroDefsMap[MacroDef->getValueAsString("Name")] = MacroDef;

auto TypeDeclList = PublicAPI->getValueAsListOfDefs("TypeDeclarations");
for (llvm::Record *TypeDecl : TypeDeclList)
TypeDeclsMap[TypeDecl->getValueAsString("Name")] = TypeDecl;

auto StructList = PublicAPI->getValueAsListOfStrings("Structs");
for (llvm::StringRef StructName : StructList)
Structs.insert(StructName);

auto FunctionList = PublicAPI->getValueAsListOfStrings("Functions");
for (llvm::StringRef FunctionName : FunctionList)
Functions.insert(FunctionName);
}

void index(llvm::RecordKeeper &Records) {
NamedTypeClass = Records.getClass(NamedTypeClassName);
PtrTypeClass = Records.getClass(PtrTypeClassName);
RestrictedPtrTypeClass = Records.getClass(RestrictedPtrTypeClassName);
StructClass = Records.getClass(StructTypeClassName);
ConstTypeClass = Records.getClass(ConstTypeClassName);
StandardSpecClass = Records.getClass(StandardSpecClassName);
PublicAPIClass = Records.getClass(PublicAPIClassName);

const auto &DefsMap = Records.getDefs();
for (auto &Pair : DefsMap) {
llvm::Record *Def = Pair.second.get();
if (isaStandardSpec(Def))
indexStandardSpecDef(Def);
if (isaPublicAPI(Def)) {
if (Def->getValueAsString("HeaderName") == StdHeader)
indexPublicAPIDef(Def);
}
}
}

public:
APIGenerator(llvm::StringRef Header, llvm::RecordKeeper &Records)
: StdHeader(Header) {
index(Records);
}

void write(llvm::raw_ostream &OS) {
for (auto &Pair : MacroDefsMap) {
const std::string &Name = Pair.first;
if (MacroSpecMap.find(Name) == MacroSpecMap.end())
llvm::PrintFatalError(Name + " not found in any standard spec.\n");

llvm::Record *MacroDef = Pair.second;
dedentAndWrite(MacroDef->getValueAsString("Defn"), OS);

OS << '\n';
}

for (auto &Pair : TypeDeclsMap) {
const std::string &Name = Pair.first;
if (TypeSpecMap.find(Name) == TypeSpecMap.end())
llvm::PrintFatalError(Name + " not found in any standard spec.\n");

llvm::Record *TypeDecl = Pair.second;
dedentAndWrite(TypeDecl->getValueAsString("Decl"), OS);

OS << '\n';
}

OS << "__BEGIN_C_DECLS\n\n";
for (auto &Name : Functions) {
if (FunctionSpecMap.find(Name) == FunctionSpecMap.end())
llvm::PrintFatalError(Name + " not found in any standard spec.\n");

llvm::Record *FunctionSpec = FunctionSpecMap[Name];
llvm::Record *RetValSpec = FunctionSpec->getValueAsDef("Return");
llvm::Record *ReturnType = RetValSpec->getValueAsDef("ReturnType");

OS << 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 << getTypeAsString(ArgType);
if (i < ArgsList.size() - 1)
OS << ", ";
}

OS << ");\n\n";
}
OS << "__END_C_DECLS\n";
}
};

namespace llvm_libc {

void writePublicAPI(llvm::raw_ostream &OS, llvm::RecordKeeper &Records) {}

const char PublicAPICommand::Name[] = "public_api";

void PublicAPICommand::run(llvm::raw_ostream &OS, const ArgVector &Args,
llvm::StringRef StdHeader,
llvm::RecordKeeper &Records,
const Command::ErrorReporter &Reporter) const {
if (Args.size() != 0) {
Reporter.printFatalError("public_api command does not take any arguments.");
}

APIGenerator G(StdHeader, Records);
G.write(OS);
}

} // namespace llvm_libc
36 changes: 36 additions & 0 deletions libc/utils/HdrGen/PublicAPICommand.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//===---------- Implementation of PublicAPICommand --------------*- C++ -*-===//
//
// 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 "Command.h"

#include "llvm/ADT/StringRef.h"

#include <string>
#include <unordered_map>
#include <unordered_set>

namespace llvm {

class raw_ostream;
class Record;
class RecordKeeper;

} // namespace llvm

namespace llvm_libc {

class PublicAPICommand : public Command {
public:
static const char Name[];

void run(llvm::raw_ostream &OS, const ArgVector &Args,
llvm::StringRef StdHeader, llvm::RecordKeeper &Records,
const Command::ErrorReporter &Reporter) const override;
};

} // namespace llvm_libc
188 changes: 0 additions & 188 deletions libc/utils/build_scripts/gen_hdr.py

This file was deleted.