diff --git a/libc/config/linux/api.td b/libc/config/linux/api.td --- a/libc/config/linux/api.td +++ b/libc/config/linux/api.td @@ -147,6 +147,10 @@ } def StdIOAPI : PublicAPI<"stdio.h"> { + let Macros = [ + SimpleMacroDef<"stderr", "stderr">, + SimpleMacroDef<"stdout", "stdout">, + ]; let Types = ["size_t", "FILE", "cookie_io_functions_t"]; } diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -276,6 +276,8 @@ 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 diff --git a/libc/config/public_api.td b/libc/config/public_api.td --- a/libc/config/public_api.td +++ b/libc/config/public_api.td @@ -22,4 +22,5 @@ list Enumerations = []; list Structs = []; list Functions = []; + list Objects = []; } diff --git a/libc/spec/spec.td b/libc/spec/spec.td --- a/libc/spec/spec.td +++ b/libc/spec/spec.td @@ -138,16 +138,23 @@ list Args = args; } +class ObjectSpec { + string Name = name; + string Type = type; +} + class HeaderSpec macros = [], list types = [], list enumerations = [], - list functions = []> { + list functions = [], + list objects = []> { string Name = name; list Functions = functions; list Types = types; list Macros = macros; list Enumerations = enumerations; + list Objects = objects; } class StandardSpec { diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -471,7 +471,10 @@ HeaderSpec StdIO = HeaderSpec< "stdio.h", - [], // Macros + [ + Macro<"stderr">, + Macro<"stdout">, + ], // Macros [ // Types SizeTType, FILE, @@ -560,6 +563,16 @@ ArgSpec, ArgSpec] >, + ], + [ + ObjectSpec< + "stdout", + "FILE *" + >, + ObjectSpec< + "stderr", + "FILE *" + >, ] >; diff --git a/libc/src/__support/File/file.h b/libc/src/__support/File/file.h --- a/libc/src/__support/File/file.h +++ b/libc/src/__support/File/file.h @@ -223,6 +223,9 @@ // 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 diff --git a/libc/src/__support/File/linux_file.cpp b/libc/src/__support/File/linux_file.cpp --- a/libc/src/__support/File/linux_file.cpp +++ b/libc/src/__support/File/linux_file.cpp @@ -165,4 +165,18 @@ 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 diff --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt --- a/libc/src/stdio/CMakeLists.txt +++ b/libc/src/stdio/CMakeLists.txt @@ -203,6 +203,29 @@ 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 diff --git a/libc/src/stdio/stderr.h b/libc/src/stdio/stderr.h new file mode 100644 --- /dev/null +++ b/libc/src/stdio/stderr.h @@ -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." diff --git a/libc/src/stdio/stderr.cpp b/libc/src/stdio/stderr.cpp new file mode 100644 --- /dev/null +++ b/libc/src/stdio/stderr.cpp @@ -0,0 +1,5 @@ +#include "src/__support/File/file.h" + +#include + +extern FILE *stderr = reinterpret_cast(__llvm_libc::stderr); diff --git a/libc/src/stdio/stdout.h b/libc/src/stdio/stdout.h new file mode 100644 --- /dev/null +++ b/libc/src/stdio/stdout.h @@ -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." diff --git a/libc/src/stdio/stdout.cpp b/libc/src/stdio/stdout.cpp new file mode 100644 --- /dev/null +++ b/libc/src/stdio/stdout.cpp @@ -0,0 +1,5 @@ +#include "src/__support/File/file.h" + +#include + +extern FILE *stdout = reinterpret_cast(__llvm_libc::stdout); diff --git a/libc/test/src/CMakeLists.txt b/libc/test/src/CMakeLists.txt --- a/libc/test/src/CMakeLists.txt +++ b/libc/test/src/CMakeLists.txt @@ -96,6 +96,8 @@ 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( diff --git a/libc/test/src/__support/File/platform_file_test.cpp b/libc/test/src/__support/File/platform_file_test.cpp --- a/libc/test/src/__support/File/platform_file_test.cpp +++ b/libc/test/src/__support/File/platform_file_test.cpp @@ -195,3 +195,8 @@ ASSERT_TRUE(file->error()); ASSERT_EQ(file->close(), 0); } + +TEST(LlvmLibcPlatformFileTest, StdOutStdErrSmokeTest) { + EXPECT_FALSE(__llvm_libc::stdout == nullptr); + EXPECT_FALSE(__llvm_libc::stderr == nullptr); +} diff --git a/libc/utils/HdrGen/PrototypeTestGen/PrototypeTestGen.cpp b/libc/utils/HdrGen/PrototypeTestGen/PrototypeTestGen.cpp --- a/libc/utils/HdrGen/PrototypeTestGen/PrototypeTestGen.cpp +++ b/libc/utils/HdrGen/PrototypeTestGen/PrototypeTestGen.cpp @@ -28,6 +28,12 @@ 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 @@ 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 @@ 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; } diff --git a/libc/utils/HdrGen/PublicAPICommand.cpp b/libc/utils/HdrGen/PublicAPICommand.cpp --- a/libc/utils/HdrGen/PublicAPICommand.cpp +++ b/libc/utils/HdrGen/PublicAPICommand.cpp @@ -114,6 +114,15 @@ OS << ");\n\n"; } + + // Make another pass over entrypoints to emit object declarations. + for (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"; } diff --git a/libc/utils/LibcTableGenUtil/APIIndexer.h b/libc/utils/LibcTableGenUtil/APIIndexer.h --- a/libc/utils/LibcTableGenUtil/APIIndexer.h +++ b/libc/utils/LibcTableGenUtil/APIIndexer.h @@ -63,13 +63,16 @@ NameToRecordMapping EnumerationSpecMap; NameToRecordMapping FunctionSpecMap; NameToRecordMapping MacroDefsMap; + NameToRecordMapping ObjectSpecMap; std::unordered_map FunctionToHeaderMap; + std::unordered_map ObjectToHeaderMap; NameSet RequiredTypes; NameSet Structs; NameSet Enumerations; NameSet Functions; + NameSet Objects; NameSet PublicHeaders; std::string getTypeAsString(llvm::Record *TypeRecord); diff --git a/libc/utils/LibcTableGenUtil/APIIndexer.cpp b/libc/utils/LibcTableGenUtil/APIIndexer.cpp --- a/libc/utils/LibcTableGenUtil/APIIndexer.cpp +++ b/libc/utils/LibcTableGenUtil/APIIndexer.cpp @@ -108,6 +108,13 @@ 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 @@ 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) {