[libc] Add global stdout and stderr objects.

They are added as entrypoint object targets. The header-gen
infrastructure has been extended to enable handling standard required
global objects. The libc-api-test has also been extended to verify the
global object declarations.

Reviewed By: lntue

Differential Revision: https://reviews.llvm.org/D126329
This commit is contained in:
Siva Chandra Reddy 2022-05-24 08:13:11 +00:00
parent 827fa2c419
commit 9b8ca3c1f1
18 changed files with 149 additions and 2 deletions

View File

@ -147,6 +147,10 @@ def StringAPI : PublicAPI<"string.h"> {
}
def StdIOAPI : PublicAPI<"stdio.h"> {
let Macros = [
SimpleMacroDef<"stderr", "stderr">,
SimpleMacroDef<"stdout", "stdout">,
];
let Types = ["size_t", "FILE", "cookie_io_functions_t"];
}

View File

@ -276,6 +276,8 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.stdio.fwrite_unlocked
libc.src.stdio.sprintf
libc.src.stdio.snprintf
libc.src.stdio.stderr
libc.src.stdio.stdout
# signal.h entrypoints
# TODO: Enable signal.h entrypoints after fixing signal.h

View File

@ -22,4 +22,5 @@ class PublicAPI<string name> {
list<string> Enumerations = [];
list<string> Structs = [];
list<string> Functions = [];
list<string> Objects = [];
}

View File

@ -138,16 +138,23 @@ class FunctionSpec<string name, RetValSpec return, list<ArgSpec> args> {
list<ArgSpec> Args = args;
}
class ObjectSpec<string name, string type> {
string Name = name;
string Type = type;
}
class HeaderSpec<string name,
list<Macro> macros = [],
list<Type> types = [],
list<EnumeratedNameValue> enumerations = [],
list<FunctionSpec> functions = []> {
list<FunctionSpec> functions = [],
list<ObjectSpec> objects = []> {
string Name = name;
list<FunctionSpec> Functions = functions;
list<Type> Types = types;
list<Macro> Macros = macros;
list<EnumeratedNameValue> Enumerations = enumerations;
list<ObjectSpec> Objects = objects;
}
class StandardSpec<string name> {

View File

@ -471,7 +471,10 @@ def StdC : StandardSpec<"stdc"> {
HeaderSpec StdIO = HeaderSpec<
"stdio.h",
[], // Macros
[
Macro<"stderr">,
Macro<"stdout">,
], // Macros
[ // Types
SizeTType,
FILE,
@ -560,6 +563,16 @@ def StdC : StandardSpec<"stdc"> {
ArgSpec<ConstCharRestrictedPtr>,
ArgSpec<VarArgType>]
>,
],
[
ObjectSpec<
"stdout",
"FILE *"
>,
ObjectSpec<
"stderr",
"FILE *"
>,
]
>;

View File

@ -223,6 +223,9 @@ public:
// library.
File *openfile(const char *path, const char *mode);
extern File *stdout;
extern File *stderr;
} // namespace __llvm_libc
#endif // LLVM_LIBC_SRC_SUPPORT_OSUTIL_FILE_H

View File

@ -165,4 +165,18 @@ File *openfile(const char *path, const char *mode) {
return file;
}
// TODO: Use the appropriate buffering modes for the standard streams below
// the different buffering modes are available.
constexpr size_t STDOUT_BUFFER_SIZE = 1024;
char stdout_buffer[STDOUT_BUFFER_SIZE];
static LinuxFile StdOut(1, stdout_buffer, STDOUT_BUFFER_SIZE, 0, false,
File::ModeFlags(File::OpenMode::APPEND));
File *stdout = &StdOut;
constexpr size_t STDERR_BUFFER_SIZE = 1024;
char stderr_buffer[STDERR_BUFFER_SIZE];
static LinuxFile StdErr(2, stderr_buffer, STDERR_BUFFER_SIZE, 0, false,
File::ModeFlags(File::OpenMode::APPEND));
File *stderr = &StdErr;
} // namespace __llvm_libc

View File

@ -203,6 +203,29 @@ add_entrypoint_object(
libc.src.__support.File.file
)
add_entrypoint_object(
stdout
SRCS
stdout.cpp
HDRS
stdout.h
DEPENDS
libc.include.stdio
libc.src.__support.File.file
libc.src.__support.File.platform_file
)
add_entrypoint_object(
stderr
SRCS
stderr.cpp
HDRS
stderr.h
DEPENDS
libc.include.stdio
libc.src.__support.File.file
libc.src.__support.File.platform_file
)
add_entrypoint_object(
sprintf

View File

@ -0,0 +1,5 @@
#include "src/__support/File/file.h"
#include <stdio.h>
extern FILE *stderr = reinterpret_cast<FILE *>(__llvm_libc::stderr);

9
libc/src/stdio/stderr.h Normal file
View File

@ -0,0 +1,9 @@
//===------------------------------------------------------------*- 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
//
//===----------------------------------------------------------------------===//
#error "Do not include this file. Instead include __support/File/file.h."

View File

@ -0,0 +1,5 @@
#include "src/__support/File/file.h"
#include <stdio.h>
extern FILE *stdout = reinterpret_cast<FILE *>(__llvm_libc::stdout);

9
libc/src/stdio/stdout.h Normal file
View File

@ -0,0 +1,9 @@
//===------------------------------------------------------------*- 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
//
//===----------------------------------------------------------------------===//
#error "Do not include this file. Instead include __support/File/file.h."

View File

@ -96,6 +96,8 @@ set_target_properties(
PROPERTIES
INCLUDE_DIRECTORIES ""
)
target_link_libraries(libc-api-test llvmlibc)
# Only include we need is the include for cpp::IsSame and our generated
# public headers.
target_include_directories(

View File

@ -195,3 +195,8 @@ TEST(LlvmLibcPlatformFileTest, IncorrectOperation) {
ASSERT_TRUE(file->error());
ASSERT_EQ(file->close(), 0);
}
TEST(LlvmLibcPlatformFileTest, StdOutStdErrSmokeTest) {
EXPECT_FALSE(__llvm_libc::stdout == nullptr);
EXPECT_FALSE(__llvm_libc::stderr == nullptr);
}

View File

@ -28,6 +28,12 @@ bool TestGeneratorMain(llvm::raw_ostream &OS, llvm::RecordKeeper &records) {
for (const auto &entrypoint : EntrypointNamesOption) {
auto match = G.FunctionToHeaderMap.find(entrypoint);
if (match == G.FunctionToHeaderMap.end()) {
auto objectMatch = G.ObjectToHeaderMap.find(entrypoint);
if (objectMatch != G.ObjectToHeaderMap.end()) {
headerFileSet.insert(objectMatch->second);
continue;
}
llvm::errs() << "ERROR: entrypoint '" << entrypoint
<< "' could not be found in spec in any public header\n";
return true;
@ -43,6 +49,17 @@ bool TestGeneratorMain(llvm::raw_ostream &OS, llvm::RecordKeeper &records) {
for (const auto &entrypoint : EntrypointNamesOption) {
auto match = G.FunctionSpecMap.find(entrypoint);
if (match == G.FunctionSpecMap.end()) {
auto objectMatch = G.ObjectSpecMap.find(entrypoint);
if (objectMatch != G.ObjectSpecMap.end()) {
auto entrypointPtr = entrypoint + "_ptr";
llvm::Record *objectSpec = G.ObjectSpecMap[entrypoint];
auto objectType = objectSpec->getValueAsString("Type");
// We just make sure that the global object is present.
OS << " " << objectType << " *" << entrypointPtr << " = &"
<< entrypoint << ";\n";
OS << " ++" << entrypointPtr << ";\n"; // To avoid unused var warning.
continue;
}
llvm::errs() << "ERROR: entrypoint '" << entrypoint
<< "' could not be found in spec in any public header\n";
return true;
@ -74,6 +91,11 @@ bool TestGeneratorMain(llvm::raw_ostream &OS, llvm::RecordKeeper &records) {
OS << " return 0;\n";
OS << "}\n\n";
// We provide dummy malloc and free implementations to support the case
// when LLVM libc does to include them.
OS << "void *malloc(size_t) { return nullptr; }\n";
OS << "void free(void *) {}\n";
return false;
}

View File

@ -114,6 +114,15 @@ void writeAPIFromIndex(APIIndexer &G,
OS << ");\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 << ";\n";
}
OS << "__END_C_DECLS\n";
}

View File

@ -108,6 +108,13 @@ void APIIndexer::indexStandardSpecDef(llvm::Record *StandardSpec) {
EnumerationSpecMap[std::string(
EnumerationSpec->getValueAsString("Name"))] = EnumerationSpec;
}
auto ObjectSpecList = HeaderSpec->getValueAsListOfDefs("Objects");
for (llvm::Record *ObjectSpec : ObjectSpecList) {
auto ObjectName = std::string(ObjectSpec->getValueAsString("Name"));
ObjectSpecMap[ObjectName] = ObjectSpec;
ObjectToHeaderMap[ObjectName] = std::string(Header);
}
}
}
}
@ -135,6 +142,10 @@ void APIIndexer::indexPublicAPIDef(llvm::Record *PublicAPI) {
auto EnumerationList = PublicAPI->getValueAsListOfStrings("Enumerations");
for (llvm::StringRef EnumerationName : EnumerationList)
Enumerations.insert(std::string(EnumerationName));
auto ObjectList = PublicAPI->getValueAsListOfStrings("Objects");
for (llvm::StringRef ObjectName : ObjectList)
Objects.insert(std::string(ObjectName));
}
void APIIndexer::index(llvm::RecordKeeper &Records) {

View File

@ -63,13 +63,16 @@ public:
NameToRecordMapping EnumerationSpecMap;
NameToRecordMapping FunctionSpecMap;
NameToRecordMapping MacroDefsMap;
NameToRecordMapping ObjectSpecMap;
std::unordered_map<std::string, std::string> FunctionToHeaderMap;
std::unordered_map<std::string, std::string> ObjectToHeaderMap;
NameSet RequiredTypes;
NameSet Structs;
NameSet Enumerations;
NameSet Functions;
NameSet Objects;
NameSet PublicHeaders;
std::string getTypeAsString(llvm::Record *TypeRecord);