diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index 7c81bef39e..896201c456 100644 --- a/ThirdPartyNotices.txt +++ b/ThirdPartyNotices.txt @@ -88,12 +88,247 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ============================================================================== -LLVM Release License +The LLVM Project is under the Apache License v2.0 with LLVM Exceptions: +============================================================================== + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +---- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. + +============================================================================== +Software from third parties included in the LLVM Project: +============================================================================== +The LLVM Project contains third party software which is under different license +terms. All such code will be identified clearly using at least one of two +mechanisms: +1) It will be in a separate directory tree with its own `LICENSE.txt` or + `LICENSE` file at the top containing the specific license and restrictions + which apply to that software, or +2) It will contain specific license and restriction terms at the top of every + file. + +============================================================================== +Legacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy): ============================================================================== University of Illinois/NCSA Open Source License -Copyright (c) 2003-2017 University of Illinois at Urbana-Champaign. +Copyright (c) 2003-2019 University of Illinois at Urbana-Champaign. All rights reserved. Developed by: diff --git a/upstream_utils/llvm_patches/0001-Fix-spelling-language-errors.patch b/upstream_utils/llvm_patches/0001-Fix-spelling-language-errors.patch new file mode 100644 index 0000000000..960285a55a --- /dev/null +++ b/upstream_utils/llvm_patches/0001-Fix-spelling-language-errors.patch @@ -0,0 +1,39 @@ +From 4f34f83152a851ce675c876f3bd0e53322110d04 Mon Sep 17 00:00:00 2001 +From: PJ Reiniger +Date: Sat, 7 May 2022 20:50:26 -0400 +Subject: [PATCH 01/31] Fix spelling / language errors + +--- + llvm/include/llvm/Support/Chrono.h | 2 +- + llvm/include/llvm/Support/ErrorHandling.h | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/llvm/include/llvm/Support/Chrono.h b/llvm/include/llvm/Support/Chrono.h +index f478549a7e4e..004519a883fd 100644 +--- a/llvm/include/llvm/Support/Chrono.h ++++ b/llvm/include/llvm/Support/Chrono.h +@@ -22,7 +22,7 @@ class raw_ostream; + namespace sys { + + /// A time point on the system clock. This is provided for two reasons: +-/// - to insulate us agains subtle differences in behavoir to differences in ++/// - to insulate us against subtle differences in behavior to differences in + /// system clock precision (which is implementation-defined and differs between + /// platforms). + /// - to shorten the type name +diff --git a/llvm/include/llvm/Support/ErrorHandling.h b/llvm/include/llvm/Support/ErrorHandling.h +index 0ec0242d569d..dd85a5892e01 100644 +--- a/llvm/include/llvm/Support/ErrorHandling.h ++++ b/llvm/include/llvm/Support/ErrorHandling.h +@@ -45,7 +45,7 @@ class StringRef; + void install_fatal_error_handler(fatal_error_handler_t handler, + void *user_data = nullptr); + +- /// Restores default error handling behaviour. ++ /// Restores default error handling behavior. + void remove_fatal_error_handler(); + + /// ScopedFatalErrorHandler - This is a simple helper class which just +-- +2.20.1.windows.1 + diff --git a/upstream_utils/llvm_patches/0002-Remove-StringRef-ArrayRef-and-Optional.patch b/upstream_utils/llvm_patches/0002-Remove-StringRef-ArrayRef-and-Optional.patch new file mode 100644 index 0000000000..4d0301f312 --- /dev/null +++ b/upstream_utils/llvm_patches/0002-Remove-StringRef-ArrayRef-and-Optional.patch @@ -0,0 +1,1762 @@ +From 3b9d1af2e3a1b031554e3f3b9d0eda4aa55d8b15 Mon Sep 17 00:00:00 2001 +From: PJ Reiniger +Date: Sat, 7 May 2022 22:09:18 -0400 +Subject: [PATCH 02/31] Remove StringRef, ArrayRef, and Optional + +--- + llvm/include/llvm/ADT/SmallSet.h | 12 +-- + llvm/include/llvm/ADT/SmallString.h | 76 +++++++++---------- + llvm/include/llvm/ADT/StringMap.h | 34 ++++----- + llvm/include/llvm/ADT/StringMapEntry.h | 23 +++--- + llvm/include/llvm/Support/Chrono.h | 10 +-- + llvm/include/llvm/Support/Compiler.h | 2 +- + llvm/include/llvm/Support/ConvertUTF.h | 25 +++--- + llvm/include/llvm/Support/ErrorHandling.h | 7 +- + .../llvm/Support/SmallVectorMemoryBuffer.h | 6 +- + llvm/include/llvm/Support/VersionTuple.h | 20 ++--- + .../llvm/Support/Windows/WindowsSupport.h | 2 - + llvm/include/llvm/Support/raw_ostream.h | 48 +++++++----- + llvm/lib/Support/ConvertUTFWrapper.cpp | 32 ++++---- + llvm/lib/Support/ErrorHandling.cpp | 15 ++-- + llvm/lib/Support/StringMap.cpp | 12 +-- + llvm/lib/Support/raw_ostream.cpp | 22 +++--- + llvm/unittests/ADT/DenseMapTest.cpp | 25 ------ + llvm/unittests/ADT/FunctionExtrasTest.cpp | 12 +-- + llvm/unittests/ADT/HashingTest.cpp | 2 +- + llvm/unittests/ADT/SmallStringTest.cpp | 50 ++++++------ + llvm/unittests/ADT/SmallVectorTest.cpp | 20 +---- + llvm/unittests/ADT/StringMapTest.cpp | 27 ++++--- + llvm/unittests/Support/ConvertUTFTest.cpp | 37 +++++---- + 23 files changed, 234 insertions(+), 285 deletions(-) + +diff --git a/llvm/include/llvm/ADT/SmallSet.h b/llvm/include/llvm/ADT/SmallSet.h +index 0600e528ee69..e4c209c5f2a9 100644 +--- a/llvm/include/llvm/ADT/SmallSet.h ++++ b/llvm/include/llvm/ADT/SmallSet.h +@@ -13,7 +13,6 @@ + #ifndef LLVM_ADT_SMALLSET_H + #define LLVM_ADT_SMALLSET_H + +-#include "llvm/ADT/None.h" + #include "llvm/ADT/SmallPtrSet.h" + #include "llvm/ADT/SmallVector.h" + #include "llvm/ADT/iterator.h" +@@ -21,6 +20,7 @@ + #include "llvm/Support/type_traits.h" + #include + #include ++#include + #include + #include + #include +@@ -177,16 +177,16 @@ public: + /// concept. + // FIXME: Add iterators that abstract over the small and large form, and then + // return those here. +- std::pair insert(const T &V) { ++ std::pair insert(const T &V) { + if (!isSmall()) +- return std::make_pair(None, Set.insert(V).second); ++ return std::make_pair(std::nullopt, Set.insert(V).second); + + VIterator I = vfind(V); + if (I != Vector.end()) // Don't reinsert if it already exists. +- return std::make_pair(None, false); ++ return std::make_pair(std::nullopt, false); + if (Vector.size() < N) { + Vector.push_back(V); +- return std::make_pair(None, true); ++ return std::make_pair(std::nullopt, true); + } + + // Otherwise, grow from vector to set. +@@ -195,7 +195,7 @@ public: + Vector.pop_back(); + } + Set.insert(V); +- return std::make_pair(None, true); ++ return std::make_pair(std::nullopt, true); + } + + template +diff --git a/llvm/include/llvm/ADT/SmallString.h b/llvm/include/llvm/ADT/SmallString.h +index 56b0639b98cc..8112741dd01d 100644 +--- a/llvm/include/llvm/ADT/SmallString.h ++++ b/llvm/include/llvm/ADT/SmallString.h +@@ -14,8 +14,8 @@ + #define LLVM_ADT_SMALLSTRING_H + + #include "llvm/ADT/SmallVector.h" +-#include "llvm/ADT/StringRef.h" + #include ++#include + + namespace llvm { + +@@ -27,11 +27,11 @@ public: + /// Default ctor - Initialize to empty. + SmallString() = default; + +- /// Initialize from a StringRef. +- SmallString(StringRef S) : SmallVector(S.begin(), S.end()) {} ++ /// Initialize from a std::string_view. ++ SmallString(std::string_view S) : SmallVector(S.begin(), S.end()) {} + +- /// Initialize by concatenating a list of StringRefs. +- SmallString(std::initializer_list Refs) ++ /// Initialize by concatenating a list of std::string_views. ++ SmallString(std::initializer_list Refs) + : SmallVector() { + this->append(Refs); + } +@@ -46,13 +46,13 @@ public: + + using SmallVector::assign; + +- /// Assign from a StringRef. +- void assign(StringRef RHS) { ++ /// Assign from a std::string_view. ++ void assign(std::string_view RHS) { + SmallVectorImpl::assign(RHS.begin(), RHS.end()); + } + +- /// Assign from a list of StringRefs. +- void assign(std::initializer_list Refs) { ++ /// Assign from a list of std::string_views. ++ void assign(std::initializer_list Refs) { + this->clear(); + append(Refs); + } +@@ -63,19 +63,19 @@ public: + + using SmallVector::append; + +- /// Append from a StringRef. +- void append(StringRef RHS) { ++ /// Append from a std::string_view. ++ void append(std::string_view RHS) { + SmallVectorImpl::append(RHS.begin(), RHS.end()); + } + +- /// Append from a list of StringRefs. +- void append(std::initializer_list Refs) { ++ /// Append from a list of std::string_views. ++ void append(std::initializer_list Refs) { + size_t SizeNeeded = this->size(); +- for (const StringRef &Ref : Refs) ++ for (const std::string_view &Ref : Refs) + SizeNeeded += Ref.size(); + this->reserve(SizeNeeded); + auto CurEnd = this->end(); +- for (const StringRef &Ref : Refs) { ++ for (const std::string_view &Ref : Refs) { + this->uninitialized_copy(Ref.begin(), Ref.end(), CurEnd); + CurEnd += Ref.size(); + } +@@ -88,29 +88,29 @@ public: + + /// Check for string equality. This is more efficient than compare() when + /// the relative ordering of inequal strings isn't needed. +- bool equals(StringRef RHS) const { ++ bool equals(std::string_view RHS) const { + return str().equals(RHS); + } + + /// Check for string equality, ignoring case. +- bool equals_insensitive(StringRef RHS) const { ++ bool equals_insensitive(std::string_view RHS) const { + return str().equals_insensitive(RHS); + } + + /// Compare two strings; the result is -1, 0, or 1 if this string is + /// lexicographically less than, equal to, or greater than the \p RHS. +- int compare(StringRef RHS) const { ++ int compare(std::string_view RHS) const { + return str().compare(RHS); + } + + /// compare_insensitive - Compare two strings, ignoring case. +- int compare_insensitive(StringRef RHS) const { ++ int compare_insensitive(std::string_view RHS) const { + return str().compare_insensitive(RHS); + } + + /// compare_numeric - Compare two strings, treating sequences of digits as + /// numbers. +- int compare_numeric(StringRef RHS) const { ++ int compare_numeric(std::string_view RHS) const { + return str().compare_numeric(RHS); + } + +@@ -119,12 +119,12 @@ public: + /// @{ + + /// startswith - Check if this string starts with the given \p Prefix. +- bool startswith(StringRef Prefix) const { ++ bool startswith(std::string_view Prefix) const { + return str().startswith(Prefix); + } + + /// endswith - Check if this string ends with the given \p Suffix. +- bool endswith(StringRef Suffix) const { ++ bool endswith(std::string_view Suffix) const { + return str().endswith(Suffix); + } + +@@ -144,7 +144,7 @@ public: + /// + /// \returns The index of the first occurrence of \p Str, or npos if not + /// found. +- size_t find(StringRef Str, size_t From = 0) const { ++ size_t find(std::string_view Str, size_t From = 0) const { + return str().find(Str, From); + } + +@@ -152,7 +152,7 @@ public: + /// + /// \returns The index of the last occurrence of \p C, or npos if not + /// found. +- size_t rfind(char C, size_t From = StringRef::npos) const { ++ size_t rfind(char C, size_t From = std::string_view::npos) const { + return str().rfind(C, From); + } + +@@ -160,7 +160,7 @@ public: + /// + /// \returns The index of the last occurrence of \p Str, or npos if not + /// found. +- size_t rfind(StringRef Str) const { ++ size_t rfind(std::string_view Str) const { + return str().rfind(Str); + } + +@@ -174,7 +174,7 @@ public: + /// not found. + /// + /// Complexity: O(size() + Chars.size()) +- size_t find_first_of(StringRef Chars, size_t From = 0) const { ++ size_t find_first_of(std::string_view Chars, size_t From = 0) const { + return str().find_first_of(Chars, From); + } + +@@ -188,13 +188,13 @@ public: + /// \p Chars, or npos if not found. + /// + /// Complexity: O(size() + Chars.size()) +- size_t find_first_not_of(StringRef Chars, size_t From = 0) const { ++ size_t find_first_not_of(std::string_view Chars, size_t From = 0) const { + return str().find_first_not_of(Chars, From); + } + + /// Find the last character in the string that is \p C, or npos if not + /// found. +- size_t find_last_of(char C, size_t From = StringRef::npos) const { ++ size_t find_last_of(char C, size_t From = std::string_view::npos) const { + return str().find_last_of(C, From); + } + +@@ -203,7 +203,7 @@ public: + /// + /// Complexity: O(size() + Chars.size()) + size_t find_last_of( +- StringRef Chars, size_t From = StringRef::npos) const { ++ std::string_view Chars, size_t From = std::string_view::npos) const { + return str().find_last_of(Chars, From); + } + +@@ -218,7 +218,7 @@ public: + + /// Return the number of non-overlapped occurrences of \p Str in the + /// string. +- size_t count(StringRef Str) const { ++ size_t count(std::string_view Str) const { + return str().count(Str); + } + +@@ -235,7 +235,7 @@ public: + /// \param N The number of characters to included in the substring. If \p N + /// exceeds the number of characters remaining in the string, the string + /// suffix (starting with \p Start) will be returned. +- StringRef substr(size_t Start, size_t N = StringRef::npos) const { ++ std::string_view substr(size_t Start, size_t N = std::string_view::npos) const { + return str().substr(Start, N); + } + +@@ -249,14 +249,14 @@ public: + /// substring. If this is npos, or less than \p Start, or exceeds the + /// number of characters remaining in the string, the string suffix + /// (starting with \p Start) will be returned. +- StringRef slice(size_t Start, size_t End) const { ++ std::string_view slice(size_t Start, size_t End) const { + return str().slice(Start, End); + } + + // Extra methods. + +- /// Explicit conversion to StringRef. +- StringRef str() const { return StringRef(this->data(), this->size()); } ++ /// Explicit conversion to std::string_view. ++ std::string_view str() const { return std::string_view(this->begin(), this->size()); } + + // TODO: Make this const, if it's safe... + const char* c_str() { +@@ -265,20 +265,20 @@ public: + return this->data(); + } + +- /// Implicit conversion to StringRef. +- operator StringRef() const { return str(); } ++ /// Implicit conversion to std::string_view. ++ operator std::string_view() const { return str(); } + + explicit operator std::string() const { + return std::string(this->data(), this->size()); + } + + // Extra operators. +- SmallString &operator=(StringRef RHS) { ++ SmallString &operator=(std::string_view RHS) { + this->assign(RHS); + return *this; + } + +- SmallString &operator+=(StringRef RHS) { ++ SmallString &operator+=(std::string_view RHS) { + this->append(RHS.begin(), RHS.end()); + return *this; + } +diff --git a/llvm/include/llvm/ADT/StringMap.h b/llvm/include/llvm/ADT/StringMap.h +index a82afc9a817c..5f463cfef943 100644 +--- a/llvm/include/llvm/ADT/StringMap.h ++++ b/llvm/include/llvm/ADT/StringMap.h +@@ -58,12 +58,12 @@ protected: + /// specified bucket will be non-null. Otherwise, it will be null. In either + /// case, the FullHashValue field of the bucket will be set to the hash value + /// of the string. +- unsigned LookupBucketFor(StringRef Key); ++ unsigned LookupBucketFor(std::string_view Key); + + /// FindKey - Look up the bucket that contains the specified key. If it exists + /// in the map, return the bucket number of the key. Otherwise return -1. + /// This does not modify the map. +- int FindKey(StringRef Key) const; ++ int FindKey(std::string_view Key) const; + + /// RemoveKey - Remove the specified StringMapEntry from the table, but do not + /// delete it. This aborts if the value isn't in the table. +@@ -71,7 +71,7 @@ protected: + + /// RemoveKey - Remove the StringMapEntry for the specified key from the + /// table, returning it. If the key is not in the table, this returns null. +- StringMapEntryBase *RemoveKey(StringRef Key); ++ StringMapEntryBase *RemoveKey(std::string_view Key); + + /// Allocate the table with the specified number of buckets and otherwise + /// setup the map as empty. +@@ -124,7 +124,7 @@ public: + : StringMapImpl(InitialSize, static_cast(sizeof(MapEntryTy))), + Allocator(A) {} + +- StringMap(std::initializer_list> List) ++ StringMap(std::initializer_list> List) + : StringMapImpl(List.size(), static_cast(sizeof(MapEntryTy))) { + for (const auto &P : List) { + insert(P); +@@ -215,14 +215,14 @@ public: + StringMapKeyIterator(end())); + } + +- iterator find(StringRef Key) { ++ iterator find(std::string_view Key) { + int Bucket = FindKey(Key); + if (Bucket == -1) + return end(); + return iterator(TheTable + Bucket, true); + } + +- const_iterator find(StringRef Key) const { ++ const_iterator find(std::string_view Key) const { + int Bucket = FindKey(Key); + if (Bucket == -1) + return end(); +@@ -231,7 +231,7 @@ public: + + /// lookup - Return the entry for the specified key, or a default + /// constructed value if no such entry exists. +- ValueTy lookup(StringRef Key) const { ++ ValueTy lookup(std::string_view Key) const { + const_iterator it = find(Key); + if (it != end()) + return it->second; +@@ -240,10 +240,10 @@ public: + + /// Lookup the ValueTy for the \p Key, or create a default constructed value + /// if the key is not in the map. +- ValueTy &operator[](StringRef Key) { return try_emplace(Key).first->second; } ++ ValueTy &operator[](std::string_view Key) { return try_emplace(Key).first->second; } + + /// count - Return 1 if the element is in the map, 0 otherwise. +- size_type count(StringRef Key) const { return find(Key) == end() ? 0 : 1; } ++ size_type count(std::string_view Key) const { return find(Key) == end() ? 0 : 1; } + + template + size_type count(const StringMapEntry &MapEntry) const { +@@ -293,14 +293,14 @@ public: + /// isn't already in the map. The bool component of the returned pair is true + /// if and only if the insertion takes place, and the iterator component of + /// the pair points to the element with key equivalent to the key of the pair. +- std::pair insert(std::pair KV) { ++ std::pair insert(std::pair KV) { + return try_emplace(KV.first, std::move(KV.second)); + } + + /// Inserts an element or assigns to the current element if the key already + /// exists. The return type is the same as try_emplace. + template +- std::pair insert_or_assign(StringRef Key, V &&Val) { ++ std::pair insert_or_assign(std::string_view Key, V &&Val) { + auto Ret = try_emplace(Key, std::forward(Val)); + if (!Ret.second) + Ret.first->second = std::forward(Val); +@@ -312,7 +312,7 @@ public: + /// if and only if the insertion takes place, and the iterator component of + /// the pair points to the element with key equivalent to the key of the pair. + template +- std::pair try_emplace(StringRef Key, ArgsTy &&... Args) { ++ std::pair try_emplace(std::string_view Key, ArgsTy &&... Args) { + unsigned BucketNo = LookupBucketFor(Key); + StringMapEntryBase *&Bucket = TheTable[BucketNo]; + if (Bucket && Bucket != getTombstoneVal()) +@@ -358,7 +358,7 @@ public: + V.Destroy(Allocator); + } + +- bool erase(StringRef Key) { ++ bool erase(std::string_view Key) { + iterator I = find(Key); + if (I == end()) + return false; +@@ -455,23 +455,23 @@ template + class StringMapKeyIterator + : public iterator_adaptor_base, + StringMapConstIterator, +- std::forward_iterator_tag, StringRef> { ++ std::forward_iterator_tag, std::string_view> { + using base = iterator_adaptor_base, + StringMapConstIterator, +- std::forward_iterator_tag, StringRef>; ++ std::forward_iterator_tag, std::string_view>; + + public: + StringMapKeyIterator() = default; + explicit StringMapKeyIterator(StringMapConstIterator Iter) + : base(std::move(Iter)) {} + +- StringRef &operator*() { ++ std::string_view &operator*() { + Key = this->wrapped()->getKey(); + return Key; + } + + private: +- StringRef Key; ++ std::string_view Key; + }; + + } // end namespace llvm +diff --git a/llvm/include/llvm/ADT/StringMapEntry.h b/llvm/include/llvm/ADT/StringMapEntry.h +index 8bfad5540230..93e13b5bb16c 100644 +--- a/llvm/include/llvm/ADT/StringMapEntry.h ++++ b/llvm/include/llvm/ADT/StringMapEntry.h +@@ -15,7 +15,8 @@ + #ifndef LLVM_ADT_STRINGMAPENTRY_H + #define LLVM_ADT_STRINGMAPENTRY_H + +-#include "llvm/ADT/StringRef.h" ++#include ++#include + + namespace llvm { + +@@ -34,13 +35,13 @@ protected: + /// type-erase the allocator and put it in a source file. + template + static void *allocateWithKey(size_t EntrySize, size_t EntryAlign, +- StringRef Key, AllocatorTy &Allocator); ++ std::string_view Key, AllocatorTy &Allocator); + }; + + // Define out-of-line to dissuade inlining. + template + void *StringMapEntryBase::allocateWithKey(size_t EntrySize, size_t EntryAlign, +- StringRef Key, ++ std::string_view Key, + AllocatorTy &Allocator) { + size_t KeyLength = Key.size(); + +@@ -82,13 +83,13 @@ public: + void setValue(const ValueTy &V) { second = V; } + }; + +-template <> class StringMapEntryStorage : public StringMapEntryBase { ++template <> class StringMapEntryStorage : public StringMapEntryBase { + public: +- explicit StringMapEntryStorage(size_t keyLength, NoneType = None) ++ explicit StringMapEntryStorage(size_t keyLength, std::nullopt_t = std::nullopt) + : StringMapEntryBase(keyLength) {} + StringMapEntryStorage(StringMapEntryStorage &entry) = delete; + +- NoneType getValue() const { return None; } ++ std::nullopt_t getValue() const { return std::nullopt; } + }; + + /// StringMapEntry - This is used to represent one value that is inserted into +@@ -99,8 +100,8 @@ class StringMapEntry final : public StringMapEntryStorage { + public: + using StringMapEntryStorage::StringMapEntryStorage; + +- StringRef getKey() const { +- return StringRef(getKeyData(), this->getKeyLength()); ++ std::string_view getKey() const { ++ return std::string_view(getKeyData(), this->getKeyLength()); + } + + /// getKeyData - Return the start of the string data that is the key for this +@@ -110,14 +111,14 @@ public: + return reinterpret_cast(this + 1); + } + +- StringRef first() const { +- return StringRef(getKeyData(), this->getKeyLength()); ++ std::string_view first() const { ++ return std::string_view(getKeyData(), this->getKeyLength()); + } + + /// Create a StringMapEntry for the specified key construct the value using + /// \p InitiVals. + template +- static StringMapEntry *Create(StringRef key, AllocatorTy &allocator, ++ static StringMapEntry *Create(std::string_view key, AllocatorTy &allocator, + InitTy &&... initVals) { + return new (StringMapEntryBase::allocateWithKey( + sizeof(StringMapEntry), alignof(StringMapEntry), key, allocator)) +diff --git a/llvm/include/llvm/Support/Chrono.h b/llvm/include/llvm/Support/Chrono.h +index 004519a883fd..2c2869de49b6 100644 +--- a/llvm/include/llvm/Support/Chrono.h ++++ b/llvm/include/llvm/Support/Chrono.h +@@ -69,7 +69,7 @@ raw_ostream &operator<<(raw_ostream &OS, sys::TimePoint<> TP); + template <> + struct format_provider> { + static void format(const sys::TimePoint<> &TP, llvm::raw_ostream &OS, +- StringRef Style); ++ std::string_view Style); + }; + + namespace detail { +@@ -121,7 +121,7 @@ private: + return duration_cast>(D).count(); + } + +- static std::pair consumeUnit(StringRef &Style, ++ static std::pair consumeUnit(std::string_view &Style, + const Dur &D) { + using namespace std::chrono; + if (Style.consume_front("ns")) +@@ -139,7 +139,7 @@ private: + return {D.count(), detail::unit::value}; + } + +- static bool consumeShowUnit(StringRef &Style) { ++ static bool consumeShowUnit(std::string_view &Style) { + if (Style.empty()) + return true; + if (Style.consume_front("-")) +@@ -151,9 +151,9 @@ private: + } + + public: +- static void format(const Dur &D, llvm::raw_ostream &Stream, StringRef Style) { ++ static void format(const Dur &D, llvm::raw_ostream &Stream, std::string_view Style) { + InternalRep count; +- StringRef unit; ++ std::string_view unit; + std::tie(count, unit) = consumeUnit(Style, D); + bool show_unit = consumeShowUnit(Style); + +diff --git a/llvm/include/llvm/Support/Compiler.h b/llvm/include/llvm/Support/Compiler.h +index 57052b596edb..f4d6612fe074 100644 +--- a/llvm/include/llvm/Support/Compiler.h ++++ b/llvm/include/llvm/Support/Compiler.h +@@ -299,7 +299,7 @@ + #endif + + /// LLVM_GSL_POINTER - Apply this to non-owning classes like +-/// StringRef to enable lifetime warnings. ++/// std::string_view to enable lifetime warnings. + #if LLVM_HAS_CPP_ATTRIBUTE(gsl::Pointer) + #define LLVM_GSL_POINTER [[gsl::Pointer]] + #else +diff --git a/llvm/include/llvm/Support/ConvertUTF.h b/llvm/include/llvm/Support/ConvertUTF.h +index 1add185330fa..7f1527f51cdf 100644 +--- a/llvm/include/llvm/Support/ConvertUTF.h ++++ b/llvm/include/llvm/Support/ConvertUTF.h +@@ -89,8 +89,11 @@ + #ifndef LLVM_SUPPORT_CONVERTUTF_H + #define LLVM_SUPPORT_CONVERTUTF_H + ++#include "wpi/span.h" ++ + #include + #include ++#include + #include + + // Wrap everything in namespace llvm so that programs can link with llvm and +@@ -180,12 +183,10 @@ unsigned getNumBytesForUTF8(UTF8 firstByte); + /*************************************************************************/ + /* Below are LLVM-specific wrappers of the functions above. */ + +-template class ArrayRef; + template class SmallVectorImpl; +-class StringRef; + + /** +- * Convert an UTF8 StringRef to UTF8, UTF16, or UTF32 depending on ++ * Convert an UTF8 string_view to UTF8, UTF16, or UTF32 depending on + * WideCharWidth. The converted data is written to ResultPtr, which needs to + * point to at least WideCharWidth * (Source.Size() + 1) bytes. On success, + * ResultPtr will point one after the end of the copied string. On failure, +@@ -193,14 +194,14 @@ class StringRef; + * the first character which could not be converted. + * \return true on success. + */ +-bool ConvertUTF8toWide(unsigned WideCharWidth, llvm::StringRef Source, ++bool ConvertUTF8toWide(unsigned WideCharWidth, std::string_view Source, + char *&ResultPtr, const UTF8 *&ErrorPtr); + + /** +-* Converts a UTF-8 StringRef to a std::wstring. ++* Converts a UTF-8 string_view to a std::wstring. + * \return true on success. + */ +-bool ConvertUTF8toWide(llvm::StringRef Source, std::wstring &Result); ++bool ConvertUTF8toWide(std::string_view Source, std::wstring &Result); + + /** + * Converts a UTF-8 C-string to a std::wstring. +@@ -258,7 +259,7 @@ inline ConversionResult convertUTF8Sequence(const UTF8 **source, + * Returns true if a blob of text starts with a UTF-16 big or little endian byte + * order mark. + */ +-bool hasUTF16ByteOrderMark(ArrayRef SrcBytes); ++bool hasUTF16ByteOrderMark(span SrcBytes); + + /** + * Converts a stream of raw bytes assumed to be UTF16 into a UTF8 std::string. +@@ -267,7 +268,7 @@ bool hasUTF16ByteOrderMark(ArrayRef SrcBytes); + * \param [out] Out Converted UTF-8 is stored here on success. + * \returns true on success + */ +-bool convertUTF16ToUTF8String(ArrayRef SrcBytes, std::string &Out); ++bool convertUTF16ToUTF8String(span SrcBytes, std::string &Out); + + /** + * Converts a UTF16 string into a UTF8 std::string. +@@ -276,22 +277,22 @@ bool convertUTF16ToUTF8String(ArrayRef SrcBytes, std::string &Out); + * \param [out] Out Converted UTF-8 is stored here on success. + * \returns true on success + */ +-bool convertUTF16ToUTF8String(ArrayRef Src, std::string &Out); ++bool convertUTF16ToUTF8String(span Src, std::string &Out); + + /** + * Converts a UTF-8 string into a UTF-16 string with native endianness. + * + * \returns true on success + */ +-bool convertUTF8ToUTF16String(StringRef SrcUTF8, ++bool convertUTF8ToUTF16String(std::string_view SrcUTF8, + SmallVectorImpl &DstUTF16); + + #if defined(_WIN32) + namespace sys { + namespace windows { +-std::error_code UTF8ToUTF16(StringRef utf8, SmallVectorImpl &utf16); ++std::error_code UTF8ToUTF16(std::string_view utf8, SmallVectorImpl &utf16); + /// Convert to UTF16 from the current code page used in the system +-std::error_code CurCPToUTF16(StringRef utf8, SmallVectorImpl &utf16); ++std::error_code CurCPToUTF16(std::string_view utf8, SmallVectorImpl &utf16); + std::error_code UTF16ToUTF8(const wchar_t *utf16, size_t utf16_len, + SmallVectorImpl &utf8); + /// Convert from UTF16 to the current code page used in the system +diff --git a/llvm/include/llvm/Support/ErrorHandling.h b/llvm/include/llvm/Support/ErrorHandling.h +index dd85a5892e01..411c78a81b24 100644 +--- a/llvm/include/llvm/Support/ErrorHandling.h ++++ b/llvm/include/llvm/Support/ErrorHandling.h +@@ -16,10 +16,9 @@ + + #include "llvm/Support/Compiler.h" + #include ++#include + + namespace llvm { +-class StringRef; +- class Twine; + + /// An error handler callback. + typedef void (*fatal_error_handler_t)(void *user_data, +@@ -72,9 +71,7 @@ LLVM_ATTRIBUTE_NORETURN void report_fatal_error(const char *reason, + bool gen_crash_diag = true); + LLVM_ATTRIBUTE_NORETURN void report_fatal_error(const std::string &reason, + bool gen_crash_diag = true); +-LLVM_ATTRIBUTE_NORETURN void report_fatal_error(StringRef reason, +- bool gen_crash_diag = true); +-LLVM_ATTRIBUTE_NORETURN void report_fatal_error(const Twine &reason, ++LLVM_ATTRIBUTE_NORETURN void report_fatal_error(std::string_view reason, + bool gen_crash_diag = true); + + /// Installs a new bad alloc error handler that should be used whenever a +diff --git a/llvm/include/llvm/Support/SmallVectorMemoryBuffer.h b/llvm/include/llvm/Support/SmallVectorMemoryBuffer.h +index 9aa4e9aec266..924ed77510fe 100644 +--- a/llvm/include/llvm/Support/SmallVectorMemoryBuffer.h ++++ b/llvm/include/llvm/Support/SmallVectorMemoryBuffer.h +@@ -42,8 +42,8 @@ public: + } + + /// Construct a named SmallVectorMemoryBuffer from the given +- /// SmallVector r-value and StringRef. +- SmallVectorMemoryBuffer(SmallVectorImpl &&SV, StringRef Name) ++ /// SmallVector r-value and std::string_view. ++ SmallVectorMemoryBuffer(SmallVectorImpl &&SV, std::string_view Name) + : SV(std::move(SV)), BufferName(std::string(Name)) { + init(this->SV.begin(), this->SV.end(), false); + } +@@ -51,7 +51,7 @@ public: + // Key function. + ~SmallVectorMemoryBuffer() override; + +- StringRef getBufferIdentifier() const override { return BufferName; } ++ std::string_view getBufferIdentifier() const override { return BufferName; } + + BufferKind getBufferKind() const override { return MemoryBuffer_Malloc; } + +diff --git a/llvm/include/llvm/Support/VersionTuple.h b/llvm/include/llvm/Support/VersionTuple.h +index a48ae0bf52bd..85f4f1a707c7 100644 +--- a/llvm/include/llvm/Support/VersionTuple.h ++++ b/llvm/include/llvm/Support/VersionTuple.h +@@ -16,13 +16,12 @@ + + #include "llvm/ADT/DenseMapInfo.h" + #include "llvm/ADT/Hashing.h" +-#include "llvm/ADT/Optional.h" ++#include + #include + #include + + namespace llvm { + class raw_ostream; +-class StringRef; + + /// Represents a version number in the form major[.minor[.subminor[.build]]]. + class VersionTuple { +@@ -69,23 +68,23 @@ public: + unsigned getMajor() const { return Major; } + + /// Retrieve the minor version number, if provided. +- Optional getMinor() const { ++ std::optional getMinor() const { + if (!HasMinor) +- return None; ++ return std::nullopt; + return Minor; + } + + /// Retrieve the subminor version number, if provided. +- Optional getSubminor() const { ++ std::optional getSubminor() const { + if (!HasSubminor) +- return None; ++ return std::nullopt; + return Subminor; + } + + /// Retrieve the build version number, if provided. +- Optional getBuild() const { ++ std::optional getBuild() const { + if (!HasBuild) +- return None; ++ return std::nullopt; + return Build; + } + +@@ -166,11 +165,6 @@ public: + + /// Retrieve a string representation of the version number. + std::string getAsString() const; +- +- /// Try to parse the given string as a version number. +- /// \returns \c true if the string does not match the regular expression +- /// [0-9]+(\.[0-9]+){0,3} +- bool tryParse(StringRef string); + }; + + /// Print a version number. +diff --git a/llvm/include/llvm/Support/Windows/WindowsSupport.h b/llvm/include/llvm/Support/Windows/WindowsSupport.h +index a45eeaba4ad5..551bd199551e 100644 +--- a/llvm/include/llvm/Support/Windows/WindowsSupport.h ++++ b/llvm/include/llvm/Support/Windows/WindowsSupport.h +@@ -35,8 +35,6 @@ + + #include "llvm/ADT/SmallVector.h" + #include "llvm/ADT/StringExtras.h" +-#include "llvm/ADT/StringRef.h" +-#include "llvm/ADT/Twine.h" + #include "llvm/Config/llvm-config.h" // Get build system configuration settings + #include "llvm/Support/Allocator.h" + #include "llvm/Support/Chrono.h" +diff --git a/llvm/include/llvm/Support/raw_ostream.h b/llvm/include/llvm/Support/raw_ostream.h +index c669c2babad9..4f6ea873304f 100644 +--- a/llvm/include/llvm/Support/raw_ostream.h ++++ b/llvm/include/llvm/Support/raw_ostream.h +@@ -14,8 +14,7 @@ + #define LLVM_SUPPORT_RAW_OSTREAM_H + + #include "llvm/ADT/SmallVector.h" +-#include "llvm/ADT/StringRef.h" +-#include "llvm/Support/DataTypes.h" ++#include "llvm/ADT/span.h" + #include + #include + #include +@@ -209,7 +208,22 @@ public: + return *this; + } + +- raw_ostream &operator<<(StringRef Str) { ++ raw_ostream &operator<<(span Arr) { ++ // Inline fast path, particularly for arrays with a known length. ++ size_t Size = Arr.size(); ++ ++ // Make sure we can use the fast path. ++ if (Size > (size_t)(OutBufEnd - OutBufCur)) ++ return write(Arr.data(), Size); ++ ++ if (Size) { ++ memcpy(OutBufCur, Arr.data(), Size); ++ OutBufCur += Size; ++ } ++ return *this; ++ } ++ ++ raw_ostream &operator<<(std::string_view Str) { + // Inline fast path, particularly for strings with a known length. + size_t Size = Str.size(); + +@@ -228,7 +242,7 @@ public: + // Inline fast path, particularly for constant strings where a sufficiently + // smart compiler will simplify strlen. + +- return this->operator<<(StringRef(Str)); ++ return this->operator<<(std::string_view(Str)); + } + + raw_ostream &operator<<(const std::string &Str) { +@@ -236,12 +250,6 @@ public: + return write(Str.data(), Str.length()); + } + +-#if __cplusplus > 201402L +- raw_ostream &operator<<(const std::string_view &Str) { +- return write(Str.data(), Str.length()); +- } +-#endif +- + raw_ostream &operator<<(const SmallVectorImpl &Str) { + return write(Str.data(), Str.size()); + } +@@ -274,7 +282,7 @@ public: + + /// Output \p Str, turning '\\', '\t', '\n', '"', and anything that doesn't + /// satisfy llvm::isPrint into an escape sequence. +- raw_ostream &write_escaped(StringRef Str, bool UseHexEscapes = false); ++ raw_ostream &write_escaped(std::string_view Str, bool UseHexEscapes = false); + + raw_ostream &write(unsigned char C); + raw_ostream &write(const char *Ptr, size_t Size); +@@ -487,14 +495,14 @@ public: + /// As a special case, if Filename is "-", then the stream will use + /// STDOUT_FILENO instead of opening a file. This will not close the stdout + /// descriptor. +- raw_fd_ostream(StringRef Filename, std::error_code &EC); +- raw_fd_ostream(StringRef Filename, std::error_code &EC, ++ raw_fd_ostream(std::string_view Filename, std::error_code &EC); ++ raw_fd_ostream(std::string_view Filename, std::error_code &EC, + sys::fs::CreationDisposition Disp); +- raw_fd_ostream(StringRef Filename, std::error_code &EC, ++ raw_fd_ostream(std::string_view Filename, std::error_code &EC, + sys::fs::FileAccess Access); +- raw_fd_ostream(StringRef Filename, std::error_code &EC, ++ raw_fd_ostream(std::string_view Filename, std::error_code &EC, + sys::fs::OpenFlags Flags); +- raw_fd_ostream(StringRef Filename, std::error_code &EC, ++ raw_fd_ostream(std::string_view Filename, std::error_code &EC, + sys::fs::CreationDisposition Disp, sys::fs::FileAccess Access, + sys::fs::OpenFlags Flags); + +@@ -597,7 +605,7 @@ public: + /// Open the specified file for reading/writing/seeking. If an error occurs, + /// information about the error is put into EC, and the stream should be + /// immediately destroyed. +- raw_fd_stream(StringRef Filename, std::error_code &EC); ++ raw_fd_stream(std::string_view Filename, std::error_code &EC); + + /// This reads the \p Size bytes into a buffer pointed by \p Ptr. + /// +@@ -677,8 +685,8 @@ public: + + void flush() = delete; + +- /// Return a StringRef for the vector contents. +- StringRef str() const { return StringRef(OS.data(), OS.size()); } ++ /// Return a std::string_view for the vector contents. ++ std::string_view str() const { return std::string_view(OS.data(), OS.size()); } + + void reserveExtraSpace(uint64_t ExtraSize) override { + OS.reserve(tell() + ExtraSize); +@@ -731,7 +739,7 @@ class Error; + /// for other names. For raw_fd_ostream instances, the stream writes to + /// a temporary file. The final output file is atomically replaced with the + /// temporary file after the \p Write function is finished. +-Error writeToOutput(StringRef OutputFileName, ++Error writeToOutput(std::string_view OutputFileName, + std::function Write); + + } // end namespace llvm +diff --git a/llvm/lib/Support/ConvertUTFWrapper.cpp b/llvm/lib/Support/ConvertUTFWrapper.cpp +index d8d46712a593..090ebb0f5af8 100644 +--- a/llvm/lib/Support/ConvertUTFWrapper.cpp ++++ b/llvm/lib/Support/ConvertUTFWrapper.cpp +@@ -6,24 +6,24 @@ + // + //===----------------------------------------------------------------------===// + +-#include "llvm/ADT/ArrayRef.h" +-#include "llvm/ADT/StringRef.h" ++#include "llvm/ADT/span.h" + #include "llvm/Support/ConvertUTF.h" + #include "llvm/Support/ErrorHandling.h" + #include "llvm/Support/SwapByteOrder.h" + #include ++#include + #include + + namespace llvm { + +-bool ConvertUTF8toWide(unsigned WideCharWidth, llvm::StringRef Source, ++bool ConvertUTF8toWide(unsigned WideCharWidth, std::string_view Source, + char *&ResultPtr, const UTF8 *&ErrorPtr) { + assert(WideCharWidth == 1 || WideCharWidth == 2 || WideCharWidth == 4); + ConversionResult result = conversionOK; + // Copy the character span over. + if (WideCharWidth == 1) { +- const UTF8 *Pos = reinterpret_cast(Source.begin()); +- if (!isLegalUTF8String(&Pos, reinterpret_cast(Source.end()))) { ++ const UTF8 *Pos = reinterpret_cast(Source.data()); ++ if (!isLegalUTF8String(&Pos, reinterpret_cast(Source.data() + Source.size()))) { + result = sourceIllegal; + ErrorPtr = Pos; + } else { +@@ -77,13 +77,13 @@ bool ConvertCodePointToUTF8(unsigned Source, char *&ResultPtr) { + return true; + } + +-bool hasUTF16ByteOrderMark(ArrayRef S) { ++bool hasUTF16ByteOrderMark(span S) { + return (S.size() >= 2 && + ((S[0] == '\xff' && S[1] == '\xfe') || + (S[0] == '\xfe' && S[1] == '\xff'))); + } + +-bool convertUTF16ToUTF8String(ArrayRef SrcBytes, std::string &Out) { ++bool convertUTF16ToUTF8String(span SrcBytes, std::string &Out) { + assert(Out.empty()); + + // Error out on an uneven byte count. +@@ -134,14 +134,14 @@ bool convertUTF16ToUTF8String(ArrayRef SrcBytes, std::string &Out) { + return true; + } + +-bool convertUTF16ToUTF8String(ArrayRef Src, std::string &Out) ++bool convertUTF16ToUTF8String(span Src, std::string &Out) + { + return convertUTF16ToUTF8String( +- llvm::ArrayRef(reinterpret_cast(Src.data()), ++ span(reinterpret_cast(Src.data()), + Src.size() * sizeof(UTF16)), Out); + } + +-bool convertUTF8ToUTF16String(StringRef SrcUTF8, ++bool convertUTF8ToUTF16String(std::string_view SrcUTF8, + SmallVectorImpl &DstUTF16) { + assert(DstUTF16.empty()); + +@@ -152,8 +152,8 @@ bool convertUTF8ToUTF16String(StringRef SrcUTF8, + return true; + } + +- const UTF8 *Src = reinterpret_cast(SrcUTF8.begin()); +- const UTF8 *SrcEnd = reinterpret_cast(SrcUTF8.end()); ++ const UTF8 *Src = reinterpret_cast(SrcUTF8.data()); ++ const UTF8 *SrcEnd = reinterpret_cast(SrcUTF8.data() + SrcUTF8.size()); + + // Allocate the same number of UTF-16 code units as UTF-8 code units. Encoding + // as UTF-16 should always require the same amount or less code units than the +@@ -184,7 +184,7 @@ static_assert(sizeof(wchar_t) == 1 || sizeof(wchar_t) == 2 || + "Expected wchar_t to be 1, 2, or 4 bytes"); + + template +-static inline bool ConvertUTF8toWideInternal(llvm::StringRef Source, ++static inline bool ConvertUTF8toWideInternal(std::string_view Source, + TResult &Result) { + // Even in the case of UTF-16, the number of bytes in a UTF-8 string is + // at least as large as the number of elements in the resulting wide +@@ -200,7 +200,7 @@ static inline bool ConvertUTF8toWideInternal(llvm::StringRef Source, + return true; + } + +-bool ConvertUTF8toWide(llvm::StringRef Source, std::wstring &Result) { ++bool ConvertUTF8toWide(std::string_view Source, std::wstring &Result) { + return ConvertUTF8toWideInternal(Source, Result); + } + +@@ -209,7 +209,7 @@ bool ConvertUTF8toWide(const char *Source, std::wstring &Result) { + Result.clear(); + return true; + } +- return ConvertUTF8toWide(llvm::StringRef(Source), Result); ++ return ConvertUTF8toWide(std::string_view(Source), Result); + } + + bool convertWideToUTF8(const std::wstring &Source, std::string &Result) { +@@ -224,7 +224,7 @@ bool convertWideToUTF8(const std::wstring &Source, std::string &Result) { + return true; + } else if (sizeof(wchar_t) == 2) { + return convertUTF16ToUTF8String( +- llvm::ArrayRef(reinterpret_cast(Source.data()), ++ span(reinterpret_cast(Source.data()), + Source.size()), + Result); + } else if (sizeof(wchar_t) == 4) { +diff --git a/llvm/lib/Support/ErrorHandling.cpp b/llvm/lib/Support/ErrorHandling.cpp +index ce6344284f06..19d1fd77af12 100644 +--- a/llvm/lib/Support/ErrorHandling.cpp ++++ b/llvm/lib/Support/ErrorHandling.cpp +@@ -14,7 +14,6 @@ + #include "llvm/Support/ErrorHandling.h" + #include "llvm-c/ErrorHandling.h" + #include "llvm/ADT/SmallVector.h" +-#include "llvm/ADT/Twine.h" + #include "llvm/Config/config.h" + #include "llvm/Support/Debug.h" + #include "llvm/Support/Errc.h" +@@ -80,18 +79,14 @@ void llvm::remove_fatal_error_handler() { + } + + void llvm::report_fatal_error(const char *Reason, bool GenCrashDiag) { +- report_fatal_error(Twine(Reason), GenCrashDiag); ++ report_fatal_error(std::string_view(Reason), GenCrashDiag); + } + + void llvm::report_fatal_error(const std::string &Reason, bool GenCrashDiag) { +- report_fatal_error(Twine(Reason), GenCrashDiag); ++ report_fatal_error(std::string_view(Reason), GenCrashDiag); + } + +-void llvm::report_fatal_error(StringRef Reason, bool GenCrashDiag) { +- report_fatal_error(Twine(Reason), GenCrashDiag); +-} +- +-void llvm::report_fatal_error(const Twine &Reason, bool GenCrashDiag) { ++void llvm::report_fatal_error(std::string_view Reason, bool GenCrashDiag) { + llvm::fatal_error_handler_t handler = nullptr; + void* handlerData = nullptr; + { +@@ -105,7 +100,7 @@ void llvm::report_fatal_error(const Twine &Reason, bool GenCrashDiag) { + } + + if (handler) { +- handler(handlerData, Reason.str(), GenCrashDiag); ++ handler(handlerData, std::string{Reason}, GenCrashDiag); + } else { + // Blast the result out to stderr. We don't try hard to make sure this + // succeeds (e.g. handling EINTR) and we can't use errs() here because +@@ -113,7 +108,7 @@ void llvm::report_fatal_error(const Twine &Reason, bool GenCrashDiag) { + SmallVector Buffer; + raw_svector_ostream OS(Buffer); + OS << "LLVM ERROR: " << Reason << "\n"; +- StringRef MessageStr = OS.str(); ++ std::string_view MessageStr = OS.str(); + ssize_t written = ::write(2, MessageStr.data(), MessageStr.size()); + (void)written; // If something went wrong, we deliberately just give up. + } +diff --git a/llvm/lib/Support/StringMap.cpp b/llvm/lib/Support/StringMap.cpp +index f65d3846623c..d9eeba619428 100644 +--- a/llvm/lib/Support/StringMap.cpp ++++ b/llvm/lib/Support/StringMap.cpp +@@ -71,7 +71,7 @@ void StringMapImpl::init(unsigned InitSize) { + /// specified bucket will be non-null. Otherwise, it will be null. In either + /// case, the FullHashValue field of the bucket will be set to the hash value + /// of the string. +-unsigned StringMapImpl::LookupBucketFor(StringRef Name) { ++unsigned StringMapImpl::LookupBucketFor(std::string_view Name) { + unsigned HTSize = NumBuckets; + if (HTSize == 0) { // Hash table unallocated so far? + init(16); +@@ -111,7 +111,7 @@ unsigned StringMapImpl::LookupBucketFor(StringRef Name) { + // Do the comparison like this because Name isn't necessarily + // null-terminated! + char *ItemStr = (char *)BucketItem + ItemSize; +- if (Name == StringRef(ItemStr, BucketItem->getKeyLength())) { ++ if (Name == std::string_view(ItemStr, BucketItem->getKeyLength())) { + // We found a match! + return BucketNo; + } +@@ -129,7 +129,7 @@ unsigned StringMapImpl::LookupBucketFor(StringRef Name) { + /// FindKey - Look up the bucket that contains the specified key. If it exists + /// in the map, return the bucket number of the key. Otherwise return -1. + /// This does not modify the map. +-int StringMapImpl::FindKey(StringRef Key) const { ++int StringMapImpl::FindKey(std::string_view Key) const { + unsigned HTSize = NumBuckets; + if (HTSize == 0) + return -1; // Really empty table? +@@ -155,7 +155,7 @@ int StringMapImpl::FindKey(StringRef Key) const { + // Do the comparison like this because NameStart isn't necessarily + // null-terminated! + char *ItemStr = (char *)BucketItem + ItemSize; +- if (Key == StringRef(ItemStr, BucketItem->getKeyLength())) { ++ if (Key == std::string_view(ItemStr, BucketItem->getKeyLength())) { + // We found a match! + return BucketNo; + } +@@ -174,14 +174,14 @@ int StringMapImpl::FindKey(StringRef Key) const { + /// delete it. This aborts if the value isn't in the table. + void StringMapImpl::RemoveKey(StringMapEntryBase *V) { + const char *VStr = (char *)V + ItemSize; +- StringMapEntryBase *V2 = RemoveKey(StringRef(VStr, V->getKeyLength())); ++ StringMapEntryBase *V2 = RemoveKey(std::string_view(VStr, V->getKeyLength())); + (void)V2; + assert(V == V2 && "Didn't find key?"); + } + + /// RemoveKey - Remove the StringMapEntry for the specified key from the + /// table, returning it. If the key is not in the table, this returns null. +-StringMapEntryBase *StringMapImpl::RemoveKey(StringRef Key) { ++StringMapEntryBase *StringMapImpl::RemoveKey(std::string_view Key) { + int Bucket = FindKey(Key); + if (Bucket == -1) + return nullptr; +diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp +index d4e1c884d125..306cc981ed8f 100644 +--- a/llvm/lib/Support/raw_ostream.cpp ++++ b/llvm/lib/Support/raw_ostream.cpp +@@ -160,7 +160,7 @@ raw_ostream &raw_ostream::write_uuid(const uuid_t UUID) { + } + + +-raw_ostream &raw_ostream::write_escaped(StringRef Str, ++raw_ostream &raw_ostream::write_escaped(std::string_view Str, + bool UseHexEscapes) { + for (unsigned char c : Str) { + switch (c) { +@@ -564,7 +564,7 @@ void format_object_base::home() { + // raw_fd_ostream + //===----------------------------------------------------------------------===// + +-static int getFD(StringRef Filename, std::error_code &EC, ++static int getFD(std::string_view Filename, std::error_code &EC, + sys::fs::CreationDisposition Disp, sys::fs::FileAccess Access, + sys::fs::OpenFlags Flags) { + assert((Access & sys::fs::FA_Write) && +@@ -590,25 +590,25 @@ static int getFD(StringRef Filename, std::error_code &EC, + return FD; + } + +-raw_fd_ostream::raw_fd_ostream(StringRef Filename, std::error_code &EC) ++raw_fd_ostream::raw_fd_ostream(std::string_view Filename, std::error_code &EC) + : raw_fd_ostream(Filename, EC, sys::fs::CD_CreateAlways, sys::fs::FA_Write, + sys::fs::OF_None) {} + +-raw_fd_ostream::raw_fd_ostream(StringRef Filename, std::error_code &EC, ++raw_fd_ostream::raw_fd_ostream(std::string_view Filename, std::error_code &EC, + sys::fs::CreationDisposition Disp) + : raw_fd_ostream(Filename, EC, Disp, sys::fs::FA_Write, sys::fs::OF_None) {} + +-raw_fd_ostream::raw_fd_ostream(StringRef Filename, std::error_code &EC, ++raw_fd_ostream::raw_fd_ostream(std::string_view Filename, std::error_code &EC, + sys::fs::FileAccess Access) + : raw_fd_ostream(Filename, EC, sys::fs::CD_CreateAlways, Access, + sys::fs::OF_None) {} + +-raw_fd_ostream::raw_fd_ostream(StringRef Filename, std::error_code &EC, ++raw_fd_ostream::raw_fd_ostream(std::string_view Filename, std::error_code &EC, + sys::fs::OpenFlags Flags) + : raw_fd_ostream(Filename, EC, sys::fs::CD_CreateAlways, sys::fs::FA_Write, + Flags) {} + +-raw_fd_ostream::raw_fd_ostream(StringRef Filename, std::error_code &EC, ++raw_fd_ostream::raw_fd_ostream(std::string_view Filename, std::error_code &EC, + sys::fs::CreationDisposition Disp, + sys::fs::FileAccess Access, + sys::fs::OpenFlags Flags) +@@ -698,7 +698,7 @@ raw_fd_ostream::~raw_fd_ostream() { + // the input is UTF-8 or transcode from the local codepage to UTF-8 before + // quoting it. If they don't, this may mess up the encoding, but this is still + // probably the best compromise we can make. +-static bool write_console_impl(int FD, StringRef Data) { ++static bool write_console_impl(int FD, std::string_view Data) { + SmallVector WideText; + + // Fall back to ::write if it wasn't valid UTF-8. +@@ -741,7 +741,7 @@ void raw_fd_ostream::write_impl(const char *Ptr, size_t Size) { + // If this is a Windows console device, try re-encoding from UTF-8 to UTF-16 + // and using WriteConsoleW. If that fails, fall back to plain write(). + if (IsWindowsConsole) +- if (write_console_impl(FD, StringRef(Ptr, Size))) ++ if (write_console_impl(FD, std::string_view(Ptr, Size))) + return; + #endif + +@@ -905,7 +905,7 @@ raw_ostream &llvm::nulls() { + // File Streams + //===----------------------------------------------------------------------===// + +-raw_fd_stream::raw_fd_stream(StringRef Filename, std::error_code &EC) ++raw_fd_stream::raw_fd_stream(std::string_view Filename, std::error_code &EC) + : raw_fd_ostream(getFD(Filename, EC, sys::fs::CD_CreateAlways, + sys::fs::FA_Write | sys::fs::FA_Read, + sys::fs::OF_None), +@@ -988,7 +988,7 @@ void buffer_ostream::anchor() {} + + void buffer_unique_ostream::anchor() {} + +-Error llvm::writeToOutput(StringRef OutputFileName, ++Error llvm::writeToOutput(std::string_view OutputFileName, + std::function Write) { + if (OutputFileName == "-") + return Write(outs()); +diff --git a/llvm/unittests/ADT/DenseMapTest.cpp b/llvm/unittests/ADT/DenseMapTest.cpp +index 352270adec0f..a122f1fe3bdf 100644 +--- a/llvm/unittests/ADT/DenseMapTest.cpp ++++ b/llvm/unittests/ADT/DenseMapTest.cpp +@@ -481,31 +481,6 @@ TEST(DenseMapCustomTest, ReserveTest) { + } + } + +-// Make sure DenseMap works with StringRef keys. +-TEST(DenseMapCustomTest, StringRefTest) { +- DenseMap M; +- +- M["a"] = 1; +- M["b"] = 2; +- M["c"] = 3; +- +- EXPECT_EQ(3u, M.size()); +- EXPECT_EQ(1, M.lookup("a")); +- EXPECT_EQ(2, M.lookup("b")); +- EXPECT_EQ(3, M.lookup("c")); +- +- EXPECT_EQ(0, M.lookup("q")); +- +- // Test the empty string, spelled various ways. +- EXPECT_EQ(0, M.lookup("")); +- EXPECT_EQ(0, M.lookup(StringRef())); +- EXPECT_EQ(0, M.lookup(StringRef("a", 0))); +- M[""] = 42; +- EXPECT_EQ(42, M.lookup("")); +- EXPECT_EQ(42, M.lookup(StringRef())); +- EXPECT_EQ(42, M.lookup(StringRef("a", 0))); +-} +- + // Key traits that allows lookup with either an unsigned or char* key; + // In the latter case, "a" == 0, "b" == 1 and so on. + struct TestDenseMapInfo { +diff --git a/llvm/unittests/ADT/FunctionExtrasTest.cpp b/llvm/unittests/ADT/FunctionExtrasTest.cpp +index 3c6f179c190f..899690b8aae8 100644 +--- a/llvm/unittests/ADT/FunctionExtrasTest.cpp ++++ b/llvm/unittests/ADT/FunctionExtrasTest.cpp +@@ -249,23 +249,23 @@ TEST(UniqueFunctionTest, Const) { + + // Overloaded call operator correctly resolved. + struct ChooseCorrectOverload { +- StringRef operator()() { return "non-const"; } +- StringRef operator()() const { return "const"; } ++ std::string_view operator()() { return "non-const"; } ++ std::string_view operator()() const { return "const"; } + }; +- unique_function ChooseMutable = ChooseCorrectOverload(); ++ unique_function ChooseMutable = ChooseCorrectOverload(); + ChooseCorrectOverload A; + EXPECT_EQ("non-const", ChooseMutable()); + EXPECT_EQ("non-const", A()); +- unique_function ChooseConst = ChooseCorrectOverload(); ++ unique_function ChooseConst = ChooseCorrectOverload(); + const ChooseCorrectOverload &X = A; + EXPECT_EQ("const", ChooseConst()); + EXPECT_EQ("const", X()); + } + + // Test that overloads on unique_functions are resolved as expected. +-std::string returns(StringRef) { return "not a function"; } ++std::string returns(std::string_view) { return "not a function"; } + std::string returns(unique_function F) { return "number"; } +-std::string returns(unique_function F) { return "string"; } ++std::string returns(unique_function F) { return "string"; } + + TEST(UniqueFunctionTest, SFINAE) { + EXPECT_EQ("not a function", returns("boo!")); +diff --git a/llvm/unittests/ADT/HashingTest.cpp b/llvm/unittests/ADT/HashingTest.cpp +index d2cda3afdda0..3605bbdcdfe3 100644 +--- a/llvm/unittests/ADT/HashingTest.cpp ++++ b/llvm/unittests/ADT/HashingTest.cpp +@@ -276,7 +276,7 @@ TEST(HashingTest, HashCombineRangeGoldenTest) { + #endif + }; + for (unsigned i = 0; i < sizeof(golden_data)/sizeof(*golden_data); ++i) { +- StringRef str = golden_data[i].s; ++ std::string_view str = golden_data[i].s; + hash_code hash = hash_combine_range(str.begin(), str.end()); + #if 0 // Enable this to generate paste-able text for the above structure. + std::string member_str = "\"" + str.str() + "\","; +diff --git a/llvm/unittests/ADT/SmallStringTest.cpp b/llvm/unittests/ADT/SmallStringTest.cpp +index b207f582e919..bee3875d11c9 100644 +--- a/llvm/unittests/ADT/SmallStringTest.cpp ++++ b/llvm/unittests/ADT/SmallStringTest.cpp +@@ -50,43 +50,43 @@ TEST_F(SmallStringTest, AssignRepeated) { + } + + TEST_F(SmallStringTest, AssignIterPair) { +- StringRef abc = "abc"; ++ std::string_view abc = "abc"; + theString.assign(abc.begin(), abc.end()); + EXPECT_EQ(3u, theString.size()); + EXPECT_STREQ("abc", theString.c_str()); + } + +-TEST_F(SmallStringTest, AssignStringRef) { +- StringRef abc = "abc"; ++TEST_F(SmallStringTest, AssignStringView) { ++ std::string_view abc = "abc"; + theString.assign(abc); + EXPECT_EQ(3u, theString.size()); + EXPECT_STREQ("abc", theString.c_str()); + } + + TEST_F(SmallStringTest, AssignSmallVector) { +- StringRef abc = "abc"; ++ std::string_view abc = "abc"; + SmallVector abcVec(abc.begin(), abc.end()); + theString.assign(abcVec); + EXPECT_EQ(3u, theString.size()); + EXPECT_STREQ("abc", theString.c_str()); + } + +-TEST_F(SmallStringTest, AssignStringRefs) { ++TEST_F(SmallStringTest, AssignStringViews) { + theString.assign({"abc", "def", "ghi"}); + EXPECT_EQ(9u, theString.size()); + EXPECT_STREQ("abcdefghi", theString.c_str()); + } + + TEST_F(SmallStringTest, AppendIterPair) { +- StringRef abc = "abc"; ++ std::string_view abc = "abc"; + theString.append(abc.begin(), abc.end()); + theString.append(abc.begin(), abc.end()); + EXPECT_EQ(6u, theString.size()); + EXPECT_STREQ("abcabc", theString.c_str()); + } + +-TEST_F(SmallStringTest, AppendStringRef) { +- StringRef abc = "abc"; ++TEST_F(SmallStringTest, AppendStringView) { ++ std::string_view abc = "abc"; + theString.append(abc); + theString.append(abc); + EXPECT_EQ(6u, theString.size()); +@@ -94,7 +94,7 @@ TEST_F(SmallStringTest, AppendStringRef) { + } + + TEST_F(SmallStringTest, AppendSmallVector) { +- StringRef abc = "abc"; ++ std::string_view abc = "abc"; + SmallVector abcVec(abc.begin(), abc.end()); + theString.append(abcVec); + theString.append(abcVec); +@@ -102,11 +102,11 @@ TEST_F(SmallStringTest, AppendSmallVector) { + EXPECT_STREQ("abcabc", theString.c_str()); + } + +-TEST_F(SmallStringTest, AppendStringRefs) { ++TEST_F(SmallStringTest, AppendStringViews) { + theString.append({"abc", "def", "ghi"}); + EXPECT_EQ(9u, theString.size()); + EXPECT_STREQ("abcdefghi", theString.c_str()); +- StringRef Jkl = "jkl"; ++ std::string_view Jkl = "jkl"; + std::string Mno = "mno"; + SmallString<4> Pqr("pqr"); + const char *Stu = "stu"; +@@ -115,15 +115,15 @@ TEST_F(SmallStringTest, AppendStringRefs) { + EXPECT_STREQ("abcdefghijklmnopqrstu", theString.c_str()); + } + +-TEST_F(SmallStringTest, StringRefConversion) { +- StringRef abc = "abc"; ++TEST_F(SmallStringTest, StringViewConversion) { ++ std::string_view abc = "abc"; + theString.assign(abc.begin(), abc.end()); +- StringRef theStringRef = theString; +- EXPECT_EQ("abc", theStringRef); ++ std::string_view theStringView = theString; ++ EXPECT_EQ("abc", theStringView); + } + + TEST_F(SmallStringTest, StdStringConversion) { +- StringRef abc = "abc"; ++ std::string_view abc = "abc"; + theString.assign(abc.begin(), abc.end()); + std::string theStdString = std::string(theString); + EXPECT_EQ("abc", theStdString); +@@ -149,29 +149,29 @@ TEST_F(SmallStringTest, Slice) { + TEST_F(SmallStringTest, Find) { + theString = "hello"; + EXPECT_EQ(2U, theString.find('l')); +- EXPECT_EQ(StringRef::npos, theString.find('z')); +- EXPECT_EQ(StringRef::npos, theString.find("helloworld")); ++ EXPECT_EQ(std::string_view::npos, theString.find('z')); ++ EXPECT_EQ(std::string_view::npos, theString.find("helloworld")); + EXPECT_EQ(0U, theString.find("hello")); + EXPECT_EQ(1U, theString.find("ello")); +- EXPECT_EQ(StringRef::npos, theString.find("zz")); ++ EXPECT_EQ(std::string_view::npos, theString.find("zz")); + EXPECT_EQ(2U, theString.find("ll", 2)); +- EXPECT_EQ(StringRef::npos, theString.find("ll", 3)); ++ EXPECT_EQ(std::string_view::npos, theString.find("ll", 3)); + EXPECT_EQ(0U, theString.find("")); + + EXPECT_EQ(3U, theString.rfind('l')); +- EXPECT_EQ(StringRef::npos, theString.rfind('z')); +- EXPECT_EQ(StringRef::npos, theString.rfind("helloworld")); ++ EXPECT_EQ(std::string_view::npos, theString.rfind('z')); ++ EXPECT_EQ(std::string_view::npos, theString.rfind("helloworld")); + EXPECT_EQ(0U, theString.rfind("hello")); + EXPECT_EQ(1U, theString.rfind("ello")); +- EXPECT_EQ(StringRef::npos, theString.rfind("zz")); ++ EXPECT_EQ(std::string_view::npos, theString.rfind("zz")); + + EXPECT_EQ(2U, theString.find_first_of('l')); + EXPECT_EQ(1U, theString.find_first_of("el")); +- EXPECT_EQ(StringRef::npos, theString.find_first_of("xyz")); ++ EXPECT_EQ(std::string_view::npos, theString.find_first_of("xyz")); + + EXPECT_EQ(1U, theString.find_first_not_of('h')); + EXPECT_EQ(4U, theString.find_first_not_of("hel")); +- EXPECT_EQ(StringRef::npos, theString.find_first_not_of("hello")); ++ EXPECT_EQ(std::string_view::npos, theString.find_first_not_of("hello")); + + theString = "hellx xello hell ello world foo bar hello"; + EXPECT_EQ(36U, theString.find("hello")); +diff --git a/llvm/unittests/ADT/SmallVectorTest.cpp b/llvm/unittests/ADT/SmallVectorTest.cpp +index 06b98efe66ef..1914f38fac6c 100644 +--- a/llvm/unittests/ADT/SmallVectorTest.cpp ++++ b/llvm/unittests/ADT/SmallVectorTest.cpp +@@ -11,7 +11,7 @@ + //===----------------------------------------------------------------------===// + + #include "llvm/ADT/SmallVector.h" +-#include "llvm/ADT/ArrayRef.h" ++#include "llvm/ADT/span.h" + #include "llvm/Support/Compiler.h" + #include "gtest/gtest.h" + #include +@@ -1044,24 +1044,6 @@ TEST(SmallVectorTest, DefaultInlinedElements) { + EXPECT_EQ(NestedV[0][0][0], 42); + } + +-TEST(SmallVectorTest, InitializerList) { +- SmallVector V1 = {}; +- EXPECT_TRUE(V1.empty()); +- V1 = {0, 0}; +- EXPECT_TRUE(makeArrayRef(V1).equals({0, 0})); +- V1 = {-1, -1}; +- EXPECT_TRUE(makeArrayRef(V1).equals({-1, -1})); +- +- SmallVector V2 = {1, 2, 3, 4}; +- EXPECT_TRUE(makeArrayRef(V2).equals({1, 2, 3, 4})); +- V2.assign({4}); +- EXPECT_TRUE(makeArrayRef(V2).equals({4})); +- V2.append({3, 2}); +- EXPECT_TRUE(makeArrayRef(V2).equals({4, 3, 2})); +- V2.insert(V2.begin() + 1, 5); +- EXPECT_TRUE(makeArrayRef(V2).equals({4, 5, 3, 2})); +-} +- + template + class SmallVectorReferenceInvalidationTest : public SmallVectorTestBase { + protected: +diff --git a/llvm/unittests/ADT/StringMapTest.cpp b/llvm/unittests/ADT/StringMapTest.cpp +index 98fbd6e1df5a..4c4aec2184b7 100644 +--- a/llvm/unittests/ADT/StringMapTest.cpp ++++ b/llvm/unittests/ADT/StringMapTest.cpp +@@ -7,7 +7,6 @@ + //===----------------------------------------------------------------------===// + + #include "llvm/ADT/StringMap.h" +-#include "llvm/ADT/Twine.h" + #include "llvm/Support/DataTypes.h" + #include "gtest/gtest.h" + #include +@@ -37,10 +36,10 @@ protected: + + // Lookup tests + EXPECT_EQ(0u, testMap.count(testKey)); +- EXPECT_EQ(0u, testMap.count(StringRef(testKeyFirst, testKeyLength))); ++ EXPECT_EQ(0u, testMap.count(std::string_view(testKeyFirst, testKeyLength))); + EXPECT_EQ(0u, testMap.count(testKeyStr)); + EXPECT_TRUE(testMap.find(testKey) == testMap.end()); +- EXPECT_TRUE(testMap.find(StringRef(testKeyFirst, testKeyLength)) == ++ EXPECT_TRUE(testMap.find(std::string_view(testKeyFirst, testKeyLength)) == + testMap.end()); + EXPECT_TRUE(testMap.find(testKeyStr) == testMap.end()); + } +@@ -60,10 +59,10 @@ protected: + + // Lookup tests + EXPECT_EQ(1u, testMap.count(testKey)); +- EXPECT_EQ(1u, testMap.count(StringRef(testKeyFirst, testKeyLength))); ++ EXPECT_EQ(1u, testMap.count(std::string_view(testKeyFirst, testKeyLength))); + EXPECT_EQ(1u, testMap.count(testKeyStr)); + EXPECT_TRUE(testMap.find(testKey) == testMap.begin()); +- EXPECT_TRUE(testMap.find(StringRef(testKeyFirst, testKeyLength)) == ++ EXPECT_TRUE(testMap.find(std::string_view(testKeyFirst, testKeyLength)) == + testMap.begin()); + EXPECT_TRUE(testMap.find(testKeyStr) == testMap.begin()); + } +@@ -103,10 +102,10 @@ TEST_F(StringMapTest, ConstEmptyMapTest) { + + // Lookup tests + EXPECT_EQ(0u, constTestMap.count(testKey)); +- EXPECT_EQ(0u, constTestMap.count(StringRef(testKeyFirst, testKeyLength))); ++ EXPECT_EQ(0u, constTestMap.count(std::string_view(testKeyFirst, testKeyLength))); + EXPECT_EQ(0u, constTestMap.count(testKeyStr)); + EXPECT_TRUE(constTestMap.find(testKey) == constTestMap.end()); +- EXPECT_TRUE(constTestMap.find(StringRef(testKeyFirst, testKeyLength)) == ++ EXPECT_TRUE(constTestMap.find(std::string_view(testKeyFirst, testKeyLength)) == + constTestMap.end()); + EXPECT_TRUE(constTestMap.find(testKeyStr) == constTestMap.end()); + } +@@ -227,7 +226,7 @@ TEST_F(StringMapTest, StringMapEntryTest) { + MallocAllocator Allocator; + StringMap::value_type *entry = + StringMap::value_type::Create( +- StringRef(testKeyFirst, testKeyLength), Allocator, 1u); ++ std::string_view(testKeyFirst, testKeyLength), 1u); + EXPECT_STREQ(testKey, entry->first().data()); + EXPECT_EQ(1u, entry->second); + entry->Destroy(Allocator); +@@ -238,7 +237,7 @@ TEST_F(StringMapTest, InsertTest) { + SCOPED_TRACE("InsertTest"); + testMap.insert( + StringMap::value_type::Create( +- StringRef(testKeyFirst, testKeyLength), ++ std::string_view(testKeyFirst, testKeyLength), + testMap.getAllocator(), 1u)); + assertSingleItemMap(); + } +@@ -311,7 +310,7 @@ TEST_F(StringMapTest, IterMapKeys) { + auto Keys = to_vector<4>(Map.keys()); + llvm::sort(Keys); + +- SmallVector Expected = {"A", "B", "C", "D"}; ++ SmallVector Expected = {"A", "B", "C", "D"}; + EXPECT_EQ(Expected, Keys); + } + +@@ -353,13 +352,13 @@ private: + TEST_F(StringMapTest, MoveOnly) { + StringMap t; + t.insert(std::make_pair("Test", MoveOnly(42))); +- StringRef Key = "Test"; ++ std::string_view Key = "Test"; + StringMapEntry::Create(Key, t.getAllocator(), MoveOnly(42)) + ->Destroy(t.getAllocator()); + } + + TEST_F(StringMapTest, CtorArg) { +- StringRef Key = "Test"; ++ std::string_view Key = "Test"; + MallocAllocator Allocator; + StringMapEntry::Create(Key, Allocator, Immovable()) + ->Destroy(Allocator); +@@ -534,7 +533,7 @@ TEST(StringMapCustomTest, InitialSizeTest) { + CountCtorCopyAndMove::Copy = 0; + for (int i = 0; i < Size; ++i) + Map.insert(std::pair( +- std::piecewise_construct, std::forward_as_tuple(Twine(i).str()), ++ std::piecewise_construct, std::forward_as_tuple(std::to_string(i)), + std::forward_as_tuple(i))); + // After the initial move, the map will move the Elts in the Entry. + EXPECT_EQ((unsigned)Size * 2, CountCtorCopyAndMove::Move); +@@ -603,7 +602,7 @@ TEST(StringMapCustomTest, StringMapEntrySize) { + else + LargeValue = std::numeric_limits::max() + 1ULL; + StringMapEntry LargeEntry(LargeValue); +- StringRef Key = LargeEntry.getKey(); ++ std::string_view Key = LargeEntry.getKey(); + EXPECT_EQ(LargeValue, Key.size()); + + // Test that the entry can hold at least max size_t. +diff --git a/llvm/unittests/Support/ConvertUTFTest.cpp b/llvm/unittests/Support/ConvertUTFTest.cpp +index 7bda6ea28ad6..9c798437a12d 100644 +--- a/llvm/unittests/Support/ConvertUTFTest.cpp ++++ b/llvm/unittests/Support/ConvertUTFTest.cpp +@@ -7,7 +7,6 @@ + //===----------------------------------------------------------------------===// + + #include "llvm/Support/ConvertUTF.h" +-#include "llvm/ADT/ArrayRef.h" + #include "gtest/gtest.h" + #include + #include +@@ -17,7 +16,7 @@ using namespace llvm; + TEST(ConvertUTFTest, ConvertUTF16LittleEndianToUTF8String) { + // Src is the look of disapproval. + alignas(UTF16) static const char Src[] = "\xff\xfe\xa0\x0c_\x00\xa0\x0c"; +- ArrayRef Ref(Src, sizeof(Src) - 1); ++ span Ref(Src, sizeof(Src) - 1); + std::string Result; + bool Success = convertUTF16ToUTF8String(Ref, Result); + EXPECT_TRUE(Success); +@@ -28,7 +27,7 @@ TEST(ConvertUTFTest, ConvertUTF16LittleEndianToUTF8String) { + TEST(ConvertUTFTest, ConvertUTF16BigEndianToUTF8String) { + // Src is the look of disapproval. + alignas(UTF16) static const char Src[] = "\xfe\xff\x0c\xa0\x00_\x0c\xa0"; +- ArrayRef Ref(Src, sizeof(Src) - 1); ++ span Ref(Src, sizeof(Src) - 1); + std::string Result; + bool Success = convertUTF16ToUTF8String(Ref, Result); + EXPECT_TRUE(Success); +@@ -39,7 +38,7 @@ TEST(ConvertUTFTest, ConvertUTF16BigEndianToUTF8String) { + TEST(ConvertUTFTest, ConvertUTF8ToUTF16String) { + // Src is the look of disapproval. + static const char Src[] = "\xe0\xb2\xa0_\xe0\xb2\xa0"; +- StringRef Ref(Src, sizeof(Src) - 1); ++ std::string_view Ref(Src, sizeof(Src) - 1); + SmallVector Result; + bool Success = convertUTF8ToUTF16String(Ref, Result); + EXPECT_TRUE(Success); +@@ -51,37 +50,37 @@ TEST(ConvertUTFTest, ConvertUTF8ToUTF16String) { + + TEST(ConvertUTFTest, OddLengthInput) { + std::string Result; +- bool Success = convertUTF16ToUTF8String(makeArrayRef("xxxxx", 5), Result); ++ bool Success = convertUTF16ToUTF8String(span("xxxxx", 5), Result); + EXPECT_FALSE(Success); + } + + TEST(ConvertUTFTest, Empty) { + std::string Result; +- bool Success = convertUTF16ToUTF8String(llvm::ArrayRef(None), Result); ++ bool Success = convertUTF16ToUTF8String(span(), Result); + EXPECT_TRUE(Success); + EXPECT_TRUE(Result.empty()); + } + + TEST(ConvertUTFTest, HasUTF16BOM) { +- bool HasBOM = hasUTF16ByteOrderMark(makeArrayRef("\xff\xfe", 2)); ++ bool HasBOM = hasUTF16ByteOrderMark("\xff\xfe"); + EXPECT_TRUE(HasBOM); +- HasBOM = hasUTF16ByteOrderMark(makeArrayRef("\xfe\xff", 2)); ++ HasBOM = hasUTF16ByteOrderMark("\xfe\xff"); + EXPECT_TRUE(HasBOM); +- HasBOM = hasUTF16ByteOrderMark(makeArrayRef("\xfe\xff ", 3)); ++ HasBOM = hasUTF16ByteOrderMark("\xfe\xff "); + EXPECT_TRUE(HasBOM); // Don't care about odd lengths. +- HasBOM = hasUTF16ByteOrderMark(makeArrayRef("\xfe\xff\x00asdf", 6)); ++ HasBOM = hasUTF16ByteOrderMark("\xfe\xff\x00asdf"); + EXPECT_TRUE(HasBOM); + +- HasBOM = hasUTF16ByteOrderMark(None); ++ HasBOM = hasUTF16ByteOrderMark(""); + EXPECT_FALSE(HasBOM); +- HasBOM = hasUTF16ByteOrderMark(makeArrayRef("\xfe", 1)); ++ HasBOM = hasUTF16ByteOrderMark("\xfe"); + EXPECT_FALSE(HasBOM); + } + + TEST(ConvertUTFTest, UTF16WrappersForConvertUTF16ToUTF8String) { + // Src is the look of disapproval. + alignas(UTF16) static const char Src[] = "\xff\xfe\xa0\x0c_\x00\xa0\x0c"; +- ArrayRef SrcRef = makeArrayRef((const UTF16 *)Src, 4); ++ span SrcRef((const UTF16 *)Src, 4); + std::string Result; + bool Success = convertUTF16ToUTF8String(SrcRef, Result); + EXPECT_TRUE(Success); +@@ -98,7 +97,7 @@ TEST(ConvertUTFTest, ConvertUTF8toWide) { + std::wstring Expected(L"\x0ca0_\x0ca0"); + EXPECT_EQ(Expected, Result); + Result.clear(); +- Success = ConvertUTF8toWide(StringRef(Src, 7), Result); ++ Success = ConvertUTF8toWide(Src, Result); + EXPECT_TRUE(Success); + EXPECT_EQ(Expected, Result); + } +@@ -147,7 +146,7 @@ struct ConvertUTFResultContainer { + }; + + std::pair> +-ConvertUTF8ToUnicodeScalarsLenient(StringRef S) { ++ConvertUTF8ToUnicodeScalarsLenient(std::string_view S) { + const UTF8 *SourceStart = reinterpret_cast(S.data()); + + const UTF8 *SourceNext = SourceStart; +@@ -164,7 +163,7 @@ ConvertUTF8ToUnicodeScalarsLenient(StringRef S) { + } + + std::pair> +-ConvertUTF8ToUnicodeScalarsPartialLenient(StringRef S) { ++ConvertUTF8ToUnicodeScalarsPartialLenient(std::string_view S) { + const UTF8 *SourceStart = reinterpret_cast(S.data()); + + const UTF8 *SourceNext = SourceStart; +@@ -182,7 +181,7 @@ ConvertUTF8ToUnicodeScalarsPartialLenient(StringRef S) { + + ::testing::AssertionResult + CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer Expected, +- StringRef S, bool Partial = false) { ++ std::string_view S, bool Partial = false) { + ConversionResult ErrorCode; + std::vector Decoded; + if (!Partial) +@@ -277,7 +276,7 @@ TEST(ConvertUTFTest, UTF8ToUTF32Lenient) { + // U+0000 NULL + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0x0000), +- StringRef("\x00", 1))); ++ std::string_view("\x00", 1))); + + // U+0080 PADDING CHARACTER + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( +@@ -1051,7 +1050,7 @@ TEST(ConvertUTFTest, UTF8ToUTF32Lenient) { + // U+0000 NULL + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0x0000), +- StringRef("\x00", 1))); ++ std::string_view("\x00", 1))); + + // Overlong sequences of the above. + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( +-- +2.20.1.windows.1 + diff --git a/upstream_utils/llvm_patches/0003-Wrap-std-min-max-calls-in-parens-for-windows-warning.patch b/upstream_utils/llvm_patches/0003-Wrap-std-min-max-calls-in-parens-for-windows-warning.patch new file mode 100644 index 0000000000..c233dacf6c --- /dev/null +++ b/upstream_utils/llvm_patches/0003-Wrap-std-min-max-calls-in-parens-for-windows-warning.patch @@ -0,0 +1,225 @@ +From 81df3d81b0bffd848890d7c01d946e65744fb649 Mon Sep 17 00:00:00 2001 +From: PJ Reiniger +Date: Sat, 7 May 2022 22:12:41 -0400 +Subject: [PATCH 03/31] Wrap std::min/max calls in parens, for windows warnings + +--- + llvm/include/llvm/ADT/DenseMap.h | 4 ++-- + llvm/include/llvm/ADT/DenseMapInfo.h | 2 +- + llvm/include/llvm/ADT/SmallVector.h | 12 ++++++------ + llvm/include/llvm/Support/ConvertUTF.h | 2 +- + llvm/include/llvm/Support/MathExtras.h | 22 +++++++++++----------- + llvm/lib/Support/SmallVector.cpp | 2 +- + 6 files changed, 22 insertions(+), 22 deletions(-) + +diff --git a/llvm/include/llvm/ADT/DenseMap.h b/llvm/include/llvm/ADT/DenseMap.h +index 595eabd0ffb4..588c39faea2f 100644 +--- a/llvm/include/llvm/ADT/DenseMap.h ++++ b/llvm/include/llvm/ADT/DenseMap.h +@@ -389,7 +389,7 @@ protected: + return 0; + // +1 is required because of the strict equality. + // For example if NumEntries is 48, we need to return 401. +- return NextPowerOf2(NumEntries * 4 / 3 + 1); ++ return static_cast(NextPowerOf2(NumEntries * 4 / 3 + 1)); + } + + void moveFromOldBuckets(BucketT *OldBucketsBegin, BucketT *OldBucketsEnd) { +@@ -825,7 +825,7 @@ public: + // Reduce the number of buckets. + unsigned NewNumBuckets = 0; + if (OldNumEntries) +- NewNumBuckets = std::max(64, 1 << (Log2_32_Ceil(OldNumEntries) + 1)); ++ NewNumBuckets = (std::max)(64, 1 << (Log2_32_Ceil(OldNumEntries) + 1)); + if (NewNumBuckets == NumBuckets) { + this->BaseT::initEmpty(); + return; +diff --git a/llvm/include/llvm/ADT/DenseMapInfo.h b/llvm/include/llvm/ADT/DenseMapInfo.h +index d276acbfa6a6..0040ac36217e 100644 +--- a/llvm/include/llvm/ADT/DenseMapInfo.h ++++ b/llvm/include/llvm/ADT/DenseMapInfo.h +@@ -285,7 +285,7 @@ template struct DenseMapInfo> { + template <> struct DenseMapInfo { + static inline hash_code getEmptyKey() { return hash_code(-1); } + static inline hash_code getTombstoneKey() { return hash_code(-2); } +- static unsigned getHashValue(hash_code val) { return val; } ++ static unsigned getHashValue(hash_code val) { return static_cast(val); } + static bool isEqual(hash_code LHS, hash_code RHS) { return LHS == RHS; } + }; + +diff --git a/llvm/include/llvm/ADT/SmallVector.h b/llvm/include/llvm/ADT/SmallVector.h +index b8a11030fc33..602fcc5b7a98 100644 +--- a/llvm/include/llvm/ADT/SmallVector.h ++++ b/llvm/include/llvm/ADT/SmallVector.h +@@ -49,12 +49,12 @@ protected: + + /// The maximum value of the Size_T used. + static constexpr size_t SizeTypeMax() { +- return std::numeric_limits::max(); ++ return (std::numeric_limits::max)(); + } + + SmallVectorBase() = delete; + SmallVectorBase(void *FirstEl, size_t TotalCapacity) +- : BeginX(FirstEl), Capacity(TotalCapacity) {} ++ : BeginX(FirstEl), Capacity(static_cast(TotalCapacity)) {} + + /// This is a helper for \a grow() that's out of line to reduce code + /// duplication. This function will report a fatal error if it can't grow at +@@ -83,7 +83,7 @@ public: + /// which will only be overwritten. + void set_size(size_t N) { + assert(N <= capacity()); +- Size = N; ++ Size = static_cast(N); + } + }; + +@@ -263,7 +263,7 @@ public: + + size_type size_in_bytes() const { return size() * sizeof(T); } + size_type max_size() const { +- return std::min(this->SizeTypeMax(), size_type(-1) / sizeof(T)); ++ return (std::min)(this->SizeTypeMax(), size_type(-1) / sizeof(T)); + } + + size_t capacity_in_bytes() const { return capacity() * sizeof(T); } +@@ -448,7 +448,7 @@ void SmallVectorTemplateBase::takeAllocationForGrow( + free(this->begin()); + + this->BeginX = NewElts; +- this->Capacity = NewCapacity; ++ this->Capacity = static_cast(NewCapacity); + } + + /// SmallVectorTemplateBase - This is where we put +@@ -674,7 +674,7 @@ public: + } + + // Assign over existing elements. +- std::fill_n(this->begin(), std::min(NumElts, this->size()), Elt); ++ std::fill_n(this->begin(), (std::min)(NumElts, this->size()), Elt); + if (NumElts > this->size()) + std::uninitialized_fill_n(this->end(), NumElts - this->size(), Elt); + else if (NumElts < this->size()) +diff --git a/llvm/include/llvm/Support/ConvertUTF.h b/llvm/include/llvm/Support/ConvertUTF.h +index 7f1527f51cdf..b085c8a179e8 100644 +--- a/llvm/include/llvm/Support/ConvertUTF.h ++++ b/llvm/include/llvm/Support/ConvertUTF.h +@@ -112,7 +112,7 @@ namespace llvm { + typedef unsigned int UTF32; /* at least 32 bits */ + typedef unsigned short UTF16; /* at least 16 bits */ + typedef unsigned char UTF8; /* typically 8 bits */ +-typedef unsigned char Boolean; /* 0 or 1 */ ++typedef bool Boolean; /* 0 or 1 */ + + /* Some fundamental constants */ + #define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD +diff --git a/llvm/include/llvm/Support/MathExtras.h b/llvm/include/llvm/Support/MathExtras.h +index 753b1998c40c..db9fbc148ae3 100644 +--- a/llvm/include/llvm/Support/MathExtras.h ++++ b/llvm/include/llvm/Support/MathExtras.h +@@ -97,7 +97,7 @@ template struct TrailingZerosCounter { + // Bisection method. + unsigned ZeroBits = 0; + T Shift = std::numeric_limits::digits >> 1; +- T Mask = std::numeric_limits::max() >> Shift; ++ T Mask = (std::numeric_limits::max)() >> Shift; + while (Shift) { + if ((Val & Mask) == 0) { + Val >>= Shift; +@@ -238,7 +238,7 @@ unsigned countLeadingZeros(T Val, ZeroBehavior ZB = ZB_Width) { + /// valid arguments. + template T findFirstSet(T Val, ZeroBehavior ZB = ZB_Max) { + if (ZB == ZB_Max && Val == 0) +- return std::numeric_limits::max(); ++ return (std::numeric_limits::max)(); + + return countTrailingZeros(Val, ZB_Undefined); + } +@@ -279,7 +279,7 @@ template T maskLeadingZeros(unsigned N) { + /// valid arguments. + template T findLastSet(T Val, ZeroBehavior ZB = ZB_Max) { + if (ZB == ZB_Max && Val == 0) +- return std::numeric_limits::max(); ++ return (std::numeric_limits::max)(); + + // Use ^ instead of - because both gcc and llvm can remove the associated ^ + // in the __builtin_clz intrinsic on x86. +@@ -594,26 +594,26 @@ inline double Log2(double Value) { + /// (32 bit edition.) + /// Ex. Log2_32(32) == 5, Log2_32(1) == 0, Log2_32(0) == -1, Log2_32(6) == 2 + inline unsigned Log2_32(uint32_t Value) { +- return 31 - countLeadingZeros(Value); ++ return static_cast(31 - countLeadingZeros(Value)); + } + + /// Return the floor log base 2 of the specified value, -1 if the value is zero. + /// (64 bit edition.) + inline unsigned Log2_64(uint64_t Value) { +- return 63 - countLeadingZeros(Value); ++ return static_cast(63 - countLeadingZeros(Value)); + } + + /// Return the ceil log base 2 of the specified value, 32 if the value is zero. + /// (32 bit edition). + /// Ex. Log2_32_Ceil(32) == 5, Log2_32_Ceil(1) == 0, Log2_32_Ceil(6) == 3 + inline unsigned Log2_32_Ceil(uint32_t Value) { +- return 32 - countLeadingZeros(Value - 1); ++ return static_cast(32 - countLeadingZeros(Value - 1)); + } + + /// Return the ceil log base 2 of the specified value, 64 if the value is zero. + /// (64 bit edition.) + inline unsigned Log2_64_Ceil(uint64_t Value) { +- return 64 - countLeadingZeros(Value - 1); ++ return static_cast(64 - countLeadingZeros(Value - 1)); + } + + /// Return the greatest common divisor of the values using Euclid's algorithm. +@@ -807,7 +807,7 @@ SaturatingAdd(T X, T Y, bool *ResultOverflowed = nullptr) { + T Z = X + Y; + Overflowed = (Z < X || Z < Y); + if (Overflowed) +- return std::numeric_limits::max(); ++ return (std::numeric_limits::max)(); + else + return Z; + } +@@ -832,7 +832,7 @@ SaturatingMultiply(T X, T Y, bool *ResultOverflowed = nullptr) { + // Special case: if X or Y is 0, Log2_64 gives -1, and Log2Z + // will necessarily be less than Log2Max as desired. + int Log2Z = Log2_64(X) + Log2_64(Y); +- const T Max = std::numeric_limits::max(); ++ const T Max = (std::numeric_limits::max)(); + int Log2Max = Log2_64(Max); + if (Log2Z < Log2Max) { + return X * Y; +@@ -952,9 +952,9 @@ std::enable_if_t::value, T> MulOverflow(T X, T Y, T &Result) { + // Check how the max allowed absolute value (2^n for negative, 2^(n-1) for + // positive) divided by an argument compares to the other. + if (IsNegative) +- return UX > (static_cast(std::numeric_limits::max()) + U(1)) / UY; ++ return UX > (static_cast((std::numeric_limits::max)()) + U(1)) / UY; + else +- return UX > (static_cast(std::numeric_limits::max())) / UY; ++ return UX > (static_cast((std::numeric_limits::max)())) / UY; + } + + } // End llvm namespace +diff --git a/llvm/lib/Support/SmallVector.cpp b/llvm/lib/Support/SmallVector.cpp +index 0005f7840912..26901fe97d20 100644 +--- a/llvm/lib/Support/SmallVector.cpp ++++ b/llvm/lib/Support/SmallVector.cpp +@@ -95,7 +95,7 @@ static size_t getNewCapacity(size_t MinSize, size_t TSize, size_t OldCapacity) { + // In theory 2*capacity can overflow if the capacity is 64 bit, but the + // original capacity would never be large enough for this to be a problem. + size_t NewCapacity = 2 * OldCapacity + 1; // Always grow. +- return std::min(std::max(NewCapacity, MinSize), MaxSize); ++ return (std::min)((std::max)(NewCapacity, MinSize), MaxSize); + } + + // Note: Moving this function into the header may cause performance regression. +-- +2.20.1.windows.1 + diff --git a/upstream_utils/llvm_patches/0004-Change-uniqe_function-storage-size.patch b/upstream_utils/llvm_patches/0004-Change-uniqe_function-storage-size.patch new file mode 100644 index 0000000000..15f8c982c8 --- /dev/null +++ b/upstream_utils/llvm_patches/0004-Change-uniqe_function-storage-size.patch @@ -0,0 +1,34 @@ +From 086a4a7ee635c68d81b85044c9f8f4317b2b164c Mon Sep 17 00:00:00 2001 +From: PJ Reiniger +Date: Sat, 7 May 2022 22:13:55 -0400 +Subject: [PATCH 04/31] Change uniqe_function storage size + +--- + llvm/include/llvm/ADT/FunctionExtras.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/llvm/include/llvm/ADT/FunctionExtras.h b/llvm/include/llvm/ADT/FunctionExtras.h +index e67ef7377c88..1a26cb702cae 100644 +--- a/llvm/include/llvm/ADT/FunctionExtras.h ++++ b/llvm/include/llvm/ADT/FunctionExtras.h +@@ -72,7 +72,7 @@ using EnableIfCallable = + + template class UniqueFunctionBase { + protected: +- static constexpr size_t InlineStorageSize = sizeof(void *) * 3; ++ static constexpr size_t InlineStorageSize = sizeof(void *) * 4; + + template + struct IsSizeLessThanThresholdT : std::false_type {}; +@@ -151,7 +151,7 @@ protected: + "Should always use all of the out-of-line storage for inline storage!"); + + // For in-line storage, we just provide an aligned character buffer. We +- // provide three pointers worth of storage here. ++ // provide four pointers worth of storage here. + // This is mutable as an inlined `const unique_function` may + // still modify its own mutable members. + mutable +-- +2.20.1.windows.1 + diff --git a/upstream_utils/llvm_patches/0005-Threading-updates.patch b/upstream_utils/llvm_patches/0005-Threading-updates.patch new file mode 100644 index 0000000000..7ad3cde3cc --- /dev/null +++ b/upstream_utils/llvm_patches/0005-Threading-updates.patch @@ -0,0 +1,188 @@ +From a53483a33c8d5778e5594a49376b5bafe742d126 Mon Sep 17 00:00:00 2001 +From: PJ Reiniger +Date: Sat, 7 May 2022 22:17:19 -0400 +Subject: [PATCH 05/31] Threading updates + +- Remove guards for threads and exception +- Prefer scope gaurd over lock gaurd +--- + llvm/include/llvm/Support/Compiler.h | 6 ----- + llvm/lib/Support/ErrorHandling.cpp | 38 +++++----------------------- + llvm/lib/Support/ManagedStatic.cpp | 10 ++++---- + 3 files changed, 11 insertions(+), 43 deletions(-) + +diff --git a/llvm/include/llvm/Support/Compiler.h b/llvm/include/llvm/Support/Compiler.h +index f4d6612fe074..989d25bb03b9 100644 +--- a/llvm/include/llvm/Support/Compiler.h ++++ b/llvm/include/llvm/Support/Compiler.h +@@ -525,7 +525,6 @@ void AnnotateIgnoreWritesEnd(const char *file, int line); + /// initialize to some constant value. In almost all circumstances this is most + /// appropriate for use with a pointer, integer, or small aggregation of + /// pointers and integers. +-#if LLVM_ENABLE_THREADS + #if __has_feature(cxx_thread_local) || defined(_MSC_VER) + #define LLVM_THREAD_LOCAL thread_local + #else +@@ -533,11 +532,6 @@ void AnnotateIgnoreWritesEnd(const char *file, int line); + // we only need the restricted functionality that provides. + #define LLVM_THREAD_LOCAL __thread + #endif +-#else // !LLVM_ENABLE_THREADS +-// If threading is disabled entirely, this compiles to nothing and you get +-// a normal global variable. +-#define LLVM_THREAD_LOCAL +-#endif + + /// \macro LLVM_ENABLE_EXCEPTIONS + /// Whether LLVM is built with exception support. +diff --git a/llvm/lib/Support/ErrorHandling.cpp b/llvm/lib/Support/ErrorHandling.cpp +index 19d1fd77af12..2403db9c80f1 100644 +--- a/llvm/lib/Support/ErrorHandling.cpp ++++ b/llvm/lib/Support/ErrorHandling.cpp +@@ -44,7 +44,6 @@ static void *ErrorHandlerUserData = nullptr; + static fatal_error_handler_t BadAllocErrorHandler = nullptr; + static void *BadAllocErrorHandlerUserData = nullptr; + +-#if LLVM_ENABLE_THREADS == 1 + // Mutexes to synchronize installing error handlers and calling error handlers. + // Do not use ManagedStatic, or that may allocate memory while attempting to + // report an OOM. +@@ -58,22 +57,17 @@ static void *BadAllocErrorHandlerUserData = nullptr; + // builds. We can remove these ifdefs if that script goes away. + static std::mutex ErrorHandlerMutex; + static std::mutex BadAllocErrorHandlerMutex; +-#endif + + void llvm::install_fatal_error_handler(fatal_error_handler_t handler, + void *user_data) { +-#if LLVM_ENABLE_THREADS == 1 +- std::lock_guard Lock(ErrorHandlerMutex); +-#endif ++ std::scoped_lock Lock(ErrorHandlerMutex); + assert(!ErrorHandler && "Error handler already registered!\n"); + ErrorHandler = handler; + ErrorHandlerUserData = user_data; + } + + void llvm::remove_fatal_error_handler() { +-#if LLVM_ENABLE_THREADS == 1 +- std::lock_guard Lock(ErrorHandlerMutex); +-#endif ++ std::scoped_lock Lock(ErrorHandlerMutex); + ErrorHandler = nullptr; + ErrorHandlerUserData = nullptr; + } +@@ -92,9 +86,7 @@ void llvm::report_fatal_error(std::string_view Reason, bool GenCrashDiag) { + { + // Only acquire the mutex while reading the handler, so as not to invoke a + // user-supplied callback under a lock. +-#if LLVM_ENABLE_THREADS == 1 +- std::lock_guard Lock(ErrorHandlerMutex); +-#endif ++ std::scoped_lock Lock(ErrorHandlerMutex); + handler = ErrorHandler; + handlerData = ErrorHandlerUserData; + } +@@ -123,18 +115,14 @@ void llvm::report_fatal_error(std::string_view Reason, bool GenCrashDiag) { + + void llvm::install_bad_alloc_error_handler(fatal_error_handler_t handler, + void *user_data) { +-#if LLVM_ENABLE_THREADS == 1 +- std::lock_guard Lock(BadAllocErrorHandlerMutex); +-#endif ++ std::scoped_lock Lock(BadAllocErrorHandlerMutex); + assert(!ErrorHandler && "Bad alloc error handler already registered!\n"); + BadAllocErrorHandler = handler; + BadAllocErrorHandlerUserData = user_data; + } + + void llvm::remove_bad_alloc_error_handler() { +-#if LLVM_ENABLE_THREADS == 1 +- std::lock_guard Lock(BadAllocErrorHandlerMutex); +-#endif ++ std::scoped_lock Lock(BadAllocErrorHandlerMutex); + BadAllocErrorHandler = nullptr; + BadAllocErrorHandlerUserData = nullptr; + } +@@ -145,9 +133,7 @@ void llvm::report_bad_alloc_error(const char *Reason, bool GenCrashDiag) { + { + // Only acquire the mutex while reading the handler, so as not to invoke a + // user-supplied callback under a lock. +-#if LLVM_ENABLE_THREADS == 1 +- std::lock_guard Lock(BadAllocErrorHandlerMutex); +-#endif ++ std::scoped_lock Lock(BadAllocErrorHandlerMutex); + Handler = BadAllocErrorHandler; + HandlerData = BadAllocErrorHandlerUserData; + } +@@ -157,10 +143,6 @@ void llvm::report_bad_alloc_error(const char *Reason, bool GenCrashDiag) { + llvm_unreachable("bad alloc handler should not return"); + } + +-#ifdef LLVM_ENABLE_EXCEPTIONS +- // If exceptions are enabled, make OOM in malloc look like OOM in new. +- throw std::bad_alloc(); +-#else + // Don't call the normal error handler. It may allocate memory. Directly write + // an OOM to stderr and abort. + const char *OOMMessage = "LLVM ERROR: out of memory\n"; +@@ -169,15 +151,8 @@ void llvm::report_bad_alloc_error(const char *Reason, bool GenCrashDiag) { + (void)!::write(2, Reason, strlen(Reason)); + (void)!::write(2, Newline, strlen(Newline)); + abort(); +-#endif + } + +-#ifdef LLVM_ENABLE_EXCEPTIONS +-// Do not set custom new handler if exceptions are enabled. In this case OOM +-// errors are handled by throwing 'std::bad_alloc'. +-void llvm::install_out_of_memory_new_handler() { +-} +-#else + // Causes crash on allocation failure. It is called prior to the handler set by + // 'install_bad_alloc_error_handler'. + static void out_of_memory_new_handler() { +@@ -192,7 +167,6 @@ void llvm::install_out_of_memory_new_handler() { + assert((old == nullptr || old == out_of_memory_new_handler) && + "new-handler already installed"); + } +-#endif + + void llvm::llvm_unreachable_internal(const char *msg, const char *file, + unsigned line) { +diff --git a/llvm/lib/Support/ManagedStatic.cpp b/llvm/lib/Support/ManagedStatic.cpp +index a6ae67066ea0..fc798b7ec1b7 100644 +--- a/llvm/lib/Support/ManagedStatic.cpp ++++ b/llvm/lib/Support/ManagedStatic.cpp +@@ -12,23 +12,23 @@ + + #include "llvm/Support/ManagedStatic.h" + #include "llvm/Config/config.h" +-#include "llvm/Support/Threading.h" ++#include "llvm/Support/mutex.h" + #include + #include + using namespace llvm; + + static const ManagedStaticBase *StaticList = nullptr; + +-static std::recursive_mutex *getManagedStaticMutex() { +- static std::recursive_mutex m; ++static llvm::mutex *getManagedStaticMutex() { ++ static llvm::mutex m; + return &m; + } + + void ManagedStaticBase::RegisterManagedStatic(void *(*Creator)(), + void (*Deleter)(void*)) const { + assert(Creator); +- if (llvm_is_multithreaded()) { +- std::lock_guard Lock(*getManagedStaticMutex()); ++ if (1) { ++ std::scoped_lock Lock(*getManagedStaticMutex()); + + if (!Ptr.load(std::memory_order_relaxed)) { + void *Tmp = Creator(); +-- +2.20.1.windows.1 + diff --git a/upstream_utils/llvm_patches/0006-Remove-DJB-hash-dependency.patch b/upstream_utils/llvm_patches/0006-Remove-DJB-hash-dependency.patch new file mode 100644 index 0000000000..b6246d381f --- /dev/null +++ b/upstream_utils/llvm_patches/0006-Remove-DJB-hash-dependency.patch @@ -0,0 +1,62 @@ +From 04f1186ea8f0160da1a1863a344ff9a9f55b6733 Mon Sep 17 00:00:00 2001 +From: PJ Reiniger +Date: Sat, 7 May 2022 22:24:34 -0400 +Subject: [PATCH 06/31] Remove DJB hash dependency + +--- + llvm/lib/Support/StringMap.cpp | 21 ++++++++++++++++++--- + 1 file changed, 18 insertions(+), 3 deletions(-) + +diff --git a/llvm/lib/Support/StringMap.cpp b/llvm/lib/Support/StringMap.cpp +index d9eeba619428..f0d5ae6de646 100644 +--- a/llvm/lib/Support/StringMap.cpp ++++ b/llvm/lib/Support/StringMap.cpp +@@ -12,11 +12,26 @@ + + #include "llvm/ADT/StringMap.h" + #include "llvm/ADT/StringExtras.h" +-#include "llvm/Support/DJB.h" ++#include "llvm/Support/Compiler.h" + #include "llvm/Support/MathExtras.h" + + using namespace llvm; + ++/// HashString - Hash function for strings. ++/// ++/// This is the Bernstein hash function. ++// ++// FIXME: Investigate whether a modified bernstein hash function performs ++// better: http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx ++// X*33+c -> X*33^c ++static inline unsigned HashString(std::string_view str, ++ unsigned result = 0) noexcept { ++ for (std::string_view::size_type i = 0, e = str.size(); i != e; ++i) { ++ result = result * 33 + static_cast(str[i]); ++ } ++ return result; ++} ++ + /// Returns the number of buckets to allocate to ensure that the DenseMap can + /// accommodate \p NumEntries without need to grow(). + static unsigned getMinBucketToReserveForEntries(unsigned NumEntries) { +@@ -77,7 +92,7 @@ unsigned StringMapImpl::LookupBucketFor(std::string_view Name) { + init(16); + HTSize = NumBuckets; + } +- unsigned FullHashValue = djbHash(Name, 0); ++ unsigned FullHashValue = HashString(Name); + unsigned BucketNo = FullHashValue & (HTSize - 1); + unsigned *HashTable = (unsigned *)(TheTable + NumBuckets + 1); + +@@ -133,7 +148,7 @@ int StringMapImpl::FindKey(std::string_view Key) const { + unsigned HTSize = NumBuckets; + if (HTSize == 0) + return -1; // Really empty table? +- unsigned FullHashValue = djbHash(Key, 0); ++ unsigned FullHashValue = HashString(Key); + unsigned BucketNo = FullHashValue & (HTSize - 1); + unsigned *HashTable = (unsigned *)(TheTable + NumBuckets + 1); + +-- +2.20.1.windows.1 + diff --git a/upstream_utils/llvm_patches/0007-ifdef-guard-safety.patch b/upstream_utils/llvm_patches/0007-ifdef-guard-safety.patch new file mode 100644 index 0000000000..8e84dac0e7 --- /dev/null +++ b/upstream_utils/llvm_patches/0007-ifdef-guard-safety.patch @@ -0,0 +1,354 @@ +From d16ac9833a8469fe49b564ad21e2331875b450b8 Mon Sep 17 00:00:00 2001 +From: PJ Reiniger +Date: Sat, 7 May 2022 22:28:13 -0400 +Subject: [PATCH 07/31] #ifdef guard safety + +Prevents redefinition if someone is pulling in real LLVM, since the macros are in global namespace +--- + llvm/include/llvm/Support/Compiler.h | 54 ++++++++++++++++++++++++++++ + 1 file changed, 54 insertions(+) + +diff --git a/llvm/include/llvm/Support/Compiler.h b/llvm/include/llvm/Support/Compiler.h +index 989d25bb03b9..14f95ae8cc52 100644 +--- a/llvm/include/llvm/Support/Compiler.h ++++ b/llvm/include/llvm/Support/Compiler.h +@@ -80,6 +80,7 @@ + /// * 1916: VS2017, version 15.9 + /// * 1920: VS2019, version 16.0 + /// * 1921: VS2019, version 16.1 ++#ifndef LLVM_MSC_PREREQ + #ifdef _MSC_VER + #define LLVM_MSC_PREREQ(version) (_MSC_VER >= (version)) + +@@ -91,28 +92,33 @@ + #else + #define LLVM_MSC_PREREQ(version) 0 + #endif ++#endif + + /// Does the compiler support ref-qualifiers for *this? + /// + /// Sadly, this is separate from just rvalue reference support because GCC + /// and MSVC implemented this later than everything else. This appears to be + /// corrected in MSVC 2019 but not MSVC 2017. ++#ifndef LLVM_HAS_RVALUE_REFERENCE_THIS + #if __has_feature(cxx_rvalue_references) || LLVM_GNUC_PREREQ(4, 8, 1) || \ + LLVM_MSC_PREREQ(1920) + #define LLVM_HAS_RVALUE_REFERENCE_THIS 1 + #else + #define LLVM_HAS_RVALUE_REFERENCE_THIS 0 + #endif ++#endif + + /// Expands to '&' if ref-qualifiers for *this are supported. + /// + /// This can be used to provide lvalue/rvalue overrides of member functions. + /// The rvalue override should be guarded by LLVM_HAS_RVALUE_REFERENCE_THIS ++#ifndef LLVM_LVALUE_FUNCTION + #if LLVM_HAS_RVALUE_REFERENCE_THIS + #define LLVM_LVALUE_FUNCTION & + #else + #define LLVM_LVALUE_FUNCTION + #endif ++#endif + + /// LLVM_LIBRARY_VISIBILITY - If a class marked with this attribute is linked + /// into a shared library, then the class should be private to the library and +@@ -132,21 +138,26 @@ + #define LLVM_EXTERNAL_VISIBILITY + #endif + ++#ifndef LLVM_PREFETCH + #if defined(__GNUC__) + #define LLVM_PREFETCH(addr, rw, locality) __builtin_prefetch(addr, rw, locality) + #else + #define LLVM_PREFETCH(addr, rw, locality) + #endif ++#endif + ++#ifndef LLVM_ATTRIBUTE_USED + #if __has_attribute(used) || LLVM_GNUC_PREREQ(3, 1, 0) + #define LLVM_ATTRIBUTE_USED __attribute__((__used__)) + #else + #define LLVM_ATTRIBUTE_USED + #endif ++#endif + + /// LLVM_NODISCARD - Warn if a type or return value is discarded. + + // Use the 'nodiscard' attribute in C++17 or newer mode. ++#ifndef LLVM_NODISCARD + #if defined(__cplusplus) && __cplusplus > 201402L && LLVM_HAS_CPP_ATTRIBUTE(nodiscard) + #define LLVM_NODISCARD [[nodiscard]] + #elif LLVM_HAS_CPP_ATTRIBUTE(clang::warn_unused_result) +@@ -160,6 +171,7 @@ + #else + #define LLVM_NODISCARD + #endif ++#endif + + // Indicate that a non-static, non-const C++ member function reinitializes + // the entire object to a known state, independent of the previous state of +@@ -182,11 +194,13 @@ + // more portable solution: + // (void)unused_var_name; + // Prefer cast-to-void wherever it is sufficient. ++#ifndef LLVM_ATTRIBUTE_UNUSED + #if __has_attribute(unused) || LLVM_GNUC_PREREQ(3, 1, 0) + #define LLVM_ATTRIBUTE_UNUSED __attribute__((__unused__)) + #else + #define LLVM_ATTRIBUTE_UNUSED + #endif ++#endif + + // FIXME: Provide this for PE/COFF targets. + #if (__has_attribute(weak) || LLVM_GNUC_PREREQ(4, 0, 0)) && \ +@@ -196,6 +210,7 @@ + #define LLVM_ATTRIBUTE_WEAK + #endif + ++#ifndef LLVM_READNONE + // Prior to clang 3.2, clang did not accept any spelling of + // __has_attribute(const), so assume it is supported. + #if defined(__clang__) || defined(__GNUC__) +@@ -204,14 +219,18 @@ + #else + #define LLVM_READNONE + #endif ++#endif + ++#ifndef LLVM_READONLY + #if __has_attribute(pure) || defined(__GNUC__) + // aka 'PURE' but following LLVM Conventions. + #define LLVM_READONLY __attribute__((__pure__)) + #else + #define LLVM_READONLY + #endif ++#endif + ++#ifndef LLVM_LIKELY + #if __has_builtin(__builtin_expect) || LLVM_GNUC_PREREQ(4, 0, 0) + #define LLVM_LIKELY(EXPR) __builtin_expect((bool)(EXPR), true) + #define LLVM_UNLIKELY(EXPR) __builtin_expect((bool)(EXPR), false) +@@ -219,9 +238,11 @@ + #define LLVM_LIKELY(EXPR) (EXPR) + #define LLVM_UNLIKELY(EXPR) (EXPR) + #endif ++#endif + + /// LLVM_ATTRIBUTE_NOINLINE - On compilers where we have a directive to do so, + /// mark a method "not for inlining". ++#ifndef LLVM_ATTRIBUTE_NOINLINE + #if __has_attribute(noinline) || LLVM_GNUC_PREREQ(3, 4, 0) + #define LLVM_ATTRIBUTE_NOINLINE __attribute__((noinline)) + #elif defined(_MSC_VER) +@@ -229,11 +250,13 @@ + #else + #define LLVM_ATTRIBUTE_NOINLINE + #endif ++#endif + + /// LLVM_ATTRIBUTE_ALWAYS_INLINE - On compilers where we have a directive to do + /// so, mark a method "always inline" because it is performance sensitive. GCC + /// 3.4 supported this but is buggy in various cases and produces unimplemented + /// errors, just use it in GCC 4.0 and later. ++#ifndef LLVM_ATTRIBUTE_ALWAYS_INLINE + #if __has_attribute(always_inline) || LLVM_GNUC_PREREQ(4, 0, 0) + #define LLVM_ATTRIBUTE_ALWAYS_INLINE inline __attribute__((always_inline)) + #elif defined(_MSC_VER) +@@ -241,7 +264,9 @@ + #else + #define LLVM_ATTRIBUTE_ALWAYS_INLINE inline + #endif ++#endif + ++#ifndef LLVM_ATTRIBUTE_NORETURN + #ifdef __GNUC__ + #define LLVM_ATTRIBUTE_NORETURN __attribute__((noreturn)) + #elif defined(_MSC_VER) +@@ -249,7 +274,9 @@ + #else + #define LLVM_ATTRIBUTE_NORETURN + #endif ++#endif + ++#ifndef LLVM_ATTRIBUTE_RETURNS_NONNULL + #if __has_attribute(returns_nonnull) || LLVM_GNUC_PREREQ(4, 9, 0) + #define LLVM_ATTRIBUTE_RETURNS_NONNULL __attribute__((returns_nonnull)) + #elif defined(_MSC_VER) +@@ -257,9 +284,11 @@ + #else + #define LLVM_ATTRIBUTE_RETURNS_NONNULL + #endif ++#endif + + /// \macro LLVM_ATTRIBUTE_RETURNS_NOALIAS Used to mark a function as returning a + /// pointer that does not alias any other valid pointer. ++#ifndef LLVM_ATTRIBUTE_RETURNS_NOALIAS + #ifdef __GNUC__ + #define LLVM_ATTRIBUTE_RETURNS_NOALIAS __attribute__((__malloc__)) + #elif defined(_MSC_VER) +@@ -267,8 +296,10 @@ + #else + #define LLVM_ATTRIBUTE_RETURNS_NOALIAS + #endif ++#endif + + /// LLVM_FALLTHROUGH - Mark fallthrough cases in switch statements. ++#ifndef LLVM_FALLTHROUGH + #if defined(__cplusplus) && __cplusplus > 201402L && LLVM_HAS_CPP_ATTRIBUTE(fallthrough) + #define LLVM_FALLTHROUGH [[fallthrough]] + #elif LLVM_HAS_CPP_ATTRIBUTE(gnu::fallthrough) +@@ -280,6 +311,7 @@ + #else + #define LLVM_FALLTHROUGH + #endif ++#endif + + /// LLVM_REQUIRE_CONSTANT_INITIALIZATION - Apply this to globals to ensure that + /// they are constant initialized. +@@ -308,28 +340,35 @@ + + /// LLVM_EXTENSION - Support compilers where we have a keyword to suppress + /// pedantic diagnostics. ++#ifndef LLVM_EXTENSION + #ifdef __GNUC__ + #define LLVM_EXTENSION __extension__ + #else + #define LLVM_EXTENSION + #endif ++#endif + + // LLVM_ATTRIBUTE_DEPRECATED(decl, "message") + // This macro will be removed. + // Use C++14's attribute instead: [[deprecated("message")]] ++#ifndef LLVM_ATTRIBUTE_DEPRECATED + #define LLVM_ATTRIBUTE_DEPRECATED(decl, message) [[deprecated(message)]] decl ++#endif + + /// LLVM_BUILTIN_UNREACHABLE - On compilers which support it, expands + /// to an expression which states that it is undefined behavior for the + /// compiler to reach this point. Otherwise is not defined. ++#ifndef LLVM_BUILTIN_UNREACHABLE + #if __has_builtin(__builtin_unreachable) || LLVM_GNUC_PREREQ(4, 5, 0) + # define LLVM_BUILTIN_UNREACHABLE __builtin_unreachable() + #elif defined(_MSC_VER) + # define LLVM_BUILTIN_UNREACHABLE __assume(false) + #endif ++#endif + + /// LLVM_BUILTIN_TRAP - On compilers which support it, expands to an expression + /// which causes the program to exit abnormally. ++#ifndef LLVM_BUILTIN_TRAP + #if __has_builtin(__builtin_trap) || LLVM_GNUC_PREREQ(4, 3, 0) + # define LLVM_BUILTIN_TRAP __builtin_trap() + #elif defined(_MSC_VER) +@@ -341,10 +380,12 @@ + #else + # define LLVM_BUILTIN_TRAP *(volatile int*)0x11 = 0 + #endif ++#endif + + /// LLVM_BUILTIN_DEBUGTRAP - On compilers which support it, expands to + /// an expression which causes the program to break while running + /// under a debugger. ++#ifndef LLVM_BUILTIN_DEBUGTRAP + #if __has_builtin(__builtin_debugtrap) + # define LLVM_BUILTIN_DEBUGTRAP __builtin_debugtrap() + #elif defined(_MSC_VER) +@@ -358,9 +399,11 @@ + // program to abort if encountered. + # define LLVM_BUILTIN_DEBUGTRAP + #endif ++#endif + + /// \macro LLVM_ASSUME_ALIGNED + /// Returns a pointer with an assumed alignment. ++#ifndef LLVM_ASSUME_ALIGNED + #if __has_builtin(__builtin_assume_aligned) || LLVM_GNUC_PREREQ(4, 7, 0) + # define LLVM_ASSUME_ALIGNED(p, a) __builtin_assume_aligned(p, a) + #elif defined(LLVM_BUILTIN_UNREACHABLE) +@@ -369,6 +412,7 @@ + #else + # define LLVM_ASSUME_ALIGNED(p, a) (p) + #endif ++#endif + + /// \macro LLVM_PACKED + /// Used to specify a packed structure. +@@ -388,6 +432,7 @@ + /// long long l; + /// }; + /// LLVM_PACKED_END ++#ifndef LLVM_PACKED + #ifdef _MSC_VER + # define LLVM_PACKED(d) __pragma(pack(push, 1)) d __pragma(pack(pop)) + # define LLVM_PACKED_START __pragma(pack(push, 1)) +@@ -397,11 +442,13 @@ + # define LLVM_PACKED_START _Pragma("pack(push, 1)") + # define LLVM_PACKED_END _Pragma("pack(pop)") + #endif ++#endif + + /// \macro LLVM_PTR_SIZE + /// A constant integer equivalent to the value of sizeof(void*). + /// Generally used in combination with alignas or when doing computation in the + /// preprocessor. ++#ifndef LLVM_PTR_SIZE + #ifdef __SIZEOF_POINTER__ + # define LLVM_PTR_SIZE __SIZEOF_POINTER__ + #elif defined(_WIN64) +@@ -413,6 +460,7 @@ + #else + # define LLVM_PTR_SIZE sizeof(void *) + #endif ++#endif + + /// \macro LLVM_MEMORY_SANITIZER_BUILD + /// Whether LLVM itself is built with MemorySanitizer instrumentation. +@@ -483,11 +531,13 @@ void AnnotateIgnoreWritesEnd(const char *file, int line); + + /// \macro LLVM_NO_SANITIZE + /// Disable a particular sanitizer for a function. ++#ifndef LLVM_NO_SANITIZE + #if __has_attribute(no_sanitize) + #define LLVM_NO_SANITIZE(KIND) __attribute__((no_sanitize(KIND))) + #else + #define LLVM_NO_SANITIZE(KIND) + #endif ++#endif + + /// Mark debug helper function definitions like dump() that should not be + /// stripped from debug builds. +@@ -495,17 +545,20 @@ void AnnotateIgnoreWritesEnd(const char *file, int line); + /// `#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)` so they do always + /// get stripped in release builds. + // FIXME: Move this to a private config.h as it's not usable in public headers. ++#ifndef LLVM_DUMP_METHOD + #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + #define LLVM_DUMP_METHOD LLVM_ATTRIBUTE_NOINLINE LLVM_ATTRIBUTE_USED + #else + #define LLVM_DUMP_METHOD LLVM_ATTRIBUTE_NOINLINE + #endif ++#endif + + /// \macro LLVM_PRETTY_FUNCTION + /// Gets a user-friendly looking function signature for the current scope + /// using the best available method on each platform. The exact format of the + /// resulting string is implementation specific and non-portable, so this should + /// only be used, for example, for logging or diagnostics. ++#ifndef LLVM_PRETTY_FUNCTION + #if defined(_MSC_VER) + #define LLVM_PRETTY_FUNCTION __FUNCSIG__ + #elif defined(__GNUC__) || defined(__clang__) +@@ -513,6 +566,7 @@ void AnnotateIgnoreWritesEnd(const char *file, int line); + #else + #define LLVM_PRETTY_FUNCTION __func__ + #endif ++#endif + + /// \macro LLVM_THREAD_LOCAL + /// A thread-local storage specifier which can be used with globals, +-- +2.20.1.windows.1 + diff --git a/upstream_utils/llvm_patches/0008-Explicitly-use-std.patch b/upstream_utils/llvm_patches/0008-Explicitly-use-std.patch new file mode 100644 index 0000000000..1eed55e543 --- /dev/null +++ b/upstream_utils/llvm_patches/0008-Explicitly-use-std.patch @@ -0,0 +1,81 @@ +From 945a1d5a54ad830f857625417da414d05d367a37 Mon Sep 17 00:00:00 2001 +From: PJ Reiniger +Date: Sat, 7 May 2022 22:37:34 -0400 +Subject: [PATCH 08/31] Explicitly use std:: + +--- + llvm/include/llvm/ADT/SmallSet.h | 2 +- + llvm/include/llvm/Support/MathExtras.h | 2 +- + llvm/lib/Support/ErrorHandling.cpp | 2 +- + llvm/unittests/ADT/SmallPtrSetTest.cpp | 2 +- + llvm/unittests/ADT/StringMapTest.cpp | 2 +- + 5 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/llvm/include/llvm/ADT/SmallSet.h b/llvm/include/llvm/ADT/SmallSet.h +index e4c209c5f2a9..f2b402ccf9a0 100644 +--- a/llvm/include/llvm/ADT/SmallSet.h ++++ b/llvm/include/llvm/ADT/SmallSet.h +@@ -269,7 +269,7 @@ bool operator==(const SmallSet &LHS, const SmallSet &RHS) { + return false; + + // All elements in LHS must also be in RHS +- return all_of(LHS, [&RHS](const T &E) { return RHS.count(E); }); ++ return std::all_of(LHS.begin(), LHS.end(), [&RHS](const T &E) { return RHS.count(E); }); + } + + /// Inequality comparison for SmallSet. +diff --git a/llvm/include/llvm/Support/MathExtras.h b/llvm/include/llvm/Support/MathExtras.h +index db9fbc148ae3..da843ef79ff9 100644 +--- a/llvm/include/llvm/Support/MathExtras.h ++++ b/llvm/include/llvm/Support/MathExtras.h +@@ -586,7 +586,7 @@ inline double Log2(double Value) { + #if defined(__ANDROID_API__) && __ANDROID_API__ < 18 + return __builtin_log(Value) / __builtin_log(2.0); + #else +- return log2(Value); ++ return std::log2(Value); + #endif + } + +diff --git a/llvm/lib/Support/ErrorHandling.cpp b/llvm/lib/Support/ErrorHandling.cpp +index 2403db9c80f1..9c0c6fb868f2 100644 +--- a/llvm/lib/Support/ErrorHandling.cpp ++++ b/llvm/lib/Support/ErrorHandling.cpp +@@ -210,7 +210,7 @@ void LLVMResetFatalErrorHandler() { + // I'd rather not double the line count of the following. + #define MAP_ERR_TO_COND(x, y) \ + case x: \ +- return make_error_code(errc::y) ++ return std::make_error_code(std::errc::y) + + std::error_code llvm::mapWindowsError(unsigned EV) { + switch (EV) { +diff --git a/llvm/unittests/ADT/SmallPtrSetTest.cpp b/llvm/unittests/ADT/SmallPtrSetTest.cpp +index 6f3c94eed273..531f81ab5b3f 100644 +--- a/llvm/unittests/ADT/SmallPtrSetTest.cpp ++++ b/llvm/unittests/ADT/SmallPtrSetTest.cpp +@@ -298,7 +298,7 @@ TEST(SmallPtrSetTest, dereferenceAndIterate) { + + // Sort. We should hit the first element just once and the final element N + // times. +- llvm::sort(std::begin(Found), std::end(Found)); ++ std::sort(std::begin(Found), std::end(Found)); + for (auto F = std::begin(Found), E = std::end(Found); F != E; ++F) + EXPECT_EQ(F - Found + 1, *F); + } +diff --git a/llvm/unittests/ADT/StringMapTest.cpp b/llvm/unittests/ADT/StringMapTest.cpp +index 4c4aec2184b7..5211f01bbd73 100644 +--- a/llvm/unittests/ADT/StringMapTest.cpp ++++ b/llvm/unittests/ADT/StringMapTest.cpp +@@ -308,7 +308,7 @@ TEST_F(StringMapTest, IterMapKeys) { + Map["D"] = 3; + + auto Keys = to_vector<4>(Map.keys()); +- llvm::sort(Keys); ++ std::sort(Keys.begin(), Keys.end()); + + SmallVector Expected = {"A", "B", "C", "D"}; + EXPECT_EQ(Expected, Keys); +-- +2.20.1.windows.1 + diff --git a/upstream_utils/llvm_patches/0009-Remove-format_provider.patch b/upstream_utils/llvm_patches/0009-Remove-format_provider.patch new file mode 100644 index 0000000000..ffb233b3b9 --- /dev/null +++ b/upstream_utils/llvm_patches/0009-Remove-format_provider.patch @@ -0,0 +1,234 @@ +From 1f42fe3358dc55b9686d9c2dd0725dd7e4a14efa Mon Sep 17 00:00:00 2001 +From: PJ Reiniger +Date: Sat, 7 May 2022 22:53:50 -0400 +Subject: [PATCH 09/31] Remove format_provider + +--- + llvm/include/llvm/Support/Chrono.h | 109 ------------------------ + llvm/include/llvm/Support/raw_ostream.h | 5 -- + llvm/unittests/Support/Chrono.cpp | 61 ------------- + 3 files changed, 175 deletions(-) + +diff --git a/llvm/include/llvm/Support/Chrono.h b/llvm/include/llvm/Support/Chrono.h +index 2c2869de49b6..06829d1fbe14 100644 +--- a/llvm/include/llvm/Support/Chrono.h ++++ b/llvm/include/llvm/Support/Chrono.h +@@ -10,7 +10,6 @@ + #define LLVM_SUPPORT_CHRONO_H + + #include "llvm/Support/Compiler.h" +-#include "llvm/Support/FormatProviders.h" + + #include + #include +@@ -58,114 +57,6 @@ toTimePoint(std::time_t T, uint32_t nsec) { + + raw_ostream &operator<<(raw_ostream &OS, sys::TimePoint<> TP); + +-/// Format provider for TimePoint<> +-/// +-/// The options string is a strftime format string, with extensions: +-/// - %L is millis: 000-999 +-/// - %f is micros: 000000-999999 +-/// - %N is nanos: 000000000 - 999999999 +-/// +-/// If no options are given, the default format is "%Y-%m-%d %H:%M:%S.%N". +-template <> +-struct format_provider> { +- static void format(const sys::TimePoint<> &TP, llvm::raw_ostream &OS, +- std::string_view Style); +-}; +- +-namespace detail { +-template struct unit { static const char value[]; }; +-template const char unit::value[] = ""; +- +-template <> struct unit> { static const char value[]; }; +-template <> struct unit> { static const char value[]; }; +-template <> struct unit> { static const char value[]; }; +-template <> struct unit { static const char value[]; }; +-template <> struct unit { static const char value[]; }; +-template <> struct unit { static const char value[]; }; +-} // namespace detail +- +-/// Implementation of format_provider for duration types. +-/// +-/// The options string of a duration type has the grammar: +-/// +-/// duration_options ::= [unit][show_unit [number_options]] +-/// unit ::= `h`|`m`|`s`|`ms|`us`|`ns` +-/// show_unit ::= `+` | `-` +-/// number_options ::= options string for a integral or floating point type +-/// +-/// Examples +-/// ================================= +-/// | options | Input | Output | +-/// ================================= +-/// | "" | 1s | 1 s | +-/// | "ms" | 1s | 1000 ms | +-/// | "ms-" | 1s | 1000 | +-/// | "ms-n" | 1s | 1,000 | +-/// | "" | 1.0s | 1.00 s | +-/// ================================= +-/// +-/// If the unit of the duration type is not one of the units specified above, +-/// it is still possible to format it, provided you explicitly request a +-/// display unit or you request that the unit is not displayed. +- +-template +-struct format_provider> { +-private: +- typedef std::chrono::duration Dur; +- typedef std::conditional_t::value, +- double, intmax_t> +- InternalRep; +- +- template static InternalRep getAs(const Dur &D) { +- using namespace std::chrono; +- return duration_cast>(D).count(); +- } +- +- static std::pair consumeUnit(std::string_view &Style, +- const Dur &D) { +- using namespace std::chrono; +- if (Style.consume_front("ns")) +- return {getAs(D), "ns"}; +- if (Style.consume_front("us")) +- return {getAs(D), "us"}; +- if (Style.consume_front("ms")) +- return {getAs(D), "ms"}; +- if (Style.consume_front("s")) +- return {getAs>(D), "s"}; +- if (Style.consume_front("m")) +- return {getAs>(D), "m"}; +- if (Style.consume_front("h")) +- return {getAs>(D), "h"}; +- return {D.count(), detail::unit::value}; +- } +- +- static bool consumeShowUnit(std::string_view &Style) { +- if (Style.empty()) +- return true; +- if (Style.consume_front("-")) +- return false; +- if (Style.consume_front("+")) +- return true; +- assert(0 && "Unrecognised duration format"); +- return true; +- } +- +-public: +- static void format(const Dur &D, llvm::raw_ostream &Stream, std::string_view Style) { +- InternalRep count; +- std::string_view unit; +- std::tie(count, unit) = consumeUnit(Style, D); +- bool show_unit = consumeShowUnit(Style); +- +- format_provider::format(count, Stream, Style); +- +- if (show_unit) { +- assert(!unit.empty()); +- Stream << " " << unit; +- } +- } +-}; +- + } // namespace llvm + + #endif // LLVM_SUPPORT_CHRONO_H +diff --git a/llvm/include/llvm/Support/raw_ostream.h b/llvm/include/llvm/Support/raw_ostream.h +index 4f6ea873304f..94d0ed66eced 100644 +--- a/llvm/include/llvm/Support/raw_ostream.h ++++ b/llvm/include/llvm/Support/raw_ostream.h +@@ -29,11 +29,6 @@ + + namespace llvm { + +-class formatv_object_base; +-class format_object_base; +-class FormattedString; +-class FormattedNumber; +-class FormattedBytes; + template class LLVM_NODISCARD Expected; + + namespace sys { +diff --git a/llvm/unittests/Support/Chrono.cpp b/llvm/unittests/Support/Chrono.cpp +index 9a08a5c1bfdf..3c049de18c0a 100644 +--- a/llvm/unittests/Support/Chrono.cpp ++++ b/llvm/unittests/Support/Chrono.cpp +@@ -30,37 +30,6 @@ TEST(Chrono, TimeTConversion) { + EXPECT_EQ(TP, toTimePoint(toTimeT(TP))); + } + +-TEST(Chrono, TimePointFormat) { +- using namespace std::chrono; +- struct tm TM {}; +- TM.tm_year = 106; +- TM.tm_mon = 0; +- TM.tm_mday = 2; +- TM.tm_hour = 15; +- TM.tm_min = 4; +- TM.tm_sec = 5; +- TM.tm_isdst = -1; +- TimePoint<> T = +- system_clock::from_time_t(mktime(&TM)) + nanoseconds(123456789); +- +- // operator<< uses the format YYYY-MM-DD HH:MM:SS.NNNNNNNNN +- std::string S; +- raw_string_ostream OS(S); +- OS << T; +- EXPECT_EQ("2006-01-02 15:04:05.123456789", OS.str()); +- +- // formatv default style matches operator<<. +- EXPECT_EQ("2006-01-02 15:04:05.123456789", formatv("{0}", T).str()); +- // formatv supports strftime-style format strings. +- EXPECT_EQ("15:04:05", formatv("{0:%H:%M:%S}", T).str()); +- // formatv supports our strftime extensions for sub-second precision. +- EXPECT_EQ("123", formatv("{0:%L}", T).str()); +- EXPECT_EQ("123456", formatv("{0:%f}", T).str()); +- EXPECT_EQ("123456789", formatv("{0:%N}", T).str()); +- // our extensions don't interfere with %% escaping. +- EXPECT_EQ("%foo", formatv("{0:%%foo}", T).str()); +-} +- + // Test that toTimePoint and toTimeT can be called with a arguments with varying + // precisions. + TEST(Chrono, ImplicitConversions) { +@@ -78,34 +47,4 @@ TEST(Chrono, ImplicitConversions) { + EXPECT_EQ(TimeT, toTimeT(Nano)); + } + +-TEST(Chrono, DurationFormat) { +- EXPECT_EQ("1 h", formatv("{0}", hours(1)).str()); +- EXPECT_EQ("1 m", formatv("{0}", minutes(1)).str()); +- EXPECT_EQ("1 s", formatv("{0}", seconds(1)).str()); +- EXPECT_EQ("1 ms", formatv("{0}", milliseconds(1)).str()); +- EXPECT_EQ("1 us", formatv("{0}", microseconds(1)).str()); +- EXPECT_EQ("1 ns", formatv("{0}", nanoseconds(1)).str()); +- +- EXPECT_EQ("1 s", formatv("{0:+}", seconds(1)).str()); +- EXPECT_EQ("1", formatv("{0:-}", seconds(1)).str()); +- +- EXPECT_EQ("1000 ms", formatv("{0:ms}", seconds(1)).str()); +- EXPECT_EQ("1000000 us", formatv("{0:us}", seconds(1)).str()); +- EXPECT_EQ("1000", formatv("{0:ms-}", seconds(1)).str()); +- +- EXPECT_EQ("1,000 ms", formatv("{0:+n}", milliseconds(1000)).str()); +- EXPECT_EQ("0x3e8", formatv("{0:-x}", milliseconds(1000)).str()); +- EXPECT_EQ("010", formatv("{0:-3}", milliseconds(10)).str()); +- EXPECT_EQ("10,000", formatv("{0:ms-n}", seconds(10)).str()); +- +- EXPECT_EQ("1.00 s", formatv("{0}", duration(1)).str()); +- EXPECT_EQ("0.123 s", formatv("{0:+3}", duration(0.123f)).str()); +- EXPECT_EQ("1.230e-01 s", formatv("{0:+e3}", duration(0.123f)).str()); +- +- typedef duration> +- microfortnights; +- EXPECT_EQ("1.00", formatv("{0:-}", microfortnights(1)).str()); +- EXPECT_EQ("1209.60 ms", formatv("{0:ms}", microfortnights(1)).str()); +-} +- + } // anonymous namespace +-- +2.20.1.windows.1 + diff --git a/upstream_utils/llvm_patches/0010-Remove-reverse-iterator.patch b/upstream_utils/llvm_patches/0010-Remove-reverse-iterator.patch new file mode 100644 index 0000000000..1b4abf6739 --- /dev/null +++ b/upstream_utils/llvm_patches/0010-Remove-reverse-iterator.patch @@ -0,0 +1,266 @@ +From 29e7b322eab2284b434cc270bb36ade7c8a62755 Mon Sep 17 00:00:00 2001 +From: PJ Reiniger +Date: Sat, 7 May 2022 23:02:07 -0400 +Subject: [PATCH 10/31] Remove reverse iterator + +--- + llvm/include/llvm/ADT/DenseMap.h | 74 ++++------------------------- + llvm/include/llvm/ADT/SmallPtrSet.h | 21 +------- + 2 files changed, 11 insertions(+), 84 deletions(-) + +diff --git a/llvm/include/llvm/ADT/DenseMap.h b/llvm/include/llvm/ADT/DenseMap.h +index 588c39faea2f..5e5c020adf0b 100644 +--- a/llvm/include/llvm/ADT/DenseMap.h ++++ b/llvm/include/llvm/ADT/DenseMap.h +@@ -76,8 +76,6 @@ public: + // empty buckets. + if (empty()) + return end(); +- if (shouldReverseIterate()) +- return makeIterator(getBucketsEnd() - 1, getBuckets(), *this); + return makeIterator(getBuckets(), getBucketsEnd(), *this); + } + inline iterator end() { +@@ -86,8 +84,6 @@ public: + inline const_iterator begin() const { + if (empty()) + return end(); +- if (shouldReverseIterate()) +- return makeConstIterator(getBucketsEnd() - 1, getBuckets(), *this); + return makeConstIterator(getBuckets(), getBucketsEnd(), *this); + } + inline const_iterator end() const { +@@ -150,18 +146,14 @@ public: + iterator find(const_arg_type_t Val) { + BucketT *TheBucket; + if (LookupBucketFor(Val, TheBucket)) +- return makeIterator(TheBucket, +- shouldReverseIterate() ? getBuckets() +- : getBucketsEnd(), ++ return makeIterator(TheBucket, getBucketsEnd(), + *this, true); + return end(); + } + const_iterator find(const_arg_type_t Val) const { + const BucketT *TheBucket; + if (LookupBucketFor(Val, TheBucket)) +- return makeConstIterator(TheBucket, +- shouldReverseIterate() ? getBuckets() +- : getBucketsEnd(), ++ return makeConstIterator(TheBucket, getBucketsEnd(), + *this, true); + return end(); + } +@@ -175,9 +167,7 @@ public: + iterator find_as(const LookupKeyT &Val) { + BucketT *TheBucket; + if (LookupBucketFor(Val, TheBucket)) +- return makeIterator(TheBucket, +- shouldReverseIterate() ? getBuckets() +- : getBucketsEnd(), ++ return makeIterator(TheBucket, getBucketsEnd(), + *this, true); + return end(); + } +@@ -185,9 +175,7 @@ public: + const_iterator find_as(const LookupKeyT &Val) const { + const BucketT *TheBucket; + if (LookupBucketFor(Val, TheBucket)) +- return makeConstIterator(TheBucket, +- shouldReverseIterate() ? getBuckets() +- : getBucketsEnd(), ++ return makeConstIterator(TheBucket, getBucketsEnd(), + *this, true); + return end(); + } +@@ -222,20 +210,14 @@ public: + std::pair try_emplace(KeyT &&Key, Ts &&... Args) { + BucketT *TheBucket; + if (LookupBucketFor(Key, TheBucket)) +- return std::make_pair(makeIterator(TheBucket, +- shouldReverseIterate() +- ? getBuckets() +- : getBucketsEnd(), ++ return std::make_pair(makeIterator(TheBucket, getBucketsEnd(), + *this, true), + false); // Already in map. + + // Otherwise, insert the new element. + TheBucket = + InsertIntoBucket(TheBucket, std::move(Key), std::forward(Args)...); +- return std::make_pair(makeIterator(TheBucket, +- shouldReverseIterate() +- ? getBuckets() +- : getBucketsEnd(), ++ return std::make_pair(makeIterator(TheBucket, getBucketsEnd(), + *this, true), + true); + } +@@ -247,19 +229,13 @@ public: + std::pair try_emplace(const KeyT &Key, Ts &&... Args) { + BucketT *TheBucket; + if (LookupBucketFor(Key, TheBucket)) +- return std::make_pair(makeIterator(TheBucket, +- shouldReverseIterate() +- ? getBuckets() +- : getBucketsEnd(), ++ return std::make_pair(makeIterator(TheBucket, getBucketsEnd(), + *this, true), + false); // Already in map. + + // Otherwise, insert the new element. + TheBucket = InsertIntoBucket(TheBucket, Key, std::forward(Args)...); +- return std::make_pair(makeIterator(TheBucket, +- shouldReverseIterate() +- ? getBuckets() +- : getBucketsEnd(), ++ return std::make_pair(makeIterator(TheBucket, getBucketsEnd(), + *this, true), + true); + } +@@ -274,20 +250,14 @@ public: + const LookupKeyT &Val) { + BucketT *TheBucket; + if (LookupBucketFor(Val, TheBucket)) +- return std::make_pair(makeIterator(TheBucket, +- shouldReverseIterate() +- ? getBuckets() +- : getBucketsEnd(), ++ return std::make_pair(makeIterator(TheBucket, getBucketsEnd(), + *this, true), + false); // Already in map. + + // Otherwise, insert the new element. + TheBucket = InsertIntoBucketWithLookup(TheBucket, std::move(KV.first), + std::move(KV.second), Val); +- return std::make_pair(makeIterator(TheBucket, +- shouldReverseIterate() +- ? getBuckets() +- : getBucketsEnd(), ++ return std::make_pair(makeIterator(TheBucket, getBucketsEnd(), + *this, true), + true); + } +@@ -464,20 +434,12 @@ private: + iterator makeIterator(BucketT *P, BucketT *E, + DebugEpochBase &Epoch, + bool NoAdvance=false) { +- if (shouldReverseIterate()) { +- BucketT *B = P == getBucketsEnd() ? getBuckets() : P + 1; +- return iterator(B, E, Epoch, NoAdvance); +- } + return iterator(P, E, Epoch, NoAdvance); + } + + const_iterator makeConstIterator(const BucketT *P, const BucketT *E, + const DebugEpochBase &Epoch, + const bool NoAdvance=false) const { +- if (shouldReverseIterate()) { +- const BucketT *B = P == getBucketsEnd() ? getBuckets() : P + 1; +- return const_iterator(B, E, Epoch, NoAdvance); +- } + return const_iterator(P, E, Epoch, NoAdvance); + } + +@@ -1214,10 +1176,6 @@ public: + assert(isHandleInSync() && "invalid construction!"); + + if (NoAdvance) return; +- if (shouldReverseIterate()) { +- RetreatPastEmptyBuckets(); +- return; +- } + AdvancePastEmptyBuckets(); + } + +@@ -1232,16 +1190,10 @@ public: + + reference operator*() const { + assert(isHandleInSync() && "invalid iterator access!"); +- assert(Ptr != End && "dereferencing end() iterator"); +- if (shouldReverseIterate()) +- return Ptr[-1]; + return *Ptr; + } + pointer operator->() const { + assert(isHandleInSync() && "invalid iterator access!"); +- assert(Ptr != End && "dereferencing end() iterator"); +- if (shouldReverseIterate()) +- return &(Ptr[-1]); + return Ptr; + } + +@@ -1261,12 +1213,6 @@ public: + + inline DenseMapIterator& operator++() { // Preincrement + assert(isHandleInSync() && "invalid iterator access!"); +- assert(Ptr != End && "incrementing end() iterator"); +- if (shouldReverseIterate()) { +- --Ptr; +- RetreatPastEmptyBuckets(); +- return *this; +- } + ++Ptr; + AdvancePastEmptyBuckets(); + return *this; +diff --git a/llvm/include/llvm/ADT/SmallPtrSet.h b/llvm/include/llvm/ADT/SmallPtrSet.h +index 981b741669b0..e46a5171e301 100644 +--- a/llvm/include/llvm/ADT/SmallPtrSet.h ++++ b/llvm/include/llvm/ADT/SmallPtrSet.h +@@ -226,10 +226,6 @@ protected: + public: + explicit SmallPtrSetIteratorImpl(const void *const *BP, const void*const *E) + : Bucket(BP), End(E) { +- if (shouldReverseIterate()) { +- RetreatIfNotValid(); +- return; +- } + AdvanceIfNotValid(); + } + +@@ -281,22 +277,11 @@ public: + // Most methods are provided by the base class. + + const PtrTy operator*() const { +- assert(isHandleInSync() && "invalid iterator access!"); +- if (shouldReverseIterate()) { +- assert(Bucket > End); +- return PtrTraits::getFromVoidPointer(const_cast(Bucket[-1])); +- } + assert(Bucket < End); + return PtrTraits::getFromVoidPointer(const_cast(*Bucket)); + } + + inline SmallPtrSetIterator& operator++() { // Preincrement +- assert(isHandleInSync() && "invalid iterator access!"); +- if (shouldReverseIterate()) { +- --Bucket; +- RetreatIfNotValid(); +- return *this; +- } + ++Bucket; + AdvanceIfNotValid(); + return *this; +@@ -400,8 +385,6 @@ public: + } + + iterator begin() const { +- if (shouldReverseIterate()) +- return makeIterator(EndPointer() - 1); + return makeIterator(CurArray); + } + iterator end() const { return makeIterator(EndPointer()); } +@@ -409,9 +392,7 @@ public: + private: + /// Create an iterator that dereferences to same place as the given pointer. + iterator makeIterator(const void *const *P) const { +- if (shouldReverseIterate()) +- return iterator(P == EndPointer() ? CurArray : P + 1, CurArray, *this); +- return iterator(P, EndPointer(), *this); ++ return iterator(P, EndPointer()); + } + }; + +-- +2.20.1.windows.1 + diff --git a/upstream_utils/llvm_patches/0011-Remove-allocator-from-collections.patch b/upstream_utils/llvm_patches/0011-Remove-allocator-from-collections.patch new file mode 100644 index 0000000000..d034e97cba --- /dev/null +++ b/upstream_utils/llvm_patches/0011-Remove-allocator-from-collections.patch @@ -0,0 +1,237 @@ +From be653ec8d4b0a5944fcf084a911389cb0c5cc205 Mon Sep 17 00:00:00 2001 +From: PJ Reiniger +Date: Sat, 7 May 2022 23:07:38 -0400 +Subject: [PATCH 11/31] Remove allocator from collections + +--- + llvm/include/llvm/ADT/StringMap.h | 33 +++++++------------------- + llvm/include/llvm/ADT/StringMapEntry.h | 25 +++++++------------ + llvm/unittests/ADT/StringMapTest.cpp | 14 +++++------ + 3 files changed, 23 insertions(+), 49 deletions(-) + +diff --git a/llvm/include/llvm/ADT/StringMap.h b/llvm/include/llvm/ADT/StringMap.h +index 5f463cfef943..3b40bba37f58 100644 +--- a/llvm/include/llvm/ADT/StringMap.h ++++ b/llvm/include/llvm/ADT/StringMap.h +@@ -104,10 +104,8 @@ public: + /// keys that are "strings", which are basically ranges of bytes. This does some + /// funky memory allocation and hashing things to make it extremely efficient, + /// storing the string data *after* the value in the map. +-template ++template + class StringMap : public StringMapImpl { +- AllocatorTy Allocator; +- + public: + using MapEntryTy = StringMapEntry; + +@@ -116,14 +114,6 @@ public: + explicit StringMap(unsigned InitialSize) + : StringMapImpl(InitialSize, static_cast(sizeof(MapEntryTy))) {} + +- explicit StringMap(AllocatorTy A) +- : StringMapImpl(static_cast(sizeof(MapEntryTy))), Allocator(A) { +- } +- +- StringMap(unsigned InitialSize, AllocatorTy A) +- : StringMapImpl(InitialSize, static_cast(sizeof(MapEntryTy))), +- Allocator(A) {} +- + StringMap(std::initializer_list> List) + : StringMapImpl(List.size(), static_cast(sizeof(MapEntryTy))) { + for (const auto &P : List) { +@@ -132,11 +122,10 @@ public: + } + + StringMap(StringMap &&RHS) +- : StringMapImpl(std::move(RHS)), Allocator(std::move(RHS.Allocator)) {} ++ : StringMapImpl(std::move(RHS)) {} + +- StringMap(const StringMap &RHS) +- : StringMapImpl(static_cast(sizeof(MapEntryTy))), +- Allocator(RHS.Allocator) { ++ StringMap(const StringMap &RHS) : ++ StringMapImpl(static_cast(sizeof(MapEntryTy))) { + if (RHS.empty()) + return; + +@@ -156,7 +145,7 @@ public: + } + + TheTable[I] = MapEntryTy::Create( +- static_cast(Bucket)->getKey(), Allocator, ++ static_cast(Bucket)->getKey(), + static_cast(Bucket)->getValue()); + HashTable[I] = RHSHashTable[I]; + } +@@ -171,7 +160,6 @@ public: + + StringMap &operator=(StringMap RHS) { + StringMapImpl::swap(RHS); +- std::swap(Allocator, RHS.Allocator); + return *this; + } + +@@ -183,16 +171,13 @@ public: + for (unsigned I = 0, E = NumBuckets; I != E; ++I) { + StringMapEntryBase *Bucket = TheTable[I]; + if (Bucket && Bucket != getTombstoneVal()) { +- static_cast(Bucket)->Destroy(Allocator); ++ static_cast(Bucket)->Destroy(); + } + } + } + free(TheTable); + } + +- AllocatorTy &getAllocator() { return Allocator; } +- const AllocatorTy &getAllocator() const { return Allocator; } +- + using key_type = const char *; + using mapped_type = ValueTy; + using value_type = StringMapEntry; +@@ -321,7 +306,7 @@ public: + + if (Bucket == getTombstoneVal()) + --NumTombstones; +- Bucket = MapEntryTy::Create(Key, Allocator, std::forward(Args)...); ++ Bucket = MapEntryTy::Create(Key, std::forward(Args)...); + ++NumItems; + assert(NumItems + NumTombstones <= NumBuckets); + +@@ -339,7 +324,7 @@ public: + for (unsigned I = 0, E = NumBuckets; I != E; ++I) { + StringMapEntryBase *&Bucket = TheTable[I]; + if (Bucket && Bucket != getTombstoneVal()) { +- static_cast(Bucket)->Destroy(Allocator); ++ static_cast(Bucket)->Destroy(); + } + Bucket = nullptr; + } +@@ -355,7 +340,7 @@ public: + void erase(iterator I) { + MapEntryTy &V = *I; + remove(&V); +- V.Destroy(Allocator); ++ V.Destroy(); + } + + bool erase(std::string_view Key) { +diff --git a/llvm/include/llvm/ADT/StringMapEntry.h b/llvm/include/llvm/ADT/StringMapEntry.h +index 93e13b5bb16c..66a30698d787 100644 +--- a/llvm/include/llvm/ADT/StringMapEntry.h ++++ b/llvm/include/llvm/ADT/StringMapEntry.h +@@ -33,22 +33,14 @@ protected: + /// Helper to tail-allocate \p Key. It'd be nice to generalize this so it + /// could be reused elsewhere, maybe even taking an llvm::function_ref to + /// type-erase the allocator and put it in a source file. +- template + static void *allocateWithKey(size_t EntrySize, size_t EntryAlign, +- std::string_view Key, AllocatorTy &Allocator); +-}; +- +-// Define out-of-line to dissuade inlining. +-template +-void *StringMapEntryBase::allocateWithKey(size_t EntrySize, size_t EntryAlign, +- std::string_view Key, +- AllocatorTy &Allocator) { ++ std::string_view Key) { + size_t KeyLength = Key.size(); + + // Allocate a new item with space for the string at the end and a null + // terminator. + size_t AllocSize = EntrySize + KeyLength + 1; +- void *Allocation = Allocator.Allocate(AllocSize, EntryAlign); ++ void *Allocation = safe_malloc(AllocSize); + assert(Allocation && "Unhandled out-of-memory"); + + // Copy the string information. +@@ -58,6 +50,7 @@ void *StringMapEntryBase::allocateWithKey(size_t EntrySize, size_t EntryAlign, + Buffer[KeyLength] = 0; // Null terminate for convenience of clients. + return Allocation; + } ++}; + + /// StringMapEntryStorage - Holds the value in a StringMapEntry. + /// +@@ -117,11 +110,11 @@ public: + + /// Create a StringMapEntry for the specified key construct the value using + /// \p InitiVals. +- template +- static StringMapEntry *Create(std::string_view key, AllocatorTy &allocator, ++ template ++ static StringMapEntry *Create(std::string_view key, + InitTy &&... initVals) { + return new (StringMapEntryBase::allocateWithKey( +- sizeof(StringMapEntry), alignof(StringMapEntry), key, allocator)) ++ sizeof(StringMapEntry), alignof(StringMapEntry), key)) + StringMapEntry(key.size(), std::forward(initVals)...); + } + +@@ -134,12 +127,10 @@ public: + + /// Destroy - Destroy this StringMapEntry, releasing memory back to the + /// specified allocator. +- template void Destroy(AllocatorTy &allocator) { ++ void Destroy() { + // Free memory referenced by the item. +- size_t AllocSize = sizeof(StringMapEntry) + this->getKeyLength() + 1; + this->~StringMapEntry(); +- allocator.Deallocate(static_cast(this), AllocSize, +- alignof(StringMapEntry)); ++ std::free(static_cast(this)); + } + }; + +diff --git a/llvm/unittests/ADT/StringMapTest.cpp b/llvm/unittests/ADT/StringMapTest.cpp +index 5211f01bbd73..28d710fe69e9 100644 +--- a/llvm/unittests/ADT/StringMapTest.cpp ++++ b/llvm/unittests/ADT/StringMapTest.cpp +@@ -223,13 +223,12 @@ TEST_F(StringMapTest, IterationTest) { + + // Test StringMapEntry::Create() method. + TEST_F(StringMapTest, StringMapEntryTest) { +- MallocAllocator Allocator; + StringMap::value_type *entry = + StringMap::value_type::Create( + std::string_view(testKeyFirst, testKeyLength), 1u); + EXPECT_STREQ(testKey, entry->first().data()); + EXPECT_EQ(1u, entry->second); +- entry->Destroy(Allocator); ++ entry->Destroy(); + } + + // Test insert() method. +@@ -238,7 +237,7 @@ TEST_F(StringMapTest, InsertTest) { + testMap.insert( + StringMap::value_type::Create( + std::string_view(testKeyFirst, testKeyLength), +- testMap.getAllocator(), 1u)); ++ 1u)); + assertSingleItemMap(); + } + +@@ -353,15 +352,14 @@ TEST_F(StringMapTest, MoveOnly) { + StringMap t; + t.insert(std::make_pair("Test", MoveOnly(42))); + std::string_view Key = "Test"; +- StringMapEntry::Create(Key, t.getAllocator(), MoveOnly(42)) +- ->Destroy(t.getAllocator()); ++ StringMapEntry::Create(Key, MoveOnly(42)) ++ ->Destroy(); + } + + TEST_F(StringMapTest, CtorArg) { + std::string_view Key = "Test"; +- MallocAllocator Allocator; +- StringMapEntry::Create(Key, Allocator, Immovable()) +- ->Destroy(Allocator); ++ StringMapEntry::Create(Key, Immovable()) ++ ->Destroy(); + } + + TEST_F(StringMapTest, MoveConstruct) { +-- +2.20.1.windows.1 + diff --git a/upstream_utils/llvm_patches/0012-Remove-EpochTracker.patch b/upstream_utils/llvm_patches/0012-Remove-EpochTracker.patch new file mode 100644 index 0000000000..c5fb640be9 --- /dev/null +++ b/upstream_utils/llvm_patches/0012-Remove-EpochTracker.patch @@ -0,0 +1,91 @@ +From ba61e94bf9e2273b6a2e5d3d4b2bb74f76536b19 Mon Sep 17 00:00:00 2001 +From: PJ Reiniger +Date: Sat, 7 May 2022 23:08:15 -0400 +Subject: [PATCH 12/31] Remove EpochTracker + +--- + llvm/include/llvm/ADT/SmallPtrSet.h | 14 ++++---------- + llvm/lib/Support/SmallPtrSet.cpp | 1 - + 2 files changed, 4 insertions(+), 11 deletions(-) + +diff --git a/llvm/include/llvm/ADT/SmallPtrSet.h b/llvm/include/llvm/ADT/SmallPtrSet.h +index e46a5171e301..2edc1f5e2066 100644 +--- a/llvm/include/llvm/ADT/SmallPtrSet.h ++++ b/llvm/include/llvm/ADT/SmallPtrSet.h +@@ -14,7 +14,6 @@ + #ifndef LLVM_ADT_SMALLPTRSET_H + #define LLVM_ADT_SMALLPTRSET_H + +-#include "llvm/ADT/EpochTracker.h" + #include "llvm/Support/Compiler.h" + #include "llvm/Support/ReverseIteration.h" + #include "llvm/Support/type_traits.h" +@@ -46,7 +45,7 @@ namespace llvm { + /// (-2), to allow deletion. The hash table is resized when the table is 3/4 or + /// more. When this happens, the table is doubled in size. + /// +-class SmallPtrSetImplBase : public DebugEpochBase { ++class SmallPtrSetImplBase { + friend class SmallPtrSetIteratorImpl; + + protected: +@@ -92,7 +91,6 @@ public: + size_type size() const { return NumNonEmpty - NumTombstones; } + + void clear() { +- incrementEpoch(); + // If the capacity of the array is huge, and the # elements used is small, + // shrink the array. + if (!isSmall()) { +@@ -139,14 +137,12 @@ protected: + if (LastTombstone != nullptr) { + *LastTombstone = Ptr; + --NumTombstones; +- incrementEpoch(); + return std::make_pair(LastTombstone, true); + } + + // Nope, there isn't. If we stay small, just 'pushback' now. + if (NumNonEmpty < CurArraySize) { + SmallArray[NumNonEmpty++] = Ptr; +- incrementEpoch(); + return std::make_pair(SmallArray + (NumNonEmpty - 1), true); + } + // Otherwise, hit the big set case, which will call grow. +@@ -259,8 +255,7 @@ protected: + + /// SmallPtrSetIterator - This implements a const_iterator for SmallPtrSet. + template +-class SmallPtrSetIterator : public SmallPtrSetIteratorImpl, +- DebugEpochBase::HandleBase { ++class SmallPtrSetIterator : public SmallPtrSetIteratorImpl { + using PtrTraits = PointerLikeTypeTraits; + + public: +@@ -270,9 +265,8 @@ public: + using difference_type = std::ptrdiff_t; + using iterator_category = std::forward_iterator_tag; + +- explicit SmallPtrSetIterator(const void *const *BP, const void *const *E, +- const DebugEpochBase &Epoch) +- : SmallPtrSetIteratorImpl(BP, E), DebugEpochBase::HandleBase(&Epoch) {} ++ explicit SmallPtrSetIterator(const void *const *BP, const void *const *E) ++ : SmallPtrSetIteratorImpl(BP, E) {} + + // Most methods are provided by the base class. + +diff --git a/llvm/lib/Support/SmallPtrSet.cpp b/llvm/lib/Support/SmallPtrSet.cpp +index f6e2dfb8a6c9..4549db08e3ee 100644 +--- a/llvm/lib/Support/SmallPtrSet.cpp ++++ b/llvm/lib/Support/SmallPtrSet.cpp +@@ -59,7 +59,6 @@ SmallPtrSetImplBase::insert_imp_big(const void *Ptr) { + else + ++NumNonEmpty; // Track density. + *Bucket = Ptr; +- incrementEpoch(); + return std::make_pair(Bucket, true); + } + +-- +2.20.1.windows.1 + diff --git a/upstream_utils/llvm_patches/0013-Add-compiler-warning-pragrams.patch b/upstream_utils/llvm_patches/0013-Add-compiler-warning-pragrams.patch new file mode 100644 index 0000000000..ad6d0d8b0b --- /dev/null +++ b/upstream_utils/llvm_patches/0013-Add-compiler-warning-pragrams.patch @@ -0,0 +1,230 @@ +From 2e9ec7d3e5c58424fde4312784e2a9faab58fe2c Mon Sep 17 00:00:00 2001 +From: PJ Reiniger +Date: Sun, 8 May 2022 13:34:07 -0400 +Subject: [PATCH 13/31] Add compiler warning pragrams + +--- + llvm/include/llvm/ADT/FunctionExtras.h | 10 ++++++++++ + llvm/include/llvm/ADT/Hashing.h | 9 +++++++++ + llvm/include/llvm/ADT/SmallVector.h | 8 ++++++++ + llvm/include/llvm/Support/MathExtras.h | 9 +++++++++ + llvm/include/llvm/Support/MemAlloc.h | 13 +++++++++++++ + llvm/lib/Support/raw_ostream.cpp | 4 ++++ + llvm/unittests/ADT/DenseMapTest.cpp | 4 ++++ + llvm/unittests/ADT/MapVectorTest.cpp | 7 +++++++ + llvm/unittests/ADT/SmallVectorTest.cpp | 4 ++++ + llvm/unittests/Support/AlignOfTest.cpp | 7 +++---- + 10 files changed, 71 insertions(+), 4 deletions(-) + +diff --git a/llvm/include/llvm/ADT/FunctionExtras.h b/llvm/include/llvm/ADT/FunctionExtras.h +index 1a26cb702cae..3b834236b8fa 100644 +--- a/llvm/include/llvm/ADT/FunctionExtras.h ++++ b/llvm/include/llvm/ADT/FunctionExtras.h +@@ -54,6 +54,12 @@ namespace llvm { + /// It can hold functions with a non-const operator(), like mutable lambdas. + template class unique_function; + ++// GCC warns on OutOfLineStorage ++#if defined(__GNUC__) && !defined(__clang__) ++#pragma GCC diagnostic push ++#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" ++#endif ++ + namespace detail { + + template +@@ -405,6 +411,10 @@ public: + } + }; + ++#if defined(__GNUC__) && !defined(__clang__) ++#pragma GCC diagnostic pop ++#endif ++ + } // end namespace llvm + + #endif // LLVM_ADT_FUNCTIONEXTRAS_H +diff --git a/llvm/include/llvm/ADT/Hashing.h b/llvm/include/llvm/ADT/Hashing.h +index e296c1c53ebd..8f90b4214b92 100644 +--- a/llvm/include/llvm/ADT/Hashing.h ++++ b/llvm/include/llvm/ADT/Hashing.h +@@ -55,6 +55,11 @@ + #include + #include + ++#ifdef _WIN32 ++#pragma warning(push) ++#pragma warning(disable : 26495) ++#endif ++ + namespace llvm { + + /// An opaque object representing a hash code. +@@ -679,4 +684,8 @@ hash_code hash_value(const std::basic_string &arg) { + + } // namespace llvm + ++#ifdef _WIN32 ++#pragma warning(pop) ++#endif ++ + #endif +diff --git a/llvm/include/llvm/ADT/SmallVector.h b/llvm/include/llvm/ADT/SmallVector.h +index 602fcc5b7a98..690d512a43c8 100644 +--- a/llvm/include/llvm/ADT/SmallVector.h ++++ b/llvm/include/llvm/ADT/SmallVector.h +@@ -13,6 +13,14 @@ + #ifndef LLVM_ADT_SMALLVECTOR_H + #define LLVM_ADT_SMALLVECTOR_H + ++// This file uses std::memcpy() to copy std::pair. ++// That type is POD, but the standard doesn't guarantee that. GCC doesn't treat ++// the type as POD so it throws a warning. We want to consider this a warning ++// instead of an error. ++#if __GNUC__ >= 8 ++#pragma GCC diagnostic warning "-Wclass-memaccess" ++#endif ++ + #include "llvm/ADT/iterator_range.h" + #include "llvm/Support/Compiler.h" + #include "llvm/Support/ErrorHandling.h" +diff --git a/llvm/include/llvm/Support/MathExtras.h b/llvm/include/llvm/Support/MathExtras.h +index da843ef79ff9..fac12dd0e4c6 100644 +--- a/llvm/include/llvm/Support/MathExtras.h ++++ b/llvm/include/llvm/Support/MathExtras.h +@@ -435,6 +435,11 @@ inline uint64_t maxUIntN(uint64_t N) { + return UINT64_MAX >> (64 - N); + } + ++#ifdef _WIN32 ++#pragma warning(push) ++#pragma warning(disable : 4146) ++#endif ++ + /// Gets the minimum value for a N-bit signed integer. + inline int64_t minIntN(int64_t N) { + assert(N > 0 && N <= 64 && "integer width out of range"); +@@ -442,6 +447,10 @@ inline int64_t minIntN(int64_t N) { + return UINT64_C(1) + ~(UINT64_C(1) << (N - 1)); + } + ++#ifdef _WIN32 ++#pragma warning(pop) ++#endif ++ + /// Gets the maximum value for a N-bit signed integer. + inline int64_t maxIntN(int64_t N) { + assert(N > 0 && N <= 64 && "integer width out of range"); +diff --git a/llvm/include/llvm/Support/MemAlloc.h b/llvm/include/llvm/Support/MemAlloc.h +index d6012bd5a698..01007deb89bb 100644 +--- a/llvm/include/llvm/Support/MemAlloc.h ++++ b/llvm/include/llvm/Support/MemAlloc.h +@@ -22,6 +22,14 @@ + + namespace llvm { + ++#ifdef _WIN32 ++#pragma warning(push) ++// Warning on NONNULL, report is not known to abort ++#pragma warning(disable : 6387) ++#pragma warning(disable : 28196) ++#pragma warning(disable : 28183) ++#endif ++ + LLVM_ATTRIBUTE_RETURNS_NONNULL inline void *safe_malloc(size_t Sz) { + void *Result = std::malloc(Sz); + if (Result == nullptr) { +@@ -84,4 +92,9 @@ allocate_buffer(size_t Size, size_t Alignment); + void deallocate_buffer(void *Ptr, size_t Size, size_t Alignment); + + } // namespace llvm ++ ++#ifdef _WIN32 ++#pragma warning(pop) ++#endif ++ + #endif +diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp +index 306cc981ed8f..bff7b6c9a77b 100644 +--- a/llvm/lib/Support/raw_ostream.cpp ++++ b/llvm/lib/Support/raw_ostream.cpp +@@ -10,6 +10,10 @@ + // + //===----------------------------------------------------------------------===// + ++#ifdef _WIN32 ++#define _CRT_NONSTDC_NO_WARNINGS ++#endif ++ + #include "llvm/Support/raw_ostream.h" + #include "llvm/ADT/STLExtras.h" + #include "llvm/ADT/StringExtras.h" +diff --git a/llvm/unittests/ADT/DenseMapTest.cpp b/llvm/unittests/ADT/DenseMapTest.cpp +index a122f1fe3bdf..123012a3118d 100644 +--- a/llvm/unittests/ADT/DenseMapTest.cpp ++++ b/llvm/unittests/ADT/DenseMapTest.cpp +@@ -6,6 +6,10 @@ + // + //===----------------------------------------------------------------------===// + ++#if defined(__GNUC__) && !defined(__clang__) ++#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" ++#endif ++ + #include "llvm/ADT/DenseMap.h" + #include "gtest/gtest.h" + #include +diff --git a/llvm/unittests/ADT/MapVectorTest.cpp b/llvm/unittests/ADT/MapVectorTest.cpp +index 552f9956bdc2..20ebcd753bcc 100644 +--- a/llvm/unittests/ADT/MapVectorTest.cpp ++++ b/llvm/unittests/ADT/MapVectorTest.cpp +@@ -6,6 +6,13 @@ + // + //===----------------------------------------------------------------------===// + ++#if defined(__GNUC__) ++#pragma GCC diagnostic ignored "-Wpedantic" ++#if !defined(__clang__) ++#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" ++#endif ++#endif ++ + #include "llvm/ADT/MapVector.h" + #include "llvm/ADT/iterator_range.h" + #include "gtest/gtest.h" +diff --git a/llvm/unittests/ADT/SmallVectorTest.cpp b/llvm/unittests/ADT/SmallVectorTest.cpp +index 1914f38fac6c..387229c32d5a 100644 +--- a/llvm/unittests/ADT/SmallVectorTest.cpp ++++ b/llvm/unittests/ADT/SmallVectorTest.cpp +@@ -17,6 +17,10 @@ + #include + #include + ++#if defined(__GNUC__) ++#pragma GCC diagnostic ignored "-Wpedantic" ++#endif ++ + using namespace llvm; + + namespace { +diff --git a/llvm/unittests/Support/AlignOfTest.cpp b/llvm/unittests/Support/AlignOfTest.cpp +index f84895c18602..6a50205b143b 100644 +--- a/llvm/unittests/Support/AlignOfTest.cpp ++++ b/llvm/unittests/Support/AlignOfTest.cpp +@@ -31,10 +31,9 @@ namespace { + #pragma clang diagnostic ignored "-Wunknown-pragmas" + #pragma clang diagnostic ignored "-Winaccessible-base" + #elif ((__GNUC__ * 100) + __GNUC_MINOR__) >= 402 +-// Pragma based warning suppression was introduced in GGC 4.2. Additionally +-// this warning is "enabled by default". The warning still appears if -Wall is +-// suppressed. Apparently GCC suppresses it when -w is specifed, which is odd. +-#pragma GCC diagnostic warning "-w" ++#pragma GCC diagnostic warning "-Wunknown-pragmas" ++#pragma GCC diagnostic warning "-Winaccessible-base" ++#pragma GCC diagnostic warning "-Wunused-function" + #endif + + // Define some fixed alignment types to use in these tests. +-- +2.20.1.windows.1 + diff --git a/upstream_utils/llvm_patches/0014-Remove-unused-functions.patch b/upstream_utils/llvm_patches/0014-Remove-unused-functions.patch new file mode 100644 index 0000000000..cd5534853f --- /dev/null +++ b/upstream_utils/llvm_patches/0014-Remove-unused-functions.patch @@ -0,0 +1,743 @@ +From e8352137c16b81ab7af248e608eca3a1d8221e37 Mon Sep 17 00:00:00 2001 +From: PJ Reiniger +Date: Sun, 8 May 2022 13:43:50 -0400 +Subject: [PATCH 14/31] Remove unused functions + +--- + llvm/include/llvm/ADT/SmallString.h | 80 ------ + llvm/include/llvm/Support/Errno.h | 9 - + llvm/include/llvm/Support/VersionTuple.h | 33 --- + llvm/include/llvm/Support/raw_ostream.h | 115 +------- + llvm/lib/Support/raw_ostream.cpp | 327 ----------------------- + 5 files changed, 8 insertions(+), 556 deletions(-) + +diff --git a/llvm/include/llvm/ADT/SmallString.h b/llvm/include/llvm/ADT/SmallString.h +index 8112741dd01d..85987811f3c4 100644 +--- a/llvm/include/llvm/ADT/SmallString.h ++++ b/llvm/include/llvm/ADT/SmallString.h +@@ -86,48 +86,12 @@ public: + /// @name String Comparison + /// @{ + +- /// Check for string equality. This is more efficient than compare() when +- /// the relative ordering of inequal strings isn't needed. +- bool equals(std::string_view RHS) const { +- return str().equals(RHS); +- } +- +- /// Check for string equality, ignoring case. +- bool equals_insensitive(std::string_view RHS) const { +- return str().equals_insensitive(RHS); +- } +- + /// Compare two strings; the result is -1, 0, or 1 if this string is + /// lexicographically less than, equal to, or greater than the \p RHS. + int compare(std::string_view RHS) const { + return str().compare(RHS); + } + +- /// compare_insensitive - Compare two strings, ignoring case. +- int compare_insensitive(std::string_view RHS) const { +- return str().compare_insensitive(RHS); +- } +- +- /// compare_numeric - Compare two strings, treating sequences of digits as +- /// numbers. +- int compare_numeric(std::string_view RHS) const { +- return str().compare_numeric(RHS); +- } +- +- /// @} +- /// @name String Predicates +- /// @{ +- +- /// startswith - Check if this string starts with the given \p Prefix. +- bool startswith(std::string_view Prefix) const { +- return str().startswith(Prefix); +- } +- +- /// endswith - Check if this string ends with the given \p Suffix. +- bool endswith(std::string_view Suffix) const { +- return str().endswith(Suffix); +- } +- + /// @} + /// @name String Searching + /// @{ +@@ -208,50 +172,6 @@ public: + } + + /// @} +- /// @name Helpful Algorithms +- /// @{ +- +- /// Return the number of occurrences of \p C in the string. +- size_t count(char C) const { +- return str().count(C); +- } +- +- /// Return the number of non-overlapped occurrences of \p Str in the +- /// string. +- size_t count(std::string_view Str) const { +- return str().count(Str); +- } +- +- /// @} +- /// @name Substring Operations +- /// @{ +- +- /// Return a reference to the substring from [Start, Start + N). +- /// +- /// \param Start The index of the starting character in the substring; if +- /// the index is npos or greater than the length of the string then the +- /// empty substring will be returned. +- /// +- /// \param N The number of characters to included in the substring. If \p N +- /// exceeds the number of characters remaining in the string, the string +- /// suffix (starting with \p Start) will be returned. +- std::string_view substr(size_t Start, size_t N = std::string_view::npos) const { +- return str().substr(Start, N); +- } +- +- /// Return a reference to the substring from [Start, End). +- /// +- /// \param Start The index of the starting character in the substring; if +- /// the index is npos or greater than the length of the string then the +- /// empty substring will be returned. +- /// +- /// \param End The index following the last character to include in the +- /// substring. If this is npos, or less than \p Start, or exceeds the +- /// number of characters remaining in the string, the string suffix +- /// (starting with \p Start) will be returned. +- std::string_view slice(size_t Start, size_t End) const { +- return str().slice(Start, End); +- } + + // Extra methods. + +diff --git a/llvm/include/llvm/Support/Errno.h b/llvm/include/llvm/Support/Errno.h +index 07df6765d9db..d9bf68bb369e 100644 +--- a/llvm/include/llvm/Support/Errno.h ++++ b/llvm/include/llvm/Support/Errno.h +@@ -20,15 +20,6 @@ + namespace llvm { + namespace sys { + +-/// Returns a string representation of the errno value, using whatever +-/// thread-safe variant of strerror() is available. Be sure to call this +-/// immediately after the function that set errno, or errno may have been +-/// overwritten by an intervening call. +-std::string StrError(); +- +-/// Like the no-argument version above, but uses \p errnum instead of errno. +-std::string StrError(int errnum); +- + template + inline decltype(auto) RetryAfterSignal(const FailT &Fail, const Fun &F, + const Args &... As) { +diff --git a/llvm/include/llvm/Support/VersionTuple.h b/llvm/include/llvm/Support/VersionTuple.h +index 85f4f1a707c7..a28e12adcc25 100644 +--- a/llvm/include/llvm/Support/VersionTuple.h ++++ b/llvm/include/llvm/Support/VersionTuple.h +@@ -158,39 +158,6 @@ public: + friend bool operator>=(const VersionTuple &X, const VersionTuple &Y) { + return !(X < Y); + } +- +- friend llvm::hash_code hash_value(const VersionTuple &VT) { +- return llvm::hash_combine(VT.Major, VT.Minor, VT.Subminor, VT.Build); +- } +- +- /// Retrieve a string representation of the version number. +- std::string getAsString() const; +-}; +- +-/// Print a version number. +-raw_ostream &operator<<(raw_ostream &Out, const VersionTuple &V); +- +-// Provide DenseMapInfo for version tuples. +-template <> struct DenseMapInfo { +- static inline VersionTuple getEmptyKey() { return VersionTuple(0x7FFFFFFF); } +- static inline VersionTuple getTombstoneKey() { +- return VersionTuple(0x7FFFFFFE); +- } +- static unsigned getHashValue(const VersionTuple &Value) { +- unsigned Result = Value.getMajor(); +- if (auto Minor = Value.getMinor()) +- Result = detail::combineHashValue(Result, *Minor); +- if (auto Subminor = Value.getSubminor()) +- Result = detail::combineHashValue(Result, *Subminor); +- if (auto Build = Value.getBuild()) +- Result = detail::combineHashValue(Result, *Build); +- +- return Result; +- } +- +- static bool isEqual(const VersionTuple &LHS, const VersionTuple &RHS) { +- return LHS == RHS; +- } + }; + + } // end namespace llvm +diff --git a/llvm/include/llvm/Support/raw_ostream.h b/llvm/include/llvm/Support/raw_ostream.h +index 94d0ed66eced..50443e018aaf 100644 +--- a/llvm/include/llvm/Support/raw_ostream.h ++++ b/llvm/include/llvm/Support/raw_ostream.h +@@ -249,32 +249,6 @@ public: + return write(Str.data(), Str.size()); + } + +- raw_ostream &operator<<(unsigned long N); +- raw_ostream &operator<<(long N); +- raw_ostream &operator<<(unsigned long long N); +- raw_ostream &operator<<(long long N); +- raw_ostream &operator<<(const void *P); +- +- raw_ostream &operator<<(unsigned int N) { +- return this->operator<<(static_cast(N)); +- } +- +- raw_ostream &operator<<(int N) { +- return this->operator<<(static_cast(N)); +- } +- +- raw_ostream &operator<<(double N); +- +- /// Output \p N in hexadecimal, without any prefix or padding. +- raw_ostream &write_hex(unsigned long long N); +- +- // Change the foreground color of text. +- raw_ostream &operator<<(Colors C); +- +- /// Output a formatted UUID with dash separators. +- using uuid_t = uint8_t[16]; +- raw_ostream &write_uuid(const uuid_t UUID); +- + /// Output \p Str, turning '\\', '\t', '\n', '"', and anything that doesn't + /// satisfy llvm::isPrint into an escape sequence. + raw_ostream &write_escaped(std::string_view Str, bool UseHexEscapes = false); +@@ -282,21 +256,6 @@ public: + raw_ostream &write(unsigned char C); + raw_ostream &write(const char *Ptr, size_t Size); + +- // Formatted output, see the format() function in Support/Format.h. +- raw_ostream &operator<<(const format_object_base &Fmt); +- +- // Formatted output, see the leftJustify() function in Support/Format.h. +- raw_ostream &operator<<(const FormattedString &); +- +- // Formatted output, see the formatHex() function in Support/Format.h. +- raw_ostream &operator<<(const FormattedNumber &); +- +- // Formatted output, see the formatv() function in Support/FormatVariadic.h. +- raw_ostream &operator<<(const formatv_object_base &); +- +- // Formatted output, see the format_bytes() function in Support/Format.h. +- raw_ostream &operator<<(const FormattedBytes &); +- + /// indent - Insert 'NumSpaces' spaces. + raw_ostream &indent(unsigned NumSpaces); + +@@ -311,14 +270,19 @@ public: + /// @param BG if true change the background, default: change foreground + /// @returns itself so it can be used within << invocations + virtual raw_ostream &changeColor(enum Colors Color, bool Bold = false, +- bool BG = false); ++ bool BG = false) { ++ (void)Color; ++ (void)Bold; ++ (void)BG; ++ return *this; ++ } + + /// Resets the colors to terminal defaults. Call this when you are done + /// outputting colored text, or before program exit. +- virtual raw_ostream &resetColor(); ++ virtual raw_ostream &resetColor() { return *this; } + + /// Reverses the foreground and background colors. +- virtual raw_ostream &reverseColor(); ++ virtual raw_ostream &reverseColor() { return *this; } + + /// This function determines if this stream is connected to a "tty" or + /// "console" window. That is, the output would be displayed to the user +@@ -391,10 +355,6 @@ private: + /// unused bytes in the buffer. + void copy_to_buffer(const char *Ptr, size_t Size); + +- /// Compute whether colors should be used and do the necessary work such as +- /// flushing. The result is affected by calls to enable_color(). +- bool prepare_colors(); +- + /// Flush the tied-to stream (if present) and then write the required data. + void flush_tied_then_write(const char *Ptr, size_t Size); + +@@ -445,7 +405,6 @@ class raw_fd_ostream : public raw_pwrite_stream { + int FD; + bool ShouldClose; + bool SupportsSeeking = false; +- mutable Optional HasColors; + + #ifdef _WIN32 + /// True if this fd refers to a Windows console device. Mintty and other +@@ -519,10 +478,6 @@ public: + /// to the offset specified from the beginning of the file. + uint64_t seek(uint64_t off); + +- bool is_displayed() const override; +- +- bool has_colors() const override; +- + std::error_code error() const { return EC; } + + /// Return the value of the flag in this raw_fd_ostream indicating whether an +@@ -541,38 +496,6 @@ public: + /// - from The Zen of Python, by Tim Peters + /// + void clear_error() { EC = std::error_code(); } +- +- /// Locks the underlying file. +- /// +- /// @returns RAII object that releases the lock upon leaving the scope, if the +- /// locking was successful. Otherwise returns corresponding +- /// error code. +- /// +- /// The function blocks the current thread until the lock become available or +- /// error occurs. +- /// +- /// Possible use of this function may be as follows: +- /// +- /// @code{.cpp} +- /// if (auto L = stream.lock()) { +- /// // ... do action that require file to be locked. +- /// } else { +- /// handleAllErrors(std::move(L.takeError()), [&](ErrorInfoBase &EIB) { +- /// // ... handle lock error. +- /// }); +- /// } +- /// @endcode +- LLVM_NODISCARD Expected lock(); +- +- /// Tries to lock the underlying file within the specified period. +- /// +- /// @returns RAII object that releases the lock upon leaving the scope, if the +- /// locking was successful. Otherwise returns corresponding +- /// error code. +- /// +- /// It is used as @ref lock. +- LLVM_NODISCARD +- Expected tryLockFor(std::chrono::milliseconds Timeout); + }; + + /// This returns a reference to a raw_fd_ostream for standard output. Use it +@@ -602,17 +525,6 @@ public: + /// immediately destroyed. + raw_fd_stream(std::string_view Filename, std::error_code &EC); + +- /// This reads the \p Size bytes into a buffer pointed by \p Ptr. +- /// +- /// \param Ptr The start of the buffer to hold data to be read. +- /// +- /// \param Size The number of bytes to be read. +- /// +- /// On success, the number of bytes read is returned, and the file position is +- /// advanced by this number. On error, -1 is returned, use error() to get the +- /// error code. +- ssize_t read(char *Ptr, size_t Size); +- + /// Check if \p OS is a pointer of type raw_fd_stream*. + static bool classof(const raw_ostream *OS); + }; +@@ -726,17 +638,6 @@ public: + ~buffer_unique_ostream() override { *OS << str(); } + }; + +-class Error; +- +-/// This helper creates an output stream and then passes it to \p Write. +-/// The stream created is based on the specified \p OutputFileName: +-/// llvm::outs for "-", raw_null_ostream for "/dev/null", and raw_fd_ostream +-/// for other names. For raw_fd_ostream instances, the stream writes to +-/// a temporary file. The final output file is atomically replaced with the +-/// temporary file after the \p Write function is finished. +-Error writeToOutput(std::string_view OutputFileName, +- std::function Write); +- + } // end namespace llvm + + #endif // LLVM_SUPPORT_RAW_OSTREAM_H +diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp +index bff7b6c9a77b..611043dc5925 100644 +--- a/llvm/lib/Support/raw_ostream.cpp ++++ b/llvm/lib/Support/raw_ostream.cpp +@@ -121,49 +121,6 @@ void raw_ostream::SetBufferAndMode(char *BufferStart, size_t Size, + assert(OutBufStart <= OutBufEnd && "Invalid size!"); + } + +-raw_ostream &raw_ostream::operator<<(unsigned long N) { +- write_integer(*this, static_cast(N), 0, IntegerStyle::Integer); +- return *this; +-} +- +-raw_ostream &raw_ostream::operator<<(long N) { +- write_integer(*this, static_cast(N), 0, IntegerStyle::Integer); +- return *this; +-} +- +-raw_ostream &raw_ostream::operator<<(unsigned long long N) { +- write_integer(*this, static_cast(N), 0, IntegerStyle::Integer); +- return *this; +-} +- +-raw_ostream &raw_ostream::operator<<(long long N) { +- write_integer(*this, static_cast(N), 0, IntegerStyle::Integer); +- return *this; +-} +- +-raw_ostream &raw_ostream::write_hex(unsigned long long N) { +- llvm::write_hex(*this, N, HexPrintStyle::Lower); +- return *this; +-} +- +-raw_ostream &raw_ostream::operator<<(Colors C) { +- if (C == Colors::RESET) +- resetColor(); +- else +- changeColor(C); +- return *this; +-} +- +-raw_ostream &raw_ostream::write_uuid(const uuid_t UUID) { +- for (int Idx = 0; Idx < 16; ++Idx) { +- *this << format("%02" PRIX32, UUID[Idx]); +- if (Idx == 3 || Idx == 5 || Idx == 7 || Idx == 9) +- *this << "-"; +- } +- return *this; +-} +- +- + raw_ostream &raw_ostream::write_escaped(std::string_view Str, + bool UseHexEscapes) { + for (unsigned char c : Str) { +@@ -309,172 +266,6 @@ void raw_ostream::flush_tied_then_write(const char *Ptr, size_t Size) { + write_impl(Ptr, Size); + } + +-// Formatted output. +-raw_ostream &raw_ostream::operator<<(const format_object_base &Fmt) { +- // If we have more than a few bytes left in our output buffer, try +- // formatting directly onto its end. +- size_t NextBufferSize = 127; +- size_t BufferBytesLeft = OutBufEnd - OutBufCur; +- if (BufferBytesLeft > 3) { +- size_t BytesUsed = Fmt.print(OutBufCur, BufferBytesLeft); +- +- // Common case is that we have plenty of space. +- if (BytesUsed <= BufferBytesLeft) { +- OutBufCur += BytesUsed; +- return *this; +- } +- +- // Otherwise, we overflowed and the return value tells us the size to try +- // again with. +- NextBufferSize = BytesUsed; +- } +- +- // If we got here, we didn't have enough space in the output buffer for the +- // string. Try printing into a SmallVector that is resized to have enough +- // space. Iterate until we win. +- SmallVector V; +- +- while (true) { +- V.resize(NextBufferSize); +- +- // Try formatting into the SmallVector. +- size_t BytesUsed = Fmt.print(V.data(), NextBufferSize); +- +- // If BytesUsed fit into the vector, we win. +- if (BytesUsed <= NextBufferSize) +- return write(V.data(), BytesUsed); +- +- // Otherwise, try again with a new size. +- assert(BytesUsed > NextBufferSize && "Didn't grow buffer!?"); +- NextBufferSize = BytesUsed; +- } +-} +- +-raw_ostream &raw_ostream::operator<<(const formatv_object_base &Obj) { +- Obj.format(*this); +- return *this; +-} +- +-raw_ostream &raw_ostream::operator<<(const FormattedString &FS) { +- unsigned LeftIndent = 0; +- unsigned RightIndent = 0; +- const ssize_t Difference = FS.Width - FS.Str.size(); +- if (Difference > 0) { +- switch (FS.Justify) { +- case FormattedString::JustifyNone: +- break; +- case FormattedString::JustifyLeft: +- RightIndent = Difference; +- break; +- case FormattedString::JustifyRight: +- LeftIndent = Difference; +- break; +- case FormattedString::JustifyCenter: +- LeftIndent = Difference / 2; +- RightIndent = Difference - LeftIndent; +- break; +- } +- } +- indent(LeftIndent); +- (*this) << FS.Str; +- indent(RightIndent); +- return *this; +-} +- +-raw_ostream &raw_ostream::operator<<(const FormattedNumber &FN) { +- if (FN.Hex) { +- HexPrintStyle Style; +- if (FN.Upper && FN.HexPrefix) +- Style = HexPrintStyle::PrefixUpper; +- else if (FN.Upper && !FN.HexPrefix) +- Style = HexPrintStyle::Upper; +- else if (!FN.Upper && FN.HexPrefix) +- Style = HexPrintStyle::PrefixLower; +- else +- Style = HexPrintStyle::Lower; +- llvm::write_hex(*this, FN.HexValue, Style, FN.Width); +- } else { +- llvm::SmallString<16> Buffer; +- llvm::raw_svector_ostream Stream(Buffer); +- llvm::write_integer(Stream, FN.DecValue, 0, IntegerStyle::Integer); +- if (Buffer.size() < FN.Width) +- indent(FN.Width - Buffer.size()); +- (*this) << Buffer; +- } +- return *this; +-} +- +-raw_ostream &raw_ostream::operator<<(const FormattedBytes &FB) { +- if (FB.Bytes.empty()) +- return *this; +- +- size_t LineIndex = 0; +- auto Bytes = FB.Bytes; +- const size_t Size = Bytes.size(); +- HexPrintStyle HPS = FB.Upper ? HexPrintStyle::Upper : HexPrintStyle::Lower; +- uint64_t OffsetWidth = 0; +- if (FB.FirstByteOffset.hasValue()) { +- // Figure out how many nibbles are needed to print the largest offset +- // represented by this data set, so that we can align the offset field +- // to the right width. +- size_t Lines = Size / FB.NumPerLine; +- uint64_t MaxOffset = *FB.FirstByteOffset + Lines * FB.NumPerLine; +- unsigned Power = 0; +- if (MaxOffset > 0) +- Power = llvm::Log2_64_Ceil(MaxOffset); +- OffsetWidth = std::max(4, llvm::alignTo(Power, 4) / 4); +- } +- +- // The width of a block of data including all spaces for group separators. +- unsigned NumByteGroups = +- alignTo(FB.NumPerLine, FB.ByteGroupSize) / FB.ByteGroupSize; +- unsigned BlockCharWidth = FB.NumPerLine * 2 + NumByteGroups - 1; +- +- while (!Bytes.empty()) { +- indent(FB.IndentLevel); +- +- if (FB.FirstByteOffset.hasValue()) { +- uint64_t Offset = FB.FirstByteOffset.getValue(); +- llvm::write_hex(*this, Offset + LineIndex, HPS, OffsetWidth); +- *this << ": "; +- } +- +- auto Line = Bytes.take_front(FB.NumPerLine); +- +- size_t CharsPrinted = 0; +- // Print the hex bytes for this line in groups +- for (size_t I = 0; I < Line.size(); ++I, CharsPrinted += 2) { +- if (I && (I % FB.ByteGroupSize) == 0) { +- ++CharsPrinted; +- *this << " "; +- } +- llvm::write_hex(*this, Line[I], HPS, 2); +- } +- +- if (FB.ASCII) { +- // Print any spaces needed for any bytes that we didn't print on this +- // line so that the ASCII bytes are correctly aligned. +- assert(BlockCharWidth >= CharsPrinted); +- indent(BlockCharWidth - CharsPrinted + 2); +- *this << "|"; +- +- // Print the ASCII char values for each byte on this line +- for (uint8_t Byte : Line) { +- if (isPrint(Byte)) +- *this << static_cast(Byte); +- else +- *this << '.'; +- } +- *this << '|'; +- } +- +- Bytes = Bytes.drop_front(Line.size()); +- LineIndex += Line.size(); +- if (LineIndex < Size) +- *this << '\n'; +- } +- return *this; +-} + + template + static raw_ostream &write_padding(raw_ostream &OS, unsigned NumChars) { +@@ -507,63 +298,8 @@ raw_ostream &raw_ostream::write_zeros(unsigned NumZeros) { + return write_padding<'\0'>(*this, NumZeros); + } + +-bool raw_ostream::prepare_colors() { +- // Colors were explicitly disabled. +- if (!ColorEnabled) +- return false; +- +- // Colors require changing the terminal but this stream is not going to a +- // terminal. +- if (sys::Process::ColorNeedsFlush() && !is_displayed()) +- return false; +- +- if (sys::Process::ColorNeedsFlush()) +- flush(); +- +- return true; +-} +- +-raw_ostream &raw_ostream::changeColor(enum Colors colors, bool bold, bool bg) { +- if (!prepare_colors()) +- return *this; +- +- const char *colorcode = +- (colors == SAVEDCOLOR) +- ? sys::Process::OutputBold(bg) +- : sys::Process::OutputColor(static_cast(colors), bold, bg); +- if (colorcode) +- write(colorcode, strlen(colorcode)); +- return *this; +-} +- +-raw_ostream &raw_ostream::resetColor() { +- if (!prepare_colors()) +- return *this; +- +- if (const char *colorcode = sys::Process::ResetColor()) +- write(colorcode, strlen(colorcode)); +- return *this; +-} +- +-raw_ostream &raw_ostream::reverseColor() { +- if (!prepare_colors()) +- return *this; +- +- if (const char *colorcode = sys::Process::OutputReverse()) +- write(colorcode, strlen(colorcode)); +- return *this; +-} +- + void raw_ostream::anchor() {} + +-//===----------------------------------------------------------------------===// +-// Formatted Output +-//===----------------------------------------------------------------------===// +- +-// Out of line virtual method. +-void format_object_base::home() { +-} +- + //===----------------------------------------------------------------------===// + // raw_fd_ostream + //===----------------------------------------------------------------------===// +@@ -854,31 +590,6 @@ size_t raw_fd_ostream::preferred_buffer_size() const { + #endif + } + +-bool raw_fd_ostream::is_displayed() const { +- return sys::Process::FileDescriptorIsDisplayed(FD); +-} +- +-bool raw_fd_ostream::has_colors() const { +- if (!HasColors) +- HasColors = sys::Process::FileDescriptorHasColors(FD); +- return *HasColors; +-} +- +-Expected raw_fd_ostream::lock() { +- std::error_code EC = sys::fs::lockFile(FD); +- if (!EC) +- return sys::fs::FileLocker(FD); +- return errorCodeToError(EC); +-} +- +-Expected +-raw_fd_ostream::tryLockFor(std::chrono::milliseconds Timeout) { +- std::error_code EC = sys::fs::tryLockFile(FD, Timeout); +- if (!EC) +- return sys::fs::FileLocker(FD); +- return errorCodeToError(EC); +-} +- + void raw_fd_ostream::anchor() {} + + //===----------------------------------------------------------------------===// +@@ -922,16 +633,6 @@ raw_fd_stream::raw_fd_stream(std::string_view Filename, std::error_code &EC) + EC = std::make_error_code(std::errc::invalid_argument); + } + +-ssize_t raw_fd_stream::read(char *Ptr, size_t Size) { +- assert(get_fd() >= 0 && "File already closed."); +- ssize_t Ret = ::read(get_fd(), (void *)Ptr, Size); +- if (Ret >= 0) +- inc_pos(Ret); +- else +- error_detected(std::error_code(errno, std::generic_category())); +- return Ret; +-} +- + bool raw_fd_stream::classof(const raw_ostream *OS) { + return OS->get_kind() == OStreamKind::OK_FDStream; + } +@@ -991,31 +692,3 @@ void raw_pwrite_stream::anchor() {} + void buffer_ostream::anchor() {} + + void buffer_unique_ostream::anchor() {} +- +-Error llvm::writeToOutput(std::string_view OutputFileName, +- std::function Write) { +- if (OutputFileName == "-") +- return Write(outs()); +- +- if (OutputFileName == "/dev/null") { +- raw_null_ostream Out; +- return Write(Out); +- } +- +- unsigned Mode = sys::fs::all_read | sys::fs::all_write | sys::fs::all_exe; +- Expected Temp = +- sys::fs::TempFile::create(OutputFileName + ".temp-stream-%%%%%%", Mode); +- if (!Temp) +- return createFileError(OutputFileName, Temp.takeError()); +- +- raw_fd_ostream Out(Temp->FD, false); +- +- if (Error E = Write(Out)) { +- if (Error DiscardError = Temp->discard()) +- return joinErrors(std::move(E), std::move(DiscardError)); +- return E; +- } +- Out.flush(); +- +- return Temp->keep(OutputFileName); +-} +-- +2.20.1.windows.1 + diff --git a/upstream_utils/llvm_patches/0015-Detemplatize-small-vector-base.patch b/upstream_utils/llvm_patches/0015-Detemplatize-small-vector-base.patch new file mode 100644 index 0000000000..1affda8a3d --- /dev/null +++ b/upstream_utils/llvm_patches/0015-Detemplatize-small-vector-base.patch @@ -0,0 +1,143 @@ +From b887e8f6e34e2ff2a3ad69f67f84f49ceb1f455e Mon Sep 17 00:00:00 2001 +From: PJ Reiniger +Date: Thu, 5 May 2022 23:18:34 -0400 +Subject: [PATCH 15/31] Detemplatize small vector base + +--- + llvm/include/llvm/ADT/SmallVector.h | 21 +++++++----------- + llvm/lib/Support/SmallVector.cpp | 34 +++++------------------------ + 2 files changed, 13 insertions(+), 42 deletions(-) + +diff --git a/llvm/include/llvm/ADT/SmallVector.h b/llvm/include/llvm/ADT/SmallVector.h +index 690d512a43c8..695015ae3248 100644 +--- a/llvm/include/llvm/ADT/SmallVector.h ++++ b/llvm/include/llvm/ADT/SmallVector.h +@@ -50,14 +50,14 @@ namespace llvm { + /// Using 64 bit size is desirable for cases like SmallVector, where a + /// 32 bit size would limit the vector to ~4GB. SmallVectors are used for + /// buffering bitcode output - which can exceed 4GB. +-template class SmallVectorBase { ++class SmallVectorBase { + protected: + void *BeginX; +- Size_T Size = 0, Capacity; ++ unsigned Size = 0, Capacity; + + /// The maximum value of the Size_T used. + static constexpr size_t SizeTypeMax() { +- return (std::numeric_limits::max)(); ++ return (std::numeric_limits::max)(); + } + + SmallVectorBase() = delete; +@@ -95,15 +95,10 @@ public: + } + }; + +-template +-using SmallVectorSizeType = +- typename std::conditional= 8, uint64_t, +- uint32_t>::type; +- + /// Figure out the offset of the first element. + template struct SmallVectorAlignmentAndSize { +- alignas(SmallVectorBase>) char Base[sizeof( +- SmallVectorBase>)]; ++ alignas(SmallVectorBase) char Base[sizeof( ++ SmallVectorBase)]; + alignas(T) char FirstEl[sizeof(T)]; + }; + +@@ -112,8 +107,8 @@ template struct SmallVectorAlignmentAndSize { + /// to avoid unnecessarily requiring T to be complete. + template + class SmallVectorTemplateCommon +- : public SmallVectorBase> { +- using Base = SmallVectorBase>; ++ : public SmallVectorBase { ++ using Base = SmallVectorBase; + + /// Find the address of the first element. For this pointer math to be valid + /// with small-size of 0 for T with lots of alignment, it's important that +@@ -360,7 +355,7 @@ protected: + /// in \p NewCapacity. This is the first section of \a grow(). + T *mallocForGrow(size_t MinSize, size_t &NewCapacity) { + return static_cast( +- SmallVectorBase>::mallocForGrow( ++ SmallVectorBase::mallocForGrow( + MinSize, sizeof(T), NewCapacity)); + } + +diff --git a/llvm/lib/Support/SmallVector.cpp b/llvm/lib/Support/SmallVector.cpp +index 26901fe97d20..3fc16097bd30 100644 +--- a/llvm/lib/Support/SmallVector.cpp ++++ b/llvm/lib/Support/SmallVector.cpp +@@ -41,10 +41,6 @@ static_assert(sizeof(SmallVector) == + sizeof(unsigned) * 2 + sizeof(void *) * 2, + "wasted space in SmallVector size 1"); + +-static_assert(sizeof(SmallVector) == +- sizeof(void *) * 2 + sizeof(void *), +- "1 byte elements have word-sized type for size and capacity"); +- + /// Report that MinSize doesn't fit into this vector's size type. Throws + /// std::length_error or calls report_fatal_error. + LLVM_ATTRIBUTE_NORETURN +@@ -76,9 +72,8 @@ static void report_at_maximum_capacity(size_t MaxSize) { + } + + // Note: Moving this function into the header may cause performance regression. +-template + static size_t getNewCapacity(size_t MinSize, size_t TSize, size_t OldCapacity) { +- constexpr size_t MaxSize = std::numeric_limits::max(); ++ constexpr size_t MaxSize = std::numeric_limits::max(); + + // Ensure we can fit the new capacity. + // This is only going to be applicable when the capacity is 32 bit. +@@ -99,18 +94,16 @@ static size_t getNewCapacity(size_t MinSize, size_t TSize, size_t OldCapacity) { + } + + // Note: Moving this function into the header may cause performance regression. +-template +-void *SmallVectorBase::mallocForGrow(size_t MinSize, size_t TSize, ++void *SmallVectorBase::mallocForGrow(size_t MinSize, size_t TSize, + size_t &NewCapacity) { +- NewCapacity = getNewCapacity(MinSize, TSize, this->capacity()); ++ NewCapacity = getNewCapacity(MinSize, TSize, this->capacity()); + return llvm::safe_malloc(NewCapacity * TSize); + } + + // Note: Moving this function into the header may cause performance regression. +-template +-void SmallVectorBase::grow_pod(void *FirstEl, size_t MinSize, ++void SmallVectorBase::grow_pod(void *FirstEl, size_t MinSize, + size_t TSize) { +- size_t NewCapacity = getNewCapacity(MinSize, TSize, this->capacity()); ++ size_t NewCapacity = getNewCapacity(MinSize, TSize, this->capacity()); + void *NewElts; + if (BeginX == FirstEl) { + NewElts = safe_malloc(NewCapacity * TSize); +@@ -125,20 +118,3 @@ void SmallVectorBase::grow_pod(void *FirstEl, size_t MinSize, + this->BeginX = NewElts; + this->Capacity = NewCapacity; + } +- +-template class llvm::SmallVectorBase; +- +-// Disable the uint64_t instantiation for 32-bit builds. +-// Both uint32_t and uint64_t instantiations are needed for 64-bit builds. +-// This instantiation will never be used in 32-bit builds, and will cause +-// warnings when sizeof(Size_T) > sizeof(size_t). +-#if SIZE_MAX > UINT32_MAX +-template class llvm::SmallVectorBase; +- +-// Assertions to ensure this #if stays in sync with SmallVectorSizeType. +-static_assert(sizeof(SmallVectorSizeType) == sizeof(uint64_t), +- "Expected SmallVectorBase variant to be in use."); +-#else +-static_assert(sizeof(SmallVectorSizeType) == sizeof(uint32_t), +- "Expected SmallVectorBase variant to be in use."); +-#endif +-- +2.20.1.windows.1 + diff --git a/upstream_utils/llvm_patches/0016-Add-vectors-to-raw_ostream.patch b/upstream_utils/llvm_patches/0016-Add-vectors-to-raw_ostream.patch new file mode 100644 index 0000000000..2bd8a1bb09 --- /dev/null +++ b/upstream_utils/llvm_patches/0016-Add-vectors-to-raw_ostream.patch @@ -0,0 +1,217 @@ +From eada33299195b46a8942e198ae8ae41185cd4f45 Mon Sep 17 00:00:00 2001 +From: PJ Reiniger +Date: Sun, 8 May 2022 13:48:59 -0400 +Subject: [PATCH 16/31] Add vectors to raw_ostream + +--- + llvm/include/llvm/Support/raw_ostream.h | 115 ++++++++++++++++++++++++ + llvm/lib/Support/raw_ostream.cpp | 47 ++++++++++ + 2 files changed, 162 insertions(+) + +diff --git a/llvm/include/llvm/Support/raw_ostream.h b/llvm/include/llvm/Support/raw_ostream.h +index 50443e018aaf..8008170be3e5 100644 +--- a/llvm/include/llvm/Support/raw_ostream.h ++++ b/llvm/include/llvm/Support/raw_ostream.h +@@ -26,6 +26,7 @@ + #endif + #include + #include ++#include + + namespace llvm { + +@@ -249,12 +250,24 @@ public: + return write(Str.data(), Str.size()); + } + ++ raw_ostream &operator<<(const std::vector &Arr) { ++ // Avoid the fast path, it would only increase code size for a marginal win. ++ return write(Arr.data(), Arr.size()); ++ } ++ ++ raw_ostream &operator<<(const SmallVectorImpl &Arr) { ++ return write(Arr.data(), Arr.size()); ++ } ++ + /// Output \p Str, turning '\\', '\t', '\n', '"', and anything that doesn't + /// satisfy llvm::isPrint into an escape sequence. + raw_ostream &write_escaped(std::string_view Str, bool UseHexEscapes = false); + + raw_ostream &write(unsigned char C); + raw_ostream &write(const char *Ptr, size_t Size); ++ raw_ostream &write(const uint8_t *Ptr, size_t Size) { ++ return write(reinterpret_cast(Ptr), Size); ++ } + + /// indent - Insert 'NumSpaces' spaces. + raw_ostream &indent(unsigned NumSpaces); +@@ -600,6 +613,108 @@ public: + } + }; + ++/// A raw_ostream that writes to a vector. This is a ++/// simple adaptor class. This class does not encounter output errors. ++/// raw_vector_ostream operates without a buffer, delegating all memory ++/// management to the vector. Thus the vector is always up-to-date, ++/// may be used directly and there is no need to call flush(). ++class raw_vector_ostream : public raw_pwrite_stream { ++ std::vector &OS; ++ ++ /// See raw_ostream::write_impl. ++ void write_impl(const char *Ptr, size_t Size) override; ++ ++ void pwrite_impl(const char *Ptr, size_t Size, uint64_t Offset) override; ++ ++ /// Return the current position within the stream. ++ uint64_t current_pos() const override; ++ ++public: ++ /// Construct a new raw_svector_ostream. ++ /// ++ /// \param O The vector to write to; this should generally have at least 128 ++ /// bytes free to avoid any extraneous memory overhead. ++ explicit raw_vector_ostream(std::vector &O) : OS(O) { ++ SetUnbuffered(); ++ } ++ ++ ~raw_vector_ostream() override = default; ++ ++ void flush() = delete; ++ ++ /// Return a std::string_view for the vector contents. ++ std::string_view str() { return std::string_view(OS.data(), OS.size()); } ++}; ++ ++/// A raw_ostream that writes to an SmallVector or SmallString. This is a ++/// simple adaptor class. This class does not encounter output errors. ++/// raw_svector_ostream operates without a buffer, delegating all memory ++/// management to the SmallString. Thus the SmallString is always up-to-date, ++/// may be used directly and there is no need to call flush(). ++class raw_usvector_ostream : public raw_pwrite_stream { ++ SmallVectorImpl &OS; ++ ++ /// See raw_ostream::write_impl. ++ void write_impl(const char *Ptr, size_t Size) override; ++ ++ void pwrite_impl(const char *Ptr, size_t Size, uint64_t Offset) override; ++ ++ /// Return the current position within the stream. ++ uint64_t current_pos() const override; ++ ++public: ++ /// Construct a new raw_svector_ostream. ++ /// ++ /// \param O The vector to write to; this should generally have at least 128 ++ /// bytes free to avoid any extraneous memory overhead. ++ explicit raw_usvector_ostream(SmallVectorImpl &O) : OS(O) { ++ SetUnbuffered(); ++ } ++ ++ ~raw_usvector_ostream() override = default; ++ ++ void flush() = delete; ++ ++ /// Return an span for the vector contents. ++ span array() { return {OS.data(), OS.size()}; } ++ span array() const { return {OS.data(), OS.size()}; } ++}; ++ ++/// A raw_ostream that writes to a vector. This is a ++/// simple adaptor class. This class does not encounter output errors. ++/// raw_vector_ostream operates without a buffer, delegating all memory ++/// management to the vector. Thus the vector is always up-to-date, ++/// may be used directly and there is no need to call flush(). ++class raw_uvector_ostream : public raw_pwrite_stream { ++ std::vector &OS; ++ ++ /// See raw_ostream::write_impl. ++ void write_impl(const char *Ptr, size_t Size) override; ++ ++ void pwrite_impl(const char *Ptr, size_t Size, uint64_t Offset) override; ++ ++ /// Return the current position within the stream. ++ uint64_t current_pos() const override; ++ ++public: ++ /// Construct a new raw_svector_ostream. ++ /// ++ /// \param O The vector to write to; this should generally have at least 128 ++ /// bytes free to avoid any extraneous memory overhead. ++ explicit raw_uvector_ostream(std::vector &O) : OS(O) { ++ SetUnbuffered(); ++ } ++ ++ ~raw_uvector_ostream() override = default; ++ ++ void flush() = delete; ++ ++ /// Return a span for the vector contents. ++ span array() { return {OS.data(), OS.size()}; } ++ span array() const { return {OS.data(), OS.size()}; } ++}; ++ ++ + /// A raw_ostream that discards all output. + class raw_null_ostream : public raw_pwrite_stream { + /// See raw_ostream::write_impl. +diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp +index 611043dc5925..3abeed3dac21 100644 +--- a/llvm/lib/Support/raw_ostream.cpp ++++ b/llvm/lib/Support/raw_ostream.cpp +@@ -664,6 +664,53 @@ void raw_svector_ostream::pwrite_impl(const char *Ptr, size_t Size, + memcpy(OS.data() + Offset, Ptr, Size); + } + ++//===----------------------------------------------------------------------===// ++// raw_vector_ostream ++//===----------------------------------------------------------------------===// ++ ++uint64_t raw_vector_ostream::current_pos() const { return OS.size(); } ++ ++void raw_vector_ostream::write_impl(const char *Ptr, size_t Size) { ++ OS.insert(OS.end(), Ptr, Ptr + Size); ++} ++ ++void raw_vector_ostream::pwrite_impl(const char *Ptr, size_t Size, ++ uint64_t Offset) { ++ memcpy(OS.data() + Offset, Ptr, Size); ++} ++ ++//===----------------------------------------------------------------------===// ++// raw_usvector_ostream ++//===----------------------------------------------------------------------===// ++ ++uint64_t raw_usvector_ostream::current_pos() const { return OS.size(); } ++ ++void raw_usvector_ostream::write_impl(const char *Ptr, size_t Size) { ++ OS.append(reinterpret_cast(Ptr), ++ reinterpret_cast(Ptr) + Size); ++} ++ ++void raw_usvector_ostream::pwrite_impl(const char *Ptr, size_t Size, ++ uint64_t Offset) { ++ memcpy(OS.data() + Offset, Ptr, Size); ++} ++ ++//===----------------------------------------------------------------------===// ++// raw_uvector_ostream ++//===----------------------------------------------------------------------===// ++ ++uint64_t raw_uvector_ostream::current_pos() const { return OS.size(); } ++ ++void raw_uvector_ostream::write_impl(const char *Ptr, size_t Size) { ++ OS.insert(OS.end(), reinterpret_cast(Ptr), ++ reinterpret_cast(Ptr) + Size); ++} ++ ++void raw_uvector_ostream::pwrite_impl(const char *Ptr, size_t Size, ++ uint64_t Offset) { ++ memcpy(OS.data() + Offset, Ptr, Size); ++} ++ + //===----------------------------------------------------------------------===// + // raw_null_ostream + //===----------------------------------------------------------------------===// +-- +2.20.1.windows.1 + diff --git a/upstream_utils/llvm_patches/0017-Extra-collections-features.patch b/upstream_utils/llvm_patches/0017-Extra-collections-features.patch new file mode 100644 index 0000000000..dbc12dca58 --- /dev/null +++ b/upstream_utils/llvm_patches/0017-Extra-collections-features.patch @@ -0,0 +1,165 @@ +From fc8a16f804f5d37e179f8eac521f8430e663d1a0 Mon Sep 17 00:00:00 2001 +From: PJ Reiniger +Date: Tue, 3 May 2022 22:16:10 -0400 +Subject: [PATCH 17/31] Extra collections features + +--- + llvm/include/llvm/ADT/StringMap.h | 103 +++++++++++++++++++++++++++++- + llvm/lib/Support/raw_ostream.cpp | 8 +++ + 2 files changed, 110 insertions(+), 1 deletion(-) + +diff --git a/llvm/include/llvm/ADT/StringMap.h b/llvm/include/llvm/ADT/StringMap.h +index 3b40bba37f58..af4be84e469d 100644 +--- a/llvm/include/llvm/ADT/StringMap.h ++++ b/llvm/include/llvm/ADT/StringMap.h +@@ -40,7 +40,7 @@ protected: + + protected: + explicit StringMapImpl(unsigned itemSize) : ItemSize(itemSize) {} +- StringMapImpl(StringMapImpl &&RHS) ++ StringMapImpl(StringMapImpl &&RHS) noexcept + : TheTable(RHS.TheTable), NumBuckets(RHS.NumBuckets), + NumItems(RHS.NumItems), NumTombstones(RHS.NumTombstones), + ItemSize(RHS.ItemSize) { +@@ -390,11 +390,27 @@ public: + return Tmp; + } + ++ DerivedTy &operator--() { // Predecrement ++ --Ptr; ++ ReversePastEmptyBuckets(); ++ return static_cast(*this); ++ } ++ ++ DerivedTy operator--(int) { // Post-decrement ++ DerivedTy Tmp(Ptr); ++ --*this; ++ return Tmp; ++ } ++ + private: + void AdvancePastEmptyBuckets() { + while (*Ptr == nullptr || *Ptr == StringMapImpl::getTombstoneVal()) + ++Ptr; + } ++ void ReversePastEmptyBuckets() { ++ while (*Ptr == nullptr || *Ptr == StringMapImpl::getTombstoneVal()) ++ --Ptr; ++ } + }; + + template +@@ -459,6 +475,91 @@ private: + std::string_view Key; + }; + ++template ++bool operator==(const StringMap& lhs, const StringMap& rhs) { ++ // same instance? ++ if (&lhs == &rhs) return true; ++ ++ // first check that sizes are identical ++ if (lhs.size() != rhs.size()) return false; ++ ++ // copy into vectors and sort by key ++ SmallVector, 16> lhs_items; ++ lhs_items.reserve(lhs.size()); ++ for (auto i = lhs.begin(), end = lhs.end(); i != end; ++i) ++ lhs_items.push_back(i); ++ std::sort(lhs_items.begin(), lhs_items.end(), ++ [](const StringMapConstIterator& a, ++ const StringMapConstIterator& b) { ++ return a->getKey() < b->getKey(); ++ }); ++ ++ SmallVector, 16> rhs_items; ++ rhs_items.reserve(rhs.size()); ++ for (auto i = rhs.begin(), end = rhs.end(); i != end; ++i) ++ rhs_items.push_back(i); ++ std::sort(rhs_items.begin(), rhs_items.end(), ++ [](const StringMapConstIterator& a, ++ const StringMapConstIterator& b) { ++ return a->getKey() < b->getKey(); ++ }); ++ ++ // compare vector keys and values ++ for (auto a = lhs_items.begin(), b = rhs_items.begin(), ++ aend = lhs_items.end(), bend = rhs_items.end(); ++ a != aend && b != bend; ++a, ++b) { ++ if ((*a)->first() != (*b)->first() || (*a)->second != (*b)->second) ++ return false; ++ } ++ return true; ++} ++ ++template ++inline bool operator!=(const StringMap& lhs, ++ const StringMap& rhs) { ++ return !(lhs == rhs); ++} ++ ++template ++bool operator<(const StringMap& lhs, const StringMap& rhs) { ++ // same instance? ++ if (&lhs == &rhs) return false; ++ ++ // copy into vectors and sort by key ++ SmallVector lhs_keys; ++ lhs_keys.reserve(lhs.size()); ++ for (auto i = lhs.begin(), end = lhs.end(); i != end; ++i) ++ lhs_keys.push_back(i->getKey()); ++ std::sort(lhs_keys.begin(), lhs_keys.end()); ++ ++ SmallVector rhs_keys; ++ rhs_keys.reserve(rhs.size()); ++ for (auto i = rhs.begin(), end = rhs.end(); i != end; ++i) ++ rhs_keys.push_back(i->getKey()); ++ std::sort(rhs_keys.begin(), rhs_keys.end()); ++ ++ // use std::vector comparison ++ return lhs_keys < rhs_keys; ++} ++ ++template ++inline bool operator<=(const StringMap& lhs, ++ const StringMap& rhs) { ++ return !(rhs < lhs); ++} ++ ++template ++inline bool operator>(const StringMap& lhs, ++ const StringMap& rhs) { ++ return !(lhs <= rhs); ++} ++ ++template ++inline bool operator>=(const StringMap& lhs, ++ const StringMap& rhs) { ++ return !(lhs < rhs); ++} ++ + } // end namespace llvm + + #endif // LLVM_ADT_STRINGMAP_H +diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp +index 3abeed3dac21..27bba0ca2f06 100644 +--- a/llvm/lib/Support/raw_ostream.cpp ++++ b/llvm/lib/Support/raw_ostream.cpp +@@ -78,6 +78,14 @@ constexpr raw_ostream::Colors raw_ostream::WHITE; + constexpr raw_ostream::Colors raw_ostream::SAVEDCOLOR; + constexpr raw_ostream::Colors raw_ostream::RESET; + ++namespace { ++// Find the length of an array. ++template ++constexpr inline size_t array_lengthof(T (&)[N]) { ++ return N; ++} ++} // namespace ++ + raw_ostream::~raw_ostream() { + // raw_ostream's subclasses should take care to flush the buffer + // in their destructors. +-- +2.20.1.windows.1 + diff --git a/upstream_utils/llvm_patches/0018-EpochTracker-abi-macro.patch b/upstream_utils/llvm_patches/0018-EpochTracker-abi-macro.patch new file mode 100644 index 0000000000..ebb339aa6d --- /dev/null +++ b/upstream_utils/llvm_patches/0018-EpochTracker-abi-macro.patch @@ -0,0 +1,25 @@ +From 0986c8a95cad971a1bf84259e1ab698497ca205c Mon Sep 17 00:00:00 2001 +From: PJ Reiniger +Date: Wed, 4 May 2022 00:01:00 -0400 +Subject: [PATCH 18/31] EpochTracker abi macro + +--- + llvm/include/llvm/ADT/EpochTracker.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/llvm/include/llvm/ADT/EpochTracker.h b/llvm/include/llvm/ADT/EpochTracker.h +index 7a2e4220afec..8f98f3d582c7 100644 +--- a/llvm/include/llvm/ADT/EpochTracker.h ++++ b/llvm/include/llvm/ADT/EpochTracker.h +@@ -21,7 +21,7 @@ + + namespace llvm { + +-#if LLVM_ENABLE_ABI_BREAKING_CHECKS ++#ifndef NDEBUG //ifndef LLVM_ENABLE_ABI_BREAKING_CHECKS + + /// A base class for data structure classes wishing to make iterators + /// ("handles") pointing into themselves fail-fast. When building without +-- +2.20.1.windows.1 + diff --git a/upstream_utils/llvm_patches/0019-Delete-numbers-from-mathextras.patch b/upstream_utils/llvm_patches/0019-Delete-numbers-from-mathextras.patch new file mode 100644 index 0000000000..69b7c75690 --- /dev/null +++ b/upstream_utils/llvm_patches/0019-Delete-numbers-from-mathextras.patch @@ -0,0 +1,59 @@ +From f2b2850258fe3aed54c64f8afac2565b00ddf4b0 Mon Sep 17 00:00:00 2001 +From: PJ Reiniger +Date: Thu, 5 May 2022 18:09:45 -0400 +Subject: [PATCH 19/31] Delete numbers from mathextras + +--- + llvm/include/llvm/Support/MathExtras.h | 36 -------------------------- + 1 file changed, 36 deletions(-) + +diff --git a/llvm/include/llvm/Support/MathExtras.h b/llvm/include/llvm/Support/MathExtras.h +index fac12dd0e4c6..e8f1f2aca610 100644 +--- a/llvm/include/llvm/Support/MathExtras.h ++++ b/llvm/include/llvm/Support/MathExtras.h +@@ -50,42 +50,6 @@ enum ZeroBehavior { + ZB_Width + }; + +-/// Mathematical constants. +-namespace numbers { +-// TODO: Track C++20 std::numbers. +-// TODO: Favor using the hexadecimal FP constants (requires C++17). +-constexpr double e = 2.7182818284590452354, // (0x1.5bf0a8b145749P+1) https://oeis.org/A001113 +- egamma = .57721566490153286061, // (0x1.2788cfc6fb619P-1) https://oeis.org/A001620 +- ln2 = .69314718055994530942, // (0x1.62e42fefa39efP-1) https://oeis.org/A002162 +- ln10 = 2.3025850929940456840, // (0x1.24bb1bbb55516P+1) https://oeis.org/A002392 +- log2e = 1.4426950408889634074, // (0x1.71547652b82feP+0) +- log10e = .43429448190325182765, // (0x1.bcb7b1526e50eP-2) +- pi = 3.1415926535897932385, // (0x1.921fb54442d18P+1) https://oeis.org/A000796 +- inv_pi = .31830988618379067154, // (0x1.45f306bc9c883P-2) https://oeis.org/A049541 +- sqrtpi = 1.7724538509055160273, // (0x1.c5bf891b4ef6bP+0) https://oeis.org/A002161 +- inv_sqrtpi = .56418958354775628695, // (0x1.20dd750429b6dP-1) https://oeis.org/A087197 +- sqrt2 = 1.4142135623730950488, // (0x1.6a09e667f3bcdP+0) https://oeis.org/A00219 +- inv_sqrt2 = .70710678118654752440, // (0x1.6a09e667f3bcdP-1) +- sqrt3 = 1.7320508075688772935, // (0x1.bb67ae8584caaP+0) https://oeis.org/A002194 +- inv_sqrt3 = .57735026918962576451, // (0x1.279a74590331cP-1) +- phi = 1.6180339887498948482; // (0x1.9e3779b97f4a8P+0) https://oeis.org/A001622 +-constexpr float ef = 2.71828183F, // (0x1.5bf0a8P+1) https://oeis.org/A001113 +- egammaf = .577215665F, // (0x1.2788d0P-1) https://oeis.org/A001620 +- ln2f = .693147181F, // (0x1.62e430P-1) https://oeis.org/A002162 +- ln10f = 2.30258509F, // (0x1.26bb1cP+1) https://oeis.org/A002392 +- log2ef = 1.44269504F, // (0x1.715476P+0) +- log10ef = .434294482F, // (0x1.bcb7b2P-2) +- pif = 3.14159265F, // (0x1.921fb6P+1) https://oeis.org/A000796 +- inv_pif = .318309886F, // (0x1.45f306P-2) https://oeis.org/A049541 +- sqrtpif = 1.77245385F, // (0x1.c5bf8aP+0) https://oeis.org/A002161 +- inv_sqrtpif = .564189584F, // (0x1.20dd76P-1) https://oeis.org/A087197 +- sqrt2f = 1.41421356F, // (0x1.6a09e6P+0) https://oeis.org/A002193 +- inv_sqrt2f = .707106781F, // (0x1.6a09e6P-1) +- sqrt3f = 1.73205081F, // (0x1.bb67aeP+0) https://oeis.org/A002194 +- inv_sqrt3f = .577350269F, // (0x1.279a74P-1) +- phif = 1.61803399F; // (0x1.9e377aP+0) https://oeis.org/A001622 +-} // namespace numbers +- + namespace detail { + template struct TrailingZerosCounter { + static unsigned count(T Val, ZeroBehavior) { +-- +2.20.1.windows.1 + diff --git a/upstream_utils/llvm_patches/0020-Add-lerp-and-sgn.patch b/upstream_utils/llvm_patches/0020-Add-lerp-and-sgn.patch new file mode 100644 index 0000000000..451e4844db --- /dev/null +++ b/upstream_utils/llvm_patches/0020-Add-lerp-and-sgn.patch @@ -0,0 +1,43 @@ +From 2a2ac87a1ac74a1c6e29c205b3fd979ab77722b4 Mon Sep 17 00:00:00 2001 +From: PJ Reiniger +Date: Tue, 3 May 2022 22:50:24 -0400 +Subject: [PATCH 20/31] Add lerp and sgn + +--- + llvm/include/llvm/Support/MathExtras.h | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/llvm/include/llvm/Support/MathExtras.h b/llvm/include/llvm/Support/MathExtras.h +index e8f1f2aca610..8116c58bd0d6 100644 +--- a/llvm/include/llvm/Support/MathExtras.h ++++ b/llvm/include/llvm/Support/MathExtras.h +@@ -930,6 +930,26 @@ std::enable_if_t::value, T> MulOverflow(T X, T Y, T &Result) { + return UX > (static_cast((std::numeric_limits::max)())) / UY; + } + ++// Typesafe implementation of the signum function. ++// Returns -1 if negative, 1 if positive, 0 if 0. ++template ++constexpr int sgn(T val) { ++ return (T(0) < val) - (val < T(0)); ++} ++ ++/** ++ * Linearly interpolates between two values. ++ * ++ * @param startValue The start value. ++ * @param endValue The end value. ++ * @param t The fraction for interpolation. ++ * ++ * @return The interpolated value. ++ */ ++template ++constexpr T Lerp(const T& startValue, const T& endValue, double t) { ++ return startValue + (endValue - startValue) * t; ++} + } // End llvm namespace + + #endif +-- +2.20.1.windows.1 + diff --git a/upstream_utils/llvm_patches/0021-Fixup-includes.patch b/upstream_utils/llvm_patches/0021-Fixup-includes.patch new file mode 100644 index 0000000000..dc4f8927c6 --- /dev/null +++ b/upstream_utils/llvm_patches/0021-Fixup-includes.patch @@ -0,0 +1,172 @@ +From b013523b36cba3c8fc0ced2ca013a8763573d5a0 Mon Sep 17 00:00:00 2001 +From: PJ Reiniger +Date: Sun, 8 May 2022 16:38:11 -0400 +Subject: [PATCH 21/31] Fixup includes + +--- + llvm/include/llvm/ADT/StringMap.h | 5 ++++- + llvm/include/llvm/ADT/StringMapEntry.h | 4 ++++ + llvm/include/llvm/Support/PointerLikeTypeTraits.h | 1 + + llvm/lib/Support/ConvertUTFWrapper.cpp | 1 + + llvm/lib/Support/ErrorHandling.cpp | 7 +++---- + llvm/lib/Support/raw_ostream.cpp | 12 ++++++------ + llvm/unittests/ADT/SmallPtrSetTest.cpp | 2 ++ + llvm/unittests/ADT/StringMapTest.cpp | 1 + + llvm/unittests/Support/ConvertUTFTest.cpp | 2 ++ + 9 files changed, 24 insertions(+), 11 deletions(-) + +diff --git a/llvm/include/llvm/ADT/StringMap.h b/llvm/include/llvm/ADT/StringMap.h +index af4be84e469d..c41057cb6792 100644 +--- a/llvm/include/llvm/ADT/StringMap.h ++++ b/llvm/include/llvm/ADT/StringMap.h +@@ -14,7 +14,10 @@ + #define LLVM_ADT_STRINGMAP_H + + #include "llvm/ADT/StringMapEntry.h" +-#include "llvm/Support/AllocatorBase.h" ++#include "llvm/Support/MemAlloc.h" ++#include "llvm/Support/SmallVector.h" ++#include "llvm/Support/iterator.h" ++#include "llvm/Support/iterator_range.h" + #include "llvm/Support/PointerLikeTypeTraits.h" + #include + #include +diff --git a/llvm/include/llvm/ADT/StringMapEntry.h b/llvm/include/llvm/ADT/StringMapEntry.h +index 66a30698d787..1201bb8e69d4 100644 +--- a/llvm/include/llvm/ADT/StringMapEntry.h ++++ b/llvm/include/llvm/ADT/StringMapEntry.h +@@ -15,6 +15,10 @@ + #ifndef LLVM_ADT_STRINGMAPENTRY_H + #define LLVM_ADT_STRINGMAPENTRY_H + ++#include "wpi/MemAlloc.h" ++ ++#include ++#include + #include + #include + +diff --git a/llvm/include/llvm/Support/PointerLikeTypeTraits.h b/llvm/include/llvm/Support/PointerLikeTypeTraits.h +index 1b15f930bd87..acadd5e89a16 100644 +--- a/llvm/include/llvm/Support/PointerLikeTypeTraits.h ++++ b/llvm/include/llvm/Support/PointerLikeTypeTraits.h +@@ -16,6 +16,7 @@ + + #include "llvm/Support/DataTypes.h" + #include ++#include + #include + + namespace llvm { +diff --git a/llvm/lib/Support/ConvertUTFWrapper.cpp b/llvm/lib/Support/ConvertUTFWrapper.cpp +index 090ebb0f5af8..87da616b75d4 100644 +--- a/llvm/lib/Support/ConvertUTFWrapper.cpp ++++ b/llvm/lib/Support/ConvertUTFWrapper.cpp +@@ -8,6 +8,7 @@ + + #include "llvm/ADT/span.h" + #include "llvm/Support/ConvertUTF.h" ++#include "llvm/Support/SmallVector.h" + #include "llvm/Support/ErrorHandling.h" + #include "llvm/Support/SwapByteOrder.h" + #include +diff --git a/llvm/lib/Support/ErrorHandling.cpp b/llvm/lib/Support/ErrorHandling.cpp +index 9c0c6fb868f2..4b6ddd30c6e4 100644 +--- a/llvm/lib/Support/ErrorHandling.cpp ++++ b/llvm/lib/Support/ErrorHandling.cpp +@@ -28,12 +28,11 @@ + #include + #include + +-#if defined(HAVE_UNISTD_H) +-# include ++#ifndef _WIN32 ++#include + #endif + #if defined(_MSC_VER) +-# include +-# include ++#include + #endif + + using namespace llvm; +diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp +index 27bba0ca2f06..6b9e5f9e44bd 100644 +--- a/llvm/lib/Support/raw_ostream.cpp ++++ b/llvm/lib/Support/raw_ostream.cpp +@@ -15,7 +15,8 @@ + #endif + + #include "llvm/Support/raw_ostream.h" +-#include "llvm/ADT/STLExtras.h" ++#include "wpi/SmallString.h" ++#include "wpi/SmallVector.h" + #include "llvm/ADT/StringExtras.h" + #include "llvm/Config/config.h" + #include "llvm/Support/Compiler.h" +@@ -35,12 +36,11 @@ + #include + + // may provide O_BINARY. +-#if defined(HAVE_FCNTL_H) + # include +-#endif + +-#if defined(HAVE_UNISTD_H) +-# include ++#ifndef _WIN32 ++#include ++#include + #endif + + #if defined(__CYGWIN__) +@@ -62,7 +62,7 @@ + + #ifdef _WIN32 + #include "llvm/Support/ConvertUTF.h" +-#include "llvm/Support/Windows/WindowsSupport.h" ++#include "Windows/WindowsSupport.h" + #endif + + using namespace llvm; +diff --git a/llvm/unittests/ADT/SmallPtrSetTest.cpp b/llvm/unittests/ADT/SmallPtrSetTest.cpp +index 531f81ab5b3f..3db8b6e37d31 100644 +--- a/llvm/unittests/ADT/SmallPtrSetTest.cpp ++++ b/llvm/unittests/ADT/SmallPtrSetTest.cpp +@@ -15,6 +15,8 @@ + #include "llvm/Support/PointerLikeTypeTraits.h" + #include "gtest/gtest.h" + ++#include ++ + using namespace llvm; + + TEST(SmallPtrSetTest, Assignment) { +diff --git a/llvm/unittests/ADT/StringMapTest.cpp b/llvm/unittests/ADT/StringMapTest.cpp +index 28d710fe69e9..60571cff6927 100644 +--- a/llvm/unittests/ADT/StringMapTest.cpp ++++ b/llvm/unittests/ADT/StringMapTest.cpp +@@ -9,6 +9,7 @@ + #include "llvm/ADT/StringMap.h" + #include "llvm/Support/DataTypes.h" + #include "gtest/gtest.h" ++#include + #include + #include + using namespace llvm; +diff --git a/llvm/unittests/Support/ConvertUTFTest.cpp b/llvm/unittests/Support/ConvertUTFTest.cpp +index 9c798437a12d..2fee8ad5c012 100644 +--- a/llvm/unittests/Support/ConvertUTFTest.cpp ++++ b/llvm/unittests/Support/ConvertUTFTest.cpp +@@ -7,6 +7,8 @@ + //===----------------------------------------------------------------------===// + + #include "llvm/Support/ConvertUTF.h" ++#include "llvm/Support/SmallString.h" ++#include "llvm/Support/SmallVector.h" + #include "gtest/gtest.h" + #include + #include +-- +2.20.1.windows.1 + diff --git a/upstream_utils/llvm_patches/0022-use-std-is_trivially_copy_constructible.patch b/upstream_utils/llvm_patches/0022-use-std-is_trivially_copy_constructible.patch new file mode 100644 index 0000000000..84a6755b1e --- /dev/null +++ b/upstream_utils/llvm_patches/0022-use-std-is_trivially_copy_constructible.patch @@ -0,0 +1,141 @@ +From 6df7cc04af50a1afad6f8baf8ea0520576944bed Mon Sep 17 00:00:00 2001 +From: PJ Reiniger +Date: Sun, 8 May 2022 16:42:09 -0400 +Subject: [PATCH 22/31] use std is_trivially_copy_constructible + +--- + llvm/include/llvm/ADT/PointerIntPair.h | 12 ---- + llvm/include/llvm/Support/type_traits.h | 91 +------------------------ + 2 files changed, 2 insertions(+), 101 deletions(-) + +diff --git a/llvm/include/llvm/ADT/PointerIntPair.h b/llvm/include/llvm/ADT/PointerIntPair.h +index cb8b202c48b7..f9c4d2dd14c7 100644 +--- a/llvm/include/llvm/ADT/PointerIntPair.h ++++ b/llvm/include/llvm/ADT/PointerIntPair.h +@@ -127,18 +127,6 @@ public: + } + }; + +-// Specialize is_trivially_copyable to avoid limitation of llvm::is_trivially_copyable +-// when compiled with gcc 4.9. +-template +-struct is_trivially_copyable> : std::true_type { +-#ifdef HAVE_STD_IS_TRIVIALLY_COPYABLE +- static_assert(std::is_trivially_copyable>::value, +- "inconsistent behavior between llvm:: and std:: implementation of is_trivially_copyable"); +-#endif +-}; +- + + template + struct PointerIntPairInfo { +diff --git a/llvm/include/llvm/Support/type_traits.h b/llvm/include/llvm/Support/type_traits.h +index 7b7d5d991f3f..72a2e84ad452 100644 +--- a/llvm/include/llvm/Support/type_traits.h ++++ b/llvm/include/llvm/Support/type_traits.h +@@ -92,98 +92,11 @@ union trivial_helper { + + } // end namespace detail + +-/// An implementation of `std::is_trivially_copy_constructible` since we have +-/// users with STLs that don't yet include it. + template +-struct is_trivially_copy_constructible +- : std::is_copy_constructible< +- ::llvm::detail::copy_construction_triviality_helper> {}; +-template +-struct is_trivially_copy_constructible : std::true_type {}; +-template +-struct is_trivially_copy_constructible : std::false_type {}; ++using is_trivially_move_constructible = std::is_trivially_move_constructible; + +-/// An implementation of `std::is_trivially_move_constructible` since we have +-/// users with STLs that don't yet include it. +-template +-struct is_trivially_move_constructible +- : std::is_move_constructible< +- ::llvm::detail::move_construction_triviality_helper> {}; + template +-struct is_trivially_move_constructible : std::true_type {}; +-template +-struct is_trivially_move_constructible : std::true_type {}; +- +- +-template +-struct is_copy_assignable { +- template +- static auto get(F*) -> decltype(std::declval() = std::declval(), std::true_type{}); +- static std::false_type get(...); +- static constexpr bool value = decltype(get((T*)nullptr))::value; +-}; +- +-template +-struct is_move_assignable { +- template +- static auto get(F*) -> decltype(std::declval() = std::declval(), std::true_type{}); +- static std::false_type get(...); +- static constexpr bool value = decltype(get((T*)nullptr))::value; +-}; +- +- +-// An implementation of `std::is_trivially_copyable` since STL version +-// is not equally supported by all compilers, especially GCC 4.9. +-// Uniform implementation of this trait is important for ABI compatibility +-// as it has an impact on SmallVector's ABI (among others). +-template +-class is_trivially_copyable { +- +- // copy constructors +- static constexpr bool has_trivial_copy_constructor = +- std::is_copy_constructible>::value; +- static constexpr bool has_deleted_copy_constructor = +- !std::is_copy_constructible::value; +- +- // move constructors +- static constexpr bool has_trivial_move_constructor = +- std::is_move_constructible>::value; +- static constexpr bool has_deleted_move_constructor = +- !std::is_move_constructible::value; +- +- // copy assign +- static constexpr bool has_trivial_copy_assign = +- is_copy_assignable>::value; +- static constexpr bool has_deleted_copy_assign = +- !is_copy_assignable::value; +- +- // move assign +- static constexpr bool has_trivial_move_assign = +- is_move_assignable>::value; +- static constexpr bool has_deleted_move_assign = +- !is_move_assignable::value; +- +- // destructor +- static constexpr bool has_trivial_destructor = +- std::is_destructible>::value; +- +- public: +- +- static constexpr bool value = +- has_trivial_destructor && +- (has_deleted_move_assign || has_trivial_move_assign) && +- (has_deleted_move_constructor || has_trivial_move_constructor) && +- (has_deleted_copy_assign || has_trivial_copy_assign) && +- (has_deleted_copy_constructor || has_trivial_copy_constructor); +- +-#ifdef HAVE_STD_IS_TRIVIALLY_COPYABLE +- static_assert(value == std::is_trivially_copyable::value, +- "inconsistent behavior between llvm:: and std:: implementation of is_trivially_copyable"); +-#endif +-}; +-template +-class is_trivially_copyable : public std::true_type { +-}; ++using is_trivially_copy_constructible = std::is_trivially_copy_constructible; + + + } // end namespace llvm +-- +2.20.1.windows.1 + diff --git a/upstream_utils/llvm_patches/0023-Windows-Support.patch b/upstream_utils/llvm_patches/0023-Windows-Support.patch new file mode 100644 index 0000000000..034a29a655 --- /dev/null +++ b/upstream_utils/llvm_patches/0023-Windows-Support.patch @@ -0,0 +1,223 @@ +From 403d6612f25ae09b3bbeffe3ed75e8c498f80da5 Mon Sep 17 00:00:00 2001 +From: PJ Reiniger +Date: Tue, 3 May 2022 20:22:38 -0400 +Subject: [PATCH 23/31] Windows Support + +--- + .../llvm/Support/Windows/WindowsSupport.h | 45 +++++---- + llvm/lib/Support/ConvertUTF.cpp | 95 +++++++++++++++++++ + llvm/lib/Support/MemoryBuffer.cpp | 4 +- + 3 files changed, 126 insertions(+), 18 deletions(-) + +diff --git a/llvm/include/llvm/Support/Windows/WindowsSupport.h b/llvm/include/llvm/Support/Windows/WindowsSupport.h +index 551bd199551e..50b0843beb29 100644 +--- a/llvm/include/llvm/Support/Windows/WindowsSupport.h ++++ b/llvm/include/llvm/Support/Windows/WindowsSupport.h +@@ -35,8 +35,6 @@ + + #include "llvm/ADT/SmallVector.h" + #include "llvm/ADT/StringExtras.h" +-#include "llvm/Config/llvm-config.h" // Get build system configuration settings +-#include "llvm/Support/Allocator.h" + #include "llvm/Support/Chrono.h" + #include "llvm/Support/Compiler.h" + #include "llvm/Support/ErrorHandling.h" +@@ -44,18 +42,46 @@ + #include + #include + #include ++#define WIN32_NO_STATUS + #include ++#undef WIN32_NO_STATUS ++#include ++#include + + // Must be included after windows.h + #include + + namespace llvm { + ++/// Returns the Windows version as Major.Minor.0.BuildNumber. Uses ++/// RtlGetVersion or GetVersionEx under the hood depending on what is available. ++/// GetVersionEx is deprecated, but this API exposes the build number which can ++/// be useful for working around certain kernel bugs. ++inline llvm::VersionTuple GetWindowsOSVersion() { ++ typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW); ++ HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll"); ++ if (hMod) { ++ auto getVer = (RtlGetVersionPtr)::GetProcAddress(hMod, "RtlGetVersion"); ++ if (getVer) { ++ RTL_OSVERSIONINFOEXW info{}; ++ info.dwOSVersionInfoSize = sizeof(info); ++ if (getVer((PRTL_OSVERSIONINFOW)&info) == ((NTSTATUS)0x00000000L)) { ++ return llvm::VersionTuple(info.dwMajorVersion, info.dwMinorVersion, 0, ++ info.dwBuildNumber); ++ } ++ } ++ } ++ return llvm::VersionTuple(0, 0, 0, 0); ++} ++ + /// Determines if the program is running on Windows 8 or newer. This + /// reimplements one of the helpers in the Windows 8.1 SDK, which are intended + /// to supercede raw calls to GetVersionEx. Old SDKs, Cygwin, and MinGW don't + /// yet have VersionHelpers.h, so we have our own helper. +-bool RunningWindows8OrGreater(); ++inline bool RunningWindows8OrGreater() { ++ // Windows 8 is version 6.2, service pack 0. ++ return GetWindowsOSVersion() >= llvm::VersionTuple(6, 2, 0, 0); ++} + + /// Returns the Windows version as Major.Minor.0.BuildNumber. Uses + /// RtlGetVersion or GetVersionEx under the hood depending on what is available. +@@ -228,19 +254,6 @@ inline FILETIME toFILETIME(TimePoint<> TP) { + return Time; + } + +-namespace windows { +-// Returns command line arguments. Unlike arguments given to main(), +-// this function guarantees that the returned arguments are encoded in +-// UTF-8 regardless of the current code page setting. +-std::error_code GetCommandLineArguments(SmallVectorImpl &Args, +- BumpPtrAllocator &Alloc); +- +-/// Convert UTF-8 path to a suitable UTF-16 path for use with the Win32 Unicode +-/// File API. +-std::error_code widenPath(const Twine &Path8, SmallVectorImpl &Path16, +- size_t MaxPathLen = MAX_PATH); +- +-} // end namespace windows + } // end namespace sys + } // end namespace llvm. + +diff --git a/llvm/lib/Support/ConvertUTF.cpp b/llvm/lib/Support/ConvertUTF.cpp +index e24a918c5c89..b4c7fffee8f7 100644 +--- a/llvm/lib/Support/ConvertUTF.cpp ++++ b/llvm/lib/Support/ConvertUTF.cpp +@@ -51,6 +51,11 @@ + #endif + #include + ++#ifdef _WIN32 ++#include "wpi/WindowsError.h" ++#include "Windows/WindowsSupport.h" ++#endif ++ + /* + * This code extensively uses fall-through switches. + * Keep the compiler from warning about that. +@@ -733,6 +738,96 @@ ConversionResult ConvertUTF8toUTF32(const UTF8 **sourceStart, + + --------------------------------------------------------------------- */ + ++#ifdef _WIN32 ++ ++namespace sys { ++namespace windows { ++std::error_code CodePageToUTF16(unsigned codepage, ++ std::string_view original, ++ wpi::SmallVectorImpl &utf16) { ++ if (!original.empty()) { ++ int len = ::MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, original.data(), ++ original.size(), utf16.begin(), 0); ++ ++ if (len == 0) { ++ return mapWindowsError(::GetLastError()); ++ } ++ ++ utf16.reserve(len + 1); ++ utf16.set_size(len); ++ ++ len = ::MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, original.data(), ++ original.size(), utf16.begin(), utf16.size()); ++ ++ if (len == 0) { ++ return mapWindowsError(::GetLastError()); ++ } ++ } ++ ++ // Make utf16 null terminated. ++ utf16.push_back(0); ++ utf16.pop_back(); ++ ++ return std::error_code(); ++} ++ ++std::error_code UTF8ToUTF16(std::string_view utf8, ++ wpi::SmallVectorImpl &utf16) { ++ return CodePageToUTF16(CP_UTF8, utf8, utf16); ++} ++ ++std::error_code CurCPToUTF16(std::string_view curcp, ++ wpi::SmallVectorImpl &utf16) { ++ return CodePageToUTF16(CP_ACP, curcp, utf16); ++} ++ ++static ++std::error_code UTF16ToCodePage(unsigned codepage, const wchar_t *utf16, ++ size_t utf16_len, ++ wpi::SmallVectorImpl &converted) { ++ if (utf16_len) { ++ // Get length. ++ int len = ::WideCharToMultiByte(codepage, 0, utf16, utf16_len, converted.begin(), ++ 0, NULL, NULL); ++ ++ if (len == 0) { ++ return mapWindowsError(::GetLastError()); ++ } ++ ++ converted.reserve(len); ++ converted.set_size(len); ++ ++ // Now do the actual conversion. ++ len = ::WideCharToMultiByte(codepage, 0, utf16, utf16_len, converted.data(), ++ converted.size(), NULL, NULL); ++ ++ if (len == 0) { ++ return mapWindowsError(::GetLastError()); ++ } ++ } ++ ++ // Make the new string null terminated. ++ converted.push_back(0); ++ converted.pop_back(); ++ ++ return std::error_code(); ++} ++ ++std::error_code UTF16ToUTF8(const wchar_t *utf16, size_t utf16_len, ++ wpi::SmallVectorImpl &utf8) { ++ return UTF16ToCodePage(CP_UTF8, utf16, utf16_len, utf8); ++} ++ ++std::error_code UTF16ToCurCP(const wchar_t *utf16, size_t utf16_len, ++ wpi::SmallVectorImpl &curcp) { ++ return UTF16ToCodePage(CP_ACP, utf16, utf16_len, curcp); ++} ++ ++} // end namespace windows ++} // end namespace sys ++ ++#endif // _WIN32 ++ + } // namespace llvm + + ConvertUTF_RESTORE_WARNINGS +diff --git a/llvm/lib/Support/MemoryBuffer.cpp b/llvm/lib/Support/MemoryBuffer.cpp +index bcf13d828a5d..cddad9ca0660 100644 +--- a/llvm/lib/Support/MemoryBuffer.cpp ++++ b/llvm/lib/Support/MemoryBuffer.cpp +@@ -225,8 +225,8 @@ public: + + static ErrorOr> + getMemoryBufferForStream(sys::fs::file_t FD, const Twine &BufferName) { +- const ssize_t ChunkSize = 4096*4; +- SmallString Buffer; ++ constexpr size_t ChunkSize = 4096*4; ++ SmallVector Buffer; + // Read into Buffer until we hit EOF. + for (;;) { + Buffer.reserve(Buffer.size() + ChunkSize); +-- +2.20.1.windows.1 + diff --git a/upstream_utils/llvm_patches/0024-Prefer-fmtlib.patch b/upstream_utils/llvm_patches/0024-Prefer-fmtlib.patch new file mode 100644 index 0000000000..541df1bb94 --- /dev/null +++ b/upstream_utils/llvm_patches/0024-Prefer-fmtlib.patch @@ -0,0 +1,58 @@ +From c67b1c2baf278050fb43bee7a7241392d64fbb0d Mon Sep 17 00:00:00 2001 +From: PJ Reiniger +Date: Sun, 8 May 2022 16:46:20 -0400 +Subject: [PATCH 24/31] Prefer fmtlib + +--- + llvm/lib/Support/ErrorHandling.cpp | 20 ++++++-------------- + 1 file changed, 6 insertions(+), 14 deletions(-) + +diff --git a/llvm/lib/Support/ErrorHandling.cpp b/llvm/lib/Support/ErrorHandling.cpp +index 4b6ddd30c6e4..9ee52a02c214 100644 +--- a/llvm/lib/Support/ErrorHandling.cpp ++++ b/llvm/lib/Support/ErrorHandling.cpp +@@ -22,7 +22,7 @@ + #include "llvm/Support/Signals.h" + #include "llvm/Support/Threading.h" + #include "llvm/Support/WindowsError.h" +-#include "llvm/Support/raw_ostream.h" ++#include "fmt/format.h" + #include + #include + #include +@@ -93,15 +93,7 @@ void llvm::report_fatal_error(std::string_view Reason, bool GenCrashDiag) { + if (handler) { + handler(handlerData, std::string{Reason}, GenCrashDiag); + } else { +- // Blast the result out to stderr. We don't try hard to make sure this +- // succeeds (e.g. handling EINTR) and we can't use errs() here because +- // raw ostreams can call report_fatal_error. +- SmallVector Buffer; +- raw_svector_ostream OS(Buffer); +- OS << "LLVM ERROR: " << Reason << "\n"; +- std::string_view MessageStr = OS.str(); +- ssize_t written = ::write(2, MessageStr.data(), MessageStr.size()); +- (void)written; // If something went wrong, we deliberately just give up. ++ fmt::print(stderr, "LLVM ERROR: {}\n", Reason); + } + + // If we reached here, we are failing ungracefully. Run the interrupt handlers +@@ -173,11 +165,11 @@ void llvm::llvm_unreachable_internal(const char *msg, const char *file, + // llvm_unreachable is intended to be used to indicate "impossible" + // situations, and not legitimate runtime errors. + if (msg) +- dbgs() << msg << "\n"; +- dbgs() << "UNREACHABLE executed"; ++ fmt::print(stderr, "{}\n", msg); ++ std::fputs("UNREACHABLE executed", stderr); + if (file) +- dbgs() << " at " << file << ":" << line; +- dbgs() << "!\n"; ++ fmt::print(stderr, " at {}:{}", file, line); ++ fmt::print(stderr, "{}", "!\n"); + abort(); + #ifdef LLVM_BUILTIN_UNREACHABLE + // Windows systems and possibly others don't declare abort() to be noreturn, +-- +2.20.1.windows.1 + diff --git a/upstream_utils/llvm_patches/0025-prefer-wpi-s-fs.h.patch b/upstream_utils/llvm_patches/0025-prefer-wpi-s-fs.h.patch new file mode 100644 index 0000000000..214bb790c5 --- /dev/null +++ b/upstream_utils/llvm_patches/0025-prefer-wpi-s-fs.h.patch @@ -0,0 +1,37 @@ +From 07ffe44dd483a6fd847030ca9b76a225d5b7b2de Mon Sep 17 00:00:00 2001 +From: PJ Reiniger +Date: Sun, 8 May 2022 16:49:36 -0400 +Subject: [PATCH 25/31] prefer wpi's fs.h + +--- + llvm/include/llvm/Support/raw_ostream.h | 7 ++----- + 1 file changed, 2 insertions(+), 5 deletions(-) + +diff --git a/llvm/include/llvm/Support/raw_ostream.h b/llvm/include/llvm/Support/raw_ostream.h +index 8008170be3e5..27fb5c03e474 100644 +--- a/llvm/include/llvm/Support/raw_ostream.h ++++ b/llvm/include/llvm/Support/raw_ostream.h +@@ -28,18 +28,15 @@ + #include + #include + +-namespace llvm { +- +-template class LLVM_NODISCARD Expected; + +-namespace sys { + namespace fs { + enum FileAccess : unsigned; + enum OpenFlags : unsigned; + enum CreationDisposition : unsigned; + class FileLocker; + } // end namespace fs +-} // end namespace sys ++ ++namespace llvm { + + /// This class implements an extremely fast bulk output stream that can *only* + /// output to a stream. It does not support seeking, reopening, rewinding, line +-- +2.20.1.windows.1 + diff --git a/upstream_utils/llvm_patches/0026-Remove-unused-functions.patch b/upstream_utils/llvm_patches/0026-Remove-unused-functions.patch new file mode 100644 index 0000000000..a80783d054 --- /dev/null +++ b/upstream_utils/llvm_patches/0026-Remove-unused-functions.patch @@ -0,0 +1,266 @@ +From 7bac24e5310d67d6828b687aec2043966a955093 Mon Sep 17 00:00:00 2001 +From: PJ Reiniger +Date: Sun, 8 May 2022 19:16:51 -0400 +Subject: [PATCH 26/31] Remove unused functions + +--- + llvm/include/llvm/Support/raw_ostream.h | 3 +- + llvm/lib/Support/ErrorHandling.cpp | 16 ----- + llvm/lib/Support/raw_ostream.cpp | 48 +++++++-------- + llvm/unittests/ADT/SmallStringTest.cpp | 81 ------------------------- + 4 files changed, 22 insertions(+), 126 deletions(-) + +diff --git a/llvm/include/llvm/Support/raw_ostream.h b/llvm/include/llvm/Support/raw_ostream.h +index 27fb5c03e474..d228b05f4f9b 100644 +--- a/llvm/include/llvm/Support/raw_ostream.h ++++ b/llvm/include/llvm/Support/raw_ostream.h +@@ -72,7 +72,6 @@ private: + /// for a \see write_impl() call to handle the data which has been put into + /// this buffer. + char *OutBufStart, *OutBufEnd, *OutBufCur; +- bool ColorEnabled = false; + + /// Optional stream this stream is tied to. If this stream is written to, the + /// tied-to stream will be flushed first. +@@ -305,7 +304,7 @@ public: + + // Enable or disable colors. Once enable_colors(false) is called, + // changeColor() has no effect until enable_colors(true) is called. +- virtual void enable_colors(bool enable) { ColorEnabled = enable; } ++ virtual void enable_colors(bool /*enable*/) {} + + /// Tie this stream to the specified stream. Replaces any existing tied-to + /// stream. Specifying a nullptr unties the stream. +diff --git a/llvm/lib/Support/ErrorHandling.cpp b/llvm/lib/Support/ErrorHandling.cpp +index 9ee52a02c214..77c3a37475d0 100644 +--- a/llvm/lib/Support/ErrorHandling.cpp ++++ b/llvm/lib/Support/ErrorHandling.cpp +@@ -178,22 +178,6 @@ void llvm::llvm_unreachable_internal(const char *msg, const char *file, + #endif + } + +-static void bindingsErrorHandler(void *user_data, const std::string& reason, +- bool gen_crash_diag) { +- LLVMFatalErrorHandler handler = +- LLVM_EXTENSION reinterpret_cast(user_data); +- handler(reason.c_str()); +-} +- +-void LLVMInstallFatalErrorHandler(LLVMFatalErrorHandler Handler) { +- install_fatal_error_handler(bindingsErrorHandler, +- LLVM_EXTENSION reinterpret_cast(Handler)); +-} +- +-void LLVMResetFatalErrorHandler() { +- remove_fatal_error_handler(); +-} +- + #ifdef _WIN32 + + #include +diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp +index 6b9e5f9e44bd..6fa250a68ed8 100644 +--- a/llvm/lib/Support/raw_ostream.cpp ++++ b/llvm/lib/Support/raw_ostream.cpp +@@ -169,16 +169,6 @@ raw_ostream &raw_ostream::write_escaped(std::string_view Str, + return *this; + } + +-raw_ostream &raw_ostream::operator<<(const void *P) { +- llvm::write_hex(*this, (uintptr_t)P, HexPrintStyle::PrefixLower); +- return *this; +-} +- +-raw_ostream &raw_ostream::operator<<(double N) { +- llvm::write_double(*this, N, FloatStyle::Exponent); +- return *this; +-} +- + void raw_ostream::flush_nonempty() { + assert(OutBufCur > OutBufStart && "Invalid call to flush_nonempty."); + size_t Length = OutBufCur - OutBufStart; +@@ -323,15 +313,22 @@ static int getFD(std::string_view Filename, std::error_code &EC, + if (Filename == "-") { + EC = std::error_code(); + // Change stdout's text/binary mode based on the Flags. +- sys::ChangeStdoutMode(Flags); ++ if (!(Flags & fs::OF_Text)) { ++#if defined(_WIN32) ++ _setmode(_fileno(stdout), _O_BINARY); ++#endif ++ } + return STDOUT_FILENO; + } + +- int FD; +- if (Access & sys::fs::FA_Read) +- EC = sys::fs::openFileForReadWrite(Filename, FD, Disp, Flags); ++ fs::file_t F; ++ if (Access & fs::FA_Read) ++ F = fs::OpenFileForReadWrite(fs::path{std::string_view{Filename.data(), Filename.size()}}, EC, Disp, Flags); + else +- EC = sys::fs::openFileForWrite(Filename, FD, Disp, Flags); ++ F = fs::OpenFileForWrite(fs::path{std::string_view{Filename.data(), Filename.size()}}, EC, Disp, Flags); ++ if (EC) ++ return -1; ++ int FD = fs::FileToFd(F, EC, Flags); + if (EC) + return -1; + +@@ -392,10 +389,7 @@ raw_fd_ostream::raw_fd_ostream(int fd, bool shouldClose, bool unbuffered, + // Get the starting position. + off_t loc = ::lseek(FD, 0, SEEK_CUR); + #ifdef _WIN32 +- // MSVCRT's _lseek(SEEK_CUR) doesn't return -1 for pipes. +- sys::fs::file_status Status; +- std::error_code EC = status(FD, Status); +- SupportsSeeking = !EC && Status.type() == sys::fs::file_type::regular_file; ++ SupportsSeeking = loc != (off_t)-1 && ::GetFileType(reinterpret_cast(::_get_osfhandle(FD))) != FILE_TYPE_PIPE; + #else + SupportsSeeking = loc != (off_t)-1; + #endif +@@ -408,10 +402,8 @@ raw_fd_ostream::raw_fd_ostream(int fd, bool shouldClose, bool unbuffered, + raw_fd_ostream::~raw_fd_ostream() { + if (FD >= 0) { + flush(); +- if (ShouldClose) { +- if (auto EC = sys::Process::SafelyCloseFileDescriptor(FD)) +- error_detected(EC); +- } ++ if (ShouldClose && ::close(FD) < 0) ++ error_detected(std::error_code(errno, std::generic_category())); + } + + #ifdef __MINGW32__ +@@ -506,7 +498,11 @@ void raw_fd_ostream::write_impl(const char *Ptr, size_t Size) { + + do { + size_t ChunkSize = std::min(Size, MaxWriteSize); ++#ifdef _WIN32 ++ int ret = ::_write(FD, Ptr, ChunkSize); ++#else + ssize_t ret = ::write(FD, Ptr, ChunkSize); ++#endif + + if (ret < 0) { + // If it's a recoverable error, swallow it and retry the write. +@@ -541,8 +537,8 @@ void raw_fd_ostream::close() { + assert(ShouldClose); + ShouldClose = false; + flush(); +- if (auto EC = sys::Process::SafelyCloseFileDescriptor(FD)) +- error_detected(EC); ++ if (::close(FD) < 0) ++ error_detected(std::error_code(errno, std::generic_category())); + FD = -1; + } + +@@ -551,8 +547,6 @@ uint64_t raw_fd_ostream::seek(uint64_t off) { + flush(); + #ifdef _WIN32 + pos = ::_lseeki64(FD, off, SEEK_SET); +-#elif defined(HAVE_LSEEK64) +- pos = ::lseek64(FD, off, SEEK_SET); + #else + pos = ::lseek(FD, off, SEEK_SET); + #endif +diff --git a/llvm/unittests/ADT/SmallStringTest.cpp b/llvm/unittests/ADT/SmallStringTest.cpp +index bee3875d11c9..87600ea4704b 100644 +--- a/llvm/unittests/ADT/SmallStringTest.cpp ++++ b/llvm/unittests/ADT/SmallStringTest.cpp +@@ -129,23 +129,6 @@ TEST_F(SmallStringTest, StdStringConversion) { + EXPECT_EQ("abc", theStdString); + } + +-TEST_F(SmallStringTest, Substr) { +- theString = "hello"; +- EXPECT_EQ("lo", theString.substr(3)); +- EXPECT_EQ("", theString.substr(100)); +- EXPECT_EQ("hello", theString.substr(0, 100)); +- EXPECT_EQ("o", theString.substr(4, 10)); +-} +- +-TEST_F(SmallStringTest, Slice) { +- theString = "hello"; +- EXPECT_EQ("l", theString.slice(2, 3)); +- EXPECT_EQ("ell", theString.slice(1, 4)); +- EXPECT_EQ("llo", theString.slice(2, 100)); +- EXPECT_EQ("", theString.slice(2, 1)); +- EXPECT_EQ("", theString.slice(10, 20)); +-} +- + TEST_F(SmallStringTest, Find) { + theString = "hello"; + EXPECT_EQ(2U, theString.find('l')); +@@ -180,68 +163,4 @@ TEST_F(SmallStringTest, Find) { + EXPECT_EQ(0U, theString.find("")); + } + +-TEST_F(SmallStringTest, Count) { +- theString = "hello"; +- EXPECT_EQ(2U, theString.count('l')); +- EXPECT_EQ(1U, theString.count('o')); +- EXPECT_EQ(0U, theString.count('z')); +- EXPECT_EQ(0U, theString.count("helloworld")); +- EXPECT_EQ(1U, theString.count("hello")); +- EXPECT_EQ(1U, theString.count("ello")); +- EXPECT_EQ(0U, theString.count("zz")); +-} +- +-TEST_F(SmallStringTest, Realloc) { +- theString = "abcd"; +- theString.reserve(100); +- EXPECT_EQ("abcd", theString); +- unsigned const N = 100000; +- theString.reserve(N); +- for (unsigned i = 0; i < N - 4; ++i) +- theString.push_back('y'); +- EXPECT_EQ("abcdyyy", theString.slice(0, 7)); +-} +- +-TEST_F(SmallStringTest, Comparisons) { +- EXPECT_EQ(-1, SmallString<10>("aab").compare("aad")); +- EXPECT_EQ( 0, SmallString<10>("aab").compare("aab")); +- EXPECT_EQ( 1, SmallString<10>("aab").compare("aaa")); +- EXPECT_EQ(-1, SmallString<10>("aab").compare("aabb")); +- EXPECT_EQ( 1, SmallString<10>("aab").compare("aa")); +- EXPECT_EQ( 1, SmallString<10>("\xFF").compare("\1")); +- +- EXPECT_EQ(-1, SmallString<10>("AaB").compare_insensitive("aAd")); +- EXPECT_EQ( 0, SmallString<10>("AaB").compare_insensitive("aab")); +- EXPECT_EQ( 1, SmallString<10>("AaB").compare_insensitive("AAA")); +- EXPECT_EQ(-1, SmallString<10>("AaB").compare_insensitive("aaBb")); +- EXPECT_EQ( 1, SmallString<10>("AaB").compare_insensitive("aA")); +- EXPECT_EQ( 1, SmallString<10>("\xFF").compare_insensitive("\1")); +- +- EXPECT_EQ(-1, SmallString<10>("aab").compare_numeric("aad")); +- EXPECT_EQ( 0, SmallString<10>("aab").compare_numeric("aab")); +- EXPECT_EQ( 1, SmallString<10>("aab").compare_numeric("aaa")); +- EXPECT_EQ(-1, SmallString<10>("aab").compare_numeric("aabb")); +- EXPECT_EQ( 1, SmallString<10>("aab").compare_numeric("aa")); +- EXPECT_EQ(-1, SmallString<10>("1").compare_numeric("10")); +- EXPECT_EQ( 0, SmallString<10>("10").compare_numeric("10")); +- EXPECT_EQ( 0, SmallString<10>("10a").compare_numeric("10a")); +- EXPECT_EQ( 1, SmallString<10>("2").compare_numeric("1")); +- EXPECT_EQ( 0, SmallString<10>("llvm_v1i64_ty").compare_numeric("llvm_v1i64_ty")); +- EXPECT_EQ( 1, SmallString<10>("\xFF").compare_numeric("\1")); +- EXPECT_EQ( 1, SmallString<10>("V16").compare_numeric("V1_q0")); +- EXPECT_EQ(-1, SmallString<10>("V1_q0").compare_numeric("V16")); +- EXPECT_EQ(-1, SmallString<10>("V8_q0").compare_numeric("V16")); +- EXPECT_EQ( 1, SmallString<10>("V16").compare_numeric("V8_q0")); +- EXPECT_EQ(-1, SmallString<10>("V1_q0").compare_numeric("V8_q0")); +- EXPECT_EQ( 1, SmallString<10>("V8_q0").compare_numeric("V1_q0")); +-} +- +-// Check gtest prints SmallString as a string instead of a container of chars. +-// The code is in utils/unittest/googletest/internal/custom/gtest-printers.h +-TEST_F(SmallStringTest, GTestPrinter) { +- EXPECT_EQ(R"("foo")", ::testing::PrintToString(SmallString<1>("foo"))); +- const SmallVectorImpl &ErasedSmallString = SmallString<1>("foo"); +- EXPECT_EQ(R"("foo")", ::testing::PrintToString(ErasedSmallString)); +-} +- + } // namespace +-- +2.20.1.windows.1 + diff --git a/upstream_utils/llvm_patches/0027-Add-convienence-feature-to-SmallString.patch b/upstream_utils/llvm_patches/0027-Add-convienence-feature-to-SmallString.patch new file mode 100644 index 0000000000..f8527e2ac0 --- /dev/null +++ b/upstream_utils/llvm_patches/0027-Add-convienence-feature-to-SmallString.patch @@ -0,0 +1,31 @@ +From 0e20a9a457bbcef8bd07a0c06fceb9641fba0ad2 Mon Sep 17 00:00:00 2001 +From: PJ Reiniger +Date: Mon, 9 May 2022 00:18:29 -0400 +Subject: [PATCH 27/31] Add convienence feature to SmallString + +--- + llvm/include/llvm/ADT/SmallString.h | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/llvm/include/llvm/ADT/SmallString.h b/llvm/include/llvm/ADT/SmallString.h +index 85987811f3c4..d3d469d45d15 100644 +--- a/llvm/include/llvm/ADT/SmallString.h ++++ b/llvm/include/llvm/ADT/SmallString.h +@@ -188,9 +188,11 @@ public: + /// Implicit conversion to std::string_view. + operator std::string_view() const { return str(); } + +- explicit operator std::string() const { +- return std::string(this->data(), this->size()); +- } ++ /// Explicit conversion to std::string. ++ std::string string() const { return {this->begin(), this->size()}; } ++ ++ /// Implicit conversion to std::string. ++ operator std::string() const { return string(); } + + // Extra operators. + SmallString &operator=(std::string_view RHS) { +-- +2.20.1.windows.1 + diff --git a/upstream_utils/llvm_patches/0028-OS-specific-changes.patch b/upstream_utils/llvm_patches/0028-OS-specific-changes.patch new file mode 100644 index 0000000000..12fd43d156 --- /dev/null +++ b/upstream_utils/llvm_patches/0028-OS-specific-changes.patch @@ -0,0 +1,46 @@ +From 96ace137b394c0af8b3f1d9f3862cdf171adcfa4 Mon Sep 17 00:00:00 2001 +From: PJ Reiniger +Date: Sun, 8 May 2022 19:30:43 -0400 +Subject: [PATCH 28/31] OS specific changes + +--- + llvm/lib/Support/ErrorHandling.cpp | 13 +++++++------ + 1 file changed, 7 insertions(+), 6 deletions(-) + +diff --git a/llvm/lib/Support/ErrorHandling.cpp b/llvm/lib/Support/ErrorHandling.cpp +index 77c3a37475d0..f70f15f27da4 100644 +--- a/llvm/lib/Support/ErrorHandling.cpp ++++ b/llvm/lib/Support/ErrorHandling.cpp +@@ -96,12 +96,7 @@ void llvm::report_fatal_error(std::string_view Reason, bool GenCrashDiag) { + fmt::print(stderr, "LLVM ERROR: {}\n", Reason); + } + +- // If we reached here, we are failing ungracefully. Run the interrupt handlers +- // to make sure any special cleanups get done, in particular that we remove +- // files registered with RemoveFileOnSignal. +- sys::RunInterruptHandlers(); +- +- abort(); ++ exit(1); + } + + void llvm::install_bad_alloc_error_handler(fatal_error_handler_t handler, +@@ -138,9 +133,15 @@ void llvm::report_bad_alloc_error(const char *Reason, bool GenCrashDiag) { + // an OOM to stderr and abort. + const char *OOMMessage = "LLVM ERROR: out of memory\n"; + const char *Newline = "\n"; ++#ifdef _WIN32 ++ (void)!::_write(2, OOMMessage, strlen(OOMMessage)); ++ (void)!::_write(2, Reason, strlen(Reason)); ++ (void)!::_write(2, Newline, strlen(Newline)); ++#else + (void)!::write(2, OOMMessage, strlen(OOMMessage)); + (void)!::write(2, Reason, strlen(Reason)); + (void)!::write(2, Newline, strlen(Newline)); ++#endif + abort(); + } + +-- +2.20.1.windows.1 + diff --git a/upstream_utils/llvm_patches/0029-Use-smallvector-for-UTF-conversion.patch b/upstream_utils/llvm_patches/0029-Use-smallvector-for-UTF-conversion.patch new file mode 100644 index 0000000000..7098d834f8 --- /dev/null +++ b/upstream_utils/llvm_patches/0029-Use-smallvector-for-UTF-conversion.patch @@ -0,0 +1,155 @@ +From 6ec969e4a7a0bcbb3b54a48dc2bff84e1b431632 Mon Sep 17 00:00:00 2001 +From: PJ Reiniger +Date: Mon, 9 May 2022 00:04:30 -0400 +Subject: [PATCH 29/31] Use smallvector for UTF conversion + +--- + llvm/include/llvm/Support/ConvertUTF.h | 6 +++--- + llvm/lib/Support/ConvertUTFWrapper.cpp | 6 +++--- + llvm/unittests/Support/ConvertUTFTest.cpp | 22 +++++++++++----------- + 3 files changed, 17 insertions(+), 17 deletions(-) + +diff --git a/llvm/include/llvm/Support/ConvertUTF.h b/llvm/include/llvm/Support/ConvertUTF.h +index b085c8a179e8..c82947006afb 100644 +--- a/llvm/include/llvm/Support/ConvertUTF.h ++++ b/llvm/include/llvm/Support/ConvertUTF.h +@@ -213,7 +213,7 @@ bool ConvertUTF8toWide(const char *Source, std::wstring &Result); + * Converts a std::wstring to a UTF-8 encoded std::string. + * \return true on success. + */ +-bool convertWideToUTF8(const std::wstring &Source, std::string &Result); ++bool convertWideToUTF8(const std::wstring &Source, SmallVectorImpl &Result); + + + /** +@@ -268,7 +268,7 @@ bool hasUTF16ByteOrderMark(span SrcBytes); + * \param [out] Out Converted UTF-8 is stored here on success. + * \returns true on success + */ +-bool convertUTF16ToUTF8String(span SrcBytes, std::string &Out); ++bool convertUTF16ToUTF8String(span SrcBytes, SmallVectorImpl &Out); + + /** + * Converts a UTF16 string into a UTF8 std::string. +@@ -277,7 +277,7 @@ bool convertUTF16ToUTF8String(span SrcBytes, std::string &Out); + * \param [out] Out Converted UTF-8 is stored here on success. + * \returns true on success + */ +-bool convertUTF16ToUTF8String(span Src, std::string &Out); ++bool convertUTF16ToUTF8String(span Src, SmallVectorImpl &Out); + + /** + * Converts a UTF-8 string into a UTF-16 string with native endianness. +diff --git a/llvm/lib/Support/ConvertUTFWrapper.cpp b/llvm/lib/Support/ConvertUTFWrapper.cpp +index 87da616b75d4..c6c52d25adb1 100644 +--- a/llvm/lib/Support/ConvertUTFWrapper.cpp ++++ b/llvm/lib/Support/ConvertUTFWrapper.cpp +@@ -84,7 +84,7 @@ bool hasUTF16ByteOrderMark(span S) { + (S[0] == '\xfe' && S[1] == '\xff'))); + } + +-bool convertUTF16ToUTF8String(span SrcBytes, std::string &Out) { ++bool convertUTF16ToUTF8String(span SrcBytes, SmallVectorImpl &Out) { + assert(Out.empty()); + + // Error out on an uneven byte count. +@@ -135,7 +135,7 @@ bool convertUTF16ToUTF8String(span SrcBytes, std::string &Out) { + return true; + } + +-bool convertUTF16ToUTF8String(span Src, std::string &Out) ++bool convertUTF16ToUTF8String(span Src, SmallVectorImpl &Out) + { + return convertUTF16ToUTF8String( + span(reinterpret_cast(Src.data()), +@@ -213,7 +213,7 @@ bool ConvertUTF8toWide(const char *Source, std::wstring &Result) { + return ConvertUTF8toWide(std::string_view(Source), Result); + } + +-bool convertWideToUTF8(const std::wstring &Source, std::string &Result) { ++bool convertWideToUTF8(const std::wstring &Source, SmallVectorImpl &Result) { + if (sizeof(wchar_t) == 1) { + const UTF8 *Start = reinterpret_cast(Source.data()); + const UTF8 *End = +diff --git a/llvm/unittests/Support/ConvertUTFTest.cpp b/llvm/unittests/Support/ConvertUTFTest.cpp +index 2fee8ad5c012..bbdc041cf1d1 100644 +--- a/llvm/unittests/Support/ConvertUTFTest.cpp ++++ b/llvm/unittests/Support/ConvertUTFTest.cpp +@@ -19,22 +19,22 @@ TEST(ConvertUTFTest, ConvertUTF16LittleEndianToUTF8String) { + // Src is the look of disapproval. + alignas(UTF16) static const char Src[] = "\xff\xfe\xa0\x0c_\x00\xa0\x0c"; + span Ref(Src, sizeof(Src) - 1); +- std::string Result; ++ SmallString<20> Result; + bool Success = convertUTF16ToUTF8String(Ref, Result); + EXPECT_TRUE(Success); + std::string Expected("\xe0\xb2\xa0_\xe0\xb2\xa0"); +- EXPECT_EQ(Expected, Result); ++ EXPECT_EQ(Expected, Result.string()); + } + + TEST(ConvertUTFTest, ConvertUTF16BigEndianToUTF8String) { + // Src is the look of disapproval. + alignas(UTF16) static const char Src[] = "\xfe\xff\x0c\xa0\x00_\x0c\xa0"; + span Ref(Src, sizeof(Src) - 1); +- std::string Result; ++ SmallString<20> Result; + bool Success = convertUTF16ToUTF8String(Ref, Result); + EXPECT_TRUE(Success); + std::string Expected("\xe0\xb2\xa0_\xe0\xb2\xa0"); +- EXPECT_EQ(Expected, Result); ++ EXPECT_EQ(Expected, Result.string()); + } + + TEST(ConvertUTFTest, ConvertUTF8ToUTF16String) { +@@ -51,16 +51,16 @@ TEST(ConvertUTFTest, ConvertUTF8ToUTF16String) { + } + + TEST(ConvertUTFTest, OddLengthInput) { +- std::string Result; ++ SmallString<20> Result; + bool Success = convertUTF16ToUTF8String(span("xxxxx", 5), Result); + EXPECT_FALSE(Success); + } + + TEST(ConvertUTFTest, Empty) { +- std::string Result; ++ SmallString<20> Result; + bool Success = convertUTF16ToUTF8String(span(), Result); + EXPECT_TRUE(Success); +- EXPECT_TRUE(Result.empty()); ++ EXPECT_TRUE(Result.string().empty()); + } + + TEST(ConvertUTFTest, HasUTF16BOM) { +@@ -83,11 +83,11 @@ TEST(ConvertUTFTest, UTF16WrappersForConvertUTF16ToUTF8String) { + // Src is the look of disapproval. + alignas(UTF16) static const char Src[] = "\xff\xfe\xa0\x0c_\x00\xa0\x0c"; + span SrcRef((const UTF16 *)Src, 4); +- std::string Result; ++ SmallString<20> Result; + bool Success = convertUTF16ToUTF8String(SrcRef, Result); + EXPECT_TRUE(Success); + std::string Expected("\xe0\xb2\xa0_\xe0\xb2\xa0"); +- EXPECT_EQ(Expected, Result); ++ EXPECT_EQ(Expected, Result.string()); + } + + TEST(ConvertUTFTest, ConvertUTF8toWide) { +@@ -107,11 +107,11 @@ TEST(ConvertUTFTest, ConvertUTF8toWide) { + TEST(ConvertUTFTest, convertWideToUTF8) { + // Src is the look of disapproval. + static const wchar_t Src[] = L"\x0ca0_\x0ca0"; +- std::string Result; ++ SmallString<20> Result; + bool Success = convertWideToUTF8(Src, Result); + EXPECT_TRUE(Success); + std::string Expected("\xe0\xb2\xa0_\xe0\xb2\xa0"); +- EXPECT_EQ(Expected, Result); ++ EXPECT_EQ(Expected, Result.string()); + } + + struct ConvertUTFResultContainer { +-- +2.20.1.windows.1 + diff --git a/upstream_utils/llvm_patches/0030-Prefer-to-use-static-pointers-in-raw_ostream.patch b/upstream_utils/llvm_patches/0030-Prefer-to-use-static-pointers-in-raw_ostream.patch new file mode 100644 index 0000000000..4a50806547 --- /dev/null +++ b/upstream_utils/llvm_patches/0030-Prefer-to-use-static-pointers-in-raw_ostream.patch @@ -0,0 +1,37 @@ +From 6f0f7e6e4256d642673e3441468f44f6bd94fe12 Mon Sep 17 00:00:00 2001 +From: PJ Reiniger +Date: Thu, 19 May 2022 00:58:36 -0400 +Subject: [PATCH 30/31] Prefer to use static pointers in raw_ostream + +See #1401 +--- + llvm/lib/Support/raw_ostream.cpp | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp +index 6fa250a68ed8..6be6809ad951 100644 +--- a/llvm/lib/Support/raw_ostream.cpp ++++ b/llvm/lib/Support/raw_ostream.cpp +@@ -601,15 +601,15 @@ void raw_fd_ostream::anchor() {} + raw_fd_ostream &llvm::outs() { + // Set buffer settings to model stdout behavior. + std::error_code EC; +- static raw_fd_ostream S("-", EC, sys::fs::OF_None); ++ static raw_fd_ostream* S = new raw_fd_ostream("-", EC, sys::fs::OF_None); + assert(!EC); +- return S; ++ return *S; + } + + raw_fd_ostream &llvm::errs() { + // Set standard error to be unbuffered and tied to outs() by default. +- static raw_fd_ostream S(STDERR_FILENO, false, true); +- return S; ++ static raw_fd_ostream* S = new raw_fd_ostream(STDERR_FILENO, false, true); ++ return *S; + } + + /// nulls() - This returns a reference to a raw_ostream which discards output. +-- +2.20.1.windows.1 + diff --git a/upstream_utils/llvm_patches/0031-constexpr-endian-byte-swap.patch b/upstream_utils/llvm_patches/0031-constexpr-endian-byte-swap.patch new file mode 100644 index 0000000000..5545fdc07f --- /dev/null +++ b/upstream_utils/llvm_patches/0031-constexpr-endian-byte-swap.patch @@ -0,0 +1,27 @@ +From 63e46567fe7f9d3e48beaca7827125bfe06e1004 Mon Sep 17 00:00:00 2001 +From: PJ Reiniger +Date: Thu, 19 May 2022 01:12:41 -0400 +Subject: [PATCH 31/31] constexpr endian byte swap + +--- + llvm/include/llvm/Support/Endian.h | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/llvm/include/llvm/Support/Endian.h b/llvm/include/llvm/Support/Endian.h +index 5e7c1e961b9d..2e883ff05b7e 100644 +--- a/llvm/include/llvm/Support/Endian.h ++++ b/llvm/include/llvm/Support/Endian.h +@@ -55,7 +55,9 @@ inline value_type byte_swap(value_type value, endianness endian) { + /// Swap the bytes of value to match the given endianness. + template + inline value_type byte_swap(value_type value) { +- return byte_swap(value, endian); ++ if constexpr ((endian != native) && (endian != system_endianness())) ++ sys::swapByteOrder(value); ++ return value; + } + + /// Read a value of a particular endianness from memory. +-- +2.20.1.windows.1 + diff --git a/upstream_utils/update_llvm.py b/upstream_utils/update_llvm.py new file mode 100644 index 0000000000..1fe877604c --- /dev/null +++ b/upstream_utils/update_llvm.py @@ -0,0 +1,202 @@ +#!/usr/bin/env python3 + +import os +import shutil + +from upstream_utils import setup_upstream_repo, comment_out_invalid_includes, walk_cwd_and_copy_if, am_patches, walk_if, copy_to + + +def run_global_replacements(wpiutil_llvm_files): + + for wpi_file in wpiutil_llvm_files: + with open(wpi_file) as f: + content = f.read() + + # Rename namespace from llvm to wpi + content = content.replace("namespace llvm", "namespace wpi") + content = content.replace("llvm::", "wpi::") + + # Fix #includes + content = content.replace("include \"llvm/ADT", "include \"wpi") + content = content.replace("include \"llvm/Config", "include \"wpi") + content = content.replace("include \"llvm/Support", "include \"wpi") + + # Remove unused headers + content = content.replace("#include \"llvm-c/ErrorHandling.h\"\n", "") + content = content.replace("#include \"wpi/Debug.h\"\n", "") + content = content.replace("#include \"wpi/Error.h\"\n", "") + content = content.replace("#include \"wpi/Format.h\"\n", "") + content = content.replace("#include \"wpi/FormatVariadic.h\"\n", "") + content = content.replace("#include \"wpi/NativeFormatting.h\"\n", "") + content = content.replace("#include \"wpi/Threading.h\"\n", "") + content = content.replace("#include \"wpi/DataTypes.h\"\n", "") + content = content.replace("#include \"wpi/llvm-config.h\"\n", "") + content = content.replace("#include \"wpi/abi-breaking.h\"\n", "") + content = content.replace("#include \"wpi/config.h\"\n", "") + content = content.replace("#include \"wpi/Signals.h\"\n", "") + content = content.replace("#include \"wpi/Process.h\"\n", "") + content = content.replace("#include \"wpi/Path.h\"\n", "") + content = content.replace("#include \"wpi/Program.h\"\n", "") + + # Fix include guards + content = content.replace("LLVM_ADT_", "WPIUTIL_WPI_") + content = content.replace("LLVM_SUPPORT_", "WPIUTIL_WPI_") + content = content.replace("LLVM_DEFINED_HAS_FEATURE", + "WPI_DEFINED_HAS_FEATURE") + + content = content.replace("const std::string_view &", + "std::string_view ") + content = content.replace("sys::fs::openFileForRead", + "fs::OpenFileForRead") + content = content.replace("sys::fs::closeFile", "fs::CloseFile") + content = content.replace("sys::fs::", "fs::") + + # Replace wpi/FileSystem.h with wpi/fs.h + content = content.replace("include \"wpi/FileSystem.h\"", + "include \"wpi/fs.h\"") + content = content.replace("#include \"wpi/ReverseIteration.h\"", + "#include \"wpi/PointerLikeTypeTraits.h\"") + + # Replace llvm_unreachable() with wpi_unreachable() + content = content.replace("llvm_unreachable", "wpi_unreachable") + content = content.replace("llvm_shutdown", "wpi_shutdown") + + content = content.replace("llvm_is_multithreaded()", "1") + + # Revert message in copyright header + content = content.replace("/// Defines the wpi::", + "/// Defines the llvm::") + content = content.replace("// end llvm namespace", + "// end wpi namespace") + content = content.replace("// end namespace llvm", + "// end namespace wpi") + content = content.replace("// End llvm namespace", + "// End wpi namespace") + + content = content.replace("fs::openFileForRead", "fs::OpenFileForRead") + + with open(wpi_file, "w") as f: + f.write(content) + + +def flattened_llvm_files(llvm, dirs_to_keep): + file_lookup = {} + + for dir_to_keep in dirs_to_keep: + dir_to_crawl = os.path.join(llvm, dir_to_keep) + for root, _, files in os.walk(dir_to_crawl): + for f in files: + file_lookup[f] = os.path.join(root, f) + + return file_lookup + + +def find_wpiutil_llvm_files(wpiutil_root, subfolder): + + # These files have substantial changes, not worth managing with the patching process + ignore_list = [ + "StringExtras.h", "StringExtras.cpp", "MemoryBuffer.cpp", + "MemoryBuffer.h", "SmallVectorMemoryBuffer.h" + ] + + wpiutil_files = [] + for root, _, files in os.walk(os.path.join(wpiutil_root, subfolder)): + for f in files: + if f not in ignore_list: + full_file = os.path.join(root, f) + with open(full_file, 'r') as ff: + contents = ff.read() + if "LLVM Compiler" in contents or "LLVM Project" in contents: + wpiutil_files.append(full_file) + + return wpiutil_files + + +def overwrite_files(wpiutil_files, llvm_files): + # Very sparse rips from LLVM sources. Not worth tyring to make match upstream + unmatched_files_whitelist = ["fs.h", "fs.cpp", "function_ref.h"] + + for wpi_file in wpiutil_files: + wpi_base_name = os.path.basename(wpi_file) + if wpi_base_name not in llvm_files: + if wpi_base_name not in unmatched_files_whitelist: + print(f"No file match for {wpi_file}, check if LLVM deleted it") + else: + shutil.copyfile(llvm_files[wpi_base_name], wpi_file) + + +def overwrite_source(wpiutil_root, llvm_root): + llvm_files = flattened_llvm_files(llvm_root, [ + "llvm/include/llvm/ADT/", "llvm/include/llvm/Config", + "llvm/include/llvm/Support/", "llvm/lib/Support/" + ]) + wpi_files = find_wpiutil_llvm_files( + wpiutil_root, "src/main/native/include/wpi") + find_wpiutil_llvm_files( + wpiutil_root, "src/main/native/cpp/llvm") + + overwrite_files(wpi_files, llvm_files) + run_global_replacements(wpi_files) + + +def overwrite_tests(wpiutil_root, llvm_root): + llvm_files = flattened_llvm_files(llvm_root, [ + "llvm/unittests/ADT/", "llvm/unittests/Config", + "llvm/unittests/Support/" + ]) + wpi_files = find_wpiutil_llvm_files(wpiutil_root, + "src/test/native/cpp/llvm") + + overwrite_files(wpi_files, llvm_files) + run_global_replacements(wpi_files) + + +def main(): + root, repo = setup_upstream_repo("https://github.com/llvm/llvm-project", + "llvmorg-13.0.0") + wpiutil = os.path.join(root, "wpiutil") + + patch_root = os.path.join(root, "upstream_utils/llvm_patches") + # yapf: disable + frontend_patches = [ + os.path.join(patch_root, "0001-Fix-spelling-language-errors.patch"), + os.path.join(patch_root, "0002-Remove-StringRef-ArrayRef-and-Optional.patch"), + os.path.join(patch_root, "0003-Wrap-std-min-max-calls-in-parens-for-windows-warning.patch"), + os.path.join(patch_root, "0004-Change-uniqe_function-storage-size.patch"), + os.path.join(patch_root, "0005-Threading-updates.patch"), + os.path.join(patch_root, "0006-Remove-DJB-hash-dependency.patch"), + os.path.join(patch_root, "0007-ifdef-guard-safety.patch"), + os.path.join(patch_root, "0008-Explicitly-use-std.patch"), + os.path.join(patch_root, "0009-Remove-format_provider.patch"), + os.path.join(patch_root, "0010-Remove-reverse-iterator.patch"), + os.path.join(patch_root, "0011-Remove-allocator-from-collections.patch"), + os.path.join(patch_root, "0012-Remove-EpochTracker.patch"), + os.path.join(patch_root, "0013-Add-compiler-warning-pragrams.patch"), + os.path.join(patch_root, "0014-Remove-unused-functions.patch"), + os.path.join(patch_root, "0015-Detemplatize-small-vector-base.patch"), + os.path.join(patch_root, "0016-Add-vectors-to-raw_ostream.patch"), + os.path.join(patch_root, "0017-Extra-collections-features.patch"), + os.path.join(patch_root, "0018-EpochTracker-abi-macro.patch"), + os.path.join(patch_root, "0019-Delete-numbers-from-mathextras.patch"), + os.path.join(patch_root, "0020-Add-lerp-and-sgn.patch"), + os.path.join(patch_root, "0021-Fixup-includes.patch"), + os.path.join(patch_root, "0022-use-std-is_trivially_copy_constructible.patch"), + os.path.join(patch_root, "0023-Windows-Support.patch"), + os.path.join(patch_root, "0024-Prefer-fmtlib.patch"), + os.path.join(patch_root, "0025-prefer-wpi-s-fs.h.patch"), + os.path.join(patch_root, "0026-Remove-unused-functions.patch"), + os.path.join(patch_root, "0027-Add-convienence-feature-to-SmallString.patch"), + os.path.join(patch_root, "0028-OS-specific-changes.patch"), + os.path.join(patch_root, "0029-Use-smallvector-for-UTF-conversion.patch"), + os.path.join(patch_root, "0030-Prefer-to-use-static-pointers-in-raw_ostream.patch"), + os.path.join(patch_root, "0031-constexpr-endian-byte-swap.patch"), + + ] + # yapf: enable + am_patches(repo, frontend_patches, use_threeway=True) + + overwrite_source(wpiutil, repo) + overwrite_tests(wpiutil, repo) + + +if __name__ == "__main__": + main() diff --git a/wpimath/src/main/native/include/frc/interpolation/TimeInterpolatableBuffer.h b/wpimath/src/main/native/include/frc/interpolation/TimeInterpolatableBuffer.h index 68b4532d81..b946dee1f2 100644 --- a/wpimath/src/main/native/include/frc/interpolation/TimeInterpolatableBuffer.h +++ b/wpimath/src/main/native/include/frc/interpolation/TimeInterpolatableBuffer.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include #include diff --git a/wpiutil/.styleguide b/wpiutil/.styleguide index 3c728f4549..4301c7da66 100644 --- a/wpiutil/.styleguide +++ b/wpiutil/.styleguide @@ -48,6 +48,8 @@ generatedFileExclude { src/main/native/include/wpi/SmallString\.h$ src/main/native/include/wpi/SmallVector\.h$ src/main/native/include/wpi/StringMap\.h$ + src/main/native/include/wpi/StringMapEntry\.h$ + src/main/native/include/wpi/STLForwardCompat\.h$ src/main/native/include/wpi/StringRef\.h$ src/main/native/include/wpi/SwapByteOrder\.h$ src/main/native/include/wpi/Twine\.h$ diff --git a/wpiutil/src/main/native/cpp/MemoryBuffer.cpp b/wpiutil/src/main/native/cpp/MemoryBuffer.cpp index ad1e7cf96e..6b9e19bdd9 100644 --- a/wpiutil/src/main/native/cpp/MemoryBuffer.cpp +++ b/wpiutil/src/main/native/cpp/MemoryBuffer.cpp @@ -49,6 +49,7 @@ #include "wpi/Errc.h" #include "wpi/Errno.h" #include "wpi/MappedFileRegion.h" +#include "wpi/MathExtras.h" #include "wpi/SmallVector.h" #include "wpi/SmallVectorMemoryBuffer.h" #include "wpi/fs.h" diff --git a/wpiutil/src/main/native/cpp/llvm/ConvertUTF.cpp b/wpiutil/src/main/native/cpp/llvm/ConvertUTF.cpp index 3050e631f2..81c0c04dd7 100644 --- a/wpiutil/src/main/native/cpp/llvm/ConvertUTF.cpp +++ b/wpiutil/src/main/native/cpp/llvm/ConvertUTF.cpp @@ -1,9 +1,8 @@ /*===--- ConvertUTF.c - Universal Character Names conversions ---------------=== * - * The LLVM Compiler Infrastructure - * - * This file is distributed under the University of Illinois Open Source - * License. See LICENSE.TXT for details. + * 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 * *===------------------------------------------------------------------------=*/ /* @@ -829,6 +828,6 @@ std::error_code UTF16ToCurCP(const wchar_t *utf16, size_t utf16_len, #endif // _WIN32 -} // namespace llvm +} // namespace wpi ConvertUTF_RESTORE_WARNINGS diff --git a/wpiutil/src/main/native/cpp/llvm/ConvertUTFWrapper.cpp b/wpiutil/src/main/native/cpp/llvm/ConvertUTFWrapper.cpp index db190d0bb6..53cf117512 100644 --- a/wpiutil/src/main/native/cpp/llvm/ConvertUTFWrapper.cpp +++ b/wpiutil/src/main/native/cpp/llvm/ConvertUTFWrapper.cpp @@ -1,19 +1,68 @@ //===-- ConvertUTFWrapper.cpp - Wrap ConvertUTF.h with clang data types -----=== // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 "wpi/span.h" #include "wpi/ConvertUTF.h" #include "wpi/SmallVector.h" +#include "wpi/ErrorHandling.h" +#include "wpi/SwapByteOrder.h" #include +#include #include namespace wpi { +bool ConvertUTF8toWide(unsigned WideCharWidth, std::string_view Source, + char *&ResultPtr, const UTF8 *&ErrorPtr) { + assert(WideCharWidth == 1 || WideCharWidth == 2 || WideCharWidth == 4); + ConversionResult result = conversionOK; + // Copy the character span over. + if (WideCharWidth == 1) { + const UTF8 *Pos = reinterpret_cast(Source.data()); + if (!isLegalUTF8String(&Pos, reinterpret_cast(Source.data() + Source.size()))) { + result = sourceIllegal; + ErrorPtr = Pos; + } else { + memcpy(ResultPtr, Source.data(), Source.size()); + ResultPtr += Source.size(); + } + } else if (WideCharWidth == 2) { + const UTF8 *sourceStart = (const UTF8*)Source.data(); + // FIXME: Make the type of the result buffer correct instead of + // using reinterpret_cast. + UTF16 *targetStart = reinterpret_cast(ResultPtr); + ConversionFlags flags = strictConversion; + result = ConvertUTF8toUTF16( + &sourceStart, sourceStart + Source.size(), + &targetStart, targetStart + Source.size(), flags); + if (result == conversionOK) + ResultPtr = reinterpret_cast(targetStart); + else + ErrorPtr = sourceStart; + } else if (WideCharWidth == 4) { + const UTF8 *sourceStart = (const UTF8*)Source.data(); + // FIXME: Make the type of the result buffer correct instead of + // using reinterpret_cast. + UTF32 *targetStart = reinterpret_cast(ResultPtr); + ConversionFlags flags = strictConversion; + result = ConvertUTF8toUTF32( + &sourceStart, sourceStart + Source.size(), + &targetStart, targetStart + Source.size(), flags); + if (result == conversionOK) + ResultPtr = reinterpret_cast(targetStart); + else + ErrorPtr = sourceStart; + } + assert((result != targetExhausted) + && "ConvertUTF8toUTFXX exhausted target buffer"); + return result == conversionOK; +} + bool ConvertCodePointToUTF8(unsigned Source, char *&ResultPtr) { const UTF32 *SourceStart = &Source; const UTF32 *SourceEnd = SourceStart + 1; @@ -35,23 +84,28 @@ bool hasUTF16ByteOrderMark(span S) { (S[0] == '\xfe' && S[1] == '\xff'))); } -bool convertUTF16ToUTF8String(span SrcUTF16, - SmallVectorImpl &DstUTF8) { - assert(DstUTF8.empty()); +bool convertUTF16ToUTF8String(span SrcBytes, SmallVectorImpl &Out) { + assert(Out.empty()); + + // Error out on an uneven byte count. + if (SrcBytes.size() % 2) + return false; // Avoid OOB by returning early on empty input. - if (SrcUTF16.empty()) + if (SrcBytes.empty()) return true; - const UTF16 *Src = SrcUTF16.begin(); - const UTF16 *SrcEnd = SrcUTF16.end(); + const UTF16 *Src = reinterpret_cast(SrcBytes.begin()); + const UTF16 *SrcEnd = reinterpret_cast(SrcBytes.end()); + + assert((uintptr_t)Src % sizeof(UTF16) == 0); // Byteswap if necessary. std::vector ByteSwapped; if (Src[0] == UNI_UTF16_BYTE_ORDER_MARK_SWAPPED) { ByteSwapped.insert(ByteSwapped.end(), Src, SrcEnd); for (unsigned I = 0, E = ByteSwapped.size(); I != E; ++I) - ByteSwapped[I] = (ByteSwapped[I] << 8) | (ByteSwapped[I] >> 8); + ByteSwapped[I] = wpi::ByteSwap_16(ByteSwapped[I]); Src = &ByteSwapped[0]; SrcEnd = &ByteSwapped[ByteSwapped.size() - 1] + 1; } @@ -62,25 +116,32 @@ bool convertUTF16ToUTF8String(span SrcUTF16, // Just allocate enough space up front. We'll shrink it later. Allocate // enough that we can fit a null terminator without reallocating. - DstUTF8.resize(SrcUTF16.size() * UNI_MAX_UTF8_BYTES_PER_CODE_POINT + 1); - UTF8 *Dst = reinterpret_cast(&DstUTF8[0]); - UTF8 *DstEnd = Dst + DstUTF8.size(); + Out.resize(SrcBytes.size() * UNI_MAX_UTF8_BYTES_PER_CODE_POINT + 1); + UTF8 *Dst = reinterpret_cast(&Out[0]); + UTF8 *DstEnd = Dst + Out.size(); ConversionResult CR = ConvertUTF16toUTF8(&Src, SrcEnd, &Dst, DstEnd, strictConversion); assert(CR != targetExhausted); if (CR != conversionOK) { - DstUTF8.clear(); + Out.clear(); return false; } - DstUTF8.resize(reinterpret_cast(Dst) - &DstUTF8[0]); - DstUTF8.push_back(0); - DstUTF8.pop_back(); + Out.resize(reinterpret_cast(Dst) - &Out[0]); + Out.push_back(0); + Out.pop_back(); return true; } +bool convertUTF16ToUTF8String(span Src, SmallVectorImpl &Out) +{ + return convertUTF16ToUTF8String( + span(reinterpret_cast(Src.data()), + Src.size() * sizeof(UTF16)), Out); +} + bool convertUTF8ToUTF16String(std::string_view SrcUTF8, SmallVectorImpl &DstUTF16) { assert(DstUTF16.empty()); @@ -119,5 +180,74 @@ bool convertUTF8ToUTF16String(std::string_view SrcUTF8, return true; } +static_assert(sizeof(wchar_t) == 1 || sizeof(wchar_t) == 2 || + sizeof(wchar_t) == 4, + "Expected wchar_t to be 1, 2, or 4 bytes"); + +template +static inline bool ConvertUTF8toWideInternal(std::string_view Source, + TResult &Result) { + // Even in the case of UTF-16, the number of bytes in a UTF-8 string is + // at least as large as the number of elements in the resulting wide + // string, because surrogate pairs take at least 4 bytes in UTF-8. + Result.resize(Source.size() + 1); + char *ResultPtr = reinterpret_cast(&Result[0]); + const UTF8 *ErrorPtr; + if (!ConvertUTF8toWide(sizeof(wchar_t), Source, ResultPtr, ErrorPtr)) { + Result.clear(); + return false; + } + Result.resize(reinterpret_cast(ResultPtr) - &Result[0]); + return true; +} + +bool ConvertUTF8toWide(std::string_view Source, std::wstring &Result) { + return ConvertUTF8toWideInternal(Source, Result); +} + +bool ConvertUTF8toWide(const char *Source, std::wstring &Result) { + if (!Source) { + Result.clear(); + return true; + } + return ConvertUTF8toWide(std::string_view(Source), Result); +} + +bool convertWideToUTF8(const std::wstring &Source, SmallVectorImpl &Result) { + if (sizeof(wchar_t) == 1) { + const UTF8 *Start = reinterpret_cast(Source.data()); + const UTF8 *End = + reinterpret_cast(Source.data() + Source.size()); + if (!isLegalUTF8String(&Start, End)) + return false; + Result.resize(Source.size()); + memcpy(&Result[0], Source.data(), Source.size()); + return true; + } else if (sizeof(wchar_t) == 2) { + return convertUTF16ToUTF8String( + span(reinterpret_cast(Source.data()), + Source.size()), + Result); + } else if (sizeof(wchar_t) == 4) { + const UTF32 *Start = reinterpret_cast(Source.data()); + const UTF32 *End = + reinterpret_cast(Source.data() + Source.size()); + Result.resize(UNI_MAX_UTF8_BYTES_PER_CODE_POINT * Source.size()); + UTF8 *ResultPtr = reinterpret_cast(&Result[0]); + UTF8 *ResultEnd = reinterpret_cast(&Result[0] + Result.size()); + if (ConvertUTF32toUTF8(&Start, End, &ResultPtr, ResultEnd, + strictConversion) == conversionOK) { + Result.resize(reinterpret_cast(ResultPtr) - &Result[0]); + return true; + } else { + Result.clear(); + return false; + } + } else { + wpi_unreachable( + "Control should never reach this point; see static_assert further up"); + } +} + } // end namespace wpi diff --git a/wpiutil/src/main/native/cpp/llvm/ErrorHandling.cpp b/wpiutil/src/main/native/cpp/llvm/ErrorHandling.cpp index 22ae63638f..32926273dc 100644 --- a/wpiutil/src/main/native/cpp/llvm/ErrorHandling.cpp +++ b/wpiutil/src/main/native/cpp/llvm/ErrorHandling.cpp @@ -1,9 +1,8 @@ //===- lib/Support/ErrorHandling.cpp - Callbacks for errors ---------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -14,10 +13,10 @@ #include "wpi/ErrorHandling.h" #include "wpi/SmallVector.h" +#include "wpi/Errc.h" #include "wpi/WindowsError.h" #include "fmt/format.h" #include -#include #include #include #include @@ -25,7 +24,6 @@ #ifndef _WIN32 #include #endif - #if defined(_MSC_VER) #include #endif @@ -53,7 +51,7 @@ static std::mutex ErrorHandlerMutex; static std::mutex BadAllocErrorHandlerMutex; void wpi::install_fatal_error_handler(fatal_error_handler_t handler, - void *user_data) { + void *user_data) { std::scoped_lock Lock(ErrorHandlerMutex); assert(!ErrorHandler && "Error handler already registered!\n"); ErrorHandler = handler; @@ -95,7 +93,7 @@ void wpi::report_fatal_error(std::string_view Reason, bool GenCrashDiag) { } void wpi::install_bad_alloc_error_handler(fatal_error_handler_t handler, - void *user_data) { + void *user_data) { std::scoped_lock Lock(BadAllocErrorHandlerMutex); assert(!ErrorHandler && "Bad alloc error handler already registered!\n"); BadAllocErrorHandler = handler; @@ -126,13 +124,17 @@ void wpi::report_bad_alloc_error(const char *Reason, bool GenCrashDiag) { // Don't call the normal error handler. It may allocate memory. Directly write // an OOM to stderr and abort. - char OOMMessage[] = "LLVM ERROR: out of memory\n"; + const char *OOMMessage = "LLVM ERROR: out of memory\n"; + const char *Newline = "\n"; #ifdef _WIN32 - int written = ::_write(2, OOMMessage, strlen(OOMMessage)); + (void)!::_write(2, OOMMessage, strlen(OOMMessage)); + (void)!::_write(2, Reason, strlen(Reason)); + (void)!::_write(2, Newline, strlen(Newline)); #else - ssize_t written = ::write(2, OOMMessage, strlen(OOMMessage)); + (void)!::write(2, OOMMessage, strlen(OOMMessage)); + (void)!::write(2, Reason, strlen(Reason)); + (void)!::write(2, Newline, strlen(Newline)); #endif - (void)written; abort(); } @@ -142,28 +144,17 @@ static void out_of_memory_new_handler() { wpi::report_bad_alloc_error("Allocation failed"); } -// Installs new handler that causes crash on allocation failure. It does not -// need to be called explicitly, if this file is linked to application, because -// in this case it is called during construction of 'new_handler_installer'. +// Installs new handler that causes crash on allocation failure. It is called by +// InitLLVM. void wpi::install_out_of_memory_new_handler() { - static bool out_of_memory_new_handler_installed = false; - if (!out_of_memory_new_handler_installed) { - std::set_new_handler(out_of_memory_new_handler); - out_of_memory_new_handler_installed = true; - } + std::new_handler old = std::set_new_handler(out_of_memory_new_handler); + (void)old; + assert((old == nullptr || old == out_of_memory_new_handler) && + "new-handler already installed"); } -// Static object that causes installation of 'out_of_memory_new_handler' before -// execution of 'main'. -static class NewHandlerInstaller { -public: - NewHandlerInstaller() { - install_out_of_memory_new_handler(); - } -} new_handler_installer; - void wpi::wpi_unreachable_internal(const char *msg, const char *file, - unsigned line) { + unsigned line) { // This code intentionally doesn't call the ErrorHandler callback, because // wpi_unreachable is intended to be used to indicate "impossible" // situations, and not legitimate runtime errors. @@ -183,7 +174,6 @@ void wpi::wpi_unreachable_internal(const char *msg, const char *file, #ifdef _WIN32 -#include #include // I'd rather not double the line count of the following. diff --git a/wpiutil/src/main/native/cpp/llvm/Hashing.cpp b/wpiutil/src/main/native/cpp/llvm/Hashing.cpp index e916751518..a719d3ffd9 100644 --- a/wpiutil/src/main/native/cpp/llvm/Hashing.cpp +++ b/wpiutil/src/main/native/cpp/llvm/Hashing.cpp @@ -1,9 +1,8 @@ //===-------------- lib/Support/Hashing.cpp -------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/wpiutil/src/main/native/cpp/llvm/ManagedStatic.cpp b/wpiutil/src/main/native/cpp/llvm/ManagedStatic.cpp index a8c82bfa91..43c59c8380 100644 --- a/wpiutil/src/main/native/cpp/llvm/ManagedStatic.cpp +++ b/wpiutil/src/main/native/cpp/llvm/ManagedStatic.cpp @@ -1,9 +1,8 @@ //===-- ManagedStatic.cpp - Static Global wrapper -------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -18,43 +17,32 @@ using namespace wpi; static const ManagedStaticBase *StaticList = nullptr; -static wpi::mutex *ManagedStaticMutex = nullptr; -static std::once_flag mutex_init_flag; -static void initializeMutex() { - ManagedStaticMutex = new wpi::mutex(); -} - -static wpi::mutex* getManagedStaticMutex() { - std::call_once(mutex_init_flag, initializeMutex); - return ManagedStaticMutex; -} - -void ManagedStaticBase::RegisterManagedStatic(void* created, - void (*Deleter)(void*)) const { - std::scoped_lock Lock(*getManagedStaticMutex()); - - if (!Ptr.load(std::memory_order_relaxed)) { - void *Tmp = created; - - Ptr.store(Tmp, std::memory_order_release); - DeleterFn = Deleter; - - // Add to list of managed statics. - Next = StaticList; - StaticList = this; - } +static wpi::mutex *getManagedStaticMutex() { + static wpi::mutex m; + return &m; } void ManagedStaticBase::RegisterManagedStatic(void *(*Creator)(), void (*Deleter)(void*)) const { assert(Creator); - std::scoped_lock Lock(*getManagedStaticMutex()); + if (1) { + std::scoped_lock Lock(*getManagedStaticMutex()); - if (!Ptr.load(std::memory_order_relaxed)) { - void *Tmp = Creator(); + if (!Ptr.load(std::memory_order_relaxed)) { + void *Tmp = Creator(); - Ptr.store(Tmp, std::memory_order_release); + Ptr.store(Tmp, std::memory_order_release); + DeleterFn = Deleter; + + // Add to list of managed statics. + Next = StaticList; + StaticList = this; + } + } else { + assert(!Ptr && !DeleterFn && !Next && + "Partially initialized ManagedStatic!?"); + Ptr = Creator(); DeleterFn = Deleter; // Add to list of managed statics. @@ -80,9 +68,10 @@ void ManagedStaticBase::destroy() const { } /// wpi_shutdown - Deallocate and destroy all ManagedStatic variables. +/// IMPORTANT: it's only safe to call wpi_shutdown() in single thread, +/// without any other threads executing LLVM APIs. +/// wpi_shutdown() should be the last use of LLVM APIs. void wpi::wpi_shutdown() { - std::scoped_lock Lock(*getManagedStaticMutex()); - while (StaticList) StaticList->destroy(); } diff --git a/wpiutil/src/main/native/cpp/llvm/MemAlloc.cpp b/wpiutil/src/main/native/cpp/llvm/MemAlloc.cpp new file mode 100644 index 0000000000..9a36c23fd9 --- /dev/null +++ b/wpiutil/src/main/native/cpp/llvm/MemAlloc.cpp @@ -0,0 +1,34 @@ +//===- MemAlloc.cpp - Memory allocation functions -------------------------===// +// +// 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 "wpi/MemAlloc.h" + +// These are out of line to have __cpp_aligned_new not affect ABI. + +LLVM_ATTRIBUTE_RETURNS_NONNULL LLVM_ATTRIBUTE_RETURNS_NOALIAS void * +wpi::allocate_buffer(size_t Size, size_t Alignment) { + return ::operator new(Size +#ifdef __cpp_aligned_new + , + std::align_val_t(Alignment) +#endif + ); +} + +void wpi::deallocate_buffer(void *Ptr, size_t Size, size_t Alignment) { + ::operator delete(Ptr +#ifdef __cpp_sized_deallocation + , + Size +#endif +#ifdef __cpp_aligned_new + , + std::align_val_t(Alignment) +#endif + ); +} diff --git a/wpiutil/src/main/native/cpp/llvm/SmallPtrSet.cpp b/wpiutil/src/main/native/cpp/llvm/SmallPtrSet.cpp index 1b33f4c5ac..c5cb23cca0 100644 --- a/wpiutil/src/main/native/cpp/llvm/SmallPtrSet.cpp +++ b/wpiutil/src/main/native/cpp/llvm/SmallPtrSet.cpp @@ -1,9 +1,8 @@ //===- llvm/ADT/SmallPtrSet.cpp - 'Normally small' pointer set ------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -14,9 +13,9 @@ #include "wpi/SmallPtrSet.h" #include "wpi/DenseMapInfo.h" +#include "wpi/ErrorHandling.h" #include "wpi/MathExtras.h" #include "wpi/MemAlloc.h" -#include "wpi/ErrorHandling.h" #include #include #include diff --git a/wpiutil/src/main/native/cpp/llvm/SmallVector.cpp b/wpiutil/src/main/native/cpp/llvm/SmallVector.cpp index 974fec9c5a..20528094b7 100644 --- a/wpiutil/src/main/native/cpp/llvm/SmallVector.cpp +++ b/wpiutil/src/main/native/cpp/llvm/SmallVector.cpp @@ -1,9 +1,8 @@ //===- llvm/ADT/SmallVector.cpp - 'Normally small' vectors ----------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -12,21 +11,99 @@ //===----------------------------------------------------------------------===// #include "wpi/SmallVector.h" -#include "wpi/MemAlloc.h" +#include +#ifdef LLVM_ENABLE_EXCEPTIONS +#include +#endif using namespace wpi; -/// grow_pod - This is an implementation of the grow() method which only works -/// on POD-like datatypes and is out of line to reduce code duplication. -void SmallVectorBase::grow_pod(void *FirstEl, size_t MinCapacity, - size_t TSize) { - // Ensure we can fit the new capacity in 32 bits. - if (MinCapacity > UINT32_MAX) - report_bad_alloc_error("SmallVector capacity overflow during allocation"); +// Check that no bytes are wasted and everything is well-aligned. +namespace { +struct Struct16B { + alignas(16) void *X; +}; +struct Struct32B { + alignas(32) void *X; +}; +} +static_assert(sizeof(SmallVector) == + sizeof(unsigned) * 2 + sizeof(void *), + "wasted space in SmallVector size 0"); +static_assert(alignof(SmallVector) >= alignof(Struct16B), + "wrong alignment for 16-byte aligned T"); +static_assert(alignof(SmallVector) >= alignof(Struct32B), + "wrong alignment for 32-byte aligned T"); +static_assert(sizeof(SmallVector) >= alignof(Struct16B), + "missing padding for 16-byte aligned T"); +static_assert(sizeof(SmallVector) >= alignof(Struct32B), + "missing padding for 32-byte aligned T"); +static_assert(sizeof(SmallVector) == + sizeof(unsigned) * 2 + sizeof(void *) * 2, + "wasted space in SmallVector size 1"); - size_t NewCapacity = 2 * capacity() + 1; // Always grow. - NewCapacity = - std::min(std::max(NewCapacity, MinCapacity), size_t(UINT32_MAX)); +/// Report that MinSize doesn't fit into this vector's size type. Throws +/// std::length_error or calls report_fatal_error. +LLVM_ATTRIBUTE_NORETURN +static void report_size_overflow(size_t MinSize, size_t MaxSize); +static void report_size_overflow(size_t MinSize, size_t MaxSize) { + std::string Reason = "SmallVector unable to grow. Requested capacity (" + + std::to_string(MinSize) + + ") is larger than maximum value for size type (" + + std::to_string(MaxSize) + ")"; +#ifdef LLVM_ENABLE_EXCEPTIONS + throw std::length_error(Reason); +#else + report_fatal_error(Reason); +#endif +} +/// Report that this vector is already at maximum capacity. Throws +/// std::length_error or calls report_fatal_error. +LLVM_ATTRIBUTE_NORETURN static void report_at_maximum_capacity(size_t MaxSize); +static void report_at_maximum_capacity(size_t MaxSize) { + std::string Reason = + "SmallVector capacity unable to grow. Already at maximum size " + + std::to_string(MaxSize); +#ifdef LLVM_ENABLE_EXCEPTIONS + throw std::length_error(Reason); +#else + report_fatal_error(Reason); +#endif +} + +// Note: Moving this function into the header may cause performance regression. +static size_t getNewCapacity(size_t MinSize, size_t TSize, size_t OldCapacity) { + constexpr size_t MaxSize = std::numeric_limits::max(); + + // Ensure we can fit the new capacity. + // This is only going to be applicable when the capacity is 32 bit. + if (MinSize > MaxSize) + report_size_overflow(MinSize, MaxSize); + + // Ensure we can meet the guarantee of space for at least one more element. + // The above check alone will not catch the case where grow is called with a + // default MinSize of 0, but the current capacity cannot be increased. + // This is only going to be applicable when the capacity is 32 bit. + if (OldCapacity == MaxSize) + report_at_maximum_capacity(MaxSize); + + // In theory 2*capacity can overflow if the capacity is 64 bit, but the + // original capacity would never be large enough for this to be a problem. + size_t NewCapacity = 2 * OldCapacity + 1; // Always grow. + return (std::min)((std::max)(NewCapacity, MinSize), MaxSize); +} + +// Note: Moving this function into the header may cause performance regression. +void *SmallVectorBase::mallocForGrow(size_t MinSize, size_t TSize, + size_t &NewCapacity) { + NewCapacity = getNewCapacity(MinSize, TSize, this->capacity()); + return wpi::safe_malloc(NewCapacity * TSize); +} + +// Note: Moving this function into the header may cause performance regression. +void SmallVectorBase::grow_pod(void *FirstEl, size_t MinSize, + size_t TSize) { + size_t NewCapacity = getNewCapacity(MinSize, TSize, this->capacity()); void *NewElts; if (BeginX == FirstEl) { NewElts = safe_malloc(NewCapacity * TSize); diff --git a/wpiutil/src/main/native/cpp/llvm/StringMap.cpp b/wpiutil/src/main/native/cpp/llvm/StringMap.cpp index 768801d848..68ece29b72 100644 --- a/wpiutil/src/main/native/cpp/llvm/StringMap.cpp +++ b/wpiutil/src/main/native/cpp/llvm/StringMap.cpp @@ -1,9 +1,8 @@ //===--- StringMap.cpp - String Hash table map implementation -------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -15,7 +14,6 @@ #include "wpi/StringExtras.h" #include "wpi/Compiler.h" #include "wpi/MathExtras.h" -#include using namespace wpi; @@ -65,23 +63,22 @@ StringMapImpl::StringMapImpl(unsigned InitSize, unsigned itemSize) { } void StringMapImpl::init(unsigned InitSize) { - assert((InitSize & (InitSize-1)) == 0 && + assert((InitSize & (InitSize - 1)) == 0 && "Init Size must be a power of 2 or zero!"); unsigned NewNumBuckets = InitSize ? InitSize : 16; NumItems = 0; NumTombstones = 0; - TheTable = static_cast( - safe_calloc(NewNumBuckets+1, - sizeof(StringMapEntryBase **) + sizeof(unsigned))); + TheTable = static_cast(safe_calloc( + NewNumBuckets + 1, sizeof(StringMapEntryBase **) + sizeof(unsigned))); // Set the member only if TheTable was successfully allocated NumBuckets = NewNumBuckets; // Allocate one extra bucket, set it to look filled so the iterators stop at // end. - TheTable[NumBuckets] = (StringMapEntryBase*)2; + TheTable[NumBuckets] = (StringMapEntryBase *)2; } /// LookupBucketFor - Look up the bucket that the specified string should end @@ -91,12 +88,12 @@ void StringMapImpl::init(unsigned InitSize) { /// of the string. unsigned StringMapImpl::LookupBucketFor(std::string_view Name) { unsigned HTSize = NumBuckets; - if (HTSize == 0) { // Hash table unallocated so far? + if (HTSize == 0) { // Hash table unallocated so far? init(16); HTSize = NumBuckets; } unsigned FullHashValue = HashString(Name); - unsigned BucketNo = FullHashValue & (HTSize-1); + unsigned BucketNo = FullHashValue & (HTSize - 1); unsigned *HashTable = (unsigned *)(TheTable + NumBuckets + 1); unsigned ProbeAmt = 1; @@ -118,7 +115,8 @@ unsigned StringMapImpl::LookupBucketFor(std::string_view Name) { if (BucketItem == getTombstoneVal()) { // Skip over tombstones. However, remember the first one we see. - if (FirstTombstone == -1) FirstTombstone = BucketNo; + if (FirstTombstone == -1) + FirstTombstone = BucketNo; } else if (LLVM_LIKELY(HashTable[BucketNo] == FullHashValue)) { // If the full hash value matches, check deeply for a match. The common // case here is that we are only looking at the buckets (for item info @@ -127,7 +125,7 @@ unsigned StringMapImpl::LookupBucketFor(std::string_view Name) { // Do the comparison like this because Name isn't necessarily // null-terminated! - char *ItemStr = (char*)BucketItem+ItemSize; + char *ItemStr = (char *)BucketItem + ItemSize; if (Name == std::string_view(ItemStr, BucketItem->getKeyLength())) { // We found a match! return BucketNo; @@ -135,7 +133,7 @@ unsigned StringMapImpl::LookupBucketFor(std::string_view Name) { } // Okay, we didn't find the item. Probe to the next bucket. - BucketNo = (BucketNo+ProbeAmt) & (HTSize-1); + BucketNo = (BucketNo + ProbeAmt) & (HTSize - 1); // Use quadratic probing, it has fewer clumping artifacts than linear // probing and has good cache behavior in the common case. @@ -148,9 +146,10 @@ unsigned StringMapImpl::LookupBucketFor(std::string_view Name) { /// This does not modify the map. int StringMapImpl::FindKey(std::string_view Key) const { unsigned HTSize = NumBuckets; - if (HTSize == 0) return -1; // Really empty table? + if (HTSize == 0) + return -1; // Really empty table? unsigned FullHashValue = HashString(Key); - unsigned BucketNo = FullHashValue & (HTSize-1); + unsigned BucketNo = FullHashValue & (HTSize - 1); unsigned *HashTable = (unsigned *)(TheTable + NumBuckets + 1); unsigned ProbeAmt = 1; @@ -170,7 +169,7 @@ int StringMapImpl::FindKey(std::string_view Key) const { // Do the comparison like this because NameStart isn't necessarily // null-terminated! - char *ItemStr = (char*)BucketItem+ItemSize; + char *ItemStr = (char *)BucketItem + ItemSize; if (Key == std::string_view(ItemStr, BucketItem->getKeyLength())) { // We found a match! return BucketNo; @@ -178,7 +177,7 @@ int StringMapImpl::FindKey(std::string_view Key) const { } // Okay, we didn't find the item. Probe to the next bucket. - BucketNo = (BucketNo+ProbeAmt) & (HTSize-1); + BucketNo = (BucketNo + ProbeAmt) & (HTSize - 1); // Use quadratic probing, it has fewer clumping artifacts than linear // probing and has good cache behavior in the common case. @@ -189,7 +188,7 @@ int StringMapImpl::FindKey(std::string_view Key) const { /// RemoveKey - Remove the specified StringMapEntry from the table, but do not /// delete it. This aborts if the value isn't in the table. void StringMapImpl::RemoveKey(StringMapEntryBase *V) { - const char *VStr = (char*)V + ItemSize; + const char *VStr = (char *)V + ItemSize; StringMapEntryBase *V2 = RemoveKey(std::string_view(VStr, V->getKeyLength())); (void)V2; assert(V == V2 && "Didn't find key?"); @@ -199,7 +198,8 @@ void StringMapImpl::RemoveKey(StringMapEntryBase *V) { /// table, returning it. If the key is not in the table, this returns null. StringMapEntryBase *StringMapImpl::RemoveKey(std::string_view Key) { int Bucket = FindKey(Key); - if (Bucket == -1) return nullptr; + if (Bucket == -1) + return nullptr; StringMapEntryBase *Result = TheTable[Bucket]; TheTable[Bucket] = getTombstoneVal(); @@ -220,7 +220,7 @@ unsigned StringMapImpl::RehashTable(unsigned BucketNo) { // the buckets are empty (meaning that many are filled with tombstones), // grow/rehash the table. if (LLVM_UNLIKELY(NumItems * 4 > NumBuckets * 3)) { - NewSize = NumBuckets*2; + NewSize = NumBuckets * 2; } else if (LLVM_UNLIKELY(NumBuckets - (NumItems + NumTombstones) <= NumBuckets / 8)) { NewSize = NumBuckets; @@ -231,11 +231,11 @@ unsigned StringMapImpl::RehashTable(unsigned BucketNo) { unsigned NewBucketNo = BucketNo; // Allocate one extra bucket which will always be non-empty. This allows the // iterators to stop at end. - auto NewTableArray = static_cast( - safe_calloc(NewSize+1, sizeof(StringMapEntryBase *) + sizeof(unsigned))); + auto NewTableArray = static_cast(safe_calloc( + NewSize + 1, sizeof(StringMapEntryBase *) + sizeof(unsigned))); unsigned *NewHashArray = (unsigned *)(NewTableArray + NewSize + 1); - NewTableArray[NewSize] = (StringMapEntryBase*)2; + NewTableArray[NewSize] = (StringMapEntryBase *)2; // Rehash all the items into their new buckets. Luckily :) we already have // the hash values available, so we don't have to rehash any strings. @@ -244,10 +244,10 @@ unsigned StringMapImpl::RehashTable(unsigned BucketNo) { if (Bucket && Bucket != getTombstoneVal()) { // Fast case, bucket available. unsigned FullHash = HashTable[I]; - unsigned NewBucket = FullHash & (NewSize-1); + unsigned NewBucket = FullHash & (NewSize - 1); if (!NewTableArray[NewBucket]) { - NewTableArray[FullHash & (NewSize-1)] = Bucket; - NewHashArray[FullHash & (NewSize-1)] = FullHash; + NewTableArray[FullHash & (NewSize - 1)] = Bucket; + NewHashArray[FullHash & (NewSize - 1)] = FullHash; if (I == BucketNo) NewBucketNo = NewBucket; continue; @@ -256,7 +256,7 @@ unsigned StringMapImpl::RehashTable(unsigned BucketNo) { // Otherwise probe for a spot. unsigned ProbeSize = 1; do { - NewBucket = (NewBucket + ProbeSize++) & (NewSize-1); + NewBucket = (NewBucket + ProbeSize++) & (NewSize - 1); } while (NewTableArray[NewBucket]); // Finally found a slot. Fill it in. diff --git a/wpiutil/src/main/native/cpp/llvm/Windows/WindowsSupport.h b/wpiutil/src/main/native/cpp/llvm/Windows/WindowsSupport.h index 7307337977..167571210c 100644 --- a/wpiutil/src/main/native/cpp/llvm/Windows/WindowsSupport.h +++ b/wpiutil/src/main/native/cpp/llvm/Windows/WindowsSupport.h @@ -1,9 +1,8 @@ //===- WindowsSupport.h - Common Windows Include File -----------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -19,8 +18,8 @@ //=== is guaranteed to work on *all* Win32 variants. //===----------------------------------------------------------------------===// -#ifndef LLVM_SUPPORT_WINDOWSSUPPORT_H -#define LLVM_SUPPORT_WINDOWSSUPPORT_H +#ifndef WPIUTIL_WPI_WINDOWSSUPPORT_H +#define WPIUTIL_WPI_WINDOWSSUPPORT_H // mingw-w64 tends to define it as 0x0502 in its headers. #undef _WIN32_WINNT @@ -38,10 +37,10 @@ #include "wpi/StringExtras.h" #include "wpi/Chrono.h" #include "wpi/Compiler.h" +#include "wpi/ErrorHandling.h" #include "wpi/VersionTuple.h" #include #include -#include #include #define WIN32_NO_STATUS #include @@ -49,6 +48,9 @@ #include #include +// Must be included after windows.h +#include + namespace wpi { /// Returns the Windows version as Major.Minor.0.BuildNumber. Uses @@ -81,23 +83,19 @@ inline bool RunningWindows8OrGreater() { return GetWindowsOSVersion() >= wpi::VersionTuple(6, 2, 0, 0); } -inline bool MakeErrMsg(std::string *ErrMsg, const std::string &prefix) { - if (!ErrMsg) - return true; - char *buffer = NULL; - DWORD LastError = GetLastError(); - DWORD R = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_MAX_WIDTH_MASK, - NULL, LastError, 0, (LPSTR)&buffer, 1, NULL); - if (R) - *ErrMsg = prefix + ": " + buffer; - else - *ErrMsg = prefix + ": Unknown error"; - *ErrMsg += " (0x" + wpi::utohexstr(LastError) + ")"; +/// Returns the Windows version as Major.Minor.0.BuildNumber. Uses +/// RtlGetVersion or GetVersionEx under the hood depending on what is available. +/// GetVersionEx is deprecated, but this API exposes the build number which can +/// be useful for working around certain kernel bugs. +wpi::VersionTuple GetWindowsOSVersion(); - LocalFree(buffer); - return R != 0; +bool MakeErrMsg(std::string *ErrMsg, const std::string &prefix); + +// Include GetLastError() in a fatal error message. +LLVM_ATTRIBUTE_NORETURN inline void ReportLastErrorFatal(const char *Msg) { + std::string ErrMsg; + MakeErrMsg(&ErrMsg, Msg); + wpi::report_fatal_error(ErrMsg); } template @@ -164,6 +162,22 @@ struct JobHandleTraits : CommonHandleTraits { } }; +struct CryptContextTraits : CommonHandleTraits { + typedef HCRYPTPROV handle_type; + + static handle_type GetInvalid() { + return 0; + } + + static void Close(handle_type h) { + ::CryptReleaseContext(h, 0); + } + + static bool IsValid(handle_type h) { + return h != GetInvalid(); + } +}; + struct RegTraits : CommonHandleTraits { typedef HKEY handle_type; @@ -190,6 +204,7 @@ struct FileHandleTraits : CommonHandleTraits {}; typedef ScopedHandle ScopedCommonHandle; typedef ScopedHandle ScopedFileHandle; +typedef ScopedHandle ScopedCryptContext; typedef ScopedHandle ScopedRegHandle; typedef ScopedHandle ScopedFindHandle; typedef ScopedHandle ScopedJobHandle; diff --git a/wpiutil/src/main/native/cpp/llvm/raw_os_ostream.cpp b/wpiutil/src/main/native/cpp/llvm/raw_os_ostream.cpp index 1fb6c51bcb..682a9a06d9 100644 --- a/wpiutil/src/main/native/cpp/llvm/raw_os_ostream.cpp +++ b/wpiutil/src/main/native/cpp/llvm/raw_os_ostream.cpp @@ -1,9 +1,8 @@ //===--- raw_os_ostream.cpp - Implement the raw_os_ostream class ----------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/wpiutil/src/main/native/cpp/llvm/raw_ostream.cpp b/wpiutil/src/main/native/cpp/llvm/raw_ostream.cpp index 622e0bf954..f6e0e8415d 100644 --- a/wpiutil/src/main/native/cpp/llvm/raw_ostream.cpp +++ b/wpiutil/src/main/native/cpp/llvm/raw_ostream.cpp @@ -1,9 +1,8 @@ //===--- raw_ostream.cpp - Implement the raw_ostream classes --------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -21,19 +20,17 @@ #include "wpi/StringExtras.h" #include "wpi/Compiler.h" #include "wpi/ErrorHandling.h" -#include "wpi/MathExtras.h" -#include "wpi/WindowsError.h" #include "wpi/fs.h" +#include "wpi/MathExtras.h" #include #include #include #include #include #include -#include // may provide O_BINARY. -#include +# include #ifndef _WIN32 #include @@ -64,6 +61,17 @@ using namespace wpi; +constexpr raw_ostream::Colors raw_ostream::BLACK; +constexpr raw_ostream::Colors raw_ostream::RED; +constexpr raw_ostream::Colors raw_ostream::GREEN; +constexpr raw_ostream::Colors raw_ostream::YELLOW; +constexpr raw_ostream::Colors raw_ostream::BLUE; +constexpr raw_ostream::Colors raw_ostream::MAGENTA; +constexpr raw_ostream::Colors raw_ostream::CYAN; +constexpr raw_ostream::Colors raw_ostream::WHITE; +constexpr raw_ostream::Colors raw_ostream::SAVEDCOLOR; +constexpr raw_ostream::Colors raw_ostream::RESET; + namespace { // Find the length of an array. template @@ -78,13 +86,10 @@ raw_ostream::~raw_ostream() { assert(OutBufCur == OutBufStart && "raw_ostream destructor called with non-empty buffer!"); - if (BufferMode == InternalBuffer) + if (BufferMode == BufferKind::InternalBuffer) delete [] OutBufStart; } -// An out of line virtual method to provide a home for the class vtable. -void raw_ostream::handle() {} - size_t raw_ostream::preferred_buffer_size() const { // BUFSIZ is intended to be a reasonable default. return BUFSIZ; @@ -101,14 +106,14 @@ void raw_ostream::SetBuffered() { void raw_ostream::SetBufferAndMode(char *BufferStart, size_t Size, BufferKind Mode) { - assert(((Mode == Unbuffered && !BufferStart && Size == 0) || - (Mode != Unbuffered && BufferStart && Size != 0)) && + assert(((Mode == BufferKind::Unbuffered && !BufferStart && Size == 0) || + (Mode != BufferKind::Unbuffered && BufferStart && Size != 0)) && "stream must be unbuffered or have at least one byte"); // Make sure the current buffer is free of content (we can't flush here; the // child buffer management logic will be in write_impl). assert(GetNumBytesInBuffer() == 0 && "Current buffer is non-empty!"); - if (BufferMode == InternalBuffer) + if (BufferMode == BufferKind::InternalBuffer) delete [] OutBufStart; OutBufStart = BufferStart; OutBufEnd = OutBufStart+Size; @@ -162,15 +167,15 @@ void raw_ostream::flush_nonempty() { assert(OutBufCur > OutBufStart && "Invalid call to flush_nonempty."); size_t Length = OutBufCur - OutBufStart; OutBufCur = OutBufStart; - write_impl(OutBufStart, Length); + flush_tied_then_write(OutBufStart, Length); } raw_ostream &raw_ostream::write(unsigned char C) { // Group exceptional cases into a single branch. if (LLVM_UNLIKELY(OutBufCur >= OutBufEnd)) { if (LLVM_UNLIKELY(!OutBufStart)) { - if (BufferMode == Unbuffered) { - write_impl(reinterpret_cast(&C), 1); + if (BufferMode == BufferKind::Unbuffered) { + flush_tied_then_write(reinterpret_cast(&C), 1); return *this; } // Set up a buffer and start over. @@ -189,8 +194,8 @@ raw_ostream &raw_ostream::write(const char *Ptr, size_t Size) { // Group exceptional cases into a single branch. if (LLVM_UNLIKELY(size_t(OutBufEnd - OutBufCur) < Size)) { if (LLVM_UNLIKELY(!OutBufStart)) { - if (BufferMode == Unbuffered) { - write_impl(Ptr, Size); + if (BufferMode == BufferKind::Unbuffered) { + flush_tied_then_write(Ptr, Size); return *this; } // Set up a buffer and start over. @@ -206,7 +211,7 @@ raw_ostream &raw_ostream::write(const char *Ptr, size_t Size) { if (LLVM_UNLIKELY(OutBufCur == OutBufStart)) { assert(NumBytes != 0 && "undefined behavior"); size_t BytesToWrite = Size - (Size % NumBytes); - write_impl(Ptr, BytesToWrite); + flush_tied_then_write(Ptr, BytesToWrite); size_t BytesRemaining = Size - BytesToWrite; if (BytesRemaining > size_t(OutBufEnd - OutBufCur)) { // Too much left over to copy into our buffer. @@ -247,6 +252,13 @@ void raw_ostream::copy_to_buffer(const char *Ptr, size_t Size) { OutBufCur += Size; } +void raw_ostream::flush_tied_then_write(const char *Ptr, size_t Size) { + if (TiedStream) + TiedStream->flush(); + write_impl(Ptr, Size); +} + + template static raw_ostream &write_padding(raw_ostream &OS, unsigned NumChars) { static const char Chars[] = {C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, @@ -294,8 +306,7 @@ static int getFD(std::string_view Filename, std::error_code &EC, // the owner of stdout and may set the "binary" flag globally based on Flags. if (Filename == "-") { EC = std::error_code(); - // If user requested binary then put stdout into binary mode if - // possible. + // Change stdout's text/binary mode based on the Flags. if (!(Flags & fs::OF_Text)) { #if defined(_WIN32) _setmode(_fileno(stdout), _O_BINARY); @@ -305,11 +316,10 @@ static int getFD(std::string_view Filename, std::error_code &EC, } fs::file_t F; - if (Access & fs::FA_Read) { + if (Access & fs::FA_Read) F = fs::OpenFileForReadWrite(fs::path{std::string_view{Filename.data(), Filename.size()}}, EC, Disp, Flags); - } else { + else F = fs::OpenFileForWrite(fs::path{std::string_view{Filename.data(), Filename.size()}}, EC, Disp, Flags); - } if (EC) return -1; int FD = fs::FileToFd(F, EC, Flags); @@ -345,13 +355,16 @@ raw_fd_ostream::raw_fd_ostream(std::string_view Filename, std::error_code &EC, /// FD is the file descriptor that this writes to. If ShouldClose is true, this /// closes the file when the stream is destroyed. -raw_fd_ostream::raw_fd_ostream(int fd, bool shouldClose, bool unbuffered) - : raw_pwrite_stream(unbuffered), FD(fd), ShouldClose(shouldClose) { +raw_fd_ostream::raw_fd_ostream(int fd, bool shouldClose, bool unbuffered, + OStreamKind K) + : raw_pwrite_stream(unbuffered, K), FD(fd), ShouldClose(shouldClose) { if (FD < 0 ) { ShouldClose = false; return; } + enable_colors(true); + // Do not attempt to close stdout or stderr. We used to try to maintain the // property that tools that support writing file to stdout should not also // write informational output to stdout, but in practice we were never able to @@ -370,7 +383,6 @@ raw_fd_ostream::raw_fd_ostream(int fd, bool shouldClose, bool unbuffered) // Get the starting position. off_t loc = ::lseek(FD, 0, SEEK_CUR); #ifdef _WIN32 - // MSVCRT's _lseek(SEEK_CUR) doesn't return -1 for pipes. SupportsSeeking = loc != (off_t)-1 && ::GetFileType(reinterpret_cast(::_get_osfhandle(FD))) != FILE_TYPE_PIPE; #else SupportsSeeking = loc != (off_t)-1; @@ -402,7 +414,7 @@ raw_fd_ostream::~raw_fd_ostream() { // destructing raw_ostream objects which may have errors. if (has_error()) report_fatal_error("IO failure on output stream: " + error().message(), - /*GenCrashDiag=*/false); + /*gen_crash_diag=*/false); } #if defined(_WIN32) @@ -565,7 +577,7 @@ size_t raw_fd_ostream::preferred_buffer_size() const { // If this is a terminal, don't use buffering. Line buffering // would be a more traditional thing to do, but it's not worth // the complexity. - if (S_ISCHR(statbuf.st_mode) && isatty(FD)) + if (S_ISCHR(statbuf.st_mode) && is_displayed()) return 0; // Return the preferred block size. return statbuf.st_blksize; @@ -580,28 +592,45 @@ void raw_fd_ostream::anchor() {} // outs(), errs(), nulls() //===----------------------------------------------------------------------===// -/// outs() - This returns a reference to a raw_ostream for standard output. -/// Use it like: outs() << "foo" << "bar"; -raw_ostream &wpi::outs() { +raw_fd_ostream &wpi::outs() { // Set buffer settings to model stdout behavior. std::error_code EC; - static raw_fd_ostream* S = new raw_fd_ostream("-", EC, fs::F_None); + static raw_fd_ostream* S = new raw_fd_ostream("-", EC, fs::OF_None); assert(!EC); return *S; } -/// errs() - This returns a reference to a raw_ostream for standard error. -/// Use it like: errs() << "foo" << "bar"; -raw_ostream &wpi::errs() { - // Set standard error to be unbuffered by default. +raw_fd_ostream &wpi::errs() { + // Set standard error to be unbuffered and tied to outs() by default. static raw_fd_ostream* S = new raw_fd_ostream(STDERR_FILENO, false, true); return *S; } /// nulls() - This returns a reference to a raw_ostream which discards output. raw_ostream &wpi::nulls() { - static raw_null_ostream* S = new raw_null_ostream; - return *S; + static raw_null_ostream S; + return S; +} + +//===----------------------------------------------------------------------===// +// File Streams +//===----------------------------------------------------------------------===// + +raw_fd_stream::raw_fd_stream(std::string_view Filename, std::error_code &EC) + : raw_fd_ostream(getFD(Filename, EC, fs::CD_CreateAlways, + fs::FA_Write | fs::FA_Read, + fs::OF_None), + true, false, OStreamKind::OK_FDStream) { + if (EC) + return; + + // Do not support non-seekable files. + if (!supportsSeeking()) + EC = std::make_error_code(std::errc::invalid_argument); +} + +bool raw_fd_stream::classof(const raw_ostream *OS) { + return OS->get_kind() == OStreamKind::OK_FDStream; } //===----------------------------------------------------------------------===// @@ -691,15 +720,18 @@ raw_null_ostream::~raw_null_ostream() { #endif } -void raw_null_ostream::write_impl(const char * /*Ptr*/, size_t /*Size*/) {} +void raw_null_ostream::write_impl(const char *Ptr, size_t Size) { +} uint64_t raw_null_ostream::current_pos() const { return 0; } -void raw_null_ostream::pwrite_impl(const char * /*Ptr*/, size_t /*Size*/, - uint64_t /*Offset*/) {} +void raw_null_ostream::pwrite_impl(const char *Ptr, size_t Size, + uint64_t Offset) {} void raw_pwrite_stream::anchor() {} void buffer_ostream::anchor() {} + +void buffer_unique_ostream::anchor() {} diff --git a/wpiutil/src/main/native/include/wpi/AlignOf.h b/wpiutil/src/main/native/include/wpi/AlignOf.h index adddd13b24..5d400e4807 100644 --- a/wpiutil/src/main/native/include/wpi/AlignOf.h +++ b/wpiutil/src/main/native/include/wpi/AlignOf.h @@ -1,146 +1,34 @@ //===--- AlignOf.h - Portable calculation of type alignment -----*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // -// This file defines the AlignedCharArray and AlignedCharArrayUnion classes. +// This file defines the AlignedCharArrayUnion class. // //===----------------------------------------------------------------------===// #ifndef WPIUTIL_WPI_ALIGNOF_H #define WPIUTIL_WPI_ALIGNOF_H -#include "wpi/Compiler.h" -#include +#include namespace wpi { -/// \struct AlignedCharArray -/// Helper for building an aligned character array type. +/// A suitably aligned and sized character array member which can hold elements +/// of any type. /// -/// This template is used to explicitly build up a collection of aligned -/// character array types. We have to build these up using a macro and explicit -/// specialization to cope with MSVC (at least till 2015) where only an -/// integer literal can be used to specify an alignment constraint. Once built -/// up here, we can then begin to indirect between these using normal C++ -/// template parameters. - -// MSVC requires special handling here. -#ifndef _MSC_VER - -template -struct AlignedCharArray { - alignas(Alignment) char buffer[Size]; +/// This template is equivalent to std::aligned_union_t<1, ...>, but we cannot +/// use it due to a bug in the MSVC x86 compiler: +/// https://github.com/microsoft/STL/issues/1533 +/// Using `alignas` here works around the bug. +template struct AlignedCharArrayUnion { + using AlignedUnion = std::aligned_union_t<1, T, Ts...>; + alignas(alignof(AlignedUnion)) char buffer[sizeof(AlignedUnion)]; }; -#else // _MSC_VER - -/// Create a type with an aligned char buffer. -template -struct AlignedCharArray; - -// We provide special variations of this template for the most common -// alignments because __declspec(align(...)) doesn't actually work when it is -// a member of a by-value function argument in MSVC, even if the alignment -// request is something reasonably like 8-byte or 16-byte. Note that we can't -// even include the declspec with the union that forces the alignment because -// MSVC warns on the existence of the declspec despite the union member forcing -// proper alignment. - -template -struct AlignedCharArray<1, Size> { - union { - char aligned; - char buffer[Size]; - }; -}; - -template -struct AlignedCharArray<2, Size> { - union { - short aligned; - char buffer[Size]; - }; -}; - -template -struct AlignedCharArray<4, Size> { - union { - int aligned; - char buffer[Size]; - }; -}; - -template -struct AlignedCharArray<8, Size> { - union { - double aligned; - char buffer[Size]; - }; -}; - - -// The rest of these are provided with a __declspec(align(...)) and we simply -// can't pass them by-value as function arguments on MSVC. - -#define LLVM_ALIGNEDCHARARRAY_TEMPLATE_ALIGNMENT(x) \ - template \ - struct AlignedCharArray { \ - __declspec(align(x)) char buffer[Size]; \ - }; - -LLVM_ALIGNEDCHARARRAY_TEMPLATE_ALIGNMENT(16) -LLVM_ALIGNEDCHARARRAY_TEMPLATE_ALIGNMENT(32) -LLVM_ALIGNEDCHARARRAY_TEMPLATE_ALIGNMENT(64) -LLVM_ALIGNEDCHARARRAY_TEMPLATE_ALIGNMENT(128) - -#undef LLVM_ALIGNEDCHARARRAY_TEMPLATE_ALIGNMENT - -#endif // _MSC_VER - -namespace detail { -template -class AlignerImpl { - T1 t1; T2 t2; T3 t3; T4 t4; T5 t5; T6 t6; T7 t7; T8 t8; T9 t9; T10 t10; - - AlignerImpl() = delete; -}; - -template -union SizerImpl { - char arr1[sizeof(T1)], arr2[sizeof(T2)], arr3[sizeof(T3)], arr4[sizeof(T4)], - arr5[sizeof(T5)], arr6[sizeof(T6)], arr7[sizeof(T7)], arr8[sizeof(T8)], - arr9[sizeof(T9)], arr10[sizeof(T10)]; -}; -} // end namespace detail - -/// This union template exposes a suitably aligned and sized character -/// array member which can hold elements of any of up to ten types. -/// -/// These types may be arrays, structs, or any other types. The goal is to -/// expose a char array buffer member which can be used as suitable storage for -/// a placement new of any of these types. Support for more than ten types can -/// be added at the cost of more boilerplate. -template -struct AlignedCharArrayUnion : wpi::AlignedCharArray< - alignof(wpi::detail::AlignerImpl), - sizeof(::wpi::detail::SizerImpl)> { -}; } // end namespace wpi -#endif // LLVM_SUPPORT_ALIGNOF_H +#endif // WPIUTIL_WPI_ALIGNOF_H diff --git a/wpiutil/src/main/native/include/wpi/Chrono.h b/wpiutil/src/main/native/include/wpi/Chrono.h index 33341c6f9c..5557809765 100644 --- a/wpiutil/src/main/native/include/wpi/Chrono.h +++ b/wpiutil/src/main/native/include/wpi/Chrono.h @@ -1,9 +1,8 @@ //===- llvm/Support/Chrono.h - Utilities for Timing Manipulation-*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -33,21 +32,21 @@ template using TimePoint = std::chrono::time_point; /// Convert a TimePoint to std::time_t -LLVM_ATTRIBUTE_ALWAYS_INLINE std::time_t toTimeT(TimePoint<> TP) { +inline std::time_t toTimeT(TimePoint<> TP) { using namespace std::chrono; return system_clock::to_time_t( time_point_cast(TP)); } /// Convert a std::time_t to a TimePoint -LLVM_ATTRIBUTE_ALWAYS_INLINE TimePoint +inline TimePoint toTimePoint(std::time_t T) { using namespace std::chrono; return time_point_cast(system_clock::from_time_t(T)); } /// Convert a std::time_t + nanoseconds to a TimePoint -LLVM_ATTRIBUTE_ALWAYS_INLINE TimePoint<> +inline TimePoint<> toTimePoint(std::time_t T, uint32_t nsec) { using namespace std::chrono; return time_point_cast(system_clock::from_time_t(T)) diff --git a/wpiutil/src/main/native/include/wpi/Compiler.h b/wpiutil/src/main/native/include/wpi/Compiler.h index 5ceb605983..e393e61689 100644 --- a/wpiutil/src/main/native/include/wpi/Compiler.h +++ b/wpiutil/src/main/native/include/wpi/Compiler.h @@ -1,21 +1,24 @@ //===-- llvm/Support/Compiler.h - Compiler abstraction support --*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // // This file defines several macros, based on the current compiler. This allows -// use of compiler-specific features in a way that remains portable. +// use of compiler-specific features in a way that remains portable. This header +// can be included from either C or C++. // //===----------------------------------------------------------------------===// #ifndef WPIUTIL_WPI_COMPILER_H #define WPIUTIL_WPI_COMPILER_H + +#ifdef __cplusplus #include +#endif #include #if defined(_MSC_VER) @@ -34,14 +37,20 @@ # define __has_attribute(x) 0 #endif -#ifndef __has_cpp_attribute -# define __has_cpp_attribute(x) 0 -#endif - #ifndef __has_builtin # define __has_builtin(x) 0 #endif +// Only use __has_cpp_attribute in C++ mode. GCC defines __has_cpp_attribute in +// C mode, but the :: in __has_cpp_attribute(scoped::attribute) is invalid. +#ifndef LLVM_HAS_CPP_ATTRIBUTE +#if defined(__cplusplus) && defined(__has_cpp_attribute) +# define LLVM_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) +#else +# define LLVM_HAS_CPP_ATTRIBUTE(x) 0 +#endif +#endif + /// \macro LLVM_GNUC_PREREQ /// Extend the default __GNUC_PREREQ even if glibc's features.h isn't /// available. @@ -61,14 +70,22 @@ /// \macro LLVM_MSC_PREREQ /// Is the compiler MSVC of at least the specified version? /// The common \param version values to check for are: -/// * 1900: Microsoft Visual Studio 2015 / 14.0 +/// * 1910: VS2017, version 15.1 & 15.2 +/// * 1911: VS2017, version 15.3 & 15.4 +/// * 1912: VS2017, version 15.5 +/// * 1913: VS2017, version 15.6 +/// * 1914: VS2017, version 15.7 +/// * 1915: VS2017, version 15.8 +/// * 1916: VS2017, version 15.9 +/// * 1920: VS2019, version 16.0 +/// * 1921: VS2019, version 16.1 #ifndef LLVM_MSC_PREREQ #ifdef _MSC_VER #define LLVM_MSC_PREREQ(version) (_MSC_VER >= (version)) -// We require at least MSVC 2015. -#if !LLVM_MSC_PREREQ(1900) -#error wpiutil requires at least MSVC 2015. +// We require at least MSVC 2017. +#if !LLVM_MSC_PREREQ(1910) +#error LLVM requires at least MSVC 2017. #endif #else @@ -79,9 +96,11 @@ /// Does the compiler support ref-qualifiers for *this? /// /// Sadly, this is separate from just rvalue reference support because GCC -/// and MSVC implemented this later than everything else. +/// and MSVC implemented this later than everything else. This appears to be +/// corrected in MSVC 2019 but not MSVC 2017. #ifndef LLVM_HAS_RVALUE_REFERENCE_THIS -#if __has_feature(cxx_rvalue_references) || LLVM_GNUC_PREREQ(4, 8, 1) +#if __has_feature(cxx_rvalue_references) || LLVM_GNUC_PREREQ(4, 8, 1) || \ + LLVM_MSC_PREREQ(1920) #define LLVM_HAS_RVALUE_REFERENCE_THIS 1 #else #define LLVM_HAS_RVALUE_REFERENCE_THIS 0 @@ -100,6 +119,24 @@ #endif #endif +/// LLVM_LIBRARY_VISIBILITY - If a class marked with this attribute is linked +/// into a shared library, then the class should be private to the library and +/// not accessible from outside it. Can also be used to mark variables and +/// functions, making them private to any shared library they are linked into. +/// On PE/COFF targets, library visibility is the default, so this isn't needed. +/// +/// LLVM_EXTERNAL_VISIBILITY - classes, functions, and variables marked with +/// this attribute will be made public and visible outside of any shared library +/// they are linked in to. +#if (__has_attribute(visibility) || LLVM_GNUC_PREREQ(4, 0, 0)) && \ + !defined(__MINGW32__) && !defined(__CYGWIN__) && !defined(_WIN32) +#define LLVM_LIBRARY_VISIBILITY __attribute__ ((visibility("hidden"))) +#define LLVM_EXTERNAL_VISIBILITY __attribute__ ((visibility("default"))) +#else +#define LLVM_LIBRARY_VISIBILITY +#define LLVM_EXTERNAL_VISIBILITY +#endif + #ifndef LLVM_PREFETCH #if defined(__GNUC__) #define LLVM_PREFETCH(addr, rw, locality) __builtin_prefetch(addr, rw, locality) @@ -117,25 +154,37 @@ #endif /// LLVM_NODISCARD - Warn if a type or return value is discarded. + +// Use the 'nodiscard' attribute in C++17 or newer mode. #ifndef LLVM_NODISCARD -#if __cplusplus > 201402L && __has_cpp_attribute(nodiscard) +#if defined(__cplusplus) && __cplusplus > 201402L && LLVM_HAS_CPP_ATTRIBUTE(nodiscard) #define LLVM_NODISCARD [[nodiscard]] -// Detect MSVC directly, since __cplusplus still defaults to old version -#elif _MSVC_LANG >= 201703L -#define LLVM_NODISCARD [[nodiscard]] -#elif _MSC_VER -#define LLVM_NODISCARD -#elif !__cplusplus -// Workaround for llvm.org/PR23435, since clang 3.6 and below emit a spurious -// error when __has_cpp_attribute is given a scoped attribute in C mode. -#define LLVM_NODISCARD -#elif __has_cpp_attribute(clang::warn_unused_result) +#elif LLVM_HAS_CPP_ATTRIBUTE(clang::warn_unused_result) #define LLVM_NODISCARD [[clang::warn_unused_result]] +// Clang in C++14 mode claims that it has the 'nodiscard' attribute, but also +// warns in the pedantic mode that 'nodiscard' is a C++17 extension (PR33518). +// Use the 'nodiscard' attribute in C++14 mode only with GCC. +// TODO: remove this workaround when PR33518 is resolved. +#elif defined(__GNUC__) && LLVM_HAS_CPP_ATTRIBUTE(nodiscard) +#define LLVM_NODISCARD [[nodiscard]] #else #define LLVM_NODISCARD #endif #endif +// Indicate that a non-static, non-const C++ member function reinitializes +// the entire object to a known state, independent of the previous state of +// the object. +// +// The clang-tidy check bugprone-use-after-move recognizes this attribute as a +// marker that a moved-from object has left the indeterminate state and can be +// reused. +#if LLVM_HAS_CPP_ATTRIBUTE(clang::reinitializes) +#define LLVM_ATTRIBUTE_REINITIALIZES [[clang::reinitializes]] +#else +#define LLVM_ATTRIBUTE_REINITIALIZES +#endif + // Some compilers warn about unused functions. When a function is sometimes // used or not depending on build settings (e.g. a function only called from // within "assert"), this attribute can be used to suppress such warnings. @@ -152,6 +201,14 @@ #endif #endif +// FIXME: Provide this for PE/COFF targets. +#if (__has_attribute(weak) || LLVM_GNUC_PREREQ(4, 0, 0)) && \ + (!defined(__MINGW32__) && !defined(__CYGWIN__) && !defined(_WIN32)) +#define LLVM_ATTRIBUTE_WEAK __attribute__((__weak__)) +#else +#define LLVM_ATTRIBUTE_WEAK +#endif + #ifndef LLVM_READNONE // Prior to clang 3.2, clang did not accept any spelling of // __has_attribute(const), so assume it is supported. @@ -200,7 +257,7 @@ /// errors, just use it in GCC 4.0 and later. #ifndef LLVM_ATTRIBUTE_ALWAYS_INLINE #if __has_attribute(always_inline) || LLVM_GNUC_PREREQ(4, 0, 0) -#define LLVM_ATTRIBUTE_ALWAYS_INLINE __attribute__((always_inline)) inline +#define LLVM_ATTRIBUTE_ALWAYS_INLINE inline __attribute__((always_inline)) #elif defined(_MSC_VER) #define LLVM_ATTRIBUTE_ALWAYS_INLINE __forceinline #else @@ -242,26 +299,44 @@ /// LLVM_FALLTHROUGH - Mark fallthrough cases in switch statements. #ifndef LLVM_FALLTHROUGH -#if __cplusplus > 201402L && __has_cpp_attribute(fallthrough) +#if defined(__cplusplus) && __cplusplus > 201402L && LLVM_HAS_CPP_ATTRIBUTE(fallthrough) #define LLVM_FALLTHROUGH [[fallthrough]] -// Detect MSVC directly, since __cplusplus still defaults to old version -#elif _MSVC_LANG >= 201703L -#define LLVM_FALLTHROUGH [[fallthrough]] -#elif _MSC_VER -#define LLVM_FALLTHROUGH -#elif __has_cpp_attribute(gnu::fallthrough) +#elif LLVM_HAS_CPP_ATTRIBUTE(gnu::fallthrough) #define LLVM_FALLTHROUGH [[gnu::fallthrough]] -#elif !__cplusplus -// Workaround for llvm.org/PR23435, since clang 3.6 and below emit a spurious -// error when __has_cpp_attribute is given a scoped attribute in C mode. -#define LLVM_FALLTHROUGH -#elif __has_cpp_attribute(clang::fallthrough) +#elif __has_attribute(fallthrough) +#define LLVM_FALLTHROUGH __attribute__((fallthrough)) +#elif LLVM_HAS_CPP_ATTRIBUTE(clang::fallthrough) #define LLVM_FALLTHROUGH [[clang::fallthrough]] #else #define LLVM_FALLTHROUGH #endif #endif +/// LLVM_REQUIRE_CONSTANT_INITIALIZATION - Apply this to globals to ensure that +/// they are constant initialized. +#if LLVM_HAS_CPP_ATTRIBUTE(clang::require_constant_initialization) +#define LLVM_REQUIRE_CONSTANT_INITIALIZATION \ + [[clang::require_constant_initialization]] +#else +#define LLVM_REQUIRE_CONSTANT_INITIALIZATION +#endif + +/// LLVM_GSL_OWNER - Apply this to owning classes like SmallVector to enable +/// lifetime warnings. +#if LLVM_HAS_CPP_ATTRIBUTE(gsl::Owner) +#define LLVM_GSL_OWNER [[gsl::Owner]] +#else +#define LLVM_GSL_OWNER +#endif + +/// LLVM_GSL_POINTER - Apply this to non-owning classes like +/// std::string_view to enable lifetime warnings. +#if LLVM_HAS_CPP_ATTRIBUTE(gsl::Pointer) +#define LLVM_GSL_POINTER [[gsl::Pointer]] +#else +#define LLVM_GSL_POINTER +#endif + /// LLVM_EXTENSION - Support compilers where we have a keyword to suppress /// pedantic diagnostics. #ifndef LLVM_EXTENSION @@ -273,20 +348,10 @@ #endif // LLVM_ATTRIBUTE_DEPRECATED(decl, "message") +// This macro will be removed. +// Use C++14's attribute instead: [[deprecated("message")]] #ifndef LLVM_ATTRIBUTE_DEPRECATED -#if __has_feature(attribute_deprecated_with_message) -# define LLVM_ATTRIBUTE_DEPRECATED(decl, message) \ - decl __attribute__((deprecated(message))) -#elif defined(__GNUC__) -# define LLVM_ATTRIBUTE_DEPRECATED(decl, message) \ - decl __attribute__((deprecated)) -#elif defined(_MSC_VER) -# define LLVM_ATTRIBUTE_DEPRECATED(decl, message) \ - __declspec(deprecated(message)) decl -#else -# define LLVM_ATTRIBUTE_DEPRECATED(decl, message) \ - decl -#endif +#define LLVM_ATTRIBUTE_DEPRECATED(decl, message) [[deprecated(message)]] decl #endif /// LLVM_BUILTIN_UNREACHABLE - On compilers which support it, expands @@ -341,7 +406,6 @@ #if __has_builtin(__builtin_assume_aligned) || LLVM_GNUC_PREREQ(4, 7, 0) # define LLVM_ASSUME_ALIGNED(p, a) __builtin_assume_aligned(p, a) #elif defined(LLVM_BUILTIN_UNREACHABLE) -// As of today, clang does not support __builtin_assume_aligned. # define LLVM_ASSUME_ALIGNED(p, a) \ (((uintptr_t(p) % (a)) == 0) ? (p) : (LLVM_BUILTIN_UNREACHABLE, (p))) #else @@ -349,16 +413,6 @@ #endif #endif -/// \macro LLVM_ALIGNAS -/// Used to specify a minimum alignment for a structure or variable. -#ifndef LLVM_ALIGNAS -#if __GNUC__ && !__has_feature(cxx_alignas) && !LLVM_GNUC_PREREQ(4, 8, 1) -# define LLVM_ALIGNAS(x) __attribute__((aligned(x))) -#else -# define LLVM_ALIGNAS(x) alignas(x) -#endif -#endif - /// \macro LLVM_PACKED /// Used to specify a packed structure. /// LLVM_PACKED( @@ -391,8 +445,8 @@ /// \macro LLVM_PTR_SIZE /// A constant integer equivalent to the value of sizeof(void*). -/// Generally used in combination with LLVM_ALIGNAS or when doing computation in -/// the preprocessor. +/// Generally used in combination with alignas or when doing computation in the +/// preprocessor. #ifndef LLVM_PTR_SIZE #ifdef __SIZEOF_POINTER__ # define LLVM_PTR_SIZE __SIZEOF_POINTER__ @@ -407,6 +461,73 @@ #endif #endif +/// \macro LLVM_MEMORY_SANITIZER_BUILD +/// Whether LLVM itself is built with MemorySanitizer instrumentation. +#if __has_feature(memory_sanitizer) +# define LLVM_MEMORY_SANITIZER_BUILD 1 +# include +# define LLVM_NO_SANITIZE_MEMORY_ATTRIBUTE __attribute__((no_sanitize_memory)) +#else +# define LLVM_MEMORY_SANITIZER_BUILD 0 +# define __msan_allocated_memory(p, size) +# define __msan_unpoison(p, size) +# define LLVM_NO_SANITIZE_MEMORY_ATTRIBUTE +#endif + +/// \macro LLVM_ADDRESS_SANITIZER_BUILD +/// Whether LLVM itself is built with AddressSanitizer instrumentation. +#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) +# define LLVM_ADDRESS_SANITIZER_BUILD 1 +# include +#else +# define LLVM_ADDRESS_SANITIZER_BUILD 0 +# define __asan_poison_memory_region(p, size) +# define __asan_unpoison_memory_region(p, size) +#endif + +/// \macro LLVM_THREAD_SANITIZER_BUILD +/// Whether LLVM itself is built with ThreadSanitizer instrumentation. +#if __has_feature(thread_sanitizer) || defined(__SANITIZE_THREAD__) +# define LLVM_THREAD_SANITIZER_BUILD 1 +#else +# define LLVM_THREAD_SANITIZER_BUILD 0 +#endif + +#if LLVM_THREAD_SANITIZER_BUILD +// Thread Sanitizer is a tool that finds races in code. +// See http://code.google.com/p/data-race-test/wiki/DynamicAnnotations . +// tsan detects these exact functions by name. +#ifdef __cplusplus +extern "C" { +#endif +void AnnotateHappensAfter(const char *file, int line, const volatile void *cv); +void AnnotateHappensBefore(const char *file, int line, const volatile void *cv); +void AnnotateIgnoreWritesBegin(const char *file, int line); +void AnnotateIgnoreWritesEnd(const char *file, int line); +#ifdef __cplusplus +} +#endif + +// This marker is used to define a happens-before arc. The race detector will +// infer an arc from the begin to the end when they share the same pointer +// argument. +# define TsanHappensBefore(cv) AnnotateHappensBefore(__FILE__, __LINE__, cv) + +// This marker defines the destination of a happens-before arc. +# define TsanHappensAfter(cv) AnnotateHappensAfter(__FILE__, __LINE__, cv) + +// Ignore any races on writes between here and the next TsanIgnoreWritesEnd. +# define TsanIgnoreWritesBegin() AnnotateIgnoreWritesBegin(__FILE__, __LINE__) + +// Resume checking for racy writes. +# define TsanIgnoreWritesEnd() AnnotateIgnoreWritesEnd(__FILE__, __LINE__) +#else +# define TsanHappensBefore(cv) +# define TsanHappensAfter(cv) +# define TsanIgnoreWritesBegin() +# define TsanIgnoreWritesEnd() +#endif + /// \macro LLVM_NO_SANITIZE /// Disable a particular sanitizer for a function. #ifndef LLVM_NO_SANITIZE @@ -451,66 +572,28 @@ /// extern globals, and static globals. /// /// This is essentially an extremely restricted analog to C++11's thread_local -/// support, and uses that when available. However, it falls back on -/// platform-specific or vendor-provided extensions when necessary. These -/// extensions don't support many of the C++11 thread_local's features. You -/// should only use this for PODs that you can statically initialize to -/// some constant value. In almost all circumstances this is most appropriate -/// for use with a pointer, integer, or small aggregation of pointers and -/// integers. -#ifndef LLVM_THREAD_LOCAL -#if __has_feature(cxx_thread_local) +/// support. It uses thread_local if available, falling back on gcc __thread +/// if not. __thread doesn't support many of the C++11 thread_local's +/// features. You should only use this for PODs that you can statically +/// initialize to some constant value. In almost all circumstances this is most +/// appropriate for use with a pointer, integer, or small aggregation of +/// pointers and integers. +#if __has_feature(cxx_thread_local) || defined(_MSC_VER) #define LLVM_THREAD_LOCAL thread_local -#elif defined(_MSC_VER) -// MSVC supports this with a __declspec. -#define LLVM_THREAD_LOCAL __declspec(thread) #else // Clang, GCC, and other compatible compilers used __thread prior to C++11 and // we only need the restricted functionality that provides. #define LLVM_THREAD_LOCAL __thread #endif + +/// \macro LLVM_ENABLE_EXCEPTIONS +/// Whether LLVM is built with exception support. +#if __has_feature(cxx_exceptions) +#define LLVM_ENABLE_EXCEPTIONS 1 +#elif defined(__GNUC__) && defined(__EXCEPTIONS) +#define LLVM_ENABLE_EXCEPTIONS 1 +#elif defined(_MSC_VER) && defined(_CPPUNWIND) +#define LLVM_ENABLE_EXCEPTIONS 1 #endif -namespace wpi { - -/// Allocate a buffer of memory with the given size and alignment. -/// -/// When the compiler supports aligned operator new, this will use it to to -/// handle even over-aligned allocations. -/// -/// However, this doesn't make any attempt to leverage the fancier techniques -/// like posix_memalign due to portability. It is mostly intended to allow -/// compatibility with platforms that, after aligned allocation was added, use -/// reduced default alignment. -inline void *allocate_buffer(size_t Size, size_t Alignment) { - return ::operator new(Size -#ifdef __cpp_aligned_new - , - std::align_val_t(Alignment) -#endif - ); -} - -/// Deallocate a buffer of memory with the given size and alignment. -/// -/// If supported, this will used the sized delete operator. Also if supported, -/// this will pass the alignment to the delete operator. -/// -/// The pointer must have been allocated with the corresponding new operator, -/// most likely using the above helper. -inline void deallocate_buffer(void *Ptr, size_t Size, size_t Alignment) { - ::operator delete(Ptr -#ifdef __cpp_sized_deallocation - , - Size -#endif -#ifdef __cpp_aligned_new - , - std::align_val_t(Alignment) -#endif - ); -} - -} // End namespace wpi - #endif diff --git a/wpiutil/src/main/native/include/wpi/ConvertUTF.h b/wpiutil/src/main/native/include/wpi/ConvertUTF.h index aa113b1526..a5f80468b8 100644 --- a/wpiutil/src/main/native/include/wpi/ConvertUTF.h +++ b/wpiutil/src/main/native/include/wpi/ConvertUTF.h @@ -1,9 +1,8 @@ /*===--- ConvertUTF.h - Universal Character Names conversions ---------------=== * - * The LLVM Compiler Infrastructure - * - * This file is distributed under the University of Illinois Open Source - * License. See LICENSE.TXT for details. + * 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 * *==------------------------------------------------------------------------==*/ /* @@ -87,8 +86,8 @@ ------------------------------------------------------------------------ */ -#ifndef LLVM_SUPPORT_CONVERTUTF_H -#define LLVM_SUPPORT_CONVERTUTF_H +#ifndef WPIUTIL_WPI_CONVERTUTF_H +#define WPIUTIL_WPI_CONVERTUTF_H #include "wpi/span.h" @@ -97,14 +96,11 @@ #include #include -// Wrap everything in namespace wpi so that programs can link with wpiutil and +// Wrap everything in namespace wpi so that programs can link with llvm and // their own version of the unicode libraries. namespace wpi { -template -class SmallVectorImpl; - /* --------------------------------------------------------------------- The following 4 definitions are compiler-specific. The C standard does not guarantee that wchar_t has at least @@ -187,6 +183,38 @@ unsigned getNumBytesForUTF8(UTF8 firstByte); /*************************************************************************/ /* Below are LLVM-specific wrappers of the functions above. */ +template class SmallVectorImpl; + +/** + * Convert an UTF8 string_view to UTF8, UTF16, or UTF32 depending on + * WideCharWidth. The converted data is written to ResultPtr, which needs to + * point to at least WideCharWidth * (Source.Size() + 1) bytes. On success, + * ResultPtr will point one after the end of the copied string. On failure, + * ResultPtr will not be changed, and ErrorPtr will be set to the location of + * the first character which could not be converted. + * \return true on success. + */ +bool ConvertUTF8toWide(unsigned WideCharWidth, std::string_view Source, + char *&ResultPtr, const UTF8 *&ErrorPtr); + +/** +* Converts a UTF-8 string_view to a std::wstring. +* \return true on success. +*/ +bool ConvertUTF8toWide(std::string_view Source, std::wstring &Result); + +/** +* Converts a UTF-8 C-string to a std::wstring. +* \return true on success. +*/ +bool ConvertUTF8toWide(const char *Source, std::wstring &Result); + +/** +* Converts a std::wstring to a UTF-8 encoded std::string. +* \return true on success. +*/ +bool convertWideToUTF8(const std::wstring &Source, SmallVectorImpl &Result); + /** * Convert an Unicode code point to UTF8 sequence. @@ -215,10 +243,10 @@ bool ConvertCodePointToUTF8(unsigned Source, char *&ResultPtr); * * \sa ConvertUTF8toUTF32 */ -static inline ConversionResult convertUTF8Sequence(const UTF8 **source, - const UTF8 *sourceEnd, - UTF32 *target, - ConversionFlags flags) { +inline ConversionResult convertUTF8Sequence(const UTF8 **source, + const UTF8 *sourceEnd, + UTF32 *target, + ConversionFlags flags) { if (*source == sourceEnd) return sourceExhausted; unsigned size = getNumBytesForUTF8(**source); @@ -234,12 +262,22 @@ static inline ConversionResult convertUTF8Sequence(const UTF8 **source, bool hasUTF16ByteOrderMark(span SrcBytes); /** - * Converts a UTF-16 string into a UTF-8 string. + * Converts a stream of raw bytes assumed to be UTF16 into a UTF8 std::string. * + * \param [in] SrcBytes A buffer of what is assumed to be UTF-16 encoded text. + * \param [out] Out Converted UTF-8 is stored here on success. * \returns true on success */ -bool convertUTF16ToUTF8String(span SrcUTF16, - SmallVectorImpl &DstUTF8); +bool convertUTF16ToUTF8String(span SrcBytes, SmallVectorImpl &Out); + +/** +* Converts a UTF16 string into a UTF8 std::string. +* +* \param [in] Src A buffer of UTF-16 encoded text. +* \param [out] Out Converted UTF-8 is stored here on success. +* \returns true on success +*/ +bool convertUTF16ToUTF8String(span Src, SmallVectorImpl &Out); /** * Converts a UTF-8 string into a UTF-16 string with native endianness. diff --git a/wpiutil/src/main/native/include/wpi/DenseMap.h b/wpiutil/src/main/native/include/wpi/DenseMap.h index 12bb71260a..2fb7e953ec 100644 --- a/wpiutil/src/main/native/include/wpi/DenseMap.h +++ b/wpiutil/src/main/native/include/wpi/DenseMap.h @@ -1,9 +1,8 @@ //===- llvm/ADT/DenseMap.h - Dense probed hash table ------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -19,12 +18,14 @@ #include "wpi/AlignOf.h" #include "wpi/Compiler.h" #include "wpi/MathExtras.h" +#include "wpi/MemAlloc.h" #include "wpi/PointerLikeTypeTraits.h" #include "wpi/type_traits.h" #include #include #include #include +#include #include #include #include @@ -38,6 +39,8 @@ namespace detail { // implementation without requiring two members. template struct DenseMapPair : public std::pair { + using std::pair::pair; + KeyT &getFirst() { return std::pair::first; } const KeyT &getFirst() const { return std::pair::first; } ValueT &getSecond() { return std::pair::second; } @@ -113,8 +116,8 @@ public: } const KeyT EmptyKey = getEmptyKey(), TombstoneKey = getTombstoneKey(); - if (isPodLike::value && isPodLike::value) { - // Use a simpler loop when these are trivial types. + if (std::is_trivially_destructible::value) { + // Use a simpler loop when values don't need destruction. for (BucketT *P = getBuckets(), *E = getBucketsEnd(); P != E; ++P) P->getFirst() = EmptyKey; } else { @@ -143,13 +146,15 @@ public: iterator find(const_arg_type_t Val) { BucketT *TheBucket; if (LookupBucketFor(Val, TheBucket)) - return makeIterator(TheBucket, getBucketsEnd(), *this, true); + return makeIterator(TheBucket, getBucketsEnd(), + *this, true); return end(); } const_iterator find(const_arg_type_t Val) const { const BucketT *TheBucket; if (LookupBucketFor(Val, TheBucket)) - return makeConstIterator(TheBucket, getBucketsEnd(), *this, true); + return makeConstIterator(TheBucket, getBucketsEnd(), + *this, true); return end(); } @@ -162,14 +167,16 @@ public: iterator find_as(const LookupKeyT &Val) { BucketT *TheBucket; if (LookupBucketFor(Val, TheBucket)) - return makeIterator(TheBucket, getBucketsEnd(), *this, true); + return makeIterator(TheBucket, getBucketsEnd(), + *this, true); return end(); } template const_iterator find_as(const LookupKeyT &Val) const { const BucketT *TheBucket; if (LookupBucketFor(Val, TheBucket)) - return makeConstIterator(TheBucket, getBucketsEnd(), *this, true); + return makeConstIterator(TheBucket, getBucketsEnd(), + *this, true); return end(); } @@ -203,16 +210,16 @@ public: std::pair try_emplace(KeyT &&Key, Ts &&... Args) { BucketT *TheBucket; if (LookupBucketFor(Key, TheBucket)) - return std::make_pair( - makeIterator(TheBucket, getBucketsEnd(), *this, true), - false); // Already in map. + return std::make_pair(makeIterator(TheBucket, getBucketsEnd(), + *this, true), + false); // Already in map. // Otherwise, insert the new element. TheBucket = InsertIntoBucket(TheBucket, std::move(Key), std::forward(Args)...); - return std::make_pair( - makeIterator(TheBucket, getBucketsEnd(), *this, true), - true); + return std::make_pair(makeIterator(TheBucket, getBucketsEnd(), + *this, true), + true); } // Inserts key,value pair into the map if the key isn't already in the map. @@ -222,15 +229,15 @@ public: std::pair try_emplace(const KeyT &Key, Ts &&... Args) { BucketT *TheBucket; if (LookupBucketFor(Key, TheBucket)) - return std::make_pair( - makeIterator(TheBucket, getBucketsEnd(), *this, true), - false); // Already in map. + return std::make_pair(makeIterator(TheBucket, getBucketsEnd(), + *this, true), + false); // Already in map. // Otherwise, insert the new element. TheBucket = InsertIntoBucket(TheBucket, Key, std::forward(Args)...); - return std::make_pair( - makeIterator(TheBucket, getBucketsEnd(), *this, true), - true); + return std::make_pair(makeIterator(TheBucket, getBucketsEnd(), + *this, true), + true); } /// Alternate version of insert() which allows a different, and possibly @@ -243,16 +250,16 @@ public: const LookupKeyT &Val) { BucketT *TheBucket; if (LookupBucketFor(Val, TheBucket)) - return std::make_pair( - makeIterator(TheBucket, getBucketsEnd(), *this, true), - false); // Already in map. + return std::make_pair(makeIterator(TheBucket, getBucketsEnd(), + *this, true), + false); // Already in map. // Otherwise, insert the new element. TheBucket = InsertIntoBucketWithLookup(TheBucket, std::move(KV.first), std::move(KV.second), Val); - return std::make_pair( - makeIterator(TheBucket, getBucketsEnd(), *this, true), - true); + return std::make_pair(makeIterator(TheBucket, getBucketsEnd(), + *this, true), + true); } /// insert - Range insertion of pairs. @@ -389,7 +396,8 @@ protected: setNumEntries(other.getNumEntries()); setNumTombstones(other.getNumTombstones()); - if (isPodLike::value && isPodLike::value) + if (std::is_trivially_copyable::value && + std::is_trivially_copyable::value) memcpy(reinterpret_cast(getBuckets()), other.getBuckets(), getNumBuckets() * sizeof(BucketT)); else @@ -679,7 +687,7 @@ class DenseMap : public DenseMapBase, unsigned NumBuckets; public: - /// Create a DenseMap wth an optional \p InitialReserve that guarantee that + /// Create a DenseMap with an optional \p InitialReserve that guarantee that /// this number of elements can be inserted in the map without grow() explicit DenseMap(unsigned InitialReserve = 0) { init(InitialReserve); } @@ -706,7 +714,7 @@ public: ~DenseMap() { this->destroyAll(); - operator delete(Buckets); + deallocate_buffer(Buckets, sizeof(BucketT) * NumBuckets, alignof(BucketT)); } void swap(DenseMap& RHS) { @@ -726,7 +734,7 @@ public: DenseMap& operator=(DenseMap &&other) { this->destroyAll(); - operator delete(Buckets); + deallocate_buffer(Buckets, sizeof(BucketT) * NumBuckets, alignof(BucketT)); init(0); swap(other); return *this; @@ -734,7 +742,7 @@ public: void copyFrom(const DenseMap& other) { this->destroyAll(); - operator delete(Buckets); + deallocate_buffer(Buckets, sizeof(BucketT) * NumBuckets, alignof(BucketT)); if (allocateBuckets(other.NumBuckets)) { this->BaseT::copyFrom(other); } else { @@ -767,10 +775,12 @@ public: this->moveFromOldBuckets(OldBuckets, OldBuckets+OldNumBuckets); // Free the old table. - operator delete(OldBuckets); + deallocate_buffer(OldBuckets, sizeof(BucketT) * OldNumBuckets, + alignof(BucketT)); } void shrink_and_clear() { + unsigned OldNumBuckets = NumBuckets; unsigned OldNumEntries = NumEntries; this->destroyAll(); @@ -783,7 +793,8 @@ public: return; } - operator delete(Buckets); + deallocate_buffer(Buckets, sizeof(BucketT) * OldNumBuckets, + alignof(BucketT)); init(NewNumBuckets); } @@ -819,7 +830,8 @@ private: return false; } - Buckets = static_cast(operator new(sizeof(BucketT) * NumBuckets)); + Buckets = static_cast( + allocate_buffer(sizeof(BucketT) * NumBuckets, alignof(BucketT))); return true; } }; @@ -874,6 +886,9 @@ public: this->insert(I, E); } + SmallDenseMap(std::initializer_list Vals) + : SmallDenseMap(Vals.begin(), Vals.end()) {} + ~SmallDenseMap() { this->destroyAll(); deallocateBuckets(); @@ -904,7 +919,7 @@ public: std::swap(*LHSB, *RHSB); continue; } - // Swap separately and handle any assymetry. + // Swap separately and handle any asymmetry. std::swap(LHSB->getFirst(), RHSB->getFirst()); if (hasLHSValue) { ::new (&RHSB->getSecond()) ValueT(std::move(LHSB->getSecond())); @@ -986,16 +1001,13 @@ public: } void grow(unsigned AtLeast) { - if (AtLeast >= InlineBuckets) + if (AtLeast > InlineBuckets) AtLeast = std::max(64, NextPowerOf2(AtLeast-1)); if (Small) { - if (AtLeast < InlineBuckets) - return; // Nothing to do. - // First move the inline buckets into a temporary storage. AlignedCharArrayUnion TmpStorage; - BucketT *TmpBegin = reinterpret_cast(TmpStorage.buffer); + BucketT *TmpBegin = reinterpret_cast(&TmpStorage); BucketT *TmpEnd = TmpBegin; // Loop over the buckets, moving non-empty, non-tombstones into the @@ -1015,10 +1027,13 @@ public: P->getFirst().~KeyT(); } - // Now make this map use the large rep, and move all the entries back - // into it. - Small = false; - new (getLargeRep()) LargeRep(allocateBuckets(AtLeast)); + // AtLeast == InlineBuckets can happen if there are many tombstones, + // and grow() is used to remove them. Usually we always switch to the + // large rep here. + if (AtLeast > InlineBuckets) { + Small = false; + new (getLargeRep()) LargeRep(allocateBuckets(AtLeast)); + } this->moveFromOldBuckets(TmpBegin, TmpEnd); return; } @@ -1034,7 +1049,8 @@ public: this->moveFromOldBuckets(OldRep.Buckets, OldRep.Buckets+OldRep.NumBuckets); // Free the old table. - operator delete(OldRep.Buckets); + deallocate_buffer(OldRep.Buckets, sizeof(BucketT) * OldRep.NumBuckets, + alignof(BucketT)); } void shrink_and_clear() { @@ -1081,8 +1097,8 @@ private: assert(Small); // Note that this cast does not violate aliasing rules as we assert that // the memory's dynamic type is the small, inline bucket buffer, and the - // 'storage.buffer' static type is 'char *'. - return reinterpret_cast(storage.buffer); + // 'storage' is a POD containing a char buffer. + return reinterpret_cast(&storage); } BucketT *getInlineBuckets() { @@ -1093,7 +1109,7 @@ private: const LargeRep *getLargeRep() const { assert(!Small); // Note, same rule about aliasing as with getInlineBuckets. - return reinterpret_cast(storage.buffer); + return reinterpret_cast(&storage); } LargeRep *getLargeRep() { @@ -1118,15 +1134,17 @@ private: if (Small) return; - operator delete(getLargeRep()->Buckets); + deallocate_buffer(getLargeRep()->Buckets, + sizeof(BucketT) * getLargeRep()->NumBuckets, + alignof(BucketT)); getLargeRep()->~LargeRep(); } LargeRep allocateBuckets(unsigned Num) { assert(Num > InlineBuckets && "Must allocate more buckets than are inline"); - LargeRep Rep = { - static_cast(operator new(sizeof(BucketT) * Num)), Num - }; + LargeRep Rep = {static_cast(allocate_buffer( + sizeof(BucketT) * Num, alignof(BucketT))), + Num}; return Rep; } }; @@ -1137,8 +1155,6 @@ class DenseMapIterator : DebugEpochBase::HandleBase { friend class DenseMapIterator; friend class DenseMapIterator; - using ConstIterator = DenseMapIterator; - public: using difference_type = ptrdiff_t; using value_type = @@ -1167,7 +1183,7 @@ public: // for const iterator destinations so it doesn't end up as a user defined copy // constructor. template ::type> + typename = std::enable_if_t> DenseMapIterator( const DenseMapIterator &I) : DebugEpochBase::HandleBase(I), Ptr(I.Ptr), End(I.End) {} @@ -1181,19 +1197,18 @@ public: return Ptr; } - bool operator==(const ConstIterator &RHS) const { - assert((!Ptr || isHandleInSync()) && "handle not in sync!"); + friend bool operator==(const DenseMapIterator &LHS, + const DenseMapIterator &RHS) { + assert((!LHS.Ptr || LHS.isHandleInSync()) && "handle not in sync!"); assert((!RHS.Ptr || RHS.isHandleInSync()) && "handle not in sync!"); - assert(getEpochAddress() == RHS.getEpochAddress() && + assert(LHS.getEpochAddress() == RHS.getEpochAddress() && "comparing incomparable iterators!"); - return Ptr == RHS.Ptr; + return LHS.Ptr == RHS.Ptr; } - bool operator!=(const ConstIterator &RHS) const { - assert((!Ptr || isHandleInSync()) && "handle not in sync!"); - assert((!RHS.Ptr || RHS.isHandleInSync()) && "handle not in sync!"); - assert(getEpochAddress() == RHS.getEpochAddress() && - "comparing incomparable iterators!"); - return Ptr != RHS.Ptr; + + friend bool operator!=(const DenseMapIterator &LHS, + const DenseMapIterator &RHS) { + return !(LHS == RHS); } inline DenseMapIterator& operator++() { // Preincrement @@ -1236,4 +1251,4 @@ inline size_t capacity_in_bytes(const DenseMap &X) { } // end namespace wpi -#endif // LLVM_ADT_DENSEMAP_H +#endif // WPIUTIL_WPI_DENSEMAP_H diff --git a/wpiutil/src/main/native/include/wpi/DenseMapInfo.h b/wpiutil/src/main/native/include/wpi/DenseMapInfo.h index b6525a32c4..54b2c2cb3f 100644 --- a/wpiutil/src/main/native/include/wpi/DenseMapInfo.h +++ b/wpiutil/src/main/native/include/wpi/DenseMapInfo.h @@ -1,9 +1,8 @@ //===- llvm/ADT/DenseMapInfo.h - Type traits for DenseMap -------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -15,16 +14,31 @@ #define WPIUTIL_WPI_DENSEMAPINFO_H #include "wpi/Hashing.h" -#include "wpi/PointerLikeTypeTraits.h" -#include "wpi/span.h" #include #include #include -#include #include namespace wpi { +namespace detail { + +/// Simplistic combination of 32-bit hash values into 32-bit hash values. +static inline unsigned combineHashValue(unsigned a, unsigned b) { + uint64_t key = (uint64_t)a << 32 | (uint64_t)b; + key += ~(key << 32); + key ^= (key >> 22); + key += ~(key << 13); + key ^= (key >> 8); + key += (key << 3); + key ^= (key >> 15); + key += ~(key << 27); + key ^= (key >> 31); + return (unsigned)key; +} + +} // end namespace detail + template struct DenseMapInfo { //static inline T getEmptyKey(); @@ -33,18 +47,28 @@ struct DenseMapInfo { //static bool isEqual(const T &LHS, const T &RHS); }; -// Provide DenseMapInfo for all pointers. +// Provide DenseMapInfo for all pointers. Come up with sentinel pointer values +// that are aligned to alignof(T) bytes, but try to avoid requiring T to be +// complete. This allows clients to instantiate DenseMap with forward +// declared key types. Assume that no pointer key type requires more than 4096 +// bytes of alignment. template struct DenseMapInfo { + // The following should hold, but it would require T to be complete: + // static_assert(alignof(T) <= (1 << Log2MaxAlign), + // "DenseMap does not support pointer keys requiring more than " + // "Log2MaxAlign bits of alignment"); + static constexpr uintptr_t Log2MaxAlign = 12; + static inline T* getEmptyKey() { uintptr_t Val = static_cast(-1); - Val <<= PointerLikeTypeTraits::NumLowBitsAvailable; + Val <<= Log2MaxAlign; return reinterpret_cast(Val); } static inline T* getTombstoneKey() { uintptr_t Val = static_cast(-2); - Val <<= PointerLikeTypeTraits::NumLowBitsAvailable; + Val <<= Log2MaxAlign; return reinterpret_cast(Val); } @@ -67,6 +91,17 @@ template<> struct DenseMapInfo { } }; +// Provide DenseMapInfo for unsigned chars. +template <> struct DenseMapInfo { + static inline unsigned char getEmptyKey() { return ~0; } + static inline unsigned char getTombstoneKey() { return ~0 - 1; } + static unsigned getHashValue(const unsigned char &Val) { return Val * 37U; } + + static bool isEqual(const unsigned char &LHS, const unsigned char &RHS) { + return LHS == RHS; + } +}; + // Provide DenseMapInfo for unsigned shorts. template <> struct DenseMapInfo { static inline unsigned short getEmptyKey() { return 0xFFFF; } @@ -187,17 +222,8 @@ struct DenseMapInfo> { } static unsigned getHashValue(const Pair& PairVal) { - uint64_t key = (uint64_t)FirstInfo::getHashValue(PairVal.first) << 32 - | (uint64_t)SecondInfo::getHashValue(PairVal.second); - key += ~(key << 32); - key ^= (key >> 22); - key += ~(key << 13); - key ^= (key >> 8); - key += (key << 3); - key ^= (key >> 15); - key += ~(key << 27); - key ^= (key >> 31); - return (unsigned)key; + return detail::combineHashValue(FirstInfo::getHashValue(PairVal.first), + SecondInfo::getHashValue(PairVal.second)); } static bool isEqual(const Pair &LHS, const Pair &RHS) { @@ -206,59 +232,53 @@ struct DenseMapInfo> { } }; -// Provide DenseMapInfo for std::string_view. -template <> struct DenseMapInfo { - static inline std::string_view getEmptyKey() { - return std::string_view(reinterpret_cast(~static_cast(0)), - 0); +// Provide DenseMapInfo for all tuples whose members have info. +template struct DenseMapInfo> { + using Tuple = std::tuple; + + static inline Tuple getEmptyKey() { + return Tuple(DenseMapInfo::getEmptyKey()...); } - static inline std::string_view getTombstoneKey() { - return std::string_view(reinterpret_cast(~static_cast(1)), - 0); + static inline Tuple getTombstoneKey() { + return Tuple(DenseMapInfo::getTombstoneKey()...); } - static unsigned getHashValue(std::string_view Val) { - assert(Val.data() != getEmptyKey().data() && "Cannot hash the empty key!"); - assert(Val.data() != getTombstoneKey().data() && - "Cannot hash the tombstone key!"); - return (unsigned)(hash_value(Val)); + template + static unsigned getHashValueImpl(const Tuple &values, std::false_type) { + using EltType = typename std::tuple_element::type; + std::integral_constant atEnd; + return detail::combineHashValue( + DenseMapInfo::getHashValue(std::get(values)), + getHashValueImpl(values, atEnd)); } - static bool isEqual(std::string_view LHS, std::string_view RHS) { - if (RHS.data() == getEmptyKey().data()) - return LHS.data() == getEmptyKey().data(); - if (RHS.data() == getTombstoneKey().data()) - return LHS.data() == getTombstoneKey().data(); - return LHS == RHS; - } -}; - -// Provide DenseMapInfo for spans. -template struct DenseMapInfo> { - static inline span getEmptyKey() { - return span(reinterpret_cast(~static_cast(0)), - size_t(0)); + template + static unsigned getHashValueImpl(const Tuple &, std::true_type) { + return 0; } - static inline span getTombstoneKey() { - return span(reinterpret_cast(~static_cast(1)), - size_t(0)); + static unsigned getHashValue(const std::tuple &values) { + std::integral_constant atEnd; + return getHashValueImpl<0>(values, atEnd); } - static unsigned getHashValue(span Val) { - assert(Val.data() != getEmptyKey().data() && "Cannot hash the empty key!"); - assert(Val.data() != getTombstoneKey().data() && - "Cannot hash the tombstone key!"); - return (unsigned)(hash_value(Val)); + template + static bool isEqualImpl(const Tuple &lhs, const Tuple &rhs, std::false_type) { + using EltType = typename std::tuple_element::type; + std::integral_constant atEnd; + return DenseMapInfo::isEqual(std::get(lhs), std::get(rhs)) && + isEqualImpl(lhs, rhs, atEnd); } - static bool isEqual(span LHS, span RHS) { - if (RHS.data() == getEmptyKey().data()) - return LHS.data() == getEmptyKey().data(); - if (RHS.data() == getTombstoneKey().data()) - return LHS.data() == getTombstoneKey().data(); - return LHS == RHS; + template + static bool isEqualImpl(const Tuple &, const Tuple &, std::true_type) { + return true; + } + + static bool isEqual(const Tuple &lhs, const Tuple &rhs) { + std::integral_constant atEnd; + return isEqualImpl<0>(lhs, rhs, atEnd); } }; @@ -271,4 +291,4 @@ template <> struct DenseMapInfo { } // end namespace wpi -#endif // LLVM_ADT_DENSEMAPINFO_H +#endif // WPIUTIL_WPI_DENSEMAPINFO_H diff --git a/wpiutil/src/main/native/include/wpi/Endian.h b/wpiutil/src/main/native/include/wpi/Endian.h index b31cb2d6da..d243a13c4f 100644 --- a/wpiutil/src/main/native/include/wpi/Endian.h +++ b/wpiutil/src/main/native/include/wpi/Endian.h @@ -1,9 +1,8 @@ //===- Endian.h - Utilities for IO with endian specific data ----*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -14,14 +13,8 @@ #ifndef WPIUTIL_WPI_ENDIAN_H #define WPIUTIL_WPI_ENDIAN_H -#include "wpi/AlignOf.h" #include "wpi/Compiler.h" #include "wpi/SwapByteOrder.h" - -#if defined(__linux__) || defined(__GNU__) -#include -#endif - #include #include #include @@ -38,7 +31,7 @@ enum {aligned = 0, unaligned = 1}; namespace detail { -// value is either alignment, or alignof(T) if alignment is 0. +/// ::value is either alignment, or alignof(T) if alignment is 0. template struct PickAlignment { enum { value = alignment == 0 ? alignof(T) : alignment }; @@ -49,13 +42,7 @@ struct PickAlignment { namespace endian { constexpr endianness system_endianness() { -#ifdef _WIN32 - return little; -#elif defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && __BYTE_ORDER == __BIG_ENDIAN - return big; -#else - return little; -#endif + return sys::IsBigEndianHost ? big : little; } template @@ -89,13 +76,7 @@ template inline value_type read(const void *memory) { - value_type ret; - - memcpy(&ret, - LLVM_ASSUME_ALIGNED( - memory, (detail::PickAlignment::value)), - sizeof(value_type)); - return byte_swap(ret); + return read(memory, endian); } /// Read a value of a particular endianness from a buffer, and increment the @@ -110,9 +91,7 @@ inline value_type readNext(const CharT *&memory, endianness endian) { template inline value_type readNext(const CharT *&memory) { - value_type ret = read(memory); - memory += sizeof(value_type); - return ret; + return readNext(memory, endian); } /// Write a value to memory with a particular endianness. @@ -128,12 +107,12 @@ template inline void write(void *memory, value_type value) { - value = byte_swap(value); - memcpy(LLVM_ASSUME_ALIGNED( - memory, (detail::PickAlignment::value)), - &value, sizeof(value_type)); + write(memory, value, endian); } +template +using make_unsigned_t = std::make_unsigned_t; + /// Read a value of a particular endianness from memory, for a location /// that starts at the given bit offset within the first byte. template @@ -152,15 +131,15 @@ inline value_type readAtBitAlignment(const void *memory, uint64_t startBit) { val[1] = byte_swap(val[1]); // Shift bits from the lower value into place. - std::make_unsigned_t lowerVal = val[0] >> startBit; + make_unsigned_t lowerVal = val[0] >> startBit; // Mask off upper bits after right shift in case of signed type. - std::make_unsigned_t numBitsFirstVal = + make_unsigned_t numBitsFirstVal = (sizeof(value_type) * 8) - startBit; - lowerVal &= ((std::make_unsigned_t)1 << numBitsFirstVal) - 1; + lowerVal &= ((make_unsigned_t)1 << numBitsFirstVal) - 1; // Get the bits from the upper value. - std::make_unsigned_t upperVal = - val[1] & (((std::make_unsigned_t)1 << startBit) - 1); + make_unsigned_t upperVal = + val[1] & (((make_unsigned_t)1 << startBit) - 1); // Shift them in to place. upperVal <<= numBitsFirstVal; @@ -188,15 +167,15 @@ inline void writeAtBitAlignment(void *memory, value_type value, // Mask off any existing bits in the upper part of the lower value that // we want to replace. - val[0] &= ((std::make_unsigned_t)1 << startBit) - 1; - std::make_unsigned_t numBitsFirstVal = + val[0] &= ((make_unsigned_t)1 << startBit) - 1; + make_unsigned_t numBitsFirstVal = (sizeof(value_type) * 8) - startBit; - std::make_unsigned_t lowerVal = value; + make_unsigned_t lowerVal = value; if (startBit > 0) { // Mask off the upper bits in the new value that are not going to go into // the lower value. This avoids a left shift of a negative value, which // is undefined behavior. - lowerVal &= (((std::make_unsigned_t)1 << numBitsFirstVal) - 1); + lowerVal &= (((make_unsigned_t)1 << numBitsFirstVal) - 1); // Now shift the new bits into place lowerVal <<= startBit; } @@ -204,11 +183,11 @@ inline void writeAtBitAlignment(void *memory, value_type value, // Mask off any existing bits in the lower part of the upper value that // we want to replace. - val[1] &= ~(((std::make_unsigned_t)1 << startBit) - 1); + val[1] &= ~(((make_unsigned_t)1 << startBit) - 1); // Next shift the bits that go into the upper value into position. - std::make_unsigned_t upperVal = value >> numBitsFirstVal; + make_unsigned_t upperVal = value >> numBitsFirstVal; // Mask off upper bits after right shift in case of signed type. - upperVal &= ((std::make_unsigned_t)1 << startBit) - 1; + upperVal &= ((make_unsigned_t)1 << startBit) - 1; val[1] |= upperVal; // Finally, rewrite values. @@ -224,10 +203,13 @@ inline void writeAtBitAlignment(void *memory, value_type value, namespace detail { -template +template ::value> struct packed_endian_specific_integral { + using value_type = ValueType; + static constexpr endianness endian = Endian; + static constexpr std::size_t alignment = Alignment; + packed_endian_specific_integral() = default; explicit packed_endian_specific_integral(value_type val) { *this = val; } @@ -263,8 +245,9 @@ struct packed_endian_specific_integral { } private: - AlignedCharArray::value, - sizeof(value_type)> Value; + struct { + alignas(ALIGN) char buffer[sizeof(value_type)]; + } Value; public: struct ref { @@ -355,6 +338,17 @@ using unaligned_int32_t = using unaligned_int64_t = detail::packed_endian_specific_integral; +template +using little_t = detail::packed_endian_specific_integral; +template +using big_t = detail::packed_endian_specific_integral; + +template +using aligned_little_t = + detail::packed_endian_specific_integral; +template +using aligned_big_t = detail::packed_endian_specific_integral; + namespace endian { template inline T read(const void *P, endianness E) { diff --git a/wpiutil/src/main/native/include/wpi/EpochTracker.h b/wpiutil/src/main/native/include/wpi/EpochTracker.h index b26800b5e3..4a84822468 100644 --- a/wpiutil/src/main/native/include/wpi/EpochTracker.h +++ b/wpiutil/src/main/native/include/wpi/EpochTracker.h @@ -1,9 +1,8 @@ //===- llvm/ADT/EpochTracker.h - ADT epoch tracking --------------*- C++ -*-==// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -13,29 +12,15 @@ // //===----------------------------------------------------------------------===// -#ifndef WPIUTIL_WPI_EPOCH_TRACKER_H -#define WPIUTIL_WPI_EPOCH_TRACKER_H +#ifndef WPIUTIL_WPI_EPOCHTRACKER_H +#define WPIUTIL_WPI_EPOCHTRACKER_H + #include namespace wpi { -#ifdef NDEBUG //ifndef LLVM_ENABLE_ABI_BREAKING_CHECKS - -class DebugEpochBase { -public: - void incrementEpoch() {} - - class HandleBase { - public: - HandleBase() = default; - explicit HandleBase(const DebugEpochBase *) {} - bool isHandleInSync() const { return true; } - const void *getEpochAddress() const { return nullptr; } - }; -}; - -#else +#ifndef NDEBUG //ifndef LLVM_ENABLE_ABI_BREAKING_CHECKS /// A base class for data structure classes wishing to make iterators /// ("handles") pointing into themselves fail-fast. When building without @@ -90,6 +75,21 @@ public: }; }; +#else + +class DebugEpochBase { +public: + void incrementEpoch() {} + + class HandleBase { + public: + HandleBase() = default; + explicit HandleBase(const DebugEpochBase *) {} + bool isHandleInSync() const { return true; } + const void *getEpochAddress() const { return nullptr; } + }; +}; + #endif // LLVM_ENABLE_ABI_BREAKING_CHECKS } // namespace wpi diff --git a/wpiutil/src/main/native/include/wpi/Errc.h b/wpiutil/src/main/native/include/wpi/Errc.h index ebce58ada7..dee28dfdc1 100644 --- a/wpiutil/src/main/native/include/wpi/Errc.h +++ b/wpiutil/src/main/native/include/wpi/Errc.h @@ -1,9 +1,8 @@ -//===- llvm/Support/Errc.h - Defines the llvm::errc enum --------*- C++ -*-===// +//===- llvm/Support/Errc.h - Defines the wpi::errc enum --------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -11,7 +10,7 @@ // some problems with std::errc that can be avoided by using our own // enumeration: // -// * std::errc is a namespace in some implementations. That meas that ADL +// * std::errc is a namespace in some implementations. That means that ADL // doesn't work and it is sometimes necessary to write std::make_error_code // or in templates: // using std::make_error_code; @@ -23,7 +22,7 @@ // the intersection of all the ones we support. // // * std::errc is just marked with is_error_condition_enum. This means that -// common patters like AnErrorCode == errc::no_such_file_or_directory take +// common patterns like AnErrorCode == errc::no_such_file_or_directory take // 4 virtual calls instead of two comparisons. //===----------------------------------------------------------------------===// diff --git a/wpiutil/src/main/native/include/wpi/Errno.h b/wpiutil/src/main/native/include/wpi/Errno.h index 042d43212b..febfc372a9 100644 --- a/wpiutil/src/main/native/include/wpi/Errno.h +++ b/wpiutil/src/main/native/include/wpi/Errno.h @@ -1,9 +1,8 @@ //===- llvm/Support/Errno.h - Portable+convenient errno handling -*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -22,8 +21,8 @@ namespace wpi { namespace sys { template -inline auto RetryAfterSignal(const FailT &Fail, const Fun &F, - const Args &... As) -> decltype(F(As...)) { +inline decltype(auto) RetryAfterSignal(const FailT &Fail, const Fun &F, + const Args &... As) { decltype(F(As...)) Res; do { errno = 0; @@ -35,4 +34,4 @@ inline auto RetryAfterSignal(const FailT &Fail, const Fun &F, } // namespace sys } // namespace wpi -#endif // WPIUTIL_WPI_ERRNO_H +#endif // WPIUTIL_WPI_ERRNO_H diff --git a/wpiutil/src/main/native/include/wpi/ErrorHandling.h b/wpiutil/src/main/native/include/wpi/ErrorHandling.h index da9af4b178..90d87d9472 100644 --- a/wpiutil/src/main/native/include/wpi/ErrorHandling.h +++ b/wpiutil/src/main/native/include/wpi/ErrorHandling.h @@ -1,9 +1,8 @@ //===- llvm/Support/ErrorHandling.h - Fatal error handling ------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -20,6 +19,7 @@ #include namespace wpi { + /// An error handler callback. typedef void (*fatal_error_handler_t)(void *user_data, const std::string& reason, @@ -65,7 +65,7 @@ namespace wpi { /// /// If no error handler is installed the default is to print the message to /// standard error, followed by a newline. -/// After the error handler is called this function will call exit(1), it +/// After the error handler is called this function will call abort(), it /// does not return. LLVM_ATTRIBUTE_NORETURN void report_fatal_error(const char *reason, bool gen_crash_diag = true); @@ -100,31 +100,32 @@ void install_out_of_memory_new_handler(); /// Reports a bad alloc error, calling any user defined bad alloc /// error handler. In contrast to the generic 'report_fatal_error' -/// functions, this function is expected to return, e.g. the user -/// defined error handler throws an exception. +/// functions, this function might not terminate, e.g. the user +/// defined error handler throws an exception, but it won't return. /// /// Note: When throwing an exception in the bad alloc handler, make sure that /// the following unwind succeeds, e.g. do not trigger additional allocations /// in the unwind chain. /// -/// If no error handler is installed (default), then a bad_alloc exception -/// is thrown, if LLVM is compiled with exception support, otherwise an -/// assertion is called. -void report_bad_alloc_error(const char *Reason, bool GenCrashDiag = true); +/// If no error handler is installed (default), throws a bad_alloc exception +/// if LLVM is compiled with exception support. Otherwise prints the error +/// to standard error and calls abort(). +LLVM_ATTRIBUTE_NORETURN void report_bad_alloc_error(const char *Reason, + bool GenCrashDiag = true); /// This function calls abort(), and prints the optional message to stderr. /// Use the wpi_unreachable macro (that adds location info), instead of /// calling this function directly. LLVM_ATTRIBUTE_NORETURN void wpi_unreachable_internal(const char *msg = nullptr, const char *file = nullptr, - unsigned line = 0); + unsigned line = 0); } /// Marks that the current location is not supposed to be reachable. /// In !NDEBUG builds, prints the message and location info to stderr. /// In NDEBUG builds, becomes an optimizer hint that the current location /// is not supposed to be reachable. On compilers that don't support -/// such hints, prints a reduced message instead. +/// such hints, prints a reduced message instead and aborts the program. /// /// Use this instead of assert(0). It conveys intent more clearly and /// allows compilers to omit some unnecessary code. diff --git a/wpiutil/src/main/native/include/wpi/FunctionExtras.h b/wpiutil/src/main/native/include/wpi/FunctionExtras.h index 57bc8dcffc..ce80ee2388 100644 --- a/wpiutil/src/main/native/include/wpi/FunctionExtras.h +++ b/wpiutil/src/main/native/include/wpi/FunctionExtras.h @@ -1,9 +1,8 @@ //===- FunctionExtras.h - Function type erasure utilities -------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// /// \file @@ -12,11 +11,11 @@ /// in ``. /// /// It provides `unique_function`, which works like `std::function` but supports -/// move-only callable objects. +/// move-only callable objects and const-qualification. /// /// Future plans: -/// - Add a `function` that provides const, volatile, and ref-qualified support, -/// which doesn't work with `std::function`. +/// - Add a `function` that provides ref-qualified support, which doesn't work +/// with `std::function`. /// - Provide support for specifying multiple signatures to type erase callable /// objects with an overload set, such as those produced by generic lambdas. /// - Expand to include a copyable utility that directly replaces std::function @@ -30,17 +29,29 @@ /// //===----------------------------------------------------------------------===// -#ifndef WPIUTIL_WPI_FUNCTION_EXTRAS_H -#define WPIUTIL_WPI_FUNCTION_EXTRAS_H +#ifndef WPIUTIL_WPI_FUNCTIONEXTRAS_H +#define WPIUTIL_WPI_FUNCTIONEXTRAS_H -#include "wpi/Compiler.h" #include "wpi/PointerIntPair.h" #include "wpi/PointerUnion.h" +#include "wpi/STLForwardCompat.h" +#include "wpi/MemAlloc.h" +#include "wpi/type_traits.h" #include #include namespace wpi { +/// unique_function is a type-erasing functor similar to std::function. +/// +/// It can hold move-only function objects, like lambdas capturing unique_ptrs. +/// Accordingly, it is movable but not copyable. +/// +/// It supports const-qualification: +/// - unique_function has a const operator(). +/// It can only hold functions which themselves have a const operator(). +/// - unique_function has a non-const operator(). +/// It can hold functions with a non-const operator(), like mutable lambdas. template class unique_function; // GCC warns on OutOfLineStorage @@ -49,16 +60,32 @@ template class unique_function; #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" #endif -template -class unique_function { +namespace detail { + +template +using EnableIfTrivial = + std::enable_if_t::value && + std::is_trivially_destructible::value>; +template +using EnableUnlessSameType = + std::enable_if_t, ThisT>::value>; +template +using EnableIfCallable = + std::enable_if_t::value || + std::is_convertible()( + std::declval()...)), + Ret>::value>; + +template class UniqueFunctionBase { +protected: static constexpr size_t InlineStorageSize = sizeof(void *) * 4; - // MSVC has a bug and ICEs if we give it a particular dependent value - // expression as part of the `std::conditional` below. To work around this, - // we build that into a template struct's constexpr bool. - template struct IsSizeLessThanThresholdT { - static constexpr bool value = sizeof(T) <= (2 * sizeof(void *)); - }; + template + struct IsSizeLessThanThresholdT : std::false_type {}; + + template + struct IsSizeLessThanThresholdT< + T, std::enable_if_t> : std::true_type {}; // Provide a type function to map parameters that won't observe extra copies // or moves and which are small enough to likely pass in register to values @@ -69,13 +96,24 @@ class unique_function { // The heuristic used is related to common ABI register passing conventions. // It doesn't have to be exact though, and in one way it is more strict // because we want to still be able to observe either moves *or* copies. + template struct AdjustedParamTBase { + static_assert(!std::is_reference::value, + "references should be handled by template specialization"); + using type = typename std::conditional< + wpi::is_trivially_copy_constructible::value && + wpi::is_trivially_move_constructible::value && + IsSizeLessThanThresholdT::value, + T, T &>::type; + }; + + // This specialization ensures that 'AdjustedParam&>' or + // 'AdjustedParam&&>' does not trigger a compile-time error when 'T' is + // an incomplete type and V a templated type. + template struct AdjustedParamTBase { using type = T &; }; + template struct AdjustedParamTBase { using type = T &; }; + template - using AdjustedParamT = typename std::conditional< - !std::is_reference::value && - std::is_trivially_copy_constructible::value && - std::is_trivially_move_constructible::value && - IsSizeLessThanThresholdT::value, - T, T &>::type; + using AdjustedParamT = typename AdjustedParamTBase::type; // The type of the erased function pointer we use as a callback to dispatch to // the stored callable when it is trivial to move and destroy. @@ -120,8 +158,11 @@ class unique_function { // For in-line storage, we just provide an aligned character buffer. We // provide four pointers worth of storage here. - typename std::aligned_storage::type - InlineStorage; + // This is mutable as an inlined `const unique_function` may + // still modify its own mutable members. + mutable + typename std::aligned_storage::type + InlineStorage; } StorageUnion; // A compressed pointer to either our dispatching callback or our table of @@ -144,11 +185,25 @@ class unique_function { .template get(); } - void *getInlineStorage() { return &StorageUnion.InlineStorage; } + CallPtrT getCallPtr() const { + return isTrivialCallback() ? getTrivialCallback() + : getNonTrivialCallbacks()->CallPtr; + } - void *getOutOfLineStorage() { + // These three functions are only const in the narrow sense. They return + // mutable pointers to function state. + // This allows unique_function::operator() to be const, even if the + // underlying functor may be internally mutable. + // + // const callers must ensure they're only used in const-correct ways. + void *getCalleePtr() const { + return isInlineStorage() ? getInlineStorage() : getOutOfLineStorage(); + } + void *getInlineStorage() const { return &StorageUnion.InlineStorage; } + void *getOutOfLineStorage() const { return StorageUnion.OutOfLineStorage.StoragePtr; } + size_t getOutOfLineStorageSize() const { return StorageUnion.OutOfLineStorage.Size; } @@ -160,10 +215,11 @@ class unique_function { StorageUnion.OutOfLineStorage = {Ptr, Size, Alignment}; } - template - static ReturnT CallImpl(void *CallableAddr, AdjustedParamT... Params) { - return (*reinterpret_cast(CallableAddr))( - std::forward(Params)...); + template + static ReturnT CallImpl(void *CallableAddr, + AdjustedParamT... Params) { + auto &Func = *reinterpret_cast(CallableAddr); + return Func(std::forward(Params)...); } template @@ -177,11 +233,54 @@ class unique_function { reinterpret_cast(CallableAddr)->~CallableT(); } -public: - unique_function() = default; - unique_function(std::nullptr_t /*null_callable*/) {} + // The pointers to call/move/destroy functions are determined for each + // callable type (and called-as type, which determines the overload chosen). + // (definitions are out-of-line). - ~unique_function() { + // By default, we need an object that contains all the different + // type erased behaviors needed. Create a static instance of the struct type + // here and each instance will contain a pointer to it. + // Wrap in a struct to avoid https://gcc.gnu.org/PR71954 + template + struct CallbacksHolder { + static NonTrivialCallbacks Callbacks; + }; + // See if we can create a trivial callback. We need the callable to be + // trivially moved and trivially destroyed so that we don't have to store + // type erased callbacks for those operations. + template + struct CallbacksHolder> { + static TrivialCallback Callbacks; + }; + + // A simple tag type so the call-as type to be passed to the constructor. + template struct CalledAs {}; + + // Essentially the "main" unique_function constructor, but subclasses + // provide the qualified type to be used for the call. + // (We always store a T, even if the call will use a pointer to const T). + template + UniqueFunctionBase(CallableT Callable, CalledAs) { + bool IsInlineStorage = true; + void *CallableAddr = getInlineStorage(); + if (sizeof(CallableT) > InlineStorageSize || + alignof(CallableT) > alignof(decltype(StorageUnion.InlineStorage))) { + IsInlineStorage = false; + // Allocate out-of-line storage. FIXME: Use an explicit alignment + // parameter in C++17 mode. + auto Size = sizeof(CallableT); + auto Alignment = alignof(CallableT); + CallableAddr = allocate_buffer(Size, Alignment); + setOutOfLineStorage(CallableAddr, Size, Alignment); + } + + // Now move into the storage. + new (CallableAddr) CallableT(std::move(Callable)); + CallbackAndInlineFlag.setPointerAndInt( + &CallbacksHolder::Callbacks, IsInlineStorage); + } + + ~UniqueFunctionBase() { if (!CallbackAndInlineFlag.getPointer()) return; @@ -197,7 +296,7 @@ public: getOutOfLineStorageAlignment()); } - unique_function(unique_function &&RHS) noexcept { + UniqueFunctionBase(UniqueFunctionBase &&RHS) noexcept { // Copy the callback and inline flag. CallbackAndInlineFlag = RHS.CallbackAndInlineFlag; @@ -226,83 +325,96 @@ public: #endif } - unique_function &operator=(unique_function &&RHS) noexcept { + UniqueFunctionBase &operator=(UniqueFunctionBase &&RHS) noexcept { if (this == &RHS) return *this; // Because we don't try to provide any exception safety guarantees we can // implement move assignment very simply by first destroying the current // object and then move-constructing over top of it. - this->~unique_function(); - new (this) unique_function(std::move(RHS)); + this->~UniqueFunctionBase(); + new (this) UniqueFunctionBase(std::move(RHS)); return *this; } - template - unique_function(CallableT Callable, - std::enable_if_t< - std::is_invocable_r_v< - ReturnT, CallableT, ParamTs...>>* = nullptr) { - bool IsInlineStorage = true; - void *CallableAddr = getInlineStorage(); - if (sizeof(CallableT) > InlineStorageSize || - alignof(CallableT) > alignof(decltype(StorageUnion.InlineStorage))) { - IsInlineStorage = false; - // Allocate out-of-line storage. FIXME: Use an explicit alignment - // parameter in C++17 mode. - auto Size = sizeof(CallableT); - auto Alignment = alignof(CallableT); - CallableAddr = allocate_buffer(Size, Alignment); - setOutOfLineStorage(CallableAddr, Size, Alignment); - } - - // Now move into the storage. - new (CallableAddr) CallableT(std::move(Callable)); - - // See if we can create a trivial callback. We need the callable to be - // trivially moved and trivially destroyed so that we don't have to store - // type erased callbacks for those operations. - // - // FIXME: We should use constexpr if here and below to avoid instantiating - // the non-trivial static objects when unnecessary. While the linker should - // remove them, it is still wasteful. - if (std::is_trivially_move_constructible::value && - std::is_trivially_destructible::value) { - // We need to create a nicely aligned object. We use a static variable - // for this because it is a trivial struct. - static TrivialCallback Callback = { &CallImpl }; - - CallbackAndInlineFlag = {&Callback, IsInlineStorage}; - return; - } - - // Otherwise, we need to point at an object that contains all the different - // type erased behaviors needed. Create a static instance of the struct type - // here and then use a pointer to that. - static NonTrivialCallbacks Callbacks = { - &CallImpl, &MoveImpl, &DestroyImpl}; - - CallbackAndInlineFlag = {&Callbacks, IsInlineStorage}; - } - - ReturnT operator()(ParamTs... Params) { - void *CallableAddr = - isInlineStorage() ? getInlineStorage() : getOutOfLineStorage(); - - return (isTrivialCallback() - ? getTrivialCallback() - : getNonTrivialCallbacks()->CallPtr)(CallableAddr, Params...); - } + UniqueFunctionBase() = default; +public: explicit operator bool() const { return (bool)CallbackAndInlineFlag.getPointer(); } }; +template +template +typename UniqueFunctionBase::NonTrivialCallbacks UniqueFunctionBase< + R, P...>::CallbacksHolder::Callbacks = { + &CallImpl, &MoveImpl, &DestroyImpl}; + +template +template +typename UniqueFunctionBase::TrivialCallback + UniqueFunctionBase::CallbacksHolder< + CallableT, CalledAsT, EnableIfTrivial>::Callbacks{ + &CallImpl}; + +} // namespace detail + +template +class unique_function : public detail::UniqueFunctionBase { + using Base = detail::UniqueFunctionBase; + +public: + unique_function() = default; + unique_function(std::nullptr_t) {} + unique_function(unique_function &&) = default; + unique_function(const unique_function &) = delete; + unique_function &operator=(unique_function &&) = default; + unique_function &operator=(const unique_function &) = delete; + + template + unique_function( + CallableT Callable, + detail::EnableUnlessSameType * = nullptr, + detail::EnableIfCallable * = nullptr) + : Base(std::forward(Callable), + typename Base::template CalledAs{}) {} + + R operator()(P... Params) { + return this->getCallPtr()(this->getCalleePtr(), Params...); + } +}; + +template +class unique_function + : public detail::UniqueFunctionBase { + using Base = detail::UniqueFunctionBase; + +public: + unique_function() = default; + unique_function(std::nullptr_t) {} + unique_function(unique_function &&) = default; + unique_function(const unique_function &) = delete; + unique_function &operator=(unique_function &&) = default; + unique_function &operator=(const unique_function &) = delete; + + template + unique_function( + CallableT Callable, + detail::EnableUnlessSameType * = nullptr, + detail::EnableIfCallable * = nullptr) + : Base(std::forward(Callable), + typename Base::template CalledAs{}) {} + + R operator()(P... Params) const { + return this->getCallPtr()(this->getCalleePtr(), Params...); + } +}; + #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic pop #endif } // end namespace wpi -#endif // WPIUTIL_WPI_FUNCTION_H +#endif // WPIUTIL_WPI_FUNCTIONEXTRAS_H diff --git a/wpiutil/src/main/native/include/wpi/Hashing.h b/wpiutil/src/main/native/include/wpi/Hashing.h index e58bfdd192..c2c1cb17f9 100644 --- a/wpiutil/src/main/native/include/wpi/Hashing.h +++ b/wpiutil/src/main/native/include/wpi/Hashing.h @@ -1,9 +1,8 @@ //===-- llvm/ADT/Hashing.h - Utilities for hashing --------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -45,15 +44,14 @@ #ifndef WPIUTIL_WPI_HASHING_H #define WPIUTIL_WPI_HASHING_H -#include "wpi/Endian.h" +#include "wpi/ErrorHandling.h" #include "wpi/SwapByteOrder.h" #include "wpi/type_traits.h" -#include #include #include #include #include -#include +#include #include #ifdef _WIN32 @@ -108,8 +106,7 @@ public: /// differing argument types even if they would implicit promote to a common /// type without changing the value. template -typename std::enable_if::value, hash_code>::type -hash_value(T value); +std::enable_if_t::value, hash_code> hash_value(T value); /// Compute a hash_code for a pointer's address. /// @@ -120,6 +117,10 @@ template hash_code hash_value(const T *ptr); template hash_code hash_value(const std::pair &arg); +/// Compute a hash_code for a tuple. +template +hash_code hash_value(const std::tuple &arg); + /// Compute a hash_code for a standard string. template hash_code hash_value(const std::basic_string &arg); @@ -151,7 +152,7 @@ namespace detail { inline uint64_t fetch64(const char *p) { uint64_t result; memcpy(&result, p, sizeof(result)); - if (support::endian::system_endianness() == support::big) + if (sys::IsBigEndianHost) sys::swapByteOrder(result); return result; } @@ -159,16 +160,16 @@ inline uint64_t fetch64(const char *p) { inline uint32_t fetch32(const char *p) { uint32_t result; memcpy(&result, p, sizeof(result)); - if (support::endian::system_endianness() == support::big) + if (sys::IsBigEndianHost) sys::swapByteOrder(result); return result; } /// Some primes between 2^63 and 2^64 for various uses. -static const uint64_t k0 = 0xc3a5c85c97cb3127ULL; -static const uint64_t k1 = 0xb492b66fbe98f273ULL; -static const uint64_t k2 = 0x9ae16a3b2f90404fULL; -static const uint64_t k3 = 0xc949d7c7509e6557ULL; +static constexpr uint64_t k0 = 0xc3a5c85c97cb3127ULL; +static constexpr uint64_t k1 = 0xb492b66fbe98f273ULL; +static constexpr uint64_t k2 = 0x9ae16a3b2f90404fULL; +static constexpr uint64_t k3 = 0xc949d7c7509e6557ULL; /// Bitwise right rotate. /// Normally this will compile to a single instruction, especially if the @@ -198,7 +199,7 @@ inline uint64_t hash_1to3_bytes(const char *s, size_t len, uint64_t seed) { uint8_t b = s[len >> 1]; uint8_t c = s[len - 1]; uint32_t y = static_cast(a) + (static_cast(b) << 8); - uint32_t z = static_cast(len + (static_cast(c) << 2)); + uint32_t z = static_cast(len) + (static_cast(c) << 2); return shift_mix(y * k2 ^ z * k3 ^ seed) * k2; } @@ -264,7 +265,7 @@ inline uint64_t hash_short(const char *s, size_t length, uint64_t seed) { /// Currently, the algorithm for computing hash codes is based on CityHash and /// keeps 56 bytes of arbitrary state. struct hash_state { - uint64_t h0, h1, h2, h3, h4, h5, h6; + uint64_t h0 = 0, h1 = 0, h2 = 0, h3 = 0, h4 = 0, h5 = 0, h6 = 0; /// Create a new hash_state structure and initialize it based on the /// seed and the first 64-byte chunk. @@ -367,7 +368,7 @@ template struct is_hashable_data > /// Helper to get the hashable data representation for a type. /// This variant is enabled when the type itself can be used. template -typename std::enable_if::value, T>::type +std::enable_if_t::value, T> get_hashable_data(const T &value) { return value; } @@ -375,7 +376,7 @@ get_hashable_data(const T &value) { /// This variant is enabled when we must first call hash_value and use the /// result as our data. template -typename std::enable_if::value, size_t>::type +std::enable_if_t::value, size_t> get_hashable_data(const T &value) { using ::wpi::hash_value; return hash_value(value); @@ -449,7 +450,7 @@ hash_code hash_combine_range_impl(InputIteratorT first, InputIteratorT last) { /// are stored in contiguous memory, this routine avoids copying each value /// and directly reads from the underlying memory. template -typename std::enable_if::value, hash_code>::type +std::enable_if_t::value, hash_code> hash_combine_range_impl(ValueT *first, ValueT *last) { const uint64_t seed = get_execution_seed(); const char *s_begin = reinterpret_cast(first); @@ -499,7 +500,7 @@ namespace detail { /// useful at minimizing the code in the recursive calls to ease the pain /// caused by a lack of variadic functions. struct hash_combine_recursive_helper { - char buffer[64]; + char buffer[64] = {}; hash_state state; const uint64_t seed; @@ -547,7 +548,7 @@ public: // store types smaller than the buffer. if (!store_and_advance(buffer_ptr, buffer_end, data, partial_store_size)) - abort(); + wpi_unreachable("buffer smaller than stored type"); } return buffer_ptr; } @@ -574,7 +575,7 @@ public: // Check whether the entire set of values fit in the buffer. If so, we'll // use the optimized short hashing routine and skip state entirely. if (length == 0) - return static_cast(hash_short(buffer, buffer_ptr - buffer, seed)); + return hash_short(buffer, buffer_ptr - buffer, seed); // Mix the final buffer, rotating it if we did a partial fill in order to // simulate doing a mix of the last 64-bytes. That is how the algorithm @@ -586,7 +587,7 @@ public: state.mix(buffer); length += buffer_ptr - buffer; - return static_cast(state.finalize(length)); + return state.finalize(length); } }; @@ -625,7 +626,7 @@ inline hash_code hash_integer_value(uint64_t value) { const uint64_t seed = get_execution_seed(); const char *s = reinterpret_cast(&value); const uint64_t a = fetch32(s); - return static_cast(hash_16_bytes(seed + (a << 3), fetch32(s + 4))); + return hash_16_bytes(seed + (a << 3), fetch32(s + 4)); } } // namespace detail @@ -634,8 +635,7 @@ inline hash_code hash_integer_value(uint64_t value) { // Declared and documented above, but defined here so that any of the hashing // infrastructure is available. template -typename std::enable_if::value, hash_code>::type -hash_value(T value) { +std::enable_if_t::value, hash_code> hash_value(T value) { return ::wpi::hashing::detail::hash_integer_value( static_cast(value)); } @@ -654,6 +654,26 @@ hash_code hash_value(const std::pair &arg) { return hash_combine(arg.first, arg.second); } +// Implementation details for the hash_value overload for std::tuple<...>(...). +namespace hashing { +namespace detail { + +template +hash_code hash_value_tuple_helper(const std::tuple &arg, + std::index_sequence) { + return hash_combine(std::get(arg)...); +} + +} // namespace detail +} // namespace hashing + +template +hash_code hash_value(const std::tuple &arg) { + // TODO: Use std::apply when LLVM starts using C++17. + return ::wpi::hashing::detail::hash_value_tuple_helper( + arg, typename std::index_sequence_for()); +} + // Declared and documented above, but defined here so that any of the hashing // infrastructure is available. template @@ -661,11 +681,6 @@ hash_code hash_value(const std::basic_string &arg) { return hash_combine_range(arg.begin(), arg.end()); } -template -hash_code hash_value(const std::basic_string_view &arg) { - return hash_combine_range(arg.begin(), arg.end()); -} - } // namespace wpi #ifdef _WIN32 diff --git a/wpiutil/src/main/native/include/wpi/ManagedStatic.h b/wpiutil/src/main/native/include/wpi/ManagedStatic.h index 28c11fa152..fac9989067 100644 --- a/wpiutil/src/main/native/include/wpi/ManagedStatic.h +++ b/wpiutil/src/main/native/include/wpi/ManagedStatic.h @@ -1,9 +1,8 @@ //===-- llvm/Support/ManagedStatic.h - Static Global wrapper ----*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -33,19 +32,41 @@ template struct object_deleter { static void call(void *Ptr) { delete[](T *)Ptr; } }; +// ManagedStatic must be initialized to zero, and it must *not* have a dynamic +// initializer because managed statics are often created while running other +// dynamic initializers. In standard C++11, the best way to accomplish this is +// with a constexpr default constructor. However, different versions of the +// Visual C++ compiler have had bugs where, even though the constructor may be +// constexpr, a dynamic initializer may be emitted depending on optimization +// settings. For the affected versions of MSVC, use the old linker +// initialization pattern of not providing a constructor and leaving the fields +// uninitialized. See http://llvm.org/PR41367 for details. +#if !defined(_MSC_VER) || (_MSC_VER >= 1925) || defined(__clang__) +#define LLVM_USE_CONSTEXPR_CTOR +#endif + /// ManagedStaticBase - Common base class for ManagedStatic instances. class ManagedStaticBase { protected: +#ifdef LLVM_USE_CONSTEXPR_CTOR + mutable std::atomic Ptr{}; + mutable void (*DeleterFn)(void *) = nullptr; + mutable const ManagedStaticBase *Next = nullptr; +#else // This should only be used as a static variable, which guarantees that this // will be zero initialized. mutable std::atomic Ptr; - mutable void (*DeleterFn)(void*); + mutable void (*DeleterFn)(void *); mutable const ManagedStaticBase *Next; +#endif void RegisterManagedStatic(void *(*creator)(), void (*deleter)(void*)) const; - void RegisterManagedStatic(void *created, void (*deleter)(void*)) const; public: +#ifdef LLVM_USE_CONSTEXPR_CTOR + constexpr ManagedStaticBase() = default; +#endif + /// isConstructed - Return true if this object has not been created yet. bool isConstructed() const { return Ptr != nullptr; } @@ -61,12 +82,6 @@ template , class Deleter = object_deleter> class ManagedStatic : public ManagedStaticBase { public: - ManagedStatic() = default; - - ManagedStatic(C* created, void(*deleter)(void*)) { - RegisterManagedStatic(created, deleter); - } - // Accessors. C &operator*() { void *Tmp = Ptr.load(std::memory_order_acquire); @@ -87,6 +102,12 @@ public: } const C *operator->() const { return &**this; } + + // Extract the instance, leaving the ManagedStatic uninitialized. The + // user is then responsible for the lifetime of the returned instance. + C *claim() { + return static_cast(Ptr.exchange(nullptr)); + } }; /// wpi_shutdown - Deallocate and destroy all ManagedStatic variables. diff --git a/wpiutil/src/main/native/include/wpi/MapVector.h b/wpiutil/src/main/native/include/wpi/MapVector.h index 34e0267183..06b85a77f0 100644 --- a/wpiutil/src/main/native/include/wpi/MapVector.h +++ b/wpiutil/src/main/native/include/wpi/MapVector.h @@ -1,9 +1,8 @@ //===- llvm/ADT/MapVector.h - Map w/ deterministic value order --*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -237,4 +236,4 @@ struct SmallMapVector } // end namespace wpi -#endif // LLVM_ADT_MAPVECTOR_H +#endif // WPIUTIL_WPI_MAPVECTOR_H diff --git a/wpiutil/src/main/native/include/wpi/MathExtras.h b/wpiutil/src/main/native/include/wpi/MathExtras.h index ac88cb96da..ea669ee28b 100644 --- a/wpiutil/src/main/native/include/wpi/MathExtras.h +++ b/wpiutil/src/main/native/include/wpi/MathExtras.h @@ -1,9 +1,8 @@ //===-- llvm/Support/MathExtras.h - Useful math functions -------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -15,15 +14,18 @@ #define WPIUTIL_WPI_MATHEXTRAS_H #include "wpi/Compiler.h" -#include -#include #include #include #include +#include #include #include #include +#ifdef __ANDROID_NDK__ +#include +#endif + #ifdef _MSC_VER // Declare these intrinsics manually rather including intrin.h. It's very // expensive, and MathExtras.h is popular. @@ -37,6 +39,7 @@ unsigned char _BitScanReverse64(unsigned long *_Index, unsigned __int64 _Mask); #endif namespace wpi { + /// The behavior an operation has on an input of 0. enum ZeroBehavior { /// The returned value is undefined. @@ -49,14 +52,14 @@ enum ZeroBehavior { namespace detail { template struct TrailingZerosCounter { - static std::size_t count(T Val, ZeroBehavior) { + static unsigned count(T Val, ZeroBehavior) { if (!Val) return std::numeric_limits::digits; if (Val & 0x1) return 0; // Bisection method. - std::size_t ZeroBits = 0; + unsigned ZeroBits = 0; T Shift = std::numeric_limits::digits >> 1; T Mask = (std::numeric_limits::max)() >> Shift; while (Shift) { @@ -71,13 +74,13 @@ template struct TrailingZerosCounter { } }; -#if __GNUC__ >= 4 || defined(_MSC_VER) +#if defined(__GNUC__) || defined(_MSC_VER) template struct TrailingZerosCounter { - static std::size_t count(T Val, ZeroBehavior ZB) { + static unsigned count(T Val, ZeroBehavior ZB) { if (ZB != ZB_Undefined && Val == 0) return 32; -#if __has_builtin(__builtin_ctz) || LLVM_GNUC_PREREQ(4, 0, 0) +#if __has_builtin(__builtin_ctz) || defined(__GNUC__) return __builtin_ctz(Val); #elif defined(_MSC_VER) unsigned long Index; @@ -89,11 +92,11 @@ template struct TrailingZerosCounter { #if !defined(_MSC_VER) || defined(_M_X64) template struct TrailingZerosCounter { - static std::size_t count(T Val, ZeroBehavior ZB) { + static unsigned count(T Val, ZeroBehavior ZB) { if (ZB != ZB_Undefined && Val == 0) return 64; -#if __has_builtin(__builtin_ctzll) || LLVM_GNUC_PREREQ(4, 0, 0) +#if __has_builtin(__builtin_ctzll) || defined(__GNUC__) return __builtin_ctzll(Val); #elif defined(_MSC_VER) unsigned long Index; @@ -114,7 +117,7 @@ template struct TrailingZerosCounter { /// \param ZB the behavior on an input of 0. Only ZB_Width and ZB_Undefined are /// valid arguments. template -std::size_t countTrailingZeros(T Val, ZeroBehavior ZB = ZB_Width) { +unsigned countTrailingZeros(T Val, ZeroBehavior ZB = ZB_Width) { static_assert(std::numeric_limits::is_integer && !std::numeric_limits::is_signed, "Only unsigned integral types are allowed."); @@ -123,12 +126,12 @@ std::size_t countTrailingZeros(T Val, ZeroBehavior ZB = ZB_Width) { namespace detail { template struct LeadingZerosCounter { - static std::size_t count(T Val, ZeroBehavior) { + static unsigned count(T Val, ZeroBehavior) { if (!Val) return std::numeric_limits::digits; // Bisection method. - std::size_t ZeroBits = 0; + unsigned ZeroBits = 0; for (T Shift = std::numeric_limits::digits >> 1; Shift; Shift >>= 1) { T Tmp = Val >> Shift; if (Tmp) @@ -140,13 +143,13 @@ template struct LeadingZerosCounter { } }; -#if __GNUC__ >= 4 || defined(_MSC_VER) +#if defined(__GNUC__) || defined(_MSC_VER) template struct LeadingZerosCounter { - static std::size_t count(T Val, ZeroBehavior ZB) { + static unsigned count(T Val, ZeroBehavior ZB) { if (ZB != ZB_Undefined && Val == 0) return 32; -#if __has_builtin(__builtin_clz) || LLVM_GNUC_PREREQ(4, 0, 0) +#if __has_builtin(__builtin_clz) || defined(__GNUC__) return __builtin_clz(Val); #elif defined(_MSC_VER) unsigned long Index; @@ -158,11 +161,11 @@ template struct LeadingZerosCounter { #if !defined(_MSC_VER) || defined(_M_X64) template struct LeadingZerosCounter { - static std::size_t count(T Val, ZeroBehavior ZB) { + static unsigned count(T Val, ZeroBehavior ZB) { if (ZB != ZB_Undefined && Val == 0) return 64; -#if __has_builtin(__builtin_clzll) || LLVM_GNUC_PREREQ(4, 0, 0) +#if __has_builtin(__builtin_clzll) || defined(__GNUC__) return __builtin_clzll(Val); #elif defined(_MSC_VER) unsigned long Index; @@ -183,7 +186,7 @@ template struct LeadingZerosCounter { /// \param ZB the behavior on an input of 0. Only ZB_Width and ZB_Undefined are /// valid arguments. template -std::size_t countLeadingZeros(T Val, ZeroBehavior ZB = ZB_Width) { +unsigned countLeadingZeros(T Val, ZeroBehavior ZB = ZB_Width) { static_assert(std::numeric_limits::is_integer && !std::numeric_limits::is_signed, "Only unsigned integral types are allowed."); @@ -273,6 +276,34 @@ T reverseBits(T Val) { return Val; } +#if __has_builtin(__builtin_bitreverse8) +template<> +inline uint8_t reverseBits(uint8_t Val) { + return __builtin_bitreverse8(Val); +} +#endif + +#if __has_builtin(__builtin_bitreverse16) +template<> +inline uint16_t reverseBits(uint16_t Val) { + return __builtin_bitreverse16(Val); +} +#endif + +#if __has_builtin(__builtin_bitreverse32) +template<> +inline uint32_t reverseBits(uint32_t Val) { + return __builtin_bitreverse32(Val); +} +#endif + +#if __has_builtin(__builtin_bitreverse64) +template<> +inline uint64_t reverseBits(uint64_t Val) { + return __builtin_bitreverse64(Val); +} +#endif + // NOTE: The following support functions use the _32/_64 extensions instead of // type overloading so that signed and unsigned integers can be used without // ambiguity. @@ -325,14 +356,12 @@ constexpr inline bool isShiftedInt(int64_t x) { /// to keep MSVC from (incorrectly) warning on isUInt<64> that we're shifting /// left too many places. template -constexpr inline typename std::enable_if<(N < 64), bool>::type -isUInt(uint64_t X) { +constexpr inline std::enable_if_t<(N < 64), bool> isUInt(uint64_t X) { static_assert(N > 0, "isUInt<0> doesn't make sense"); return X < (UINT64_C(1) << (N)); } template -constexpr inline typename std::enable_if= 64, bool>::type -isUInt(uint64_t X) { +constexpr inline std::enable_if_t= 64, bool> isUInt(uint64_t) { return true; } @@ -379,7 +408,7 @@ inline uint64_t maxUIntN(uint64_t N) { inline int64_t minIntN(int64_t N) { assert(N > 0 && N <= 64 && "integer width out of range"); - return -(UINT64_C(1)<<(N-1)); + return UINT64_C(1) + ~(UINT64_C(1) << (N - 1)); } #ifdef _WIN32 @@ -450,7 +479,7 @@ constexpr inline bool isPowerOf2_64(uint64_t Value) { /// \param ZB the behavior on an input of all ones. Only ZB_Width and /// ZB_Undefined are valid arguments. template -std::size_t countLeadingOnes(T Value, ZeroBehavior ZB = ZB_Width) { +unsigned countLeadingOnes(T Value, ZeroBehavior ZB = ZB_Width) { static_assert(std::numeric_limits::is_integer && !std::numeric_limits::is_signed, "Only unsigned integral types are allowed."); @@ -466,7 +495,7 @@ std::size_t countLeadingOnes(T Value, ZeroBehavior ZB = ZB_Width) { /// \param ZB the behavior on an input of all ones. Only ZB_Width and /// ZB_Undefined are valid arguments. template -std::size_t countTrailingOnes(T Value, ZeroBehavior ZB = ZB_Width) { +unsigned countTrailingOnes(T Value, ZeroBehavior ZB = ZB_Width) { static_assert(std::numeric_limits::is_integer && !std::numeric_limits::is_signed, "Only unsigned integral types are allowed."); @@ -478,7 +507,7 @@ template struct PopulationCounter { static unsigned count(T Value) { // Generic version, forward to 32 bits. static_assert(SizeOfT <= 4, "Not implemented!"); -#if __GNUC__ >= 4 +#if defined(__GNUC__) return __builtin_popcount(Value); #else uint32_t v = Value; @@ -491,7 +520,7 @@ template struct PopulationCounter { template struct PopulationCounter { static unsigned count(T Value) { -#if __GNUC__ >= 4 +#if defined(__GNUC__) return __builtin_popcountll(Value); #else uint64_t v = Value; @@ -515,6 +544,16 @@ inline unsigned countPopulation(T Value) { return detail::PopulationCounter::count(Value); } +/// Compile time Log2. +/// Valid only for positive powers of two. +template constexpr inline size_t CTLog2() { + static_assert(kValue > 0 && wpi::isPowerOf2_64(kValue), + "Value is not a valid power of 2"); + return 1 + CTLog2(); +} + +template <> constexpr inline size_t CTLog2<1>() { return 0; } + /// Return the log base 2 of the specified value. inline double Log2(double Value) { #if defined(__ANDROID_API__) && __ANDROID_API__ < 18 @@ -551,15 +590,20 @@ inline unsigned Log2_64_Ceil(uint64_t Value) { } /// Return the greatest common divisor of the values using Euclid's algorithm. -inline uint64_t GreatestCommonDivisor64(uint64_t A, uint64_t B) { +template +inline T greatestCommonDivisor(T A, T B) { while (B) { - uint64_t T = B; + T Tmp = B; B = A % B; - A = T; + A = Tmp; } return A; } +inline uint64_t GreatestCommonDivisor64(uint64_t A, uint64_t B) { + return greatestCommonDivisor(A, B); +} + /// This function takes a 64-bit integer and returns the bit equivalent double. inline double BitsToDouble(uint64_t Bits) { double D; @@ -607,25 +651,6 @@ constexpr inline uint64_t MinAlign(uint64_t A, uint64_t B) { return (A | B) & (1 + ~(A | B)); } -/// Aligns \c Addr to \c Alignment bytes, rounding up. -/// -/// Alignment should be a power of two. This method rounds up, so -/// alignAddr(7, 4) == 8 and alignAddr(8, 4) == 8. -inline uintptr_t alignAddr(const void *Addr, size_t Alignment) { - assert(Alignment && isPowerOf2_64((uint64_t)Alignment) && - "Alignment is not a power of two!"); - - assert((uintptr_t)Addr + Alignment - 1 >= (uintptr_t)Addr); - - return (((uintptr_t)Addr + Alignment - 1) & ~(uintptr_t)(Alignment - 1)); -} - -/// Returns the necessary adjustment for aligning \c Ptr to \c Alignment -/// bytes, rounding up. -inline size_t alignmentAdjustment(const void *Ptr, size_t Alignment) { - return alignAddr(Ptr, Alignment) - (uintptr_t)Ptr; -} - /// Returns the next power of two (in 64-bits) that is strictly greater than A. /// Returns zero on overflow. inline uint64_t NextPowerOf2(uint64_t A) { @@ -691,18 +716,10 @@ inline uint64_t divideCeil(uint64_t Numerator, uint64_t Denominator) { return alignTo(Numerator, Denominator) / Denominator; } -/// \c alignTo for contexts where a constant expression is required. -/// \sa alignTo -/// -/// \todo FIXME: remove when \c constexpr becomes really \c constexpr -template -struct AlignTo { - static_assert(Align != 0u, "Align must be non-zero"); - template - struct from_value { - static const uint64_t value = (Value + Align - 1) / Align * Align; - }; -}; +/// Returns the integer nearest(Numerator / Denominator). +inline uint64_t divideNearest(uint64_t Numerator, uint64_t Denominator) { + return (Numerator + (Denominator / 2)) / Denominator; +} /// Returns the largest uint64_t less than or equal to \p Value and is /// \p Skew mod \p Align. \p Align must be non-zero @@ -712,13 +729,6 @@ inline uint64_t alignDown(uint64_t Value, uint64_t Align, uint64_t Skew = 0) { return (Value - Skew) / Align * Align + Skew; } -/// Returns the offset to the next integer (mod 2**64) that is greater than -/// or equal to \p Value and is a multiple of \p Align. \p Align must be -/// non-zero. -inline uint64_t OffsetToAlignment(uint64_t Value, uint64_t Align) { - return alignTo(Value, Align) - Value; -} - /// Sign-extend the number in the bottom B bits of X to a 32-bit integer. /// Requires 0 < B <= 32. template constexpr inline int32_t SignExtend32(uint32_t X) { @@ -728,7 +738,7 @@ template constexpr inline int32_t SignExtend32(uint32_t X) { } /// Sign-extend the number in the bottom B bits of X to a 32-bit integer. -/// Requires 0 < B < 32. +/// Requires 0 < B <= 32. inline int32_t SignExtend32(uint32_t X, unsigned B) { assert(B > 0 && "Bit width can't be 0."); assert(B <= 32 && "Bit width out of range."); @@ -736,7 +746,7 @@ inline int32_t SignExtend32(uint32_t X, unsigned B) { } /// Sign-extend the number in the bottom B bits of X to a 64-bit integer. -/// Requires 0 < B < 64. +/// Requires 0 < B <= 64. template constexpr inline int64_t SignExtend64(uint64_t x) { static_assert(B > 0, "Bit width can't be 0."); static_assert(B <= 64, "Bit width out of range."); @@ -744,7 +754,7 @@ template constexpr inline int64_t SignExtend64(uint64_t x) { } /// Sign-extend the number in the bottom B bits of X to a 64-bit integer. -/// Requires 0 < B < 64. +/// Requires 0 < B <= 64. inline int64_t SignExtend64(uint64_t X, unsigned B) { assert(B > 0 && "Bit width can't be 0."); assert(B <= 64 && "Bit width out of range."); @@ -754,16 +764,15 @@ inline int64_t SignExtend64(uint64_t X, unsigned B) { /// Subtract two unsigned integers, X and Y, of type T and return the absolute /// value of the result. template -typename std::enable_if::value, T>::type -AbsoluteDifference(T X, T Y) { - return (std::max)(X, Y) - (std::min)(X, Y); +std::enable_if_t::value, T> AbsoluteDifference(T X, T Y) { + return X > Y ? (X - Y) : (Y - X); } /// Add two unsigned integers, X and Y, of type T. Clamp the result to the /// maximum representable value of T on overflow. ResultOverflowed indicates if /// the result is larger than the maximum representable value of type T. template -typename std::enable_if::value, T>::type +std::enable_if_t::value, T> SaturatingAdd(T X, T Y, bool *ResultOverflowed = nullptr) { bool Dummy; bool &Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy; @@ -780,7 +789,7 @@ SaturatingAdd(T X, T Y, bool *ResultOverflowed = nullptr) { /// maximum representable value of T on overflow. ResultOverflowed indicates if /// the result is larger than the maximum representable value of type T. template -typename std::enable_if::value, T>::type +std::enable_if_t::value, T> SaturatingMultiply(T X, T Y, bool *ResultOverflowed = nullptr) { bool Dummy; bool &Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy; @@ -826,7 +835,7 @@ SaturatingMultiply(T X, T Y, bool *ResultOverflowed = nullptr) { /// overflow. ResultOverflowed indicates if the result is larger than the /// maximum representable value of type T. template -typename std::enable_if::value, T>::type +std::enable_if_t::value, T> SaturatingMultiplyAdd(T X, T Y, T A, bool *ResultOverflowed = nullptr) { bool Dummy; bool &Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy; @@ -838,6 +847,89 @@ SaturatingMultiplyAdd(T X, T Y, T A, bool *ResultOverflowed = nullptr) { return SaturatingAdd(A, Product, &Overflowed); } +/// Use this rather than HUGE_VALF; the latter causes warnings on MSVC. +extern const float huge_valf; + + +/// Add two signed integers, computing the two's complement truncated result, +/// returning true if overflow occured. +template +std::enable_if_t::value, T> AddOverflow(T X, T Y, T &Result) { +#if __has_builtin(__builtin_add_overflow) + return __builtin_add_overflow(X, Y, &Result); +#else + // Perform the unsigned addition. + using U = std::make_unsigned_t; + const U UX = static_cast(X); + const U UY = static_cast(Y); + const U UResult = UX + UY; + + // Convert to signed. + Result = static_cast(UResult); + + // Adding two positive numbers should result in a positive number. + if (X > 0 && Y > 0) + return Result <= 0; + // Adding two negatives should result in a negative number. + if (X < 0 && Y < 0) + return Result >= 0; + return false; +#endif +} + +/// Subtract two signed integers, computing the two's complement truncated +/// result, returning true if an overflow ocurred. +template +std::enable_if_t::value, T> SubOverflow(T X, T Y, T &Result) { +#if __has_builtin(__builtin_sub_overflow) + return __builtin_sub_overflow(X, Y, &Result); +#else + // Perform the unsigned addition. + using U = std::make_unsigned_t; + const U UX = static_cast(X); + const U UY = static_cast(Y); + const U UResult = UX - UY; + + // Convert to signed. + Result = static_cast(UResult); + + // Subtracting a positive number from a negative results in a negative number. + if (X <= 0 && Y > 0) + return Result >= 0; + // Subtracting a negative number from a positive results in a positive number. + if (X >= 0 && Y < 0) + return Result <= 0; + return false; +#endif +} + +/// Multiply two signed integers, computing the two's complement truncated +/// result, returning true if an overflow ocurred. +template +std::enable_if_t::value, T> MulOverflow(T X, T Y, T &Result) { + // Perform the unsigned multiplication on absolute values. + using U = std::make_unsigned_t; + const U UX = X < 0 ? (0 - static_cast(X)) : static_cast(X); + const U UY = Y < 0 ? (0 - static_cast(Y)) : static_cast(Y); + const U UResult = UX * UY; + + // Convert to signed. + const bool IsNegative = (X < 0) ^ (Y < 0); + Result = IsNegative ? (0 - UResult) : UResult; + + // If any of the args was 0, result is 0 and no overflow occurs. + if (UX == 0 || UY == 0) + return false; + + // UX and UY are in [1, 2^n], where n is the number of digits. + // Check how the max allowed absolute value (2^n for negative, 2^(n-1) for + // positive) divided by an argument compares to the other. + if (IsNegative) + return UX > (static_cast((std::numeric_limits::max)()) + U(1)) / UY; + else + return UX > (static_cast((std::numeric_limits::max)())) / UY; +} + // Typesafe implementation of the signum function. // Returns -1 if negative, 1 if positive, 0 if 0. template @@ -858,7 +950,6 @@ template constexpr T Lerp(const T& startValue, const T& endValue, double t) { return startValue + (endValue - startValue) * t; } - -} // namespace wpi +} // End wpi namespace #endif diff --git a/wpiutil/src/main/native/include/wpi/MemAlloc.h b/wpiutil/src/main/native/include/wpi/MemAlloc.h index addccb5431..32909ff38d 100644 --- a/wpiutil/src/main/native/include/wpi/MemAlloc.h +++ b/wpiutil/src/main/native/include/wpi/MemAlloc.h @@ -1,9 +1,8 @@ //===- MemAlloc.h - Memory allocation functions -----------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// /// \file @@ -33,29 +32,69 @@ namespace wpi { LLVM_ATTRIBUTE_RETURNS_NONNULL inline void *safe_malloc(size_t Sz) { void *Result = std::malloc(Sz); - if (Result == nullptr) + if (Result == nullptr) { + // It is implementation-defined whether allocation occurs if the space + // requested is zero (ISO/IEC 9899:2018 7.22.3). Retry, requesting + // non-zero, if the space requested was zero. + if (Sz == 0) + return safe_malloc(1); report_bad_alloc_error("Allocation failed"); + } return Result; } LLVM_ATTRIBUTE_RETURNS_NONNULL inline void *safe_calloc(size_t Count, size_t Sz) { void *Result = std::calloc(Count, Sz); - if (Result == nullptr) + if (Result == nullptr) { + // It is implementation-defined whether allocation occurs if the space + // requested is zero (ISO/IEC 9899:2018 7.22.3). Retry, requesting + // non-zero, if the space requested was zero. + if (Count == 0 || Sz == 0) + return safe_malloc(1); report_bad_alloc_error("Allocation failed"); + } return Result; } LLVM_ATTRIBUTE_RETURNS_NONNULL inline void *safe_realloc(void *Ptr, size_t Sz) { void *Result = std::realloc(Ptr, Sz); - if (Result == nullptr) + if (Result == nullptr) { + // It is implementation-defined whether allocation occurs if the space + // requested is zero (ISO/IEC 9899:2018 7.22.3). Retry, requesting + // non-zero, if the space requested was zero. + if (Sz == 0) + return safe_malloc(1); report_bad_alloc_error("Allocation failed"); + } return Result; } +/// Allocate a buffer of memory with the given size and alignment. +/// +/// When the compiler supports aligned operator new, this will use it to to +/// handle even over-aligned allocations. +/// +/// However, this doesn't make any attempt to leverage the fancier techniques +/// like posix_memalign due to portability. It is mostly intended to allow +/// compatibility with platforms that, after aligned allocation was added, use +/// reduced default alignment. +LLVM_ATTRIBUTE_RETURNS_NONNULL LLVM_ATTRIBUTE_RETURNS_NOALIAS void * +allocate_buffer(size_t Size, size_t Alignment); + +/// Deallocate a buffer of memory with the given size and alignment. +/// +/// If supported, this will used the sized delete operator. Also if supported, +/// this will pass the alignment to the delete operator. +/// +/// The pointer must have been allocated with the corresponding new operator, +/// most likely using the above helper. +void deallocate_buffer(void *Ptr, size_t Size, size_t Alignment); + +} // namespace wpi + #ifdef _WIN32 #pragma warning(pop) #endif -} #endif diff --git a/wpiutil/src/main/native/include/wpi/PointerIntPair.h b/wpiutil/src/main/native/include/wpi/PointerIntPair.h index e6a1212787..6e6acf8957 100644 --- a/wpiutil/src/main/native/include/wpi/PointerIntPair.h +++ b/wpiutil/src/main/native/include/wpi/PointerIntPair.h @@ -1,9 +1,8 @@ //===- llvm/ADT/PointerIntPair.h - Pair for pointer and int -----*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -14,7 +13,9 @@ #ifndef WPIUTIL_WPI_POINTERINTPAIR_H #define WPIUTIL_WPI_POINTERINTPAIR_H +#include "wpi/Compiler.h" #include "wpi/PointerLikeTypeTraits.h" +#include "wpi/type_traits.h" #include #include #include @@ -59,19 +60,19 @@ public: IntType getInt() const { return (IntType)Info::getInt(Value); } - void setPointer(PointerTy PtrVal) { + void setPointer(PointerTy PtrVal) LLVM_LVALUE_FUNCTION { Value = Info::updatePointer(Value, PtrVal); } - void setInt(IntType IntVal) { + void setInt(IntType IntVal) LLVM_LVALUE_FUNCTION { Value = Info::updateInt(Value, static_cast(IntVal)); } - void initWithPointer(PointerTy PtrVal) { + void initWithPointer(PointerTy PtrVal) LLVM_LVALUE_FUNCTION { Value = Info::updatePointer(0, PtrVal); } - void setPointerAndInt(PointerTy PtrVal, IntType IntVal) { + void setPointerAndInt(PointerTy PtrVal, IntType IntVal) LLVM_LVALUE_FUNCTION { Value = Info::updateInt(Info::updatePointer(0, PtrVal), static_cast(IntVal)); } @@ -89,7 +90,7 @@ public: void *getOpaqueValue() const { return reinterpret_cast(Value); } - void setFromOpaqueValue(void *Val) { + void setFromOpaqueValue(void *Val) LLVM_LVALUE_FUNCTION { Value = reinterpret_cast(Val); } @@ -126,6 +127,7 @@ public: } }; + template struct PointerIntPairInfo { static_assert(PtrTraits::NumLowBitsAvailable < @@ -133,7 +135,7 @@ struct PointerIntPairInfo { "cannot use a pointer type that has all bits free"); static_assert(IntBits <= PtrTraits::NumLowBitsAvailable, "PointerIntPair with integer size too large for pointer"); - enum : uintptr_t { + enum MaskAndShiftConstants : uintptr_t { /// PointerBitMask - The bits that come from the pointer. PointerBitMask = ~(uintptr_t)(((intptr_t)1 << PtrTraits::NumLowBitsAvailable) - 1), @@ -176,12 +178,6 @@ struct PointerIntPairInfo { } }; -template struct isPodLike; -template -struct isPodLike> { - static const bool value = true; -}; - // Provide specialization of DenseMapInfo for PointerIntPair. template struct DenseMapInfo> { @@ -227,7 +223,8 @@ struct PointerLikeTypeTraits< return PointerIntPair::getFromOpaqueValue(P); } - enum { NumLowBitsAvailable = PtrTraits::NumLowBitsAvailable - IntBits }; + static constexpr int NumLowBitsAvailable = + PtrTraits::NumLowBitsAvailable - IntBits; }; } // end namespace wpi diff --git a/wpiutil/src/main/native/include/wpi/PointerLikeTypeTraits.h b/wpiutil/src/main/native/include/wpi/PointerLikeTypeTraits.h index fa136e005f..ffca61a141 100644 --- a/wpiutil/src/main/native/include/wpi/PointerLikeTypeTraits.h +++ b/wpiutil/src/main/native/include/wpi/PointerLikeTypeTraits.h @@ -1,9 +1,8 @@ //===- llvm/Support/PointerLikeTypeTraits.h - Pointer Traits ----*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -15,8 +14,8 @@ #ifndef WPIUTIL_WPI_POINTERLIKETYPETRAITS_H #define WPIUTIL_WPI_POINTERLIKETYPETRAITS_H +#include #include -#include #include namespace wpi { @@ -38,8 +37,9 @@ template struct HasPointerLikeTypeTraits { }; // sizeof(T) is valid only for a complete T. -template struct HasPointerLikeTypeTraits< - T, decltype((sizeof(PointerLikeTypeTraits) + sizeof(T)), void())> { +template +struct HasPointerLikeTypeTraits< + T, decltype((sizeof(PointerLikeTypeTraits) + sizeof(T)), void())> { static const bool value = true; }; @@ -57,7 +57,8 @@ template struct PointerLikeTypeTraits { static inline void *getAsVoidPointer(T *P) { return P; } static inline T *getFromVoidPointer(void *P) { return static_cast(P); } - enum { NumLowBitsAvailable = detail::ConstantLog2::value }; + static constexpr int NumLowBitsAvailable = + detail::ConstantLog2::value; }; template <> struct PointerLikeTypeTraits { @@ -71,7 +72,7 @@ template <> struct PointerLikeTypeTraits { /// /// All clients should use assertions to do a run-time check to ensure that /// this is actually true. - enum { NumLowBitsAvailable = 2 }; + static constexpr int NumLowBitsAvailable = 2; }; // Provide PointerLikeTypeTraits for const things. @@ -84,7 +85,7 @@ template struct PointerLikeTypeTraits { static inline const T getFromVoidPointer(const void *P) { return NonConst::getFromVoidPointer(const_cast(P)); } - enum { NumLowBitsAvailable = NonConst::NumLowBitsAvailable }; + static constexpr int NumLowBitsAvailable = NonConst::NumLowBitsAvailable; }; // Provide PointerLikeTypeTraits for const pointers. @@ -97,7 +98,7 @@ template struct PointerLikeTypeTraits { static inline const T *getFromVoidPointer(const void *P) { return NonConst::getFromVoidPointer(const_cast(P)); } - enum { NumLowBitsAvailable = NonConst::NumLowBitsAvailable }; + static constexpr int NumLowBitsAvailable = NonConst::NumLowBitsAvailable; }; // Provide PointerLikeTypeTraits for uintptr_t. @@ -109,7 +110,7 @@ template <> struct PointerLikeTypeTraits { return reinterpret_cast(P); } // No bits are available! - enum { NumLowBitsAvailable = 0 }; + static constexpr int NumLowBitsAvailable = 0; }; /// Provide suitable custom traits struct for function pointers. @@ -122,7 +123,8 @@ template <> struct PointerLikeTypeTraits { /// potentially use alignment attributes on functions to satisfy that. template struct FunctionPointerLikeTypeTraits { - enum { NumLowBitsAvailable = detail::ConstantLog2::value }; + static constexpr int NumLowBitsAvailable = + detail::ConstantLog2::value; static inline void *getAsVoidPointer(FunctionPointerT P) { assert((reinterpret_cast(P) & ~((uintptr_t)-1 << NumLowBitsAvailable)) == 0 && diff --git a/wpiutil/src/main/native/include/wpi/PointerUnion.h b/wpiutil/src/main/native/include/wpi/PointerUnion.h index 8d6e580bae..2f951dad15 100644 --- a/wpiutil/src/main/native/include/wpi/PointerUnion.h +++ b/wpiutil/src/main/native/include/wpi/PointerUnion.h @@ -1,9 +1,8 @@ //===- llvm/ADT/PointerUnion.h - Discriminated Union of 2 Ptrs --*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -54,22 +53,84 @@ struct PointerUnionTypeSelectorReturn< typename PointerUnionTypeSelector::Return; }; -/// Provide PointerLikeTypeTraits for void* that is used by PointerUnion -/// for the two template arguments. -template class PointerUnionUIntTraits { -public: - static inline void *getAsVoidPointer(void *P) { return P; } - static inline void *getFromVoidPointer(void *P) { return P; } +namespace pointer_union_detail { + /// Determine the number of bits required to store integers with values < n. + /// This is ceil(log2(n)). + constexpr int bitsRequired(unsigned n) { + return n > 1 ? 1 + bitsRequired((n + 1) / 2) : 0; + } - enum { - PT1BitsAv = (int)(PointerLikeTypeTraits::NumLowBitsAvailable), - PT2BitsAv = (int)(PointerLikeTypeTraits::NumLowBitsAvailable), - NumLowBitsAvailable = PT1BitsAv < PT2BitsAv ? PT1BitsAv : PT2BitsAv + template constexpr int lowBitsAvailable() { + return std::min({PointerLikeTypeTraits::NumLowBitsAvailable...}); + } + + /// Find the index of a type in a list of types. TypeIndex::Index + /// is the index of T in Us, or sizeof...(Us) if T does not appear in the + /// list. + template struct TypeIndex; + template struct TypeIndex { + static constexpr int Index = 0; + }; + template + struct TypeIndex { + static constexpr int Index = 1 + TypeIndex::Index; + }; + template struct TypeIndex { + static constexpr int Index = 0; }; -}; -/// A discriminated union of two pointer types, with the discriminator in the -/// low bit of the pointer. + /// Find the first type in a list of types. + template struct GetFirstType { + using type = T; + }; + + /// Provide PointerLikeTypeTraits for void* that is used by PointerUnion + /// for the template arguments. + template class PointerUnionUIntTraits { + public: + static inline void *getAsVoidPointer(void *P) { return P; } + static inline void *getFromVoidPointer(void *P) { return P; } + static constexpr int NumLowBitsAvailable = lowBitsAvailable(); + }; + + template + class PointerUnionMembers; + + template + class PointerUnionMembers { + protected: + ValTy Val; + PointerUnionMembers() = default; + PointerUnionMembers(ValTy Val) : Val(Val) {} + + friend struct PointerLikeTypeTraits; + }; + + template + class PointerUnionMembers + : public PointerUnionMembers { + using Base = PointerUnionMembers; + public: + using Base::Base; + PointerUnionMembers() = default; + PointerUnionMembers(Type V) + : Base(ValTy(const_cast( + PointerLikeTypeTraits::getAsVoidPointer(V)), + I)) {} + + using Base::operator=; + Derived &operator=(Type V) { + this->Val = ValTy( + const_cast(PointerLikeTypeTraits::getAsVoidPointer(V)), + I); + return static_cast(*this); + }; + }; +} + +/// A discriminated union of two or more pointer types, with the discriminator +/// in the low bit of the pointer. /// /// This implementation is extremely efficient in space due to leveraging the /// low bits of the pointer, while exposing a natural and type-safe API. @@ -84,49 +145,40 @@ public: /// P = (float*)0; /// Y = P.get(); // ok. /// X = P.get(); // runtime assertion failure. -template class PointerUnion { -public: - using ValTy = - PointerIntPair>; - -private: - ValTy Val; - - struct IsPT1 { - static const int Num = 0; - }; - struct IsPT2 { - static const int Num = 1; - }; - template struct UNION_DOESNT_CONTAIN_TYPE {}; +template +class PointerUnion + : public pointer_union_detail::PointerUnionMembers< + PointerUnion, + PointerIntPair< + void *, pointer_union_detail::bitsRequired(sizeof...(PTs)), int, + pointer_union_detail::PointerUnionUIntTraits>, + 0, PTs...> { + // The first type is special because we want to directly cast a pointer to a + // default-initialized union to a pointer to the first type. But we don't + // want PointerUnion to be a 'template ' + // because it's much more convenient to have a name for the whole pack. So + // split off the first type here. + using First = typename pointer_union_detail::GetFirstType::type; + using Base = typename PointerUnion::PointerUnionMembers; public: PointerUnion() = default; - PointerUnion(PT1 V) - : Val(const_cast( - PointerLikeTypeTraits::getAsVoidPointer(V))) {} - PointerUnion(PT2 V) - : Val(const_cast(PointerLikeTypeTraits::getAsVoidPointer(V)), - 1) {} + + PointerUnion(std::nullptr_t) : PointerUnion() {} + using Base::Base; /// Test if the pointer held in the union is null, regardless of /// which type it is. - bool isNull() const { - // Convert from the void* to one of the pointer types, to make sure that - // we recursively strip off low bits if we have a nested PointerUnion. - return !PointerLikeTypeTraits::getFromVoidPointer(Val.getPointer()); - } + bool isNull() const { return !this->Val.getPointer(); } explicit operator bool() const { return !isNull(); } /// Test if the Union currently holds the type matching T. - template int is() const { - using Ty = typename ::wpi::PointerUnionTypeSelector< - PT1, T, IsPT1, - ::wpi::PointerUnionTypeSelector>>::Return; - int TyNo = Ty::Num; - return static_cast(Val.getInt()) == TyNo; + template bool is() const { + constexpr int Index = pointer_union_detail::TypeIndex::Index; + static_assert(Index < sizeof...(PTs), + "PointerUnion::is given type not in the union"); + return this->Val.getInt() == Index; } /// Returns the value of the specified pointer type. @@ -134,11 +186,11 @@ public: /// If the specified pointer type is incorrect, assert. template T get() const { assert(is() && "Invalid accessor called"); - return PointerLikeTypeTraits::getFromVoidPointer(Val.getPointer()); + return PointerLikeTypeTraits::getFromVoidPointer(this->Val.getPointer()); } /// Returns the current pointer if it is of the specified pointer type, - /// otherwises returns null. + /// otherwise returns null. template T dyn_cast() const { if (is()) return get(); @@ -147,342 +199,91 @@ public: /// If the union is set to the first pointer type get an address pointing to /// it. - PT1 const *getAddrOfPtr1() const { + First const *getAddrOfPtr1() const { return const_cast(this)->getAddrOfPtr1(); } /// If the union is set to the first pointer type get an address pointing to /// it. - PT1 *getAddrOfPtr1() { - assert(is() && "Val is not the first pointer"); + First *getAddrOfPtr1() { + assert(is() && "Val is not the first pointer"); assert( - get() == Val.getPointer() && + PointerLikeTypeTraits::getAsVoidPointer(get()) == + this->Val.getPointer() && "Can't get the address because PointerLikeTypeTraits changes the ptr"); - return const_cast( - reinterpret_cast(Val.getAddrOfPointer())); + return const_cast( + reinterpret_cast(this->Val.getAddrOfPointer())); } /// Assignment from nullptr which just clears the union. const PointerUnion &operator=(std::nullptr_t) { - Val.initWithPointer(nullptr); + this->Val.initWithPointer(nullptr); return *this; } - /// Assignment operators - Allow assigning into this union from either - /// pointer type, setting the discriminator to remember what it came from. - const PointerUnion &operator=(const PT1 &RHS) { - Val.initWithPointer( - const_cast(PointerLikeTypeTraits::getAsVoidPointer(RHS))); - return *this; - } - const PointerUnion &operator=(const PT2 &RHS) { - Val.setPointerAndInt( - const_cast(PointerLikeTypeTraits::getAsVoidPointer(RHS)), - 1); - return *this; - } + /// Assignment from elements of the union. + using Base::operator=; - void *getOpaqueValue() const { return Val.getOpaqueValue(); } + void *getOpaqueValue() const { return this->Val.getOpaqueValue(); } static inline PointerUnion getFromOpaqueValue(void *VP) { PointerUnion V; - V.Val = ValTy::getFromOpaqueValue(VP); + V.Val = decltype(V.Val)::getFromOpaqueValue(VP); return V; } }; -template -bool operator==(PointerUnion lhs, PointerUnion rhs) { +template +bool operator==(PointerUnion lhs, PointerUnion rhs) { return lhs.getOpaqueValue() == rhs.getOpaqueValue(); } -template -bool operator!=(PointerUnion lhs, PointerUnion rhs) { +template +bool operator!=(PointerUnion lhs, PointerUnion rhs) { return lhs.getOpaqueValue() != rhs.getOpaqueValue(); } -template -bool operator<(PointerUnion lhs, PointerUnion rhs) { +template +bool operator<(PointerUnion lhs, PointerUnion rhs) { return lhs.getOpaqueValue() < rhs.getOpaqueValue(); } // Teach SmallPtrSet that PointerUnion is "basically a pointer", that has // # low bits available = min(PT1bits,PT2bits)-1. -template -struct PointerLikeTypeTraits> { - static inline void *getAsVoidPointer(const PointerUnion &P) { +template +struct PointerLikeTypeTraits> { + static inline void *getAsVoidPointer(const PointerUnion &P) { return P.getOpaqueValue(); } - static inline PointerUnion getFromVoidPointer(void *P) { - return PointerUnion::getFromOpaqueValue(P); + static inline PointerUnion getFromVoidPointer(void *P) { + return PointerUnion::getFromOpaqueValue(P); } - // The number of bits available are the min of the two pointer types. - enum { - NumLowBitsAvailable = PointerLikeTypeTraits< - typename PointerUnion::ValTy>::NumLowBitsAvailable - }; -}; - -/// A pointer union of three pointer types. See documentation for PointerUnion -/// for usage. -template class PointerUnion3 { -public: - using InnerUnion = PointerUnion; - using ValTy = PointerUnion; - -private: - ValTy Val; - - struct IsInnerUnion { - ValTy Val; - - IsInnerUnion(ValTy val) : Val(val) {} - - template int is() const { - return Val.template is() && - Val.template get().template is(); - } - - template T get() const { - return Val.template get().template get(); - } - }; - - struct IsPT3 { - ValTy Val; - - IsPT3(ValTy val) : Val(val) {} - - template int is() const { return Val.template is(); } - template T get() const { return Val.template get(); } - }; - -public: - PointerUnion3() = default; - PointerUnion3(PT1 V) { Val = InnerUnion(V); } - PointerUnion3(PT2 V) { Val = InnerUnion(V); } - PointerUnion3(PT3 V) { Val = V; } - - /// Test if the pointer held in the union is null, regardless of - /// which type it is. - bool isNull() const { return Val.isNull(); } - explicit operator bool() const { return !isNull(); } - - /// Test if the Union currently holds the type matching T. - template int is() const { - // If T is PT1/PT2 choose IsInnerUnion otherwise choose IsPT3. - using Ty = typename ::wpi::PointerUnionTypeSelector< - PT1, T, IsInnerUnion, - ::wpi::PointerUnionTypeSelector>::Return; - return Ty(Val).template is(); - } - - /// Returns the value of the specified pointer type. - /// - /// If the specified pointer type is incorrect, assert. - template T get() const { - assert(is() && "Invalid accessor called"); - // If T is PT1/PT2 choose IsInnerUnion otherwise choose IsPT3. - using Ty = typename ::wpi::PointerUnionTypeSelector< - PT1, T, IsInnerUnion, - ::wpi::PointerUnionTypeSelector>::Return; - return Ty(Val).template get(); - } - - /// Returns the current pointer if it is of the specified pointer type, - /// otherwises returns null. - template T dyn_cast() const { - if (is()) - return get(); - return T(); - } - - /// Assignment from nullptr which just clears the union. - const PointerUnion3 &operator=(std::nullptr_t) { - Val = nullptr; - return *this; - } - - /// Assignment operators - Allow assigning into this union from either - /// pointer type, setting the discriminator to remember what it came from. - const PointerUnion3 &operator=(const PT1 &RHS) { - Val = InnerUnion(RHS); - return *this; - } - const PointerUnion3 &operator=(const PT2 &RHS) { - Val = InnerUnion(RHS); - return *this; - } - const PointerUnion3 &operator=(const PT3 &RHS) { - Val = RHS; - return *this; - } - - void *getOpaqueValue() const { return Val.getOpaqueValue(); } - static inline PointerUnion3 getFromOpaqueValue(void *VP) { - PointerUnion3 V; - V.Val = ValTy::getFromOpaqueValue(VP); - return V; - } -}; - -// Teach SmallPtrSet that PointerUnion3 is "basically a pointer", that has -// # low bits available = min(PT1bits,PT2bits,PT2bits)-2. -template -struct PointerLikeTypeTraits> { - static inline void *getAsVoidPointer(const PointerUnion3 &P) { - return P.getOpaqueValue(); - } - - static inline PointerUnion3 getFromVoidPointer(void *P) { - return PointerUnion3::getFromOpaqueValue(P); - } - - // The number of bits available are the min of the two pointer types. - enum { - NumLowBitsAvailable = PointerLikeTypeTraits< - typename PointerUnion3::ValTy>::NumLowBitsAvailable - }; -}; - -template -bool operator<(PointerUnion3 lhs, - PointerUnion3 rhs) { - return lhs.getOpaqueValue() < rhs.getOpaqueValue(); -} - -/// A pointer union of four pointer types. See documentation for PointerUnion -/// for usage. -template -class PointerUnion4 { -public: - using InnerUnion1 = PointerUnion; - using InnerUnion2 = PointerUnion; - using ValTy = PointerUnion; - -private: - ValTy Val; - -public: - PointerUnion4() = default; - PointerUnion4(PT1 V) { Val = InnerUnion1(V); } - PointerUnion4(PT2 V) { Val = InnerUnion1(V); } - PointerUnion4(PT3 V) { Val = InnerUnion2(V); } - PointerUnion4(PT4 V) { Val = InnerUnion2(V); } - - /// Test if the pointer held in the union is null, regardless of - /// which type it is. - bool isNull() const { return Val.isNull(); } - explicit operator bool() const { return !isNull(); } - - /// Test if the Union currently holds the type matching T. - template int is() const { - // If T is PT1/PT2 choose InnerUnion1 otherwise choose InnerUnion2. - using Ty = typename ::wpi::PointerUnionTypeSelector< - PT1, T, InnerUnion1, - ::wpi::PointerUnionTypeSelector>::Return; - return Val.template is() && Val.template get().template is(); - } - - /// Returns the value of the specified pointer type. - /// - /// If the specified pointer type is incorrect, assert. - template T get() const { - assert(is() && "Invalid accessor called"); - // If T is PT1/PT2 choose InnerUnion1 otherwise choose InnerUnion2. - using Ty = typename ::wpi::PointerUnionTypeSelector< - PT1, T, InnerUnion1, - ::wpi::PointerUnionTypeSelector>::Return; - return Val.template get().template get(); - } - - /// Returns the current pointer if it is of the specified pointer type, - /// otherwises returns null. - template T dyn_cast() const { - if (is()) - return get(); - return T(); - } - - /// Assignment from nullptr which just clears the union. - const PointerUnion4 &operator=(std::nullptr_t) { - Val = nullptr; - return *this; - } - - /// Assignment operators - Allow assigning into this union from either - /// pointer type, setting the discriminator to remember what it came from. - const PointerUnion4 &operator=(const PT1 &RHS) { - Val = InnerUnion1(RHS); - return *this; - } - const PointerUnion4 &operator=(const PT2 &RHS) { - Val = InnerUnion1(RHS); - return *this; - } - const PointerUnion4 &operator=(const PT3 &RHS) { - Val = InnerUnion2(RHS); - return *this; - } - const PointerUnion4 &operator=(const PT4 &RHS) { - Val = InnerUnion2(RHS); - return *this; - } - - void *getOpaqueValue() const { return Val.getOpaqueValue(); } - static inline PointerUnion4 getFromOpaqueValue(void *VP) { - PointerUnion4 V; - V.Val = ValTy::getFromOpaqueValue(VP); - return V; - } -}; - -// Teach SmallPtrSet that PointerUnion4 is "basically a pointer", that has -// # low bits available = min(PT1bits,PT2bits,PT2bits)-2. -template -struct PointerLikeTypeTraits> { - static inline void * - getAsVoidPointer(const PointerUnion4 &P) { - return P.getOpaqueValue(); - } - - static inline PointerUnion4 getFromVoidPointer(void *P) { - return PointerUnion4::getFromOpaqueValue(P); - } - - // The number of bits available are the min of the two pointer types. - enum { - NumLowBitsAvailable = PointerLikeTypeTraits< - typename PointerUnion4::ValTy>::NumLowBitsAvailable - }; + // The number of bits available are the min of the pointer types minus the + // bits needed for the discriminator. + static constexpr int NumLowBitsAvailable = PointerLikeTypeTraits::Val)>::NumLowBitsAvailable; }; // Teach DenseMap how to use PointerUnions as keys. -template struct DenseMapInfo> { - using Pair = PointerUnion; - using FirstInfo = DenseMapInfo; - using SecondInfo = DenseMapInfo; +template struct DenseMapInfo> { + using Union = PointerUnion; + using FirstInfo = + DenseMapInfo::type>; - static inline Pair getEmptyKey() { return Pair(FirstInfo::getEmptyKey()); } + static inline Union getEmptyKey() { return Union(FirstInfo::getEmptyKey()); } - static inline Pair getTombstoneKey() { - return Pair(FirstInfo::getTombstoneKey()); + static inline Union getTombstoneKey() { + return Union(FirstInfo::getTombstoneKey()); } - static unsigned getHashValue(const Pair &PairVal) { - intptr_t key = (intptr_t)PairVal.getOpaqueValue(); + static unsigned getHashValue(const Union &UnionVal) { + intptr_t key = (intptr_t)UnionVal.getOpaqueValue(); return DenseMapInfo::getHashValue(key); } - static bool isEqual(const Pair &LHS, const Pair &RHS) { - return LHS.template is() == RHS.template is() && - (LHS.template is() ? FirstInfo::isEqual(LHS.template get(), - RHS.template get()) - : SecondInfo::isEqual(LHS.template get(), - RHS.template get())); + static bool isEqual(const Union &LHS, const Union &RHS) { + return LHS == RHS; } }; diff --git a/wpiutil/src/main/native/include/wpi/STLForwardCompat.h b/wpiutil/src/main/native/include/wpi/STLForwardCompat.h new file mode 100644 index 0000000000..38a92b10e9 --- /dev/null +++ b/wpiutil/src/main/native/include/wpi/STLForwardCompat.h @@ -0,0 +1,82 @@ +//===- STLForwardCompat.h - Library features from future STLs ------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 +// +//===----------------------------------------------------------------------===// +// +// This file contains library features backported from future STL versions. +// +// These should be replaced with their STL counterparts as the C++ version LLVM +// is compiled with is updated. +// +//===----------------------------------------------------------------------===// + +#ifndef WPIUTIL_WPI_STLFORWARDCOMPAT_H +#define WPIUTIL_WPI_STLFORWARDCOMPAT_H + +#include + +namespace wpi { + +//===----------------------------------------------------------------------===// +// Features from C++17 +//===----------------------------------------------------------------------===// + +template +struct negation // NOLINT(readability-identifier-naming) + : std::integral_constant {}; + +template +struct conjunction // NOLINT(readability-identifier-naming) + : std::true_type {}; +template struct conjunction : B1 {}; +template +struct conjunction + : std::conditional, B1>::type {}; + +template +struct disjunction // NOLINT(readability-identifier-naming) + : std::false_type {}; +template struct disjunction : B1 {}; +template +struct disjunction + : std::conditional>::type {}; + +struct in_place_t // NOLINT(readability-identifier-naming) +{ + explicit in_place_t() = default; +}; +/// \warning This must not be odr-used, as it cannot be made \c inline in C++14. +constexpr in_place_t in_place; // NOLINT(readability-identifier-naming) + +template +struct in_place_type_t // NOLINT(readability-identifier-naming) +{ + explicit in_place_type_t() = default; +}; + +template +struct in_place_index_t // NOLINT(readability-identifier-naming) +{ + explicit in_place_index_t() = default; +}; + +//===----------------------------------------------------------------------===// +// Features from C++20 +//===----------------------------------------------------------------------===// + +template +struct remove_cvref // NOLINT(readability-identifier-naming) +{ + using type = std::remove_cv_t>; +}; + +template +using remove_cvref_t // NOLINT(readability-identifier-naming) + = typename wpi::remove_cvref::type; + +} // namespace wpi + +#endif // WPIUTIL_WPI_STLFORWARDCOMPAT_H diff --git a/wpiutil/src/main/native/include/wpi/SmallPtrSet.h b/wpiutil/src/main/native/include/wpi/SmallPtrSet.h index 8669527d5b..209c916f03 100644 --- a/wpiutil/src/main/native/include/wpi/SmallPtrSet.h +++ b/wpiutil/src/main/native/include/wpi/SmallPtrSet.h @@ -1,9 +1,8 @@ //===- llvm/ADT/SmallPtrSet.h - 'Normally small' pointer set ----*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -20,7 +19,6 @@ #include "wpi/type_traits.h" #include #include -#include #include #include #include @@ -270,7 +268,7 @@ public: explicit SmallPtrSetIterator(const void *const *BP, const void *const *E) : SmallPtrSetIteratorImpl(BP, E) {} - // Most methods provided by baseclass. + // Most methods are provided by the base class. const PtrTy operator*() const { assert(Bucket < End); @@ -327,14 +325,8 @@ class SmallPtrSetImpl : public SmallPtrSetImplBase { using ConstPtrTraits = PointerLikeTypeTraits; protected: - // Constructors that forward to the base. - SmallPtrSetImpl(const void **SmallStorage, const SmallPtrSetImpl &that) - : SmallPtrSetImplBase(SmallStorage, that) {} - SmallPtrSetImpl(const void **SmallStorage, unsigned SmallSize, - SmallPtrSetImpl &&that) - : SmallPtrSetImplBase(SmallStorage, SmallSize, std::move(that)) {} - explicit SmallPtrSetImpl(const void **SmallStorage, unsigned SmallSize) - : SmallPtrSetImplBase(SmallStorage, SmallSize) {} + // Forward constructors to the base. + using SmallPtrSetImplBase::SmallPtrSetImplBase; public: using iterator = SmallPtrSetIterator; @@ -353,17 +345,28 @@ public: return std::make_pair(makeIterator(p.first), p.second); } + /// Insert the given pointer with an iterator hint that is ignored. This is + /// identical to calling insert(Ptr), but allows SmallPtrSet to be used by + /// std::insert_iterator and std::inserter(). + iterator insert(iterator, PtrType Ptr) { + return insert(Ptr).first; + } + /// erase - If the set contains the specified pointer, remove it and return /// true, otherwise return false. bool erase(PtrType Ptr) { return erase_imp(PtrTraits::getAsVoidPointer(Ptr)); } - /// count - Return 1 if the specified pointer is in the set, 0 otherwise. - size_type count(ConstPtrType Ptr) const { return find(Ptr) != end() ? 1 : 0; } + size_type count(ConstPtrType Ptr) const { + return find_imp(ConstPtrTraits::getAsVoidPointer(Ptr)) != EndPointer(); + } iterator find(ConstPtrType Ptr) const { return makeIterator(find_imp(ConstPtrTraits::getAsVoidPointer(Ptr))); } + bool contains(ConstPtrType Ptr) const { + return find_imp(ConstPtrTraits::getAsVoidPointer(Ptr)) != EndPointer(); + } template void insert(IterT I, IterT E) { @@ -387,6 +390,32 @@ private: } }; +/// Equality comparison for SmallPtrSet. +/// +/// Iterates over elements of LHS confirming that each value from LHS is also in +/// RHS, and that no additional values are in RHS. +template +bool operator==(const SmallPtrSetImpl &LHS, + const SmallPtrSetImpl &RHS) { + if (LHS.size() != RHS.size()) + return false; + + for (const auto *KV : LHS) + if (!RHS.count(KV)) + return false; + + return true; +} + +/// Inequality comparison for SmallPtrSet. +/// +/// Equivalent to !(LHS == RHS). +template +bool operator!=(const SmallPtrSetImpl &LHS, + const SmallPtrSetImpl &RHS) { + return !(LHS == RHS); +} + /// SmallPtrSet - This class implements a set which is optimized for holding /// SmallSize or less elements. This internally rounds up SmallSize to the next /// power of two if it is not already a power of two. See the comments above @@ -460,4 +489,4 @@ namespace std { } // end namespace std -#endif // LLVM_ADT_SMALLPTRSET_H +#endif // WPIUTIL_WPI_SMALLPTRSET_H diff --git a/wpiutil/src/main/native/include/wpi/SmallSet.h b/wpiutil/src/main/native/include/wpi/SmallSet.h index dca204e5e9..f914ec9e53 100644 --- a/wpiutil/src/main/native/include/wpi/SmallSet.h +++ b/wpiutil/src/main/native/include/wpi/SmallSet.h @@ -1,9 +1,8 @@ //===- llvm/ADT/SmallSet.h - 'Normally small' sets --------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -16,8 +15,8 @@ #include "wpi/SmallPtrSet.h" #include "wpi/SmallVector.h" -#include "wpi/Compiler.h" #include "wpi/iterator.h" +#include "wpi/Compiler.h" #include "wpi/type_traits.h" #include #include @@ -233,6 +232,13 @@ public: return {Set.end()}; } + /// Check if the SmallSet contains the given element. + bool contains(const T &V) const { + if (isSmall()) + return vfind(V) != Vector.end(); + return Set.find(V) != Set.end(); + } + private: bool isSmall() const { return Set.empty(); } @@ -249,6 +255,31 @@ private: template class SmallSet : public SmallPtrSet {}; +/// Equality comparison for SmallSet. +/// +/// Iterates over elements of LHS confirming that each element is also a member +/// of RHS, and that RHS contains no additional values. +/// Equivalent to N calls to RHS.count. +/// For small-set mode amortized complexity is O(N^2) +/// For large-set mode amortized complexity is linear, worst case is O(N^2) (if +/// every hash collides). +template +bool operator==(const SmallSet &LHS, const SmallSet &RHS) { + if (LHS.size() != RHS.size()) + return false; + + // All elements in LHS must also be in RHS + return std::all_of(LHS.begin(), LHS.end(), [&RHS](const T &E) { return RHS.count(E); }); +} + +/// Inequality comparison for SmallSet. +/// +/// Equivalent to !(LHS == RHS). See operator== for performance notes. +template +bool operator!=(const SmallSet &LHS, const SmallSet &RHS) { + return !(LHS == RHS); +} + } // end namespace wpi -#endif // LLVM_ADT_SMALLSET_H +#endif // WPIUTIL_WPI_SMALLSET_H diff --git a/wpiutil/src/main/native/include/wpi/SmallString.h b/wpiutil/src/main/native/include/wpi/SmallString.h index da4b2e4b4e..3b51130d3c 100644 --- a/wpiutil/src/main/native/include/wpi/SmallString.h +++ b/wpiutil/src/main/native/include/wpi/SmallString.h @@ -1,9 +1,8 @@ //===- llvm/ADT/SmallString.h - 'Normally small' strings --------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -29,66 +28,58 @@ public: SmallString() = default; /// Initialize from a std::string_view. - SmallString(std::string_view S) - : SmallVector(S.begin(), S.end()) {} + SmallString(std::string_view S) : SmallVector(S.begin(), S.end()) {} + + /// Initialize by concatenating a list of std::string_views. + SmallString(std::initializer_list Refs) + : SmallVector() { + this->append(Refs); + } /// Initialize with a range. template SmallString(ItTy S, ItTy E) : SmallVector(S, E) {} - // Note that in order to add new overloads for append & assign, we have to - // duplicate the inherited versions so as not to inadvertently hide them. - /// @} /// @name String Assignment /// @{ - /// Assign from a repeated element. - void assign(size_t NumElts, char Elt) { - this->SmallVectorImpl::assign(NumElts, Elt); - } - - /// Assign from an iterator pair. - template - void assign(in_iter S, in_iter E) { - this->clear(); - SmallVectorImpl::append(S, E); - } + using SmallVector::assign; /// Assign from a std::string_view. void assign(std::string_view RHS) { - this->clear(); - SmallVectorImpl::append(RHS.begin(), RHS.end()); + SmallVectorImpl::assign(RHS.begin(), RHS.end()); } - /// Assign from a SmallVector. - void assign(const SmallVectorImpl &RHS) { + /// Assign from a list of std::string_views. + void assign(std::initializer_list Refs) { this->clear(); - SmallVectorImpl::append(RHS.begin(), RHS.end()); + append(Refs); } /// @} /// @name String Concatenation /// @{ - /// Append from an iterator pair. - template - void append(in_iter S, in_iter E) { - SmallVectorImpl::append(S, E); - } - - void append(size_t NumInputs, char Elt) { - SmallVectorImpl::append(NumInputs, Elt); - } + using SmallVector::append; /// Append from a std::string_view. void append(std::string_view RHS) { SmallVectorImpl::append(RHS.begin(), RHS.end()); } - /// Append from a SmallVector. - void append(const SmallVectorImpl &RHS) { - SmallVectorImpl::append(RHS.begin(), RHS.end()); + /// Append from a list of std::string_views. + void append(std::initializer_list Refs) { + size_t SizeNeeded = this->size(); + for (std::string_view Ref : Refs) + SizeNeeded += Ref.size(); + this->reserve(SizeNeeded); + auto CurEnd = this->end(); + for (std::string_view Ref : Refs) { + this->uninitialized_copy(Ref.begin(), Ref.end(), CurEnd); + CurEnd += Ref.size(); + } + this->set_size(SizeNeeded); } /// @} @@ -185,10 +176,7 @@ public: // Extra methods. /// Explicit conversion to std::string_view. - std::string_view str() const { return {this->begin(), this->size()}; } - - /// Explicit conversion to std::string. - std::string string() const { return {this->begin(), this->size()}; } + std::string_view str() const { return std::string_view(this->begin(), this->size()); } // TODO: Make this const, if it's safe... const char* c_str() { @@ -200,13 +188,16 @@ public: /// Implicit conversion to std::string_view. operator std::string_view() const { return str(); } + /// Explicit conversion to std::string. + std::string string() const { return {this->begin(), this->size()}; } + /// Implicit conversion to std::string. operator std::string() const { return string(); } // Extra operators. - const SmallString &operator=(std::string_view RHS) { - this->clear(); - return *this += RHS; + SmallString &operator=(std::string_view RHS) { + this->assign(RHS); + return *this; } SmallString &operator+=(std::string_view RHS) { @@ -221,4 +212,4 @@ public: } // end namespace wpi -#endif // LLVM_ADT_SMALLSTRING_H +#endif // WPIUTIL_WPI_SMALLSTRING_H diff --git a/wpiutil/src/main/native/include/wpi/SmallVector.h b/wpiutil/src/main/native/include/wpi/SmallVector.h index 430f85a5c5..fb8d3d840d 100644 --- a/wpiutil/src/main/native/include/wpi/SmallVector.h +++ b/wpiutil/src/main/native/include/wpi/SmallVector.h @@ -1,9 +1,8 @@ //===- llvm/ADT/SmallVector.h - 'Normally small' vectors --------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -23,9 +22,8 @@ #endif #include "wpi/iterator_range.h" -#include "wpi/AlignOf.h" #include "wpi/Compiler.h" -#include "wpi/MathExtras.h" +#include "wpi/ErrorHandling.h" #include "wpi/MemAlloc.h" #include "wpi/type_traits.h" #include @@ -33,8 +31,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -42,24 +42,40 @@ namespace wpi { -/// This is all the non-templated stuff common to all SmallVectors. +/// This is all the stuff common to all SmallVectors. +/// +/// The template parameter specifies the type which should be used to hold the +/// Size and Capacity of the SmallVector, so it can be adjusted. +/// Using 32 bit size is desirable to shrink the size of the SmallVector. +/// Using 64 bit size is desirable for cases like SmallVector, where a +/// 32 bit size would limit the vector to ~4GB. SmallVectors are used for +/// buffering bitcode output - which can exceed 4GB. class SmallVectorBase { protected: void *BeginX; unsigned Size = 0, Capacity; + /// The maximum value of the Size_T used. + static constexpr size_t SizeTypeMax() { + return (std::numeric_limits::max)(); + } + SmallVectorBase() = delete; - SmallVectorBase(void *FirstEl, size_t Capacity) - : BeginX(FirstEl), Capacity(static_cast(Capacity)) {} + SmallVectorBase(void *FirstEl, size_t TotalCapacity) + : BeginX(FirstEl), Capacity(static_cast(TotalCapacity)) {} + + /// This is a helper for \a grow() that's out of line to reduce code + /// duplication. This function will report a fatal error if it can't grow at + /// least to \p MinSize. + void *mallocForGrow(size_t MinSize, size_t TSize, size_t &NewCapacity); /// This is an implementation of the grow() method which only works /// on POD-like data types and is out of line to reduce code duplication. - void grow_pod(void *FirstEl, size_t MinCapacity, size_t TSize); + /// This function will report a fatal error if it cannot increase capacity. + void grow_pod(void *FirstEl, size_t MinSize, size_t TSize); public: - LLVM_ATTRIBUTE_ALWAYS_INLINE size_t size() const { return Size; } - LLVM_ATTRIBUTE_ALWAYS_INLINE size_t capacity() const { return Capacity; } LLVM_NODISCARD bool empty() const { return !Size; } @@ -73,23 +89,27 @@ public: /// of the buffer when they know that more elements are available, and only /// update the size later. This avoids the cost of value initializing elements /// which will only be overwritten. - void set_size(size_t Size) { - assert(Size <= capacity()); - this->Size = static_cast(Size); + void set_size(size_t N) { + assert(N <= capacity()); + Size = static_cast(N); } }; /// Figure out the offset of the first element. template struct SmallVectorAlignmentAndSize { - AlignedCharArrayUnion Base; - AlignedCharArrayUnion FirstEl; + alignas(SmallVectorBase) char Base[sizeof( + SmallVectorBase)]; + alignas(T) char FirstEl[sizeof(T)]; }; /// This is the part of SmallVectorTemplateBase which does not depend on whether /// the type T is a POD. The extra dummy template argument is used by ArrayRef /// to avoid unnecessarily requiring T to be complete. template -class SmallVectorTemplateCommon : public SmallVectorBase { +class SmallVectorTemplateCommon + : public SmallVectorBase { + using Base = SmallVectorBase; + /// Find the address of the first element. For this pointer math to be valid /// with small-size of 0 for T with lots of alignment, it's important that /// SmallVectorStorage is properly-aligned even for small-size of 0. @@ -101,21 +121,116 @@ class SmallVectorTemplateCommon : public SmallVectorBase { // Space after 'FirstEl' is clobbered, do not add any instance vars after it. protected: - SmallVectorTemplateCommon(size_t Size) - : SmallVectorBase(getFirstEl(), Size) {} + SmallVectorTemplateCommon(size_t Size) : Base(getFirstEl(), Size) {} - void grow_pod(size_t MinCapacity, size_t TSize) { - SmallVectorBase::grow_pod(getFirstEl(), MinCapacity, TSize); + void grow_pod(size_t MinSize, size_t TSize) { + Base::grow_pod(getFirstEl(), MinSize, TSize); } /// Return true if this is a smallvector which has not had dynamic /// memory allocated for it. - bool isSmall() const { return BeginX == getFirstEl(); } + bool isSmall() const { return this->BeginX == getFirstEl(); } /// Put this vector in a state of being small. void resetToSmall() { - BeginX = getFirstEl(); - Size = Capacity = 0; // FIXME: Setting Capacity to 0 is suspect. + this->BeginX = getFirstEl(); + this->Size = this->Capacity = 0; // FIXME: Setting Capacity to 0 is suspect. + } + + /// Return true if V is an internal reference to the given range. + bool isReferenceToRange(const void *V, const void *First, const void *Last) const { + // Use std::less to avoid UB. + std::less<> LessThan; + return !LessThan(V, First) && LessThan(V, Last); + } + + /// Return true if V is an internal reference to this vector. + bool isReferenceToStorage(const void *V) const { + return isReferenceToRange(V, this->begin(), this->end()); + } + + /// Return true if First and Last form a valid (possibly empty) range in this + /// vector's storage. + bool isRangeInStorage(const void *First, const void *Last) const { + // Use std::less to avoid UB. + std::less<> LessThan; + return !LessThan(First, this->begin()) && !LessThan(Last, First) && + !LessThan(this->end(), Last); + } + + /// Return true unless Elt will be invalidated by resizing the vector to + /// NewSize. + bool isSafeToReferenceAfterResize(const void *Elt, size_t NewSize) { + // Past the end. + if (LLVM_LIKELY(!isReferenceToStorage(Elt))) + return true; + + // Return false if Elt will be destroyed by shrinking. + if (NewSize <= this->size()) + return Elt < this->begin() + NewSize; + + // Return false if we need to grow. + return NewSize <= this->capacity(); + } + + /// Check whether Elt will be invalidated by resizing the vector to NewSize. + void assertSafeToReferenceAfterResize(const void *Elt, size_t NewSize) { + assert(isSafeToReferenceAfterResize(Elt, NewSize) && + "Attempting to reference an element of the vector in an operation " + "that invalidates it"); + } + + /// Check whether Elt will be invalidated by increasing the size of the + /// vector by N. + void assertSafeToAdd(const void *Elt, size_t N = 1) { + this->assertSafeToReferenceAfterResize(Elt, this->size() + N); + } + + /// Check whether any part of the range will be invalidated by clearing. + void assertSafeToReferenceAfterClear(const T *From, const T *To) { + if (From == To) + return; + this->assertSafeToReferenceAfterResize(From, 0); + this->assertSafeToReferenceAfterResize(To - 1, 0); + } + template < + class ItTy, + std::enable_if_t, T *>::value, + bool> = false> + void assertSafeToReferenceAfterClear(ItTy, ItTy) {} + + /// Check whether any part of the range will be invalidated by growing. + void assertSafeToAddRange(const T *From, const T *To) { + if (From == To) + return; + this->assertSafeToAdd(From, To - From); + this->assertSafeToAdd(To - 1, To - From); + } + template < + class ItTy, + std::enable_if_t, T *>::value, + bool> = false> + void assertSafeToAddRange(ItTy, ItTy) {} + + /// Reserve enough space to add one element, and return the updated element + /// pointer in case it was a reference to the storage. + template + static const T *reserveForParamAndGetAddressImpl(U *This, const T &Elt, + size_t N) { + size_t NewSize = This->size() + N; + if (LLVM_LIKELY(NewSize <= This->capacity())) + return &Elt; + + bool ReferencesStorage = false; + int64_t Index = -1; + if (!U::TakesParamByValue) { + if (LLVM_UNLIKELY(This->isReferenceToStorage(&Elt))) { + ReferencesStorage = true; + Index = &Elt - This->begin(); + } + } + This->grow(NewSize); + return ReferencesStorage ? This->begin() + Index : &Elt; } public: @@ -133,14 +248,14 @@ public: using pointer = T *; using const_pointer = const T *; + using Base::capacity; + using Base::empty; + using Base::size; + // forward iterator creation methods. - LLVM_ATTRIBUTE_ALWAYS_INLINE iterator begin() { return (iterator)this->BeginX; } - LLVM_ATTRIBUTE_ALWAYS_INLINE const_iterator begin() const { return (const_iterator)this->BeginX; } - LLVM_ATTRIBUTE_ALWAYS_INLINE iterator end() { return begin() + size(); } - LLVM_ATTRIBUTE_ALWAYS_INLINE const_iterator end() const { return begin() + size(); } // reverse iterator creation methods. @@ -150,7 +265,9 @@ public: const_reverse_iterator rend() const { return const_reverse_iterator(begin());} size_type size_in_bytes() const { return size() * sizeof(T); } - size_type max_size() const { return size_type(-1) / sizeof(T); } + size_type max_size() const { + return (std::min)(this->SizeTypeMax(), size_type(-1) / sizeof(T)); + } size_t capacity_in_bytes() const { return capacity() * sizeof(T); } @@ -159,12 +276,10 @@ public: /// Return a pointer to the vector's buffer, even if empty(). const_pointer data() const { return const_pointer(begin()); } - LLVM_ATTRIBUTE_ALWAYS_INLINE reference operator[](size_type idx) { assert(idx < size()); return begin()[idx]; } - LLVM_ATTRIBUTE_ALWAYS_INLINE const_reference operator[](size_type idx) const { assert(idx < size()); return begin()[idx]; @@ -189,11 +304,24 @@ public: } }; -/// SmallVectorTemplateBase - This is where we put method -/// implementations that are designed to work with non-POD-like T's. -template ::value> +/// SmallVectorTemplateBase - This is where we put +/// method implementations that are designed to work with non-trivial T's. +/// +/// We approximate is_trivially_copyable with trivial move/copy construction and +/// trivial destruction. While the standard doesn't specify that you're allowed +/// copy these types with memcpy, there is no way for the type to observe this. +/// This catches the important case of std::pair, which is not +/// trivially assignable. +template ::value) && + (is_trivially_move_constructible::value) && + std::is_trivially_destructible::value> class SmallVectorTemplateBase : public SmallVectorTemplateCommon { + friend class SmallVectorTemplateCommon; + protected: + static constexpr bool TakesParamByValue = false; + using ValueParamT = const T &; + SmallVectorTemplateBase(size_t Size) : SmallVectorTemplateCommon(Size) {} static void destroy_range(T *S, T *E) { @@ -223,18 +351,68 @@ protected: /// element, or MinSize more elements if specified. void grow(size_t MinSize = 0); + /// Create a new allocation big enough for \p MinSize and pass back its size + /// in \p NewCapacity. This is the first section of \a grow(). + T *mallocForGrow(size_t MinSize, size_t &NewCapacity) { + return static_cast( + SmallVectorBase::mallocForGrow( + MinSize, sizeof(T), NewCapacity)); + } + + /// Move existing elements over to the new allocation \p NewElts, the middle + /// section of \a grow(). + void moveElementsForGrow(T *NewElts); + + /// Transfer ownership of the allocation, finishing up \a grow(). + void takeAllocationForGrow(T *NewElts, size_t NewCapacity); + + /// Reserve enough space to add one element, and return the updated element + /// pointer in case it was a reference to the storage. + const T *reserveForParamAndGetAddress(const T &Elt, size_t N = 1) { + return this->reserveForParamAndGetAddressImpl(this, Elt, N); + } + + /// Reserve enough space to add one element, and return the updated element + /// pointer in case it was a reference to the storage. + T *reserveForParamAndGetAddress(T &Elt, size_t N = 1) { + return const_cast( + this->reserveForParamAndGetAddressImpl(this, Elt, N)); + } + + static T &&forward_value_param(T &&V) { return std::move(V); } + static const T &forward_value_param(const T &V) { return V; } + + void growAndAssign(size_t NumElts, const T &Elt) { + // Grow manually in case Elt is an internal reference. + size_t NewCapacity; + T *NewElts = mallocForGrow(NumElts, NewCapacity); + std::uninitialized_fill_n(NewElts, NumElts, Elt); + this->destroy_range(this->begin(), this->end()); + takeAllocationForGrow(NewElts, NewCapacity); + this->set_size(NumElts); + } + + template T &growAndEmplaceBack(ArgTypes &&... Args) { + // Grow manually in case one of Args is an internal reference. + size_t NewCapacity; + T *NewElts = mallocForGrow(0, NewCapacity); + ::new ((void *)(NewElts + this->size())) T(std::forward(Args)...); + moveElementsForGrow(NewElts); + takeAllocationForGrow(NewElts, NewCapacity); + this->set_size(this->size() + 1); + return this->back(); + } + public: void push_back(const T &Elt) { - if (LLVM_UNLIKELY(this->size() >= this->capacity())) - this->grow(); - ::new ((void*) this->end()) T(Elt); + const T *EltPtr = reserveForParamAndGetAddress(Elt); + ::new ((void *)this->end()) T(*EltPtr); this->set_size(this->size() + 1); } void push_back(T &&Elt) { - if (LLVM_UNLIKELY(this->size() >= this->capacity())) - this->grow(); - ::new ((void*) this->end()) T(::std::move(Elt)); + T *EltPtr = reserveForParamAndGetAddress(Elt); + ::new ((void *)this->end()) T(::std::move(*EltPtr)); this->set_size(this->size() + 1); } @@ -245,22 +423,29 @@ public: }; // Define this out-of-line to dissuade the C++ compiler from inlining it. -template -void SmallVectorTemplateBase::grow(size_t MinSize) { - if (MinSize > UINT32_MAX) - report_bad_alloc_error("SmallVector capacity overflow during allocation"); - - // Always grow, even from zero. - size_t NewCapacity = size_t(NextPowerOf2(this->capacity() + 2)); - NewCapacity = (std::min)((std::max)(NewCapacity, MinSize), size_t(UINT32_MAX)); - T *NewElts = static_cast(wpi::safe_malloc(NewCapacity*sizeof(T))); +template +void SmallVectorTemplateBase::grow(size_t MinSize) { + size_t NewCapacity; + T *NewElts = mallocForGrow(MinSize, NewCapacity); + moveElementsForGrow(NewElts); + takeAllocationForGrow(NewElts, NewCapacity); +} +// Define this out-of-line to dissuade the C++ compiler from inlining it. +template +void SmallVectorTemplateBase::moveElementsForGrow( + T *NewElts) { // Move the elements over. this->uninitialized_move(this->begin(), this->end(), NewElts); // Destroy the original elements. destroy_range(this->begin(), this->end()); +} +// Define this out-of-line to dissuade the C++ compiler from inlining it. +template +void SmallVectorTemplateBase::takeAllocationForGrow( + T *NewElts, size_t NewCapacity) { // If this wasn't grown from the inline copy, deallocate the old space. if (!this->isSmall()) free(this->begin()); @@ -269,12 +454,24 @@ void SmallVectorTemplateBase::grow(size_t MinSize) { this->Capacity = static_cast(NewCapacity); } - -/// SmallVectorTemplateBase - This is where we put method -/// implementations that are designed to work with POD-like T's. +/// SmallVectorTemplateBase - This is where we put +/// method implementations that are designed to work with trivially copyable +/// T's. This allows using memcpy in place of copy/move construction and +/// skipping destruction. template class SmallVectorTemplateBase : public SmallVectorTemplateCommon { + friend class SmallVectorTemplateCommon; + protected: + /// True if it's cheap enough to take parameters by value. Doing so avoids + /// overhead related to mitigations for reference invalidation. + static constexpr bool TakesParamByValue = sizeof(T) <= 2 * sizeof(void *); + + /// Either const T& or T, depending on whether it's cheap enough to take + /// parameters by value. + using ValueParamT = + typename std::conditional::type; + SmallVectorTemplateBase(size_t Size) : SmallVectorTemplateCommon(Size) {} // No need to do a destroy loop for POD's. @@ -301,8 +498,8 @@ protected: template static void uninitialized_copy( T1 *I, T1 *E, T2 *Dest, - typename std::enable_if::type, - T2>::value>::type * = nullptr) { + std::enable_if_t::type, + T2>::value> * = nullptr) { // Use memcpy for PODs iterated by pointers (which includes SmallVector // iterators): std::uninitialized_copy optimizes to memmove, but we can // use memcpy here. Note that I and E are iterators and thus might be @@ -315,11 +512,43 @@ protected: /// least one more element or MinSize if specified. void grow(size_t MinSize = 0) { this->grow_pod(MinSize, sizeof(T)); } + /// Reserve enough space to add one element, and return the updated element + /// pointer in case it was a reference to the storage. + const T *reserveForParamAndGetAddress(const T &Elt, size_t N = 1) { + return this->reserveForParamAndGetAddressImpl(this, Elt, N); + } + + /// Reserve enough space to add one element, and return the updated element + /// pointer in case it was a reference to the storage. + T *reserveForParamAndGetAddress(T &Elt, size_t N = 1) { + return const_cast( + this->reserveForParamAndGetAddressImpl(this, Elt, N)); + } + + /// Copy \p V or return a reference, depending on \a ValueParamT. + static ValueParamT forward_value_param(ValueParamT V) { return V; } + + void growAndAssign(size_t NumElts, T Elt) { + // Elt has been copied in case it's an internal reference, side-stepping + // reference invalidation problems without losing the realloc optimization. + this->set_size(0); + this->grow(NumElts); + std::uninitialized_fill_n(this->begin(), NumElts, Elt); + this->set_size(NumElts); + } + + template T &growAndEmplaceBack(ArgTypes &&... Args) { + // Use push_back with a copy in case Args has an internal reference, + // side-stepping reference invalidation problems without losing the realloc + // optimization. + push_back(T(std::forward(Args)...)); + return this->back(); + } + public: - void push_back(const T &Elt) { - if (LLVM_UNLIKELY(this->size() >= this->capacity())) - this->grow(); - memcpy(reinterpret_cast(this->end()), &Elt, sizeof(T)); + void push_back(ValueParamT Elt) { + const T *EltPtr = reserveForParamAndGetAddress(Elt); + memcpy(reinterpret_cast(this->end()), EltPtr, sizeof(T)); this->set_size(this->size() + 1); } @@ -335,12 +564,16 @@ class SmallVectorImpl : public SmallVectorTemplateBase { public: using iterator = typename SuperClass::iterator; using const_iterator = typename SuperClass::const_iterator; + using reference = typename SuperClass::reference; using size_type = typename SuperClass::size_type; protected: + using SmallVectorTemplateBase::TakesParamByValue; + using ValueParamT = typename SuperClass::ValueParamT; + // Default ctor - Initialize to empty. explicit SmallVectorImpl(unsigned N) - : SmallVectorTemplateBase::value>(N) {} + : SmallVectorTemplateBase(N) {} public: SmallVectorImpl(const SmallVectorImpl &) = delete; @@ -357,29 +590,38 @@ public: this->Size = 0; } - void resize(size_type N) { +private: + template void resizeImpl(size_type N) { if (N < this->size()) { - this->destroy_range(this->begin()+N, this->end()); - this->set_size(N); + this->pop_back_n(this->size() - N); } else if (N > this->size()) { - if (this->capacity() < N) - this->grow(N); + this->reserve(N); for (auto I = this->end(), E = this->begin() + N; I != E; ++I) - new (&*I) T(); + if (ForOverwrite) + new (&*I) T; + else + new (&*I) T(); this->set_size(N); } } - void resize(size_type N, const T &NV) { +public: + void resize(size_type N) { resizeImpl(N); } + + /// Like resize, but \ref T is POD, the new values won't be initialized. + void resize_for_overwrite(size_type N) { resizeImpl(N); } + + void resize(size_type N, ValueParamT NV) { + if (N == this->size()) + return; + if (N < this->size()) { - this->destroy_range(this->begin()+N, this->end()); - this->set_size(N); - } else if (N > this->size()) { - if (this->capacity() < N) - this->grow(N); - std::uninitialized_fill(this->end(), this->begin()+N, NV); - this->set_size(N); + this->pop_back_n(this->size() - N); + return; } + + // N > this->size(). Defer to append. + this->append(N - this->size(), NV); } void reserve(size_type N) { @@ -387,6 +629,12 @@ public: this->grow(N); } + void pop_back_n(size_type NumItems) { + assert(this->size() >= NumItems); + this->destroy_range(this->end() - NumItems, this->end()); + this->set_size(this->size() - NumItems); + } + LLVM_NODISCARD T pop_back_val() { T Result = ::std::move(this->back()); this->pop_back(); @@ -397,28 +645,21 @@ public: /// Add the specified range to the end of the SmallVector. template ::iterator_category, - std::input_iterator_tag>::value>::type> + std::input_iterator_tag>::value>> void append(in_iter in_start, in_iter in_end) { + this->assertSafeToAddRange(in_start, in_end); size_type NumInputs = std::distance(in_start, in_end); - // Grow allocated space if needed. - if (NumInputs > this->capacity() - this->size()) - this->grow(this->size()+NumInputs); - - // Copy the new elements over. + this->reserve(this->size() + NumInputs); this->uninitialized_copy(in_start, in_end, this->end()); this->set_size(this->size() + NumInputs); } - /// Add the specified range to the end of the SmallVector. - void append(size_type NumInputs, const T &Elt) { - // Grow allocated space if needed. - if (NumInputs > this->capacity() - this->size()) - this->grow(this->size()+NumInputs); - - // Copy the new elements over. - std::uninitialized_fill_n(this->end(), NumInputs, Elt); + /// Append \p NumInputs copies of \p Elt to the end. + void append(size_type NumInputs, ValueParamT Elt) { + const T *EltPtr = this->reserveForParamAndGetAddress(Elt, NumInputs); + std::uninitialized_fill_n(this->end(), NumInputs, *EltPtr); this->set_size(this->size() + NumInputs); } @@ -426,22 +667,33 @@ public: append(IL.begin(), IL.end()); } + void append(const SmallVectorImpl &RHS) { append(RHS.begin(), RHS.end()); } + + void assign(size_type NumElts, ValueParamT Elt) { + // Note that Elt could be an internal reference. + if (NumElts > this->capacity()) { + this->growAndAssign(NumElts, Elt); + return; + } + + // Assign over existing elements. + std::fill_n(this->begin(), (std::min)(NumElts, this->size()), Elt); + if (NumElts > this->size()) + std::uninitialized_fill_n(this->end(), NumElts - this->size(), Elt); + else if (NumElts < this->size()) + this->destroy_range(this->begin() + NumElts, this->end()); + this->set_size(NumElts); + } + // FIXME: Consider assigning over existing elements, rather than clearing & // re-initializing them - for all assign(...) variants. - void assign(size_type NumElts, const T &Elt) { - clear(); - if (this->capacity() < NumElts) - this->grow(NumElts); - this->set_size(NumElts); - std::uninitialized_fill(this->begin(), this->end(), Elt); - } - template ::iterator_category, - std::input_iterator_tag>::value>::type> + std::input_iterator_tag>::value>> void assign(in_iter in_start, in_iter in_end) { + this->assertSafeToReferenceAfterClear(in_start, in_end); clear(); append(in_start, in_end); } @@ -451,12 +703,13 @@ public: append(IL); } + void assign(const SmallVectorImpl &RHS) { assign(RHS.begin(), RHS.end()); } + iterator erase(const_iterator CI) { // Just cast away constness because this is a non-const member function. iterator I = const_cast(CI); - assert(I >= this->begin() && "Iterator to erase is out of bounds."); - assert(I < this->end() && "Erasing at past-the-end iterator."); + assert(this->isReferenceToStorage(CI) && "Iterator to erase is out of bounds."); iterator N = I; // Shift all elts down one. @@ -471,9 +724,7 @@ public: iterator S = const_cast(CS); iterator E = const_cast(CE); - assert(S >= this->begin() && "Range to erase is out of bounds."); - assert(S <= E && "Trying to erase invalid range."); - assert(E <= this->end() && "Trying to erase past the end."); + assert(this->isRangeInStorage(S, E) && "Range to erase is out of bounds."); iterator N = S; // Shift all elts down. @@ -484,20 +735,26 @@ public: return(N); } - iterator insert(iterator I, T &&Elt) { +private: + template iterator insert_one_impl(iterator I, ArgType &&Elt) { + // Callers ensure that ArgType is derived from T. + static_assert( + std::is_same>, + T>::value, + "ArgType must be derived from T!"); + if (I == this->end()) { // Important special case for empty vector. - this->push_back(::std::move(Elt)); + this->push_back(::std::forward(Elt)); return this->end()-1; } - assert(I >= this->begin() && "Insertion iterator is out of bounds."); - assert(I <= this->end() && "Inserting past the end of the vector."); + assert(this->isReferenceToStorage(I) && "Insertion iterator is out of bounds."); - if (this->size() >= this->capacity()) { - size_t EltNo = I-this->begin(); - this->grow(); - I = this->begin()+EltNo; - } + // Grow if necessary. + size_t Index = I - this->begin(); + std::remove_reference_t *EltPtr = + this->reserveForParamAndGetAddress(Elt); + I = this->begin() + Index; ::new ((void*) this->end()) T(::std::move(this->back())); // Push everything else over. @@ -505,45 +762,26 @@ public: this->set_size(this->size() + 1); // If we just moved the element we're inserting, be sure to update - // the reference. - T *EltPtr = &Elt; - if (I <= EltPtr && EltPtr < this->end()) + // the reference (never happens if TakesParamByValue). + static_assert(!TakesParamByValue || std::is_same::value, + "ArgType must be 'T' when taking by value!"); + if (!TakesParamByValue && this->isReferenceToRange(EltPtr, I, this->end())) ++EltPtr; - *I = ::std::move(*EltPtr); + *I = ::std::forward(*EltPtr); return I; } +public: + iterator insert(iterator I, T &&Elt) { + return insert_one_impl(I, this->forward_value_param(std::move(Elt))); + } + iterator insert(iterator I, const T &Elt) { - if (I == this->end()) { // Important special case for empty vector. - this->push_back(Elt); - return this->end()-1; - } - - assert(I >= this->begin() && "Insertion iterator is out of bounds."); - assert(I <= this->end() && "Inserting past the end of the vector."); - - if (this->size() >= this->capacity()) { - size_t EltNo = I-this->begin(); - this->grow(); - I = this->begin()+EltNo; - } - ::new ((void*) this->end()) T(std::move(this->back())); - // Push everything else over. - std::move_backward(I, this->end()-1, this->end()); - this->set_size(this->size() + 1); - - // If we just moved the element we're inserting, be sure to update - // the reference. - const T *EltPtr = &Elt; - if (I <= EltPtr && EltPtr < this->end()) - ++EltPtr; - - *I = *EltPtr; - return I; + return insert_one_impl(I, this->forward_value_param(Elt)); } - iterator insert(iterator I, size_type NumToInsert, const T &Elt) { + iterator insert(iterator I, size_type NumToInsert, ValueParamT Elt) { // Convert iterator to elt# to avoid invalidating iterator when we reserve() size_t InsertElt = I - this->begin(); @@ -552,11 +790,11 @@ public: return this->begin()+InsertElt; } - assert(I >= this->begin() && "Insertion iterator is out of bounds."); - assert(I <= this->end() && "Inserting past the end of the vector."); + assert(this->isReferenceToStorage(I) && "Insertion iterator is out of bounds."); - // Ensure there is enough space. - reserve(this->size() + NumToInsert); + // Ensure there is enough space, and get the (maybe updated) address of + // Elt. + const T *EltPtr = this->reserveForParamAndGetAddress(Elt, NumToInsert); // Uninvalidate the iterator. I = this->begin()+InsertElt; @@ -573,7 +811,12 @@ public: // Copy the existing elements that get replaced. std::move_backward(I, OldEnd-NumToInsert, OldEnd); - std::fill_n(I, NumToInsert, Elt); + // If we just moved the element we're inserting, be sure to update + // the reference (never happens if TakesParamByValue). + if (!TakesParamByValue && I <= EltPtr && EltPtr < this->end()) + EltPtr += NumToInsert; + + std::fill_n(I, NumToInsert, *EltPtr); return I; } @@ -586,18 +829,23 @@ public: size_t NumOverwritten = OldEnd-I; this->uninitialized_move(I, OldEnd, this->end()-NumOverwritten); + // If we just moved the element we're inserting, be sure to update + // the reference (never happens if TakesParamByValue). + if (!TakesParamByValue && I <= EltPtr && EltPtr < this->end()) + EltPtr += NumToInsert; + // Replace the overwritten part. - std::fill_n(I, NumOverwritten, Elt); + std::fill_n(I, NumOverwritten, *EltPtr); // Insert the non-overwritten middle part. - std::uninitialized_fill_n(OldEnd, NumToInsert-NumOverwritten, Elt); + std::uninitialized_fill_n(OldEnd, NumToInsert - NumOverwritten, *EltPtr); return I; } template ::iterator_category, - std::input_iterator_tag>::value>::type> + std::input_iterator_tag>::value>> iterator insert(iterator I, ItTy From, ItTy To) { // Convert iterator to elt# to avoid invalidating iterator when we reserve() size_t InsertElt = I - this->begin(); @@ -607,8 +855,10 @@ public: return this->begin()+InsertElt; } - assert(I >= this->begin() && "Insertion iterator is out of bounds."); - assert(I <= this->end() && "Inserting past the end of the vector."); + assert(this->isReferenceToStorage(I) && "Insertion iterator is out of bounds."); + + // Check that the reserve that follows doesn't invalidate the iterators. + this->assertSafeToAddRange(From, To); size_t NumToInsert = std::distance(From, To); @@ -658,11 +908,13 @@ public: insert(I, IL.begin(), IL.end()); } - template void emplace_back(ArgTypes &&... Args) { + template reference emplace_back(ArgTypes &&... Args) { if (LLVM_UNLIKELY(this->size() >= this->capacity())) - this->grow(); + return this->growAndEmplaceBack(std::forward(Args)...); + ::new ((void *)this->end()) T(std::forward(Args)...); this->set_size(this->size() + 1); + return this->back(); } SmallVectorImpl &operator=(const SmallVectorImpl &RHS); @@ -694,10 +946,8 @@ void SmallVectorImpl::swap(SmallVectorImpl &RHS) { std::swap(this->Capacity, RHS.Capacity); return; } - if (RHS.size() > this->capacity()) - this->grow(RHS.size()); - if (this->size() > RHS.capacity()) - RHS.grow(this->size()); + this->reserve(RHS.size()); + RHS.reserve(this->size()); // Swap the shared elements. size_t NumShared = this->size(); @@ -752,8 +1002,7 @@ SmallVectorImpl &SmallVectorImpl:: // FIXME: don't do this if they're efficiently moveable. if (this->capacity() < RHSSize) { // Destroy current elements. - this->destroy_range(this->begin(), this->end()); - this->set_size(0); + this->clear(); CurSize = 0; this->grow(RHSSize); } else if (CurSize) { @@ -812,8 +1061,7 @@ SmallVectorImpl &SmallVectorImpl::operator=(SmallVectorImpl &&RHS) { // elements. if (this->capacity() < RHSSize) { // Destroy current elements. - this->destroy_range(this->begin(), this->end()); - this->set_size(0); + this->clear(); CurSize = 0; this->grow(RHSSize); } else if (CurSize) { @@ -836,13 +1084,71 @@ SmallVectorImpl &SmallVectorImpl::operator=(SmallVectorImpl &&RHS) { /// to avoid allocating unnecessary storage. template struct SmallVectorStorage { - AlignedCharArrayUnion InlineElts[N]; + alignas(T) char InlineElts[N * sizeof(T)]; }; /// We need the storage to be properly aligned even for small-size of 0 so that /// the pointer math in \a SmallVectorTemplateCommon::getFirstEl() is /// well-defined. -template struct alignas(alignof(T)) SmallVectorStorage {}; +template struct alignas(T) SmallVectorStorage {}; + +/// Forward declaration of SmallVector so that +/// calculateSmallVectorDefaultInlinedElements can reference +/// `sizeof(SmallVector)`. +template class LLVM_GSL_OWNER SmallVector; + +/// Helper class for calculating the default number of inline elements for +/// `SmallVector`. +/// +/// This should be migrated to a constexpr function when our minimum +/// compiler support is enough for multi-statement constexpr functions. +template struct CalculateSmallVectorDefaultInlinedElements { + // Parameter controlling the default number of inlined elements + // for `SmallVector`. + // + // The default number of inlined elements ensures that + // 1. There is at least one inlined element. + // 2. `sizeof(SmallVector) <= kPreferredSmallVectorSizeof` unless + // it contradicts 1. + static constexpr size_t kPreferredSmallVectorSizeof = 64; + + // static_assert that sizeof(T) is not "too big". + // + // Because our policy guarantees at least one inlined element, it is possible + // for an arbitrarily large inlined element to allocate an arbitrarily large + // amount of inline storage. We generally consider it an antipattern for a + // SmallVector to allocate an excessive amount of inline storage, so we want + // to call attention to these cases and make sure that users are making an + // intentional decision if they request a lot of inline storage. + // + // We want this assertion to trigger in pathological cases, but otherwise + // not be too easy to hit. To accomplish that, the cutoff is actually somewhat + // larger than kPreferredSmallVectorSizeof (otherwise, + // `SmallVector>` would be one easy way to trip it, and that + // pattern seems useful in practice). + // + // One wrinkle is that this assertion is in theory non-portable, since + // sizeof(T) is in general platform-dependent. However, we don't expect this + // to be much of an issue, because most LLVM development happens on 64-bit + // hosts, and therefore sizeof(T) is expected to *decrease* when compiled for + // 32-bit hosts, dodging the issue. The reverse situation, where development + // happens on a 32-bit host and then fails due to sizeof(T) *increasing* on a + // 64-bit host, is expected to be very rare. + static_assert( + sizeof(T) <= 256, + "You are trying to use a default number of inlined elements for " + "`SmallVector` but `sizeof(T)` is really big! Please use an " + "explicit number of inlined elements with `SmallVector` to make " + "sure you really want that much inline storage."); + + // Discount the size of the header itself when calculating the maximum inline + // bytes. + static constexpr size_t PreferredInlineBytes = + kPreferredSmallVectorSizeof - sizeof(SmallVector); + static constexpr size_t NumElementsThatFit = PreferredInlineBytes / sizeof(T); + static constexpr size_t value = + NumElementsThatFit == 0 ? 1 : NumElementsThatFit; +}; /// This is a 'vector' (really, a variable-sized array), optimized /// for the case when the array is small. It contains some number of elements @@ -850,10 +1156,20 @@ template struct alignas(alignof(T)) SmallVectorStorage {}; /// elements is below that threshold. This allows normal "small" cases to be /// fast without losing generality for large inputs. /// -/// Note that this does not attempt to be exception safe. +/// \note +/// In the absence of a well-motivated choice for the number of inlined +/// elements \p N, it is recommended to use \c SmallVector (that is, +/// omitting the \p N). This will choose a default number of inlined elements +/// reasonable for allocation on the stack (for example, trying to keep \c +/// sizeof(SmallVector) around 64 bytes). /// -template -class SmallVector : public SmallVectorImpl, SmallVectorStorage { +/// \warning This does not attempt to be exception safe. +/// +/// \see https://llvm.org/docs/ProgrammersManual.html#llvm-adt-smallvector-h +template ::value> +class LLVM_GSL_OWNER SmallVector : public SmallVectorImpl, + SmallVectorStorage { public: SmallVector() : SmallVectorImpl(N) {} @@ -868,9 +1184,9 @@ public: } template ::iterator_category, - std::input_iterator_tag>::value>::type> + std::input_iterator_tag>::value>> SmallVector(ItTy S, ItTy E) : SmallVectorImpl(N) { this->append(S, E); } @@ -890,7 +1206,7 @@ public: SmallVectorImpl::operator=(RHS); } - const SmallVector &operator=(const SmallVector &RHS) { + SmallVector &operator=(const SmallVector &RHS) { SmallVectorImpl::operator=(RHS); return *this; } @@ -905,17 +1221,17 @@ public: SmallVectorImpl::operator=(::std::move(RHS)); } - const SmallVector &operator=(SmallVector &&RHS) { + SmallVector &operator=(SmallVector &&RHS) { SmallVectorImpl::operator=(::std::move(RHS)); return *this; } - const SmallVector &operator=(SmallVectorImpl &&RHS) { + SmallVector &operator=(SmallVectorImpl &&RHS) { SmallVectorImpl::operator=(::std::move(RHS)); return *this; } - const SmallVector &operator=(std::initializer_list IL) { + SmallVector &operator=(std::initializer_list IL) { this->assign(IL); return *this; } @@ -926,6 +1242,17 @@ inline size_t capacity_in_bytes(const SmallVector &X) { return X.capacity_in_bytes(); } +/// Given a range of type R, iterate the entire range and return a +/// SmallVector with elements of the vector. This is useful, for example, +/// when you want to iterate a range and then sort the results. +template +SmallVector()))>::type>::type, + Size> +to_vector(R &&Range) { + return {std::begin(Range), std::end(Range)}; +} + } // end namespace wpi namespace std { @@ -946,4 +1273,4 @@ namespace std { } // end namespace std -#endif // LLVM_ADT_SMALLVECTOR_H +#endif // WPIUTIL_WPI_SMALLVECTOR_H diff --git a/wpiutil/src/main/native/include/wpi/StringMap.h b/wpiutil/src/main/native/include/wpi/StringMap.h index dac2f20914..fc2fd06745 100644 --- a/wpiutil/src/main/native/include/wpi/StringMap.h +++ b/wpiutil/src/main/native/include/wpi/StringMap.h @@ -1,9 +1,8 @@ //===- StringMap.h - String Hash table map interface ------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -14,38 +13,20 @@ #ifndef WPIUTIL_WPI_STRINGMAP_H #define WPIUTIL_WPI_STRINGMAP_H +#include "wpi/StringMapEntry.h" +#include "wpi/MemAlloc.h" #include "wpi/SmallVector.h" #include "wpi/iterator.h" #include "wpi/iterator_range.h" -#include "wpi/MemAlloc.h" #include "wpi/PointerLikeTypeTraits.h" -#include "wpi/ErrorHandling.h" -#include -#include -#include -#include -#include #include #include -#include -#include namespace wpi { -template class StringMapConstIterator; -template class StringMapIterator; -template class StringMapKeyIterator; -template class StringMapEntry; - -/// StringMapEntryBase - Shared base class of StringMapEntry instances. -class StringMapEntryBase { - size_t StrLen; - -public: - explicit StringMapEntryBase(size_t Len) : StrLen(Len) {} - - size_t getKeyLength() const { return StrLen; } -}; +template class StringMapConstIterator; +template class StringMapIterator; +template class StringMapKeyIterator; /// StringMapImpl - This is the base class of StringMap that is shared among /// all of its instantiations. @@ -61,8 +42,7 @@ protected: unsigned ItemSize; protected: - explicit StringMapImpl(unsigned itemSize) - : ItemSize(itemSize) {} + explicit StringMapImpl(unsigned itemSize) : ItemSize(itemSize) {} StringMapImpl(StringMapImpl &&RHS) noexcept : TheTable(RHS.TheTable), NumBuckets(RHS.NumBuckets), NumItems(RHS.NumItems), NumTombstones(RHS.NumTombstones), @@ -101,10 +81,12 @@ protected: void init(unsigned Size); public: + static constexpr uintptr_t TombstoneIntVal = + static_cast(-1) + << PointerLikeTypeTraits::NumLowBitsAvailable; + static StringMapEntryBase *getTombstoneVal() { - uintptr_t Val = static_cast(-1); - Val <<= PointerLikeTypeTraits::NumLowBitsAvailable; - return reinterpret_cast(Val); + return reinterpret_cast(TombstoneIntVal); } unsigned getNumBuckets() const { return NumBuckets; } @@ -121,82 +103,6 @@ public: } }; -/// StringMapEntry - This is used to represent one value that is inserted into -/// a StringMap. It contains the Value itself and the key: the string length -/// and data. -template -class StringMapEntry : public StringMapEntryBase { -public: - ValueTy second; - - explicit StringMapEntry(size_t strLen) - : StringMapEntryBase(strLen), second() {} - template - StringMapEntry(size_t strLen, InitTy &&... InitVals) - : StringMapEntryBase(strLen), second(std::forward(InitVals)...) {} - StringMapEntry(StringMapEntry &E) = delete; - - std::string_view getKey() const { - return {getKeyData(), getKeyLength()}; - } - - const ValueTy &getValue() const { return second; } - ValueTy &getValue() { return second; } - - void setValue(const ValueTy &V) { second = V; } - - /// getKeyData - Return the start of the string data that is the key for this - /// value. The string data is always stored immediately after the - /// StringMapEntry object. - const char *getKeyData() const {return reinterpret_cast(this+1);} - - std::string_view first() const { return {getKeyData(), getKeyLength()}; } - - /// Create a StringMapEntry for the specified key construct the value using - /// \p InitiVals. - template - static StringMapEntry *Create(std::string_view Key, InitTy &&... InitVals) { - size_t KeyLength = Key.size(); - - // Allocate a new item with space for the string at the end and a null - // terminator. - size_t AllocSize = sizeof(StringMapEntry) + KeyLength + 1; - - StringMapEntry *NewItem = - static_cast(safe_malloc(AllocSize)); - - // Construct the value. - new (NewItem) StringMapEntry(KeyLength, std::forward(InitVals)...); - - // Copy the string information. - char *StrBuffer = const_cast(NewItem->getKeyData()); - if (KeyLength > 0) - memcpy(StrBuffer, Key.data(), KeyLength); - StrBuffer[KeyLength] = 0; // Null terminate for convenience of clients. - return NewItem; - } - - static StringMapEntry *Create(std::string_view Key) { - return Create(Key, ValueTy()); - } - - /// GetStringMapEntryFromKeyData - Given key data that is known to be embedded - /// into a StringMapEntry, return the StringMapEntry itself. - static StringMapEntry &GetStringMapEntryFromKeyData(const char *KeyData) { - char *Ptr = const_cast(KeyData) - sizeof(StringMapEntry); - return *reinterpret_cast(Ptr); - } - - /// Destroy - Destroy this StringMapEntry, releasing memory back to the - /// specified allocator. - void Destroy() { - // Free memory referenced by the item. - this->~StringMapEntry(); - std::free(static_cast(this)); - } -}; - - /// StringMap - This is an unconventional map that is specialized for handling /// keys that are "strings", which are basically ranges of bytes. This does some /// funky memory allocation and hashing things to make it extremely efficient, @@ -209,7 +115,7 @@ public: StringMap() : StringMapImpl(static_cast(sizeof(MapEntryTy))) {} explicit StringMap(unsigned InitialSize) - : StringMapImpl(InitialSize, static_cast(sizeof(MapEntryTy))) {} + : StringMapImpl(InitialSize, static_cast(sizeof(MapEntryTy))) {} StringMap(std::initializer_list> List) : StringMapImpl(List.size(), static_cast(sizeof(MapEntryTy))) { @@ -268,14 +174,14 @@ public: for (unsigned I = 0, E = NumBuckets; I != E; ++I) { StringMapEntryBase *Bucket = TheTable[I]; if (Bucket && Bucket != getTombstoneVal()) { - static_cast(Bucket)->Destroy(); + static_cast(Bucket)->Destroy(); } } } free(TheTable); } - using key_type = const char*; + using key_type = const char *; using mapped_type = ValueTy; using value_type = StringMapEntry; using size_type = size_t; @@ -283,17 +189,13 @@ public: using const_iterator = StringMapConstIterator; using iterator = StringMapIterator; - iterator begin() { - return iterator(TheTable, NumBuckets == 0); - } - iterator end() { - return iterator(TheTable+NumBuckets, true); - } + iterator begin() { return iterator(TheTable, NumBuckets == 0); } + iterator end() { return iterator(TheTable + NumBuckets, true); } const_iterator begin() const { return const_iterator(TheTable, NumBuckets == 0); } const_iterator end() const { - return const_iterator(TheTable+NumBuckets, true); + return const_iterator(TheTable + NumBuckets, true); } iterator_range> keys() const { @@ -303,14 +205,16 @@ public: iterator find(std::string_view Key) { int Bucket = FindKey(Key); - if (Bucket == -1) return end(); - return iterator(TheTable+Bucket, true); + if (Bucket == -1) + return end(); + return iterator(TheTable + Bucket, true); } const_iterator find(std::string_view Key) const { int Bucket = FindKey(Key); - if (Bucket == -1) return end(); - return const_iterator(TheTable+Bucket, true); + if (Bucket == -1) + return end(); + return const_iterator(TheTable + Bucket, true); } /// lookup - Return the entry for the specified key, or a default @@ -327,10 +231,33 @@ public: ValueTy &operator[](std::string_view Key) { return try_emplace(Key).first->second; } /// count - Return 1 if the element is in the map, 0 otherwise. - size_type count(std::string_view Key) const { - return find(Key) == end() ? 0 : 1; + size_type count(std::string_view Key) const { return find(Key) == end() ? 0 : 1; } + + template + size_type count(const StringMapEntry &MapEntry) const { + return count(MapEntry.getKey()); } + /// equal - check whether both of the containers are equal. + bool operator==(const StringMap &RHS) const { + if (size() != RHS.size()) + return false; + + for (const auto &KeyValue : *this) { + auto FindInRHS = RHS.find(KeyValue.getKey()); + + if (FindInRHS == RHS.end()) + return false; + + if (!(KeyValue.getValue() == FindInRHS->getValue())) + return false; + } + + return true; + } + + bool operator!=(const StringMap &RHS) const { return !(*this == RHS); } + /// insert - Insert the specified key/value pair into the map. If the key /// already exists in the map, return false and ignore the request, otherwise /// insert it and return true. @@ -338,7 +265,7 @@ public: unsigned BucketNo = LookupBucketFor(KeyValue->getKey()); StringMapEntryBase *&Bucket = TheTable[BucketNo]; if (Bucket && Bucket != getTombstoneVal()) - return false; // Already exists in map. + return false; // Already exists in map. if (Bucket == getTombstoneVal()) --NumTombstones; @@ -358,6 +285,16 @@ public: return try_emplace(KV.first, std::move(KV.second)); } + /// Inserts an element or assigns to the current element if the key already + /// exists. The return type is the same as try_emplace. + template + std::pair insert_or_assign(std::string_view Key, V &&Val) { + auto Ret = try_emplace(Key, std::forward(Val)); + if (!Ret.second) + Ret.first->second = std::forward(Val); + return Ret; + } + /// Emplace a new element for the specified key into the map if the key isn't /// already in the map. The bool component of the returned pair is true /// if and only if the insertion takes place, and the iterator component of @@ -382,14 +319,15 @@ public: // clear - Empties out the StringMap void clear() { - if (empty()) return; + if (empty()) + return; // Zap all values, resetting the keys back to non-present (not tombstone), // which is safe because we're removing all elements. for (unsigned I = 0, E = NumBuckets; I != E; ++I) { StringMapEntryBase *&Bucket = TheTable[I]; if (Bucket && Bucket != getTombstoneVal()) { - static_cast(Bucket)->Destroy(); + static_cast(Bucket)->Destroy(); } Bucket = nullptr; } @@ -400,9 +338,7 @@ public: /// remove - Remove the specified key/value pair from the map, but do not /// erase it. This aborts if the key is not in the map. - void remove(MapEntryTy *KeyValue) { - RemoveKey(KeyValue); - } + void remove(MapEntryTy *KeyValue) { RemoveKey(KeyValue); } void erase(iterator I) { MapEntryTy &V = *I; @@ -412,7 +348,8 @@ public: bool erase(std::string_view Key) { iterator I = find(Key); - if (I == end()) return false; + if (I == end()) + return false; erase(I); return true; } @@ -431,7 +368,8 @@ public: explicit StringMapIterBase(StringMapEntryBase **Bucket, bool NoAdvance = false) : Ptr(Bucket) { - if (!NoAdvance) AdvancePastEmptyBuckets(); + if (!NoAdvance) + AdvancePastEmptyBuckets(); } DerivedTy &operator=(const DerivedTy &Other) { @@ -439,13 +377,9 @@ public: return static_cast(*this); } -#if __cplusplus < 202002L - bool operator==(const DerivedTy &RHS) const { return Ptr == RHS.Ptr; } -#else friend bool operator==(const DerivedTy &LHS, const DerivedTy &RHS) { return LHS.Ptr == RHS.Ptr; } -#endif DerivedTy &operator++() { // Preincrement ++Ptr; @@ -490,15 +424,13 @@ class StringMapConstIterator const StringMapEntry>; public: - using value_type = const StringMapEntry; - StringMapConstIterator() = default; explicit StringMapConstIterator(StringMapEntryBase **Bucket, bool NoAdvance = false) : base(Bucket, NoAdvance) {} - value_type &operator*() const { - return *static_cast(*this->Ptr); + const StringMapEntry &operator*() const { + return *static_cast *>(*this->Ptr); } }; @@ -509,15 +441,13 @@ class StringMapIterator : public StringMapIterBase, StringMapIterBase, StringMapEntry>; public: - using value_type = StringMapEntry; - StringMapIterator() = default; explicit StringMapIterator(StringMapEntryBase **Bucket, bool NoAdvance = false) : base(Bucket, NoAdvance) {} - value_type &operator*() const { - return *static_cast(*this->Ptr); + StringMapEntry &operator*() const { + return *static_cast *>(*this->Ptr); } operator StringMapConstIterator() const { @@ -635,4 +565,4 @@ inline bool operator>=(const StringMap& lhs, } // end namespace wpi -#endif // LLVM_ADT_STRINGMAP_H +#endif // WPIUTIL_WPI_STRINGMAP_H diff --git a/wpiutil/src/main/native/include/wpi/StringMapEntry.h b/wpiutil/src/main/native/include/wpi/StringMapEntry.h new file mode 100644 index 0000000000..98a8fcc7a4 --- /dev/null +++ b/wpiutil/src/main/native/include/wpi/StringMapEntry.h @@ -0,0 +1,143 @@ +//===- StringMapEntry.h - String Hash table map interface -------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines the StringMapEntry class - it is intended to be a low +// dependency implementation detail of StringMap that is more suitable for +// inclusion in public headers than StringMap.h itself is. +// +//===----------------------------------------------------------------------===// + +#ifndef WPIUTIL_WPI_STRINGMAPENTRY_H +#define WPIUTIL_WPI_STRINGMAPENTRY_H + +#include "wpi/MemAlloc.h" + +#include +#include +#include +#include + +namespace wpi { + +/// StringMapEntryBase - Shared base class of StringMapEntry instances. +class StringMapEntryBase { + size_t keyLength; + +public: + explicit StringMapEntryBase(size_t keyLength) : keyLength(keyLength) {} + + size_t getKeyLength() const { return keyLength; } + +protected: + /// Helper to tail-allocate \p Key. It'd be nice to generalize this so it + /// could be reused elsewhere, maybe even taking an wpi::function_ref to + /// type-erase the allocator and put it in a source file. + static void *allocateWithKey(size_t EntrySize, size_t EntryAlign, + std::string_view Key) { + size_t KeyLength = Key.size(); + + // Allocate a new item with space for the string at the end and a null + // terminator. + size_t AllocSize = EntrySize + KeyLength + 1; + void *Allocation = safe_malloc(AllocSize); + assert(Allocation && "Unhandled out-of-memory"); + + // Copy the string information. + char *Buffer = reinterpret_cast(Allocation) + EntrySize; + if (KeyLength > 0) + ::memcpy(Buffer, Key.data(), KeyLength); + Buffer[KeyLength] = 0; // Null terminate for convenience of clients. + return Allocation; +} +}; + +/// StringMapEntryStorage - Holds the value in a StringMapEntry. +/// +/// Factored out into a separate base class to make it easier to specialize. +/// This is primarily intended to support StringSet, which doesn't need a value +/// stored at all. +template +class StringMapEntryStorage : public StringMapEntryBase { +public: + ValueTy second; + + explicit StringMapEntryStorage(size_t keyLength) + : StringMapEntryBase(keyLength), second() {} + template + StringMapEntryStorage(size_t keyLength, InitTy &&... initVals) + : StringMapEntryBase(keyLength), + second(std::forward(initVals)...) {} + StringMapEntryStorage(StringMapEntryStorage &e) = delete; + + const ValueTy &getValue() const { return second; } + ValueTy &getValue() { return second; } + + void setValue(const ValueTy &V) { second = V; } +}; + +template <> class StringMapEntryStorage : public StringMapEntryBase { +public: + explicit StringMapEntryStorage(size_t keyLength, std::nullopt_t = std::nullopt) + : StringMapEntryBase(keyLength) {} + StringMapEntryStorage(StringMapEntryStorage &entry) = delete; + + std::nullopt_t getValue() const { return std::nullopt; } +}; + +/// StringMapEntry - This is used to represent one value that is inserted into +/// a StringMap. It contains the Value itself and the key: the string length +/// and data. +template +class StringMapEntry final : public StringMapEntryStorage { +public: + using StringMapEntryStorage::StringMapEntryStorage; + + std::string_view getKey() const { + return std::string_view(getKeyData(), this->getKeyLength()); + } + + /// getKeyData - Return the start of the string data that is the key for this + /// value. The string data is always stored immediately after the + /// StringMapEntry object. + const char *getKeyData() const { + return reinterpret_cast(this + 1); + } + + std::string_view first() const { + return std::string_view(getKeyData(), this->getKeyLength()); + } + + /// Create a StringMapEntry for the specified key construct the value using + /// \p InitiVals. + template + static StringMapEntry *Create(std::string_view key, + InitTy &&... initVals) { + return new (StringMapEntryBase::allocateWithKey( + sizeof(StringMapEntry), alignof(StringMapEntry), key)) + StringMapEntry(key.size(), std::forward(initVals)...); + } + + /// GetStringMapEntryFromKeyData - Given key data that is known to be embedded + /// into a StringMapEntry, return the StringMapEntry itself. + static StringMapEntry &GetStringMapEntryFromKeyData(const char *keyData) { + char *ptr = const_cast(keyData) - sizeof(StringMapEntry); + return *reinterpret_cast(ptr); + } + + /// Destroy - Destroy this StringMapEntry, releasing memory back to the + /// specified allocator. + void Destroy() { + // Free memory referenced by the item. + this->~StringMapEntry(); + std::free(static_cast(this)); + } +}; + +} // end namespace wpi + +#endif // WPIUTIL_WPI_STRINGMAPENTRY_H diff --git a/wpiutil/src/main/native/include/wpi/SwapByteOrder.h b/wpiutil/src/main/native/include/wpi/SwapByteOrder.h index 50ad8324e6..ab2791a25d 100644 --- a/wpiutil/src/main/native/include/wpi/SwapByteOrder.h +++ b/wpiutil/src/main/native/include/wpi/SwapByteOrder.h @@ -1,9 +1,8 @@ //===- SwapByteOrder.h - Generic and optimized byte swaps -------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -15,19 +14,43 @@ #ifndef WPIUTIL_WPI_SWAPBYTEORDER_H #define WPIUTIL_WPI_SWAPBYTEORDER_H -#include "wpi/Compiler.h" #include -#include +#include +#include #if defined(_MSC_VER) && !defined(_DEBUG) #include #endif -namespace wpi { -namespace sys { +#if defined(__linux__) || defined(__GNU__) || defined(__HAIKU__) || \ + defined(__Fuchsia__) || defined(__EMSCRIPTEN__) +#include +#elif defined(_AIX) +#include +#elif defined(__sun) +/* Solaris provides _BIG_ENDIAN/_LITTLE_ENDIAN selector in sys/types.h */ +#include +#define BIG_ENDIAN 4321 +#define LITTLE_ENDIAN 1234 +#if defined(_BIG_ENDIAN) +#define BYTE_ORDER BIG_ENDIAN +#else +#define BYTE_ORDER LITTLE_ENDIAN +#endif +#elif defined(__MVS__) +#define BIG_ENDIAN 4321 +#define LITTLE_ENDIAN 1234 +#define BYTE_ORDER BIG_ENDIAN +#else +#if !defined(BYTE_ORDER) && !defined(_WIN32) +#include +#endif +#endif -/// SwapByteOrder_16 - This function returns a byte-swapped representation of +namespace wpi { + +/// ByteSwap_16 - This function returns a byte-swapped representation of /// the 16-bit argument. -inline uint16_t SwapByteOrder_16(uint16_t value) { +inline uint16_t ByteSwap_16(uint16_t value) { #if defined(_MSC_VER) && !defined(_DEBUG) // The DLL version of the runtime lacks these functions (bug!?), but in a // release build they're replaced with BSWAP instructions anyway. @@ -39,10 +62,9 @@ inline uint16_t SwapByteOrder_16(uint16_t value) { #endif } -/// SwapByteOrder_32 - This function returns a byte-swapped representation of -/// the 32-bit argument. -inline uint32_t SwapByteOrder_32(uint32_t value) { -#if defined(__llvm__) || (LLVM_GNUC_PREREQ(4, 3, 0) && !defined(__ICC)) +/// This function returns a byte-swapped representation of the 32-bit argument. +inline uint32_t ByteSwap_32(uint32_t value) { +#if defined(__llvm__) || (defined(__GNUC__) && !defined(__ICC)) return __builtin_bswap32(value); #elif defined(_MSC_VER) && !defined(_DEBUG) return _byteswap_ulong(value); @@ -55,45 +77,55 @@ inline uint32_t SwapByteOrder_32(uint32_t value) { #endif } -/// SwapByteOrder_64 - This function returns a byte-swapped representation of -/// the 64-bit argument. -inline uint64_t SwapByteOrder_64(uint64_t value) { -#if defined(__llvm__) || (LLVM_GNUC_PREREQ(4, 3, 0) && !defined(__ICC)) +/// This function returns a byte-swapped representation of the 64-bit argument. +inline uint64_t ByteSwap_64(uint64_t value) { +#if defined(__llvm__) || (defined(__GNUC__) && !defined(__ICC)) return __builtin_bswap64(value); #elif defined(_MSC_VER) && !defined(_DEBUG) return _byteswap_uint64(value); #else - uint64_t Hi = SwapByteOrder_32(uint32_t(value)); - uint32_t Lo = SwapByteOrder_32(uint32_t(value >> 32)); + uint64_t Hi = ByteSwap_32(uint32_t(value)); + uint32_t Lo = ByteSwap_32(uint32_t(value >> 32)); return (Hi << 32) | Lo; #endif } +namespace sys { + +#if defined(BYTE_ORDER) && defined(BIG_ENDIAN) && BYTE_ORDER == BIG_ENDIAN +constexpr bool IsBigEndianHost = true; +#else +constexpr bool IsBigEndianHost = false; +#endif + +static const bool IsLittleEndianHost = !IsBigEndianHost; + inline unsigned char getSwappedBytes(unsigned char C) { return C; } inline signed char getSwappedBytes(signed char C) { return C; } inline char getSwappedBytes(char C) { return C; } -inline unsigned short getSwappedBytes(unsigned short C) { return SwapByteOrder_16(C); } -inline signed short getSwappedBytes( signed short C) { return SwapByteOrder_16(C); } +inline unsigned short getSwappedBytes(unsigned short C) { return ByteSwap_16(C); } +inline signed short getSwappedBytes( signed short C) { return ByteSwap_16(C); } -inline unsigned int getSwappedBytes(unsigned int C) { return SwapByteOrder_32(C); } -inline signed int getSwappedBytes( signed int C) { return SwapByteOrder_32(C); } +inline unsigned int getSwappedBytes(unsigned int C) { return ByteSwap_32(C); } +inline signed int getSwappedBytes( signed int C) { return ByteSwap_32(C); } -#if __LONG_MAX__ == __INT_MAX__ -inline unsigned long getSwappedBytes(unsigned long C) { return SwapByteOrder_32(C); } -inline signed long getSwappedBytes( signed long C) { return SwapByteOrder_32(C); } -#elif __LONG_MAX__ == __LONG_LONG_MAX__ -inline unsigned long getSwappedBytes(unsigned long C) { return SwapByteOrder_64(C); } -inline signed long getSwappedBytes( signed long C) { return SwapByteOrder_64(C); } -#else -#error "Unknown long size!" -#endif +inline unsigned long getSwappedBytes(unsigned long C) { + // Handle LLP64 and LP64 platforms. + return sizeof(long) == sizeof(int) ? ByteSwap_32((uint32_t)C) + : ByteSwap_64((uint64_t)C); +} +inline signed long getSwappedBytes(signed long C) { + // Handle LLP64 and LP64 platforms. + return sizeof(long) == sizeof(int) ? ByteSwap_32((uint32_t)C) + : ByteSwap_64((uint64_t)C); +} inline unsigned long long getSwappedBytes(unsigned long long C) { - return SwapByteOrder_64(C); + return ByteSwap_64(C); } inline signed long long getSwappedBytes(signed long long C) { - return SwapByteOrder_64(C); + return ByteSwap_64(C); } inline float getSwappedBytes(float C) { @@ -102,7 +134,7 @@ inline float getSwappedBytes(float C) { float f; } in, out; in.f = C; - out.i = SwapByteOrder_32(in.i); + out.i = ByteSwap_32(in.i); return out.f; } @@ -112,10 +144,16 @@ inline double getSwappedBytes(double C) { double d; } in, out; in.d = C; - out.i = SwapByteOrder_64(in.i); + out.i = ByteSwap_64(in.i); return out.d; } +template +inline std::enable_if_t::value, T> getSwappedBytes(T C) { + return static_cast( + getSwappedBytes(static_cast>(C))); +} + template inline void swapByteOrder(T &Value) { Value = getSwappedBytes(Value); diff --git a/wpiutil/src/main/native/include/wpi/VersionTuple.h b/wpiutil/src/main/native/include/wpi/VersionTuple.h index 59df41ccab..c08ff372d0 100644 --- a/wpiutil/src/main/native/include/wpi/VersionTuple.h +++ b/wpiutil/src/main/native/include/wpi/VersionTuple.h @@ -1,9 +1,8 @@ //===- VersionTuple.h - Version Number Handling -----------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// /// @@ -15,10 +14,14 @@ #ifndef WPIUTIL_WPI_VERSIONTUPLE_H #define WPIUTIL_WPI_VERSIONTUPLE_H +#include "wpi/DenseMapInfo.h" +#include "wpi/Hashing.h" #include #include +#include namespace wpi { +class raw_ostream; /// Represents a version number in the form major[.minor[.subminor[.build]]]. class VersionTuple { @@ -85,6 +88,27 @@ public: return Build; } + /// Return a version tuple that contains only the first 3 version components. + VersionTuple withoutBuild() const { + if (HasBuild) + return VersionTuple(Major, Minor, Subminor); + return *this; + } + + /// Return a version tuple that contains only components that are non-zero. + VersionTuple normalize() const { + VersionTuple Result = *this; + if (Result.Build == 0) { + Result.HasBuild = false; + if (Result.Subminor == 0) { + Result.HasSubminor = false; + if (Result.Minor == 0) + Result.HasMinor = false; + } + } + return Result; + } + /// Determine if two version numbers are equivalent. If not /// provided, minor and subminor version numbers are considered to be zero. friend bool operator==(const VersionTuple &X, const VersionTuple &Y) { diff --git a/wpiutil/src/main/native/include/wpi/WindowsError.h b/wpiutil/src/main/native/include/wpi/WindowsError.h index 565e2b76c8..54a5236932 100644 --- a/wpiutil/src/main/native/include/wpi/WindowsError.h +++ b/wpiutil/src/main/native/include/wpi/WindowsError.h @@ -1,9 +1,8 @@ //===-- WindowsError.h - Support for mapping windows errors to posix-------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// diff --git a/wpiutil/src/main/native/include/wpi/iterator.h b/wpiutil/src/main/native/include/wpi/iterator.h index 70bbdab9a0..1b954b395c 100644 --- a/wpiutil/src/main/native/include/wpi/iterator.h +++ b/wpiutil/src/main/native/include/wpi/iterator.h @@ -1,9 +1,8 @@ //===- iterator.h - Utilities for using and defining iterators --*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -11,7 +10,6 @@ #define WPIUTIL_WPI_ITERATOR_H #include "wpi/iterator_range.h" -#include #include #include #include @@ -65,9 +63,14 @@ namespace wpi { template -class iterator_facade_base - : public std::iterator { +class iterator_facade_base { +public: + using iterator_category = IteratorCategoryT; + using value_type = T; + using difference_type = DifferenceTypeT; + using pointer = PointerT; + using reference = ReferenceT; + protected: enum { IsRandomAccess = std::is_base_of(this)->operator==(RHS); + return !(static_cast(*this) == RHS); } +#endif bool operator>(const DerivedT &RHS) const { static_assert( IsRandomAccess, "Relational operators are only defined for random access iterators."); - return !static_cast(this)->operator<(RHS) && - !static_cast(this)->operator==(RHS); + return !(static_cast(*this) < RHS) && + !(static_cast(*this) == RHS); } bool operator<=(const DerivedT &RHS) const { static_assert( IsRandomAccess, "Relational operators are only defined for random access iterators."); - return !static_cast(this)->operator>(RHS); + return !(static_cast(*this) > RHS); } bool operator>=(const DerivedT &RHS) const { static_assert( IsRandomAccess, "Relational operators are only defined for random access iterators."); - return !static_cast(this)->operator<(RHS); + return !(static_cast(*this) < RHS); } PointerT operator->() { return &static_cast(this)->operator*(); } @@ -195,14 +200,14 @@ template < typename T = typename std::iterator_traits::value_type, typename DifferenceTypeT = typename std::iterator_traits::difference_type, - typename PointerT = typename std::conditional< + typename PointerT = std::conditional_t< std::is_same::value_type>::value, - typename std::iterator_traits::pointer, T *>::type, - typename ReferenceT = typename std::conditional< + typename std::iterator_traits::pointer, T *>, + typename ReferenceT = std::conditional_t< std::is_same::value_type>::value, - typename std::iterator_traits::reference, T &>::type> + typename std::iterator_traits::reference, T &>> class iterator_adaptor_base : public iterator_facade_base { @@ -261,12 +266,16 @@ public: return *static_cast(this); } - bool operator==(const DerivedT &RHS) const { return I == RHS.I; } - bool operator<(const DerivedT &RHS) const { + friend bool operator==(const iterator_adaptor_base &LHS, + const iterator_adaptor_base &RHS) { + return LHS.I == RHS.I; + } + friend bool operator<(const iterator_adaptor_base &LHS, + const iterator_adaptor_base &RHS) { static_assert( BaseT::IsRandomAccess, "Relational operators are only defined for random access iterators."); - return I < RHS.I; + return LHS.I < RHS.I; } ReferenceT operator*() const { return *I; } @@ -282,8 +291,8 @@ public: /// using iterator = pointee_iterator::iterator>; /// \endcode template ())>::type> + typename T = std::remove_reference_t())>> struct pointee_iterator : iterator_adaptor_base< pointee_iterator, WrappedIteratorT, @@ -334,6 +343,13 @@ make_pointer_range(RangeT &&Range) { PointerIteratorT(std::end(std::forward(Range)))); } +template ())>, + typename T2 = std::add_pointer_t> +using raw_pointer_iterator = + pointer_iterator, T2>; + } // end namespace wpi -#endif // LLVM_ADT_ITERATOR_H +#endif // WPIUTIL_WPI_ITERATOR_H diff --git a/wpiutil/src/main/native/include/wpi/iterator_range.h b/wpiutil/src/main/native/include/wpi/iterator_range.h index 78a60c2f7c..ce0e4ee7ef 100644 --- a/wpiutil/src/main/native/include/wpi/iterator_range.h +++ b/wpiutil/src/main/native/include/wpi/iterator_range.h @@ -1,9 +1,8 @@ //===- iterator_range.h - A range adaptor for iterators ---------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// /// \file @@ -19,7 +18,6 @@ #ifndef WPIUTIL_WPI_ITERATOR_RANGE_H #define WPIUTIL_WPI_ITERATOR_RANGE_H -#include #include namespace wpi { @@ -45,6 +43,7 @@ public: IteratorT begin() const { return begin_iterator; } IteratorT end() const { return end_iterator; } + bool empty() const { return begin_iterator == end_iterator; } }; /// Convenience function for iterating over sub-ranges. @@ -59,11 +58,6 @@ template iterator_range make_range(std::pair p) { return iterator_range(std::move(p.first), std::move(p.second)); } -template -iterator_range()))> drop_begin(T &&t, - int n) { - return make_range(std::next(adl_begin(t), n), adl_end(t)); -} } #endif diff --git a/wpiutil/src/main/native/include/wpi/raw_os_ostream.h b/wpiutil/src/main/native/include/wpi/raw_os_ostream.h index 4335e02032..87cd5961a1 100644 --- a/wpiutil/src/main/native/include/wpi/raw_os_ostream.h +++ b/wpiutil/src/main/native/include/wpi/raw_os_ostream.h @@ -1,9 +1,8 @@ //===- raw_os_ostream.h - std::ostream adaptor for raw_ostream --*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // diff --git a/wpiutil/src/main/native/include/wpi/raw_ostream.h b/wpiutil/src/main/native/include/wpi/raw_ostream.h index 84ec925435..52c2ce8568 100644 --- a/wpiutil/src/main/native/include/wpi/raw_ostream.h +++ b/wpiutil/src/main/native/include/wpi/raw_ostream.h @@ -1,9 +1,8 @@ //===--- raw_ostream.h - Raw output stream ----------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -17,18 +16,24 @@ #include "wpi/SmallVector.h" #include "wpi/span.h" #include +#include #include #include #include #include +#if __cplusplus > 201402L #include -#include +#endif #include +#include +#include + namespace fs { enum FileAccess : unsigned; enum OpenFlags : unsigned; enum CreationDisposition : unsigned; +class FileLocker; } // end namespace fs namespace wpi { @@ -38,7 +43,16 @@ namespace wpi { /// buffered disciplines etc. It is a simple buffer that outputs /// a chunk at a time. class raw_ostream { +public: + // Class kinds to support LLVM-style RTTI. + enum class OStreamKind { + OK_OStream, + OK_FDStream, + }; + private: + OStreamKind Kind; + /// The buffer is handled in such a way that the buffer is /// uninitialized, unbuffered, or out of space when OutBufCur >= /// OutBufEnd. Thus a single comparison suffices to determine if we @@ -59,7 +73,11 @@ private: /// this buffer. char *OutBufStart, *OutBufEnd, *OutBufCur; - enum BufferKind { + /// Optional stream this stream is tied to. If this stream is written to, the + /// tied-to stream will be flushed first. + raw_ostream *TiedStream = nullptr; + + enum class BufferKind { Unbuffered = 0, InternalBuffer, ExternalBuffer @@ -67,7 +85,7 @@ private: public: // color order matches ANSI escape sequence, don't change - enum Colors { + enum class Colors { BLACK = 0, RED, GREEN, @@ -76,11 +94,25 @@ public: MAGENTA, CYAN, WHITE, - SAVEDCOLOR + SAVEDCOLOR, + RESET, }; - explicit raw_ostream(bool unbuffered = false) - : BufferMode(unbuffered ? Unbuffered : InternalBuffer) { + static constexpr Colors BLACK = Colors::BLACK; + static constexpr Colors RED = Colors::RED; + static constexpr Colors GREEN = Colors::GREEN; + static constexpr Colors YELLOW = Colors::YELLOW; + static constexpr Colors BLUE = Colors::BLUE; + static constexpr Colors MAGENTA = Colors::MAGENTA; + static constexpr Colors CYAN = Colors::CYAN; + static constexpr Colors WHITE = Colors::WHITE; + static constexpr Colors SAVEDCOLOR = Colors::SAVEDCOLOR; + static constexpr Colors RESET = Colors::RESET; + + explicit raw_ostream(bool unbuffered = false, + OStreamKind K = OStreamKind::OK_OStream) + : Kind(K), BufferMode(unbuffered ? BufferKind::Unbuffered + : BufferKind::InternalBuffer) { // Start out ready to flush. OutBufStart = OutBufEnd = OutBufCur = nullptr; } @@ -93,10 +125,19 @@ public: /// tell - Return the current offset with the file. uint64_t tell() const { return current_pos() + GetNumBytesInBuffer(); } + OStreamKind get_kind() const { return Kind; } + //===--------------------------------------------------------------------===// // Configuration Interface //===--------------------------------------------------------------------===// + /// If possible, pre-allocate \p ExtraSize bytes for stream data. + /// i.e. it extends internal buffers to keep additional ExtraSize bytes. + /// So that the stream could keep at least tell() + ExtraSize bytes + /// without re-allocations. reserveExtraSpace() does not change + /// the size/data of the stream. + virtual void reserveExtraSpace(uint64_t ExtraSize) {} + /// Set the stream to be buffered, with an automatically determined buffer /// size. void SetBuffered(); @@ -104,13 +145,13 @@ public: /// Set the stream to be buffered, using the specified buffer size. void SetBufferSize(size_t Size) { flush(); - SetBufferAndMode(new char[Size], Size, InternalBuffer); + SetBufferAndMode(new char[Size], Size, BufferKind::InternalBuffer); } size_t GetBufferSize() const { // If we're supposed to be buffered but haven't actually gotten around // to allocating the buffer yet, return the value that would be used. - if (BufferMode != Unbuffered && OutBufStart == nullptr) + if (BufferMode != BufferKind::Unbuffered && OutBufStart == nullptr) return preferred_buffer_size(); // Otherwise just return the size of the allocated buffer. @@ -122,7 +163,7 @@ public: /// when the stream is being set to unbuffered. void SetUnbuffered() { flush(); - SetBufferAndMode(nullptr, 0, Unbuffered); + SetBufferAndMode(nullptr, 0, BufferKind::Unbuffered); } size_t GetNumBytesInBuffer() const { @@ -237,9 +278,8 @@ public: /// @param Bold bold/brighter text, default false /// @param BG if true change the background, default: change foreground /// @returns itself so it can be used within << invocations - virtual raw_ostream &changeColor(enum Colors Color, - bool Bold = false, - bool BG = false) { + virtual raw_ostream &changeColor(enum Colors Color, bool Bold = false, + bool BG = false) { (void)Color; (void)Bold; (void)BG; @@ -259,8 +299,17 @@ public: virtual bool is_displayed() const { return false; } /// This function determines if this stream is displayed and supports colors. + /// The result is unaffected by calls to enable_color(). virtual bool has_colors() const { return is_displayed(); } + // Enable or disable colors. Once enable_colors(false) is called, + // changeColor() has no effect until enable_colors(true) is called. + virtual void enable_colors(bool /*enable*/) {} + + /// Tie this stream to the specified stream. Replaces any existing tied-to + /// stream. Specifying a nullptr unties the stream. + void tie(raw_ostream *TieTo) { TiedStream = TieTo; } + //===--------------------------------------------------------------------===// // Subclass Interface //===--------------------------------------------------------------------===// @@ -281,9 +330,6 @@ private: /// \invariant { Size > 0 } virtual void write_impl(const char *Ptr, size_t Size) = 0; - // An out of line virtual method to provide a home for the class vtable. - virtual void handle(); - /// Return the current position within the stream, not counting the bytes /// currently in the buffer. virtual uint64_t current_pos() const = 0; @@ -293,7 +339,7 @@ protected: /// use only by subclasses which can arrange for the output to go directly /// into the desired output buffer, instead of being copied on each flush. void SetBuffer(char *BufferStart, size_t Size) { - SetBufferAndMode(BufferStart, Size, ExternalBuffer); + SetBufferAndMode(BufferStart, Size, BufferKind::ExternalBuffer); } /// Return an efficient buffer size for the underlying output mechanism. @@ -318,9 +364,23 @@ private: /// unused bytes in the buffer. void copy_to_buffer(const char *Ptr, size_t Size); + /// Flush the tied-to stream (if present) and then write the required data. + void flush_tied_then_write(const char *Ptr, size_t Size); + virtual void anchor(); }; +/// Call the appropriate insertion operator, given an rvalue reference to a +/// raw_ostream object and return a stream of the same type as the argument. +template +std::enable_if_t::value && + std::is_base_of::value, + OStream &&> +operator<<(OStream &&OS, const T &Value) { + OS << Value; + return std::move(OS); +} + /// An abstract base class for streams implementations that also support a /// pwrite operation. This is useful for code that can mostly stream out data, /// but needs to patch in a header that needs to know the output size. @@ -329,10 +389,11 @@ class raw_pwrite_stream : public raw_ostream { void anchor() override; public: - explicit raw_pwrite_stream(bool Unbuffered = false) - : raw_ostream(Unbuffered) {} + explicit raw_pwrite_stream(bool Unbuffered = false, + OStreamKind K = OStreamKind::OK_OStream) + : raw_ostream(Unbuffered, K) {} void pwrite(const char *Ptr, size_t Size, uint64_t Offset) { -#ifndef NDBEBUG +#ifndef NDEBUG uint64_t Pos = tell(); // /dev/null always reports a pos of 0, so we cannot perform this check // in that case. @@ -352,8 +413,7 @@ public: class raw_fd_ostream : public raw_pwrite_stream { int FD; bool ShouldClose; - - bool SupportsSeeking; + bool SupportsSeeking = false; #ifdef _WIN32 /// True if this fd refers to a Windows console device. Mintty and other @@ -363,7 +423,7 @@ class raw_fd_ostream : public raw_pwrite_stream { std::error_code EC; - uint64_t pos; + uint64_t pos = 0; /// See raw_ostream::write_impl. void write_impl(const char *Ptr, size_t Size) override; @@ -377,10 +437,17 @@ class raw_fd_ostream : public raw_pwrite_stream { /// Determine an efficient buffer size. size_t preferred_buffer_size() const override; + void anchor() override; + +protected: /// Set the flag indicating that an output error has been encountered. void error_detected(std::error_code EC) { this->EC = EC; } - void anchor() override; + /// Return the file descriptor. + int get_fd() const { return FD; } + + // Update the file position by increasing \p Delta. + void inc_pos(uint64_t Delta) { pos += Delta; } public: /// Open the specified file for writing. If an error occurs, information @@ -405,7 +472,8 @@ public: /// FD is the file descriptor that this writes to. If ShouldClose is true, /// this closes the file when the stream is destroyed. If FD is for stdout or /// stderr, it will not be closed. - raw_fd_ostream(int fd, bool shouldClose, bool unbuffered=false); + raw_fd_ostream(int fd, bool shouldClose, bool unbuffered = false, + OStreamKind K = OStreamKind::OK_OStream); ~raw_fd_ostream() override; @@ -413,7 +481,7 @@ public: /// fsync. void close(); - bool supportsSeeking() { return SupportsSeeking; } + bool supportsSeeking() const { return SupportsSeeking; } /// Flushes the stream and repositions the underlying file descriptor position /// to the offset specified from the beginning of the file. @@ -439,17 +507,37 @@ public: void clear_error() { EC = std::error_code(); } }; -/// This returns a reference to a raw_ostream for standard output. Use it like: -/// outs() << "foo" << "bar"; -raw_ostream &outs(); +/// This returns a reference to a raw_fd_ostream for standard output. Use it +/// like: outs() << "foo" << "bar"; +raw_fd_ostream &outs(); -/// This returns a reference to a raw_ostream for standard error. Use it like: -/// errs() << "foo" << "bar"; -raw_ostream &errs(); +/// This returns a reference to a raw_ostream for standard error. +/// Use it like: errs() << "foo" << "bar"; +/// By default, the stream is tied to stdout to ensure stdout is flushed before +/// stderr is written, to ensure the error messages are written in their +/// expected place. +raw_fd_ostream &errs(); /// This returns a reference to a raw_ostream which simply discards output. raw_ostream &nulls(); +//===----------------------------------------------------------------------===// +// File Streams +//===----------------------------------------------------------------------===// + +/// A raw_ostream of a file for reading/writing/seeking. +/// +class raw_fd_stream : public raw_fd_ostream { +public: + /// Open the specified file for reading/writing/seeking. If an error occurs, + /// information about the error is put into EC, and the stream should be + /// immediately destroyed. + raw_fd_stream(std::string_view Filename, std::error_code &EC); + + /// Check if \p OS is a pointer of type raw_fd_stream*. + static bool classof(const raw_ostream *OS); +}; + //===----------------------------------------------------------------------===// // Output Stream Adaptors //===----------------------------------------------------------------------===// @@ -467,7 +555,9 @@ class raw_string_ostream : public raw_ostream { uint64_t current_pos() const override { return OS.size(); } public: - explicit raw_string_ostream(std::string &O) : OS(O) {} + explicit raw_string_ostream(std::string &O) : OS(O) { + SetUnbuffered(); + } ~raw_string_ostream() override; /// Flushes the stream contents to the target string and returns the string's @@ -476,6 +566,10 @@ public: flush(); return OS; } + + void reserveExtraSpace(uint64_t ExtraSize) override { + OS.reserve(tell() + ExtraSize); + } }; /// A raw_ostream that writes to an SmallVector or SmallString. This is a @@ -508,7 +602,11 @@ public: void flush() = delete; /// Return a std::string_view for the vector contents. - std::string_view str() { return std::string_view(OS.data(), OS.size()); } + std::string_view str() const { return std::string_view(OS.data(), OS.size()); } + + void reserveExtraSpace(uint64_t ExtraSize) override { + OS.reserve(tell() + ExtraSize); + } }; /// A raw_ostream that writes to a vector. This is a @@ -639,6 +737,18 @@ public: ~buffer_ostream() override { OS << str(); } }; +class buffer_unique_ostream : public raw_svector_ostream { + std::unique_ptr OS; + SmallVector Buffer; + + virtual void anchor() override; + +public: + buffer_unique_ostream(std::unique_ptr OS) + : raw_svector_ostream(Buffer), OS(std::move(OS)) {} + ~buffer_unique_ostream() override { *OS << str(); } +}; + } // end namespace wpi -#endif // LLVM_SUPPORT_RAW_OSTREAM_H +#endif // WPIUTIL_WPI_RAW_OSTREAM_H diff --git a/wpiutil/src/main/native/include/wpi/type_traits.h b/wpiutil/src/main/native/include/wpi/type_traits.h index 5d35ee136e..53e18d342e 100644 --- a/wpiutil/src/main/native/include/wpi/type_traits.h +++ b/wpiutil/src/main/native/include/wpi/type_traits.h @@ -1,9 +1,8 @@ //===- llvm/Support/type_traits.h - Simplfied type traits -------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -18,41 +17,8 @@ #include #include -#ifndef __has_feature -#define WPI_DEFINED_HAS_FEATURE -#define __has_feature(x) 0 -#endif - namespace wpi { -/// isPodLike - This is a type trait that is used to determine whether a given -/// type can be copied around with memcpy instead of running ctors etc. -template -struct isPodLike { - // std::is_trivially_copyable is available in libc++ with clang, libstdc++ - // that comes with GCC 5. -#if (__has_feature(is_trivially_copyable) && defined(_LIBCPP_VERSION)) || \ - (defined(__GNUC__) && __GNUC__ >= 5) - // If the compiler supports the is_trivially_copyable trait use it, as it - // matches the definition of isPodLike closely. - static const bool value = std::is_trivially_copyable::value; -#elif __has_feature(is_trivially_copyable) - // Use the internal name if the compiler supports is_trivially_copyable but we - // don't know if the standard library does. This is the case for clang in - // conjunction with libstdc++ from GCC 4.x. - static const bool value = __is_trivially_copyable(T); -#else - // If we don't know anything else, we can (at least) assume that all non-class - // types are PODs. - static const bool value = !std::is_class::value; -#endif -}; - -// std::pair's are pod-like if their elements are. -template -struct isPodLike> { - static const bool value = isPodLike::value && isPodLike::value; -}; /// Metafunction that determines whether the given type is either an /// integral type or an enumeration type, including enum classes. @@ -62,7 +28,7 @@ struct isPodLike> { /// Also note that enum classes aren't implicitly convertible to integral types, /// the value may therefore need to be explicitly converted before being used. template class is_integral_or_enum { - using UnderlyingT = typename std::remove_reference::type; + using UnderlyingT = std::remove_reference_t; public: static const bool value = @@ -79,7 +45,7 @@ struct add_lvalue_reference_if_not_pointer { using type = T &; }; template struct add_lvalue_reference_if_not_pointer< - T, typename std::enable_if::value>::type> { + T, std::enable_if_t::value>> { using type = T; }; @@ -89,9 +55,8 @@ template struct add_const_past_pointer { using type = const T; }; template -struct add_const_past_pointer< - T, typename std::enable_if::value>::type> { - using type = const typename std::remove_pointer::type *; +struct add_const_past_pointer::value>> { + using type = const std::remove_pointer_t *; }; template @@ -99,27 +64,41 @@ struct const_pointer_or_const_ref { using type = const T &; }; template -struct const_pointer_or_const_ref< - T, typename std::enable_if::value>::type> { +struct const_pointer_or_const_ref::value>> { using type = typename add_const_past_pointer::type; }; -} // namespace wpi +namespace detail { +/// Internal utility to detect trivial copy construction. +template union copy_construction_triviality_helper { + T t; + copy_construction_triviality_helper() = default; + copy_construction_triviality_helper(const copy_construction_triviality_helper&) = default; + ~copy_construction_triviality_helper() = default; +}; +/// Internal utility to detect trivial move construction. +template union move_construction_triviality_helper { + T t; + move_construction_triviality_helper() = default; + move_construction_triviality_helper(move_construction_triviality_helper&&) = default; + ~move_construction_triviality_helper() = default; +}; -// If the compiler supports detecting whether a class is final, define -// an LLVM_IS_FINAL macro. If it cannot be defined properly, this -// macro will be left undefined. -#ifndef LLVM_IS_FINAL -#if __cplusplus >= 201402L || defined(_MSC_VER) -#define LLVM_IS_FINAL(Ty) std::is_final() -#elif __has_feature(is_final) || LLVM_GNUC_PREREQ(4, 7, 0) -#define LLVM_IS_FINAL(Ty) __is_final(Ty) -#endif -#endif +template +union trivial_helper { + T t; +}; -#ifdef WPI_DEFINED_HAS_FEATURE -#undef __has_feature -#undef WPI_DEFINED_HAS_FEATURE -#endif +} // end namespace detail -#endif // LLVM_SUPPORT_TYPE_TRAITS_H +template +using is_trivially_move_constructible = std::is_trivially_move_constructible; + +template +using is_trivially_copy_constructible = std::is_trivially_copy_constructible; + + +} // end namespace wpi + +#endif // WPIUTIL_WPI_TYPE_TRAITS_H diff --git a/wpiutil/src/test/native/cpp/json/unit-cbor.cpp b/wpiutil/src/test/native/cpp/json/unit-cbor.cpp index 2e37a17d04..dfb3b60539 100644 --- a/wpiutil/src/test/native/cpp/json/unit-cbor.cpp +++ b/wpiutil/src/test/native/cpp/json/unit-cbor.cpp @@ -35,6 +35,9 @@ SOFTWARE. #include "gtest/gtest.h" #include "unit-json.h" + +#include + using wpi::json; #include diff --git a/wpiutil/src/test/native/cpp/json/unit-constructor1.cpp b/wpiutil/src/test/native/cpp/json/unit-constructor1.cpp index 85b9b91e61..273bfb6203 100644 --- a/wpiutil/src/test/native/cpp/json/unit-constructor1.cpp +++ b/wpiutil/src/test/native/cpp/json/unit-constructor1.cpp @@ -35,6 +35,7 @@ SOFTWARE. #include "gtest/gtest.h" #include +#include #include #include #include diff --git a/wpiutil/src/test/native/cpp/json/unit-conversions.cpp b/wpiutil/src/test/native/cpp/json/unit-conversions.cpp index b60e5ac1a7..601055c59e 100644 --- a/wpiutil/src/test/native/cpp/json/unit-conversions.cpp +++ b/wpiutil/src/test/native/cpp/json/unit-conversions.cpp @@ -38,6 +38,7 @@ SOFTWARE. using wpi::json; using wpi::JsonTest; +#include #include //#include #include diff --git a/wpiutil/src/test/native/cpp/json/unit-element_access2.cpp b/wpiutil/src/test/native/cpp/json/unit-element_access2.cpp index 4b6412326e..ab953bd228 100644 --- a/wpiutil/src/test/native/cpp/json/unit-element_access2.cpp +++ b/wpiutil/src/test/native/cpp/json/unit-element_access2.cpp @@ -35,6 +35,9 @@ SOFTWARE. #include "gtest/gtest.h" #include "unit-json.h" + +#include + using wpi::json; class JsonElementObjectAccessTestBase { diff --git a/wpiutil/src/test/native/cpp/json/unit-msgpack.cpp b/wpiutil/src/test/native/cpp/json/unit-msgpack.cpp index c0a237d398..73665ea0a3 100644 --- a/wpiutil/src/test/native/cpp/json/unit-msgpack.cpp +++ b/wpiutil/src/test/native/cpp/json/unit-msgpack.cpp @@ -37,6 +37,7 @@ SOFTWARE. #include "unit-json.h" using wpi::json; +#include #include TEST(MessagePackDiscardedTest, Case) diff --git a/wpiutil/src/test/native/cpp/json/unit-pointer_access.cpp b/wpiutil/src/test/native/cpp/json/unit-pointer_access.cpp index a1a7fa4feb..3224b053b0 100644 --- a/wpiutil/src/test/native/cpp/json/unit-pointer_access.cpp +++ b/wpiutil/src/test/native/cpp/json/unit-pointer_access.cpp @@ -35,6 +35,9 @@ SOFTWARE. #include "gtest/gtest.h" #include "unit-json.h" + +#include + using wpi::json; TEST(JsonPointerTest, TypesCreate) diff --git a/wpiutil/src/test/native/cpp/llvm/Chrono.cpp b/wpiutil/src/test/native/cpp/llvm/Chrono.cpp new file mode 100644 index 0000000000..5fe3e65af3 --- /dev/null +++ b/wpiutil/src/test/native/cpp/llvm/Chrono.cpp @@ -0,0 +1,49 @@ +//===- llvm/unittest/Support/Chrono.cpp - Time utilities tests ------------===// +// +// 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 "wpi/Chrono.h" +#include "wpi/SmallVector.h" +#include "gtest/gtest.h" + +using namespace wpi; +using namespace wpi::sys; +using namespace std::chrono; + +namespace { + +TEST(Chrono, TimeTConversion) { + EXPECT_EQ(time_t(0), toTimeT(toTimePoint(time_t(0)))); + EXPECT_EQ(time_t(1), toTimeT(toTimePoint(time_t(1)))); + EXPECT_EQ(time_t(47), toTimeT(toTimePoint(time_t(47)))); + + TimePoint<> TP; + EXPECT_EQ(TP, toTimePoint(toTimeT(TP))); + TP += seconds(1); + EXPECT_EQ(TP, toTimePoint(toTimeT(TP))); + TP += hours(47); + EXPECT_EQ(TP, toTimePoint(toTimeT(TP))); +} + +// Test that toTimePoint and toTimeT can be called with a arguments with varying +// precisions. +TEST(Chrono, ImplicitConversions) { + std::time_t TimeT = 47; + TimePoint Sec = toTimePoint(TimeT); + TimePoint Milli = toTimePoint(TimeT); + TimePoint Micro = toTimePoint(TimeT); + TimePoint Nano = toTimePoint(TimeT); + EXPECT_EQ(Sec, Milli); + EXPECT_EQ(Sec, Micro); + EXPECT_EQ(Sec, Nano); + EXPECT_EQ(TimeT, toTimeT(Sec)); + EXPECT_EQ(TimeT, toTimeT(Milli)); + EXPECT_EQ(TimeT, toTimeT(Micro)); + EXPECT_EQ(TimeT, toTimeT(Nano)); +} + +} // anonymous namespace diff --git a/wpiutil/src/test/native/cpp/llvm/ConvertUTFTest.cpp b/wpiutil/src/test/native/cpp/llvm/ConvertUTFTest.cpp new file mode 100644 index 0000000000..e3cf2663a4 --- /dev/null +++ b/wpiutil/src/test/native/cpp/llvm/ConvertUTFTest.cpp @@ -0,0 +1,1712 @@ +//===- llvm/unittest/Support/ConvertUTFTest.cpp - ConvertUTF tests --------===// +// +// 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 "wpi/ConvertUTF.h" +#include "wpi/SmallString.h" +#include "wpi/SmallVector.h" +#include "gtest/gtest.h" +#include +#include + +using namespace wpi; + +TEST(ConvertUTFTest, ConvertUTF16LittleEndianToUTF8String) { + // Src is the look of disapproval. + alignas(UTF16) static const char Src[] = "\xff\xfe\xa0\x0c_\x00\xa0\x0c"; + span Ref(Src, sizeof(Src) - 1); + SmallString<20> Result; + bool Success = convertUTF16ToUTF8String(Ref, Result); + EXPECT_TRUE(Success); + std::string Expected("\xe0\xb2\xa0_\xe0\xb2\xa0"); + EXPECT_EQ(Expected, Result.string()); +} + +TEST(ConvertUTFTest, ConvertUTF16BigEndianToUTF8String) { + // Src is the look of disapproval. + alignas(UTF16) static const char Src[] = "\xfe\xff\x0c\xa0\x00_\x0c\xa0"; + span Ref(Src, sizeof(Src) - 1); + SmallString<20> Result; + bool Success = convertUTF16ToUTF8String(Ref, Result); + EXPECT_TRUE(Success); + std::string Expected("\xe0\xb2\xa0_\xe0\xb2\xa0"); + EXPECT_EQ(Expected, Result.string()); +} + +TEST(ConvertUTFTest, ConvertUTF8ToUTF16String) { + // Src is the look of disapproval. + static const char Src[] = "\xe0\xb2\xa0_\xe0\xb2\xa0"; + std::string_view Ref(Src, sizeof(Src) - 1); + SmallVector Result; + bool Success = convertUTF8ToUTF16String(Ref, Result); + EXPECT_TRUE(Success); + static const UTF16 Expected[] = {0x0CA0, 0x005f, 0x0CA0, 0}; + ASSERT_EQ(3u, Result.size()); + for (int I = 0, E = 3; I != E; ++I) + EXPECT_EQ(Expected[I], Result[I]); +} + +TEST(ConvertUTFTest, OddLengthInput) { + SmallString<20> Result; + bool Success = convertUTF16ToUTF8String(span("xxxxx", 5), Result); + EXPECT_FALSE(Success); +} + +TEST(ConvertUTFTest, Empty) { + SmallString<20> Result; + bool Success = convertUTF16ToUTF8String(span(), Result); + EXPECT_TRUE(Success); + EXPECT_TRUE(Result.string().empty()); +} + +TEST(ConvertUTFTest, HasUTF16BOM) { + bool HasBOM = hasUTF16ByteOrderMark("\xff\xfe"); + EXPECT_TRUE(HasBOM); + HasBOM = hasUTF16ByteOrderMark("\xfe\xff"); + EXPECT_TRUE(HasBOM); + HasBOM = hasUTF16ByteOrderMark("\xfe\xff "); + EXPECT_TRUE(HasBOM); // Don't care about odd lengths. + HasBOM = hasUTF16ByteOrderMark("\xfe\xff\x00asdf"); + EXPECT_TRUE(HasBOM); + + HasBOM = hasUTF16ByteOrderMark(""); + EXPECT_FALSE(HasBOM); + HasBOM = hasUTF16ByteOrderMark("\xfe"); + EXPECT_FALSE(HasBOM); +} + +TEST(ConvertUTFTest, UTF16WrappersForConvertUTF16ToUTF8String) { + // Src is the look of disapproval. + alignas(UTF16) static const char Src[] = "\xff\xfe\xa0\x0c_\x00\xa0\x0c"; + span SrcRef((const UTF16 *)Src, 4); + SmallString<20> Result; + bool Success = convertUTF16ToUTF8String(SrcRef, Result); + EXPECT_TRUE(Success); + std::string Expected("\xe0\xb2\xa0_\xe0\xb2\xa0"); + EXPECT_EQ(Expected, Result.string()); +} + +TEST(ConvertUTFTest, ConvertUTF8toWide) { + // Src is the look of disapproval. + static const char Src[] = "\xe0\xb2\xa0_\xe0\xb2\xa0"; + std::wstring Result; + bool Success = ConvertUTF8toWide((const char*)Src, Result); + EXPECT_TRUE(Success); + std::wstring Expected(L"\x0ca0_\x0ca0"); + EXPECT_EQ(Expected, Result); + Result.clear(); + Success = ConvertUTF8toWide(Src, Result); + EXPECT_TRUE(Success); + EXPECT_EQ(Expected, Result); +} + +TEST(ConvertUTFTest, convertWideToUTF8) { + // Src is the look of disapproval. + static const wchar_t Src[] = L"\x0ca0_\x0ca0"; + SmallString<20> Result; + bool Success = convertWideToUTF8(Src, Result); + EXPECT_TRUE(Success); + std::string Expected("\xe0\xb2\xa0_\xe0\xb2\xa0"); + EXPECT_EQ(Expected, Result.string()); +} + +struct ConvertUTFResultContainer { + ConversionResult ErrorCode; + std::vector UnicodeScalars; + + ConvertUTFResultContainer(ConversionResult ErrorCode) + : ErrorCode(ErrorCode) {} + + ConvertUTFResultContainer + withScalars(unsigned US0 = 0x110000, unsigned US1 = 0x110000, + unsigned US2 = 0x110000, unsigned US3 = 0x110000, + unsigned US4 = 0x110000, unsigned US5 = 0x110000, + unsigned US6 = 0x110000, unsigned US7 = 0x110000) { + ConvertUTFResultContainer Result(*this); + if (US0 != 0x110000) + Result.UnicodeScalars.push_back(US0); + if (US1 != 0x110000) + Result.UnicodeScalars.push_back(US1); + if (US2 != 0x110000) + Result.UnicodeScalars.push_back(US2); + if (US3 != 0x110000) + Result.UnicodeScalars.push_back(US3); + if (US4 != 0x110000) + Result.UnicodeScalars.push_back(US4); + if (US5 != 0x110000) + Result.UnicodeScalars.push_back(US5); + if (US6 != 0x110000) + Result.UnicodeScalars.push_back(US6); + if (US7 != 0x110000) + Result.UnicodeScalars.push_back(US7); + return Result; + } +}; + +std::pair> +ConvertUTF8ToUnicodeScalarsLenient(std::string_view S) { + const UTF8 *SourceStart = reinterpret_cast(S.data()); + + const UTF8 *SourceNext = SourceStart; + std::vector Decoded(S.size(), 0); + UTF32 *TargetStart = Decoded.data(); + + auto ErrorCode = + ConvertUTF8toUTF32(&SourceNext, SourceStart + S.size(), &TargetStart, + Decoded.data() + Decoded.size(), lenientConversion); + + Decoded.resize(TargetStart - Decoded.data()); + + return std::make_pair(ErrorCode, Decoded); +} + +std::pair> +ConvertUTF8ToUnicodeScalarsPartialLenient(std::string_view S) { + const UTF8 *SourceStart = reinterpret_cast(S.data()); + + const UTF8 *SourceNext = SourceStart; + std::vector Decoded(S.size(), 0); + UTF32 *TargetStart = Decoded.data(); + + auto ErrorCode = ConvertUTF8toUTF32Partial( + &SourceNext, SourceStart + S.size(), &TargetStart, + Decoded.data() + Decoded.size(), lenientConversion); + + Decoded.resize(TargetStart - Decoded.data()); + + return std::make_pair(ErrorCode, Decoded); +} + +::testing::AssertionResult +CheckConvertUTF8ToUnicodeScalars(ConvertUTFResultContainer Expected, + std::string_view S, bool Partial = false) { + ConversionResult ErrorCode; + std::vector Decoded; + if (!Partial) + std::tie(ErrorCode, Decoded) = ConvertUTF8ToUnicodeScalarsLenient(S); + else + std::tie(ErrorCode, Decoded) = ConvertUTF8ToUnicodeScalarsPartialLenient(S); + + if (Expected.ErrorCode != ErrorCode) + return ::testing::AssertionFailure() << "Expected error code " + << Expected.ErrorCode << ", actual " + << ErrorCode; + + if (Expected.UnicodeScalars != Decoded) + return ::testing::AssertionFailure() + << "Expected lenient decoded result:\n" + << ::testing::PrintToString(Expected.UnicodeScalars) << "\n" + << "Actual result:\n" << ::testing::PrintToString(Decoded); + + return ::testing::AssertionSuccess(); +} + +TEST(ConvertUTFTest, UTF8ToUTF32Lenient) { + + // + // 1-byte sequences + // + + // U+0041 LATIN CAPITAL LETTER A + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0x0041), "\x41")); + + // + // 2-byte sequences + // + + // U+0283 LATIN SMALL LETTER ESH + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0x0283), + "\xca\x83")); + + // U+03BA GREEK SMALL LETTER KAPPA + // U+1F79 GREEK SMALL LETTER OMICRON WITH OXIA + // U+03C3 GREEK SMALL LETTER SIGMA + // U+03BC GREEK SMALL LETTER MU + // U+03B5 GREEK SMALL LETTER EPSILON + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK) + .withScalars(0x03ba, 0x1f79, 0x03c3, 0x03bc, 0x03b5), + "\xce\xba\xe1\xbd\xb9\xcf\x83\xce\xbc\xce\xb5")); + + // + // 3-byte sequences + // + + // U+4F8B CJK UNIFIED IDEOGRAPH-4F8B + // U+6587 CJK UNIFIED IDEOGRAPH-6587 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0x4f8b, 0x6587), + "\xe4\xbe\x8b\xe6\x96\x87")); + + // U+D55C HANGUL SYLLABLE HAN + // U+AE00 HANGUL SYLLABLE GEUL + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xd55c, 0xae00), + "\xed\x95\x9c\xea\xb8\x80")); + + // U+1112 HANGUL CHOSEONG HIEUH + // U+1161 HANGUL JUNGSEONG A + // U+11AB HANGUL JONGSEONG NIEUN + // U+1100 HANGUL CHOSEONG KIYEOK + // U+1173 HANGUL JUNGSEONG EU + // U+11AF HANGUL JONGSEONG RIEUL + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK) + .withScalars(0x1112, 0x1161, 0x11ab, 0x1100, 0x1173, 0x11af), + "\xe1\x84\x92\xe1\x85\xa1\xe1\x86\xab\xe1\x84\x80\xe1\x85\xb3" + "\xe1\x86\xaf")); + + // + // 4-byte sequences + // + + // U+E0100 VARIATION SELECTOR-17 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0x000E0100), + "\xf3\xa0\x84\x80")); + + // + // First possible sequence of a certain length + // + + // U+0000 NULL + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0x0000), + std::string_view("\x00", 1))); + + // U+0080 PADDING CHARACTER + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0x0080), + "\xc2\x80")); + + // U+0800 SAMARITAN LETTER ALAF + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0x0800), + "\xe0\xa0\x80")); + + // U+10000 LINEAR B SYLLABLE B008 A + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0x10000), + "\xf0\x90\x80\x80")); + + // U+200000 (invalid) + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xf8\x88\x80\x80\x80")); + + // U+4000000 (invalid) + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xfc\x84\x80\x80\x80\x80")); + + // + // Last possible sequence of a certain length + // + + // U+007F DELETE + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0x007f), "\x7f")); + + // U+07FF (unassigned) + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0x07ff), + "\xdf\xbf")); + + // U+FFFF (noncharacter) + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xffff), + "\xef\xbf\xbf")); + + // U+1FFFFF (invalid) + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xf7\xbf\xbf\xbf")); + + // U+3FFFFFF (invalid) + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xfb\xbf\xbf\xbf\xbf")); + + // U+7FFFFFFF (invalid) + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xfd\xbf\xbf\xbf\xbf\xbf")); + + // + // Other boundary conditions + // + + // U+D7FF (unassigned) + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xd7ff), + "\xed\x9f\xbf")); + + // U+E000 (private use) + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xe000), + "\xee\x80\x80")); + + // U+FFFD REPLACEMENT CHARACTER + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfffd), + "\xef\xbf\xbd")); + + // U+10FFFF (noncharacter) + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0x10ffff), + "\xf4\x8f\xbf\xbf")); + + // U+110000 (invalid) + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xf4\x90\x80\x80")); + + // + // Unexpected continuation bytes + // + + // A sequence of unexpected continuation bytes that don't follow a first + // byte, every byte is a maximal subpart. + + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xbf")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd), + "\x80\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd), + "\x80\xbf")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd), + "\xbf\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd), + "\x80\xbf\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\x80\xbf\x80\xbf")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\x80\xbf\x82\xbf\xaa")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xaa\xb0\xbb\xbf\xaa\xa0")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xaa\xb0\xbb\xbf\xaa\xa0\x8f")); + + // All continuation bytes (0x80--0xbf). + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, + 0xfffd, 0xfffd, 0xfffd, 0xfffd) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, + 0xfffd, 0xfffd, 0xfffd, 0xfffd) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, + 0xfffd, 0xfffd, 0xfffd, 0xfffd) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, + 0xfffd, 0xfffd, 0xfffd, 0xfffd) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, + 0xfffd, 0xfffd, 0xfffd, 0xfffd) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, + 0xfffd, 0xfffd, 0xfffd, 0xfffd) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, + 0xfffd, 0xfffd, 0xfffd, 0xfffd) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, + 0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" + "\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" + "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf" + "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf")); + + // + // Lonely start bytes + // + + // Start bytes of 2-byte sequences (0xc0--0xdf). + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, + 0xfffd, 0xfffd, 0xfffd, 0xfffd) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, + 0xfffd, 0xfffd, 0xfffd, 0xfffd) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, + 0xfffd, 0xfffd, 0xfffd, 0xfffd) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, + 0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" + "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf")); + + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0x0020, 0xfffd, 0x0020, + 0xfffd, 0x0020, 0xfffd, 0x0020) + .withScalars(0xfffd, 0x0020, 0xfffd, 0x0020, + 0xfffd, 0x0020, 0xfffd, 0x0020) + .withScalars(0xfffd, 0x0020, 0xfffd, 0x0020, + 0xfffd, 0x0020, 0xfffd, 0x0020) + .withScalars(0xfffd, 0x0020, 0xfffd, 0x0020, + 0xfffd, 0x0020, 0xfffd, 0x0020) + .withScalars(0xfffd, 0x0020, 0xfffd, 0x0020, + 0xfffd, 0x0020, 0xfffd, 0x0020) + .withScalars(0xfffd, 0x0020, 0xfffd, 0x0020, + 0xfffd, 0x0020, 0xfffd, 0x0020) + .withScalars(0xfffd, 0x0020, 0xfffd, 0x0020, + 0xfffd, 0x0020, 0xfffd, 0x0020) + .withScalars(0xfffd, 0x0020, 0xfffd, 0x0020, + 0xfffd, 0x0020, 0xfffd, 0x0020), + "\xc0\x20\xc1\x20\xc2\x20\xc3\x20\xc4\x20\xc5\x20\xc6\x20\xc7\x20" + "\xc8\x20\xc9\x20\xca\x20\xcb\x20\xcc\x20\xcd\x20\xce\x20\xcf\x20" + "\xd0\x20\xd1\x20\xd2\x20\xd3\x20\xd4\x20\xd5\x20\xd6\x20\xd7\x20" + "\xd8\x20\xd9\x20\xda\x20\xdb\x20\xdc\x20\xdd\x20\xde\x20\xdf\x20")); + + // Start bytes of 3-byte sequences (0xe0--0xef). + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, + 0xfffd, 0xfffd, 0xfffd, 0xfffd) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, + 0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef")); + + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0x0020, 0xfffd, 0x0020, + 0xfffd, 0x0020, 0xfffd, 0x0020) + .withScalars(0xfffd, 0x0020, 0xfffd, 0x0020, + 0xfffd, 0x0020, 0xfffd, 0x0020) + .withScalars(0xfffd, 0x0020, 0xfffd, 0x0020, + 0xfffd, 0x0020, 0xfffd, 0x0020) + .withScalars(0xfffd, 0x0020, 0xfffd, 0x0020, + 0xfffd, 0x0020, 0xfffd, 0x0020), + "\xe0\x20\xe1\x20\xe2\x20\xe3\x20\xe4\x20\xe5\x20\xe6\x20\xe7\x20" + "\xe8\x20\xe9\x20\xea\x20\xeb\x20\xec\x20\xed\x20\xee\x20\xef\x20")); + + // Start bytes of 4-byte sequences (0xf0--0xf7). + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, + 0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7")); + + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0x0020, 0xfffd, 0x0020, + 0xfffd, 0x0020, 0xfffd, 0x0020) + .withScalars(0xfffd, 0x0020, 0xfffd, 0x0020, + 0xfffd, 0x0020, 0xfffd, 0x0020), + "\xf0\x20\xf1\x20\xf2\x20\xf3\x20\xf4\x20\xf5\x20\xf6\x20\xf7\x20")); + + // Start bytes of 5-byte sequences (0xf8--0xfb). + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xf8\xf9\xfa\xfb")); + + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0x0020, 0xfffd, 0x0020, + 0xfffd, 0x0020, 0xfffd, 0x0020), + "\xf8\x20\xf9\x20\xfa\x20\xfb\x20")); + + // Start bytes of 6-byte sequences (0xfc--0xfd). + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd), + "\xfc\xfd")); + + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0x0020, 0xfffd, 0x0020), + "\xfc\x20\xfd\x20")); + + // + // Other bytes (0xc0--0xc1, 0xfe--0xff). + // + + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xc0")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xc1")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xfe")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xff")); + + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xc0\xc1\xfe\xff")); + + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xfe\xfe\xff\xff")); + + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xfe\x80\x80\x80\x80\x80")); + + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xff\x80\x80\x80\x80\x80")); + + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0x0020, 0xfffd, 0x0020, + 0xfffd, 0x0020, 0xfffd, 0x0020), + "\xc0\x20\xc1\x20\xfe\x20\xff\x20")); + + // + // Sequences with one continuation byte missing + // + + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xc2")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xdf")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), + "\xe0\xa0")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), + "\xe0\xbf")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), + "\xe1\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), + "\xec\xbf")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), + "\xed\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), + "\xed\x9f")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), + "\xee\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), + "\xef\xbf")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), + "\xf0\x90\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), + "\xf0\xbf\xbf")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), + "\xf1\x80\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), + "\xf3\xbf\xbf")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), + "\xf4\x80\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), + "\xf4\x8f\xbf")); + + // Overlong sequences with one trailing byte missing. + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), + "\xc0")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), + "\xc1")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd), + "\xe0\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd), + "\xe0\x9f")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd), + "\xf0\x80\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd), + "\xf0\x8f\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xf8\x80\x80\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xfc\x80\x80\x80\x80")); + + // Sequences that represent surrogates with one trailing byte missing. + // High surrogates + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd), + "\xed\xa0")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd), + "\xed\xac")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd), + "\xed\xaf")); + // Low surrogates + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd), + "\xed\xb0")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd), + "\xed\xb4")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd), + "\xed\xbf")); + + // Ill-formed 4-byte sequences. + // 11110zzz 10zzyyyy 10yyyyxx 10xxxxxx + // U+1100xx (invalid) + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd), + "\xf4\x90\x80")); + // U+13FBxx (invalid) + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd), + "\xf4\xbf\xbf")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd), + "\xf5\x80\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd), + "\xf6\x80\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd), + "\xf7\x80\x80")); + // U+1FFBxx (invalid) + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd), + "\xf7\xbf\xbf")); + + // Ill-formed 5-byte sequences. + // 111110uu 10zzzzzz 10zzyyyy 10yyyyxx 10xxxxxx + // U+2000xx (invalid) + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xf8\x88\x80\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xf8\xbf\xbf\xbf")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xf9\x80\x80\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xfa\x80\x80\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xfb\x80\x80\x80")); + // U+3FFFFxx (invalid) + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xfb\xbf\xbf\xbf")); + + // Ill-formed 6-byte sequences. + // 1111110u 10uuuuuu 10uzzzzz 10zzzyyyy 10yyyyxx 10xxxxxx + // U+40000xx (invalid) + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xfc\x84\x80\x80\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xfc\xbf\xbf\xbf\xbf")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xfd\x80\x80\x80\x80")); + // U+7FFFFFxx (invalid) + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xfd\xbf\xbf\xbf\xbf")); + + // + // Sequences with two continuation bytes missing + // + + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), + "\xf0\x90")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), + "\xf0\xbf")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), + "\xf1\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), + "\xf3\xbf")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), + "\xf4\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), + "\xf4\x8f")); + + // Overlong sequences with two trailing byte missing. + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xe0")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd), + "\xf0\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd), + "\xf0\x8f")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd), + "\xf8\x80\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xfc\x80\x80\x80")); + + // Sequences that represent surrogates with two trailing bytes missing. + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xed")); + + // Ill-formed 4-byte sequences. + // 11110zzz 10zzyyyy 10yyyyxx 10xxxxxx + // U+110yxx (invalid) + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd), + "\xf4\x90")); + // U+13Fyxx (invalid) + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd), + "\xf4\xbf")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd), + "\xf5\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd), + "\xf6\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd), + "\xf7\x80")); + // U+1FFyxx (invalid) + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd), + "\xf7\xbf")); + + // Ill-formed 5-byte sequences. + // 111110uu 10zzzzzz 10zzyyyy 10yyyyxx 10xxxxxx + // U+200yxx (invalid) + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd), + "\xf8\x88\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd), + "\xf8\xbf\xbf")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd), + "\xf9\x80\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd), + "\xfa\x80\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd), + "\xfb\x80\x80")); + // U+3FFFyxx (invalid) + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd), + "\xfb\xbf\xbf")); + + // Ill-formed 6-byte sequences. + // 1111110u 10uuuuuu 10zzzzzz 10zzyyyy 10yyyyxx 10xxxxxx + // U+4000yxx (invalid) + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xfc\x84\x80\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xfc\xbf\xbf\xbf")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xfd\x80\x80\x80")); + // U+7FFFFyxx (invalid) + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xfd\xbf\xbf\xbf")); + + // + // Sequences with three continuation bytes missing + // + + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xf0")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xf1")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xf2")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xf3")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xf4")); + + // Broken overlong sequences. + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xf0")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd), + "\xf8\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd), + "\xfc\x80\x80")); + + // Ill-formed 4-byte sequences. + // 11110zzz 10zzyyyy 10yyyyxx 10xxxxxx + // U+14yyxx (invalid) + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xf5")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xf6")); + // U+1Cyyxx (invalid) + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xf7")); + + // Ill-formed 5-byte sequences. + // 111110uu 10zzzzzz 10zzyyyy 10yyyyxx 10xxxxxx + // U+20yyxx (invalid) + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd), + "\xf8\x88")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd), + "\xf8\xbf")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd), + "\xf9\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd), + "\xfa\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd), + "\xfb\x80")); + // U+3FCyyxx (invalid) + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd), + "\xfb\xbf")); + + // Ill-formed 6-byte sequences. + // 1111110u 10uuuuuu 10zzzzzz 10zzyyyy 10yyyyxx 10xxxxxx + // U+400yyxx (invalid) + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd), + "\xfc\x84\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd), + "\xfc\xbf\xbf")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd), + "\xfd\x80\x80")); + // U+7FFCyyxx (invalid) + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd, 0xfffd), + "\xfd\xbf\xbf")); + + // + // Sequences with four continuation bytes missing + // + + // Ill-formed 5-byte sequences. + // 111110uu 10zzzzzz 10zzyyyy 10yyyyxx 10xxxxxx + // U+uzyyxx (invalid) + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xf8")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xf9")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xfa")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xfb")); + // U+3zyyxx (invalid) + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xfb")); + + // Broken overlong sequences. + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xf8")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd), + "\xfc\x80")); + + // Ill-formed 6-byte sequences. + // 1111110u 10uuuuuu 10zzzzzz 10zzyyyy 10yyyyxx 10xxxxxx + // U+uzzyyxx (invalid) + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd), + "\xfc\x84")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd), + "\xfc\xbf")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd), + "\xfd\x80")); + // U+7Fzzyyxx (invalid) + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd), + "\xfd\xbf")); + + // + // Sequences with five continuation bytes missing + // + + // Ill-formed 6-byte sequences. + // 1111110u 10uuuuuu 10zzzzzz 10zzyyyy 10yyyyxx 10xxxxxx + // U+uzzyyxx (invalid) + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xfc")); + // U+uuzzyyxx (invalid) + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd), "\xfd")); + + // + // Consecutive sequences with trailing bytes missing + // + + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, /**/ 0xfffd, 0xfffd, /**/ 0xfffd, 0xfffd, 0xfffd) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd) + .withScalars(0xfffd, /**/ 0xfffd, /**/ 0xfffd, 0xfffd, 0xfffd) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xc0" "\xe0\x80" "\xf0\x80\x80" + "\xf8\x80\x80\x80" + "\xfc\x80\x80\x80\x80" + "\xdf" "\xef\xbf" "\xf7\xbf\xbf" + "\xfb\xbf\xbf\xbf" + "\xfd\xbf\xbf\xbf\xbf")); + + // + // Overlong UTF-8 sequences + // + + // U+002F SOLIDUS + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0x002f), "\x2f")); + + // Overlong sequences of the above. + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd), + "\xc0\xaf")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd), + "\xe0\x80\xaf")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xf0\x80\x80\xaf")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xf8\x80\x80\x80\xaf")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xfc\x80\x80\x80\x80\xaf")); + + // U+0000 NULL + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0x0000), + std::string_view("\x00", 1))); + + // Overlong sequences of the above. + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd), + "\xc0\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd), + "\xe0\x80\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xf0\x80\x80\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xf8\x80\x80\x80\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xfc\x80\x80\x80\x80\x80")); + + // Other overlong sequences. + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd), + "\xc0\xbf")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd), + "\xc1\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal).withScalars(0xfffd, 0xfffd), + "\xc1\xbf")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd), + "\xe0\x9f\xbf")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd), + "\xed\xa0\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd), + "\xed\xbf\xbf")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xf0\x8f\x80\x80")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xf0\x8f\xbf\xbf")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xf8\x87\xbf\xbf\xbf")); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xfc\x83\xbf\xbf\xbf\xbf")); + + // + // Isolated surrogates + // + + // Unicode 6.3.0: + // + // D71. High-surrogate code point: A Unicode code point in the range + // U+D800 to U+DBFF. + // + // D73. Low-surrogate code point: A Unicode code point in the range + // U+DC00 to U+DFFF. + + // Note: U+E0100 is in UTF16. + + // High surrogates + + // U+D800 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd), + "\xed\xa0\x80")); + + // U+DB40 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd), + "\xed\xac\xa0")); + + // U+DBFF + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd), + "\xed\xaf\xbf")); + + // Low surrogates + + // U+DC00 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd), + "\xed\xb0\x80")); + + // U+DD00 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd), + "\xed\xb4\x80")); + + // U+DFFF + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd), + "\xed\xbf\xbf")); + + // Surrogate pairs + + // U+D800 U+DC00 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xed\xa0\x80\xed\xb0\x80")); + + // U+D800 U+DD00 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xed\xa0\x80\xed\xb4\x80")); + + // U+D800 U+DFFF + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xed\xa0\x80\xed\xbf\xbf")); + + // U+DB40 U+DC00 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xed\xac\xa0\xed\xb0\x80")); + + // U+DB40 U+DD00 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xed\xac\xa0\xed\xb4\x80")); + + // U+DB40 U+DFFF + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xed\xac\xa0\xed\xbf\xbf")); + + // U+DBFF U+DC00 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xed\xaf\xbf\xed\xb0\x80")); + + // U+DBFF U+DD00 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xed\xaf\xbf\xed\xb4\x80")); + + // U+DBFF U+DFFF + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceIllegal) + .withScalars(0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd), + "\xed\xaf\xbf\xed\xbf\xbf")); + + // + // Noncharacters + // + + // Unicode 6.3.0: + // + // D14. Noncharacter: A code point that is permanently reserved for + // internal use and that should never be interchanged. Noncharacters + // consist of the values U+nFFFE and U+nFFFF (where n is from 0 to 1016) + // and the values U+FDD0..U+FDEF. + + // U+FFFE + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfffe), + "\xef\xbf\xbe")); + + // U+FFFF + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xffff), + "\xef\xbf\xbf")); + + // U+1FFFE + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0x1fffe), + "\xf0\x9f\xbf\xbe")); + + // U+1FFFF + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0x1ffff), + "\xf0\x9f\xbf\xbf")); + + // U+2FFFE + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0x2fffe), + "\xf0\xaf\xbf\xbe")); + + // U+2FFFF + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0x2ffff), + "\xf0\xaf\xbf\xbf")); + + // U+3FFFE + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0x3fffe), + "\xf0\xbf\xbf\xbe")); + + // U+3FFFF + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0x3ffff), + "\xf0\xbf\xbf\xbf")); + + // U+4FFFE + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0x4fffe), + "\xf1\x8f\xbf\xbe")); + + // U+4FFFF + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0x4ffff), + "\xf1\x8f\xbf\xbf")); + + // U+5FFFE + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0x5fffe), + "\xf1\x9f\xbf\xbe")); + + // U+5FFFF + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0x5ffff), + "\xf1\x9f\xbf\xbf")); + + // U+6FFFE + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0x6fffe), + "\xf1\xaf\xbf\xbe")); + + // U+6FFFF + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0x6ffff), + "\xf1\xaf\xbf\xbf")); + + // U+7FFFE + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0x7fffe), + "\xf1\xbf\xbf\xbe")); + + // U+7FFFF + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0x7ffff), + "\xf1\xbf\xbf\xbf")); + + // U+8FFFE + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0x8fffe), + "\xf2\x8f\xbf\xbe")); + + // U+8FFFF + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0x8ffff), + "\xf2\x8f\xbf\xbf")); + + // U+9FFFE + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0x9fffe), + "\xf2\x9f\xbf\xbe")); + + // U+9FFFF + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0x9ffff), + "\xf2\x9f\xbf\xbf")); + + // U+AFFFE + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xafffe), + "\xf2\xaf\xbf\xbe")); + + // U+AFFFF + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xaffff), + "\xf2\xaf\xbf\xbf")); + + // U+BFFFE + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xbfffe), + "\xf2\xbf\xbf\xbe")); + + // U+BFFFF + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xbffff), + "\xf2\xbf\xbf\xbf")); + + // U+CFFFE + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xcfffe), + "\xf3\x8f\xbf\xbe")); + + // U+CFFFF + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xcfffF), + "\xf3\x8f\xbf\xbf")); + + // U+DFFFE + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xdfffe), + "\xf3\x9f\xbf\xbe")); + + // U+DFFFF + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xdffff), + "\xf3\x9f\xbf\xbf")); + + // U+EFFFE + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xefffe), + "\xf3\xaf\xbf\xbe")); + + // U+EFFFF + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xeffff), + "\xf3\xaf\xbf\xbf")); + + // U+FFFFE + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xffffe), + "\xf3\xbf\xbf\xbe")); + + // U+FFFFF + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfffff), + "\xf3\xbf\xbf\xbf")); + + // U+10FFFE + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0x10fffe), + "\xf4\x8f\xbf\xbe")); + + // U+10FFFF + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0x10ffff), + "\xf4\x8f\xbf\xbf")); + + // U+FDD0 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfdd0), + "\xef\xb7\x90")); + + // U+FDD1 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfdd1), + "\xef\xb7\x91")); + + // U+FDD2 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfdd2), + "\xef\xb7\x92")); + + // U+FDD3 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfdd3), + "\xef\xb7\x93")); + + // U+FDD4 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfdd4), + "\xef\xb7\x94")); + + // U+FDD5 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfdd5), + "\xef\xb7\x95")); + + // U+FDD6 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfdd6), + "\xef\xb7\x96")); + + // U+FDD7 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfdd7), + "\xef\xb7\x97")); + + // U+FDD8 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfdd8), + "\xef\xb7\x98")); + + // U+FDD9 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfdd9), + "\xef\xb7\x99")); + + // U+FDDA + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfdda), + "\xef\xb7\x9a")); + + // U+FDDB + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfddb), + "\xef\xb7\x9b")); + + // U+FDDC + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfddc), + "\xef\xb7\x9c")); + + // U+FDDD + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfddd), + "\xef\xb7\x9d")); + + // U+FDDE + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfdde), + "\xef\xb7\x9e")); + + // U+FDDF + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfddf), + "\xef\xb7\x9f")); + + // U+FDE0 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfde0), + "\xef\xb7\xa0")); + + // U+FDE1 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfde1), + "\xef\xb7\xa1")); + + // U+FDE2 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfde2), + "\xef\xb7\xa2")); + + // U+FDE3 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfde3), + "\xef\xb7\xa3")); + + // U+FDE4 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfde4), + "\xef\xb7\xa4")); + + // U+FDE5 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfde5), + "\xef\xb7\xa5")); + + // U+FDE6 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfde6), + "\xef\xb7\xa6")); + + // U+FDE7 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfde7), + "\xef\xb7\xa7")); + + // U+FDE8 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfde8), + "\xef\xb7\xa8")); + + // U+FDE9 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfde9), + "\xef\xb7\xa9")); + + // U+FDEA + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfdea), + "\xef\xb7\xaa")); + + // U+FDEB + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfdeb), + "\xef\xb7\xab")); + + // U+FDEC + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfdec), + "\xef\xb7\xac")); + + // U+FDED + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfded), + "\xef\xb7\xad")); + + // U+FDEE + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfdee), + "\xef\xb7\xae")); + + // U+FDEF + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfdef), + "\xef\xb7\xaf")); + + // U+FDF0 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfdf0), + "\xef\xb7\xb0")); + + // U+FDF1 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfdf1), + "\xef\xb7\xb1")); + + // U+FDF2 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfdf2), + "\xef\xb7\xb2")); + + // U+FDF3 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfdf3), + "\xef\xb7\xb3")); + + // U+FDF4 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfdf4), + "\xef\xb7\xb4")); + + // U+FDF5 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfdf5), + "\xef\xb7\xb5")); + + // U+FDF6 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfdf6), + "\xef\xb7\xb6")); + + // U+FDF7 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfdf7), + "\xef\xb7\xb7")); + + // U+FDF8 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfdf8), + "\xef\xb7\xb8")); + + // U+FDF9 + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfdf9), + "\xef\xb7\xb9")); + + // U+FDFA + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfdfa), + "\xef\xb7\xba")); + + // U+FDFB + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfdfb), + "\xef\xb7\xbb")); + + // U+FDFC + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfdfc), + "\xef\xb7\xbc")); + + // U+FDFD + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfdfd), + "\xef\xb7\xbd")); + + // U+FDFE + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfdfe), + "\xef\xb7\xbe")); + + // U+FDFF + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0xfdff), + "\xef\xb7\xbf")); +} + +TEST(ConvertUTFTest, UTF8ToUTF32PartialLenient) { + // U+0041 LATIN CAPITAL LETTER A + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(conversionOK).withScalars(0x0041), + "\x41", true)); + + // + // Sequences with one continuation byte missing + // + + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceExhausted), + "\xc2", true)); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceExhausted), + "\xdf", true)); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceExhausted), + "\xe0\xa0", true)); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceExhausted), + "\xe0\xbf", true)); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceExhausted), + "\xe1\x80", true)); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceExhausted), + "\xec\xbf", true)); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceExhausted), + "\xed\x80", true)); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceExhausted), + "\xed\x9f", true)); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceExhausted), + "\xee\x80", true)); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceExhausted), + "\xef\xbf", true)); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceExhausted), + "\xf0\x90\x80", true)); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceExhausted), + "\xf0\xbf\xbf", true)); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceExhausted), + "\xf1\x80\x80", true)); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceExhausted), + "\xf3\xbf\xbf", true)); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceExhausted), + "\xf4\x80\x80", true)); + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceExhausted), + "\xf4\x8f\xbf", true)); + + EXPECT_TRUE(CheckConvertUTF8ToUnicodeScalars( + ConvertUTFResultContainer(sourceExhausted).withScalars(0x0041), + "\x41\xc2", true)); +} + diff --git a/wpiutil/src/test/native/cpp/llvm/DenseMapTest.cpp b/wpiutil/src/test/native/cpp/llvm/DenseMapTest.cpp new file mode 100644 index 0000000000..6e08de430d --- /dev/null +++ b/wpiutil/src/test/native/cpp/llvm/DenseMapTest.cpp @@ -0,0 +1,637 @@ +//===- llvm/unittest/ADT/DenseMapMap.cpp - DenseMap unit tests --*- 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 +// +//===----------------------------------------------------------------------===// + +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif + +#include "wpi/DenseMap.h" +#include "gtest/gtest.h" +#include +#include + +using namespace wpi; + +namespace { + +uint32_t getTestKey(int i, uint32_t *) { return i; } +uint32_t getTestValue(int i, uint32_t *) { return 42 + i; } + +uint32_t *getTestKey(int i, uint32_t **) { + static uint32_t dummy_arr1[8192]; + assert(i < 8192 && "Only support 8192 dummy keys."); + return &dummy_arr1[i]; +} +uint32_t *getTestValue(int i, uint32_t **) { + static uint32_t dummy_arr1[8192]; + assert(i < 8192 && "Only support 8192 dummy keys."); + return &dummy_arr1[i]; +} + +/// A test class that tries to check that construction and destruction +/// occur correctly. +class CtorTester { + static std::set Constructed; + int Value; + +public: + explicit CtorTester(int Value = 0) : Value(Value) { + EXPECT_TRUE(Constructed.insert(this).second); + } + CtorTester(uint32_t Value) : Value(Value) { + EXPECT_TRUE(Constructed.insert(this).second); + } + CtorTester(const CtorTester &Arg) : Value(Arg.Value) { + EXPECT_TRUE(Constructed.insert(this).second); + } + CtorTester &operator=(const CtorTester &) = default; + ~CtorTester() { + EXPECT_EQ(1u, Constructed.erase(this)); + } + operator uint32_t() const { return Value; } + + int getValue() const { return Value; } + bool operator==(const CtorTester &RHS) const { return Value == RHS.Value; } +}; + +std::set CtorTester::Constructed; + +struct CtorTesterMapInfo { + static inline CtorTester getEmptyKey() { return CtorTester(-1); } + static inline CtorTester getTombstoneKey() { return CtorTester(-2); } + static unsigned getHashValue(const CtorTester &Val) { + return Val.getValue() * 37u; + } + static bool isEqual(const CtorTester &LHS, const CtorTester &RHS) { + return LHS == RHS; + } +}; + +CtorTester getTestKey(int i, CtorTester *) { return CtorTester(i); } +CtorTester getTestValue(int i, CtorTester *) { return CtorTester(42 + i); } + +// Test fixture, with helper functions implemented by forwarding to global +// function overloads selected by component types of the type parameter. This +// allows all of the map implementations to be tested with shared +// implementations of helper routines. +template +class DenseMapTest : public ::testing::Test { +protected: + T Map; + + static typename T::key_type *const dummy_key_ptr; + static typename T::mapped_type *const dummy_value_ptr; + + typename T::key_type getKey(int i = 0) { + return getTestKey(i, dummy_key_ptr); + } + typename T::mapped_type getValue(int i = 0) { + return getTestValue(i, dummy_value_ptr); + } +}; + +template +typename T::key_type *const DenseMapTest::dummy_key_ptr = nullptr; +template +typename T::mapped_type *const DenseMapTest::dummy_value_ptr = nullptr; + +// Register these types for testing. +typedef ::testing::Types, + DenseMap, + DenseMap, + SmallDenseMap, + SmallDenseMap, + SmallDenseMap + > DenseMapTestTypes; +TYPED_TEST_SUITE(DenseMapTest, DenseMapTestTypes, ); + +// Empty map tests +TYPED_TEST(DenseMapTest, EmptyIntMapTest) { + // Size tests + EXPECT_EQ(0u, this->Map.size()); + EXPECT_TRUE(this->Map.empty()); + + // Iterator tests + EXPECT_TRUE(this->Map.begin() == this->Map.end()); + + // Lookup tests + EXPECT_FALSE(this->Map.count(this->getKey())); + EXPECT_TRUE(this->Map.find(this->getKey()) == this->Map.end()); + EXPECT_EQ(typename TypeParam::mapped_type(), + this->Map.lookup(this->getKey())); +} + +// Constant map tests +TYPED_TEST(DenseMapTest, ConstEmptyMapTest) { + const TypeParam &ConstMap = this->Map; + EXPECT_EQ(0u, ConstMap.size()); + EXPECT_TRUE(ConstMap.empty()); + EXPECT_TRUE(ConstMap.begin() == ConstMap.end()); +} + +// A map with a single entry +TYPED_TEST(DenseMapTest, SingleEntryMapTest) { + this->Map[this->getKey()] = this->getValue(); + + // Size tests + EXPECT_EQ(1u, this->Map.size()); + EXPECT_FALSE(this->Map.begin() == this->Map.end()); + EXPECT_FALSE(this->Map.empty()); + + // Iterator tests + typename TypeParam::iterator it = this->Map.begin(); + EXPECT_EQ(this->getKey(), it->first); + EXPECT_EQ(this->getValue(), it->second); + ++it; + EXPECT_TRUE(it == this->Map.end()); + + // Lookup tests + EXPECT_TRUE(this->Map.count(this->getKey())); + EXPECT_TRUE(this->Map.find(this->getKey()) == this->Map.begin()); + EXPECT_EQ(this->getValue(), this->Map.lookup(this->getKey())); + EXPECT_EQ(this->getValue(), this->Map[this->getKey()]); +} + +// Test clear() method +TYPED_TEST(DenseMapTest, ClearTest) { + this->Map[this->getKey()] = this->getValue(); + this->Map.clear(); + + EXPECT_EQ(0u, this->Map.size()); + EXPECT_TRUE(this->Map.empty()); + EXPECT_TRUE(this->Map.begin() == this->Map.end()); +} + +// Test erase(iterator) method +TYPED_TEST(DenseMapTest, EraseTest) { + this->Map[this->getKey()] = this->getValue(); + this->Map.erase(this->Map.begin()); + + EXPECT_EQ(0u, this->Map.size()); + EXPECT_TRUE(this->Map.empty()); + EXPECT_TRUE(this->Map.begin() == this->Map.end()); +} + +// Test erase(value) method +TYPED_TEST(DenseMapTest, EraseTest2) { + this->Map[this->getKey()] = this->getValue(); + this->Map.erase(this->getKey()); + + EXPECT_EQ(0u, this->Map.size()); + EXPECT_TRUE(this->Map.empty()); + EXPECT_TRUE(this->Map.begin() == this->Map.end()); +} + +// Test insert() method +TYPED_TEST(DenseMapTest, InsertTest) { + this->Map.insert(std::make_pair(this->getKey(), this->getValue())); + EXPECT_EQ(1u, this->Map.size()); + EXPECT_EQ(this->getValue(), this->Map[this->getKey()]); +} + +// Test copy constructor method +TYPED_TEST(DenseMapTest, CopyConstructorTest) { + this->Map[this->getKey()] = this->getValue(); + TypeParam copyMap(this->Map); + + EXPECT_EQ(1u, copyMap.size()); + EXPECT_EQ(this->getValue(), copyMap[this->getKey()]); +} + +// Test copy constructor method where SmallDenseMap isn't small. +TYPED_TEST(DenseMapTest, CopyConstructorNotSmallTest) { + for (int Key = 0; Key < 5; ++Key) + this->Map[this->getKey(Key)] = this->getValue(Key); + TypeParam copyMap(this->Map); + + EXPECT_EQ(5u, copyMap.size()); + for (int Key = 0; Key < 5; ++Key) + EXPECT_EQ(this->getValue(Key), copyMap[this->getKey(Key)]); +} + +// Test copying from a default-constructed map. +TYPED_TEST(DenseMapTest, CopyConstructorFromDefaultTest) { + TypeParam copyMap(this->Map); + + EXPECT_TRUE(copyMap.empty()); +} + +// Test copying from an empty map where SmallDenseMap isn't small. +TYPED_TEST(DenseMapTest, CopyConstructorFromEmptyTest) { + for (int Key = 0; Key < 5; ++Key) + this->Map[this->getKey(Key)] = this->getValue(Key); + this->Map.clear(); + TypeParam copyMap(this->Map); + + EXPECT_TRUE(copyMap.empty()); +} + +// Test assignment operator method +TYPED_TEST(DenseMapTest, AssignmentTest) { + this->Map[this->getKey()] = this->getValue(); + TypeParam copyMap = this->Map; + + EXPECT_EQ(1u, copyMap.size()); + EXPECT_EQ(this->getValue(), copyMap[this->getKey()]); + + // test self-assignment. + copyMap = static_cast(copyMap); + EXPECT_EQ(1u, copyMap.size()); + EXPECT_EQ(this->getValue(), copyMap[this->getKey()]); +} + +TYPED_TEST(DenseMapTest, AssignmentTestNotSmall) { + for (int Key = 0; Key < 5; ++Key) + this->Map[this->getKey(Key)] = this->getValue(Key); + TypeParam copyMap = this->Map; + + EXPECT_EQ(5u, copyMap.size()); + for (int Key = 0; Key < 5; ++Key) + EXPECT_EQ(this->getValue(Key), copyMap[this->getKey(Key)]); + + // test self-assignment. + copyMap = static_cast(copyMap); + EXPECT_EQ(5u, copyMap.size()); + for (int Key = 0; Key < 5; ++Key) + EXPECT_EQ(this->getValue(Key), copyMap[this->getKey(Key)]); +} + +// Test swap method +TYPED_TEST(DenseMapTest, SwapTest) { + this->Map[this->getKey()] = this->getValue(); + TypeParam otherMap; + + this->Map.swap(otherMap); + EXPECT_EQ(0u, this->Map.size()); + EXPECT_TRUE(this->Map.empty()); + EXPECT_EQ(1u, otherMap.size()); + EXPECT_EQ(this->getValue(), otherMap[this->getKey()]); + + this->Map.swap(otherMap); + EXPECT_EQ(0u, otherMap.size()); + EXPECT_TRUE(otherMap.empty()); + EXPECT_EQ(1u, this->Map.size()); + EXPECT_EQ(this->getValue(), this->Map[this->getKey()]); + + // Make this more interesting by inserting 100 numbers into the map. + for (int i = 0; i < 100; ++i) + this->Map[this->getKey(i)] = this->getValue(i); + + this->Map.swap(otherMap); + EXPECT_EQ(0u, this->Map.size()); + EXPECT_TRUE(this->Map.empty()); + EXPECT_EQ(100u, otherMap.size()); + for (int i = 0; i < 100; ++i) + EXPECT_EQ(this->getValue(i), otherMap[this->getKey(i)]); + + this->Map.swap(otherMap); + EXPECT_EQ(0u, otherMap.size()); + EXPECT_TRUE(otherMap.empty()); + EXPECT_EQ(100u, this->Map.size()); + for (int i = 0; i < 100; ++i) + EXPECT_EQ(this->getValue(i), this->Map[this->getKey(i)]); +} + +// A more complex iteration test +TYPED_TEST(DenseMapTest, IterationTest) { + bool visited[100]; + std::map visitedIndex; + + // Insert 100 numbers into the map + for (int i = 0; i < 100; ++i) { + visited[i] = false; + visitedIndex[this->getKey(i)] = i; + + this->Map[this->getKey(i)] = this->getValue(i); + } + + // Iterate over all numbers and mark each one found. + for (typename TypeParam::iterator it = this->Map.begin(); + it != this->Map.end(); ++it) + visited[visitedIndex[it->first]] = true; + + // Ensure every number was visited. + for (int i = 0; i < 100; ++i) + ASSERT_TRUE(visited[i]) << "Entry #" << i << " was never visited"; +} + +// const_iterator test +TYPED_TEST(DenseMapTest, ConstIteratorTest) { + // Check conversion from iterator to const_iterator. + typename TypeParam::iterator it = this->Map.begin(); + typename TypeParam::const_iterator cit(it); + EXPECT_TRUE(it == cit); + + // Check copying of const_iterators. + typename TypeParam::const_iterator cit2(cit); + EXPECT_TRUE(cit == cit2); +} + +namespace { +// Simple class that counts how many moves and copy happens when growing a map +struct CountCopyAndMove { + static int Move; + static int Copy; + CountCopyAndMove() {} + + CountCopyAndMove(const CountCopyAndMove &) { Copy++; } + CountCopyAndMove &operator=(const CountCopyAndMove &) { + Copy++; + return *this; + } + CountCopyAndMove(CountCopyAndMove &&) { Move++; } + CountCopyAndMove &operator=(const CountCopyAndMove &&) { + Move++; + return *this; + } +}; +int CountCopyAndMove::Copy = 0; +int CountCopyAndMove::Move = 0; + +} // anonymous namespace + +// Test initializer list construction. +TEST(DenseMapCustomTest, InitializerList) { + DenseMap M({{0, 0}, {0, 1}, {1, 2}}); + EXPECT_EQ(2u, M.size()); + EXPECT_EQ(1u, M.count(0)); + EXPECT_EQ(0, M[0]); + EXPECT_EQ(1u, M.count(1)); + EXPECT_EQ(2, M[1]); +} + +// Test initializer list construction. +TEST(DenseMapCustomTest, EqualityComparison) { + DenseMap M1({{0, 0}, {1, 2}}); + DenseMap M2({{0, 0}, {1, 2}}); + DenseMap M3({{0, 0}, {1, 3}}); + + EXPECT_EQ(M1, M2); + EXPECT_NE(M1, M3); +} + +// Test for the default minimum size of a DenseMap +TEST(DenseMapCustomTest, DefaultMinReservedSizeTest) { + // IF THIS VALUE CHANGE, please update InitialSizeTest, InitFromIterator, and + // ReserveTest as well! + const int ExpectedInitialBucketCount = 64; + // Formula from DenseMap::getMinBucketToReserveForEntries() + const int ExpectedMaxInitialEntries = ExpectedInitialBucketCount * 3 / 4 - 1; + + DenseMap Map; + // Will allocate 64 buckets + Map.reserve(1); + unsigned MemorySize = Map.getMemorySize(); + CountCopyAndMove::Copy = 0; + CountCopyAndMove::Move = 0; + for (int i = 0; i < ExpectedMaxInitialEntries; ++i) + Map.insert(std::pair(std::piecewise_construct, + std::forward_as_tuple(i), + std::forward_as_tuple())); + // Check that we didn't grow + EXPECT_EQ(MemorySize, Map.getMemorySize()); + // Check that move was called the expected number of times + EXPECT_EQ(ExpectedMaxInitialEntries, CountCopyAndMove::Move); + // Check that no copy occurred + EXPECT_EQ(0, CountCopyAndMove::Copy); + + // Adding one extra element should grow the map + Map.insert(std::pair( + std::piecewise_construct, + std::forward_as_tuple(ExpectedMaxInitialEntries), + std::forward_as_tuple())); + // Check that we grew + EXPECT_NE(MemorySize, Map.getMemorySize()); + // Check that move was called the expected number of times + // This relies on move-construction elision, and cannot be reliably tested. + // EXPECT_EQ(ExpectedMaxInitialEntries + 2, CountCopyAndMove::Move); + // Check that no copy occurred + EXPECT_EQ(0, CountCopyAndMove::Copy); +} + +// Make sure creating the map with an initial size of N actually gives us enough +// buckets to insert N items without increasing allocation size. +TEST(DenseMapCustomTest, InitialSizeTest) { + // Test a few different sizes, 48 is *not* a random choice: we need a value + // that is 2/3 of a power of two to stress the grow() condition, and the power + // of two has to be at least 64 because of minimum size allocation in the + // DenseMap (see DefaultMinReservedSizeTest). 66 is a value just above the + // 64 default init. + for (auto Size : {1, 2, 48, 66}) { + DenseMap Map(Size); + unsigned MemorySize = Map.getMemorySize(); + CountCopyAndMove::Copy = 0; + CountCopyAndMove::Move = 0; + for (int i = 0; i < Size; ++i) + Map.insert(std::pair(std::piecewise_construct, + std::forward_as_tuple(i), + std::forward_as_tuple())); + // Check that we didn't grow + EXPECT_EQ(MemorySize, Map.getMemorySize()); + // Check that move was called the expected number of times + EXPECT_EQ(Size, CountCopyAndMove::Move); + // Check that no copy occurred + EXPECT_EQ(0, CountCopyAndMove::Copy); + } +} + +// Make sure creating the map with a iterator range does not trigger grow() +TEST(DenseMapCustomTest, InitFromIterator) { + std::vector> Values; + // The size is a random value greater than 64 (hardcoded DenseMap min init) + const int Count = 65; + for (int i = 0; i < Count; i++) + Values.emplace_back(i, CountCopyAndMove()); + + CountCopyAndMove::Move = 0; + CountCopyAndMove::Copy = 0; + DenseMap Map(Values.begin(), Values.end()); + // Check that no move occurred + EXPECT_EQ(0, CountCopyAndMove::Move); + // Check that copy was called the expected number of times + EXPECT_EQ(Count, CountCopyAndMove::Copy); +} + +// Make sure reserve actually gives us enough buckets to insert N items +// without increasing allocation size. +TEST(DenseMapCustomTest, ReserveTest) { + // Test a few different size, 48 is *not* a random choice: we need a value + // that is 2/3 of a power of two to stress the grow() condition, and the power + // of two has to be at least 64 because of minimum size allocation in the + // DenseMap (see DefaultMinReservedSizeTest). 66 is a value just above the + // 64 default init. + for (auto Size : {1, 2, 48, 66}) { + DenseMap Map; + Map.reserve(Size); + unsigned MemorySize = Map.getMemorySize(); + CountCopyAndMove::Copy = 0; + CountCopyAndMove::Move = 0; + for (int i = 0; i < Size; ++i) + Map.insert(std::pair(std::piecewise_construct, + std::forward_as_tuple(i), + std::forward_as_tuple())); + // Check that we didn't grow + EXPECT_EQ(MemorySize, Map.getMemorySize()); + // Check that move was called the expected number of times + EXPECT_EQ(Size, CountCopyAndMove::Move); + // Check that no copy occurred + EXPECT_EQ(0, CountCopyAndMove::Copy); + } +} + +// Key traits that allows lookup with either an unsigned or char* key; +// In the latter case, "a" == 0, "b" == 1 and so on. +struct TestDenseMapInfo { + static inline unsigned getEmptyKey() { return ~0; } + static inline unsigned getTombstoneKey() { return ~0U - 1; } + static unsigned getHashValue(const unsigned& Val) { return Val * 37U; } + static unsigned getHashValue(const char* Val) { + return (unsigned)(Val[0] - 'a') * 37U; + } + static bool isEqual(const unsigned& LHS, const unsigned& RHS) { + return LHS == RHS; + } + static bool isEqual(const char* LHS, const unsigned& RHS) { + return (unsigned)(LHS[0] - 'a') == RHS; + } +}; + +// find_as() tests +TEST(DenseMapCustomTest, FindAsTest) { + DenseMap map; + map[0] = 1; + map[1] = 2; + map[2] = 3; + + // Size tests + EXPECT_EQ(3u, map.size()); + + // Normal lookup tests + EXPECT_EQ(1u, map.count(1)); + EXPECT_EQ(1u, map.find(0)->second); + EXPECT_EQ(2u, map.find(1)->second); + EXPECT_EQ(3u, map.find(2)->second); + EXPECT_TRUE(map.find(3) == map.end()); + + // find_as() tests + EXPECT_EQ(1u, map.find_as("a")->second); + EXPECT_EQ(2u, map.find_as("b")->second); + EXPECT_EQ(3u, map.find_as("c")->second); + EXPECT_TRUE(map.find_as("d") == map.end()); +} + +TEST(DenseMapCustomTest, SmallDenseMapInitializerList) { + SmallDenseMap M = {{0, 0}, {0, 1}, {1, 2}}; + EXPECT_EQ(2u, M.size()); + EXPECT_EQ(1u, M.count(0)); + EXPECT_EQ(0, M[0]); + EXPECT_EQ(1u, M.count(1)); + EXPECT_EQ(2, M[1]); +} + +struct ContiguousDenseMapInfo { + static inline unsigned getEmptyKey() { return ~0; } + static inline unsigned getTombstoneKey() { return ~0U - 1; } + static unsigned getHashValue(const unsigned& Val) { return Val; } + static bool isEqual(const unsigned& LHS, const unsigned& RHS) { + return LHS == RHS; + } +}; + +// Test that filling a small dense map with exactly the number of elements in +// the map grows to have enough space for an empty bucket. +TEST(DenseMapCustomTest, SmallDenseMapGrowTest) { + SmallDenseMap map; + // Add some number of elements, then delete a few to leave us some tombstones. + // If we just filled the map with 32 elements we'd grow because of not enough + // tombstones which masks the issue here. + for (unsigned i = 0; i < 20; ++i) + map[i] = i + 1; + for (unsigned i = 0; i < 10; ++i) + map.erase(i); + for (unsigned i = 20; i < 32; ++i) + map[i] = i + 1; + + // Size tests + EXPECT_EQ(22u, map.size()); + + // Try to find an element which doesn't exist. There was a bug in + // SmallDenseMap which led to a map with num elements == small capacity not + // having an empty bucket any more. Finding an element not in the map would + // therefore never terminate. + EXPECT_TRUE(map.find(32) == map.end()); +} + +TEST(DenseMapCustomTest, LargeSmallDenseMapCompaction) { + SmallDenseMap map; + // Fill to < 3/4 load. + for (unsigned i = 0; i < 95; ++i) + map[i] = i; + // And erase, leaving behind tombstones. + for (unsigned i = 0; i < 95; ++i) + map.erase(i); + // Fill further, so that less than 1/8 are empty, but still below 3/4 load. + for (unsigned i = 95; i < 128; ++i) + map[i] = i; + + EXPECT_EQ(33u, map.size()); + // Similar to the previous test, check for a non-existing element, as an + // indirect check that tombstones have been removed. + EXPECT_TRUE(map.find(0) == map.end()); +} + +TEST(DenseMapCustomTest, TryEmplaceTest) { + DenseMap> Map; + std::unique_ptr P(new int(2)); + auto Try1 = Map.try_emplace(0, new int(1)); + EXPECT_TRUE(Try1.second); + auto Try2 = Map.try_emplace(0, std::move(P)); + EXPECT_FALSE(Try2.second); + EXPECT_EQ(Try1.first, Try2.first); + EXPECT_NE(nullptr, P); +} + +TEST(DenseMapCustomTest, ConstTest) { + // Test that const pointers work okay for count and find, even when the + // underlying map is a non-const pointer. + DenseMap Map; + int A; + int *B = &A; + const int *C = &A; + Map.insert({B, 0}); + EXPECT_EQ(Map.count(B), 1u); + EXPECT_EQ(Map.count(C), 1u); + EXPECT_NE(Map.find(B), Map.end()); + EXPECT_NE(Map.find(C), Map.end()); +} + +struct IncompleteStruct; + +TEST(DenseMapCustomTest, OpaquePointerKey) { + // Test that we can use a pointer to an incomplete type as a DenseMap key. + // This is an important build time optimization, since many classes have + // DenseMap members. + DenseMap Map; + int Keys[3] = {0, 0, 0}; + IncompleteStruct *K1 = reinterpret_cast(&Keys[0]); + IncompleteStruct *K2 = reinterpret_cast(&Keys[1]); + IncompleteStruct *K3 = reinterpret_cast(&Keys[2]); + Map.insert({K1, 1}); + Map.insert({K2, 2}); + Map.insert({K3, 3}); + EXPECT_EQ(Map.count(K1), 1u); + EXPECT_EQ(Map[K1], 1); + EXPECT_EQ(Map[K2], 2); + EXPECT_EQ(Map[K3], 3); + Map.clear(); + EXPECT_EQ(Map.find(K1), Map.end()); + EXPECT_EQ(Map.find(K2), Map.end()); + EXPECT_EQ(Map.find(K3), Map.end()); +} +} diff --git a/wpiutil/src/test/native/cpp/llvm/EndianTest.cpp b/wpiutil/src/test/native/cpp/llvm/EndianTest.cpp new file mode 100644 index 0000000000..89edec4990 --- /dev/null +++ b/wpiutil/src/test/native/cpp/llvm/EndianTest.cpp @@ -0,0 +1,211 @@ +//===- unittests/Support/EndianTest.cpp - Endian.h tests ------------------===// +// +// 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 "wpi/Endian.h" +#include "gtest/gtest.h" +#include +#include +using namespace wpi; +using namespace support; + +#undef max + +namespace { + +TEST(Endian, Read) { + // These are 5 bytes so we can be sure at least one of the reads is unaligned. + unsigned char bigval[] = {0x00, 0x01, 0x02, 0x03, 0x04}; + unsigned char littleval[] = {0x00, 0x04, 0x03, 0x02, 0x01}; + int32_t BigAsHost = 0x00010203; + EXPECT_EQ(BigAsHost, (endian::read(bigval))); + int32_t LittleAsHost = 0x02030400; + EXPECT_EQ(LittleAsHost,(endian::read(littleval))); + + EXPECT_EQ((endian::read(bigval + 1)), + (endian::read(littleval + 1))); +} + +TEST(Endian, ReadBitAligned) { + // Simple test to make sure we properly pull out the 0x0 word. + unsigned char littleval[] = {0x3f, 0x00, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff}; + unsigned char bigval[] = {0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xc0}; + EXPECT_EQ( + (endian::readAtBitAlignment(&littleval[0], 6)), + 0x0); + EXPECT_EQ((endian::readAtBitAlignment(&bigval[0], 6)), + 0x0); + // Test to make sure that signed right shift of 0xf0000000 is masked + // properly. + unsigned char littleval2[] = {0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00}; + unsigned char bigval2[] = {0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + EXPECT_EQ( + (endian::readAtBitAlignment(&littleval2[0], 4)), + 0x0f000000); + EXPECT_EQ((endian::readAtBitAlignment(&bigval2[0], 4)), + 0x0f000000); + // Test to make sure left shift of start bit doesn't overflow. + EXPECT_EQ( + (endian::readAtBitAlignment(&littleval2[0], 1)), + 0x78000000); + EXPECT_EQ((endian::readAtBitAlignment(&bigval2[0], 1)), + 0x78000000); + // Test to make sure 64-bit int doesn't overflow. + unsigned char littleval3[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + unsigned char bigval3[] = {0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + EXPECT_EQ((endian::readAtBitAlignment( + &littleval3[0], 4)), + 0x0f00000000000000); + EXPECT_EQ( + (endian::readAtBitAlignment(&bigval3[0], 4)), + 0x0f00000000000000); +} + +TEST(Endian, WriteBitAligned) { + // This test ensures that signed right shift of 0xffffaa is masked + // properly. + unsigned char bigval[8] = {0x00}; + endian::writeAtBitAlignment(bigval, (int)0xffffaaaa, + 4); + EXPECT_EQ(bigval[0], 0xff); + EXPECT_EQ(bigval[1], 0xfa); + EXPECT_EQ(bigval[2], 0xaa); + EXPECT_EQ(bigval[3], 0xa0); + EXPECT_EQ(bigval[4], 0x00); + EXPECT_EQ(bigval[5], 0x00); + EXPECT_EQ(bigval[6], 0x00); + EXPECT_EQ(bigval[7], 0x0f); + + unsigned char littleval[8] = {0x00}; + endian::writeAtBitAlignment(littleval, + (int)0xffffaaaa, 4); + EXPECT_EQ(littleval[0], 0xa0); + EXPECT_EQ(littleval[1], 0xaa); + EXPECT_EQ(littleval[2], 0xfa); + EXPECT_EQ(littleval[3], 0xff); + EXPECT_EQ(littleval[4], 0x0f); + EXPECT_EQ(littleval[5], 0x00); + EXPECT_EQ(littleval[6], 0x00); + EXPECT_EQ(littleval[7], 0x00); + + // This test makes sure 1<<31 doesn't overflow. + // Test to make sure left shift of start bit doesn't overflow. + unsigned char bigval2[8] = {0x00}; + endian::writeAtBitAlignment(bigval2, (int)0xffffffff, + 1); + EXPECT_EQ(bigval2[0], 0xff); + EXPECT_EQ(bigval2[1], 0xff); + EXPECT_EQ(bigval2[2], 0xff); + EXPECT_EQ(bigval2[3], 0xfe); + EXPECT_EQ(bigval2[4], 0x00); + EXPECT_EQ(bigval2[5], 0x00); + EXPECT_EQ(bigval2[6], 0x00); + EXPECT_EQ(bigval2[7], 0x01); + + unsigned char littleval2[8] = {0x00}; + endian::writeAtBitAlignment(littleval2, + (int)0xffffffff, 1); + EXPECT_EQ(littleval2[0], 0xfe); + EXPECT_EQ(littleval2[1], 0xff); + EXPECT_EQ(littleval2[2], 0xff); + EXPECT_EQ(littleval2[3], 0xff); + EXPECT_EQ(littleval2[4], 0x01); + EXPECT_EQ(littleval2[5], 0x00); + EXPECT_EQ(littleval2[6], 0x00); + EXPECT_EQ(littleval2[7], 0x00); + + // Test to make sure 64-bit int doesn't overflow. + unsigned char bigval64[16] = {0x00}; + endian::writeAtBitAlignment( + bigval64, (int64_t)0xffffffffffffffff, 1); + EXPECT_EQ(bigval64[0], 0xff); + EXPECT_EQ(bigval64[1], 0xff); + EXPECT_EQ(bigval64[2], 0xff); + EXPECT_EQ(bigval64[3], 0xff); + EXPECT_EQ(bigval64[4], 0xff); + EXPECT_EQ(bigval64[5], 0xff); + EXPECT_EQ(bigval64[6], 0xff); + EXPECT_EQ(bigval64[7], 0xfe); + EXPECT_EQ(bigval64[8], 0x00); + EXPECT_EQ(bigval64[9], 0x00); + EXPECT_EQ(bigval64[10], 0x00); + EXPECT_EQ(bigval64[11], 0x00); + EXPECT_EQ(bigval64[12], 0x00); + EXPECT_EQ(bigval64[13], 0x00); + EXPECT_EQ(bigval64[14], 0x00); + EXPECT_EQ(bigval64[15], 0x01); + + unsigned char littleval64[16] = {0x00}; + endian::writeAtBitAlignment( + littleval64, (int64_t)0xffffffffffffffff, 1); + EXPECT_EQ(littleval64[0], 0xfe); + EXPECT_EQ(littleval64[1], 0xff); + EXPECT_EQ(littleval64[2], 0xff); + EXPECT_EQ(littleval64[3], 0xff); + EXPECT_EQ(littleval64[4], 0xff); + EXPECT_EQ(littleval64[5], 0xff); + EXPECT_EQ(littleval64[6], 0xff); + EXPECT_EQ(littleval64[7], 0xff); + EXPECT_EQ(littleval64[8], 0x01); + EXPECT_EQ(littleval64[9], 0x00); + EXPECT_EQ(littleval64[10], 0x00); + EXPECT_EQ(littleval64[11], 0x00); + EXPECT_EQ(littleval64[12], 0x00); + EXPECT_EQ(littleval64[13], 0x00); + EXPECT_EQ(littleval64[14], 0x00); + EXPECT_EQ(littleval64[15], 0x00); +} + +TEST(Endian, Write) { + unsigned char data[5]; + endian::write(data, -1362446643); + EXPECT_EQ(data[0], 0xAE); + EXPECT_EQ(data[1], 0xCA); + EXPECT_EQ(data[2], 0xB6); + EXPECT_EQ(data[3], 0xCD); + endian::write(data + 1, -1362446643); + EXPECT_EQ(data[1], 0xAE); + EXPECT_EQ(data[2], 0xCA); + EXPECT_EQ(data[3], 0xB6); + EXPECT_EQ(data[4], 0xCD); + + endian::write(data, -1362446643); + EXPECT_EQ(data[0], 0xCD); + EXPECT_EQ(data[1], 0xB6); + EXPECT_EQ(data[2], 0xCA); + EXPECT_EQ(data[3], 0xAE); + endian::write(data + 1, -1362446643); + EXPECT_EQ(data[1], 0xCD); + EXPECT_EQ(data[2], 0xB6); + EXPECT_EQ(data[3], 0xCA); + EXPECT_EQ(data[4], 0xAE); +} + +TEST(Endian, PackedEndianSpecificIntegral) { + // These are 5 bytes so we can be sure at least one of the reads is unaligned. + unsigned char big[] = {0x00, 0x01, 0x02, 0x03, 0x04}; + unsigned char little[] = {0x00, 0x04, 0x03, 0x02, 0x01}; + big32_t *big_val = + reinterpret_cast(big + 1); + little32_t *little_val = + reinterpret_cast(little + 1); + + EXPECT_EQ(*big_val, *little_val); +} + +TEST(Endian, PacketEndianSpecificIntegralAsEnum) { + enum class Test : uint16_t { ONETWO = 0x0102, TWOONE = 0x0201 }; + unsigned char bytes[] = {0x01, 0x02}; + using LittleTest = little_t; + using BigTest = big_t; + EXPECT_EQ(Test::TWOONE, *reinterpret_cast(bytes)); + EXPECT_EQ(Test::ONETWO, *reinterpret_cast(bytes)); +} + +} // end anon namespace diff --git a/wpiutil/src/test/native/cpp/llvm/ErrnoTest.cpp b/wpiutil/src/test/native/cpp/llvm/ErrnoTest.cpp new file mode 100644 index 0000000000..fed8ad61c0 --- /dev/null +++ b/wpiutil/src/test/native/cpp/llvm/ErrnoTest.cpp @@ -0,0 +1,38 @@ +//===- ErrnoTest.cpp - Error handling unit tests --------------------------===// +// +// 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 "wpi/Errno.h" +#include "gtest/gtest.h" + +using namespace wpi::sys; + +TEST(ErrnoTest, RetryAfterSignal) { + EXPECT_EQ(1, RetryAfterSignal(-1, [] { return 1; })); + + EXPECT_EQ(-1, RetryAfterSignal(-1, [] { + errno = EAGAIN; + return -1; + })); + EXPECT_EQ(EAGAIN, errno); + + unsigned calls = 0; + EXPECT_EQ(1, RetryAfterSignal(-1, [&calls] { + errno = EINTR; + ++calls; + return calls == 1 ? -1 : 1; + })); + EXPECT_EQ(2u, calls); + + EXPECT_EQ(1, RetryAfterSignal(-1, [](int x) { return x; }, 1)); + + std::unique_ptr P(RetryAfterSignal(nullptr, [] { return new int(47); })); + EXPECT_EQ(47, *P); + + errno = EINTR; + EXPECT_EQ(-1, RetryAfterSignal(-1, [] { return -1; })); +} diff --git a/wpiutil/src/test/native/cpp/llvm/FunctionExtrasTest.cpp b/wpiutil/src/test/native/cpp/llvm/FunctionExtrasTest.cpp index 5dcd11fb87..9094e4e0bd 100644 --- a/wpiutil/src/test/native/cpp/llvm/FunctionExtrasTest.cpp +++ b/wpiutil/src/test/native/cpp/llvm/FunctionExtrasTest.cpp @@ -1,9 +1,8 @@ //===- FunctionExtrasTest.cpp - Unit tests for function type erasure ------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// @@ -11,6 +10,7 @@ #include "gtest/gtest.h" #include +#include using namespace wpi; @@ -225,4 +225,70 @@ TEST(UniqueFunctionTest, CountForwardingMoves) { UnmovableF(X); } +TEST(UniqueFunctionTest, Const) { + // Can assign from const lambda. + unique_function Plus2 = [X(std::make_unique(2))](int Y) { + return *X + Y; + }; + EXPECT_EQ(5, Plus2(3)); + + // Can call through a const ref. + const auto &Plus2Ref = Plus2; + EXPECT_EQ(5, Plus2Ref(3)); + + // Can move-construct and assign. + unique_function Plus2A = std::move(Plus2); + EXPECT_EQ(5, Plus2A(3)); + unique_function Plus2B; + Plus2B = std::move(Plus2A); + EXPECT_EQ(5, Plus2B(3)); + + // Can convert to non-const function type, but not back. + unique_function Plus2C = std::move(Plus2B); + EXPECT_EQ(5, Plus2C(3)); + + // Overloaded call operator correctly resolved. + struct ChooseCorrectOverload { + std::string_view operator()() { return "non-const"; } + std::string_view operator()() const { return "const"; } + }; + unique_function ChooseMutable = ChooseCorrectOverload(); + ChooseCorrectOverload A; + EXPECT_EQ("non-const", ChooseMutable()); + EXPECT_EQ("non-const", A()); + unique_function ChooseConst = ChooseCorrectOverload(); + const ChooseCorrectOverload &X = A; + EXPECT_EQ("const", ChooseConst()); + EXPECT_EQ("const", X()); +} + +// Test that overloads on unique_functions are resolved as expected. +std::string returns(std::string_view) { return "not a function"; } +std::string returns(unique_function F) { return "number"; } +std::string returns(unique_function F) { return "string"; } + +TEST(UniqueFunctionTest, SFINAE) { + EXPECT_EQ("not a function", returns("boo!")); + EXPECT_EQ("number", returns([] { return 42; })); + EXPECT_EQ("string", returns([] { return "hello"; })); +} + +// A forward declared type, and a templated type. +class Incomplete; +template class Templated { T A; }; + +// Check that we can define unique_function that have references to +// incomplete types, even if those types are templated over an +// incomplete type. +TEST(UniqueFunctionTest, IncompleteTypes) { + unique_function &&)> + IncompleteArgumentRValueReference; + unique_function &)> + IncompleteArgumentLValueReference; + unique_function *)> IncompleteArgumentPointer; + unique_function &()> IncompleteResultLValueReference; + unique_function && ()> IncompleteResultRValueReference2; + unique_function *()> IncompleteResultPointer; +} + } // anonymous namespace diff --git a/wpiutil/src/test/native/cpp/llvm/MapVectorTest.cpp b/wpiutil/src/test/native/cpp/llvm/MapVectorTest.cpp new file mode 100644 index 0000000000..99bda8ad27 --- /dev/null +++ b/wpiutil/src/test/native/cpp/llvm/MapVectorTest.cpp @@ -0,0 +1,430 @@ +//===- unittest/ADT/MapVectorTest.cpp - MapVector unit tests ----*- 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 +// +//===----------------------------------------------------------------------===// + +#if defined(__GNUC__) +#pragma GCC diagnostic ignored "-Wpedantic" +#if !defined(__clang__) +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif +#endif + +#include "wpi/MapVector.h" +#include "wpi/iterator_range.h" +#include "gtest/gtest.h" +#include + +using namespace wpi; + +TEST(MapVectorTest, swap) { + MapVector MV1, MV2; + std::pair::iterator, bool> R; + + R = MV1.insert(std::make_pair(1, 2)); + ASSERT_EQ(R.first, MV1.begin()); + EXPECT_EQ(R.first->first, 1); + EXPECT_EQ(R.first->second, 2); + EXPECT_TRUE(R.second); + + EXPECT_FALSE(MV1.empty()); + EXPECT_TRUE(MV2.empty()); + MV2.swap(MV1); + EXPECT_TRUE(MV1.empty()); + EXPECT_FALSE(MV2.empty()); + + auto I = MV1.find(1); + ASSERT_EQ(MV1.end(), I); + + I = MV2.find(1); + ASSERT_EQ(I, MV2.begin()); + EXPECT_EQ(I->first, 1); + EXPECT_EQ(I->second, 2); +} + +TEST(MapVectorTest, insert_pop) { + MapVector MV; + std::pair::iterator, bool> R; + + R = MV.insert(std::make_pair(1, 2)); + ASSERT_EQ(R.first, MV.begin()); + EXPECT_EQ(R.first->first, 1); + EXPECT_EQ(R.first->second, 2); + EXPECT_TRUE(R.second); + + R = MV.insert(std::make_pair(1, 3)); + ASSERT_EQ(R.first, MV.begin()); + EXPECT_EQ(R.first->first, 1); + EXPECT_EQ(R.first->second, 2); + EXPECT_FALSE(R.second); + + R = MV.insert(std::make_pair(4, 5)); + ASSERT_NE(R.first, MV.end()); + EXPECT_EQ(R.first->first, 4); + EXPECT_EQ(R.first->second, 5); + EXPECT_TRUE(R.second); + + EXPECT_EQ(MV.size(), 2u); + EXPECT_EQ(MV[1], 2); + EXPECT_EQ(MV[4], 5); + + MV.pop_back(); + EXPECT_EQ(MV.size(), 1u); + EXPECT_EQ(MV[1], 2); + + R = MV.insert(std::make_pair(4, 7)); + ASSERT_NE(R.first, MV.end()); + EXPECT_EQ(R.first->first, 4); + EXPECT_EQ(R.first->second, 7); + EXPECT_TRUE(R.second); + + EXPECT_EQ(MV.size(), 2u); + EXPECT_EQ(MV[1], 2); + EXPECT_EQ(MV[4], 7); +} + +TEST(MapVectorTest, erase) { + MapVector MV; + + MV.insert(std::make_pair(1, 2)); + MV.insert(std::make_pair(3, 4)); + MV.insert(std::make_pair(5, 6)); + ASSERT_EQ(MV.size(), 3u); + + MV.erase(MV.find(1)); + ASSERT_EQ(MV.size(), 2u); + ASSERT_EQ(MV.find(1), MV.end()); + ASSERT_EQ(MV[3], 4); + ASSERT_EQ(MV[5], 6); + + ASSERT_EQ(MV.erase(3), 1u); + ASSERT_EQ(MV.size(), 1u); + ASSERT_EQ(MV.find(3), MV.end()); + ASSERT_EQ(MV[5], 6); + + ASSERT_EQ(MV.erase(79), 0u); + ASSERT_EQ(MV.size(), 1u); +} + +TEST(MapVectorTest, remove_if) { + MapVector MV; + + MV.insert(std::make_pair(1, 11)); + MV.insert(std::make_pair(2, 12)); + MV.insert(std::make_pair(3, 13)); + MV.insert(std::make_pair(4, 14)); + MV.insert(std::make_pair(5, 15)); + MV.insert(std::make_pair(6, 16)); + ASSERT_EQ(MV.size(), 6u); + + MV.remove_if([](const std::pair &Val) { return Val.second % 2; }); + ASSERT_EQ(MV.size(), 3u); + ASSERT_EQ(MV.find(1), MV.end()); + ASSERT_EQ(MV.find(3), MV.end()); + ASSERT_EQ(MV.find(5), MV.end()); + ASSERT_EQ(MV[2], 12); + ASSERT_EQ(MV[4], 14); + ASSERT_EQ(MV[6], 16); +} + +TEST(MapVectorTest, iteration_test) { + MapVector MV; + + MV.insert(std::make_pair(1, 11)); + MV.insert(std::make_pair(2, 12)); + MV.insert(std::make_pair(3, 13)); + MV.insert(std::make_pair(4, 14)); + MV.insert(std::make_pair(5, 15)); + MV.insert(std::make_pair(6, 16)); + ASSERT_EQ(MV.size(), 6u); + + int count = 1; + for (auto P : make_range(MV.begin(), MV.end())) { + ASSERT_EQ(P.first, count); + count++; + } + + count = 6; + for (auto P : make_range(MV.rbegin(), MV.rend())) { + ASSERT_EQ(P.first, count); + count--; + } +} + +TEST(MapVectorTest, NonCopyable) { + MapVector> MV; + MV.insert(std::make_pair(1, std::make_unique(1))); + MV.insert(std::make_pair(2, std::make_unique(2))); + + ASSERT_EQ(MV.count(1), 1u); + ASSERT_EQ(*MV.find(2)->second, 2); +} + +template struct MapVectorMappedTypeTest : ::testing::Test { + using int_type = IntType; +}; + +using MapIntTypes = ::testing::Types; +TYPED_TEST_SUITE(MapVectorMappedTypeTest, MapIntTypes, ); + +TYPED_TEST(MapVectorMappedTypeTest, DifferentDenseMap) { + // Test that using a map with a mapped type other than 'unsigned' compiles + // and works. + using IntType = typename TestFixture::int_type; + using MapVectorType = MapVector>; + + MapVectorType MV; + std::pair R; + + R = MV.insert(std::make_pair(1, 2)); + ASSERT_EQ(R.first, MV.begin()); + EXPECT_EQ(R.first->first, 1); + EXPECT_EQ(R.first->second, 2); + EXPECT_TRUE(R.second); + + const std::pair Elem(1, 3); + R = MV.insert(Elem); + ASSERT_EQ(R.first, MV.begin()); + EXPECT_EQ(R.first->first, 1); + EXPECT_EQ(R.first->second, 2); + EXPECT_FALSE(R.second); + + int& value = MV[4]; + EXPECT_EQ(value, 0); + value = 5; + + EXPECT_EQ(MV.size(), 2u); + EXPECT_EQ(MV[1], 2); + EXPECT_EQ(MV[4], 5); +} + +TEST(SmallMapVectorSmallTest, insert_pop) { + SmallMapVector MV; + std::pair::iterator, bool> R; + + R = MV.insert(std::make_pair(1, 2)); + ASSERT_EQ(R.first, MV.begin()); + EXPECT_EQ(R.first->first, 1); + EXPECT_EQ(R.first->second, 2); + EXPECT_TRUE(R.second); + + R = MV.insert(std::make_pair(1, 3)); + ASSERT_EQ(R.first, MV.begin()); + EXPECT_EQ(R.first->first, 1); + EXPECT_EQ(R.first->second, 2); + EXPECT_FALSE(R.second); + + R = MV.insert(std::make_pair(4, 5)); + ASSERT_NE(R.first, MV.end()); + EXPECT_EQ(R.first->first, 4); + EXPECT_EQ(R.first->second, 5); + EXPECT_TRUE(R.second); + + EXPECT_EQ(MV.size(), 2u); + EXPECT_EQ(MV[1], 2); + EXPECT_EQ(MV[4], 5); + + MV.pop_back(); + EXPECT_EQ(MV.size(), 1u); + EXPECT_EQ(MV[1], 2); + + R = MV.insert(std::make_pair(4, 7)); + ASSERT_NE(R.first, MV.end()); + EXPECT_EQ(R.first->first, 4); + EXPECT_EQ(R.first->second, 7); + EXPECT_TRUE(R.second); + + EXPECT_EQ(MV.size(), 2u); + EXPECT_EQ(MV[1], 2); + EXPECT_EQ(MV[4], 7); +} + +TEST(SmallMapVectorSmallTest, erase) { + SmallMapVector MV; + + MV.insert(std::make_pair(1, 2)); + MV.insert(std::make_pair(3, 4)); + MV.insert(std::make_pair(5, 6)); + ASSERT_EQ(MV.size(), 3u); + + MV.erase(MV.find(1)); + ASSERT_EQ(MV.size(), 2u); + ASSERT_EQ(MV.find(1), MV.end()); + ASSERT_EQ(MV[3], 4); + ASSERT_EQ(MV[5], 6); + + ASSERT_EQ(MV.erase(3), 1u); + ASSERT_EQ(MV.size(), 1u); + ASSERT_EQ(MV.find(3), MV.end()); + ASSERT_EQ(MV[5], 6); + + ASSERT_EQ(MV.erase(79), 0u); + ASSERT_EQ(MV.size(), 1u); +} + +TEST(SmallMapVectorSmallTest, remove_if) { + SmallMapVector MV; + + MV.insert(std::make_pair(1, 11)); + MV.insert(std::make_pair(2, 12)); + MV.insert(std::make_pair(3, 13)); + MV.insert(std::make_pair(4, 14)); + MV.insert(std::make_pair(5, 15)); + MV.insert(std::make_pair(6, 16)); + ASSERT_EQ(MV.size(), 6u); + + MV.remove_if([](const std::pair &Val) { return Val.second % 2; }); + ASSERT_EQ(MV.size(), 3u); + ASSERT_EQ(MV.find(1), MV.end()); + ASSERT_EQ(MV.find(3), MV.end()); + ASSERT_EQ(MV.find(5), MV.end()); + ASSERT_EQ(MV[2], 12); + ASSERT_EQ(MV[4], 14); + ASSERT_EQ(MV[6], 16); +} + +TEST(SmallMapVectorSmallTest, iteration_test) { + SmallMapVector MV; + + MV.insert(std::make_pair(1, 11)); + MV.insert(std::make_pair(2, 12)); + MV.insert(std::make_pair(3, 13)); + MV.insert(std::make_pair(4, 14)); + MV.insert(std::make_pair(5, 15)); + MV.insert(std::make_pair(6, 16)); + ASSERT_EQ(MV.size(), 6u); + + int count = 1; + for (auto P : make_range(MV.begin(), MV.end())) { + ASSERT_EQ(P.first, count); + count++; + } + + count = 6; + for (auto P : make_range(MV.rbegin(), MV.rend())) { + ASSERT_EQ(P.first, count); + count--; + } +} + +TEST(SmallMapVectorSmallTest, NonCopyable) { + SmallMapVector, 8> MV; + MV.insert(std::make_pair(1, std::make_unique(1))); + MV.insert(std::make_pair(2, std::make_unique(2))); + + ASSERT_EQ(MV.count(1), 1u); + ASSERT_EQ(*MV.find(2)->second, 2); +} + +TEST(SmallMapVectorLargeTest, insert_pop) { + SmallMapVector MV; + std::pair::iterator, bool> R; + + R = MV.insert(std::make_pair(1, 2)); + ASSERT_EQ(R.first, MV.begin()); + EXPECT_EQ(R.first->first, 1); + EXPECT_EQ(R.first->second, 2); + EXPECT_TRUE(R.second); + + R = MV.insert(std::make_pair(1, 3)); + ASSERT_EQ(R.first, MV.begin()); + EXPECT_EQ(R.first->first, 1); + EXPECT_EQ(R.first->second, 2); + EXPECT_FALSE(R.second); + + R = MV.insert(std::make_pair(4, 5)); + ASSERT_NE(R.first, MV.end()); + EXPECT_EQ(R.first->first, 4); + EXPECT_EQ(R.first->second, 5); + EXPECT_TRUE(R.second); + + EXPECT_EQ(MV.size(), 2u); + EXPECT_EQ(MV[1], 2); + EXPECT_EQ(MV[4], 5); + + MV.pop_back(); + EXPECT_EQ(MV.size(), 1u); + EXPECT_EQ(MV[1], 2); + + R = MV.insert(std::make_pair(4, 7)); + ASSERT_NE(R.first, MV.end()); + EXPECT_EQ(R.first->first, 4); + EXPECT_EQ(R.first->second, 7); + EXPECT_TRUE(R.second); + + EXPECT_EQ(MV.size(), 2u); + EXPECT_EQ(MV[1], 2); + EXPECT_EQ(MV[4], 7); +} + +TEST(SmallMapVectorLargeTest, erase) { + SmallMapVector MV; + + MV.insert(std::make_pair(1, 2)); + MV.insert(std::make_pair(3, 4)); + MV.insert(std::make_pair(5, 6)); + ASSERT_EQ(MV.size(), 3u); + + MV.erase(MV.find(1)); + ASSERT_EQ(MV.size(), 2u); + ASSERT_EQ(MV.find(1), MV.end()); + ASSERT_EQ(MV[3], 4); + ASSERT_EQ(MV[5], 6); + + ASSERT_EQ(MV.erase(3), 1u); + ASSERT_EQ(MV.size(), 1u); + ASSERT_EQ(MV.find(3), MV.end()); + ASSERT_EQ(MV[5], 6); + + ASSERT_EQ(MV.erase(79), 0u); + ASSERT_EQ(MV.size(), 1u); +} + +TEST(SmallMapVectorLargeTest, remove_if) { + SmallMapVector MV; + + MV.insert(std::make_pair(1, 11)); + MV.insert(std::make_pair(2, 12)); + MV.insert(std::make_pair(3, 13)); + MV.insert(std::make_pair(4, 14)); + MV.insert(std::make_pair(5, 15)); + MV.insert(std::make_pair(6, 16)); + ASSERT_EQ(MV.size(), 6u); + + MV.remove_if([](const std::pair &Val) { return Val.second % 2; }); + ASSERT_EQ(MV.size(), 3u); + ASSERT_EQ(MV.find(1), MV.end()); + ASSERT_EQ(MV.find(3), MV.end()); + ASSERT_EQ(MV.find(5), MV.end()); + ASSERT_EQ(MV[2], 12); + ASSERT_EQ(MV[4], 14); + ASSERT_EQ(MV[6], 16); +} + +TEST(SmallMapVectorLargeTest, iteration_test) { + SmallMapVector MV; + + MV.insert(std::make_pair(1, 11)); + MV.insert(std::make_pair(2, 12)); + MV.insert(std::make_pair(3, 13)); + MV.insert(std::make_pair(4, 14)); + MV.insert(std::make_pair(5, 15)); + MV.insert(std::make_pair(6, 16)); + ASSERT_EQ(MV.size(), 6u); + + int count = 1; + for (auto P : make_range(MV.begin(), MV.end())) { + ASSERT_EQ(P.first, count); + count++; + } + + count = 6; + for (auto P : make_range(MV.rbegin(), MV.rend())) { + ASSERT_EQ(P.first, count); + count--; + } +} diff --git a/wpiutil/src/test/native/cpp/llvm/MathExtrasTest.cpp b/wpiutil/src/test/native/cpp/llvm/MathExtrasTest.cpp new file mode 100644 index 0000000000..f392887386 --- /dev/null +++ b/wpiutil/src/test/native/cpp/llvm/MathExtrasTest.cpp @@ -0,0 +1,607 @@ +//===- unittests/Support/MathExtrasTest.cpp - math utils tests ------------===// +// +// 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 "wpi/MathExtras.h" +#include "gtest/gtest.h" + +using namespace wpi; + +namespace { + +TEST(MathExtras, countTrailingZeros) { + uint8_t Z8 = 0; + uint16_t Z16 = 0; + uint32_t Z32 = 0; + uint64_t Z64 = 0; + EXPECT_EQ(8u, countTrailingZeros(Z8)); + EXPECT_EQ(16u, countTrailingZeros(Z16)); + EXPECT_EQ(32u, countTrailingZeros(Z32)); + EXPECT_EQ(64u, countTrailingZeros(Z64)); + + uint8_t NZ8 = 42; + uint16_t NZ16 = 42; + uint32_t NZ32 = 42; + uint64_t NZ64 = 42; + EXPECT_EQ(1u, countTrailingZeros(NZ8)); + EXPECT_EQ(1u, countTrailingZeros(NZ16)); + EXPECT_EQ(1u, countTrailingZeros(NZ32)); + EXPECT_EQ(1u, countTrailingZeros(NZ64)); +} + +TEST(MathExtras, countLeadingZeros) { + uint8_t Z8 = 0; + uint16_t Z16 = 0; + uint32_t Z32 = 0; + uint64_t Z64 = 0; + EXPECT_EQ(8u, countLeadingZeros(Z8)); + EXPECT_EQ(16u, countLeadingZeros(Z16)); + EXPECT_EQ(32u, countLeadingZeros(Z32)); + EXPECT_EQ(64u, countLeadingZeros(Z64)); + + uint8_t NZ8 = 42; + uint16_t NZ16 = 42; + uint32_t NZ32 = 42; + uint64_t NZ64 = 42; + EXPECT_EQ(2u, countLeadingZeros(NZ8)); + EXPECT_EQ(10u, countLeadingZeros(NZ16)); + EXPECT_EQ(26u, countLeadingZeros(NZ32)); + EXPECT_EQ(58u, countLeadingZeros(NZ64)); + + EXPECT_EQ(8u, countLeadingZeros(0x00F000FFu)); + EXPECT_EQ(8u, countLeadingZeros(0x00F12345u)); + for (unsigned i = 0; i <= 30; ++i) { + EXPECT_EQ(31 - i, countLeadingZeros(1u << i)); + } + + EXPECT_EQ(8u, countLeadingZeros(0x00F1234500F12345ULL)); + EXPECT_EQ(1u, countLeadingZeros(1ULL << 62)); + for (unsigned i = 0; i <= 62; ++i) { + EXPECT_EQ(63 - i, countLeadingZeros(1ULL << i)); + } +} + +TEST(MathExtras, onesMask) { + EXPECT_EQ(0U, maskLeadingOnes(0)); + EXPECT_EQ(0U, maskTrailingOnes(0)); + EXPECT_EQ(0U, maskLeadingOnes(0)); + EXPECT_EQ(0U, maskTrailingOnes(0)); + EXPECT_EQ(0U, maskLeadingOnes(0)); + EXPECT_EQ(0U, maskTrailingOnes(0)); + EXPECT_EQ(0U, maskLeadingOnes(0)); + EXPECT_EQ(0U, maskTrailingOnes(0)); + + EXPECT_EQ(0x00000003U, maskTrailingOnes(2U)); + EXPECT_EQ(0xC0000000U, maskLeadingOnes(2U)); + + EXPECT_EQ(0x000007FFU, maskTrailingOnes(11U)); + EXPECT_EQ(0xFFE00000U, maskLeadingOnes(11U)); + + EXPECT_EQ(0xFFFFFFFFU, maskTrailingOnes(32U)); + EXPECT_EQ(0xFFFFFFFFU, maskLeadingOnes(32U)); + EXPECT_EQ(0xFFFFFFFFFFFFFFFFULL, maskTrailingOnes(64U)); + EXPECT_EQ(0xFFFFFFFFFFFFFFFFULL, maskLeadingOnes(64U)); + + EXPECT_EQ(0x0000FFFFFFFFFFFFULL, maskTrailingOnes(48U)); + EXPECT_EQ(0xFFFFFFFFFFFF0000ULL, maskLeadingOnes(48U)); +} + +TEST(MathExtras, findFirstSet) { + uint8_t Z8 = 0; + uint16_t Z16 = 0; + uint32_t Z32 = 0; + uint64_t Z64 = 0; + EXPECT_EQ(0xFFULL, findFirstSet(Z8)); + EXPECT_EQ(0xFFFFULL, findFirstSet(Z16)); + EXPECT_EQ(0xFFFFFFFFULL, findFirstSet(Z32)); + EXPECT_EQ(0xFFFFFFFFFFFFFFFFULL, findFirstSet(Z64)); + + uint8_t NZ8 = 42; + uint16_t NZ16 = 42; + uint32_t NZ32 = 42; + uint64_t NZ64 = 42; + EXPECT_EQ(1u, findFirstSet(NZ8)); + EXPECT_EQ(1u, findFirstSet(NZ16)); + EXPECT_EQ(1u, findFirstSet(NZ32)); + EXPECT_EQ(1u, findFirstSet(NZ64)); +} + +TEST(MathExtras, findLastSet) { + uint8_t Z8 = 0; + uint16_t Z16 = 0; + uint32_t Z32 = 0; + uint64_t Z64 = 0; + EXPECT_EQ(0xFFULL, findLastSet(Z8)); + EXPECT_EQ(0xFFFFULL, findLastSet(Z16)); + EXPECT_EQ(0xFFFFFFFFULL, findLastSet(Z32)); + EXPECT_EQ(0xFFFFFFFFFFFFFFFFULL, findLastSet(Z64)); + + uint8_t NZ8 = 42; + uint16_t NZ16 = 42; + uint32_t NZ32 = 42; + uint64_t NZ64 = 42; + EXPECT_EQ(5u, findLastSet(NZ8)); + EXPECT_EQ(5u, findLastSet(NZ16)); + EXPECT_EQ(5u, findLastSet(NZ32)); + EXPECT_EQ(5u, findLastSet(NZ64)); +} + +TEST(MathExtras, isIntN) { + EXPECT_TRUE(isIntN(16, 32767)); + EXPECT_FALSE(isIntN(16, 32768)); +} + +TEST(MathExtras, isUIntN) { + EXPECT_TRUE(isUIntN(16, 65535)); + EXPECT_FALSE(isUIntN(16, 65536)); + EXPECT_TRUE(isUIntN(1, 0)); + EXPECT_TRUE(isUIntN(6, 63)); +} + +TEST(MathExtras, maxIntN) { + EXPECT_EQ(32767, maxIntN(16)); + EXPECT_EQ(2147483647, maxIntN(32)); + EXPECT_EQ(std::numeric_limits::max(), maxIntN(32)); + EXPECT_EQ(std::numeric_limits::max(), maxIntN(64)); +} + +TEST(MathExtras, minIntN) { + EXPECT_EQ(-32768LL, minIntN(16)); + EXPECT_EQ(-64LL, minIntN(7)); + EXPECT_EQ(std::numeric_limits::min(), minIntN(32)); + EXPECT_EQ(std::numeric_limits::min(), minIntN(64)); +} + +TEST(MathExtras, maxUIntN) { + EXPECT_EQ(0xffffULL, maxUIntN(16)); + EXPECT_EQ(0xffffffffULL, maxUIntN(32)); + EXPECT_EQ(0xffffffffffffffffULL, maxUIntN(64)); + EXPECT_EQ(1ULL, maxUIntN(1)); + EXPECT_EQ(0x0fULL, maxUIntN(4)); +} + +TEST(MathExtras, reverseBits) { + uint8_t NZ8 = 42; + uint16_t NZ16 = 42; + uint32_t NZ32 = 42; + uint64_t NZ64 = 42; + EXPECT_EQ(0x54ULL, reverseBits(NZ8)); + EXPECT_EQ(0x5400ULL, reverseBits(NZ16)); + EXPECT_EQ(0x54000000ULL, reverseBits(NZ32)); + EXPECT_EQ(0x5400000000000000ULL, reverseBits(NZ64)); +} + +TEST(MathExtras, isPowerOf2_32) { + EXPECT_FALSE(isPowerOf2_32(0)); + EXPECT_TRUE(isPowerOf2_32(1 << 6)); + EXPECT_TRUE(isPowerOf2_32(1 << 12)); + EXPECT_FALSE(isPowerOf2_32((1 << 19) + 3)); + EXPECT_FALSE(isPowerOf2_32(0xABCDEF0)); +} + +TEST(MathExtras, isPowerOf2_64) { + EXPECT_FALSE(isPowerOf2_64(0)); + EXPECT_TRUE(isPowerOf2_64(1LL << 46)); + EXPECT_TRUE(isPowerOf2_64(1LL << 12)); + EXPECT_FALSE(isPowerOf2_64((1LL << 53) + 3)); + EXPECT_FALSE(isPowerOf2_64(0xABCDEF0ABCDEF0LL)); +} + +TEST(MathExtras, PowerOf2Ceil) { + EXPECT_EQ(0U, PowerOf2Ceil(0U)); + EXPECT_EQ(8U, PowerOf2Ceil(8U)); + EXPECT_EQ(8U, PowerOf2Ceil(7U)); +} + +TEST(MathExtras, PowerOf2Floor) { + EXPECT_EQ(0U, PowerOf2Floor(0U)); + EXPECT_EQ(8U, PowerOf2Floor(8U)); + EXPECT_EQ(4U, PowerOf2Floor(7U)); +} + +TEST(MathExtras, CTLog2) { + EXPECT_EQ(CTLog2<1ULL << 0>(), 0U); + EXPECT_EQ(CTLog2<1ULL << 1>(), 1U); + EXPECT_EQ(CTLog2<1ULL << 2>(), 2U); + EXPECT_EQ(CTLog2<1ULL << 3>(), 3U); + EXPECT_EQ(CTLog2<1ULL << 4>(), 4U); + EXPECT_EQ(CTLog2<1ULL << 5>(), 5U); + EXPECT_EQ(CTLog2<1ULL << 6>(), 6U); + EXPECT_EQ(CTLog2<1ULL << 7>(), 7U); + EXPECT_EQ(CTLog2<1ULL << 8>(), 8U); + EXPECT_EQ(CTLog2<1ULL << 9>(), 9U); + EXPECT_EQ(CTLog2<1ULL << 10>(), 10U); + EXPECT_EQ(CTLog2<1ULL << 11>(), 11U); + EXPECT_EQ(CTLog2<1ULL << 12>(), 12U); + EXPECT_EQ(CTLog2<1ULL << 13>(), 13U); + EXPECT_EQ(CTLog2<1ULL << 14>(), 14U); + EXPECT_EQ(CTLog2<1ULL << 15>(), 15U); +} + +TEST(MathExtras, countLeadingOnes) { + for (int i = 30; i >= 0; --i) { + // Start with all ones and unset some bit. + EXPECT_EQ(31u - i, countLeadingOnes(0xFFFFFFFF ^ (1 << i))); + } + for (int i = 62; i >= 0; --i) { + // Start with all ones and unset some bit. + EXPECT_EQ(63u - i, countLeadingOnes(0xFFFFFFFFFFFFFFFFULL ^ (1LL << i))); + } + for (int i = 30; i >= 0; --i) { + // Start with all ones and unset some bit. + EXPECT_EQ(31u - i, countLeadingOnes(0xFFFFFFFF ^ (1 << i))); + } +} + +TEST(MathExtras, FloatBits) { + static const float kValue = 5632.34f; + EXPECT_FLOAT_EQ(kValue, BitsToFloat(FloatToBits(kValue))); +} + +TEST(MathExtras, DoubleBits) { + static const double kValue = 87987234.983498; + EXPECT_DOUBLE_EQ(kValue, BitsToDouble(DoubleToBits(kValue))); +} + +TEST(MathExtras, MinAlign) { + EXPECT_EQ(1u, MinAlign(2, 3)); + EXPECT_EQ(2u, MinAlign(2, 4)); + EXPECT_EQ(1u, MinAlign(17, 64)); + EXPECT_EQ(256u, MinAlign(256, 512)); +} + +TEST(MathExtras, NextPowerOf2) { + EXPECT_EQ(4u, NextPowerOf2(3)); + EXPECT_EQ(16u, NextPowerOf2(15)); + EXPECT_EQ(256u, NextPowerOf2(128)); +} + +TEST(MathExtras, alignTo) { + EXPECT_EQ(8u, alignTo(5, 8)); + EXPECT_EQ(24u, alignTo(17, 8)); + EXPECT_EQ(0u, alignTo(~0LL, 8)); + + EXPECT_EQ(7u, alignTo(5, 8, 7)); + EXPECT_EQ(17u, alignTo(17, 8, 1)); + EXPECT_EQ(3u, alignTo(~0LL, 8, 3)); + EXPECT_EQ(552u, alignTo(321, 255, 42)); +} + +template +void SaturatingAddTestHelper() +{ + const T Max = std::numeric_limits::max(); + bool ResultOverflowed; + + EXPECT_EQ(T(3), SaturatingAdd(T(1), T(2))); + EXPECT_EQ(T(3), SaturatingAdd(T(1), T(2), &ResultOverflowed)); + EXPECT_FALSE(ResultOverflowed); + + EXPECT_EQ(Max, SaturatingAdd(Max, T(1))); + EXPECT_EQ(Max, SaturatingAdd(Max, T(1), &ResultOverflowed)); + EXPECT_TRUE(ResultOverflowed); + + EXPECT_EQ(Max, SaturatingAdd(T(1), T(Max - 1))); + EXPECT_EQ(Max, SaturatingAdd(T(1), T(Max - 1), &ResultOverflowed)); + EXPECT_FALSE(ResultOverflowed); + + EXPECT_EQ(Max, SaturatingAdd(T(1), Max)); + EXPECT_EQ(Max, SaturatingAdd(T(1), Max, &ResultOverflowed)); + EXPECT_TRUE(ResultOverflowed); + + EXPECT_EQ(Max, SaturatingAdd(Max, Max)); + EXPECT_EQ(Max, SaturatingAdd(Max, Max, &ResultOverflowed)); + EXPECT_TRUE(ResultOverflowed); +} + +TEST(MathExtras, SaturatingAdd) { + SaturatingAddTestHelper(); + SaturatingAddTestHelper(); + SaturatingAddTestHelper(); + SaturatingAddTestHelper(); +} + +template +void SaturatingMultiplyTestHelper() +{ + const T Max = std::numeric_limits::max(); + bool ResultOverflowed; + + // Test basic multiplication. + EXPECT_EQ(T(6), SaturatingMultiply(T(2), T(3))); + EXPECT_EQ(T(6), SaturatingMultiply(T(2), T(3), &ResultOverflowed)); + EXPECT_FALSE(ResultOverflowed); + + EXPECT_EQ(T(6), SaturatingMultiply(T(3), T(2))); + EXPECT_EQ(T(6), SaturatingMultiply(T(3), T(2), &ResultOverflowed)); + EXPECT_FALSE(ResultOverflowed); + + // Test multiplication by zero. + EXPECT_EQ(T(0), SaturatingMultiply(T(0), T(0))); + EXPECT_EQ(T(0), SaturatingMultiply(T(0), T(0), &ResultOverflowed)); + EXPECT_FALSE(ResultOverflowed); + + EXPECT_EQ(T(0), SaturatingMultiply(T(1), T(0))); + EXPECT_EQ(T(0), SaturatingMultiply(T(1), T(0), &ResultOverflowed)); + EXPECT_FALSE(ResultOverflowed); + + EXPECT_EQ(T(0), SaturatingMultiply(T(0), T(1))); + EXPECT_EQ(T(0), SaturatingMultiply(T(0), T(1), &ResultOverflowed)); + EXPECT_FALSE(ResultOverflowed); + + EXPECT_EQ(T(0), SaturatingMultiply(Max, T(0))); + EXPECT_EQ(T(0), SaturatingMultiply(Max, T(0), &ResultOverflowed)); + EXPECT_FALSE(ResultOverflowed); + + EXPECT_EQ(T(0), SaturatingMultiply(T(0), Max)); + EXPECT_EQ(T(0), SaturatingMultiply(T(0), Max, &ResultOverflowed)); + EXPECT_FALSE(ResultOverflowed); + + // Test multiplication by maximum value. + EXPECT_EQ(Max, SaturatingMultiply(Max, T(2))); + EXPECT_EQ(Max, SaturatingMultiply(Max, T(2), &ResultOverflowed)); + EXPECT_TRUE(ResultOverflowed); + + EXPECT_EQ(Max, SaturatingMultiply(T(2), Max)); + EXPECT_EQ(Max, SaturatingMultiply(T(2), Max, &ResultOverflowed)); + EXPECT_TRUE(ResultOverflowed); + + EXPECT_EQ(Max, SaturatingMultiply(Max, Max)); + EXPECT_EQ(Max, SaturatingMultiply(Max, Max, &ResultOverflowed)); + EXPECT_TRUE(ResultOverflowed); + + // Test interesting boundary conditions for algorithm - + // ((1 << A) - 1) * ((1 << B) + K) for K in [-1, 0, 1] + // and A + B == std::numeric_limits::digits. + // We expect overflow iff A > B and K = 1. + const int Digits = std::numeric_limits::digits; + for (int A = 1, B = Digits - 1; B >= 1; ++A, --B) { + for (int K = -1; K <= 1; ++K) { + T X = (T(1) << A) - T(1); + T Y = (T(1) << B) + K; + bool OverflowExpected = A > B && K == 1; + + if(OverflowExpected) { + EXPECT_EQ(Max, SaturatingMultiply(X, Y)); + EXPECT_EQ(Max, SaturatingMultiply(X, Y, &ResultOverflowed)); + EXPECT_TRUE(ResultOverflowed); + } else { + EXPECT_EQ(X * Y, SaturatingMultiply(X, Y)); + EXPECT_EQ(X * Y, SaturatingMultiply(X, Y, &ResultOverflowed)); + EXPECT_FALSE(ResultOverflowed); + } + } + } +} + +TEST(MathExtras, SaturatingMultiply) { + SaturatingMultiplyTestHelper(); + SaturatingMultiplyTestHelper(); + SaturatingMultiplyTestHelper(); + SaturatingMultiplyTestHelper(); +} + +template +void SaturatingMultiplyAddTestHelper() +{ + const T Max = std::numeric_limits::max(); + bool ResultOverflowed; + + // Test basic multiply-add. + EXPECT_EQ(T(16), SaturatingMultiplyAdd(T(2), T(3), T(10))); + EXPECT_EQ(T(16), SaturatingMultiplyAdd(T(2), T(3), T(10), &ResultOverflowed)); + EXPECT_FALSE(ResultOverflowed); + + // Test multiply overflows, add doesn't overflow + EXPECT_EQ(Max, SaturatingMultiplyAdd(Max, Max, T(0), &ResultOverflowed)); + EXPECT_TRUE(ResultOverflowed); + + // Test multiply doesn't overflow, add overflows + EXPECT_EQ(Max, SaturatingMultiplyAdd(T(1), T(1), Max, &ResultOverflowed)); + EXPECT_TRUE(ResultOverflowed); + + // Test multiply-add with Max as operand + EXPECT_EQ(Max, SaturatingMultiplyAdd(T(1), T(1), Max, &ResultOverflowed)); + EXPECT_TRUE(ResultOverflowed); + + EXPECT_EQ(Max, SaturatingMultiplyAdd(T(1), Max, T(1), &ResultOverflowed)); + EXPECT_TRUE(ResultOverflowed); + + EXPECT_EQ(Max, SaturatingMultiplyAdd(Max, Max, T(1), &ResultOverflowed)); + EXPECT_TRUE(ResultOverflowed); + + EXPECT_EQ(Max, SaturatingMultiplyAdd(Max, Max, Max, &ResultOverflowed)); + EXPECT_TRUE(ResultOverflowed); + + // Test multiply-add with 0 as operand + EXPECT_EQ(T(1), SaturatingMultiplyAdd(T(1), T(1), T(0), &ResultOverflowed)); + EXPECT_FALSE(ResultOverflowed); + + EXPECT_EQ(T(1), SaturatingMultiplyAdd(T(1), T(0), T(1), &ResultOverflowed)); + EXPECT_FALSE(ResultOverflowed); + + EXPECT_EQ(T(1), SaturatingMultiplyAdd(T(0), T(0), T(1), &ResultOverflowed)); + EXPECT_FALSE(ResultOverflowed); + + EXPECT_EQ(T(0), SaturatingMultiplyAdd(T(0), T(0), T(0), &ResultOverflowed)); + EXPECT_FALSE(ResultOverflowed); + +} + +TEST(MathExtras, SaturatingMultiplyAdd) { + SaturatingMultiplyAddTestHelper(); + SaturatingMultiplyAddTestHelper(); + SaturatingMultiplyAddTestHelper(); + SaturatingMultiplyAddTestHelper(); +} + +TEST(MathExtras, IsShiftedUInt) { + EXPECT_TRUE((isShiftedUInt<1, 0>(0))); + EXPECT_TRUE((isShiftedUInt<1, 0>(1))); + EXPECT_FALSE((isShiftedUInt<1, 0>(2))); + EXPECT_FALSE((isShiftedUInt<1, 0>(3))); + EXPECT_FALSE((isShiftedUInt<1, 0>(0x8000000000000000))); + EXPECT_TRUE((isShiftedUInt<1, 63>(0x8000000000000000))); + EXPECT_TRUE((isShiftedUInt<2, 62>(0xC000000000000000))); + EXPECT_FALSE((isShiftedUInt<2, 62>(0xE000000000000000))); + + // 0x201 is ten bits long and has a 1 in the MSB and LSB. + EXPECT_TRUE((isShiftedUInt<10, 5>(uint64_t(0x201) << 5))); + EXPECT_FALSE((isShiftedUInt<10, 5>(uint64_t(0x201) << 4))); + EXPECT_FALSE((isShiftedUInt<10, 5>(uint64_t(0x201) << 6))); +} + +TEST(MathExtras, IsShiftedInt) { + EXPECT_TRUE((isShiftedInt<1, 0>(0))); + EXPECT_TRUE((isShiftedInt<1, 0>(-1))); + EXPECT_FALSE((isShiftedInt<1, 0>(2))); + EXPECT_FALSE((isShiftedInt<1, 0>(3))); + EXPECT_FALSE((isShiftedInt<1, 0>(0x8000000000000000))); + EXPECT_TRUE((isShiftedInt<1, 63>(0x8000000000000000))); + EXPECT_TRUE((isShiftedInt<2, 62>(0xC000000000000000))); + EXPECT_FALSE((isShiftedInt<2, 62>(0xE000000000000000))); + + // 0x201 is ten bits long and has a 1 in the MSB and LSB. + EXPECT_TRUE((isShiftedInt<11, 5>(int64_t(0x201) << 5))); + EXPECT_FALSE((isShiftedInt<11, 5>(int64_t(0x201) << 3))); + EXPECT_FALSE((isShiftedInt<11, 5>(int64_t(0x201) << 6))); + EXPECT_TRUE((isShiftedInt<11, 5>(-(int64_t(0x201) << 5)))); + EXPECT_FALSE((isShiftedInt<11, 5>(-(int64_t(0x201) << 3)))); + EXPECT_FALSE((isShiftedInt<11, 5>(-(int64_t(0x201) << 6)))); + + EXPECT_TRUE((isShiftedInt<6, 10>(-(int64_t(1) << 15)))); + EXPECT_FALSE((isShiftedInt<6, 10>(int64_t(1) << 15))); +} + +template +class OverflowTest : public ::testing::Test { }; + +using OverflowTestTypes = ::testing::Types; + +TYPED_TEST_SUITE(OverflowTest, OverflowTestTypes, ); + +TYPED_TEST(OverflowTest, AddNoOverflow) { + TypeParam Result; + EXPECT_FALSE(AddOverflow(1, 2, Result)); + EXPECT_EQ(Result, TypeParam(3)); +} + +TYPED_TEST(OverflowTest, AddOverflowToNegative) { + TypeParam Result; + auto MaxValue = std::numeric_limits::max(); + EXPECT_TRUE(AddOverflow(MaxValue, MaxValue, Result)); + EXPECT_EQ(Result, TypeParam(-2)); +} + +TYPED_TEST(OverflowTest, AddOverflowToMin) { + TypeParam Result; + auto MaxValue = std::numeric_limits::max(); + EXPECT_TRUE(AddOverflow(MaxValue, TypeParam(1), Result)); + EXPECT_EQ(Result, std::numeric_limits::min()); +} + +TYPED_TEST(OverflowTest, AddOverflowToZero) { + TypeParam Result; + auto MinValue = std::numeric_limits::min(); + EXPECT_TRUE(AddOverflow(MinValue, MinValue, Result)); + EXPECT_EQ(Result, TypeParam(0)); +} + +TYPED_TEST(OverflowTest, AddOverflowToMax) { + TypeParam Result; + auto MinValue = std::numeric_limits::min(); + EXPECT_TRUE(AddOverflow(MinValue, TypeParam(-1), Result)); + EXPECT_EQ(Result, std::numeric_limits::max()); +} + +TYPED_TEST(OverflowTest, SubNoOverflow) { + TypeParam Result; + EXPECT_FALSE(SubOverflow(1, 2, Result)); + EXPECT_EQ(Result, TypeParam(-1)); +} + +TYPED_TEST(OverflowTest, SubOverflowToMax) { + TypeParam Result; + auto MinValue = std::numeric_limits::min(); + EXPECT_TRUE(SubOverflow(0, MinValue, Result)); + EXPECT_EQ(Result, MinValue); +} + +TYPED_TEST(OverflowTest, SubOverflowToMin) { + TypeParam Result; + auto MinValue = std::numeric_limits::min(); + EXPECT_TRUE(SubOverflow(0, MinValue, Result)); + EXPECT_EQ(Result, MinValue); +} + +TYPED_TEST(OverflowTest, SubOverflowToNegative) { + TypeParam Result; + auto MaxValue = std::numeric_limits::max(); + auto MinValue = std::numeric_limits::min(); + EXPECT_TRUE(SubOverflow(MaxValue, MinValue, Result)); + EXPECT_EQ(Result, TypeParam(-1)); +} + +TYPED_TEST(OverflowTest, SubOverflowToPositive) { + TypeParam Result; + auto MaxValue = std::numeric_limits::max(); + auto MinValue = std::numeric_limits::min(); + EXPECT_TRUE(SubOverflow(MinValue, MaxValue, Result)); + EXPECT_EQ(Result, TypeParam(1)); +} + +TYPED_TEST(OverflowTest, MulNoOverflow) { + TypeParam Result; + EXPECT_FALSE(MulOverflow(1, 2, Result)); + EXPECT_EQ(Result, 2); + EXPECT_FALSE(MulOverflow(-1, 3, Result)); + EXPECT_EQ(Result, -3); + EXPECT_FALSE(MulOverflow(4, -2, Result)); + EXPECT_EQ(Result, -8); + EXPECT_FALSE(MulOverflow(-6, -5, Result)); + EXPECT_EQ(Result, 30); +} + +TYPED_TEST(OverflowTest, MulNoOverflowToMax) { + TypeParam Result; + auto MaxValue = std::numeric_limits::max(); + auto MinValue = std::numeric_limits::min(); + EXPECT_FALSE(MulOverflow(MinValue + 1, -1, Result)); + EXPECT_EQ(Result, MaxValue); +} + +TYPED_TEST(OverflowTest, MulOverflowToMin) { + TypeParam Result; + auto MinValue = std::numeric_limits::min(); + EXPECT_TRUE(MulOverflow(MinValue, -1, Result)); + EXPECT_EQ(Result, MinValue); +} + +TYPED_TEST(OverflowTest, MulOverflowMax) { + TypeParam Result; + auto MinValue = std::numeric_limits::min(); + auto MaxValue = std::numeric_limits::max(); + EXPECT_TRUE(MulOverflow(MinValue, MinValue, Result)); + EXPECT_EQ(Result, 0); + EXPECT_TRUE(MulOverflow(MaxValue, MaxValue, Result)); + EXPECT_EQ(Result, 1); +} + +TYPED_TEST(OverflowTest, MulResultZero) { + TypeParam Result; + EXPECT_FALSE(MulOverflow(4, 0, Result)); + EXPECT_EQ(Result, TypeParam(0)); + EXPECT_FALSE(MulOverflow(-5, 0, Result)); + EXPECT_EQ(Result, TypeParam(0)); + EXPECT_FALSE(MulOverflow(0, 5, Result)); + EXPECT_EQ(Result, TypeParam(0)); + EXPECT_FALSE(MulOverflow(0, -5, Result)); + EXPECT_EQ(Result, TypeParam(0)); +} + +} // namespace diff --git a/wpiutil/src/test/native/cpp/llvm/PointerIntPairTest.cpp b/wpiutil/src/test/native/cpp/llvm/PointerIntPairTest.cpp new file mode 100644 index 0000000000..fcca5b6388 --- /dev/null +++ b/wpiutil/src/test/native/cpp/llvm/PointerIntPairTest.cpp @@ -0,0 +1,109 @@ +//===- llvm/unittest/ADT/PointerIntPairTest.cpp - Unit tests --------------===// +// +// 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 "wpi/PointerIntPair.h" +#include "gtest/gtest.h" +#include +using namespace wpi; + +namespace { + +TEST(PointerIntPairTest, GetSet) { + struct S { + int i; + }; + S s; + + PointerIntPair Pair(&s, 1U); + EXPECT_EQ(&s, Pair.getPointer()); + EXPECT_EQ(1U, Pair.getInt()); + + Pair.setInt(2); + EXPECT_EQ(&s, Pair.getPointer()); + EXPECT_EQ(2U, Pair.getInt()); + + Pair.setPointer(nullptr); + EXPECT_EQ(nullptr, Pair.getPointer()); + EXPECT_EQ(2U, Pair.getInt()); + + Pair.setPointerAndInt(&s, 3U); + EXPECT_EQ(&s, Pair.getPointer()); + EXPECT_EQ(3U, Pair.getInt()); + + // Make sure that we can perform all of our operations on enum classes. + // + // The concern is that enum classes are only explicitly convertible to + // integers. This means that if we assume in PointerIntPair this, a + // compilation error will result. This group of tests exercises the enum class + // code to make sure that we do not run into such issues in the future. + enum class E : unsigned { + Case1, + Case2, + Case3, + }; + PointerIntPair Pair2(&s, E::Case1); + EXPECT_EQ(&s, Pair2.getPointer()); + EXPECT_EQ(E::Case1, Pair2.getInt()); + + Pair2.setInt(E::Case2); + EXPECT_EQ(&s, Pair2.getPointer()); + EXPECT_EQ(E::Case2, Pair2.getInt()); + + Pair2.setPointer(nullptr); + EXPECT_EQ(nullptr, Pair2.getPointer()); + EXPECT_EQ(E::Case2, Pair2.getInt()); + + Pair2.setPointerAndInt(&s, E::Case3); + EXPECT_EQ(&s, Pair2.getPointer()); + EXPECT_EQ(E::Case3, Pair2.getInt()); + + static_assert(std::is_trivially_copyable>::value, + "trivially copyable"); +} + +TEST(PointerIntPairTest, DefaultInitialize) { + PointerIntPair Pair; + EXPECT_EQ(nullptr, Pair.getPointer()); + EXPECT_EQ(0U, Pair.getInt()); +} + +// In real code this would be a word-sized integer limited to 31 bits. +struct Fixnum31 { + uintptr_t Value; +}; +struct FixnumPointerTraits { + static inline void *getAsVoidPointer(Fixnum31 Num) { + return reinterpret_cast(Num.Value << NumLowBitsAvailable); + } + static inline Fixnum31 getFromVoidPointer(void *P) { + // In real code this would assert that the value is in range. + return {reinterpret_cast(P) >> NumLowBitsAvailable}; + } + static constexpr int NumLowBitsAvailable = + std::numeric_limits::digits - 31; +}; +TEST(PointerIntPairTest, ManyUnusedBits) { + + PointerIntPair pair; + EXPECT_EQ((uintptr_t)0, pair.getPointer().Value); + EXPECT_FALSE(pair.getInt()); + + pair.setPointerAndInt({ 0x7FFFFFFF }, true ); + EXPECT_EQ((uintptr_t)0x7FFFFFFF, pair.getPointer().Value); + EXPECT_TRUE(pair.getInt()); + + EXPECT_EQ(FixnumPointerTraits::NumLowBitsAvailable - 1, + (int)PointerLikeTypeTraits::NumLowBitsAvailable); + + static_assert( + std::is_trivially_copyable< + PointerIntPair>::value, + "trivially copyable"); +} + +} // end anonymous namespace diff --git a/wpiutil/src/test/native/cpp/llvm/PointerUnionTest.cpp b/wpiutil/src/test/native/cpp/llvm/PointerUnionTest.cpp new file mode 100644 index 0000000000..5a94a45ef9 --- /dev/null +++ b/wpiutil/src/test/native/cpp/llvm/PointerUnionTest.cpp @@ -0,0 +1,159 @@ +//===- llvm/unittest/ADT/PointerUnionTest.cpp - Optional unit tests -------===// +// +// 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 "wpi/PointerUnion.h" +#include "gtest/gtest.h" +using namespace wpi; + +namespace { + +typedef PointerUnion PU; +typedef PointerUnion PU3; +typedef PointerUnion PU4; + +struct PointerUnionTest : public testing::Test { + float f; + int i; + double d; + long long l; + + PU a, b, c, n; + PU3 i3, f3, l3; + PU4 i4, f4, l4, d4; + PU4 i4null, f4null, l4null, d4null; + + PointerUnionTest() + : f(3.14f), i(42), d(3.14), l(42), a(&f), b(&i), c(&i), n(), i3(&i), + f3(&f), l3(&l), i4(&i), f4(&f), l4(&l), d4(&d), i4null((int *)nullptr), + f4null((float *)nullptr), l4null((long long *)nullptr), + d4null((double *)nullptr) {} +}; + +TEST_F(PointerUnionTest, Comparison) { + EXPECT_TRUE(a == a); + EXPECT_FALSE(a != a); + EXPECT_TRUE(a != b); + EXPECT_FALSE(a == b); + EXPECT_TRUE(b == c); + EXPECT_FALSE(b != c); + EXPECT_TRUE(b != n); + EXPECT_FALSE(b == n); + EXPECT_TRUE(i3 == i3); + EXPECT_FALSE(i3 != i3); + EXPECT_TRUE(i3 != f3); + EXPECT_TRUE(f3 != l3); + EXPECT_TRUE(i4 == i4); + EXPECT_FALSE(i4 != i4); + EXPECT_TRUE(i4 != f4); + EXPECT_TRUE(i4 != l4); + EXPECT_TRUE(f4 != l4); + EXPECT_TRUE(l4 != d4); + EXPECT_TRUE(i4null != f4null); + EXPECT_TRUE(i4null != l4null); + EXPECT_TRUE(i4null != d4null); +} + +TEST_F(PointerUnionTest, Null) { + EXPECT_FALSE(a.isNull()); + EXPECT_FALSE(b.isNull()); + EXPECT_TRUE(n.isNull()); + EXPECT_FALSE(!a); + EXPECT_FALSE(!b); + EXPECT_TRUE(!n); + // workaround an issue with EXPECT macros and explicit bool + EXPECT_TRUE((bool)a); + EXPECT_TRUE((bool)b); + EXPECT_FALSE(n); + + EXPECT_NE(n, b); + EXPECT_EQ(b, c); + b = nullptr; + EXPECT_EQ(n, b); + EXPECT_NE(b, c); + EXPECT_FALSE(i3.isNull()); + EXPECT_FALSE(f3.isNull()); + EXPECT_FALSE(l3.isNull()); + EXPECT_FALSE(i4.isNull()); + EXPECT_FALSE(f4.isNull()); + EXPECT_FALSE(l4.isNull()); + EXPECT_FALSE(d4.isNull()); + EXPECT_TRUE(i4null.isNull()); + EXPECT_TRUE(f4null.isNull()); + EXPECT_TRUE(l4null.isNull()); + EXPECT_TRUE(d4null.isNull()); +} + +TEST_F(PointerUnionTest, Is) { + EXPECT_FALSE(a.is()); + EXPECT_TRUE(a.is()); + EXPECT_TRUE(b.is()); + EXPECT_FALSE(b.is()); + EXPECT_TRUE(n.is()); + EXPECT_FALSE(n.is()); + EXPECT_TRUE(i3.is()); + EXPECT_TRUE(f3.is()); + EXPECT_TRUE(l3.is()); + EXPECT_TRUE(i4.is()); + EXPECT_TRUE(f4.is()); + EXPECT_TRUE(l4.is()); + EXPECT_TRUE(d4.is()); + EXPECT_TRUE(i4null.is()); + EXPECT_TRUE(f4null.is()); + EXPECT_TRUE(l4null.is()); + EXPECT_TRUE(d4null.is()); +} + +TEST_F(PointerUnionTest, Get) { + EXPECT_EQ(a.get(), &f); + EXPECT_EQ(b.get(), &i); + EXPECT_EQ(n.get(), (int *)nullptr); +} + +template struct alignas(8) Aligned {}; + +typedef PointerUnion *, Aligned<1> *, Aligned<2> *, Aligned<3> *, + Aligned<4> *, Aligned<5> *, Aligned<6> *, Aligned<7> *> + PU8; + +TEST_F(PointerUnionTest, ManyElements) { + Aligned<0> a0; + Aligned<7> a7; + + PU8 a = &a0; + EXPECT_TRUE(a.is*>()); + EXPECT_FALSE(a.is*>()); + EXPECT_FALSE(a.is*>()); + EXPECT_FALSE(a.is*>()); + EXPECT_FALSE(a.is*>()); + EXPECT_FALSE(a.is*>()); + EXPECT_FALSE(a.is*>()); + EXPECT_FALSE(a.is*>()); + EXPECT_EQ(a.dyn_cast*>(), &a0); + EXPECT_EQ(*a.getAddrOfPtr1(), &a0); + + a = &a7; + EXPECT_FALSE(a.is*>()); + EXPECT_FALSE(a.is*>()); + EXPECT_FALSE(a.is*>()); + EXPECT_FALSE(a.is*>()); + EXPECT_FALSE(a.is*>()); + EXPECT_FALSE(a.is*>()); + EXPECT_FALSE(a.is*>()); + EXPECT_TRUE(a.is*>()); + EXPECT_EQ(a.dyn_cast*>(), &a7); + + EXPECT_TRUE(a == PU8(&a7)); + EXPECT_TRUE(a != PU8(&a0)); +} + +TEST_F(PointerUnionTest, GetAddrOfPtr1) { + EXPECT_TRUE((void *)b.getAddrOfPtr1() == (void *)&b); + EXPECT_TRUE((void *)n.getAddrOfPtr1() == (void *)&n); +} + +} // end anonymous namespace diff --git a/wpiutil/src/test/native/cpp/llvm/STLForwardCompatTest.cpp b/wpiutil/src/test/native/cpp/llvm/STLForwardCompatTest.cpp new file mode 100644 index 0000000000..333359ef31 --- /dev/null +++ b/wpiutil/src/test/native/cpp/llvm/STLForwardCompatTest.cpp @@ -0,0 +1,78 @@ +//===- STLForwardCompatTest.cpp - Unit tests for STLForwardCompat ---------===// +// +// 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 "wpi/STLForwardCompat.h" +#include "gtest/gtest.h" + +namespace { + +TEST(STLForwardCompatTest, NegationTest) { + EXPECT_TRUE((wpi::negation::value)); + EXPECT_FALSE((wpi::negation::value)); +} + +struct incomplete_type; + +TEST(STLForwardCompatTest, ConjunctionTest) { + EXPECT_TRUE((wpi::conjunction<>::value)); + EXPECT_FALSE((wpi::conjunction::value)); + EXPECT_TRUE((wpi::conjunction::value)); + EXPECT_FALSE((wpi::conjunction::value)); + EXPECT_FALSE((wpi::conjunction::value)); + EXPECT_FALSE((wpi::conjunction::value)); + EXPECT_TRUE((wpi::conjunction::value)); + EXPECT_TRUE((wpi::conjunction::value)); +} + +TEST(STLForwardCompatTest, DisjunctionTest) { + EXPECT_FALSE((wpi::disjunction<>::value)); + EXPECT_FALSE((wpi::disjunction::value)); + EXPECT_TRUE((wpi::disjunction::value)); + EXPECT_TRUE((wpi::disjunction::value)); + EXPECT_TRUE((wpi::disjunction::value)); + EXPECT_TRUE((wpi::disjunction::value)); + EXPECT_TRUE((wpi::disjunction::value)); + EXPECT_TRUE((wpi::disjunction::value)); +} + +template +class STLForwardCompatRemoveCVRefTest : public ::testing::Test {}; + +using STLForwardCompatRemoveCVRefTestTypes = ::testing::Types< + // clang-format off + std::pair, + std::pair, + std::pair, + std::pair, + std::pair, + std::pair, + std::pair, + std::pair, + std::pair + // clang-format on + >; + +TYPED_TEST_SUITE(STLForwardCompatRemoveCVRefTest, + STLForwardCompatRemoveCVRefTestTypes, ); + +TYPED_TEST(STLForwardCompatRemoveCVRefTest, RemoveCVRef) { + using From = typename TypeParam::first_type; + using To = typename TypeParam::second_type; + EXPECT_TRUE( + (std::is_same::type, To>::value)); +} + +TYPED_TEST(STLForwardCompatRemoveCVRefTest, RemoveCVRefT) { + using From = typename TypeParam::first_type; + EXPECT_TRUE((std::is_same::type, + wpi::remove_cvref_t>::value)); +} + +} // namespace diff --git a/wpiutil/src/test/native/cpp/llvm/SmallPtrSetTest.cpp b/wpiutil/src/test/native/cpp/llvm/SmallPtrSetTest.cpp new file mode 100644 index 0000000000..1916d509b1 --- /dev/null +++ b/wpiutil/src/test/native/cpp/llvm/SmallPtrSetTest.cpp @@ -0,0 +1,412 @@ +//===- llvm/unittest/ADT/SmallPtrSetTest.cpp ------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// SmallPtrSet unit tests. +// +//===----------------------------------------------------------------------===// + +#include "wpi/SmallPtrSet.h" +#include "wpi/PointerIntPair.h" +#include "wpi/PointerLikeTypeTraits.h" +#include "gtest/gtest.h" + +#include + +using namespace wpi; + +TEST(SmallPtrSetTest, Assignment) { + int buf[8]; + for (int i = 0; i < 8; ++i) + buf[i] = 0; + + SmallPtrSet s1 = {&buf[0], &buf[1]}; + SmallPtrSet s2; + (s2 = s1).insert(&buf[2]); + + // Self assign as well. + (s2 = static_cast &>(s2)).insert(&buf[3]); + + s1 = s2; + EXPECT_EQ(4U, s1.size()); + for (int i = 0; i < 8; ++i) + if (i < 4) + EXPECT_TRUE(s1.count(&buf[i])); + else + EXPECT_FALSE(s1.count(&buf[i])); + + // Assign and insert with initializer lists, and ones that contain both + // duplicates and out-of-order elements. + (s2 = {&buf[6], &buf[7], &buf[6]}).insert({&buf[5], &buf[4]}); + for (int i = 0; i < 8; ++i) + if (i < 4) + EXPECT_FALSE(s2.count(&buf[i])); + else + EXPECT_TRUE(s2.count(&buf[i])); +} + +TEST(SmallPtrSetTest, GrowthTest) { + int i; + int buf[8]; + for(i=0; i<8; ++i) buf[i]=0; + + + SmallPtrSet s; + typedef SmallPtrSet::iterator iter; + + s.insert(&buf[0]); + s.insert(&buf[1]); + s.insert(&buf[2]); + s.insert(&buf[3]); + EXPECT_EQ(4U, s.size()); + + i = 0; + for(iter I=s.begin(), E=s.end(); I!=E; ++I, ++i) + (**I)++; + EXPECT_EQ(4, i); + for(i=0; i<8; ++i) + EXPECT_EQ(i<4?1:0,buf[i]); + + s.insert(&buf[4]); + s.insert(&buf[5]); + s.insert(&buf[6]); + s.insert(&buf[7]); + + i = 0; + for(iter I=s.begin(), E=s.end(); I!=E; ++I, ++i) + (**I)++; + EXPECT_EQ(8, i); + s.erase(&buf[4]); + s.erase(&buf[5]); + s.erase(&buf[6]); + s.erase(&buf[7]); + EXPECT_EQ(4U, s.size()); + + i = 0; + for(iter I=s.begin(), E=s.end(); I!=E; ++I, ++i) + (**I)++; + EXPECT_EQ(4, i); + for(i=0; i<8; ++i) + EXPECT_EQ(i<4?3:1,buf[i]); + + s.clear(); + for(i=0; i<8; ++i) buf[i]=0; + for(i=0; i<128; ++i) s.insert(&buf[i%8]); // test repeated entires + EXPECT_EQ(8U, s.size()); + for(iter I=s.begin(), E=s.end(); I!=E; ++I, ++i) + (**I)++; + for(i=0; i<8; ++i) + EXPECT_EQ(1,buf[i]); +} + +TEST(SmallPtrSetTest, CopyAndMoveTest) { + int buf[8]; + for (int i = 0; i < 8; ++i) + buf[i] = 0; + + SmallPtrSet s1; + s1.insert(&buf[0]); + s1.insert(&buf[1]); + s1.insert(&buf[2]); + s1.insert(&buf[3]); + EXPECT_EQ(4U, s1.size()); + for (int i = 0; i < 8; ++i) + if (i < 4) + EXPECT_TRUE(s1.count(&buf[i])); + else + EXPECT_FALSE(s1.count(&buf[i])); + + SmallPtrSet s2(s1); + EXPECT_EQ(4U, s2.size()); + for (int i = 0; i < 8; ++i) + if (i < 4) + EXPECT_TRUE(s2.count(&buf[i])); + else + EXPECT_FALSE(s2.count(&buf[i])); + + s1 = s2; + EXPECT_EQ(4U, s1.size()); + EXPECT_EQ(4U, s2.size()); + for (int i = 0; i < 8; ++i) + if (i < 4) + EXPECT_TRUE(s1.count(&buf[i])); + else + EXPECT_FALSE(s1.count(&buf[i])); + + SmallPtrSet s3(std::move(s1)); + EXPECT_EQ(4U, s3.size()); + EXPECT_TRUE(s1.empty()); + for (int i = 0; i < 8; ++i) + if (i < 4) + EXPECT_TRUE(s3.count(&buf[i])); + else + EXPECT_FALSE(s3.count(&buf[i])); + + // Move assign into the moved-from object. Also test move of a non-small + // container. + s3.insert(&buf[4]); + s3.insert(&buf[5]); + s3.insert(&buf[6]); + s3.insert(&buf[7]); + s1 = std::move(s3); + EXPECT_EQ(8U, s1.size()); + EXPECT_TRUE(s3.empty()); + for (int i = 0; i < 8; ++i) + EXPECT_TRUE(s1.count(&buf[i])); + + // Copy assign into a moved-from object. + s3 = s1; + EXPECT_EQ(8U, s3.size()); + EXPECT_EQ(8U, s1.size()); + for (int i = 0; i < 8; ++i) + EXPECT_TRUE(s3.count(&buf[i])); +} + +TEST(SmallPtrSetTest, SwapTest) { + int buf[10]; + + SmallPtrSet a; + SmallPtrSet b; + + a.insert(&buf[0]); + a.insert(&buf[1]); + b.insert(&buf[2]); + + EXPECT_EQ(2U, a.size()); + EXPECT_EQ(1U, b.size()); + EXPECT_TRUE(a.count(&buf[0])); + EXPECT_TRUE(a.count(&buf[1])); + EXPECT_FALSE(a.count(&buf[2])); + EXPECT_FALSE(a.count(&buf[3])); + EXPECT_FALSE(b.count(&buf[0])); + EXPECT_FALSE(b.count(&buf[1])); + EXPECT_TRUE(b.count(&buf[2])); + EXPECT_FALSE(b.count(&buf[3])); + + std::swap(a, b); + + EXPECT_EQ(1U, a.size()); + EXPECT_EQ(2U, b.size()); + EXPECT_FALSE(a.count(&buf[0])); + EXPECT_FALSE(a.count(&buf[1])); + EXPECT_TRUE(a.count(&buf[2])); + EXPECT_FALSE(a.count(&buf[3])); + EXPECT_TRUE(b.count(&buf[0])); + EXPECT_TRUE(b.count(&buf[1])); + EXPECT_FALSE(b.count(&buf[2])); + EXPECT_FALSE(b.count(&buf[3])); + + b.insert(&buf[3]); + std::swap(a, b); + + EXPECT_EQ(3U, a.size()); + EXPECT_EQ(1U, b.size()); + EXPECT_TRUE(a.count(&buf[0])); + EXPECT_TRUE(a.count(&buf[1])); + EXPECT_FALSE(a.count(&buf[2])); + EXPECT_TRUE(a.count(&buf[3])); + EXPECT_FALSE(b.count(&buf[0])); + EXPECT_FALSE(b.count(&buf[1])); + EXPECT_TRUE(b.count(&buf[2])); + EXPECT_FALSE(b.count(&buf[3])); + + std::swap(a, b); + + EXPECT_EQ(1U, a.size()); + EXPECT_EQ(3U, b.size()); + EXPECT_FALSE(a.count(&buf[0])); + EXPECT_FALSE(a.count(&buf[1])); + EXPECT_TRUE(a.count(&buf[2])); + EXPECT_FALSE(a.count(&buf[3])); + EXPECT_TRUE(b.count(&buf[0])); + EXPECT_TRUE(b.count(&buf[1])); + EXPECT_FALSE(b.count(&buf[2])); + EXPECT_TRUE(b.count(&buf[3])); + + a.insert(&buf[4]); + a.insert(&buf[5]); + a.insert(&buf[6]); + + std::swap(b, a); + + EXPECT_EQ(3U, a.size()); + EXPECT_EQ(4U, b.size()); + EXPECT_TRUE(b.count(&buf[2])); + EXPECT_TRUE(b.count(&buf[4])); + EXPECT_TRUE(b.count(&buf[5])); + EXPECT_TRUE(b.count(&buf[6])); + EXPECT_TRUE(a.count(&buf[0])); + EXPECT_TRUE(a.count(&buf[1])); + EXPECT_TRUE(a.count(&buf[3])); +} + +void checkEraseAndIterators(SmallPtrSetImpl &S) { + int buf[3]; + + S.insert(&buf[0]); + S.insert(&buf[1]); + S.insert(&buf[2]); + + // Iterators must still be valid after erase() calls; + auto B = S.begin(); + auto M = std::next(B); + auto E = S.end(); + EXPECT_TRUE(*B == &buf[0] || *B == &buf[1] || *B == &buf[2]); + EXPECT_TRUE(*M == &buf[0] || *M == &buf[1] || *M == &buf[2]); + EXPECT_TRUE(*B != *M); + int *Removable = *std::next(M); + // No iterator points to Removable now. + EXPECT_TRUE(Removable == &buf[0] || Removable == &buf[1] || + Removable == &buf[2]); + EXPECT_TRUE(Removable != *B && Removable != *M); + + S.erase(Removable); + + // B,M,E iterators should still be valid + EXPECT_EQ(B, S.begin()); + EXPECT_EQ(M, std::next(B)); + EXPECT_EQ(E, S.end()); + EXPECT_EQ(std::next(M), E); +} + +TEST(SmallPtrSetTest, EraseTest) { + // Test when set stays small. + SmallPtrSet B; + checkEraseAndIterators(B); + + // Test when set grows big. + SmallPtrSet A; + checkEraseAndIterators(A); +} + +// Verify that dereferencing and iteration work. +TEST(SmallPtrSetTest, dereferenceAndIterate) { + int Ints[] = {0, 1, 2, 3, 4, 5, 6, 7}; + SmallPtrSet S; + for (int &I : Ints) { + EXPECT_EQ(&I, *S.insert(&I).first); + EXPECT_EQ(&I, *S.find(&I)); + } + + // Iterate from each and count how many times each element is found. + int Found[sizeof(Ints)/sizeof(int)] = {0}; + for (int &I : Ints) + for (auto F = S.find(&I), E = S.end(); F != E; ++F) + ++Found[*F - Ints]; + + // Sort. We should hit the first element just once and the final element N + // times. + std::sort(std::begin(Found), std::end(Found)); + for (auto F = std::begin(Found), E = std::end(Found); F != E; ++F) + EXPECT_EQ(F - Found + 1, *F); +} + +// Verify that const pointers work for count and find even when the underlying +// SmallPtrSet is not for a const pointer type. +TEST(SmallPtrSetTest, ConstTest) { + SmallPtrSet IntSet; + int A; + int *B = &A; + const int *C = &A; + IntSet.insert(B); + EXPECT_EQ(IntSet.count(B), 1u); + EXPECT_EQ(IntSet.count(C), 1u); + EXPECT_TRUE(IntSet.contains(B)); + EXPECT_TRUE(IntSet.contains(C)); +} + +// Verify that we automatically get the const version of PointerLikeTypeTraits +// filled in for us, even for a non-pointer type +using TestPair = PointerIntPair; + +TEST(SmallPtrSetTest, ConstNonPtrTest) { + SmallPtrSet IntSet; + int A[1]; + TestPair Pair(&A[0], 1); + IntSet.insert(Pair); + EXPECT_EQ(IntSet.count(Pair), 1u); + EXPECT_TRUE(IntSet.contains(Pair)); +} + +// Test equality comparison. +TEST(SmallPtrSetTest, EqualityComparison) { + int buf[3]; + for (int i = 0; i < 3; ++i) + buf[i] = 0; + + SmallPtrSet a; + a.insert(&buf[0]); + a.insert(&buf[1]); + + SmallPtrSet b; + b.insert(&buf[1]); + b.insert(&buf[0]); + + SmallPtrSet c; + c.insert(&buf[1]); + c.insert(&buf[2]); + + SmallPtrSet d; + d.insert(&buf[0]); + + SmallPtrSet e; + e.insert(&buf[0]); + e.insert(&buf[1]); + e.insert(&buf[2]); + + EXPECT_EQ(a, b); + EXPECT_EQ(b, a); + EXPECT_NE(b, c); + EXPECT_NE(c, a); + EXPECT_NE(d, a); + EXPECT_NE(a, d); + EXPECT_NE(a, e); + EXPECT_NE(e, a); + EXPECT_NE(c, e); + EXPECT_NE(e, d); +} + +TEST(SmallPtrSetTest, Contains) { + SmallPtrSet Set; + int buf[4] = {0, 11, 22, 11}; + EXPECT_FALSE(Set.contains(&buf[0])); + EXPECT_FALSE(Set.contains(&buf[1])); + + Set.insert(&buf[0]); + Set.insert(&buf[1]); + EXPECT_TRUE(Set.contains(&buf[0])); + EXPECT_TRUE(Set.contains(&buf[1])); + EXPECT_FALSE(Set.contains(&buf[3])); + + Set.insert(&buf[1]); + EXPECT_TRUE(Set.contains(&buf[0])); + EXPECT_TRUE(Set.contains(&buf[1])); + EXPECT_FALSE(Set.contains(&buf[3])); + + Set.erase(&buf[1]); + EXPECT_TRUE(Set.contains(&buf[0])); + EXPECT_FALSE(Set.contains(&buf[1])); + + Set.insert(&buf[1]); + Set.insert(&buf[2]); + EXPECT_TRUE(Set.contains(&buf[0])); + EXPECT_TRUE(Set.contains(&buf[1])); + EXPECT_TRUE(Set.contains(&buf[2])); +} + +TEST(SmallPtrSetTest, InsertIterator) { + SmallPtrSet Set; + int Vals[5] = {11, 22, 33, 44, 55}; + int *Buf[5] = {&Vals[0], &Vals[1], &Vals[2], &Vals[3], &Vals[4]}; + + for (int *Ptr : Buf) + Set.insert(Set.begin(), Ptr); + + // Ensure that all of the values were copied into the set. + for (const auto *Ptr : Buf) + EXPECT_TRUE(Set.contains(Ptr)); +} diff --git a/wpiutil/src/test/native/cpp/llvm/SmallSetTest.cpp b/wpiutil/src/test/native/cpp/llvm/SmallSetTest.cpp new file mode 100644 index 0000000000..e245aa48dc --- /dev/null +++ b/wpiutil/src/test/native/cpp/llvm/SmallSetTest.cpp @@ -0,0 +1,194 @@ +//===- llvm/unittest/ADT/SmallSetTest.cpp ------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// SmallSet unit tests. +// +//===----------------------------------------------------------------------===// + +#include "wpi/SmallSet.h" +#include "gtest/gtest.h" +#include + +using namespace wpi; + +TEST(SmallSetTest, Insert) { + + SmallSet s1; + + for (int i = 0; i < 4; i++) + s1.insert(i); + + for (int i = 0; i < 4; i++) + s1.insert(i); + + EXPECT_EQ(4u, s1.size()); + + for (int i = 0; i < 4; i++) + EXPECT_EQ(1u, s1.count(i)); + + EXPECT_EQ(0u, s1.count(4)); +} + +TEST(SmallSetTest, Grow) { + SmallSet s1; + + for (int i = 0; i < 8; i++) + s1.insert(i); + + EXPECT_EQ(8u, s1.size()); + + for (int i = 0; i < 8; i++) + EXPECT_EQ(1u, s1.count(i)); + + EXPECT_EQ(0u, s1.count(8)); +} + +TEST(SmallSetTest, Erase) { + SmallSet s1; + + for (int i = 0; i < 8; i++) + s1.insert(i); + + EXPECT_EQ(8u, s1.size()); + + // Remove elements one by one and check if all other elements are still there. + for (int i = 0; i < 8; i++) { + EXPECT_EQ(1u, s1.count(i)); + EXPECT_TRUE(s1.erase(i)); + EXPECT_EQ(0u, s1.count(i)); + EXPECT_EQ(8u - i - 1, s1.size()); + for (int j = i + 1; j < 8; j++) + EXPECT_EQ(1u, s1.count(j)); + } + + EXPECT_EQ(0u, s1.count(8)); +} + +TEST(SmallSetTest, IteratorInt) { + SmallSet s1; + + // Test the 'small' case. + for (int i = 0; i < 3; i++) + s1.insert(i); + + std::vector V(s1.begin(), s1.end()); + // Make sure the elements are in the expected order. + std::sort(V.begin(), V.end()); + for (int i = 0; i < 3; i++) + EXPECT_EQ(i, V[i]); + + // Test the 'big' case by adding a few more elements to switch to std::set + // internally. + for (int i = 3; i < 6; i++) + s1.insert(i); + + V.assign(s1.begin(), s1.end()); + // Make sure the elements are in the expected order. + std::sort(V.begin(), V.end()); + for (int i = 0; i < 6; i++) + EXPECT_EQ(i, V[i]); +} + +TEST(SmallSetTest, IteratorString) { + // Test SmallSetIterator for SmallSet with a type with non-trivial + // ctors/dtors. + SmallSet s1; + + s1.insert("str 1"); + s1.insert("str 2"); + s1.insert("str 1"); + + std::vector V(s1.begin(), s1.end()); + std::sort(V.begin(), V.end()); + EXPECT_EQ(2u, s1.size()); + EXPECT_EQ("str 1", V[0]); + EXPECT_EQ("str 2", V[1]); + + s1.insert("str 4"); + s1.insert("str 0"); + s1.insert("str 4"); + + V.assign(s1.begin(), s1.end()); + // Make sure the elements are in the expected order. + std::sort(V.begin(), V.end()); + EXPECT_EQ(4u, s1.size()); + EXPECT_EQ("str 0", V[0]); + EXPECT_EQ("str 1", V[1]); + EXPECT_EQ("str 2", V[2]); + EXPECT_EQ("str 4", V[3]); +} + +TEST(SmallSetTest, IteratorIncMoveCopy) { + // Test SmallSetIterator for SmallSet with a type with non-trivial + // ctors/dtors. + SmallSet s1; + + s1.insert("str 1"); + s1.insert("str 2"); + + auto Iter = s1.begin(); + EXPECT_EQ("str 1", *Iter); + ++Iter; + EXPECT_EQ("str 2", *Iter); + + s1.insert("str 4"); + s1.insert("str 0"); + auto Iter2 = s1.begin(); + Iter = std::move(Iter2); + EXPECT_EQ("str 0", *Iter); +} + +TEST(SmallSetTest, EqualityComparisonTest) { + SmallSet s1small; + SmallSet s2small; + SmallSet s3large; + SmallSet s4large; + + for (int i = 1; i < 5; i++) { + s1small.insert(i); + s2small.insert(5 - i); + s3large.insert(i); + } + for (int i = 1; i < 11; i++) + s4large.insert(i); + + EXPECT_EQ(s1small, s1small); + EXPECT_EQ(s3large, s3large); + + EXPECT_EQ(s1small, s2small); + EXPECT_EQ(s1small, s3large); + EXPECT_EQ(s2small, s3large); + + EXPECT_NE(s1small, s4large); + EXPECT_NE(s4large, s3large); +} + +TEST(SmallSetTest, Contains) { + SmallSet Set; + EXPECT_FALSE(Set.contains(0)); + EXPECT_FALSE(Set.contains(1)); + + Set.insert(0); + Set.insert(1); + EXPECT_TRUE(Set.contains(0)); + EXPECT_TRUE(Set.contains(1)); + + Set.insert(1); + EXPECT_TRUE(Set.contains(0)); + EXPECT_TRUE(Set.contains(1)); + + Set.erase(1); + EXPECT_TRUE(Set.contains(0)); + EXPECT_FALSE(Set.contains(1)); + + Set.insert(1); + Set.insert(2); + EXPECT_TRUE(Set.contains(0)); + EXPECT_TRUE(Set.contains(1)); + EXPECT_TRUE(Set.contains(2)); +} diff --git a/wpiutil/src/test/native/cpp/llvm/SmallStringTest.cpp b/wpiutil/src/test/native/cpp/llvm/SmallStringTest.cpp new file mode 100644 index 0000000000..afb155ae4c --- /dev/null +++ b/wpiutil/src/test/native/cpp/llvm/SmallStringTest.cpp @@ -0,0 +1,166 @@ +//===- llvm/unittest/ADT/SmallStringTest.cpp ------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// SmallString unit tests. +// +//===----------------------------------------------------------------------===// + +#include "wpi/SmallString.h" +#include "gtest/gtest.h" +#include +#include +#include + +using namespace wpi; + +namespace { + +// Test fixture class +class SmallStringTest : public testing::Test { +protected: + typedef SmallString<40> StringType; + + StringType theString; + + void assertEmpty(StringType & v) { + // Size tests + EXPECT_EQ(0u, v.size()); + EXPECT_TRUE(v.empty()); + // Iterator tests + EXPECT_TRUE(v.begin() == v.end()); + } +}; + +// New string test. +TEST_F(SmallStringTest, EmptyStringTest) { + SCOPED_TRACE("EmptyStringTest"); + assertEmpty(theString); + EXPECT_TRUE(theString.rbegin() == theString.rend()); +} + +TEST_F(SmallStringTest, AssignRepeated) { + theString.assign(3, 'a'); + EXPECT_EQ(3u, theString.size()); + EXPECT_STREQ("aaa", theString.c_str()); +} + +TEST_F(SmallStringTest, AssignIterPair) { + std::string_view abc = "abc"; + theString.assign(abc.begin(), abc.end()); + EXPECT_EQ(3u, theString.size()); + EXPECT_STREQ("abc", theString.c_str()); +} + +TEST_F(SmallStringTest, AssignStringView) { + std::string_view abc = "abc"; + theString.assign(abc); + EXPECT_EQ(3u, theString.size()); + EXPECT_STREQ("abc", theString.c_str()); +} + +TEST_F(SmallStringTest, AssignSmallVector) { + std::string_view abc = "abc"; + SmallVector abcVec(abc.begin(), abc.end()); + theString.assign(abcVec); + EXPECT_EQ(3u, theString.size()); + EXPECT_STREQ("abc", theString.c_str()); +} + +TEST_F(SmallStringTest, AssignStringViews) { + theString.assign({"abc", "def", "ghi"}); + EXPECT_EQ(9u, theString.size()); + EXPECT_STREQ("abcdefghi", theString.c_str()); +} + +TEST_F(SmallStringTest, AppendIterPair) { + std::string_view abc = "abc"; + theString.append(abc.begin(), abc.end()); + theString.append(abc.begin(), abc.end()); + EXPECT_EQ(6u, theString.size()); + EXPECT_STREQ("abcabc", theString.c_str()); +} + +TEST_F(SmallStringTest, AppendStringView) { + std::string_view abc = "abc"; + theString.append(abc); + theString.append(abc); + EXPECT_EQ(6u, theString.size()); + EXPECT_STREQ("abcabc", theString.c_str()); +} + +TEST_F(SmallStringTest, AppendSmallVector) { + std::string_view abc = "abc"; + SmallVector abcVec(abc.begin(), abc.end()); + theString.append(abcVec); + theString.append(abcVec); + EXPECT_EQ(6u, theString.size()); + EXPECT_STREQ("abcabc", theString.c_str()); +} + +TEST_F(SmallStringTest, AppendStringViews) { + theString.append({"abc", "def", "ghi"}); + EXPECT_EQ(9u, theString.size()); + EXPECT_STREQ("abcdefghi", theString.c_str()); + std::string_view Jkl = "jkl"; + std::string Mno = "mno"; + SmallString<4> Pqr("pqr"); + const char *Stu = "stu"; + theString.append({Jkl, Mno, Pqr, Stu}); + EXPECT_EQ(21u, theString.size()); + EXPECT_STREQ("abcdefghijklmnopqrstu", theString.c_str()); +} + +TEST_F(SmallStringTest, StringViewConversion) { + std::string_view abc = "abc"; + theString.assign(abc.begin(), abc.end()); + std::string_view theStringView = theString; + EXPECT_EQ("abc", theStringView); +} + +TEST_F(SmallStringTest, StdStringConversion) { + std::string_view abc = "abc"; + theString.assign(abc.begin(), abc.end()); + std::string theStdString = std::string(theString); + EXPECT_EQ("abc", theStdString); +} + +TEST_F(SmallStringTest, Find) { + theString = "hello"; + EXPECT_EQ(2U, theString.find('l')); + EXPECT_EQ(std::string_view::npos, theString.find('z')); + EXPECT_EQ(std::string_view::npos, theString.find("helloworld")); + EXPECT_EQ(0U, theString.find("hello")); + EXPECT_EQ(1U, theString.find("ello")); + EXPECT_EQ(std::string_view::npos, theString.find("zz")); + EXPECT_EQ(2U, theString.find("ll", 2)); + EXPECT_EQ(std::string_view::npos, theString.find("ll", 3)); + EXPECT_EQ(0U, theString.find("")); + + EXPECT_EQ(3U, theString.rfind('l')); + EXPECT_EQ(std::string_view::npos, theString.rfind('z')); + EXPECT_EQ(std::string_view::npos, theString.rfind("helloworld")); + EXPECT_EQ(0U, theString.rfind("hello")); + EXPECT_EQ(1U, theString.rfind("ello")); + EXPECT_EQ(std::string_view::npos, theString.rfind("zz")); + + EXPECT_EQ(2U, theString.find_first_of('l')); + EXPECT_EQ(1U, theString.find_first_of("el")); + EXPECT_EQ(std::string_view::npos, theString.find_first_of("xyz")); + + EXPECT_EQ(1U, theString.find_first_not_of('h')); + EXPECT_EQ(4U, theString.find_first_not_of("hel")); + EXPECT_EQ(std::string_view::npos, theString.find_first_not_of("hello")); + + theString = "hellx xello hell ello world foo bar hello"; + EXPECT_EQ(36U, theString.find("hello")); + EXPECT_EQ(28U, theString.find("foo")); + EXPECT_EQ(12U, theString.find("hell", 2)); + EXPECT_EQ(0U, theString.find("")); +} + +} // namespace diff --git a/wpiutil/src/test/native/cpp/llvm/SmallVectorTest.cpp b/wpiutil/src/test/native/cpp/llvm/SmallVectorTest.cpp new file mode 100644 index 0000000000..56c27d6c0c --- /dev/null +++ b/wpiutil/src/test/native/cpp/llvm/SmallVectorTest.cpp @@ -0,0 +1,1399 @@ +//===- llvm/unittest/ADT/SmallVectorTest.cpp ------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// SmallVector unit tests. +// +//===----------------------------------------------------------------------===// + +#include "wpi/SmallVector.h" +#include "wpi/span.h" +#include "wpi/Compiler.h" +#include "gtest/gtest.h" +#include +#include + +#if defined(__GNUC__) +#pragma GCC diagnostic ignored "-Wpedantic" +#endif + +using namespace wpi; + +namespace { + +/// A helper class that counts the total number of constructor and +/// destructor calls. +class Constructable { +private: + static int numConstructorCalls; + static int numMoveConstructorCalls; + static int numCopyConstructorCalls; + static int numDestructorCalls; + static int numAssignmentCalls; + static int numMoveAssignmentCalls; + static int numCopyAssignmentCalls; + + bool constructed; + int value; + +public: + Constructable() : constructed(true), value(0) { + ++numConstructorCalls; + } + + Constructable(int val) : constructed(true), value(val) { + ++numConstructorCalls; + } + + Constructable(const Constructable & src) : constructed(true) { + value = src.value; + ++numConstructorCalls; + ++numCopyConstructorCalls; + } + + Constructable(Constructable && src) : constructed(true) { + value = src.value; + src.value = 0; + ++numConstructorCalls; + ++numMoveConstructorCalls; + } + + ~Constructable() { + EXPECT_TRUE(constructed); + ++numDestructorCalls; + constructed = false; + } + + Constructable & operator=(const Constructable & src) { + EXPECT_TRUE(constructed); + value = src.value; + ++numAssignmentCalls; + ++numCopyAssignmentCalls; + return *this; + } + + Constructable & operator=(Constructable && src) { + EXPECT_TRUE(constructed); + value = src.value; + src.value = 0; + ++numAssignmentCalls; + ++numMoveAssignmentCalls; + return *this; + } + + int getValue() const { + return abs(value); + } + + static void reset() { + numConstructorCalls = 0; + numMoveConstructorCalls = 0; + numCopyConstructorCalls = 0; + numDestructorCalls = 0; + numAssignmentCalls = 0; + numMoveAssignmentCalls = 0; + numCopyAssignmentCalls = 0; + } + + static int getNumConstructorCalls() { + return numConstructorCalls; + } + + static int getNumMoveConstructorCalls() { + return numMoveConstructorCalls; + } + + static int getNumCopyConstructorCalls() { + return numCopyConstructorCalls; + } + + static int getNumDestructorCalls() { + return numDestructorCalls; + } + + static int getNumAssignmentCalls() { + return numAssignmentCalls; + } + + static int getNumMoveAssignmentCalls() { + return numMoveAssignmentCalls; + } + + static int getNumCopyAssignmentCalls() { + return numCopyAssignmentCalls; + } + + friend bool operator==(const Constructable & c0, const Constructable & c1) { + return c0.getValue() == c1.getValue(); + } + + friend bool LLVM_ATTRIBUTE_UNUSED + operator!=(const Constructable & c0, const Constructable & c1) { + return c0.getValue() != c1.getValue(); + } +}; + +int Constructable::numConstructorCalls; +int Constructable::numCopyConstructorCalls; +int Constructable::numMoveConstructorCalls; +int Constructable::numDestructorCalls; +int Constructable::numAssignmentCalls; +int Constructable::numCopyAssignmentCalls; +int Constructable::numMoveAssignmentCalls; + +struct NonCopyable { + NonCopyable() {} + NonCopyable(NonCopyable &&) {} + NonCopyable &operator=(NonCopyable &&) { return *this; } +private: + NonCopyable(const NonCopyable &) = delete; + NonCopyable &operator=(const NonCopyable &) = delete; +}; + +LLVM_ATTRIBUTE_USED void CompileTest() { + SmallVector V; + V.resize(42); +} + +class SmallVectorTestBase : public testing::Test { +protected: + void SetUp() override { Constructable::reset(); } + + template + void assertEmpty(VectorT & v) { + // Size tests + EXPECT_EQ(0u, v.size()); + EXPECT_TRUE(v.empty()); + + // Iterator tests + EXPECT_TRUE(v.begin() == v.end()); + } + + // Assert that v contains the specified values, in order. + template + void assertValuesInOrder(VectorT & v, size_t size, ...) { + EXPECT_EQ(size, v.size()); + + va_list ap; + va_start(ap, size); + for (size_t i = 0; i < size; ++i) { + int value = va_arg(ap, int); + EXPECT_EQ(value, v[i].getValue()); + } + + va_end(ap); + } + + // Generate a sequence of values to initialize the vector. + template + void makeSequence(VectorT & v, int start, int end) { + for (int i = start; i <= end; ++i) { + v.push_back(Constructable(i)); + } + } +}; + +// Test fixture class +template +class SmallVectorTest : public SmallVectorTestBase { +protected: + VectorT theVector; + VectorT otherVector; +}; + + +typedef ::testing::Types, + SmallVector, + SmallVector, + SmallVector, + SmallVector + > SmallVectorTestTypes; +TYPED_TEST_SUITE(SmallVectorTest, SmallVectorTestTypes, ); + +// Constructor test. +TYPED_TEST(SmallVectorTest, ConstructorNonIterTest) { + SCOPED_TRACE("ConstructorTest"); + this->theVector = SmallVector(2, 2); + this->assertValuesInOrder(this->theVector, 2u, 2, 2); +} + +// Constructor test. +TYPED_TEST(SmallVectorTest, ConstructorIterTest) { + SCOPED_TRACE("ConstructorTest"); + int arr[] = {1, 2, 3}; + this->theVector = + SmallVector(std::begin(arr), std::end(arr)); + this->assertValuesInOrder(this->theVector, 3u, 1, 2, 3); +} + +// New vector test. +TYPED_TEST(SmallVectorTest, EmptyVectorTest) { + SCOPED_TRACE("EmptyVectorTest"); + this->assertEmpty(this->theVector); + EXPECT_TRUE(this->theVector.rbegin() == this->theVector.rend()); + EXPECT_EQ(0, Constructable::getNumConstructorCalls()); + EXPECT_EQ(0, Constructable::getNumDestructorCalls()); +} + +// Simple insertions and deletions. +TYPED_TEST(SmallVectorTest, PushPopTest) { + SCOPED_TRACE("PushPopTest"); + + // Track whether the vector will potentially have to grow. + bool RequiresGrowth = this->theVector.capacity() < 3; + + // Push an element + this->theVector.push_back(Constructable(1)); + + // Size tests + this->assertValuesInOrder(this->theVector, 1u, 1); + EXPECT_FALSE(this->theVector.begin() == this->theVector.end()); + EXPECT_FALSE(this->theVector.empty()); + + // Push another element + this->theVector.push_back(Constructable(2)); + this->assertValuesInOrder(this->theVector, 2u, 1, 2); + + // Insert at beginning. Reserve space to avoid reference invalidation from + // this->theVector[1]. + this->theVector.reserve(this->theVector.size() + 1); + this->theVector.insert(this->theVector.begin(), this->theVector[1]); + this->assertValuesInOrder(this->theVector, 3u, 2, 1, 2); + + // Pop one element + this->theVector.pop_back(); + this->assertValuesInOrder(this->theVector, 2u, 2, 1); + + // Pop remaining elements + this->theVector.pop_back_n(2); + this->assertEmpty(this->theVector); + + // Check number of constructor calls. Should be 2 for each list element, + // one for the argument to push_back, one for the argument to insert, + // and one for the list element itself. + if (!RequiresGrowth) { + EXPECT_EQ(5, Constructable::getNumConstructorCalls()); + EXPECT_EQ(5, Constructable::getNumDestructorCalls()); + } else { + // If we had to grow the vector, these only have a lower bound, but should + // always be equal. + EXPECT_LE(5, Constructable::getNumConstructorCalls()); + EXPECT_EQ(Constructable::getNumConstructorCalls(), + Constructable::getNumDestructorCalls()); + } +} + +// Clear test. +TYPED_TEST(SmallVectorTest, ClearTest) { + SCOPED_TRACE("ClearTest"); + + this->theVector.reserve(2); + this->makeSequence(this->theVector, 1, 2); + this->theVector.clear(); + + this->assertEmpty(this->theVector); + EXPECT_EQ(4, Constructable::getNumConstructorCalls()); + EXPECT_EQ(4, Constructable::getNumDestructorCalls()); +} + +// Resize smaller test. +TYPED_TEST(SmallVectorTest, ResizeShrinkTest) { + SCOPED_TRACE("ResizeShrinkTest"); + + this->theVector.reserve(3); + this->makeSequence(this->theVector, 1, 3); + this->theVector.resize(1); + + this->assertValuesInOrder(this->theVector, 1u, 1); + EXPECT_EQ(6, Constructable::getNumConstructorCalls()); + EXPECT_EQ(5, Constructable::getNumDestructorCalls()); +} + +// Resize bigger test. +TYPED_TEST(SmallVectorTest, ResizeGrowTest) { + SCOPED_TRACE("ResizeGrowTest"); + + this->theVector.resize(2); + + EXPECT_EQ(2, Constructable::getNumConstructorCalls()); + EXPECT_EQ(0, Constructable::getNumDestructorCalls()); + EXPECT_EQ(2u, this->theVector.size()); +} + +TYPED_TEST(SmallVectorTest, ResizeWithElementsTest) { + this->theVector.resize(2); + + Constructable::reset(); + + this->theVector.resize(4); + + size_t Ctors = Constructable::getNumConstructorCalls(); + EXPECT_TRUE(Ctors == 2 || Ctors == 4); + size_t MoveCtors = Constructable::getNumMoveConstructorCalls(); + EXPECT_TRUE(MoveCtors == 0 || MoveCtors == 2); + size_t Dtors = Constructable::getNumDestructorCalls(); + EXPECT_TRUE(Dtors == 0 || Dtors == 2); +} + +// Resize with fill value. +TYPED_TEST(SmallVectorTest, ResizeFillTest) { + SCOPED_TRACE("ResizeFillTest"); + + this->theVector.resize(3, Constructable(77)); + this->assertValuesInOrder(this->theVector, 3u, 77, 77, 77); +} + +TEST(SmallVectorTest, ResizeForOverwrite) { + { + // Heap allocated storage. + SmallVector V; + V.push_back(5U); + V.pop_back(); + V.resize_for_overwrite(V.size() + 1U); + EXPECT_EQ(5U, V.back()); + V.pop_back(); + V.resize(V.size() + 1); + EXPECT_EQ(0U, V.back()); + } + { + // Inline storage. + SmallVector V; + V.push_back(5U); + V.pop_back(); + V.resize_for_overwrite(V.size() + 1U); + EXPECT_EQ(5U, V.back()); + V.pop_back(); + V.resize(V.size() + 1); + EXPECT_EQ(0U, V.back()); + } +} + +// Overflow past fixed size. +TYPED_TEST(SmallVectorTest, OverflowTest) { + SCOPED_TRACE("OverflowTest"); + + // Push more elements than the fixed size. + this->makeSequence(this->theVector, 1, 10); + + // Test size and values. + EXPECT_EQ(10u, this->theVector.size()); + for (int i = 0; i < 10; ++i) { + EXPECT_EQ(i+1, this->theVector[i].getValue()); + } + + // Now resize back to fixed size. + this->theVector.resize(1); + + this->assertValuesInOrder(this->theVector, 1u, 1); +} + +// Iteration tests. +TYPED_TEST(SmallVectorTest, IterationTest) { + this->makeSequence(this->theVector, 1, 2); + + // Forward Iteration + typename TypeParam::iterator it = this->theVector.begin(); + EXPECT_TRUE(*it == this->theVector.front()); + EXPECT_TRUE(*it == this->theVector[0]); + EXPECT_EQ(1, it->getValue()); + ++it; + EXPECT_TRUE(*it == this->theVector[1]); + EXPECT_TRUE(*it == this->theVector.back()); + EXPECT_EQ(2, it->getValue()); + ++it; + EXPECT_TRUE(it == this->theVector.end()); + --it; + EXPECT_TRUE(*it == this->theVector[1]); + EXPECT_EQ(2, it->getValue()); + --it; + EXPECT_TRUE(*it == this->theVector[0]); + EXPECT_EQ(1, it->getValue()); + + // Reverse Iteration + typename TypeParam::reverse_iterator rit = this->theVector.rbegin(); + EXPECT_TRUE(*rit == this->theVector[1]); + EXPECT_EQ(2, rit->getValue()); + ++rit; + EXPECT_TRUE(*rit == this->theVector[0]); + EXPECT_EQ(1, rit->getValue()); + ++rit; + EXPECT_TRUE(rit == this->theVector.rend()); + --rit; + EXPECT_TRUE(*rit == this->theVector[0]); + EXPECT_EQ(1, rit->getValue()); + --rit; + EXPECT_TRUE(*rit == this->theVector[1]); + EXPECT_EQ(2, rit->getValue()); +} + +// Swap test. +TYPED_TEST(SmallVectorTest, SwapTest) { + SCOPED_TRACE("SwapTest"); + + this->makeSequence(this->theVector, 1, 2); + std::swap(this->theVector, this->otherVector); + + this->assertEmpty(this->theVector); + this->assertValuesInOrder(this->otherVector, 2u, 1, 2); +} + +// Append test +TYPED_TEST(SmallVectorTest, AppendTest) { + SCOPED_TRACE("AppendTest"); + + this->makeSequence(this->otherVector, 2, 3); + + this->theVector.push_back(Constructable(1)); + this->theVector.append(this->otherVector.begin(), this->otherVector.end()); + + this->assertValuesInOrder(this->theVector, 3u, 1, 2, 3); +} + +// Append repeated test +TYPED_TEST(SmallVectorTest, AppendRepeatedTest) { + SCOPED_TRACE("AppendRepeatedTest"); + + this->theVector.push_back(Constructable(1)); + this->theVector.append(2, Constructable(77)); + this->assertValuesInOrder(this->theVector, 3u, 1, 77, 77); +} + +// Append test +TYPED_TEST(SmallVectorTest, AppendNonIterTest) { + SCOPED_TRACE("AppendRepeatedTest"); + + this->theVector.push_back(Constructable(1)); + this->theVector.append(2, 7); + this->assertValuesInOrder(this->theVector, 3u, 1, 7, 7); +} + +struct output_iterator { + typedef std::output_iterator_tag iterator_category; + typedef int value_type; + typedef int difference_type; + typedef value_type *pointer; + typedef value_type &reference; + operator int() { return 2; } + operator Constructable() { return 7; } +}; + +TYPED_TEST(SmallVectorTest, AppendRepeatedNonForwardIterator) { + SCOPED_TRACE("AppendRepeatedTest"); + + this->theVector.push_back(Constructable(1)); + this->theVector.append(output_iterator(), output_iterator()); + this->assertValuesInOrder(this->theVector, 3u, 1, 7, 7); +} + +TYPED_TEST(SmallVectorTest, AppendSmallVector) { + SCOPED_TRACE("AppendSmallVector"); + + SmallVector otherVector = {7, 7}; + this->theVector.push_back(Constructable(1)); + this->theVector.append(otherVector); + this->assertValuesInOrder(this->theVector, 3u, 1, 7, 7); +} + +// Assign test +TYPED_TEST(SmallVectorTest, AssignTest) { + SCOPED_TRACE("AssignTest"); + + this->theVector.push_back(Constructable(1)); + this->theVector.assign(2, Constructable(77)); + this->assertValuesInOrder(this->theVector, 2u, 77, 77); +} + +// Assign test +TYPED_TEST(SmallVectorTest, AssignRangeTest) { + SCOPED_TRACE("AssignTest"); + + this->theVector.push_back(Constructable(1)); + int arr[] = {1, 2, 3}; + this->theVector.assign(std::begin(arr), std::end(arr)); + this->assertValuesInOrder(this->theVector, 3u, 1, 2, 3); +} + +// Assign test +TYPED_TEST(SmallVectorTest, AssignNonIterTest) { + SCOPED_TRACE("AssignTest"); + + this->theVector.push_back(Constructable(1)); + this->theVector.assign(2, 7); + this->assertValuesInOrder(this->theVector, 2u, 7, 7); +} + +TYPED_TEST(SmallVectorTest, AssignSmallVector) { + SCOPED_TRACE("AssignSmallVector"); + + SmallVector otherVector = {7, 7}; + this->theVector.push_back(Constructable(1)); + this->theVector.assign(otherVector); + this->assertValuesInOrder(this->theVector, 2u, 7, 7); +} + +// Move-assign test +TYPED_TEST(SmallVectorTest, MoveAssignTest) { + SCOPED_TRACE("MoveAssignTest"); + + // Set up our vector with a single element, but enough capacity for 4. + this->theVector.reserve(4); + this->theVector.push_back(Constructable(1)); + + // Set up the other vector with 2 elements. + this->otherVector.push_back(Constructable(2)); + this->otherVector.push_back(Constructable(3)); + + // Move-assign from the other vector. + this->theVector = std::move(this->otherVector); + + // Make sure we have the right result. + this->assertValuesInOrder(this->theVector, 2u, 2, 3); + + // Make sure the # of constructor/destructor calls line up. There + // are two live objects after clearing the other vector. + this->otherVector.clear(); + EXPECT_EQ(Constructable::getNumConstructorCalls()-2, + Constructable::getNumDestructorCalls()); + + // There shouldn't be any live objects any more. + this->theVector.clear(); + EXPECT_EQ(Constructable::getNumConstructorCalls(), + Constructable::getNumDestructorCalls()); +} + +// Erase a single element +TYPED_TEST(SmallVectorTest, EraseTest) { + SCOPED_TRACE("EraseTest"); + + this->makeSequence(this->theVector, 1, 3); + const auto &theConstVector = this->theVector; + this->theVector.erase(theConstVector.begin()); + this->assertValuesInOrder(this->theVector, 2u, 2, 3); +} + +// Erase a range of elements +TYPED_TEST(SmallVectorTest, EraseRangeTest) { + SCOPED_TRACE("EraseRangeTest"); + + this->makeSequence(this->theVector, 1, 3); + const auto &theConstVector = this->theVector; + this->theVector.erase(theConstVector.begin(), theConstVector.begin() + 2); + this->assertValuesInOrder(this->theVector, 1u, 3); +} + +// Insert a single element. +TYPED_TEST(SmallVectorTest, InsertTest) { + SCOPED_TRACE("InsertTest"); + + this->makeSequence(this->theVector, 1, 3); + typename TypeParam::iterator I = + this->theVector.insert(this->theVector.begin() + 1, Constructable(77)); + EXPECT_EQ(this->theVector.begin() + 1, I); + this->assertValuesInOrder(this->theVector, 4u, 1, 77, 2, 3); +} + +// Insert a copy of a single element. +TYPED_TEST(SmallVectorTest, InsertCopy) { + SCOPED_TRACE("InsertTest"); + + this->makeSequence(this->theVector, 1, 3); + Constructable C(77); + typename TypeParam::iterator I = + this->theVector.insert(this->theVector.begin() + 1, C); + EXPECT_EQ(this->theVector.begin() + 1, I); + this->assertValuesInOrder(this->theVector, 4u, 1, 77, 2, 3); +} + +// Insert repeated elements. +TYPED_TEST(SmallVectorTest, InsertRepeatedTest) { + SCOPED_TRACE("InsertRepeatedTest"); + + this->makeSequence(this->theVector, 1, 4); + Constructable::reset(); + auto I = + this->theVector.insert(this->theVector.begin() + 1, 2, Constructable(16)); + // Move construct the top element into newly allocated space, and optionally + // reallocate the whole buffer, move constructing into it. + // FIXME: This is inefficient, we shouldn't move things into newly allocated + // space, then move them up/around, there should only be 2 or 4 move + // constructions here. + EXPECT_TRUE(Constructable::getNumMoveConstructorCalls() == 2 || + Constructable::getNumMoveConstructorCalls() == 6); + // Move assign the next two to shift them up and make a gap. + EXPECT_EQ(1, Constructable::getNumMoveAssignmentCalls()); + // Copy construct the two new elements from the parameter. + EXPECT_EQ(2, Constructable::getNumCopyAssignmentCalls()); + // All without any copy construction. + EXPECT_EQ(0, Constructable::getNumCopyConstructorCalls()); + EXPECT_EQ(this->theVector.begin() + 1, I); + this->assertValuesInOrder(this->theVector, 6u, 1, 16, 16, 2, 3, 4); +} + +TYPED_TEST(SmallVectorTest, InsertRepeatedNonIterTest) { + SCOPED_TRACE("InsertRepeatedTest"); + + this->makeSequence(this->theVector, 1, 4); + Constructable::reset(); + auto I = this->theVector.insert(this->theVector.begin() + 1, 2, 7); + EXPECT_EQ(this->theVector.begin() + 1, I); + this->assertValuesInOrder(this->theVector, 6u, 1, 7, 7, 2, 3, 4); +} + +TYPED_TEST(SmallVectorTest, InsertRepeatedAtEndTest) { + SCOPED_TRACE("InsertRepeatedTest"); + + this->makeSequence(this->theVector, 1, 4); + Constructable::reset(); + auto I = this->theVector.insert(this->theVector.end(), 2, Constructable(16)); + // Just copy construct them into newly allocated space + EXPECT_EQ(2, Constructable::getNumCopyConstructorCalls()); + // Move everything across if reallocation is needed. + EXPECT_TRUE(Constructable::getNumMoveConstructorCalls() == 0 || + Constructable::getNumMoveConstructorCalls() == 4); + // Without ever moving or copying anything else. + EXPECT_EQ(0, Constructable::getNumCopyAssignmentCalls()); + EXPECT_EQ(0, Constructable::getNumMoveAssignmentCalls()); + + EXPECT_EQ(this->theVector.begin() + 4, I); + this->assertValuesInOrder(this->theVector, 6u, 1, 2, 3, 4, 16, 16); +} + +TYPED_TEST(SmallVectorTest, InsertRepeatedEmptyTest) { + SCOPED_TRACE("InsertRepeatedTest"); + + this->makeSequence(this->theVector, 10, 15); + + // Empty insert. + EXPECT_EQ(this->theVector.end(), + this->theVector.insert(this->theVector.end(), + 0, Constructable(42))); + EXPECT_EQ(this->theVector.begin() + 1, + this->theVector.insert(this->theVector.begin() + 1, + 0, Constructable(42))); +} + +// Insert range. +TYPED_TEST(SmallVectorTest, InsertRangeTest) { + SCOPED_TRACE("InsertRangeTest"); + + Constructable Arr[3] = + { Constructable(77), Constructable(77), Constructable(77) }; + + this->makeSequence(this->theVector, 1, 3); + Constructable::reset(); + auto I = this->theVector.insert(this->theVector.begin() + 1, Arr, Arr + 3); + // Move construct the top 3 elements into newly allocated space. + // Possibly move the whole sequence into new space first. + // FIXME: This is inefficient, we shouldn't move things into newly allocated + // space, then move them up/around, there should only be 2 or 3 move + // constructions here. + EXPECT_TRUE(Constructable::getNumMoveConstructorCalls() == 2 || + Constructable::getNumMoveConstructorCalls() == 5); + // Copy assign the lower 2 new elements into existing space. + EXPECT_EQ(2, Constructable::getNumCopyAssignmentCalls()); + // Copy construct the third element into newly allocated space. + EXPECT_EQ(1, Constructable::getNumCopyConstructorCalls()); + EXPECT_EQ(this->theVector.begin() + 1, I); + this->assertValuesInOrder(this->theVector, 6u, 1, 77, 77, 77, 2, 3); +} + + +TYPED_TEST(SmallVectorTest, InsertRangeAtEndTest) { + SCOPED_TRACE("InsertRangeTest"); + + Constructable Arr[3] = + { Constructable(77), Constructable(77), Constructable(77) }; + + this->makeSequence(this->theVector, 1, 3); + + // Insert at end. + Constructable::reset(); + auto I = this->theVector.insert(this->theVector.end(), Arr, Arr+3); + // Copy construct the 3 elements into new space at the top. + EXPECT_EQ(3, Constructable::getNumCopyConstructorCalls()); + // Don't copy/move anything else. + EXPECT_EQ(0, Constructable::getNumCopyAssignmentCalls()); + // Reallocation might occur, causing all elements to be moved into the new + // buffer. + EXPECT_TRUE(Constructable::getNumMoveConstructorCalls() == 0 || + Constructable::getNumMoveConstructorCalls() == 3); + EXPECT_EQ(0, Constructable::getNumMoveAssignmentCalls()); + EXPECT_EQ(this->theVector.begin() + 3, I); + this->assertValuesInOrder(this->theVector, 6u, + 1, 2, 3, 77, 77, 77); +} + +TYPED_TEST(SmallVectorTest, InsertEmptyRangeTest) { + SCOPED_TRACE("InsertRangeTest"); + + this->makeSequence(this->theVector, 1, 3); + + // Empty insert. + EXPECT_EQ(this->theVector.end(), + this->theVector.insert(this->theVector.end(), + this->theVector.begin(), + this->theVector.begin())); + EXPECT_EQ(this->theVector.begin() + 1, + this->theVector.insert(this->theVector.begin() + 1, + this->theVector.begin(), + this->theVector.begin())); +} + +// Comparison tests. +TYPED_TEST(SmallVectorTest, ComparisonTest) { + SCOPED_TRACE("ComparisonTest"); + + this->makeSequence(this->theVector, 1, 3); + this->makeSequence(this->otherVector, 1, 3); + + EXPECT_TRUE(this->theVector == this->otherVector); + EXPECT_FALSE(this->theVector != this->otherVector); + + this->otherVector.clear(); + this->makeSequence(this->otherVector, 2, 4); + + EXPECT_FALSE(this->theVector == this->otherVector); + EXPECT_TRUE(this->theVector != this->otherVector); +} + +// Constant vector tests. +TYPED_TEST(SmallVectorTest, ConstVectorTest) { + const TypeParam constVector; + + EXPECT_EQ(0u, constVector.size()); + EXPECT_TRUE(constVector.empty()); + EXPECT_TRUE(constVector.begin() == constVector.end()); +} + +// Direct array access. +TYPED_TEST(SmallVectorTest, DirectVectorTest) { + EXPECT_EQ(0u, this->theVector.size()); + this->theVector.reserve(4); + EXPECT_LE(4u, this->theVector.capacity()); + EXPECT_EQ(0, Constructable::getNumConstructorCalls()); + this->theVector.push_back(1); + this->theVector.push_back(2); + this->theVector.push_back(3); + this->theVector.push_back(4); + EXPECT_EQ(4u, this->theVector.size()); + EXPECT_EQ(8, Constructable::getNumConstructorCalls()); + EXPECT_EQ(1, this->theVector[0].getValue()); + EXPECT_EQ(2, this->theVector[1].getValue()); + EXPECT_EQ(3, this->theVector[2].getValue()); + EXPECT_EQ(4, this->theVector[3].getValue()); +} + +TYPED_TEST(SmallVectorTest, IteratorTest) { + std::list L; + this->theVector.insert(this->theVector.end(), L.begin(), L.end()); +} + +template class DualSmallVectorsTest; + +template +class DualSmallVectorsTest> : public SmallVectorTestBase { +protected: + VectorT1 theVector; + VectorT2 otherVector; + + template + static unsigned NumBuiltinElts(const SmallVector&) { return N; } +}; + +typedef ::testing::Types< + // Small mode -> Small mode. + std::pair, SmallVector>, + // Small mode -> Big mode. + std::pair, SmallVector>, + // Big mode -> Small mode. + std::pair, SmallVector>, + // Big mode -> Big mode. + std::pair, SmallVector> + > DualSmallVectorTestTypes; + +TYPED_TEST_SUITE(DualSmallVectorsTest, DualSmallVectorTestTypes, ); + +TYPED_TEST(DualSmallVectorsTest, MoveAssignment) { + SCOPED_TRACE("MoveAssignTest-DualVectorTypes"); + + // Set up our vector with four elements. + for (unsigned I = 0; I < 4; ++I) + this->otherVector.push_back(Constructable(I)); + + const Constructable *OrigDataPtr = this->otherVector.data(); + + // Move-assign from the other vector. + this->theVector = + std::move(static_cast&>(this->otherVector)); + + // Make sure we have the right result. + this->assertValuesInOrder(this->theVector, 4u, 0, 1, 2, 3); + + // Make sure the # of constructor/destructor calls line up. There + // are two live objects after clearing the other vector. + this->otherVector.clear(); + EXPECT_EQ(Constructable::getNumConstructorCalls()-4, + Constructable::getNumDestructorCalls()); + + // If the source vector (otherVector) was in small-mode, assert that we just + // moved the data pointer over. + EXPECT_TRUE(this->NumBuiltinElts(this->otherVector) == 4 || + this->theVector.data() == OrigDataPtr); + + // There shouldn't be any live objects any more. + this->theVector.clear(); + EXPECT_EQ(Constructable::getNumConstructorCalls(), + Constructable::getNumDestructorCalls()); + + // We shouldn't have copied anything in this whole process. + EXPECT_EQ(Constructable::getNumCopyConstructorCalls(), 0); +} + +struct notassignable { + int &x; + notassignable(int &x) : x(x) {} +}; + +TEST(SmallVectorCustomTest, NoAssignTest) { + int x = 0; + SmallVector vec; + vec.push_back(notassignable(x)); + x = 42; + EXPECT_EQ(42, vec.pop_back_val().x); +} + +struct MovedFrom { + bool hasValue; + MovedFrom() : hasValue(true) { + } + MovedFrom(MovedFrom&& m) : hasValue(m.hasValue) { + m.hasValue = false; + } + MovedFrom &operator=(MovedFrom&& m) { + hasValue = m.hasValue; + m.hasValue = false; + return *this; + } +}; + +TEST(SmallVectorTest, MidInsert) { + SmallVector v; + v.push_back(MovedFrom()); + v.insert(v.begin(), MovedFrom()); + for (MovedFrom &m : v) + EXPECT_TRUE(m.hasValue); +} + +enum EmplaceableArgState { + EAS_Defaulted, + EAS_Arg, + EAS_LValue, + EAS_RValue, + EAS_Failure +}; +template struct EmplaceableArg { + EmplaceableArgState State; + EmplaceableArg() : State(EAS_Defaulted) {} + EmplaceableArg(EmplaceableArg &&X) + : State(X.State == EAS_Arg ? EAS_RValue : EAS_Failure) {} + EmplaceableArg(EmplaceableArg &X) + : State(X.State == EAS_Arg ? EAS_LValue : EAS_Failure) {} + + explicit EmplaceableArg(bool) : State(EAS_Arg) {} + +private: + EmplaceableArg &operator=(EmplaceableArg &&) = delete; + EmplaceableArg &operator=(const EmplaceableArg &) = delete; +}; + +enum EmplaceableState { ES_Emplaced, ES_Moved }; +struct Emplaceable { + EmplaceableArg<0> A0; + EmplaceableArg<1> A1; + EmplaceableArg<2> A2; + EmplaceableArg<3> A3; + EmplaceableState State; + + Emplaceable() : State(ES_Emplaced) {} + + template + explicit Emplaceable(A0Ty &&A0) + : A0(std::forward(A0)), State(ES_Emplaced) {} + + template + Emplaceable(A0Ty &&A0, A1Ty &&A1) + : A0(std::forward(A0)), A1(std::forward(A1)), + State(ES_Emplaced) {} + + template + Emplaceable(A0Ty &&A0, A1Ty &&A1, A2Ty &&A2) + : A0(std::forward(A0)), A1(std::forward(A1)), + A2(std::forward(A2)), State(ES_Emplaced) {} + + template + Emplaceable(A0Ty &&A0, A1Ty &&A1, A2Ty &&A2, A3Ty &&A3) + : A0(std::forward(A0)), A1(std::forward(A1)), + A2(std::forward(A2)), A3(std::forward(A3)), + State(ES_Emplaced) {} + + Emplaceable(Emplaceable &&) : State(ES_Moved) {} + Emplaceable &operator=(Emplaceable &&) { + State = ES_Moved; + return *this; + } + +private: + Emplaceable(const Emplaceable &) = delete; + Emplaceable &operator=(const Emplaceable &) = delete; +}; + +TEST(SmallVectorTest, EmplaceBack) { + EmplaceableArg<0> A0(true); + EmplaceableArg<1> A1(true); + EmplaceableArg<2> A2(true); + EmplaceableArg<3> A3(true); + { + SmallVector V; + Emplaceable &back = V.emplace_back(); + EXPECT_TRUE(&back == &V.back()); + EXPECT_TRUE(V.size() == 1); + EXPECT_TRUE(back.State == ES_Emplaced); + EXPECT_TRUE(back.A0.State == EAS_Defaulted); + EXPECT_TRUE(back.A1.State == EAS_Defaulted); + EXPECT_TRUE(back.A2.State == EAS_Defaulted); + EXPECT_TRUE(back.A3.State == EAS_Defaulted); + } + { + SmallVector V; + Emplaceable &back = V.emplace_back(std::move(A0)); + EXPECT_TRUE(&back == &V.back()); + EXPECT_TRUE(V.size() == 1); + EXPECT_TRUE(back.State == ES_Emplaced); + EXPECT_TRUE(back.A0.State == EAS_RValue); + EXPECT_TRUE(back.A1.State == EAS_Defaulted); + EXPECT_TRUE(back.A2.State == EAS_Defaulted); + EXPECT_TRUE(back.A3.State == EAS_Defaulted); + } + { + SmallVector V; + Emplaceable &back = V.emplace_back(A0); + EXPECT_TRUE(&back == &V.back()); + EXPECT_TRUE(V.size() == 1); + EXPECT_TRUE(back.State == ES_Emplaced); + EXPECT_TRUE(back.A0.State == EAS_LValue); + EXPECT_TRUE(back.A1.State == EAS_Defaulted); + EXPECT_TRUE(back.A2.State == EAS_Defaulted); + EXPECT_TRUE(back.A3.State == EAS_Defaulted); + } + { + SmallVector V; + Emplaceable &back = V.emplace_back(A0, A1); + EXPECT_TRUE(&back == &V.back()); + EXPECT_TRUE(V.size() == 1); + EXPECT_TRUE(back.State == ES_Emplaced); + EXPECT_TRUE(back.A0.State == EAS_LValue); + EXPECT_TRUE(back.A1.State == EAS_LValue); + EXPECT_TRUE(back.A2.State == EAS_Defaulted); + EXPECT_TRUE(back.A3.State == EAS_Defaulted); + } + { + SmallVector V; + Emplaceable &back = V.emplace_back(std::move(A0), std::move(A1)); + EXPECT_TRUE(&back == &V.back()); + EXPECT_TRUE(V.size() == 1); + EXPECT_TRUE(back.State == ES_Emplaced); + EXPECT_TRUE(back.A0.State == EAS_RValue); + EXPECT_TRUE(back.A1.State == EAS_RValue); + EXPECT_TRUE(back.A2.State == EAS_Defaulted); + EXPECT_TRUE(back.A3.State == EAS_Defaulted); + } + { + SmallVector V; + Emplaceable &back = V.emplace_back(std::move(A0), A1, std::move(A2), A3); + EXPECT_TRUE(&back == &V.back()); + EXPECT_TRUE(V.size() == 1); + EXPECT_TRUE(back.State == ES_Emplaced); + EXPECT_TRUE(back.A0.State == EAS_RValue); + EXPECT_TRUE(back.A1.State == EAS_LValue); + EXPECT_TRUE(back.A2.State == EAS_RValue); + EXPECT_TRUE(back.A3.State == EAS_LValue); + } + { + SmallVector V; + V.emplace_back(); + V.emplace_back(42); + EXPECT_EQ(2U, V.size()); + EXPECT_EQ(0, V[0]); + EXPECT_EQ(42, V[1]); + } +} + +TEST(SmallVectorTest, DefaultInlinedElements) { + SmallVector V; + EXPECT_TRUE(V.empty()); + V.push_back(7); + EXPECT_EQ(V[0], 7); + + // Check that at least a couple layers of nested SmallVector's are allowed + // by the default inline elements policy. This pattern happens in practice + // with some frequency, and it seems fairly harmless even though each layer of + // SmallVector's will grow the total sizeof by a vector header beyond the + // "preferred" maximum sizeof. + SmallVector>> NestedV; + NestedV.emplace_back().emplace_back().emplace_back(42); + EXPECT_EQ(NestedV[0][0][0], 42); +} + +template +class SmallVectorReferenceInvalidationTest : public SmallVectorTestBase { +protected: + const char *AssertionMessage = + "Attempting to reference an element of the vector in an operation \" " + "\"that invalidates it"; + + VectorT V; + + template + static unsigned NumBuiltinElts(const SmallVector &) { + return N; + } + + template static bool isValueType() { + return std::is_same::value; + } + + void SetUp() override { + SmallVectorTestBase::SetUp(); + + // Fill up the small size so that insertions move the elements. + for (int I = 0, E = NumBuiltinElts(V); I != E; ++I) + V.emplace_back(I + 1); + } +}; + +// Test one type that's trivially copyable (int) and one that isn't +// (Constructable) since reference invalidation may be fixed differently for +// each. +using SmallVectorReferenceInvalidationTestTypes = + ::testing::Types, SmallVector>; + +TYPED_TEST_SUITE(SmallVectorReferenceInvalidationTest, + SmallVectorReferenceInvalidationTestTypes, ); + +TYPED_TEST(SmallVectorReferenceInvalidationTest, PushBack) { + // Note: setup adds [1, 2, ...] to V until it's at capacity in small mode. + auto &V = this->V; + int N = this->NumBuiltinElts(V); + + // Push back a reference to last element when growing from small storage. + V.push_back(V.back()); + EXPECT_EQ(N, V.back()); + + // Check that the old value is still there (not moved away). + EXPECT_EQ(N, V[V.size() - 2]); + + // Fill storage again. + V.back() = V.size(); + while (V.size() < V.capacity()) + V.push_back(V.size() + 1); + + // Push back a reference to last element when growing from large storage. + V.push_back(V.back()); + EXPECT_EQ(int(V.size()) - 1, V.back()); +} + +TYPED_TEST(SmallVectorReferenceInvalidationTest, PushBackMoved) { + // Note: setup adds [1, 2, ...] to V until it's at capacity in small mode. + auto &V = this->V; + int N = this->NumBuiltinElts(V); + + // Push back a reference to last element when growing from small storage. + V.push_back(std::move(V.back())); + EXPECT_EQ(N, V.back()); + if (this->template isValueType()) { + // Check that the value was moved (not copied). + EXPECT_EQ(0, V[V.size() - 2]); + } + + // Fill storage again. + V.back() = V.size(); + while (V.size() < V.capacity()) + V.push_back(V.size() + 1); + + // Push back a reference to last element when growing from large storage. + V.push_back(std::move(V.back())); + + // Check the values. + EXPECT_EQ(int(V.size()) - 1, V.back()); + if (this->template isValueType()) { + // Check the value got moved out. + EXPECT_EQ(0, V[V.size() - 2]); + } +} + +TYPED_TEST(SmallVectorReferenceInvalidationTest, Resize) { + auto &V = this->V; + (void)V; + int N = this->NumBuiltinElts(V); + V.resize(N + 1, V.back()); + EXPECT_EQ(N, V.back()); + + // Resize to add enough elements that V will grow again. If reference + // invalidation breaks in the future, sanitizers should be able to catch a + // use-after-free here. + V.resize(V.capacity() + 1, V.front()); + EXPECT_EQ(1, V.back()); +} + +TYPED_TEST(SmallVectorReferenceInvalidationTest, Append) { + auto &V = this->V; + (void)V; + V.append(1, V.back()); + int N = this->NumBuiltinElts(V); + EXPECT_EQ(N, V[N - 1]); + + // Append enough more elements that V will grow again. This tests growing + // when already in large mode. + // + // If reference invalidation breaks in the future, sanitizers should be able + // to catch a use-after-free here. + V.append(V.capacity() - V.size() + 1, V.front()); + EXPECT_EQ(1, V.back()); +} + +TYPED_TEST(SmallVectorReferenceInvalidationTest, AppendRange) { + auto &V = this->V; + (void)V; +#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST + EXPECT_DEATH(V.append(V.begin(), V.begin() + 1), this->AssertionMessage); + + ASSERT_EQ(3u, this->NumBuiltinElts(V)); + ASSERT_EQ(3u, V.size()); + V.pop_back(); + ASSERT_EQ(2u, V.size()); + + // Confirm this checks for growth when there's more than one element + // appended. + EXPECT_DEATH(V.append(V.begin(), V.end()), this->AssertionMessage); +#endif +} + +TYPED_TEST(SmallVectorReferenceInvalidationTest, Assign) { + // Note: setup adds [1, 2, ...] to V until it's at capacity in small mode. + auto &V = this->V; + (void)V; + int N = this->NumBuiltinElts(V); + ASSERT_EQ(unsigned(N), V.size()); + ASSERT_EQ(unsigned(N), V.capacity()); + + // Check assign that shrinks in small mode. + V.assign(1, V.back()); + EXPECT_EQ(1u, V.size()); + EXPECT_EQ(N, V[0]); + + // Check assign that grows within small mode. + ASSERT_LT(V.size(), V.capacity()); + V.assign(V.capacity(), V.back()); + for (int I = 0, E = V.size(); I != E; ++I) { + EXPECT_EQ(N, V[I]); + + // Reset to [1, 2, ...]. + V[I] = I + 1; + } + + // Check assign that grows to large mode. + ASSERT_EQ(2, V[1]); + V.assign(V.capacity() + 1, V[1]); + for (int I = 0, E = V.size(); I != E; ++I) { + EXPECT_EQ(2, V[I]); + + // Reset to [1, 2, ...]. + V[I] = I + 1; + } + + // Check assign that shrinks in large mode. + V.assign(1, V[1]); + EXPECT_EQ(2, V[0]); +} + +TYPED_TEST(SmallVectorReferenceInvalidationTest, AssignRange) { + auto &V = this->V; +#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST + EXPECT_DEATH(V.assign(V.begin(), V.end()), this->AssertionMessage); + EXPECT_DEATH(V.assign(V.begin(), V.end() - 1), this->AssertionMessage); +#endif + V.assign(V.begin(), V.begin()); + EXPECT_TRUE(V.empty()); +} + +TYPED_TEST(SmallVectorReferenceInvalidationTest, Insert) { + // Note: setup adds [1, 2, ...] to V until it's at capacity in small mode. + auto &V = this->V; + (void)V; + + // Insert a reference to the back (not at end() or else insert delegates to + // push_back()), growing out of small mode. Confirm the value was copied out + // (moving out Constructable sets it to 0). + V.insert(V.begin(), V.back()); + EXPECT_EQ(int(V.size() - 1), V.front()); + EXPECT_EQ(int(V.size() - 1), V.back()); + + // Fill up the vector again. + while (V.size() < V.capacity()) + V.push_back(V.size() + 1); + + // Grow again from large storage to large storage. + V.insert(V.begin(), V.back()); + EXPECT_EQ(int(V.size() - 1), V.front()); + EXPECT_EQ(int(V.size() - 1), V.back()); +} + +TYPED_TEST(SmallVectorReferenceInvalidationTest, InsertMoved) { + // Note: setup adds [1, 2, ...] to V until it's at capacity in small mode. + auto &V = this->V; + (void)V; + + // Insert a reference to the back (not at end() or else insert delegates to + // push_back()), growing out of small mode. Confirm the value was copied out + // (moving out Constructable sets it to 0). + V.insert(V.begin(), std::move(V.back())); + EXPECT_EQ(int(V.size() - 1), V.front()); + if (this->template isValueType()) { + // Check the value got moved out. + EXPECT_EQ(0, V.back()); + } + + // Fill up the vector again. + while (V.size() < V.capacity()) + V.push_back(V.size() + 1); + + // Grow again from large storage to large storage. + V.insert(V.begin(), std::move(V.back())); + EXPECT_EQ(int(V.size() - 1), V.front()); + if (this->template isValueType()) { + // Check the value got moved out. + EXPECT_EQ(0, V.back()); + } +} + +TYPED_TEST(SmallVectorReferenceInvalidationTest, InsertN) { + auto &V = this->V; + (void)V; + + // Cover NumToInsert <= this->end() - I. + V.insert(V.begin() + 1, 1, V.back()); + int N = this->NumBuiltinElts(V); + EXPECT_EQ(N, V[1]); + + // Cover NumToInsert > this->end() - I, inserting enough elements that V will + // also grow again; V.capacity() will be more elements than necessary but + // it's a simple way to cover both conditions. + // + // If reference invalidation breaks in the future, sanitizers should be able + // to catch a use-after-free here. + V.insert(V.begin(), V.capacity(), V.front()); + EXPECT_EQ(1, V.front()); +} + +TYPED_TEST(SmallVectorReferenceInvalidationTest, InsertRange) { + auto &V = this->V; + (void)V; +#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST + EXPECT_DEATH(V.insert(V.begin(), V.begin(), V.begin() + 1), + this->AssertionMessage); + + ASSERT_EQ(3u, this->NumBuiltinElts(V)); + ASSERT_EQ(3u, V.size()); + V.pop_back(); + ASSERT_EQ(2u, V.size()); + + // Confirm this checks for growth when there's more than one element + // inserted. + EXPECT_DEATH(V.insert(V.begin(), V.begin(), V.end()), this->AssertionMessage); +#endif +} + +TYPED_TEST(SmallVectorReferenceInvalidationTest, EmplaceBack) { + // Note: setup adds [1, 2, ...] to V until it's at capacity in small mode. + auto &V = this->V; + int N = this->NumBuiltinElts(V); + + // Push back a reference to last element when growing from small storage. + V.emplace_back(V.back()); + EXPECT_EQ(N, V.back()); + + // Check that the old value is still there (not moved away). + EXPECT_EQ(N, V[V.size() - 2]); + + // Fill storage again. + V.back() = V.size(); + while (V.size() < V.capacity()) + V.push_back(V.size() + 1); + + // Push back a reference to last element when growing from large storage. + V.emplace_back(V.back()); + EXPECT_EQ(int(V.size()) - 1, V.back()); +} + +template +class SmallVectorInternalReferenceInvalidationTest + : public SmallVectorTestBase { +protected: + const char *AssertionMessage = + "Attempting to reference an element of the vector in an operation \" " + "\"that invalidates it"; + + VectorT V; + + template + static unsigned NumBuiltinElts(const SmallVector &) { + return N; + } + + void SetUp() override { + SmallVectorTestBase::SetUp(); + + // Fill up the small size so that insertions move the elements. + for (int I = 0, E = NumBuiltinElts(V); I != E; ++I) + V.emplace_back(I + 1, I + 1); + } +}; + +// Test pairs of the same types from SmallVectorReferenceInvalidationTestTypes. +using SmallVectorInternalReferenceInvalidationTestTypes = + ::testing::Types, 3>, + SmallVector, 3>>; + +TYPED_TEST_SUITE(SmallVectorInternalReferenceInvalidationTest, + SmallVectorInternalReferenceInvalidationTestTypes, ); + +TYPED_TEST(SmallVectorInternalReferenceInvalidationTest, EmplaceBack) { + // Note: setup adds [1, 2, ...] to V until it's at capacity in small mode. + auto &V = this->V; + int N = this->NumBuiltinElts(V); + + // Push back a reference to last element when growing from small storage. + V.emplace_back(V.back().first, V.back().second); + EXPECT_EQ(N, V.back().first); + EXPECT_EQ(N, V.back().second); + + // Check that the old value is still there (not moved away). + EXPECT_EQ(N, V[V.size() - 2].first); + EXPECT_EQ(N, V[V.size() - 2].second); + + // Fill storage again. + V.back().first = V.back().second = V.size(); + while (V.size() < V.capacity()) + V.emplace_back(V.size() + 1, V.size() + 1); + + // Push back a reference to last element when growing from large storage. + V.emplace_back(V.back().first, V.back().second); + EXPECT_EQ(int(V.size()) - 1, V.back().first); + EXPECT_EQ(int(V.size()) - 1, V.back().second); +} + +} // end namespace diff --git a/wpiutil/src/test/native/cpp/llvm/StringMapTest.cpp b/wpiutil/src/test/native/cpp/llvm/StringMapTest.cpp new file mode 100644 index 0000000000..bb01d30710 --- /dev/null +++ b/wpiutil/src/test/native/cpp/llvm/StringMapTest.cpp @@ -0,0 +1,613 @@ +//===- llvm/unittest/ADT/StringMapMap.cpp - StringMap unit tests ----------===// +// +// 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 "wpi/StringMap.h" +#include "gtest/gtest.h" +#include +#include +#include +using namespace wpi; + +namespace { + +// Test fixture +class StringMapTest : public testing::Test { +protected: + StringMap testMap; + + static const char testKey[]; + static const uint32_t testValue; + static const char* testKeyFirst; + static size_t testKeyLength; + static const std::string testKeyStr; + + void assertEmptyMap() { + // Size tests + EXPECT_EQ(0u, testMap.size()); + EXPECT_TRUE(testMap.empty()); + + // Iterator tests + EXPECT_TRUE(testMap.begin() == testMap.end()); + + // Lookup tests + EXPECT_EQ(0u, testMap.count(testKey)); + EXPECT_EQ(0u, testMap.count(std::string_view(testKeyFirst, testKeyLength))); + EXPECT_EQ(0u, testMap.count(testKeyStr)); + EXPECT_TRUE(testMap.find(testKey) == testMap.end()); + EXPECT_TRUE(testMap.find(std::string_view(testKeyFirst, testKeyLength)) == + testMap.end()); + EXPECT_TRUE(testMap.find(testKeyStr) == testMap.end()); + } + + void assertSingleItemMap() { + // Size tests + EXPECT_EQ(1u, testMap.size()); + EXPECT_FALSE(testMap.begin() == testMap.end()); + EXPECT_FALSE(testMap.empty()); + + // Iterator tests + StringMap::iterator it = testMap.begin(); + EXPECT_STREQ(testKey, it->first().data()); + EXPECT_EQ(testValue, it->second); + ++it; + EXPECT_TRUE(it == testMap.end()); + + // Lookup tests + EXPECT_EQ(1u, testMap.count(testKey)); + EXPECT_EQ(1u, testMap.count(std::string_view(testKeyFirst, testKeyLength))); + EXPECT_EQ(1u, testMap.count(testKeyStr)); + EXPECT_TRUE(testMap.find(testKey) == testMap.begin()); + EXPECT_TRUE(testMap.find(std::string_view(testKeyFirst, testKeyLength)) == + testMap.begin()); + EXPECT_TRUE(testMap.find(testKeyStr) == testMap.begin()); + } +}; + +const char StringMapTest::testKey[] = "key"; +const uint32_t StringMapTest::testValue = 1u; +const char* StringMapTest::testKeyFirst = testKey; +size_t StringMapTest::testKeyLength = sizeof(testKey) - 1; +const std::string StringMapTest::testKeyStr(testKey); + +struct CountCopyAndMove { + CountCopyAndMove() = default; + CountCopyAndMove(const CountCopyAndMove &) { copy = 1; } + CountCopyAndMove(CountCopyAndMove &&) { move = 1; } + void operator=(const CountCopyAndMove &) { ++copy; } + void operator=(CountCopyAndMove &&) { ++move; } + int copy = 0; + int move = 0; +}; + +// Empty map tests. +TEST_F(StringMapTest, EmptyMapTest) { + assertEmptyMap(); +} + +// Constant map tests. +TEST_F(StringMapTest, ConstEmptyMapTest) { + const StringMap& constTestMap = testMap; + + // Size tests + EXPECT_EQ(0u, constTestMap.size()); + EXPECT_TRUE(constTestMap.empty()); + + // Iterator tests + EXPECT_TRUE(constTestMap.begin() == constTestMap.end()); + + // Lookup tests + EXPECT_EQ(0u, constTestMap.count(testKey)); + EXPECT_EQ(0u, constTestMap.count(std::string_view(testKeyFirst, testKeyLength))); + EXPECT_EQ(0u, constTestMap.count(testKeyStr)); + EXPECT_TRUE(constTestMap.find(testKey) == constTestMap.end()); + EXPECT_TRUE(constTestMap.find(std::string_view(testKeyFirst, testKeyLength)) == + constTestMap.end()); + EXPECT_TRUE(constTestMap.find(testKeyStr) == constTestMap.end()); +} + +// A map with a single entry. +TEST_F(StringMapTest, SingleEntryMapTest) { + testMap[testKey] = testValue; + assertSingleItemMap(); +} + +// Test clear() method. +TEST_F(StringMapTest, ClearTest) { + testMap[testKey] = testValue; + testMap.clear(); + assertEmptyMap(); +} + +// Test erase(iterator) method. +TEST_F(StringMapTest, EraseIteratorTest) { + testMap[testKey] = testValue; + testMap.erase(testMap.begin()); + assertEmptyMap(); +} + +// Test erase(value) method. +TEST_F(StringMapTest, EraseValueTest) { + testMap[testKey] = testValue; + testMap.erase(testKey); + assertEmptyMap(); +} + +// Test inserting two values and erasing one. +TEST_F(StringMapTest, InsertAndEraseTest) { + testMap[testKey] = testValue; + testMap["otherKey"] = 2; + testMap.erase("otherKey"); + assertSingleItemMap(); +} + +TEST_F(StringMapTest, SmallFullMapTest) { + // StringMap has a tricky corner case when the map is small (<8 buckets) and + // it fills up through a balanced pattern of inserts and erases. This can + // lead to inf-loops in some cases (PR13148) so we test it explicitly here. + wpi::StringMap Map(2); + + Map["eins"] = 1; + Map["zwei"] = 2; + Map["drei"] = 3; + Map.erase("drei"); + Map.erase("eins"); + Map["veir"] = 4; + Map["funf"] = 5; + + EXPECT_EQ(3u, Map.size()); + EXPECT_EQ(0, Map.lookup("eins")); + EXPECT_EQ(2, Map.lookup("zwei")); + EXPECT_EQ(0, Map.lookup("drei")); + EXPECT_EQ(4, Map.lookup("veir")); + EXPECT_EQ(5, Map.lookup("funf")); +} + +TEST_F(StringMapTest, CopyCtorTest) { + wpi::StringMap Map; + + Map["eins"] = 1; + Map["zwei"] = 2; + Map["drei"] = 3; + Map.erase("drei"); + Map.erase("eins"); + Map["veir"] = 4; + Map["funf"] = 5; + + EXPECT_EQ(3u, Map.size()); + EXPECT_EQ(0, Map.lookup("eins")); + EXPECT_EQ(2, Map.lookup("zwei")); + EXPECT_EQ(0, Map.lookup("drei")); + EXPECT_EQ(4, Map.lookup("veir")); + EXPECT_EQ(5, Map.lookup("funf")); + + wpi::StringMap Map2(Map); + EXPECT_EQ(3u, Map2.size()); + EXPECT_EQ(0, Map2.lookup("eins")); + EXPECT_EQ(2, Map2.lookup("zwei")); + EXPECT_EQ(0, Map2.lookup("drei")); + EXPECT_EQ(4, Map2.lookup("veir")); + EXPECT_EQ(5, Map2.lookup("funf")); +} + +// A more complex iteration test. +TEST_F(StringMapTest, IterationTest) { + bool visited[100]; + + // Insert 100 numbers into the map + for (int i = 0; i < 100; ++i) { + std::stringstream ss; + ss << "key_" << i; + testMap[ss.str()] = i; + visited[i] = false; + } + + // Iterate over all numbers and mark each one found. + for (StringMap::iterator it = testMap.begin(); + it != testMap.end(); ++it) { + std::stringstream ss; + ss << "key_" << it->second; + ASSERT_STREQ(ss.str().c_str(), it->first().data()); + visited[it->second] = true; + } + + // Ensure every number was visited. + for (int i = 0; i < 100; ++i) { + ASSERT_TRUE(visited[i]) << "Entry #" << i << " was never visited"; + } +} + +// Test StringMapEntry::Create() method. +TEST_F(StringMapTest, StringMapEntryTest) { + StringMap::value_type *entry = + StringMap::value_type::Create( + std::string_view(testKeyFirst, testKeyLength), 1u); + EXPECT_STREQ(testKey, entry->first().data()); + EXPECT_EQ(1u, entry->second); + entry->Destroy(); +} + +// Test insert() method. +TEST_F(StringMapTest, InsertTest) { + SCOPED_TRACE("InsertTest"); + testMap.insert( + StringMap::value_type::Create( + std::string_view(testKeyFirst, testKeyLength), + 1u)); + assertSingleItemMap(); +} + +// Test insert(pair) method +TEST_F(StringMapTest, InsertPairTest) { + bool Inserted; + StringMap::iterator NewIt; + std::tie(NewIt, Inserted) = + testMap.insert(std::make_pair(testKeyFirst, testValue)); + EXPECT_EQ(1u, testMap.size()); + EXPECT_EQ(testValue, testMap[testKeyFirst]); + EXPECT_EQ(testKeyFirst, NewIt->first()); + EXPECT_EQ(testValue, NewIt->second); + EXPECT_TRUE(Inserted); + + StringMap::iterator ExistingIt; + std::tie(ExistingIt, Inserted) = + testMap.insert(std::make_pair(testKeyFirst, testValue + 1)); + EXPECT_EQ(1u, testMap.size()); + EXPECT_EQ(testValue, testMap[testKeyFirst]); + EXPECT_FALSE(Inserted); + EXPECT_EQ(NewIt, ExistingIt); +} + +// Test insert(pair) method when rehashing occurs +TEST_F(StringMapTest, InsertRehashingPairTest) { + // Check that the correct iterator is returned when the inserted element is + // moved to a different bucket during internal rehashing. This depends on + // the particular key, and the implementation of StringMap and HashString. + // Changes to those might result in this test not actually checking that. + StringMap t(0); + EXPECT_EQ(0u, t.getNumBuckets()); + + StringMap::iterator It = + t.insert(std::make_pair("abcdef", 42)).first; + EXPECT_EQ(16u, t.getNumBuckets()); + EXPECT_EQ("abcdef", It->first()); + EXPECT_EQ(42u, It->second); +} + +TEST_F(StringMapTest, InsertOrAssignTest) { + struct A : CountCopyAndMove { + A(int v) : v(v) {} + int v; + }; + StringMap t(0); + + auto try1 = t.insert_or_assign("A", A(1)); + EXPECT_TRUE(try1.second); + EXPECT_EQ(1, try1.first->second.v); + EXPECT_EQ(1, try1.first->second.move); + + auto try2 = t.insert_or_assign("A", A(2)); + EXPECT_FALSE(try2.second); + EXPECT_EQ(2, try2.first->second.v); + EXPECT_EQ(2, try1.first->second.move); + + EXPECT_EQ(try1.first, try2.first); + EXPECT_EQ(0, try1.first->second.copy); +} + +TEST_F(StringMapTest, IterMapKeys) { + StringMap Map; + Map["A"] = 1; + Map["B"] = 2; + Map["C"] = 3; + Map["D"] = 3; + + auto Keys = to_vector<4>(Map.keys()); + std::sort(Keys.begin(), Keys.end()); + + SmallVector Expected = {"A", "B", "C", "D"}; + EXPECT_EQ(Expected, Keys); +} + +// Create a non-default constructable value +struct StringMapTestStruct { + StringMapTestStruct(int i) : i(i) {} + StringMapTestStruct() = delete; + int i; +}; + +TEST_F(StringMapTest, NonDefaultConstructable) { + StringMap t; + t.insert(std::make_pair("Test", StringMapTestStruct(123))); + StringMap::iterator iter = t.find("Test"); + ASSERT_NE(iter, t.end()); + ASSERT_EQ(iter->second.i, 123); +} + +struct Immovable { + Immovable() {} + Immovable(Immovable&&) = delete; // will disable the other special members +}; + +struct MoveOnly { + int i; + MoveOnly(int i) : i(i) {} + MoveOnly(const Immovable&) : i(0) {} + MoveOnly(MoveOnly &&RHS) : i(RHS.i) {} + MoveOnly &operator=(MoveOnly &&RHS) { + i = RHS.i; + return *this; + } + +private: + MoveOnly(const MoveOnly &) = delete; + MoveOnly &operator=(const MoveOnly &) = delete; +}; + +TEST_F(StringMapTest, MoveOnly) { + StringMap t; + t.insert(std::make_pair("Test", MoveOnly(42))); + std::string_view Key = "Test"; + StringMapEntry::Create(Key, MoveOnly(42)) + ->Destroy(); +} + +TEST_F(StringMapTest, CtorArg) { + std::string_view Key = "Test"; + StringMapEntry::Create(Key, Immovable()) + ->Destroy(); +} + +TEST_F(StringMapTest, MoveConstruct) { + StringMap A; + A["x"] = 42; + StringMap B = std::move(A); + ASSERT_EQ(A.size(), 0u); + ASSERT_EQ(B.size(), 1u); + ASSERT_EQ(B["x"], 42); + ASSERT_EQ(B.count("y"), 0u); +} + +TEST_F(StringMapTest, MoveAssignment) { + StringMap A; + A["x"] = 42; + StringMap B; + B["y"] = 117; + A = std::move(B); + ASSERT_EQ(A.size(), 1u); + ASSERT_EQ(B.size(), 0u); + ASSERT_EQ(A["y"], 117); + ASSERT_EQ(B.count("x"), 0u); +} + +TEST_F(StringMapTest, EqualEmpty) { + StringMap A; + StringMap B; + ASSERT_TRUE(A == B); + ASSERT_FALSE(A != B); + ASSERT_TRUE(A == A); // self check +} + +TEST_F(StringMapTest, EqualWithValues) { + StringMap A; + A["A"] = 1; + A["B"] = 2; + A["C"] = 3; + A["D"] = 3; + + StringMap B; + B["A"] = 1; + B["B"] = 2; + B["C"] = 3; + B["D"] = 3; + + ASSERT_TRUE(A == B); + ASSERT_TRUE(B == A); + ASSERT_FALSE(A != B); + ASSERT_FALSE(B != A); + ASSERT_TRUE(A == A); // self check +} + +TEST_F(StringMapTest, NotEqualMissingKeys) { + StringMap A; + A["A"] = 1; + A["B"] = 2; + + StringMap B; + B["A"] = 1; + B["B"] = 2; + B["C"] = 3; + B["D"] = 3; + + ASSERT_FALSE(A == B); + ASSERT_FALSE(B == A); + ASSERT_TRUE(A != B); + ASSERT_TRUE(B != A); +} + +TEST_F(StringMapTest, NotEqualWithDifferentValues) { + StringMap A; + A["A"] = 1; + A["B"] = 2; + A["C"] = 100; + A["D"] = 3; + + StringMap B; + B["A"] = 1; + B["B"] = 2; + B["C"] = 3; + B["D"] = 3; + + ASSERT_FALSE(A == B); + ASSERT_FALSE(B == A); + ASSERT_TRUE(A != B); + ASSERT_TRUE(B != A); +} + +struct Countable { + int &InstanceCount; + int Number; + Countable(int Number, int &InstanceCount) + : InstanceCount(InstanceCount), Number(Number) { + ++InstanceCount; + } + Countable(Countable &&C) : InstanceCount(C.InstanceCount), Number(C.Number) { + ++InstanceCount; + C.Number = -1; + } + Countable(const Countable &C) + : InstanceCount(C.InstanceCount), Number(C.Number) { + ++InstanceCount; + } + Countable &operator=(Countable C) { + Number = C.Number; + return *this; + } + ~Countable() { --InstanceCount; } +}; + +TEST_F(StringMapTest, MoveDtor) { + int InstanceCount = 0; + StringMap A; + A.insert(std::make_pair("x", Countable(42, InstanceCount))); + ASSERT_EQ(InstanceCount, 1); + auto I = A.find("x"); + ASSERT_NE(I, A.end()); + ASSERT_EQ(I->second.Number, 42); + + StringMap B; + B = std::move(A); + ASSERT_EQ(InstanceCount, 1); + ASSERT_TRUE(A.empty()); + I = B.find("x"); + ASSERT_NE(I, B.end()); + ASSERT_EQ(I->second.Number, 42); + + B = StringMap(); + ASSERT_EQ(InstanceCount, 0); + ASSERT_TRUE(B.empty()); +} + +namespace { +// Simple class that counts how many moves and copy happens when growing a map +struct CountCtorCopyAndMove { + static unsigned Ctor; + static unsigned Move; + static unsigned Copy; + int Data = 0; + CountCtorCopyAndMove(int Data) : Data(Data) { Ctor++; } + CountCtorCopyAndMove() { Ctor++; } + + CountCtorCopyAndMove(const CountCtorCopyAndMove &) { Copy++; } + CountCtorCopyAndMove &operator=(const CountCtorCopyAndMove &) { + Copy++; + return *this; + } + CountCtorCopyAndMove(CountCtorCopyAndMove &&) { Move++; } + CountCtorCopyAndMove &operator=(const CountCtorCopyAndMove &&) { + Move++; + return *this; + } +}; +unsigned CountCtorCopyAndMove::Copy = 0; +unsigned CountCtorCopyAndMove::Move = 0; +unsigned CountCtorCopyAndMove::Ctor = 0; + +} // anonymous namespace + +// Make sure creating the map with an initial size of N actually gives us enough +// buckets to insert N items without increasing allocation size. +TEST(StringMapCustomTest, InitialSizeTest) { + // 1 is an "edge value", 32 is an arbitrary power of two, and 67 is an + // arbitrary prime, picked without any good reason. + for (auto Size : {1, 32, 67}) { + StringMap Map(Size); + auto NumBuckets = Map.getNumBuckets(); + CountCtorCopyAndMove::Move = 0; + CountCtorCopyAndMove::Copy = 0; + for (int i = 0; i < Size; ++i) + Map.insert(std::pair( + std::piecewise_construct, std::forward_as_tuple(std::to_string(i)), + std::forward_as_tuple(i))); + // After the initial move, the map will move the Elts in the Entry. + EXPECT_EQ((unsigned)Size * 2, CountCtorCopyAndMove::Move); + // We copy once the pair from the Elts vector + EXPECT_EQ(0u, CountCtorCopyAndMove::Copy); + // Check that the map didn't grow + EXPECT_EQ(Map.getNumBuckets(), NumBuckets); + } +} + +TEST(StringMapCustomTest, BracketOperatorCtor) { + StringMap Map; + CountCtorCopyAndMove::Ctor = 0; + Map["abcd"]; + EXPECT_EQ(1u, CountCtorCopyAndMove::Ctor); + // Test that operator[] does not create a value when it is already in the map + CountCtorCopyAndMove::Ctor = 0; + Map["abcd"]; + EXPECT_EQ(0u, CountCtorCopyAndMove::Ctor); +} + +namespace { +struct NonMoveableNonCopyableType { + int Data = 0; + NonMoveableNonCopyableType() = default; + NonMoveableNonCopyableType(int Data) : Data(Data) {} + NonMoveableNonCopyableType(const NonMoveableNonCopyableType &) = delete; + NonMoveableNonCopyableType(NonMoveableNonCopyableType &&) = delete; +}; +} + +// Test that we can "emplace" an element in the map without involving map/move +TEST(StringMapCustomTest, EmplaceTest) { + StringMap Map; + Map.try_emplace("abcd", 42); + EXPECT_EQ(1u, Map.count("abcd")); + EXPECT_EQ(42, Map["abcd"].Data); +} + +// Test that StringMapEntryBase can handle size_t wide sizes. +TEST(StringMapCustomTest, StringMapEntryBaseSize) { + size_t LargeValue; + + // Test that the entry can represent max-unsigned. + if (sizeof(size_t) <= sizeof(unsigned)) + LargeValue = std::numeric_limits::max(); + else + LargeValue = std::numeric_limits::max() + 1ULL; + StringMapEntryBase LargeBase(LargeValue); + EXPECT_EQ(LargeValue, LargeBase.getKeyLength()); + + // Test that the entry can hold at least max size_t. + LargeValue = std::numeric_limits::max(); + StringMapEntryBase LargerBase(LargeValue); + LargeValue = std::numeric_limits::max(); + EXPECT_EQ(LargeValue, LargerBase.getKeyLength()); +} + +// Test that StringMapEntry can handle size_t wide sizes. +TEST(StringMapCustomTest, StringMapEntrySize) { + size_t LargeValue; + + // Test that the entry can represent max-unsigned. + if (sizeof(size_t) <= sizeof(unsigned)) + LargeValue = std::numeric_limits::max(); + else + LargeValue = std::numeric_limits::max() + 1ULL; + StringMapEntry LargeEntry(LargeValue); + std::string_view Key = LargeEntry.getKey(); + EXPECT_EQ(LargeValue, Key.size()); + + // Test that the entry can hold at least max size_t. + LargeValue = std::numeric_limits::max(); + StringMapEntry LargerEntry(LargeValue); + Key = LargerEntry.getKey(); + EXPECT_EQ(LargeValue, Key.size()); +} + +} // end anonymous namespace diff --git a/wpiutil/src/test/native/cpp/llvm/SwapByteOrderTest.cpp b/wpiutil/src/test/native/cpp/llvm/SwapByteOrderTest.cpp new file mode 100644 index 0000000000..abacef7ed1 --- /dev/null +++ b/wpiutil/src/test/native/cpp/llvm/SwapByteOrderTest.cpp @@ -0,0 +1,210 @@ +//===- unittests/Support/SwapByteOrderTest.cpp - swap byte order test -----===// +// +// 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 "wpi/SwapByteOrder.h" +#include "gtest/gtest.h" +#include +#include +using namespace wpi; + +#undef max + +namespace { + +TEST(ByteSwap, Swap_32) { + EXPECT_EQ(0x44332211u, ByteSwap_32(0x11223344)); + EXPECT_EQ(0xDDCCBBAAu, ByteSwap_32(0xAABBCCDD)); +} + +TEST(ByteSwap, Swap_64) { + EXPECT_EQ(0x8877665544332211ULL, ByteSwap_64(0x1122334455667788LL)); + EXPECT_EQ(0x1100FFEEDDCCBBAAULL, ByteSwap_64(0xAABBCCDDEEFF0011LL)); +} + +// In these first two tests all of the original_uintx values are truncated +// except for 64. We could avoid this, but there's really no point. + +TEST(getSwappedBytes, UnsignedRoundTrip) { + // The point of the bit twiddling of magic is to test with and without bits + // in every byte. + uint64_t value = 1; + for (std::size_t i = 0; i <= sizeof(value); ++i) { + uint8_t original_uint8 = static_cast(value); + EXPECT_EQ(original_uint8, + sys::getSwappedBytes(sys::getSwappedBytes(original_uint8))); + + uint16_t original_uint16 = static_cast(value); + EXPECT_EQ(original_uint16, + sys::getSwappedBytes(sys::getSwappedBytes(original_uint16))); + + uint32_t original_uint32 = static_cast(value); + EXPECT_EQ(original_uint32, + sys::getSwappedBytes(sys::getSwappedBytes(original_uint32))); + + uint64_t original_uint64 = static_cast(value); + EXPECT_EQ(original_uint64, + sys::getSwappedBytes(sys::getSwappedBytes(original_uint64))); + + value = (value << 8) | 0x55; // binary 0101 0101. + } +} + +TEST(getSwappedBytes, SignedRoundTrip) { + // The point of the bit twiddling of magic is to test with and without bits + // in every byte. + uint64_t value = 1; + for (std::size_t i = 0; i <= sizeof(value); ++i) { + int8_t original_int8 = static_cast(value); + EXPECT_EQ(original_int8, + sys::getSwappedBytes(sys::getSwappedBytes(original_int8))); + + int16_t original_int16 = static_cast(value); + EXPECT_EQ(original_int16, + sys::getSwappedBytes(sys::getSwappedBytes(original_int16))); + + int32_t original_int32 = static_cast(value); + EXPECT_EQ(original_int32, + sys::getSwappedBytes(sys::getSwappedBytes(original_int32))); + + int64_t original_int64 = static_cast(value); + EXPECT_EQ(original_int64, + sys::getSwappedBytes(sys::getSwappedBytes(original_int64))); + + // Test other sign. + value *= -1; + + original_int8 = static_cast(value); + EXPECT_EQ(original_int8, + sys::getSwappedBytes(sys::getSwappedBytes(original_int8))); + + original_int16 = static_cast(value); + EXPECT_EQ(original_int16, + sys::getSwappedBytes(sys::getSwappedBytes(original_int16))); + + original_int32 = static_cast(value); + EXPECT_EQ(original_int32, + sys::getSwappedBytes(sys::getSwappedBytes(original_int32))); + + original_int64 = static_cast(value); + EXPECT_EQ(original_int64, + sys::getSwappedBytes(sys::getSwappedBytes(original_int64))); + + // Return to normal sign and twiddle. + value *= -1; + value = (value << 8) | 0x55; // binary 0101 0101. + } +} + +TEST(getSwappedBytes, uint8_t) { + EXPECT_EQ(uint8_t(0x11), sys::getSwappedBytes(uint8_t(0x11))); +} + +TEST(getSwappedBytes, uint16_t) { + EXPECT_EQ(uint16_t(0x1122), sys::getSwappedBytes(uint16_t(0x2211))); +} + +TEST(getSwappedBytes, uint32_t) { + EXPECT_EQ(uint32_t(0x11223344), sys::getSwappedBytes(uint32_t(0x44332211))); +} + +TEST(getSwappedBytes, uint64_t) { + EXPECT_EQ(uint64_t(0x1122334455667788ULL), + sys::getSwappedBytes(uint64_t(0x8877665544332211ULL))); +} + +TEST(getSwappedBytes, int8_t) { + EXPECT_EQ(int8_t(0x11), sys::getSwappedBytes(int8_t(0x11))); +} + +TEST(getSwappedBytes, int16_t) { + EXPECT_EQ(int16_t(0x1122), sys::getSwappedBytes(int16_t(0x2211))); +} + +TEST(getSwappedBytes, int32_t) { + EXPECT_EQ(int32_t(0x11223344), sys::getSwappedBytes(int32_t(0x44332211))); +} + +TEST(getSwappedBytes, int64_t) { + EXPECT_EQ(int64_t(0x1122334455667788LL), + sys::getSwappedBytes(int64_t(0x8877665544332211LL))); +} + +TEST(getSwappedBytes, float) { + EXPECT_EQ(1.79366203433576585078237386661e-43f, sys::getSwappedBytes(-0.0f)); + // 0x11223344 + EXPECT_EQ(7.1653228759765625e2f, sys::getSwappedBytes(1.2795344e-28f)); +} + +TEST(getSwappedBytes, double) { + EXPECT_EQ(6.32404026676795576546008054871e-322, sys::getSwappedBytes(-0.0)); + // 0x1122334455667788 + EXPECT_EQ(-7.08687663657301358331704585496e-268, + sys::getSwappedBytes(3.84141202447173065923064450234e-226)); +} + +TEST(swapByteOrder, uint8_t) { + uint8_t value = 0x11; + sys::swapByteOrder(value); + EXPECT_EQ(uint8_t(0x11), value); +} + +TEST(swapByteOrder, uint16_t) { + uint16_t value = 0x2211; + sys::swapByteOrder(value); + EXPECT_EQ(uint16_t(0x1122), value); +} + +TEST(swapByteOrder, uint32_t) { + uint32_t value = 0x44332211; + sys::swapByteOrder(value); + EXPECT_EQ(uint32_t(0x11223344), value); +} + +TEST(swapByteOrder, uint64_t) { + uint64_t value = 0x8877665544332211ULL; + sys::swapByteOrder(value); + EXPECT_EQ(uint64_t(0x1122334455667788ULL), value); +} + +TEST(swapByteOrder, int8_t) { + int8_t value = 0x11; + sys::swapByteOrder(value); + EXPECT_EQ(int8_t(0x11), value); +} + +TEST(swapByteOrder, int16_t) { + int16_t value = 0x2211; + sys::swapByteOrder(value); + EXPECT_EQ(int16_t(0x1122), value); +} + +TEST(swapByteOrder, int32_t) { + int32_t value = 0x44332211; + sys::swapByteOrder(value); + EXPECT_EQ(int32_t(0x11223344), value); +} + +TEST(swapByteOrder, int64_t) { + int64_t value = 0x8877665544332211LL; + sys::swapByteOrder(value); + EXPECT_EQ(int64_t(0x1122334455667788LL), value); +} + +TEST(swapByteOrder, float) { + float value = 7.1653228759765625e2f; // 0x44332211 + sys::swapByteOrder(value); + EXPECT_EQ(1.2795344e-28f, value); +} + +TEST(swapByteOrder, double) { + double value = -7.08687663657301358331704585496e-268; // 0x8877665544332211 + sys::swapByteOrder(value); + EXPECT_EQ(3.84141202447173065923064450234e-226, value); +} + +}