diff --git a/libc/CMakeLists.txt b/libc/CMakeLists.txt --- a/libc/CMakeLists.txt +++ b/libc/CMakeLists.txt @@ -1,5 +1,8 @@ cmake_minimum_required(VERSION 3.4.3) +# Use old version of target_sources command which converts the source +# file paths to full paths. +cmake_policy(SET CMP0076 OLD) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules") # The top-level source directory of libc. diff --git a/libc/cmake/modules/LLVMLibCRules.cmake b/libc/cmake/modules/LLVMLibCRules.cmake --- a/libc/cmake/modules/LLVMLibCRules.cmake +++ b/libc/cmake/modules/LLVMLibCRules.cmake @@ -375,3 +375,47 @@ add_custom_target(${suite_name}) add_dependencies(check-libc ${suite_name}) endfunction(add_libc_testsuite) + +# Rule to add header only libraries. +# Usage +# add_header_library( +# +# HDRS +# DEPENDS +# ) +function(add_header_library target_name) + cmake_parse_arguments( + "ADD_HEADER" + "" # No optional arguments + "" # No Single value arguments + "HDRS;DEPENDS" # Multi-value arguments + ${ARGN} + ) + + if(NOT ADD_HEADER_HDRS) + message(FATAL_ERROR "'add_header_library' target requires a HDRS list of .h files.") + endif() + + set(FULL_HDR_PATHS "") + # TODO: Remove this foreach block when we can switch to the new + # version of the CMake policy CMP0076. + foreach(hdr IN LISTS ADD_HEADER_HDRS) + list(APPEND FULL_HDR_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/${hdr}) + endforeach() + + set(interface_target_name "${target_name}_header_library__") + + add_library(${interface_target_name} INTERFACE) + target_sources(${interface_target_name} INTERFACE ${FULL_HDR_PATHS}) + if(ADD_HEADER_DEPENDS) + add_dependencies(${interface_target_name} ${ADD_HEADER_DEPENDS}) + endif() + + add_custom_target(${target_name}) + add_dependencies(${target_name} ${interface_target_name}) + set_target_properties( + ${target_name} + PROPERTIES + "TARGET_TYPE" "HDR_LIBRARY" + ) +endfunction(add_header_library) diff --git a/libc/utils/CMakeLists.txt b/libc/utils/CMakeLists.txt --- a/libc/utils/CMakeLists.txt +++ b/libc/utils/CMakeLists.txt @@ -1,3 +1,4 @@ +add_subdirectory(CPP) add_subdirectory(HdrGen) add_subdirectory(UnitTest) add_subdirectory(benchmarks) diff --git a/libc/utils/CPP/Array.h b/libc/utils/CPP/Array.h new file mode 100644 --- /dev/null +++ b/libc/utils/CPP/Array.h @@ -0,0 +1,47 @@ +//===--------- A self contained equivalent of std::array --------*- 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 // For size_t. + +namespace __llvm_libc { +namespace cpp { + +template struct Array { + static_assert(N != 0, "Cannot create a __llvm_libc::cpp::Array of size 0."); + + T Data[N]; + + using iterator = T *; + using const_iterator = const T *; + + constexpr T *data() { return Data; } + constexpr const T *data() const { return Data; } + + constexpr T &front() { return Data[0]; } + constexpr T &front() const { return Data[0]; } + + constexpr T &back() { return Data[N - 1]; } + constexpr T &back() const { return Data[N - 1]; } + + constexpr T &operator[](size_t Index) { return Data[Index]; } + + constexpr const T &operator[](size_t Index) const { return Data[Index]; } + + constexpr size_t size() const { return N; } + + constexpr bool empty() const { return N == 0; } + + constexpr iterator begin() { return Data; } + constexpr const_iterator begin() const { return Data; } + + constexpr iterator end() { return Data + N; } + const_iterator end() const { return Data + N; } +}; + +} // namespace cpp +} // namespace __llvm_libc diff --git a/libc/utils/CPP/ArrayRef.h b/libc/utils/CPP/ArrayRef.h new file mode 100644 --- /dev/null +++ b/libc/utils/CPP/ArrayRef.h @@ -0,0 +1,90 @@ +//===----------------- Self contained ArrayRef type -------------*- 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 "Array.h" + +#include // For size_t. + +namespace __llvm_libc { +namespace cpp { + +// The implementations of ArrayRef and MutualArrayRef in this file are based +// on the implementations of the types with the same names in +// llvm/ADT/ArrayRef.h. The implementations in this file are of a limited +// functionality, but can be extended in an as needed basis. + +template class ArrayRef { +public: + using iterator = const T *; + +private: + const T *Data = nullptr; + size_t Length = 0; + +public: + ArrayRef() = default; + + // From Array. + template + ArrayRef(const Array &Arr) : Data(Arr.Data), Length(N) {} + + // Construct an ArrayRef from a single element. + explicit ArrayRef(const T &OneElt) : Data(&OneElt), Length(1) {} + + // Construct an ArrayRef from a pointer and length. + ArrayRef(const T *data, size_t length) : Data(data), Length(length) {} + + // Construct an ArrayRef from a range. + ArrayRef(const T *begin, const T *end) : Data(begin), Length(end - begin) {} + + // Construct an ArrayRef from a C array. + template + constexpr ArrayRef(const T (&Arr)[N]) : Data(Arr), Length(N) {} + + iterator begin() const { return Data; } + iterator end() const { return Data + Length; } + + bool empty() const { return Length == 0; } + + const T *data() const { return Data; } + + size_t size() const { return Length; } + + const T &operator[](size_t Index) const { return Data[Index]; } +}; + +template class MutableArrayRef : public ArrayRef { +public: + using iterator = T *; + + // From Array. + template MutableArrayRef(Array &Arr) : ArrayRef(Arr) {} + + // Construct from a single element. + explicit MutableArrayRef(T &OneElt) : ArrayRef(OneElt) {} + + // Construct from a pointer and length. + MutableArrayRef(T *data, size_t length) : ArrayRef(data, length) {} + + // Construct from a range. + MutableArrayRef(T *begin, T *end) : ArrayRef(begin, end) {} + + // Construct from a C array. + template + constexpr MutableArrayRef(T (&Arr)[N]) : ArrayRef(Arr) {} + + T *data() const { return const_cast(ArrayRef::data()); } + + iterator begin() const { return data(); } + iterator end() const { return data() + size(); } + + T &operator[](size_t Index) const { return data()[Index]; } +}; + +} // namespace cpp +} // namespace __llvm_libc diff --git a/libc/utils/CPP/CMakeLists.txt b/libc/utils/CPP/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/utils/CPP/CMakeLists.txt @@ -0,0 +1,7 @@ +add_header_library( + standalone_cpp + HDRS + Array.h + ArrayRef.h + TypeTraits.h +) diff --git a/libc/utils/CPP/README.md b/libc/utils/CPP/README.md new file mode 100644 --- /dev/null +++ b/libc/utils/CPP/README.md @@ -0,0 +1,12 @@ +This directory contains re-implementations of some C++ standard library as well +as some LLVM utilities. These are to be used with internal LLVM libc code and +tests. More utilities will be added on an as needed basis. There are certain +rules to be followed for future changes and additions: + +1. Only two kind of headers can be included: Other headers from this directory, +and free standing C headers. +2. Free standing C headers are to be included as C headers and not as C++ +headers. That is, use `#include ` and not `#include `. +3. The utilities should be defined in the namespace `__llvm_libc::cpp`. The +higher level namespace should have a `__` prefix to avoid symbol name pollution +when the utilities are used in implementation of public functions. diff --git a/libc/utils/CPP/StringRef.h b/libc/utils/CPP/StringRef.h new file mode 100644 --- /dev/null +++ b/libc/utils/CPP/StringRef.h @@ -0,0 +1,19 @@ +//===----------------- A standalone StringRef type -------------*- 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 "ArrayRef.h" + +namespace __llvm_libc { +namespace cpp { + +class StringRef : public ArrayRef { + // More methods like those in llvm::StringRef can be added as needed. +}; + +} // namespace cpp +} // namespace __llvm_libc diff --git a/libc/utils/CPP/TypeTraits.h b/libc/utils/CPP/TypeTraits.h new file mode 100644 --- /dev/null +++ b/libc/utils/CPP/TypeTraits.h @@ -0,0 +1,50 @@ +//===----------------- Self contained C++ type traits -----------*- 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 +// +//===----------------------------------------------------------------------===// + +namespace __llvm_libc { +namespace cpp { + +template struct EnableIf; +template struct EnableIf { typedef T Type; }; + +template +using EnableIfType = typename EnableIf::Type; + +struct TrueValue { + static constexpr bool Value = true; +}; + +struct FalseValue { + static constexpr bool Value = false; +}; + +template struct IsIntegral : public FalseValue {}; +template <> struct IsIntegral : public TrueValue {}; +template <> struct IsIntegral : public TrueValue {}; +template <> struct IsIntegral : public TrueValue {}; +template <> struct IsIntegral : public TrueValue {}; +template <> struct IsIntegral : public TrueValue {}; +template <> struct IsIntegral : public TrueValue {}; +template <> struct IsIntegral : public TrueValue {}; +template <> struct IsIntegral : public TrueValue {}; +template <> struct IsIntegral : public TrueValue {}; +template <> struct IsIntegral : public TrueValue {}; +template <> struct IsIntegral : public TrueValue {}; +template <> struct IsIntegral : public TrueValue {}; + +template struct IsIntegralNotBool : public IsIntegral {}; +template <> struct IsIntegralNotBool : public FalseValue {}; + +template struct IsPointerType : public FalseValue {}; +template struct IsPointerType : public TrueValue {}; + +template struct IsSame : public FalseValue {}; +template struct IsSame : public TrueValue {}; + +} // namespace cpp +} // namespace __llvm_libc diff --git a/libc/utils/UnitTest/CMakeLists.txt b/libc/utils/UnitTest/CMakeLists.txt --- a/libc/utils/UnitTest/CMakeLists.txt +++ b/libc/utils/UnitTest/CMakeLists.txt @@ -4,3 +4,5 @@ Test.h LINK_COMPONENTS Support ) +target_include_directories(LibcUnitTest PUBLIC ${LIBC_SOURCE_DIR}) +add_dependencies(LibcUnitTest standalone_cpp) diff --git a/libc/utils/UnitTest/Test.h b/libc/utils/UnitTest/Test.h --- a/libc/utils/UnitTest/Test.h +++ b/libc/utils/UnitTest/Test.h @@ -6,61 +6,20 @@ // //===----------------------------------------------------------------------===// -// This file should stricly not include any other file. Not even standard -// library headers. +// This file can only include headers from utils/CPP/. No other header should be +// included. -namespace llvm_libc { -namespace testing { - -// We define our own EnableIf and IsIntegerType traits because we do not want to -// include even the standard header . -template struct EnableIf; -template struct EnableIf { typedef T Type; }; - -template -using EnableIfType = typename EnableIf::Type; - -template struct IsIntegerType { - static const bool Value = false; -}; - -template <> struct IsIntegerType { static const bool Value = true; }; -template <> struct IsIntegerType { - static const bool Value = true; -}; - -template <> struct IsIntegerType { static const bool Value = true; }; -template <> struct IsIntegerType { - static const bool Value = true; -}; +#include "utils/CPP/TypeTraits.h" -template <> struct IsIntegerType { static const bool Value = true; }; -template <> struct IsIntegerType { - static const bool Value = true; -}; - -template <> struct IsIntegerType { static const bool Value = true; }; -template <> struct IsIntegerType { - static const bool Value = true; -}; - -template <> struct IsIntegerType { static const bool Value = true; }; -template <> struct IsIntegerType { - static const bool Value = true; -}; - -template struct IsPointerType; - -template struct IsPointerType { - static const bool Value = true; -}; +namespace __llvm_libc { +namespace testing { class RunContext; // Only the following conditions are supported. Notice that we do not have // a TRUE or FALSE condition. That is because, C library funtions do not -// return, but use integral return values to indicate true or false -// conditions. Hence, it is more appropriate to use the other comparison +// return boolean values, but use integral return values to indicate true or +// false conditions. Hence, it is more appropriate to use the other comparison // condtions for such cases. enum TestCondition { Cond_None, @@ -98,22 +57,24 @@ static void addTest(Test *T); // We make use of a template function, with |LHS| and |RHS| as explicit - // parameters, for enhanced type checking. Other gtest like test unittest - // frameworks have a similar functions which takes a boolean argument + // parameters, for enhanced type checking. Other gtest like unittest + // frameworks have a similar function which takes a boolean argument // instead of the explicit |LHS| and |RHS| arguments. This boolean argument // is the result of the |Cond| operation on |LHS| and |RHS|. Though not bad, - // mismatched |LHS| and |RHS| types can potentially succeed because of type - // promotion. - template ::Value, ValType> = 0> + // |Cond| on mismatched |LHS| and |RHS| types can potentially succeed because + // of type promotion. + template < + typename ValType, + cpp::EnableIfType::Value, ValType> = 0> static bool test(RunContext &Ctx, TestCondition Cond, ValType LHS, ValType RHS, const char *LHSStr, const char *RHSStr, const char *File, unsigned long Line) { return internal::test(Ctx, Cond, LHS, RHS, LHSStr, RHSStr, File, Line); } - template ::Value, ValType> = nullptr> + template < + typename ValType, + cpp::EnableIfType::Value, ValType> = nullptr> static bool test(RunContext &Ctx, TestCondition Cond, ValType LHS, ValType RHS, const char *LHSStr, const char *RHSStr, const char *File, unsigned long Line) { @@ -138,80 +99,80 @@ }; } // namespace testing -} // namespace llvm_libc +} // namespace __llvm_libc #define TEST(SuiteName, TestName) \ - class SuiteName##_##TestName : public llvm_libc::testing::Test { \ + class SuiteName##_##TestName : public __llvm_libc::testing::Test { \ public: \ SuiteName##_##TestName() { addTest(this); } \ - void Run(llvm_libc::testing::RunContext &) override; \ + void Run(__llvm_libc::testing::RunContext &) override; \ const char *getName() const override { return #SuiteName "." #TestName; } \ }; \ SuiteName##_##TestName SuiteName##_##TestName##_Instance; \ - void SuiteName##_##TestName::Run(llvm_libc::testing::RunContext &Ctx) + void SuiteName##_##TestName::Run(__llvm_libc::testing::RunContext &Ctx) #define TEST_F(SuiteClass, TestName) \ class SuiteClass##_##TestName : public SuiteClass { \ public: \ SuiteClass##_##TestName() { addTest(this); } \ - void Run(llvm_libc::testing::RunContext &) override; \ + void Run(__llvm_libc::testing::RunContext &) override; \ const char *getName() const override { return #SuiteClass "." #TestName; } \ }; \ SuiteClass##_##TestName SuiteClass##_##TestName##_Instance; \ - void SuiteClass##_##TestName::Run(llvm_libc::testing::RunContext &Ctx) + void SuiteClass##_##TestName::Run(__llvm_libc::testing::RunContext &Ctx) #define EXPECT_EQ(LHS, RHS) \ - llvm_libc::testing::Test::test(Ctx, llvm_libc::testing::Cond_EQ, (LHS), \ - (RHS), #LHS, #RHS, __FILE__, __LINE__) + __llvm_libc::testing::Test::test(Ctx, __llvm_libc::testing::Cond_EQ, (LHS), \ + (RHS), #LHS, #RHS, __FILE__, __LINE__) #define ASSERT_EQ(LHS, RHS) \ if (!EXPECT_EQ(LHS, RHS)) \ return #define EXPECT_NE(LHS, RHS) \ - llvm_libc::testing::Test::test(Ctx, llvm_libc::testing::Cond_NE, (LHS), \ - (RHS), #LHS, #RHS, __FILE__, __LINE__) + __llvm_libc::testing::Test::test(Ctx, __llvm_libc::testing::Cond_NE, (LHS), \ + (RHS), #LHS, #RHS, __FILE__, __LINE__) #define ASSERT_NE(LHS, RHS) \ if (!EXPECT_NE(LHS, RHS)) \ return #define EXPECT_LT(LHS, RHS) \ - llvm_libc::testing::Test::test(Ctx, llvm_libc::testing::Cond_LT, (LHS), \ - (RHS), #LHS, #RHS, __FILE__, __LINE__) + __llvm_libc::testing::Test::test(Ctx, __llvm_libc::testing::Cond_LT, (LHS), \ + (RHS), #LHS, #RHS, __FILE__, __LINE__) #define ASSERT_LT(LHS, RHS) \ if (!EXPECT_LT(LHS, RHS)) \ return #define EXPECT_LE(LHS, RHS) \ - llvm_libc::testing::Test::test(Ctx, llvm_libc::testing::Cond_LE, (LHS), \ - (RHS), #LHS, #RHS, __FILE__, __LINE__) + __llvm_libc::testing::Test::test(Ctx, __llvm_libc::testing::Cond_LE, (LHS), \ + (RHS), #LHS, #RHS, __FILE__, __LINE__) #define ASSERT_LE(LHS, RHS) \ if (!EXPECT_LE(LHS, RHS)) \ return #define EXPECT_GT(LHS, RHS) \ - llvm_libc::testing::Test::test(Ctx, llvm_libc::testing::Cond_GT, (LHS), \ - (RHS), #LHS, #RHS, __FILE__, __LINE__) + __llvm_libc::testing::Test::test(Ctx, __llvm_libc::testing::Cond_GT, (LHS), \ + (RHS), #LHS, #RHS, __FILE__, __LINE__) #define ASSERT_GT(LHS, RHS) \ if (!EXPECT_GT(LHS, RHS)) \ return #define EXPECT_GE(LHS, RHS) \ - llvm_libc::testing::Test::test(Ctx, llvm_libc::testing::Cond_GE, (LHS), \ - (RHS), #LHS, #RHS, __FILE__, __LINE__) + __llvm_libc::testing::Test::test(Ctx, __llvm_libc::testing::Cond_GE, (LHS), \ + (RHS), #LHS, #RHS, __FILE__, __LINE__) #define ASSERT_GE(LHS, RHS) \ if (!EXPECT_GE(LHS, RHS)) \ return #define EXPECT_STREQ(LHS, RHS) \ - llvm_libc::testing::Test::testStrEq(Ctx, (LHS), (RHS), #LHS, #RHS, __FILE__, \ - __LINE__) + __llvm_libc::testing::Test::testStrEq(Ctx, (LHS), (RHS), #LHS, #RHS, \ + __FILE__, __LINE__) #define ASSERT_STREQ(LHS, RHS) \ if (!EXPECT_STREQ(LHS, RHS)) \ return #define EXPECT_STRNE(LHS, RHS) \ - llvm_libc::testing::Test::testStrNe(Ctx, (LHS), (RHS), #LHS, #RHS, __FILE__, \ - __LINE__) + __llvm_libc::testing::Test::testStrNe(Ctx, (LHS), (RHS), #LHS, #RHS, \ + __FILE__, __LINE__) #define ASSERT_STRNE(LHS, RHS) \ if (!EXPECT_STRNE(LHS, RHS)) \ return diff --git a/libc/utils/UnitTest/Test.cpp b/libc/utils/UnitTest/Test.cpp --- a/libc/utils/UnitTest/Test.cpp +++ b/libc/utils/UnitTest/Test.cpp @@ -11,7 +11,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Support/raw_ostream.h" -namespace llvm_libc { +namespace __llvm_libc { namespace testing { // This need not be a class as all it has is a single read-write state variable. @@ -224,6 +224,6 @@ } } // namespace testing -} // namespace llvm_libc +} // namespace __llvm_libc -int main() { return llvm_libc::testing::Test::runTests(); } +int main() { return __llvm_libc::testing::Test::runTests(); }