[libc] Add a library of standalone C++ utilities.

Some of the existing utils in utils/UnitTest/Test.h have been moved to
this new library.

Reviewers: abrachet, gchatelet

Tags: #libc-project

Differential Revision: https://reviews.llvm.org/D73530
This commit is contained in:
Siva Chandra Reddy 2020-01-26 21:50:27 -08:00
parent 4f2e2acc4b
commit c6bc10636c
12 changed files with 317 additions and 81 deletions

View File

@ -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.

View File

@ -375,3 +375,47 @@ function(add_libc_testsuite suite_name)
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(
# <target name>
# HDRS <list of .h files part of the library>
# DEPENDS <list of dependencies>
# )
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)

View File

@ -1,3 +1,4 @@
add_subdirectory(CPP)
add_subdirectory(HdrGen)
add_subdirectory(UnitTest)
add_subdirectory(benchmarks)

47
libc/utils/CPP/Array.h Normal file
View File

@ -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 <stddef.h> // For size_t.
namespace __llvm_libc {
namespace cpp {
template <class T, size_t N> 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

90
libc/utils/CPP/ArrayRef.h Normal file
View File

@ -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 <stddef.h> // 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 <typename T> class ArrayRef {
public:
using iterator = const T *;
private:
const T *Data = nullptr;
size_t Length = 0;
public:
ArrayRef() = default;
// From Array.
template <size_t N>
ArrayRef(const Array<T, N> &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 <size_t N>
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 <typename T> class MutableArrayRef : public ArrayRef<T> {
public:
using iterator = T *;
// From Array.
template <size_t N> MutableArrayRef(Array<T, N> &Arr) : ArrayRef<T>(Arr) {}
// Construct from a single element.
explicit MutableArrayRef(T &OneElt) : ArrayRef<T>(OneElt) {}
// Construct from a pointer and length.
MutableArrayRef(T *data, size_t length) : ArrayRef<T>(data, length) {}
// Construct from a range.
MutableArrayRef(T *begin, T *end) : ArrayRef<T>(begin, end) {}
// Construct from a C array.
template <size_t N>
constexpr MutableArrayRef(T (&Arr)[N]) : ArrayRef<T>(Arr) {}
T *data() const { return const_cast<T *>(ArrayRef<T>::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

View File

@ -0,0 +1,7 @@
add_header_library(
standalone_cpp
HDRS
Array.h
ArrayRef.h
TypeTraits.h
)

12
libc/utils/CPP/README.md Normal file
View File

@ -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 <stddef.h>` and not `#include <cstddef>`.
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.

View File

@ -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<char> {
// More methods like those in llvm::StringRef can be added as needed.
};
} // namespace cpp
} // namespace __llvm_libc

View File

@ -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 <bool B, typename T> struct EnableIf;
template <typename T> struct EnableIf<true, T> { typedef T Type; };
template <bool B, typename T>
using EnableIfType = typename EnableIf<B, T>::Type;
struct TrueValue {
static constexpr bool Value = true;
};
struct FalseValue {
static constexpr bool Value = false;
};
template <typename Type> struct IsIntegral : public FalseValue {};
template <> struct IsIntegral<char> : public TrueValue {};
template <> struct IsIntegral<signed char> : public TrueValue {};
template <> struct IsIntegral<unsigned char> : public TrueValue {};
template <> struct IsIntegral<short> : public TrueValue {};
template <> struct IsIntegral<unsigned short> : public TrueValue {};
template <> struct IsIntegral<int> : public TrueValue {};
template <> struct IsIntegral<unsigned int> : public TrueValue {};
template <> struct IsIntegral<long> : public TrueValue {};
template <> struct IsIntegral<unsigned long> : public TrueValue {};
template <> struct IsIntegral<long long> : public TrueValue {};
template <> struct IsIntegral<unsigned long long> : public TrueValue {};
template <> struct IsIntegral<bool> : public TrueValue {};
template <typename Type> struct IsIntegralNotBool : public IsIntegral<Type> {};
template <> struct IsIntegralNotBool<bool> : public FalseValue {};
template <typename T> struct IsPointerType : public FalseValue {};
template <typename T> struct IsPointerType<T *> : public TrueValue {};
template <typename T1, typename T2> struct IsSame : public FalseValue {};
template <typename T> struct IsSame<T, T> : public TrueValue {};
} // namespace cpp
} // namespace __llvm_libc

View File

@ -4,3 +4,5 @@ add_llvm_library(
Test.h
LINK_COMPONENTS Support
)
target_include_directories(LibcUnitTest PUBLIC ${LIBC_SOURCE_DIR})
add_dependencies(LibcUnitTest standalone_cpp)

View File

@ -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 @@ bool Test::testStrNe(RunContext &Ctx, const char *LHS, const char *RHS,
}
} // namespace testing
} // namespace llvm_libc
} // namespace __llvm_libc
int main() { return llvm_libc::testing::Test::runTests(); }
int main() { return __llvm_libc::testing::Test::runTests(); }

View File

@ -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 {
#include "utils/CPP/TypeTraits.h"
namespace __llvm_libc {
namespace testing {
// We define our own EnableIf and IsIntegerType traits because we do not want to
// include even the standard header <type_traits>.
template <bool B, typename T> struct EnableIf;
template <typename T> struct EnableIf<true, T> { typedef T Type; };
template <bool B, typename T>
using EnableIfType = typename EnableIf<B, T>::Type;
template <typename Type> struct IsIntegerType {
static const bool Value = false;
};
template <> struct IsIntegerType<char> { static const bool Value = true; };
template <> struct IsIntegerType<unsigned char> {
static const bool Value = true;
};
template <> struct IsIntegerType<short> { static const bool Value = true; };
template <> struct IsIntegerType<unsigned short> {
static const bool Value = true;
};
template <> struct IsIntegerType<int> { static const bool Value = true; };
template <> struct IsIntegerType<unsigned int> {
static const bool Value = true;
};
template <> struct IsIntegerType<long> { static const bool Value = true; };
template <> struct IsIntegerType<unsigned long> {
static const bool Value = true;
};
template <> struct IsIntegerType<long long> { static const bool Value = true; };
template <> struct IsIntegerType<unsigned long long> {
static const bool Value = true;
};
template <typename T> struct IsPointerType;
template <typename T> struct IsPointerType<T *> {
static const bool Value = true;
};
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 @@ protected:
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 <typename ValType,
EnableIfType<IsIntegerType<ValType>::Value, ValType> = 0>
// |Cond| on mismatched |LHS| and |RHS| types can potentially succeed because
// of type promotion.
template <
typename ValType,
cpp::EnableIfType<cpp::IsIntegralNotBool<ValType>::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 <typename ValType,
EnableIfType<IsPointerType<ValType>::Value, ValType> = nullptr>
template <
typename ValType,
cpp::EnableIfType<cpp::IsPointerType<ValType>::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 @@ private:
};
} // 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