[Mlir-commits] [clang] [clang-tools-extra] [compiler-rt] [flang] [libc] [libclc] [libcxx] [lld] [llvm] [mlir] [clang-format] Add --fail-on-incomplete-format. (PR #84346)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Mon Mar 18 10:36:19 PDT 2024
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,Timm Baeder
<tbaeder at redhat.com>,Benji Smith <6193112+Benjins at users.noreply.github.com>,
Miguel Raz =?utf-8?q?Guzmán?= Macedo,ykiko <ykikoykikoykiko at gmail.com>,Adrian
Kuegel <akuegel at google.com>,Florian Hahn <flo at fhahn.com>,Jay Foad
<jay.foad at amd.com>,Qiu Chaofan <qiucofan at cn.ibm.com>,Tom Stellard
<tstellar at redhat.com>,Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,Fraser
Cormack <fraser at codeplay.com>,Jonas Paulsson <paulson1 at linux.ibm.com>,Martin
Wehking <martin.wehking at codeplay.com>,Orlando Cazalet-Hyams
<orlando.hyams at sony.com>,Kelvin Li <kkwli at users.noreply.github.com>,
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,Jie Fu <jiefu at tencent.com>,Hirofumi
Nakamura <k.nakamura.hirofumi at gmail.com>,Schrodinger ZHU Yifan
<yifanzhu at rochester.edu>,Mike Rice <michael.p.rice at intel.com>,Congcong Cai
<congcongcai0907 at 163.com>,Andrei Golubev <andrey.golubev at intel.com>,Mark de
Wever <koraq at xs4all.nl>,Shourya Goel <shouryagoel10000 at gmail.com>,Balaji V.
Iyer <43187390+bviyer at users.noreply.github.com>=?utf-8?q?,?=zhongyunde
00443407 <zhongyunde at huawei.com>,
Endre =?utf-8?q?Fülöp?= <endre.fulop at sigmatechnology.com>,David
Green <david.green at arm.com>,alx32 <103613512+alx32 at users.noreply.github.com>,Joseph
Huber <huberjn at outlook.com>,Alex Richardson <alexrichardson at google.com>,Fraser
Cormack <fraser at codeplay.com>,Alexey Bataev <a.bataev at outlook.com>,Roberto
Bampi <bampi at google.com>,Roberto Bampi <gigaroby at users.noreply.github.com>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/84346 at github.com>
https://github.com/lntue updated https://github.com/llvm/llvm-project/pull/84346
>From a10aa4485e833d7805dab8eaed4a7ffea1a08f72 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 18 Mar 2024 13:57:07 +0100
Subject: [PATCH 01/40] [libc++] Simplify the implementation of
remove_reference (#85207)
GCC 13 introduced the type trait `__remove_reference`. We can simplify
the implementation of `remove_reference` a bit by using it.
---
libcxx/include/__type_traits/remove_reference.h | 14 +++++++-------
libcxx/include/cwchar | 4 ++++
libcxx/include/execution | 4 ++++
libcxx/test/libcxx/transitive_includes/cxx23.csv | 2 --
libcxx/test/libcxx/transitive_includes/cxx26.csv | 2 --
5 files changed, 15 insertions(+), 11 deletions(-)
diff --git a/libcxx/include/__type_traits/remove_reference.h b/libcxx/include/__type_traits/remove_reference.h
index fd66417bd84fed..ba67891758adce 100644
--- a/libcxx/include/__type_traits/remove_reference.h
+++ b/libcxx/include/__type_traits/remove_reference.h
@@ -10,7 +10,6 @@
#define _LIBCPP___TYPE_TRAITS_REMOVE_REFERENCE_H
#include <__config>
-#include <cstddef>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -26,15 +25,16 @@ struct remove_reference {
template <class _Tp>
using __libcpp_remove_reference_t = __remove_reference_t(_Tp);
-#else
-// clang-format off
-template <class _Tp> struct _LIBCPP_TEMPLATE_VIS remove_reference {typedef _LIBCPP_NODEBUG _Tp type;};
-template <class _Tp> struct _LIBCPP_TEMPLATE_VIS remove_reference<_Tp&> {typedef _LIBCPP_NODEBUG _Tp type;};
-template <class _Tp> struct _LIBCPP_TEMPLATE_VIS remove_reference<_Tp&&> {typedef _LIBCPP_NODEBUG _Tp type;};
-// clang-format on
+#elif __has_builtin(__remove_reference)
+template <class _Tp>
+struct remove_reference {
+ using type _LIBCPP_NODEBUG = __remove_reference(_Tp);
+};
template <class _Tp>
using __libcpp_remove_reference_t = typename remove_reference<_Tp>::type;
+#else
+# error "remove_reference not implemented!"
#endif // __has_builtin(__remove_reference_t)
#if _LIBCPP_STD_VER >= 14
diff --git a/libcxx/include/cwchar b/libcxx/include/cwchar
index 7442438d8f447f..4cc6f56c389bfe 100644
--- a/libcxx/include/cwchar
+++ b/libcxx/include/cwchar
@@ -254,4 +254,8 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp* __constexpr_wmemchr(_Tp
_LIBCPP_END_NAMESPACE_STD
+#if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 20
+# include <cstddef>
+#endif
+
#endif // _LIBCPP_CWCHAR
diff --git a/libcxx/include/execution b/libcxx/include/execution
index 822ffa1fd3ebc4..94d434b2e4603e 100644
--- a/libcxx/include/execution
+++ b/libcxx/include/execution
@@ -142,4 +142,8 @@ _LIBCPP_END_NAMESPACE_STD
#endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_PSTL) && _LIBCPP_STD_VER >= 17
+#if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 20
+# include <cstddef>
+#endif
+
#endif // _LIBCPP_EXECUTION
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index 8150f0935900e4..062127364adfee 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -142,7 +142,6 @@ coroutine version
cstddef version
ctgmath ccomplex
ctgmath cmath
-cwchar cstddef
cwchar cwctype
cwctype cctype
deque compare
@@ -161,7 +160,6 @@ exception cstdlib
exception new
exception typeinfo
exception version
-execution cstddef
execution version
expected cstddef
expected initializer_list
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index 8150f0935900e4..062127364adfee 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -142,7 +142,6 @@ coroutine version
cstddef version
ctgmath ccomplex
ctgmath cmath
-cwchar cstddef
cwchar cwctype
cwctype cctype
deque compare
@@ -161,7 +160,6 @@ exception cstdlib
exception new
exception typeinfo
exception version
-execution cstddef
execution version
expected cstddef
expected initializer_list
>From bc70f60418f5edad1aaee91fef832a6e2301c62f Mon Sep 17 00:00:00 2001
From: zicwangupa <87221359+ZiCong-Wang at users.noreply.github.com>
Date: Mon, 18 Mar 2024 21:04:31 +0800
Subject: [PATCH 02/40] [SelectionDAG] Add m_Neg and m_Not pattern matcher and
update DAGCombiner (#85365)
Resolves #85065
---------
Co-authored-by: Matt Arsenault <arsenm2 at gmail.com>
---
llvm/include/llvm/CodeGen/SDPatternMatch.h | 12 ++++++++++++
llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp | 6 +++---
.../CodeGen/SelectionDAGPatternMatchTest.cpp | 10 ++++++++++
3 files changed, 25 insertions(+), 3 deletions(-)
diff --git a/llvm/include/llvm/CodeGen/SDPatternMatch.h b/llvm/include/llvm/CodeGen/SDPatternMatch.h
index 96ab6b160a1c77..541c0ecb5be373 100644
--- a/llvm/include/llvm/CodeGen/SDPatternMatch.h
+++ b/llvm/include/llvm/CodeGen/SDPatternMatch.h
@@ -725,6 +725,18 @@ inline auto m_False() {
},
m_Value()};
}
+
+/// Match a negate as a sub(0, v)
+template <typename ValTy>
+inline BinaryOpc_match<SpecificInt_match, ValTy> m_Neg(const ValTy &V) {
+ return m_Sub(m_Zero(), V);
+}
+
+/// Match a Not as a xor(v, -1) or xor(-1, v)
+template <typename ValTy>
+inline BinaryOpc_match<ValTy, SpecificInt_match, true> m_Not(const ValTy &V) {
+ return m_Xor(V, m_AllOnes());
+}
} // namespace SDPatternMatch
} // namespace llvm
#endif
diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index 5eb53d57c9c2bf..351041780b8547 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -2703,11 +2703,11 @@ SDValue DAGCombiner::visitADDLike(SDNode *N) {
SDValue A, B, C;
// fold ((0-A) + B) -> B-A
- if (sd_match(N0, m_Sub(m_Zero(), m_Value(A))))
+ if (sd_match(N0, m_Neg(m_Value(A))))
return DAG.getNode(ISD::SUB, DL, VT, N1, A);
// fold (A + (0-B)) -> A-B
- if (sd_match(N1, m_Sub(m_Zero(), m_Value(B))))
+ if (sd_match(N1, m_Neg(m_Value(B))))
return DAG.getNode(ISD::SUB, DL, VT, N0, B);
// fold (A+(B-A)) -> B
@@ -3812,7 +3812,7 @@ SDValue DAGCombiner::visitSUB(SDNode *N) {
return DAG.getNode(ISD::AND, DL, VT, N0, DAG.getNOT(DL, B, VT));
// fold (A - (-B * C)) -> (A + (B * C))
- if (sd_match(N1, m_OneUse(m_Mul(m_Sub(m_Zero(), m_Value(B)), m_Value(C)))))
+ if (sd_match(N1, m_OneUse(m_Mul(m_Neg(m_Value(B)), m_Value(C)))))
return DAG.getNode(ISD::ADD, DL, VT, N0,
DAG.getNode(ISD::MUL, DL, VT, B, C));
diff --git a/llvm/unittests/CodeGen/SelectionDAGPatternMatchTest.cpp b/llvm/unittests/CodeGen/SelectionDAGPatternMatchTest.cpp
index 180d4306a470f7..1967a62bbf9d11 100644
--- a/llvm/unittests/CodeGen/SelectionDAGPatternMatchTest.cpp
+++ b/llvm/unittests/CodeGen/SelectionDAGPatternMatchTest.cpp
@@ -187,10 +187,20 @@ TEST_F(SelectionDAGPatternMatchTest, matchUnaryOp) {
SDValue SExt = DAG->getNode(ISD::SIGN_EXTEND, DL, Int64VT, Op0);
SDValue Trunc = DAG->getNode(ISD::TRUNCATE, DL, Int32VT, Op1);
+ SDValue Sub = DAG->getNode(ISD::SUB, DL, Int32VT, Trunc, Op0);
+ SDValue Neg = DAG->getNegative(Op0, DL, Int32VT);
+ SDValue Not = DAG->getNOT(DL, Op0, Int32VT);
+
using namespace SDPatternMatch;
EXPECT_TRUE(sd_match(ZExt, m_UnaryOp(ISD::ZERO_EXTEND, m_Value())));
EXPECT_TRUE(sd_match(SExt, m_SExt(m_Value())));
EXPECT_TRUE(sd_match(Trunc, m_Trunc(m_Specific(Op1))));
+
+ EXPECT_TRUE(sd_match(Neg, m_Neg(m_Value())));
+ EXPECT_TRUE(sd_match(Not, m_Not(m_Value())));
+ EXPECT_FALSE(sd_match(ZExt, m_Neg(m_Value())));
+ EXPECT_FALSE(sd_match(Sub, m_Neg(m_Value())));
+ EXPECT_FALSE(sd_match(Neg, m_Not(m_Value())));
}
TEST_F(SelectionDAGPatternMatchTest, matchConstants) {
>From 4ea850b52ffbb6ca6a40242558ee005a2a894daf Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 18 Mar 2024 14:19:51 +0100
Subject: [PATCH 03/40] [libc++] Remove __unconstrained_reverse_iterator
(#85582)
`__unconstrained_reverse_iterator` has outlived its usefullness, since
the standard and subsequently the compilers have been fixed.
---
libcxx/include/__algorithm/inplace_merge.h | 4 +-
libcxx/include/__iterator/reverse_iterator.h | 194 ++----------------
.../reverse.iter.cmp/equal.pass.cpp | 47 -----
.../reverse.iter.cmp/greater-equal.pass.cpp | 47 -----
.../reverse.iter.cmp/greater.pass.cpp | 47 -----
.../reverse.iter.cmp/less-equal.pass.cpp | 47 -----
.../reverse.iter.cmp/less.pass.cpp | 47 -----
.../reverse.iter.cmp/not-equal.pass.cpp | 47 -----
.../assign.LWG3435.verify.cpp | 26 ---
.../reverse.iter.cons/ctor.default.pass.cpp | 40 ----
.../ctor.iter.explicit.verify.cpp | 22 --
.../reverse.iter.cons/ctor.iter.pass.cpp | 41 ----
.../ctor.reverse_iterator.LWG3435.verify.cpp | 25 ---
.../reverse.iter.conv/base.pass.cpp | 37 ----
.../reverse.iter.elem/arrow.pass.cpp | 122 -----------
.../reverse.iter.elem/bracket.pass.cpp | 47 -----
.../reverse.iter.elem/dereference.pass.cpp | 63 ------
.../decrement-assign.pass.cpp | 43 ----
.../increment-assign.pass.cpp | 43 ----
.../reverse.iter.nav/minus.pass.cpp | 42 ----
.../reverse.iter.nav/plus.pass.cpp | 42 ----
.../reverse.iter.nav/postdecrement.pass.cpp | 43 ----
.../reverse.iter.nav/postincrement.pass.cpp | 43 ----
.../reverse.iter.nav/predecrement.pass.cpp | 43 ----
.../reverse.iter.nav/preincrement.pass.cpp | 43 ----
.../reverse.iter.nonmember/minus.pass.cpp | 59 ------
.../types.compile.pass.cpp | 106 ----------
27 files changed, 14 insertions(+), 1396 deletions(-)
delete mode 100644 libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/equal.pass.cpp
delete mode 100644 libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/greater-equal.pass.cpp
delete mode 100644 libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/greater.pass.cpp
delete mode 100644 libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/less-equal.pass.cpp
delete mode 100644 libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/less.pass.cpp
delete mode 100644 libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/not-equal.pass.cpp
delete mode 100644 libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/assign.LWG3435.verify.cpp
delete mode 100644 libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/ctor.default.pass.cpp
delete mode 100644 libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/ctor.iter.explicit.verify.cpp
delete mode 100644 libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/ctor.iter.pass.cpp
delete mode 100644 libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/ctor.reverse_iterator.LWG3435.verify.cpp
delete mode 100644 libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.conv/base.pass.cpp
delete mode 100644 libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.elem/arrow.pass.cpp
delete mode 100644 libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.elem/bracket.pass.cpp
delete mode 100644 libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.elem/dereference.pass.cpp
delete mode 100644 libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/decrement-assign.pass.cpp
delete mode 100644 libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/increment-assign.pass.cpp
delete mode 100644 libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/minus.pass.cpp
delete mode 100644 libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/plus.pass.cpp
delete mode 100644 libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/postdecrement.pass.cpp
delete mode 100644 libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/postincrement.pass.cpp
delete mode 100644 libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/predecrement.pass.cpp
delete mode 100644 libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/preincrement.pass.cpp
delete mode 100644 libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nonmember/minus.pass.cpp
delete mode 100644 libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/types.compile.pass.cpp
diff --git a/libcxx/include/__algorithm/inplace_merge.h b/libcxx/include/__algorithm/inplace_merge.h
index eb3c0bdbc2db7f..a6bcc66a2fa47a 100644
--- a/libcxx/include/__algorithm/inplace_merge.h
+++ b/libcxx/include/__algorithm/inplace_merge.h
@@ -114,8 +114,8 @@ _LIBCPP_HIDE_FROM_ABI void __buffered_inplace_merge(
for (_BidirectionalIterator __i = __middle; __i != __last;
__d.template __incr<value_type>(), (void)++__i, (void)++__p)
::new ((void*)__p) value_type(_IterOps<_AlgPolicy>::__iter_move(__i));
- typedef __unconstrained_reverse_iterator<_BidirectionalIterator> _RBi;
- typedef __unconstrained_reverse_iterator<value_type*> _Rv;
+ typedef reverse_iterator<_BidirectionalIterator> _RBi;
+ typedef reverse_iterator<value_type*> _Rv;
typedef __invert<_Compare> _Inverted;
std::__half_inplace_merge<_AlgPolicy>(
_Rv(__p), _Rv(__buff), _RBi(__middle), _RBi(__first), _RBi(__last), _Inverted(__comp));
diff --git a/libcxx/include/__iterator/reverse_iterator.h b/libcxx/include/__iterator/reverse_iterator.h
index 9aab96866f4fa1..2ae14619348536 100644
--- a/libcxx/include/__iterator/reverse_iterator.h
+++ b/libcxx/include/__iterator/reverse_iterator.h
@@ -316,172 +316,6 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 reverse_iterator<_Ite
}
#endif
-#if _LIBCPP_STD_VER <= 17
-template <class _Iter>
-using __unconstrained_reverse_iterator = reverse_iterator<_Iter>;
-#else
-
-// __unconstrained_reverse_iterator allows us to use reverse iterators in the implementation of algorithms by working
-// around a language issue in C++20.
-// In C++20, when a reverse iterator wraps certain C++20-hostile iterators, calling comparison operators on it will
-// result in a compilation error. However, calling comparison operators on the pristine hostile iterator is not
-// an error. Thus, we cannot use reverse_iterators in the implementation of an algorithm that accepts a
-// C++20-hostile iterator. This class is an internal workaround -- it is a copy of reverse_iterator with
-// tweaks to make it support hostile iterators.
-//
-// A C++20-hostile iterator is one that defines a comparison operator where one of the arguments is an exact match
-// and the other requires an implicit conversion, for example:
-// friend bool operator==(const BaseIter&, const DerivedIter&);
-//
-// C++20 rules for rewriting equality operators create another overload of this function with parameters reversed:
-// friend bool operator==(const DerivedIter&, const BaseIter&);
-//
-// This creates an ambiguity in overload resolution.
-//
-// Clang treats this ambiguity differently in different contexts. When operator== is actually called in the function
-// body, the code is accepted with a warning. When a concept requires operator== to be a valid expression, however,
-// it evaluates to false. Thus, the implementation of reverse_iterator::operator== can actually call operator== on its
-// base iterators, but the constraints on reverse_iterator::operator== prevent it from being considered during overload
-// resolution. This class simply removes the problematic constraints from comparison functions.
-template <class _Iter>
-class __unconstrained_reverse_iterator {
- _Iter __iter_;
-
-public:
- static_assert(__has_bidirectional_iterator_category<_Iter>::value || bidirectional_iterator<_Iter>);
-
- using iterator_type = _Iter;
- using iterator_category =
- _If<__has_random_access_iterator_category<_Iter>::value,
- random_access_iterator_tag,
- __iterator_category_type<_Iter>>;
- using pointer = __iterator_pointer_type<_Iter>;
- using value_type = iter_value_t<_Iter>;
- using difference_type = iter_difference_t<_Iter>;
- using reference = iter_reference_t<_Iter>;
-
- _LIBCPP_HIDE_FROM_ABI constexpr __unconstrained_reverse_iterator() = default;
- _LIBCPP_HIDE_FROM_ABI constexpr __unconstrained_reverse_iterator(const __unconstrained_reverse_iterator&) = default;
- _LIBCPP_HIDE_FROM_ABI constexpr explicit __unconstrained_reverse_iterator(_Iter __iter) : __iter_(__iter) {}
-
- _LIBCPP_HIDE_FROM_ABI constexpr _Iter base() const { return __iter_; }
- _LIBCPP_HIDE_FROM_ABI constexpr reference operator*() const {
- auto __tmp = __iter_;
- return *--__tmp;
- }
-
- _LIBCPP_HIDE_FROM_ABI constexpr pointer operator->() const {
- if constexpr (is_pointer_v<_Iter>) {
- return std::prev(__iter_);
- } else {
- return std::prev(__iter_).operator->();
- }
- }
-
- _LIBCPP_HIDE_FROM_ABI friend constexpr iter_rvalue_reference_t<_Iter>
- iter_move(const __unconstrained_reverse_iterator& __i) noexcept(
- is_nothrow_copy_constructible_v<_Iter>&& noexcept(ranges::iter_move(--std::declval<_Iter&>()))) {
- auto __tmp = __i.base();
- return ranges::iter_move(--__tmp);
- }
-
- _LIBCPP_HIDE_FROM_ABI constexpr __unconstrained_reverse_iterator& operator++() {
- --__iter_;
- return *this;
- }
-
- _LIBCPP_HIDE_FROM_ABI constexpr __unconstrained_reverse_iterator operator++(int) {
- auto __tmp = *this;
- --__iter_;
- return __tmp;
- }
-
- _LIBCPP_HIDE_FROM_ABI constexpr __unconstrained_reverse_iterator& operator--() {
- ++__iter_;
- return *this;
- }
-
- _LIBCPP_HIDE_FROM_ABI constexpr __unconstrained_reverse_iterator operator--(int) {
- auto __tmp = *this;
- ++__iter_;
- return __tmp;
- }
-
- _LIBCPP_HIDE_FROM_ABI constexpr __unconstrained_reverse_iterator& operator+=(difference_type __n) {
- __iter_ -= __n;
- return *this;
- }
-
- _LIBCPP_HIDE_FROM_ABI constexpr __unconstrained_reverse_iterator& operator-=(difference_type __n) {
- __iter_ += __n;
- return *this;
- }
-
- _LIBCPP_HIDE_FROM_ABI constexpr __unconstrained_reverse_iterator operator+(difference_type __n) const {
- return __unconstrained_reverse_iterator(__iter_ - __n);
- }
-
- _LIBCPP_HIDE_FROM_ABI constexpr __unconstrained_reverse_iterator operator-(difference_type __n) const {
- return __unconstrained_reverse_iterator(__iter_ + __n);
- }
-
- _LIBCPP_HIDE_FROM_ABI constexpr difference_type operator-(const __unconstrained_reverse_iterator& __other) const {
- return __other.__iter_ - __iter_;
- }
-
- _LIBCPP_HIDE_FROM_ABI constexpr auto operator[](difference_type __n) const { return *(*this + __n); }
-
- // Deliberately unconstrained unlike the comparison functions in `reverse_iterator` -- see the class comment for the
- // rationale.
- _LIBCPP_HIDE_FROM_ABI friend constexpr bool
- operator==(const __unconstrained_reverse_iterator& __lhs, const __unconstrained_reverse_iterator& __rhs) {
- return __lhs.base() == __rhs.base();
- }
-
- _LIBCPP_HIDE_FROM_ABI friend constexpr bool
- operator!=(const __unconstrained_reverse_iterator& __lhs, const __unconstrained_reverse_iterator& __rhs) {
- return __lhs.base() != __rhs.base();
- }
-
- _LIBCPP_HIDE_FROM_ABI friend constexpr bool
- operator<(const __unconstrained_reverse_iterator& __lhs, const __unconstrained_reverse_iterator& __rhs) {
- return __lhs.base() > __rhs.base();
- }
-
- _LIBCPP_HIDE_FROM_ABI friend constexpr bool
- operator>(const __unconstrained_reverse_iterator& __lhs, const __unconstrained_reverse_iterator& __rhs) {
- return __lhs.base() < __rhs.base();
- }
-
- _LIBCPP_HIDE_FROM_ABI friend constexpr bool
- operator<=(const __unconstrained_reverse_iterator& __lhs, const __unconstrained_reverse_iterator& __rhs) {
- return __lhs.base() >= __rhs.base();
- }
-
- _LIBCPP_HIDE_FROM_ABI friend constexpr bool
- operator>=(const __unconstrained_reverse_iterator& __lhs, const __unconstrained_reverse_iterator& __rhs) {
- return __lhs.base() <= __rhs.base();
- }
-};
-
-#endif // _LIBCPP_STD_VER <= 17
-
-template <template <class> class _RevIter1, template <class> class _RevIter2, class _Iter>
-struct __unwrap_reverse_iter_impl {
- using _UnwrappedIter = decltype(__unwrap_iter_impl<_Iter>::__unwrap(std::declval<_Iter>()));
- using _ReverseWrapper = _RevIter1<_RevIter2<_Iter> >;
-
- static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _ReverseWrapper
- __rewrap(_ReverseWrapper __orig_iter, _UnwrappedIter __unwrapped_iter) {
- return _ReverseWrapper(
- _RevIter2<_Iter>(__unwrap_iter_impl<_Iter>::__rewrap(__orig_iter.base().base(), __unwrapped_iter)));
- }
-
- static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _UnwrappedIter __unwrap(_ReverseWrapper __i) _NOEXCEPT {
- return __unwrap_iter_impl<_Iter>::__unwrap(__i.base().base());
- }
-};
-
#if _LIBCPP_STD_VER >= 20
template <ranges::bidirectional_range _Range>
_LIBCPP_HIDE_FROM_ABI constexpr ranges::subrange<reverse_iterator<ranges::iterator_t<_Range>>,
@@ -493,24 +327,20 @@ __reverse_range(_Range&& __range) {
#endif
template <class _Iter, bool __b>
-struct __unwrap_iter_impl<reverse_iterator<reverse_iterator<_Iter> >, __b>
- : __unwrap_reverse_iter_impl<reverse_iterator, reverse_iterator, _Iter> {};
-
-#if _LIBCPP_STD_VER >= 20
-
-template <class _Iter, bool __b>
-struct __unwrap_iter_impl<reverse_iterator<__unconstrained_reverse_iterator<_Iter>>, __b>
- : __unwrap_reverse_iter_impl<reverse_iterator, __unconstrained_reverse_iterator, _Iter> {};
-
-template <class _Iter, bool __b>
-struct __unwrap_iter_impl<__unconstrained_reverse_iterator<reverse_iterator<_Iter>>, __b>
- : __unwrap_reverse_iter_impl<__unconstrained_reverse_iterator, reverse_iterator, _Iter> {};
+struct __unwrap_iter_impl<reverse_iterator<reverse_iterator<_Iter> >, __b> {
+ using _UnwrappedIter = decltype(__unwrap_iter_impl<_Iter>::__unwrap(std::declval<_Iter>()));
+ using _ReverseWrapper = reverse_iterator<reverse_iterator<_Iter> >;
-template <class _Iter, bool __b>
-struct __unwrap_iter_impl<__unconstrained_reverse_iterator<__unconstrained_reverse_iterator<_Iter>>, __b>
- : __unwrap_reverse_iter_impl<__unconstrained_reverse_iterator, __unconstrained_reverse_iterator, _Iter> {};
+ static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _ReverseWrapper
+ __rewrap(_ReverseWrapper __orig_iter, _UnwrappedIter __unwrapped_iter) {
+ return _ReverseWrapper(
+ reverse_iterator<_Iter>(__unwrap_iter_impl<_Iter>::__rewrap(__orig_iter.base().base(), __unwrapped_iter)));
+ }
-#endif // _LIBCPP_STD_VER >= 20
+ static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _UnwrappedIter __unwrap(_ReverseWrapper __i) _NOEXCEPT {
+ return __unwrap_iter_impl<_Iter>::__unwrap(__i.base().base());
+ }
+};
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/equal.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/equal.pass.cpp
deleted file mode 100644
index 583e733c07cb0b..00000000000000
--- a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/equal.pass.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// <iterator>
-
-// __unconstrained_reverse_iterator
-
-// template <BidirectionalIterator Iter1, BidirectionalIterator Iter2>
-// requires HasEqualTo<Iter1, Iter2>
-// bool operator==(const __unconstrained_reverse_iterator<Iter1>& x, const __unconstrained_reverse_iterator<Iter2>& y); // constexpr since C++17
-
-#include <iterator>
-#include <cassert>
-
-#include "test_macros.h"
-#include "test_iterators.h"
-
-template <class It>
-TEST_CONSTEXPR_CXX17 void test(It l, It r, bool x) {
- const std::__unconstrained_reverse_iterator<It> r1(l);
- const std::__unconstrained_reverse_iterator<It> r2(r);
- assert((r1 == r2) == x);
-}
-
-TEST_CONSTEXPR_CXX17 bool tests() {
- const char* s = "1234567890";
- test(bidirectional_iterator<const char*>(s), bidirectional_iterator<const char*>(s), true);
- test(bidirectional_iterator<const char*>(s), bidirectional_iterator<const char*>(s+1), false);
- test(random_access_iterator<const char*>(s), random_access_iterator<const char*>(s), true);
- test(random_access_iterator<const char*>(s), random_access_iterator<const char*>(s+1), false);
- test(s, s, true);
- test(s, s+1, false);
- return true;
-}
-
-int main(int, char**) {
- tests();
-#if TEST_STD_VER > 14
- static_assert(tests(), "");
-#endif
- return 0;
-}
diff --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/greater-equal.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/greater-equal.pass.cpp
deleted file mode 100644
index 9e908418d07565..00000000000000
--- a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/greater-equal.pass.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// <iterator>
-
-// __unconstrained_reverse_iterator
-
-// template <RandomAccessIterator Iter1, RandomAccessIterator Iter2>
-// requires HasGreater<Iter1, Iter2>
-// bool operator>=(const __unconstrained_reverse_iterator<Iter1>& x, const __unconstrained_reverse_iterator<Iter2>& y); // constexpr since C++17
-
-#include <iterator>
-#include <cassert>
-
-#include "test_macros.h"
-#include "test_iterators.h"
-
-template <class It>
-TEST_CONSTEXPR_CXX17 void test(It l, It r, bool x) {
- const std::__unconstrained_reverse_iterator<It> r1(l);
- const std::__unconstrained_reverse_iterator<It> r2(r);
- assert((r1 >= r2) == x);
-}
-
-TEST_CONSTEXPR_CXX17 bool tests() {
- const char* s = "1234567890";
- test(random_access_iterator<const char*>(s), random_access_iterator<const char*>(s), true);
- test(random_access_iterator<const char*>(s), random_access_iterator<const char*>(s+1), true);
- test(random_access_iterator<const char*>(s+1), random_access_iterator<const char*>(s), false);
- test(s, s, true);
- test(s, s+1, true);
- test(s+1, s, false);
- return true;
-}
-
-int main(int, char**) {
- tests();
-#if TEST_STD_VER > 14
- static_assert(tests(), "");
-#endif
- return 0;
-}
diff --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/greater.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/greater.pass.cpp
deleted file mode 100644
index f1afd23bab1338..00000000000000
--- a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/greater.pass.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// <iterator>
-
-// __unconstrained_reverse_iterator
-
-// template <RandomAccessIterator Iter1, RandomAccessIterator Iter2>
-// requires HasGreater<Iter1, Iter2>
-// bool operator>(const __unconstrained_reverse_iterator<Iter1>& x, const __unconstrained_reverse_iterator<Iter2>& y); // constexpr in C++17
-
-#include <iterator>
-#include <cassert>
-
-#include "test_macros.h"
-#include "test_iterators.h"
-
-template <class It>
-TEST_CONSTEXPR_CXX17 void test(It l, It r, bool x) {
- const std::__unconstrained_reverse_iterator<It> r1(l);
- const std::__unconstrained_reverse_iterator<It> r2(r);
- assert((r1 > r2) == x);
-}
-
-TEST_CONSTEXPR_CXX17 bool tests() {
- const char* s = "1234567890";
- test(random_access_iterator<const char*>(s), random_access_iterator<const char*>(s), false);
- test(random_access_iterator<const char*>(s), random_access_iterator<const char*>(s+1), true);
- test(random_access_iterator<const char*>(s+1), random_access_iterator<const char*>(s), false);
- test(s, s, false);
- test(s, s+1, true);
- test(s+1, s, false);
- return true;
-}
-
-int main(int, char**) {
- tests();
-#if TEST_STD_VER > 14
- static_assert(tests(), "");
-#endif
- return 0;
-}
diff --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/less-equal.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/less-equal.pass.cpp
deleted file mode 100644
index c710212308fac7..00000000000000
--- a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/less-equal.pass.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// <iterator>
-
-// __unconstrained_reverse_iterator
-
-// template <RandomAccessIterator Iter1, RandomAccessIterator Iter2>
-// requires HasGreater<Iter1, Iter2>
-// bool operator<=(const __unconstrained_reverse_iterator<Iter1>& x, const __unconstrained_reverse_iterator<Iter2>& y); // constexpr in C++17
-
-#include <iterator>
-#include <cassert>
-
-#include "test_macros.h"
-#include "test_iterators.h"
-
-template <class It>
-TEST_CONSTEXPR_CXX17 void test(It l, It r, bool x) {
- const std::__unconstrained_reverse_iterator<It> r1(l);
- const std::__unconstrained_reverse_iterator<It> r2(r);
- assert((r1 <= r2) == x);
-}
-
-TEST_CONSTEXPR_CXX17 bool tests() {
- const char* s = "1234567890";
- test(random_access_iterator<const char*>(s), random_access_iterator<const char*>(s), true);
- test(random_access_iterator<const char*>(s), random_access_iterator<const char*>(s+1), false);
- test(random_access_iterator<const char*>(s+1), random_access_iterator<const char*>(s), true);
- test(s, s, true);
- test(s, s+1, false);
- test(s+1, s, true);
- return true;
-}
-
-int main(int, char**) {
- tests();
-#if TEST_STD_VER > 14
- static_assert(tests(), "");
-#endif
- return 0;
-}
diff --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/less.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/less.pass.cpp
deleted file mode 100644
index ffd3a0323373fb..00000000000000
--- a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/less.pass.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// <iterator>
-
-// __unconstrained_reverse_iterator
-
-// template <RandomAccessIterator Iter1, RandomAccessIterator Iter2>
-// requires HasGreater<Iter1, Iter2>
-// bool operator<(const __unconstrained_reverse_iterator<Iter1>& x, const __unconstrained_reverse_iterator<Iter2>& y); // constexpr in C++17
-
-#include <iterator>
-#include <cassert>
-
-#include "test_macros.h"
-#include "test_iterators.h"
-
-template <class It>
-TEST_CONSTEXPR_CXX17 void test(It l, It r, bool x) {
- const std::__unconstrained_reverse_iterator<It> r1(l);
- const std::__unconstrained_reverse_iterator<It> r2(r);
- assert((r1 < r2) == x);
-}
-
-TEST_CONSTEXPR_CXX17 bool tests() {
- const char* s = "1234567890";
- test(random_access_iterator<const char*>(s), random_access_iterator<const char*>(s), false);
- test(random_access_iterator<const char*>(s), random_access_iterator<const char*>(s+1), false);
- test(random_access_iterator<const char*>(s+1), random_access_iterator<const char*>(s), true);
- test(s, s, false);
- test(s, s+1, false);
- test(s+1, s, true);
- return true;
-}
-
-int main(int, char**) {
- tests();
-#if TEST_STD_VER > 14
- static_assert(tests(), "");
-#endif
- return 0;
-}
diff --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/not-equal.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/not-equal.pass.cpp
deleted file mode 100644
index 614f159cc80522..00000000000000
--- a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cmp/not-equal.pass.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// <iterator>
-
-// __unconstrained_reverse_iterator
-
-// template <BidirectionalIterator Iter1, BidirectionalIterator Iter2>
-// requires HasEqualTo<Iter1, Iter2>
-// bool operator!=(const __unconstrained_reverse_iterator<Iter1>& x, const __unconstrained_reverse_iterator<Iter2>& y); // constexpr in C++17
-
-#include <iterator>
-#include <cassert>
-
-#include "test_macros.h"
-#include "test_iterators.h"
-
-template <class It>
-TEST_CONSTEXPR_CXX17 void test(It l, It r, bool x) {
- const std::__unconstrained_reverse_iterator<It> r1(l);
- const std::__unconstrained_reverse_iterator<It> r2(r);
- assert((r1 != r2) == x);
-}
-
-TEST_CONSTEXPR_CXX17 bool tests() {
- const char* s = "1234567890";
- test(bidirectional_iterator<const char*>(s), bidirectional_iterator<const char*>(s), false);
- test(bidirectional_iterator<const char*>(s), bidirectional_iterator<const char*>(s+1), true);
- test(random_access_iterator<const char*>(s), random_access_iterator<const char*>(s), false);
- test(random_access_iterator<const char*>(s), random_access_iterator<const char*>(s+1), true);
- test(s, s, false);
- test(s, s+1, true);
- return true;
-}
-
-int main(int, char**) {
- tests();
-#if TEST_STD_VER > 14
- static_assert(tests(), "");
-#endif
- return 0;
-}
diff --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/assign.LWG3435.verify.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/assign.LWG3435.verify.cpp
deleted file mode 100644
index 835e2b65c19c20..00000000000000
--- a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/assign.LWG3435.verify.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// <iterator>
-
-// __unconstrained_reverse_iterator
-
-// template <class U>
-// requires !same_as<U, Iter> && convertible_to<const U&, Iter> && assignable_from<Iter&, const U&>
-// __unconstrained_reverse_iterator& operator=(const __unconstrained_reverse_iterator<U>& u);
-
-#include <iterator>
-
-struct Base { };
-struct Derived : Base { };
-
-void test() {
- std::__unconstrained_reverse_iterator<Base*> base;
- std::__unconstrained_reverse_iterator<Derived*> derived;
- derived = base; // expected-error {{no viable overloaded '='}}
-}
diff --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/ctor.default.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/ctor.default.pass.cpp
deleted file mode 100644
index 66972d7243cc83..00000000000000
--- a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/ctor.default.pass.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// <iterator>
-
-// __unconstrained_reverse_iterator
-
-// __unconstrained_reverse_iterator(); // constexpr since C++17
-
-#include <iterator>
-
-#include "test_macros.h"
-#include "test_iterators.h"
-
-template <class It>
-TEST_CONSTEXPR_CXX17 void test() {
- std::__unconstrained_reverse_iterator<It> r;
- (void)r;
-}
-
-TEST_CONSTEXPR_CXX17 bool tests() {
- test<bidirectional_iterator<const char*> >();
- test<random_access_iterator<char*> >();
- test<char*>();
- test<const char*>();
- return true;
-}
-
-int main(int, char**) {
- tests();
-#if TEST_STD_VER > 14
- static_assert(tests(), "");
-#endif
- return 0;
-}
diff --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/ctor.iter.explicit.verify.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/ctor.iter.explicit.verify.cpp
deleted file mode 100644
index 6440e284f6a016..00000000000000
--- a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/ctor.iter.explicit.verify.cpp
+++ /dev/null
@@ -1,22 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// <iterator>
-
-// __unconstrained_reverse_iterator
-
-// explicit __unconstrained_reverse_iterator(Iter x);
-
-// test explicitness
-
-#include <iterator>
-
-void f() {
- char const* it = "";
- std::__unconstrained_reverse_iterator<char const*> r = it; // expected-error{{no viable conversion from 'const char *' to 'std::__unconstrained_reverse_iterator<const char *>'}}
-}
diff --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/ctor.iter.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/ctor.iter.pass.cpp
deleted file mode 100644
index e4d0874d50b5e9..00000000000000
--- a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/ctor.iter.pass.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// <iterator>
-
-// __unconstrained_reverse_iterator
-
-// explicit __unconstrained_reverse_iterator(Iter x); // constexpr since C++17
-
-#include <iterator>
-#include <cassert>
-
-#include "test_macros.h"
-#include "test_iterators.h"
-
-template <class It>
-TEST_CONSTEXPR_CXX17 void test(It i) {
- std::__unconstrained_reverse_iterator<It> r(i);
- assert(r.base() == i);
-}
-
-TEST_CONSTEXPR_CXX17 bool tests() {
- const char s[] = "123";
- test(bidirectional_iterator<const char*>(s));
- test(random_access_iterator<const char*>(s));
- test(s);
- return true;
-}
-
-int main(int, char**) {
- tests();
-#if TEST_STD_VER > 14
- static_assert(tests(), "");
-#endif
- return 0;
-}
diff --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/ctor.reverse_iterator.LWG3435.verify.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/ctor.reverse_iterator.LWG3435.verify.cpp
deleted file mode 100644
index 7ea4a61ce66020..00000000000000
--- a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.cons/ctor.reverse_iterator.LWG3435.verify.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// <iterator>
-
-// __unconstrained_reverse_iterator
-
-// template <class U>
-// requires !same_as<U, Iter> && convertible_to<const U&, Iter>
-// __unconstrained_reverse_iterator(const __unconstrained_reverse_iterator<U> &);
-
-#include <iterator>
-
-struct Base { };
-struct Derived : Base { };
-
-void test() {
- std::__unconstrained_reverse_iterator<Base*> base;
- std::__unconstrained_reverse_iterator<Derived*> derived(base); // expected-error {{no matching constructor for initialization of 'std::__unconstrained_reverse_iterator<Derived *>'}}
-}
diff --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.conv/base.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.conv/base.pass.cpp
deleted file mode 100644
index 7fd85c92b32771..00000000000000
--- a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.conv/base.pass.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// <iterator>
-
-// __unconstrained_reverse_iterator
-
-// iterator_type base() const; // constexpr since C++17
-
-#include <iterator>
-#include <cassert>
-
-#include "test_macros.h"
-#include "test_iterators.h"
-
-TEST_CONSTEXPR_CXX17 bool test() {
- typedef bidirectional_iterator<int*> Iter;
- int i = 0;
- Iter iter(&i);
- std::__unconstrained_reverse_iterator<Iter> const reverse(iter);
- std::__unconstrained_reverse_iterator<Iter>::iterator_type base = reverse.base();
- assert(base == Iter(&i));
- return true;
-}
-
-int main(int, char**) {
- test();
-#if TEST_STD_VER > 14
- static_assert(test(), "");
-#endif
- return 0;
-}
diff --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.elem/arrow.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.elem/arrow.pass.cpp
deleted file mode 100644
index f0a181bcba88f1..00000000000000
--- a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.elem/arrow.pass.cpp
+++ /dev/null
@@ -1,122 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// <iterator>
-
-// __unconstrained_reverse_iterator
-
-// pointer operator->() const; // constexpr in C++17
-
-// Be sure to respect LWG 198:
-// http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#198
-// LWG 198 was superseded by LWG 2360
-// http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2360
-
-
-#include <iterator>
-#include <list>
-#include <cassert>
-
-#include "test_macros.h"
-
-class A
-{
- int data_;
-public:
- A() : data_(1) {}
- A(const A&) = default;
- A& operator=(const A&) = default;
- ~A() {data_ = -1;}
-
- int get() const {return data_;}
-
- friend bool operator==(const A& x, const A& y)
- {return x.data_ == y.data_;}
-};
-
-template <class It>
-void
-test(It i, typename std::iterator_traits<It>::value_type x)
-{
- std::__unconstrained_reverse_iterator<It> r(i);
- assert(r->get() == x.get());
-}
-
-class B
-{
- int data_;
-public:
- B(int d=1) : data_(d) {}
- B(const B&) = default;
- B& operator=(const B&) = default;
- ~B() {data_ = -1;}
-
- int get() const {return data_;}
-
- friend bool operator==(const B& x, const B& y)
- {return x.data_ == y.data_;}
- const B *operator&() const { return nullptr; }
- B *operator&() { return nullptr; }
-};
-
-class C
-{
- int data_;
-public:
- TEST_CONSTEXPR C() : data_(1) {}
-
- TEST_CONSTEXPR int get() const {return data_;}
-
- friend TEST_CONSTEXPR bool operator==(const C& x, const C& y)
- {return x.data_ == y.data_;}
-};
-
-TEST_CONSTEXPR C gC;
-
-int main(int, char**)
-{
- A a;
- test(&a+1, A());
-
- {
- std::list<B> l;
- l.push_back(B(0));
- l.push_back(B(1));
- l.push_back(B(2));
-
- {
- std::list<B>::const_iterator i = l.begin();
- assert ( i->get() == 0 ); ++i;
- assert ( i->get() == 1 ); ++i;
- assert ( i->get() == 2 ); ++i;
- assert ( i == l.end ());
- }
-
- {
- std::list<B>::const_reverse_iterator ri = l.rbegin();
- assert ( ri->get() == 2 ); ++ri;
- assert ( ri->get() == 1 ); ++ri;
- assert ( ri->get() == 0 ); ++ri;
- assert ( ri == l.rend ());
- }
- }
-
-#if TEST_STD_VER > 14
- {
- typedef std::__unconstrained_reverse_iterator<const C *> RI;
- constexpr RI it1 = RI(&gC+1);
-
- static_assert(it1->get() == gC.get(), "");
- }
-#endif
- {
- ((void)gC);
- }
-
- return 0;
-}
diff --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.elem/bracket.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.elem/bracket.pass.cpp
deleted file mode 100644
index f9beada9e4e64b..00000000000000
--- a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.elem/bracket.pass.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// <iterator>
-
-// __unconstrained_reverse_iterator
-
-// requires RandomAccessIterator<Iter>
-// unspecified operator[](difference_type n) const; // constexpr since C++17
-
-#include <iterator>
-#include <cassert>
-
-#include "test_macros.h"
-#include "test_iterators.h"
-
-template <class It>
-TEST_CONSTEXPR_CXX17 void test(It i,
- typename std::iterator_traits<It>::difference_type n,
- typename std::iterator_traits<It>::value_type x) {
- typedef typename std::iterator_traits<It>::value_type value_type;
- const std::__unconstrained_reverse_iterator<It> r(i);
- value_type rr = r[n];
- assert(rr == x);
-}
-
-TEST_CONSTEXPR_CXX17 bool tests() {
- const char* s = "1234567890";
- test(random_access_iterator<const char*>(s+5), 4, '1');
- test(random_access_iterator<const char*>(s+5), 0, '5');
- test(s+5, 4, '1');
- test(s+5, 0, '5');
- return true;
-}
-
-int main(int, char**) {
- tests();
-#if TEST_STD_VER > 14
- static_assert(tests(), "");
-#endif
- return 0;
-}
diff --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.elem/dereference.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.elem/dereference.pass.cpp
deleted file mode 100644
index bd6b6e0df038de..00000000000000
--- a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.elem/dereference.pass.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// <iterator>
-
-// __unconstrained_reverse_iterator
-
-// reference operator*() const; // constexpr in C++17
-
-// Be sure to respect LWG 198:
-// http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#198
-// LWG 198 was superseded by LWG 2360
-// http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2360
-
-#include <iterator>
-#include <cassert>
-
-#include "test_macros.h"
-
-class A
-{
- int data_;
-public:
- A() : data_(1) {}
- A(const A&) = default;
- A& operator=(const A&) = default;
- ~A() {data_ = -1;}
-
- friend bool operator==(const A& x, const A& y)
- {return x.data_ == y.data_;}
-};
-
-template <class It>
-void
-test(It i, typename std::iterator_traits<It>::value_type x)
-{
- std::__unconstrained_reverse_iterator<It> r(i);
- assert(*r == x);
-}
-
-int main(int, char**)
-{
- A a;
- test(&a+1, A());
-
-#if TEST_STD_VER > 14
- {
- constexpr const char *p = "123456789";
- typedef std::__unconstrained_reverse_iterator<const char *> RI;
- constexpr RI it1 = RI(p+1);
- constexpr RI it2 = RI(p+2);
- static_assert(*it1 == p[0], "");
- static_assert(*it2 == p[1], "");
- }
-#endif
-
- return 0;
-}
diff --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/decrement-assign.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/decrement-assign.pass.cpp
deleted file mode 100644
index 48be8a7399e427..00000000000000
--- a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/decrement-assign.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// <iterator>
-
-// __unconstrained_reverse_iterator
-
-// requires RandomAccessIterator<Iter>
-// __unconstrained_reverse_iterator& operator-=(difference_type n); // constexpr in C++17
-
-#include <iterator>
-#include <cassert>
-
-#include "test_macros.h"
-#include "test_iterators.h"
-
-template <class It>
-TEST_CONSTEXPR_CXX17 void test(It i, typename std::iterator_traits<It>::difference_type n, It x) {
- std::__unconstrained_reverse_iterator<It> r(i);
- std::__unconstrained_reverse_iterator<It>& rr = r -= n;
- assert(r.base() == x);
- assert(&rr == &r);
-}
-
-TEST_CONSTEXPR_CXX17 bool tests() {
- const char* s = "1234567890";
- test(random_access_iterator<const char*>(s+5), 5, random_access_iterator<const char*>(s+10));
- test(s+5, 5, s+10);
- return true;
-}
-
-int main(int, char**) {
- tests();
-#if TEST_STD_VER > 14
- static_assert(tests(), "");
-#endif
- return 0;
-}
diff --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/increment-assign.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/increment-assign.pass.cpp
deleted file mode 100644
index 115d95e1485cdc..00000000000000
--- a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/increment-assign.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// <iterator>
-
-// __unconstrained_reverse_iterator
-
-// requires RandomAccessIterator<Iter>
-// __unconstrained_reverse_iterator& operator+=(difference_type n); // constexpr in C++17
-
-#include <iterator>
-#include <cassert>
-
-#include "test_macros.h"
-#include "test_iterators.h"
-
-template <class It>
-TEST_CONSTEXPR_CXX17 void test(It i, typename std::iterator_traits<It>::difference_type n, It x) {
- std::__unconstrained_reverse_iterator<It> r(i);
- std::__unconstrained_reverse_iterator<It>& rr = r += n;
- assert(r.base() == x);
- assert(&rr == &r);
-}
-
-TEST_CONSTEXPR_CXX17 bool tests() {
- char const* s = "1234567890";
- test(random_access_iterator<const char*>(s+5), 5, random_access_iterator<const char*>(s));
- test(s+5, 5, s);
- return true;
-}
-
-int main(int, char**) {
- tests();
-#if TEST_STD_VER > 14
- static_assert(tests(), "");
-#endif
- return 0;
-}
diff --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/minus.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/minus.pass.cpp
deleted file mode 100644
index c3a4d1fd9e36fb..00000000000000
--- a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/minus.pass.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// <iterator>
-
-// __unconstrained_reverse_iterator
-
-// requires RandomAccessIterator<Iter>
-// __unconstrained_reverse_iterator operator-(difference_type n) const; // constexpr in C++17
-
-#include <iterator>
-#include <cassert>
-
-#include "test_macros.h"
-#include "test_iterators.h"
-
-template <class It>
-TEST_CONSTEXPR_CXX17 void test(It i, typename std::iterator_traits<It>::difference_type n, It x) {
- const std::__unconstrained_reverse_iterator<It> r(i);
- std::__unconstrained_reverse_iterator<It> rr = r - n;
- assert(rr.base() == x);
-}
-
-TEST_CONSTEXPR_CXX17 bool tests() {
- const char* s = "1234567890";
- test(random_access_iterator<const char*>(s+5), 5, random_access_iterator<const char*>(s+10));
- test(s+5, 5, s+10);
- return true;
-}
-
-int main(int, char**) {
- tests();
-#if TEST_STD_VER > 14
- static_assert(tests(), "");
-#endif
- return 0;
-}
diff --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/plus.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/plus.pass.cpp
deleted file mode 100644
index 164c5abe8a3533..00000000000000
--- a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/plus.pass.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// <iterator>
-
-// __unconstrained_reverse_iterator
-
-// requires RandomAccessIterator<Iter>
-// __unconstrained_reverse_iterator operator+(difference_type n) const; // constexpr in C++17
-
-#include <iterator>
-#include <cassert>
-
-#include "test_macros.h"
-#include "test_iterators.h"
-
-template <class It>
-TEST_CONSTEXPR_CXX17 void test(It i, typename std::iterator_traits<It>::difference_type n, It x) {
- const std::__unconstrained_reverse_iterator<It> r(i);
- std::__unconstrained_reverse_iterator<It> rr = r + n;
- assert(rr.base() == x);
-}
-
-TEST_CONSTEXPR_CXX17 bool tests() {
- const char* s = "1234567890";
- test(random_access_iterator<const char*>(s+5), 5, random_access_iterator<const char*>(s));
- test(s+5, 5, s);
- return true;
-}
-
-int main(int, char**) {
- tests();
-#if TEST_STD_VER > 14
- static_assert(tests(), "");
-#endif
- return 0;
-}
diff --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/postdecrement.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/postdecrement.pass.cpp
deleted file mode 100644
index 3220c1f9b1eb1d..00000000000000
--- a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/postdecrement.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// <iterator>
-
-// __unconstrained_reverse_iterator
-
-// __unconstrained_reverse_iterator operator--(int); // constexpr in C++17
-
-#include <iterator>
-#include <cassert>
-
-#include "test_macros.h"
-#include "test_iterators.h"
-
-template <class It>
-TEST_CONSTEXPR_CXX17 void test(It i, It x) {
- std::__unconstrained_reverse_iterator<It> r(i);
- std::__unconstrained_reverse_iterator<It> rr = r--;
- assert(r.base() == x);
- assert(rr.base() == i);
-}
-
-TEST_CONSTEXPR_CXX17 bool tests() {
- const char* s = "123";
- test(bidirectional_iterator<const char*>(s+1), bidirectional_iterator<const char*>(s+2));
- test(random_access_iterator<const char*>(s+1), random_access_iterator<const char*>(s+2));
- test(s+1, s+2);
- return true;
-}
-
-int main(int, char**) {
- tests();
-#if TEST_STD_VER > 14
- static_assert(tests(), "");
-#endif
- return 0;
-}
diff --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/postincrement.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/postincrement.pass.cpp
deleted file mode 100644
index 47477fe89545b6..00000000000000
--- a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/postincrement.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// <iterator>
-
-// __unconstrained_reverse_iterator
-
-// __unconstrained_reverse_iterator operator++(int); // constexpr in C++17
-
-#include <iterator>
-#include <cassert>
-
-#include "test_macros.h"
-#include "test_iterators.h"
-
-template <class It>
-TEST_CONSTEXPR_CXX17 void test(It i, It x) {
- std::__unconstrained_reverse_iterator<It> r(i);
- std::__unconstrained_reverse_iterator<It> rr = r++;
- assert(r.base() == x);
- assert(rr.base() == i);
-}
-
-TEST_CONSTEXPR_CXX17 bool tests() {
- const char* s = "123";
- test(bidirectional_iterator<const char*>(s+1), bidirectional_iterator<const char*>(s));
- test(random_access_iterator<const char*>(s+1), random_access_iterator<const char*>(s));
- test(s+1, s);
- return true;
-}
-
-int main(int, char**) {
- tests();
-#if TEST_STD_VER > 14
- static_assert(tests(), "");
-#endif
- return 0;
-}
diff --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/predecrement.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/predecrement.pass.cpp
deleted file mode 100644
index 6ad41aeaf17a28..00000000000000
--- a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/predecrement.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// <iterator>
-
-// __unconstrained_reverse_iterator
-
-// __unconstrained_reverse_iterator& operator--(); // constexpr in C++17
-
-#include <iterator>
-#include <cassert>
-
-#include "test_macros.h"
-#include "test_iterators.h"
-
-template <class It>
-TEST_CONSTEXPR_CXX17 void test(It i, It x) {
- std::__unconstrained_reverse_iterator<It> r(i);
- std::__unconstrained_reverse_iterator<It>& rr = --r;
- assert(r.base() == x);
- assert(&rr == &r);
-}
-
-TEST_CONSTEXPR_CXX17 bool tests() {
- const char* s = "123";
- test(bidirectional_iterator<const char*>(s+1), bidirectional_iterator<const char*>(s+2));
- test(random_access_iterator<const char*>(s+1), random_access_iterator<const char*>(s+2));
- test(s+1, s+2);
- return true;
-}
-
-int main(int, char**) {
- tests();
-#if TEST_STD_VER > 14
- static_assert(tests(), "");
-#endif
- return 0;
-}
diff --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/preincrement.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/preincrement.pass.cpp
deleted file mode 100644
index 9c7e5b41738e9b..00000000000000
--- a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nav/preincrement.pass.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// <iterator>
-
-// __unconstrained_reverse_iterator
-
-// __unconstrained_reverse_iterator& operator++(); // constexpr in C++17
-
-#include <iterator>
-#include <cassert>
-
-#include "test_macros.h"
-#include "test_iterators.h"
-
-template <class It>
-TEST_CONSTEXPR_CXX17 void test(It i, It x) {
- std::__unconstrained_reverse_iterator<It> r(i);
- std::__unconstrained_reverse_iterator<It>& rr = ++r;
- assert(r.base() == x);
- assert(&rr == &r);
-}
-
-TEST_CONSTEXPR_CXX17 bool tests() {
- const char* s = "123";
- test(bidirectional_iterator<const char*>(s+1), bidirectional_iterator<const char*>(s));
- test(random_access_iterator<const char*>(s+1), random_access_iterator<const char*>(s));
- test(s+1, s);
- return true;
-}
-
-int main(int, char**) {
- tests();
-#if TEST_STD_VER > 14
- static_assert(tests(), "");
-#endif
- return 0;
-}
diff --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nonmember/minus.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nonmember/minus.pass.cpp
deleted file mode 100644
index 632e2655dea07f..00000000000000
--- a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/reverse.iter.nonmember/minus.pass.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// <iterator>
-
-// __unconstrained_reverse_iterator
-
-// template <RandomAccessIterator Iter1, RandomAccessIterator Iter2>
-// requires HasMinus<Iter2, Iter1>
-// auto operator-(const __unconstrained_reverse_iterator<Iter1>& x, const __unconstrained_reverse_iterator<Iter2>& y) // constexpr in C++17
-// -> decltype(y.base() - x.base());
-
-#include <iterator>
-#include <cstddef>
-#include <cassert>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "test_iterators.h"
-
-template <class, class, class = void> struct HasMinus : std::false_type {};
-template <class R1, class R2> struct HasMinus<R1, R2, decltype((R1() - R2(), void()))> : std::true_type {};
-
-template <class It1, class It2>
-TEST_CONSTEXPR_CXX17 void test(It1 l, It2 r, std::ptrdiff_t x) {
- const std::__unconstrained_reverse_iterator<It1> r1(l);
- const std::__unconstrained_reverse_iterator<It2> r2(r);
- assert((r1 - r2) == x);
-}
-
-TEST_CONSTEXPR_CXX17 bool tests() {
- char s[3] = {0};
-
- // Test same base iterator type
- test(s, s, 0);
- test(s, s+1, 1);
- test(s+1, s, -1);
-
- // Test non-subtractable base iterator types
- static_assert( HasMinus<std::__unconstrained_reverse_iterator<int*>, std::__unconstrained_reverse_iterator<int*> >::value, "");
-#if TEST_STD_VER >= 11
- static_assert(!HasMinus<std::__unconstrained_reverse_iterator<int*>, std::__unconstrained_reverse_iterator<char*> >::value, "");
-#endif
-
- return true;
-}
-
-int main(int, char**) {
- tests();
-#if TEST_STD_VER > 14
- static_assert(tests(), "");
-#endif
- return 0;
-}
diff --git a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/types.compile.pass.cpp b/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/types.compile.pass.cpp
deleted file mode 100644
index f8ffef364f37a5..00000000000000
--- a/libcxx/test/libcxx/iterators/predef.iterators/__unconstrained_reverse_iterator/types.compile.pass.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// <iterator>
-
-// __unconstrained_reverse_iterator
-
-// Test nested types and data member:
-
-// template <BidirectionalIterator Iter>
-// class __unconstrained_reverse_iterator {
-// protected:
-// Iter current;
-// public:
-// iterator<typename iterator_traits<Iterator>::iterator_category,
-// typename iterator_traits<Iterator>::value_type,
-// typename iterator_traits<Iterator>::difference_type,
-// typename iterator_traits<Iterator>::pointer,
-// typename iterator_traits<Iterator>::reference> {
-// };
-
-#include <iterator>
-#include <type_traits>
-
-#include "test_macros.h"
-#include "test_iterators.h"
-
-template <class It>
-void test() {
- typedef std::__unconstrained_reverse_iterator<It> R;
- typedef std::iterator_traits<It> T;
- static_assert((std::is_same<typename R::iterator_type, It>::value), "");
- static_assert((std::is_same<typename R::value_type, typename T::value_type>::value), "");
- static_assert((std::is_same<typename R::difference_type, typename T::difference_type>::value), "");
- static_assert((std::is_same<typename R::reference, typename T::reference>::value), "");
- static_assert((std::is_same<typename R::pointer, typename std::iterator_traits<It>::pointer>::value), "");
-
-#if TEST_STD_VER <= 14
- typedef std::iterator<typename T::iterator_category, typename T::value_type> iterator_base;
- static_assert((std::is_base_of<iterator_base, R>::value), "");
-#endif
-#if TEST_STD_VER > 17
- if constexpr (std::is_same_v<typename T::iterator_category, std::contiguous_iterator_tag>) {
- static_assert((std::is_same<typename R::iterator_category, std::random_access_iterator_tag>::value), "");
- } else {
- static_assert((std::is_same<typename R::iterator_category, typename T::iterator_category>::value), "");
- }
-#else
- static_assert((std::is_same<typename R::iterator_category, typename T::iterator_category>::value), "");
-#endif
-}
-
-#if TEST_STD_VER > 17
-
-struct FooIter {
- using iterator_category = std::bidirectional_iterator_tag;
- using value_type = void*;
- using difference_type = void*;
- using pointer = void*;
- using reference = int&;
- int& operator*() const;
-};
-template <>
-struct std::indirectly_readable_traits<FooIter> {
- using value_type = int;
-};
-template <>
-struct std::incrementable_traits<FooIter> {
- using difference_type = char;
-};
-
-// Not using `FooIter::value_type`.
-static_assert(std::is_same_v<typename std::__unconstrained_reverse_iterator<FooIter>::value_type, int>);
-// Not using `FooIter::difference_type`.
-static_assert(std::is_same_v<typename std::__unconstrained_reverse_iterator<FooIter>::difference_type, char>);
-
-#endif
-
-struct BarIter {
- bool& operator*() const;
-};
-template <>
-struct std::iterator_traits<BarIter> {
- using difference_type = char;
- using value_type = char;
- using pointer = char*;
- using reference = char&;
- using iterator_category = std::bidirectional_iterator_tag;
-};
-
-#if TEST_STD_VER > 17
- static_assert(std::is_same_v<typename std::__unconstrained_reverse_iterator<BarIter>::reference, bool&>);
-#else
- static_assert(std::is_same<typename std::__unconstrained_reverse_iterator<BarIter>::reference, char&>::value, "");
-#endif
-
-void test_all() {
- test<bidirectional_iterator<char*> >();
- test<random_access_iterator<char*> >();
- test<char*>();
-}
>From 4109b18ee5de1346c2b89a5c89b86bae5c8631d3 Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Mon, 18 Mar 2024 14:32:30 +0100
Subject: [PATCH 04/40] [libc++][CMake] Removes LIBCXX_ENABLE_CLANG_TIDY.
(#85262)
The clang-tidy selection in CMake was refactored in
https://github.com/llvm/llvm-project/pull/81362. During review it was
suggested to remove this CMake option.
---
libcxx/CMakeLists.txt | 5 -----
libcxx/docs/ReleaseNotes/19.rst | 4 +++-
libcxx/test/tools/CMakeLists.txt | 12 ++++--------
.../test/tools/clang_tidy_checks/CMakeLists.txt | 17 ++++++++++++-----
libcxx/utils/ci/buildkite-pipeline.yml | 1 -
libcxx/utils/ci/run-buildbot | 8 --------
6 files changed, 19 insertions(+), 28 deletions(-)
diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index e565c47c76687a..043d5a8295c1a6 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -123,7 +123,6 @@ option(LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS
to provide compile-time errors when using features unavailable on some version of
the shared library they shipped should turn this on and see `include/__availability`
for more details." OFF)
-option(LIBCXX_ENABLE_CLANG_TIDY "Whether to compile and run clang-tidy checks" OFF)
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
@@ -863,10 +862,6 @@ add_subdirectory(modules)
set(LIBCXX_TEST_DEPS "cxx_experimental")
-if (LIBCXX_ENABLE_CLANG_TIDY)
- list(APPEND LIBCXX_TEST_DEPS cxx-tidy)
-endif()
-
list(APPEND LIBCXX_TEST_DEPS generate-cxx-modules)
if (LIBCXX_INCLUDE_BENCHMARKS)
diff --git a/libcxx/docs/ReleaseNotes/19.rst b/libcxx/docs/ReleaseNotes/19.rst
index c70ae477fafc1d..1a46789da58443 100644
--- a/libcxx/docs/ReleaseNotes/19.rst
+++ b/libcxx/docs/ReleaseNotes/19.rst
@@ -82,7 +82,6 @@ Deprecations and Removals
libatomic is not available. If you are one such user, please reach out to the libc++ developers so we can collaborate
on a path for supporting atomics properly on freestanding platforms.
-
Upcoming Deprecations and Removals
----------------------------------
@@ -107,3 +106,6 @@ Build System Changes
- The ``LIBCXX_EXECUTOR`` and ``LIBCXXABI_EXECUTOR`` CMake variables have been removed. Please
set ``LIBCXX_TEST_PARAMS`` to ``executor=<...>`` instead.
+
+- The Cmake variable ``LIBCXX_ENABLE_CLANG_TIDY`` has been removed. The build system has been changed
+ to automatically detect the presence of ``clang-tidy`` and the required ``Clang`` libraries.
diff --git a/libcxx/test/tools/CMakeLists.txt b/libcxx/test/tools/CMakeLists.txt
index e30ad6cdd8201f..6d99c53ad46d9f 100644
--- a/libcxx/test/tools/CMakeLists.txt
+++ b/libcxx/test/tools/CMakeLists.txt
@@ -1,12 +1,8 @@
set(LIBCXX_TEST_TOOLS_PATH ${CMAKE_CURRENT_BINARY_DIR} PARENT_SCOPE)
-# TODO: Remove LIBCXX_ENABLE_CLANG_TIDY
-if(LIBCXX_ENABLE_CLANG_TIDY)
- if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
- message(STATUS "Clang-tidy can only be used when building libc++ with "
- "a clang compiler.")
- return()
- endif()
- add_subdirectory(clang_tidy_checks)
+if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
+ message(STATUS "Clang-tidy tests are disabled due to non-clang based compiler.")
+ return()
endif()
+add_subdirectory(clang_tidy_checks)
diff --git a/libcxx/test/tools/clang_tidy_checks/CMakeLists.txt b/libcxx/test/tools/clang_tidy_checks/CMakeLists.txt
index 74905a0c3ed1c4..2e5d8aa2fce74b 100644
--- a/libcxx/test/tools/clang_tidy_checks/CMakeLists.txt
+++ b/libcxx/test/tools/clang_tidy_checks/CMakeLists.txt
@@ -9,6 +9,17 @@ set(Clang_DIR_SAVE ${Clang_DIR})
# versions must match. Otherwise there likely will be ODR-violations. This had
# led to crashes and incorrect output of the clang-tidy based checks.
find_package(Clang ${CMAKE_CXX_COMPILER_VERSION})
+if(NOT Clang_FOUND)
+ message(STATUS "Clang-tidy tests are disabled since the "
+ "Clang development package is unavailable.")
+ return()
+endif()
+if(NOT TARGET clangTidy)
+ message(STATUS "Clang-tidy tests are disabled since the "
+ "Clang development package has no clangTidy target.")
+ return()
+endif()
+message(STATUS "Clang-tidy tests are enabled.")
set(SOURCES
abi_tag_on_virtual.cpp
@@ -22,11 +33,7 @@ set(SOURCES
libcpp_module.cpp
)
-if(NOT Clang_FOUND)
- message(STATUS "Could not find a suitable version of the Clang development package;
- custom libc++ clang-tidy checks will not be available.")
- return()
-endif()
+list(APPEND LIBCXX_TEST_DEPS cxx-tidy)
set(LLVM_DIR "${LLVM_DIR_SAVE}" CACHE PATH "The directory containing a CMake configuration file for LLVM." FORCE)
set(Clang_DIR "${Clang_DIR_SAVE}" CACHE PATH "The directory containing a CMake configuration file for Clang." FORCE)
diff --git a/libcxx/utils/ci/buildkite-pipeline.yml b/libcxx/utils/ci/buildkite-pipeline.yml
index e42262620d5fb0..0761b40c784d42 100644
--- a/libcxx/utils/ci/buildkite-pipeline.yml
+++ b/libcxx/utils/ci/buildkite-pipeline.yml
@@ -43,7 +43,6 @@ definitions:
environment_definitions:
_common_env: &common_env
- ENABLE_CLANG_TIDY: "On"
LLVM_SYMBOLIZER_PATH: "/usr/bin/llvm-symbolizer-${LLVM_HEAD_VERSION}"
CLANG_CRASH_DIAGNOSTICS_DIR: "crash_diagnostics"
CC: clang-${LLVM_HEAD_VERSION}
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 2905745355b68e..10b0ed607bce79 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -44,9 +44,6 @@ CMAKE The CMake binary to use. This variable is optional.
CLANG_FORMAT The clang-format binary to use when generating the format
ignore list.
-ENABLE_CLANG_TIDY Whether to compile and run clang-tidy checks. This variable
- is optional.
-
EOF
}
@@ -111,10 +108,6 @@ function clean() {
rm -rf "${BUILD_DIR}"
}
-if [ -z "${ENABLE_CLANG_TIDY}" ]; then
- ENABLE_CLANG_TIDY=Off
-fi
-
function generate-cmake-base() {
echo "--- Generating CMake"
${CMAKE} \
@@ -126,7 +119,6 @@ function generate-cmake-base() {
-DLIBCXX_ENABLE_WERROR=YES \
-DLIBCXXABI_ENABLE_WERROR=YES \
-DLIBUNWIND_ENABLE_WERROR=YES \
- -DLIBCXX_ENABLE_CLANG_TIDY=${ENABLE_CLANG_TIDY} \
-DLLVM_LIT_ARGS="-sv --xunit-xml-output test-results.xml --timeout=1500 --time-tests" \
"${@}"
}
>From 1d9fb2ee612f0ccf588d40dc4b5445cffd36e8af Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Mon, 18 Mar 2024 14:52:07 +0100
Subject: [PATCH 05/40] [clang][Interp] Disable CFStringMakeConstantString test
on AIX
It's not only the fist CFSTR call that's broken on AIX.
See https://github.com/llvm/llvm-project/commit/0a739eb75fe68b1cec4e4aaad8b5395bb5da9a89#commitcomment-139910542
---
clang/test/AST/Interp/builtin-functions.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/test/AST/Interp/builtin-functions.cpp b/clang/test/AST/Interp/builtin-functions.cpp
index a09c6d3acca5da..2e9d1a831dcf6e 100644
--- a/clang/test/AST/Interp/builtin-functions.cpp
+++ b/clang/test/AST/Interp/builtin-functions.cpp
@@ -516,8 +516,8 @@ void test7(void) {
const void *X;
#if !defined(_AIX)
X = CFSTR("\242"); // both-warning {{input conversion stopped}}
-#endif
X = CFSTR("\0"); // no-warning
X = CFSTR(242); // both-error {{cannot initialize a parameter of type 'const char *' with an rvalue of type 'int'}}
X = CFSTR("foo", "bar"); // both-error {{too many arguments to function call}}
+#endif
}
>From 4294841ebcbb22076a24267cdf5164c7aeed9941 Mon Sep 17 00:00:00 2001
From: Timm Baeder <tbaeder at redhat.com>
Date: Mon, 18 Mar 2024 14:56:16 +0100
Subject: [PATCH 06/40] [clang][ExprConst] Can't be past an invalid LValue
designator (#84293)
For the test case in C, both `LV.getLValueOffset()` and
`Ctx.getTypeSizeInChars(Ty)` are zero, so we return `true` from
`isOnePastTheEndOfCompleteObject()` and ultimately diagnose this as
being one past the end, but the diagnostic doesn't make sense.
---
clang/lib/AST/ExprConstant.cpp | 7 ++++++-
clang/test/AST/Interp/c.c | 4 ++--
clang/test/Sema/const-eval.c | 3 +--
clang/test/Sema/constexpr-void-cast.c | 14 ++++++++++++++
4 files changed, 23 insertions(+), 5 deletions(-)
create mode 100644 clang/test/Sema/constexpr-void-cast.c
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 80f49714b64f0f..592d43597dc1b4 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -9253,7 +9253,8 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr *E) {
Info.getLangOpts().CPlusPlus26)) {
// Permitted.
} else {
- if (SubExpr->getType()->isVoidPointerType()) {
+ if (SubExpr->getType()->isVoidPointerType() &&
+ Info.getLangOpts().CPlusPlus) {
if (HasValidResult)
CCEDiag(E, diag::note_constexpr_invalid_void_star_cast)
<< SubExpr->getType() << Info.getLangOpts().CPlusPlus26
@@ -12942,6 +12943,10 @@ static bool isOnePastTheEndOfCompleteObject(const ASTContext &Ctx,
if (Ty->isIncompleteType())
return true;
+ // Can't be past the end of an invalid object.
+ if (LV.getLValueDesignator().Invalid)
+ return false;
+
// We're a past-the-end pointer if we point to the byte after the object,
// no matter what our type or path is.
auto Size = Ctx.getTypeSizeInChars(Ty);
diff --git a/clang/test/AST/Interp/c.c b/clang/test/AST/Interp/c.c
index 5caebd9ce6f14e..10e23839f2ba2a 100644
--- a/clang/test/AST/Interp/c.c
+++ b/clang/test/AST/Interp/c.c
@@ -66,11 +66,11 @@ _Static_assert((&a - 100) != 0, ""); // pedantic-ref-warning {{is a GNU extensio
// pedantic-ref-note {{-100 of non-array}} \
// pedantic-expected-note {{-100 of non-array}}
/// extern variable of a composite type.
-/// FIXME: The 'cast from void*' note is missing in the new interpreter.
+/// FIXME: The 'this conversion is not allowed' note is missing in the new interpreter.
extern struct Test50S Test50;
_Static_assert(&Test50 != (void*)0, ""); // all-warning {{always true}} \
// pedantic-ref-warning {{is a GNU extension}} \
- // pedantic-ref-note {{cast from 'void *' is not allowed}} \
+ // pedantic-ref-note {{this conversion is not allowed in a constant expression}} \
// pedantic-expected-warning {{is a GNU extension}}
struct y {int x,y;};
diff --git a/clang/test/Sema/const-eval.c b/clang/test/Sema/const-eval.c
index 2e38d5e23c208a..e358aceaad5a43 100644
--- a/clang/test/Sema/const-eval.c
+++ b/clang/test/Sema/const-eval.c
@@ -134,8 +134,7 @@ void PR21945(void) { int i = (({}), 0l); }
void PR24622(void);
struct PR24622 {} pr24622;
-EVAL_EXPR(52, &pr24622 == (void *)&PR24622); // expected-error {{not an integer constant expression}}
- // expected-note at -1 {{past the end}}
+EVAL_EXPR(52, &pr24622 == (void *)&PR24622);
// We evaluate these by providing 2s' complement semantics in constant
// expressions, like we do for integers.
diff --git a/clang/test/Sema/constexpr-void-cast.c b/clang/test/Sema/constexpr-void-cast.c
new file mode 100644
index 00000000000000..c5caa3b9e58feb
--- /dev/null
+++ b/clang/test/Sema/constexpr-void-cast.c
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 -x c -fsyntax-only %s -verify=c
+// RUN: %clang_cc1 -x c -fsyntax-only %s -pedantic -verify=c-pedantic
+//
+// RUN: %clang_cc1 -x c++ -fsyntax-only %s -verify=cxx
+// RUN: %clang_cc1 -x c++ -fsyntax-only %s -pedantic -verify=cxx-pedantic
+
+// c-no-diagnostics
+// cxx-no-diagnostics
+
+void f(void);
+struct S {char c;} s;
+_Static_assert(&s != (void *)&f, ""); // c-pedantic-warning {{not an integer constant expression}} \
+ // c-pedantic-note {{this conversion is not allowed in a constant expression}} \
+ // cxx-pedantic-warning {{'_Static_assert' is a C11 extension}}
>From a8bda0b4a6eb454cb437105efc98c807bd5c4f6d Mon Sep 17 00:00:00 2001
From: Benji Smith <6193112+Benjins at users.noreply.github.com>
Date: Mon, 18 Mar 2024 09:58:22 -0400
Subject: [PATCH 07/40] [C API] Add accessors for function prefix and prologue
data (#82193)
A test is added to echo.ll, and the echo.cpp part of llvm-c-test is
updated to clone a function's prefix and prologue.
---
llvm/docs/ReleaseNotes.rst | 12 ++++++++++
llvm/include/llvm-c/Core.h | 38 +++++++++++++++++++++++++++++++
llvm/lib/IR/Core.cpp | 32 ++++++++++++++++++++++++++
llvm/test/Bindings/llvm-c/echo.ll | 14 ++++++++++++
llvm/tools/llvm-c-test/echo.cpp | 8 +++++++
5 files changed, 104 insertions(+)
diff --git a/llvm/docs/ReleaseNotes.rst b/llvm/docs/ReleaseNotes.rst
index 7be51730663bd1..5e5b94255359b2 100644
--- a/llvm/docs/ReleaseNotes.rst
+++ b/llvm/docs/ReleaseNotes.rst
@@ -131,6 +131,18 @@ Changes to the C API
* Added ``LLVMConstStringInContext2`` function, which better matches the C++
API by using ``size_t`` for string length. Deprecated ``LLVMConstStringInContext``.
+* Added the following functions for accessing a function's prefix data:
+
+ * ``LLVMHasPrefixData``
+ * ``LLVMGetPrefixData``
+ * ``LLVMSetPrefixData``
+
+* Added the following functions for accessing a function's prologue data:
+
+ * ``LLVMHasPrologueData``
+ * ``LLVMGetPrologueData``
+ * ``LLVMSetPrologueData``
+
Changes to the CodeGen infrastructure
-------------------------------------
diff --git a/llvm/include/llvm-c/Core.h b/llvm/include/llvm-c/Core.h
index 8fe9ff5fd7e9ad..f56a6c961aad74 100644
--- a/llvm/include/llvm-c/Core.h
+++ b/llvm/include/llvm-c/Core.h
@@ -2748,6 +2748,44 @@ const char *LLVMGetGC(LLVMValueRef Fn);
*/
void LLVMSetGC(LLVMValueRef Fn, const char *Name);
+/**
+ * Gets the prefix data associated with a function. Only valid on functions, and
+ * only if LLVMHasPrefixData returns true.
+ * See https://llvm.org/docs/LangRef.html#prefix-data
+ */
+LLVMValueRef LLVMGetPrefixData(LLVMValueRef Fn);
+
+/**
+ * Check if a given function has prefix data. Only valid on functions.
+ * See https://llvm.org/docs/LangRef.html#prefix-data
+ */
+LLVMBool LLVMHasPrefixData(LLVMValueRef Fn);
+
+/**
+ * Sets the prefix data for the function. Only valid on functions.
+ * See https://llvm.org/docs/LangRef.html#prefix-data
+ */
+void LLVMSetPrefixData(LLVMValueRef Fn, LLVMValueRef prefixData);
+
+/**
+ * Gets the prologue data associated with a function. Only valid on functions,
+ * and only if LLVMHasPrologueData returns true.
+ * See https://llvm.org/docs/LangRef.html#prologue-data
+ */
+LLVMValueRef LLVMGetPrologueData(LLVMValueRef Fn);
+
+/**
+ * Check if a given function has prologue data. Only valid on functions.
+ * See https://llvm.org/docs/LangRef.html#prologue-data
+ */
+LLVMBool LLVMHasPrologueData(LLVMValueRef Fn);
+
+/**
+ * Sets the prologue data for the function. Only valid on functions.
+ * See https://llvm.org/docs/LangRef.html#prologue-data
+ */
+void LLVMSetPrologueData(LLVMValueRef Fn, LLVMValueRef prologueData);
+
/**
* Add an attribute to a function.
*
diff --git a/llvm/lib/IR/Core.cpp b/llvm/lib/IR/Core.cpp
index aacb163a0d4f09..023cabc46911e5 100644
--- a/llvm/lib/IR/Core.cpp
+++ b/llvm/lib/IR/Core.cpp
@@ -2422,6 +2422,38 @@ void LLVMSetGC(LLVMValueRef Fn, const char *GC) {
F->clearGC();
}
+LLVMValueRef LLVMGetPrefixData(LLVMValueRef Fn) {
+ Function *F = unwrap<Function>(Fn);
+ return wrap(F->getPrefixData());
+}
+
+LLVMBool LLVMHasPrefixData(LLVMValueRef Fn) {
+ Function *F = unwrap<Function>(Fn);
+ return F->hasPrefixData();
+}
+
+void LLVMSetPrefixData(LLVMValueRef Fn, LLVMValueRef prefixData) {
+ Function *F = unwrap<Function>(Fn);
+ Constant *prefix = unwrap<Constant>(prefixData);
+ F->setPrefixData(prefix);
+}
+
+LLVMValueRef LLVMGetPrologueData(LLVMValueRef Fn) {
+ Function *F = unwrap<Function>(Fn);
+ return wrap(F->getPrologueData());
+}
+
+LLVMBool LLVMHasPrologueData(LLVMValueRef Fn) {
+ Function *F = unwrap<Function>(Fn);
+ return F->hasPrologueData();
+}
+
+void LLVMSetPrologueData(LLVMValueRef Fn, LLVMValueRef prologueData) {
+ Function *F = unwrap<Function>(Fn);
+ Constant *prologue = unwrap<Constant>(prologueData);
+ F->setPrologueData(prologue);
+}
+
void LLVMAddAttributeAtIndex(LLVMValueRef F, LLVMAttributeIndex Idx,
LLVMAttributeRef A) {
unwrap<Function>(F)->addAttributeAtIndex(Idx, unwrap(A));
diff --git a/llvm/test/Bindings/llvm-c/echo.ll b/llvm/test/Bindings/llvm-c/echo.ll
index be0207599478b8..953a16b7e624e1 100644
--- a/llvm/test/Bindings/llvm-c/echo.ll
+++ b/llvm/test/Bindings/llvm-c/echo.ll
@@ -334,6 +334,20 @@ define void @test_fast_math_flags_call_outer(float %a) {
ret void
}
+define void @test_func_prefix_data_01() prefix i32 123 {
+ ret void
+}
+
+define void @test_func_prefix_data_02() prefix i64 2000 {
+ ret void
+}
+
+%func_prolog_struct = type <{ i8, i8, ptr }>
+
+define void @test_func_prologue_data_01() prologue %func_prolog_struct <{ i8 235, i8 8, ptr zeroinitializer}> {
+ ret void
+}
+
!llvm.dbg.cu = !{!0, !2}
!llvm.module.flags = !{!3}
diff --git a/llvm/tools/llvm-c-test/echo.cpp b/llvm/tools/llvm-c-test/echo.cpp
index bc708e2d472edd..347863638849ce 100644
--- a/llvm/tools/llvm-c-test/echo.cpp
+++ b/llvm/tools/llvm-c-test/echo.cpp
@@ -1397,6 +1397,14 @@ static void clone_symbols(LLVMModuleRef Src, LLVMModuleRef M) {
}
LLVMDisposeValueMetadataEntries(AllMetadata);
+ // Copy any prefix data that may be on the function
+ if (LLVMHasPrefixData(Cur))
+ LLVMSetPrefixData(Fun, clone_constant(LLVMGetPrefixData(Cur), M));
+
+ // Copy any prologue data that may be on the function
+ if (LLVMHasPrologueData(Cur))
+ LLVMSetPrologueData(Fun, clone_constant(LLVMGetPrologueData(Cur), M));
+
FunCloner FC(Cur, Fun);
FC.CloneBBs(Cur);
>From 276847a65af67bdc4eb79989f196d1968cb50ae6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miguel=20Raz=20Guzm=C3=A1n=20Macedo?=
<miguelraz at ciencias.unam.mx>
Date: Mon, 18 Mar 2024 08:02:26 -0600
Subject: [PATCH 08/40] [LangRef][IR] Add 3-way compare intrinsics
llvm.scmp/llvm.ucmp (#83227)
This PR adds the `[us]cmp` intrinsics to the LangRef, `Intrinsics.td`
and some tests to the IRVerifier.
RFC: https://discourse.llvm.org/t/rfc-add-3-way-comparison-intrinsics/76685
---
llvm/docs/LangRef.rst | 57 +++++++++++++++++++++++++++++
llvm/include/llvm/IR/Intrinsics.td | 6 +++
llvm/lib/IR/Verifier.cpp | 23 ++++++++++++
llvm/test/Verifier/intrinsic-cmp.ll | 22 +++++++++++
4 files changed, 108 insertions(+)
create mode 100644 llvm/test/Verifier/intrinsic-cmp.ll
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index ecedd3a32c7b36..e07b642285b3e6 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -14574,6 +14574,63 @@ The arguments (``%a`` and ``%b``) may be of any integer type or a vector with
integer element type. The argument types must match each other, and the return
type must match the argument type.
+.. _int_scmp:
+
+'``llvm.scmp.*``' Intrinsic
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Syntax:
+"""""""
+
+This is an overloaded intrinsic. You can use ``@llvm.scmp`` on any
+integer bit width or any vector of integer elements.
+
+::
+
+ declare i2 @llvm.scmp.i2.i32(i32 %a, i32 %b)
+ declare <4 x i32> @llvm.scmp.v4i32.v4i32(<4 x i32> %a, <4 x i32> %b)
+
+Overview:
+"""""""""
+
+Return ``-1`` if ``%a`` is signed less than ``%b``, ``0`` if they are equal, and
+``1`` if ``%a`` is signed greater than ``%b``. Vector intrinsics operate on a per-element basis.
+
+Arguments:
+""""""""""
+
+The arguments (``%a`` and ``%b``) may be of any integer type or a vector with
+integer element type. The argument types must match each other, and the return
+type must be at least as wide as ``i2``, to hold the three possible return values.
+
+.. _int_ucmp:
+
+'``llvm.ucmp.*``' Intrinsic
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Syntax:
+"""""""
+
+This is an overloaded intrinsic. You can use ``@llvm.ucmp`` on any
+integer bit width or any vector of integer elements.
+
+::
+
+ declare i2 @llvm.ucmp.i2.i32(i32 %a, i32 %b)
+ declare <4 x i32> @llvm.ucmp.v4i32.v4i32(<4 x i32> %a, <4 x i32> %b)
+
+Overview:
+"""""""""
+
+Return ``-1`` if ``%a`` is unsigned less than ``%b``, ``0`` if they are equal, and
+``1`` if ``%a`` is unsigned greater than ``%b``. Vector intrinsics operate on a per-element basis.
+
+Arguments:
+""""""""""
+
+The arguments (``%a`` and ``%b``) may be of any integer type or a vector with
+integer element type. The argument types must match each other, and the return
+type must be at least as wide as ``i2``, to hold the three possible return values.
.. _int_memcpy:
diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td
index 144298fd7c0162..091f9b38107989 100644
--- a/llvm/include/llvm/IR/Intrinsics.td
+++ b/llvm/include/llvm/IR/Intrinsics.td
@@ -1528,6 +1528,12 @@ def int_umax : DefaultAttrsIntrinsic<
def int_umin : DefaultAttrsIntrinsic<
[llvm_anyint_ty], [LLVMMatchType<0>, LLVMMatchType<0>],
[IntrNoMem, IntrSpeculatable, IntrWillReturn]>;
+def int_scmp : DefaultAttrsIntrinsic<
+ [llvm_anyint_ty], [llvm_anyint_ty, LLVMMatchType<1>],
+ [IntrNoMem, IntrSpeculatable, IntrWillReturn]>;
+def int_ucmp : DefaultAttrsIntrinsic<
+ [llvm_anyint_ty], [llvm_anyint_ty, LLVMMatchType<1>],
+ [IntrNoMem, IntrSpeculatable, IntrWillReturn]>;
//===------------------------- Memory Use Markers -------------------------===//
//
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 2b9dc745d7bfc5..62dde2e6ad4243 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -5265,6 +5265,29 @@ void Verifier::visitIntrinsicCall(Intrinsic::ID ID, CallBase &Call) {
}
break;
}
+ case Intrinsic::ucmp:
+ case Intrinsic::scmp: {
+ Type *SrcTy = Call.getOperand(0)->getType();
+ Type *DestTy = Call.getType();
+
+ Check(DestTy->getScalarSizeInBits() >= 2,
+ "result type must be at least 2 bits wide", Call);
+
+ bool IsDestTypeVector = DestTy->isVectorTy();
+ Check(SrcTy->isVectorTy() == IsDestTypeVector,
+ "ucmp/scmp argument and result types must both be either vector or "
+ "scalar types",
+ Call);
+ if (IsDestTypeVector) {
+ auto SrcVecLen = cast<VectorType>(SrcTy)->getElementCount();
+ auto DestVecLen = cast<VectorType>(DestTy)->getElementCount();
+ Check(SrcVecLen == DestVecLen,
+ "return type and arguments must have the same number of "
+ "elements",
+ Call);
+ }
+ break;
+ }
case Intrinsic::coro_id: {
auto *InfoArg = Call.getArgOperand(3)->stripPointerCasts();
if (isa<ConstantPointerNull>(InfoArg))
diff --git a/llvm/test/Verifier/intrinsic-cmp.ll b/llvm/test/Verifier/intrinsic-cmp.ll
new file mode 100644
index 00000000000000..2224a5c5eba385
--- /dev/null
+++ b/llvm/test/Verifier/intrinsic-cmp.ll
@@ -0,0 +1,22 @@
+; RUN: not opt -S -passes=verify 2>&1 < %s | FileCheck %s
+
+define void @matching_vector_lens(<4 x i32> %arg1, <4 x i32> %arg2) {
+ ; CHECK: return type and arguments must have the same number of elements
+ %res = call <8 x i32> @llvm.scmp.v8i32.v4i32(<4 x i32> %arg1, <4 x i32> %arg2)
+ ret void
+}
+
+define void @result_len_is_at_least_2bits_wide(i32 %arg1, i32 %arg2) {
+ ; CHECK: result type must be at least 2 bits wide
+ %res2 = call i1 @llvm.scmp.i1.i32(i32 %arg1, i32 %arg2)
+ ret void
+}
+
+define void @both_args_are_vecs_or_neither(<4 x i32> %arg1, i32 %arg2) {
+ ; CHECK: ucmp/scmp argument and result types must both be either vector or scalar types
+ %res3 = call i2 @llvm.scmp.i2.v4i32(<4 x i32> %arg1, <4 x i32> %arg1)
+ ; CHECK: ucmp/scmp argument and result types must both be either vector or scalar types
+ %res4 = call <4 x i32> @llvm.scmp.v4i32.i32(i32 %arg2, i32 %arg2)
+ ret void
+}
+
>From eb264d825beb048c6e673ddaf5aca069511fcfb3 Mon Sep 17 00:00:00 2001
From: ykiko <ykikoykikoykiko at gmail.com>
Date: Mon, 18 Mar 2024 22:13:10 +0800
Subject: [PATCH 09/40] Add some missing Kinds to libclang python bindings
(#85571)
Add some Kinds existing in Index.h but missing in cindex.py.
---
clang/bindings/python/clang/cindex.py | 173 +++++++++++++++++++++++++-
clang/docs/ReleaseNotes.rst | 2 +
clang/include/clang-c/Index.h | 2 +-
3 files changed, 175 insertions(+), 2 deletions(-)
diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py
index 44a34ca196274c..302d99dccd77b5 100644
--- a/clang/bindings/python/clang/cindex.py
+++ b/clang/bindings/python/clang/cindex.py
@@ -1091,6 +1091,29 @@ def __repr__(self):
# Represents an @available(...) check.
CursorKind.OBJC_AVAILABILITY_CHECK_EXPR = CursorKind(148)
+# Fixed point literal.
+CursorKind.FIXED_POINT_LITERAL = CursorKind(149)
+
+# OpenMP 5.0 [2.1.4, Array Shaping].
+CursorKind.OMP_ARRAY_SHAPING_EXPR = CursorKind(150)
+
+# OpenMP 5.0 [2.1.6 Iterators].
+CursorKind.OMP_ITERATOR_EXPR = CursorKind(151)
+
+# OpenCL's addrspace_cast<> expression.
+CursorKind.CXX_ADDRSPACE_CAST_EXPR = CursorKind(152)
+
+# Expression that references a C++20 concept.
+CursorKind.CONCEPT_SPECIALIZATION_EXPR = CursorKind(153)
+
+# Expression that references a C++20 requires expression.
+CursorKind.REQUIRES_EXPR = CursorKind(154)
+
+# Expression that references a C++20 parenthesized list aggregate initializer.
+CursorKind.CXX_PAREN_LIST_INIT_EXPR = CursorKind(155)
+
+# Represents a C++26 pack indexing expression.
+CursorKind.PACK_INDEXING_EXPR = CursorKind(156)
# A statement whose specific kind is not exposed via this interface.
#
@@ -1312,6 +1335,114 @@ def __repr__(self):
# OpenMP teams distribute directive.
CursorKind.OMP_TEAMS_DISTRIBUTE_DIRECTIVE = CursorKind(271)
+# OpenMP teams distribute simd directive.
+CursorKind.OMP_TEAMS_DISTRIBUTE_DIRECTIVE = CursorKind(272)
+
+# OpenMP teams distribute parallel for simd directive.
+CursorKind.OMP_TEAMS_DISTRIBUTE_PARALLEL_FOR_SIMD_DIRECTIVE = CursorKind(273)
+
+# OpenMP teams distribute parallel for directive.
+CursorKind.OMP_TEAMS_DISTRIBUTE_PARALLEL_FOR_DIRECTIVE = CursorKind(274)
+
+# OpenMP target teams directive.
+CursorKind.OMP_TARGET_TEAMS_DIRECTIVE = CursorKind(275)
+
+# OpenMP target teams distribute directive.
+CursorKind.OMP_TARGET_TEAMS_DISTRIBUTE_DIRECTIVE = CursorKind(276)
+
+# OpenMP target teams distribute parallel for directive.
+CursorKind.OMP_TARGET_TEAMS_DISTRIBUTE_PARALLEL_FOR_DIRECTIVE = CursorKind(277)
+
+# OpenMP target teams distribute parallel for simd directive.
+CursorKind.OMP_TARGET_TEAMS_DISTRIBUTE_PARALLEL_FOR_SIMD_DIRECTIVE = CursorKind(278)
+
+# OpenMP target teams distribute simd directive.
+CursorKind.OMP_TARGET_TEAMS_DISTRIBUTE_SIMD_DIRECTIVE = CursorKind(279)
+
+# C++2a std::bit_cast expression.
+CursorKind.BUILTIN_BIT_CAST_EXPR = CursorKind(280)
+
+# OpenMP master taskloop directive.
+CursorKind.OMP_MASTER_TASK_LOOP_DIRECTIVE = CursorKind(281)
+
+# OpenMP parallel master taskloop directive.
+CursorKind.OMP_PARALLEL_MASTER_TASK_LOOP_DIRECTIVE = CursorKind(282)
+
+# OpenMP master taskloop simd directive.
+CursorKind.OMP_MASTER_TASK_LOOP_SIMD_DIRECTIVE = CursorKind(283)
+
+# OpenMP parallel master taskloop simd directive.
+CursorKind.OMP_PARALLEL_MASTER_TASK_LOOP_SIMD_DIRECTIVE = CursorKind(284)
+
+# OpenMP parallel master directive.
+CursorKind.OMP_PARALLEL_MASTER_DIRECTIVE = CursorKind(285)
+
+# OpenMP depobj directive.
+CursorKind.OMP_DEPOBJ_DIRECTIVE = CursorKind(286)
+
+# OpenMP scan directive.
+CursorKind.OMP_SCAN_DIRECTIVE = CursorKind(287)
+
+# OpenMP tile directive.
+CursorKind.OMP_TILE_DIRECTIVE = CursorKind(288)
+
+# OpenMP canonical loop.
+CursorKind.OMP_CANONICAL_LOOP = CursorKind(289)
+
+# OpenMP interop directive.
+CursorKind.OMP_INTEROP_DIRECTIVE = CursorKind(290)
+
+# OpenMP dispatch directive.
+CursorKind.OMP_DISPATCH_DIRECTIVE = CursorKind(291)
+
+# OpenMP masked directive.
+CursorKind.OMP_MASKED_DIRECTIVE = CursorKind(292)
+
+# OpenMP unroll directive.
+CursorKind.OMP_UNROLL_DIRECTIVE = CursorKind(293)
+
+# OpenMP metadirective directive.
+CursorKind.OMP_META_DIRECTIVE = CursorKind(294)
+
+# OpenMP loop directive.
+CursorKind.OMP_GENERIC_LOOP_DIRECTIVE = CursorKind(295)
+
+# OpenMP teams loop directive.
+CursorKind.OMP_TEAMS_GENERIC_LOOP_DIRECTIVE = CursorKind(296)
+
+# OpenMP target teams loop directive.
+CursorKind.OMP_TARGET_TEAMS_GENERIC_LOOP_DIRECTIVE = CursorKind(297)
+
+# OpenMP parallel loop directive.
+CursorKind.OMP_PARALLEL_GENERIC_LOOP_DIRECTIVE = CursorKind(298)
+
+# OpenMP target parallel loop directive.
+CursorKind.OMP_TARGET_PARALLEL_GENERIC_LOOP_DIRECTIVE = CursorKind(299)
+
+# OpenMP parallel masked directive.
+CursorKind.OMP_PARALLEL_MASKED_DIRECTIVE = CursorKind(300)
+
+# OpenMP masked taskloop directive.
+CursorKind.OMP_MASKED_TASK_LOOP_DIRECTIVE = CursorKind(301)
+
+# OpenMP masked taskloop simd directive.
+CursorKind.OMP_MASKED_TASK_LOOP_SIMD_DIRECTIVE = CursorKind(302)
+
+# OpenMP parallel masked taskloop directive.
+CursorKind.OMP_PARALLEL_MASKED_TASK_LOOP_DIRECTIVE = CursorKind(303)
+
+# OpenMP parallel masked taskloop simd directive.
+CursorKind.OMP_PARALLEL_MASKED_TASK_LOOP_SIMD_DIRECTIVE = CursorKind(304)
+
+# OpenMP error directive.
+CursorKind.OMP_ERROR_DIRECTIVE = CursorKind(305)
+
+# OpenMP scope directive.
+CursorKind.OMP_SCOPE_DIRECTIVE = CursorKind(306)
+
+# OpenACC Compute Construct.
+CursorKind.OPEN_ACC_COMPUTE_DIRECTIVE = CursorKind(320)
+
###
# Other Kinds
@@ -1349,6 +1480,24 @@ def __repr__(self):
CursorKind.DLLEXPORT_ATTR = CursorKind(418)
CursorKind.DLLIMPORT_ATTR = CursorKind(419)
+CursorKind.NS_RETURNS_RETAINED = CursorKind(420)
+CursorKind.NS_RETURNS_NOT_RETAINED = CursorKind(421)
+CursorKind.NS_RETURNS_AUTORELEASED = CursorKind(422)
+CursorKind.NS_CONSUMES_SELF = CursorKind(423)
+CursorKind.NS_CONSUMED = CursorKind(424)
+CursorKind.OBJC_EXCEPTION = CursorKind(425)
+CursorKind.OBJC_NSOBJECT = CursorKind(426)
+CursorKind.OBJC_INDEPENDENT_CLASS = CursorKind(427)
+CursorKind.OBJC_PRECISE_LIFETIME = CursorKind(428)
+CursorKind.OBJC_RETURNS_INNER_POINTER = CursorKind(429)
+CursorKind.OBJC_REQUIRES_SUPER = CursorKind(430)
+CursorKind.OBJC_ROOT_CLASS = CursorKind(431)
+CursorKind.OBJC_SUBCLASSING_RESTRICTED = CursorKind(432)
+CursorKind.OBJC_EXPLICIT_PROTOCOL_IMPL = CursorKind(433)
+CursorKind.OBJC_DESIGNATED_INITIALIZER = CursorKind(434)
+CursorKind.OBJC_RUNTIME_VISIBLE = CursorKind(435)
+CursorKind.OBJC_BOXABLE = CursorKind(436)
+CursorKind.FLAG_ENUM = CursorKind(437)
CursorKind.CONVERGENT_ATTR = CursorKind(438)
CursorKind.WARN_UNUSED_ATTR = CursorKind(439)
CursorKind.WARN_UNUSED_RESULT_ATTR = CursorKind(440)
@@ -1395,6 +1544,11 @@ class TemplateArgumentKind(BaseEnumeration):
TemplateArgumentKind.DECLARATION = TemplateArgumentKind(2)
TemplateArgumentKind.NULLPTR = TemplateArgumentKind(3)
TemplateArgumentKind.INTEGRAL = TemplateArgumentKind(4)
+TemplateArgumentKind.TEMPLATE = TemplateArgumentKind(5)
+TemplateArgumentKind.TEMPLATE_EXPANSION = TemplateArgumentKind(6)
+TemplateArgumentKind.EXPRESSION = TemplateArgumentKind(7)
+TemplateArgumentKind.PACK = TemplateArgumentKind(8)
+TemplateArgumentKind.INVALID = TemplateArgumentKind(9)
### Exception Specification Kinds ###
class ExceptionSpecificationKind(BaseEnumeration):
@@ -2240,9 +2394,26 @@ def __repr__(self):
TypeKind.OCLQUEUE = TypeKind(159)
TypeKind.OCLRESERVEID = TypeKind(160)
+TypeKind.OBJCOBJECT = TypeKind(161)
+TypeKind.OBJCCLASS = TypeKind(162)
+TypeKind.ATTRIBUTED = TypeKind(163)
+
+TypeKind.OCLINTELSUBGROUPAVCMCEPAYLOAD = TypeKind(164)
+TypeKind.OCLINTELSUBGROUPAVCIMEPAYLOAD = TypeKind(165)
+TypeKind.OCLINTELSUBGROUPAVCREFPAYLOAD = TypeKind(166)
+TypeKind.OCLINTELSUBGROUPAVCSICPAYLOAD = TypeKind(167)
+TypeKind.OCLINTELSUBGROUPAVCMCERESULT = TypeKind(168)
+TypeKind.OCLINTELSUBGROUPAVCIMERESULT = TypeKind(169)
+TypeKind.OCLINTELSUBGROUPAVCREFRESULT = TypeKind(170)
+TypeKind.OCLINTELSUBGROUPAVCSICRESULT = TypeKind(171)
+TypeKind.OCLINTELSUBGROUPAVCIMERESULTSINGLEREFERENCESTREAMOUT = TypeKind(172)
+TypeKind.OCLINTELSUBGROUPAVCIMERESULTSDUALREFERENCESTREAMOUT = TypeKind(173)
+TypeKind.OCLINTELSUBGROUPAVCIMERESULTSSINGLEREFERENCESTREAMIN = TypeKind(174)
+TypeKind.OCLINTELSUBGROUPAVCIMEDUALREFERENCESTREAMIN = TypeKind(175)
+
TypeKind.EXTVECTOR = TypeKind(176)
TypeKind.ATOMIC = TypeKind(177)
-
+TypeKind.BTFTAGATTRIBUTED = TypeKind(178)
class RefQualifierKind(BaseEnumeration):
"""Describes a specific ref-qualifier of a type."""
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index b4c90b1e3e5be2..3a74c070ff9ffe 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -550,6 +550,8 @@ Python Binding Changes
----------------------
- Exposed `CXRewriter` API as `class Rewriter`.
+- Add some missing kinds from Index.h (CursorKind: 149-156, 272-320, 420-437.
+ TemplateArgumentKind: 5-9. TypeKind: 161-175 and 178).
OpenMP Support
--------------
diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h
index 3f3620609b6ddc..60db3cf0966c02 100644
--- a/clang/include/clang-c/Index.h
+++ b/clang/include/clang-c/Index.h
@@ -1675,7 +1675,7 @@ enum CXCursorKind {
CXCursor_ConceptSpecializationExpr = 153,
/**
- * Expression that references a C++20 concept.
+ * Expression that references a C++20 requires expression.
*/
CXCursor_RequiresExpr = 154,
>From bfd1d95de270fe38a287b5f48928df56a39ff8ad Mon Sep 17 00:00:00 2001
From: Adrian Kuegel <akuegel at google.com>
Date: Mon, 18 Mar 2024 14:13:47 +0000
Subject: [PATCH 10/40] [mlir] Fix unused variable error in builds with asserts
enabled.
---
mlir/lib/Dialect/XeGPU/IR/XeGPUOps.cpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/mlir/lib/Dialect/XeGPU/IR/XeGPUOps.cpp b/mlir/lib/Dialect/XeGPU/IR/XeGPUOps.cpp
index 3a75b173b757c5..e6ecf26c224818 100644
--- a/mlir/lib/Dialect/XeGPU/IR/XeGPUOps.cpp
+++ b/mlir/lib/Dialect/XeGPU/IR/XeGPUOps.cpp
@@ -45,6 +45,7 @@ void CreateNdDescOp::build(OpBuilder &builder, OperationState &state,
Type tdesc, TypedValue<MemRefType> source,
llvm::ArrayRef<OpFoldResult> offsets) {
auto ty = source.getType();
+ (void)ty;
assert(ty && ty.hasStaticShape() && offsets.size() == (size_t)ty.getRank());
llvm::SmallVector<int64_t> staticOffsets;
>From fd93a5e3c06a90e931c645948aa73ee9894699d7 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Mon, 18 Mar 2024 14:24:51 +0000
Subject: [PATCH 11/40] [VPlan] Support match unary and binary recipes in
pattern matcher (NFC).
Generalize pattern matchers to take recipe types to match as template
arguments and use it to provide matchers for unary and binary recipes
with specific opcodes and a list of recipe types (VPWidenRecipe,
VPReplicateRecipe, VPWidenCastRecipe, VPInstruction)
The new matchers are used to simplify and generalize the code in
simplifyRecipes.
---
llvm/lib/Transforms/Vectorize/VPlan.h | 2 +
.../Transforms/Vectorize/VPlanPatternMatch.h | 157 ++++++++++++++++--
.../Transforms/Vectorize/VPlanTransforms.cpp | 56 ++-----
3 files changed, 158 insertions(+), 57 deletions(-)
diff --git a/llvm/lib/Transforms/Vectorize/VPlan.h b/llvm/lib/Transforms/Vectorize/VPlan.h
index af6d0081bffebc..d77c7554d50e4f 100644
--- a/llvm/lib/Transforms/Vectorize/VPlan.h
+++ b/llvm/lib/Transforms/Vectorize/VPlan.h
@@ -2136,6 +2136,8 @@ class VPReplicateRecipe : public VPRecipeWithIRFlags {
assert(isPredicated() && "Trying to get the mask of a unpredicated recipe");
return getOperand(getNumOperands() - 1);
}
+
+ unsigned getOpcode() const { return getUnderlyingInstr()->getOpcode(); }
};
/// A recipe for generating conditional branches on the bits of a mask.
diff --git a/llvm/lib/Transforms/Vectorize/VPlanPatternMatch.h b/llvm/lib/Transforms/Vectorize/VPlanPatternMatch.h
index b90c588b607564..aa253590694514 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanPatternMatch.h
+++ b/llvm/lib/Transforms/Vectorize/VPlanPatternMatch.h
@@ -50,13 +50,82 @@ template <typename Class> struct bind_ty {
}
};
+/// Match a specified integer value or vector of all elements of that
+/// value.
+struct specific_intval {
+ APInt Val;
+
+ specific_intval(APInt V) : Val(std::move(V)) {}
+
+ bool match(VPValue *VPV) {
+ if (!VPV->isLiveIn())
+ return false;
+ Value *V = VPV->getLiveInIRValue();
+ const auto *CI = dyn_cast<ConstantInt>(V);
+ if (!CI && V->getType()->isVectorTy())
+ if (const auto *C = dyn_cast<Constant>(V))
+ CI = dyn_cast_or_null<ConstantInt>(
+ C->getSplatValue(/*UndefsAllowed=*/false));
+
+ return CI && APInt::isSameValue(CI->getValue(), Val);
+ }
+};
+
+inline specific_intval m_SpecificInt(uint64_t V) {
+ return specific_intval(APInt(64, V));
+}
+
+/// Matching combinators
+template <typename LTy, typename RTy> struct match_combine_or {
+ LTy L;
+ RTy R;
+
+ match_combine_or(const LTy &Left, const RTy &Right) : L(Left), R(Right) {}
+
+ template <typename ITy> bool match(ITy *V) {
+ if (L.match(V))
+ return true;
+ if (R.match(V))
+ return true;
+ return false;
+ }
+};
+
+template <typename LTy, typename RTy>
+inline match_combine_or<LTy, RTy> m_CombineOr(const LTy &L, const RTy &R) {
+ return match_combine_or<LTy, RTy>(L, R);
+}
+
/// Match a VPValue, capturing it if we match.
inline bind_ty<VPValue> m_VPValue(VPValue *&V) { return V; }
-template <typename Op0_t, unsigned Opcode> struct UnaryVPInstruction_match {
+namespace detail {
+
+/// A helper to match an opcode against multiple recipe types.
+template <unsigned Opcode, typename...> struct MatchRecipeAndOpcode {};
+
+template <unsigned Opcode, typename RecipeTy>
+struct MatchRecipeAndOpcode<Opcode, RecipeTy> {
+ static bool match(const VPRecipeBase *R) {
+ auto *DefR = dyn_cast<RecipeTy>(R);
+ return DefR && DefR->getOpcode() == Opcode;
+ }
+};
+
+template <unsigned Opcode, typename RecipeTy, typename... RecipeTys>
+struct MatchRecipeAndOpcode<Opcode, RecipeTy, RecipeTys...> {
+ static bool match(const VPRecipeBase *R) {
+ return MatchRecipeAndOpcode<Opcode, RecipeTy>::match(R) ||
+ MatchRecipeAndOpcode<Opcode, RecipeTys...>::match(R);
+ }
+};
+} // namespace detail
+
+template <typename Op0_t, unsigned Opcode, typename... RecipeTys>
+struct UnaryRecipe_match {
Op0_t Op0;
- UnaryVPInstruction_match(Op0_t Op0) : Op0(Op0) {}
+ UnaryRecipe_match(Op0_t Op0) : Op0(Op0) {}
bool match(const VPValue *V) {
auto *DefR = V->getDefiningRecipe();
@@ -64,37 +133,58 @@ template <typename Op0_t, unsigned Opcode> struct UnaryVPInstruction_match {
}
bool match(const VPRecipeBase *R) {
- auto *DefR = dyn_cast<VPInstruction>(R);
- if (!DefR || DefR->getOpcode() != Opcode)
+ if (!detail::MatchRecipeAndOpcode<Opcode, RecipeTys...>::match(R))
return false;
- assert(DefR->getNumOperands() == 1 &&
+ assert(R->getNumOperands() == 1 &&
"recipe with matched opcode does not have 1 operands");
- return Op0.match(DefR->getOperand(0));
+ return Op0.match(R->getOperand(0));
}
};
-template <typename Op0_t, typename Op1_t, unsigned Opcode>
-struct BinaryVPInstruction_match {
+template <typename Op0_t, unsigned Opcode>
+using UnaryVPInstruction_match =
+ UnaryRecipe_match<Op0_t, Opcode, VPInstruction>;
+
+template <typename Op0_t, unsigned Opcode>
+using AllUnaryRecipe_match =
+ UnaryRecipe_match<Op0_t, Opcode, VPWidenRecipe, VPReplicateRecipe,
+ VPWidenCastRecipe, VPInstruction>;
+
+template <typename Op0_t, typename Op1_t, unsigned Opcode,
+ typename... RecipeTys>
+struct BinaryRecipe_match {
Op0_t Op0;
Op1_t Op1;
- BinaryVPInstruction_match(Op0_t Op0, Op1_t Op1) : Op0(Op0), Op1(Op1) {}
+ BinaryRecipe_match(Op0_t Op0, Op1_t Op1) : Op0(Op0), Op1(Op1) {}
bool match(const VPValue *V) {
auto *DefR = V->getDefiningRecipe();
return DefR && match(DefR);
}
+ bool match(const VPSingleDefRecipe *R) {
+ return match(static_cast<const VPRecipeBase *>(R));
+ }
+
bool match(const VPRecipeBase *R) {
- auto *DefR = dyn_cast<VPInstruction>(R);
- if (!DefR || DefR->getOpcode() != Opcode)
+ if (!detail::MatchRecipeAndOpcode<Opcode, RecipeTys...>::match(R))
return false;
- assert(DefR->getNumOperands() == 2 &&
+ assert(R->getNumOperands() == 2 &&
"recipe with matched opcode does not have 2 operands");
- return Op0.match(DefR->getOperand(0)) && Op1.match(DefR->getOperand(1));
+ return Op0.match(R->getOperand(0)) && Op1.match(R->getOperand(1));
}
};
+template <typename Op0_t, typename Op1_t, unsigned Opcode>
+using BinaryVPInstruction_match =
+ BinaryRecipe_match<Op0_t, Op1_t, Opcode, VPInstruction>;
+
+template <typename Op0_t, typename Op1_t, unsigned Opcode>
+using AllBinaryRecipe_match =
+ BinaryRecipe_match<Op0_t, Op1_t, Opcode, VPWidenRecipe, VPReplicateRecipe,
+ VPWidenCastRecipe, VPInstruction>;
+
template <unsigned Opcode, typename Op0_t>
inline UnaryVPInstruction_match<Op0_t, Opcode>
m_VPInstruction(const Op0_t &Op0) {
@@ -130,6 +220,47 @@ inline BinaryVPInstruction_match<Op0_t, Op1_t, VPInstruction::BranchOnCount>
m_BranchOnCount(const Op0_t &Op0, const Op1_t &Op1) {
return m_VPInstruction<VPInstruction::BranchOnCount>(Op0, Op1);
}
+
+template <unsigned Opcode, typename Op0_t>
+inline AllUnaryRecipe_match<Op0_t, Opcode> m_Unary(const Op0_t &Op0) {
+ return AllUnaryRecipe_match<Op0_t, Opcode>(Op0);
+}
+
+template <typename Op0_t>
+inline AllUnaryRecipe_match<Op0_t, Instruction::Trunc>
+m_Trunc(const Op0_t &Op0) {
+ return m_Unary<Instruction::Trunc, Op0_t>(Op0);
+}
+
+template <typename Op0_t>
+inline AllUnaryRecipe_match<Op0_t, Instruction::ZExt> m_ZExt(const Op0_t &Op0) {
+ return m_Unary<Instruction::ZExt, Op0_t>(Op0);
+}
+
+template <typename Op0_t>
+inline AllUnaryRecipe_match<Op0_t, Instruction::SExt> m_SExt(const Op0_t &Op0) {
+ return m_Unary<Instruction::SExt, Op0_t>(Op0);
+}
+
+template <typename Op0_t>
+inline match_combine_or<AllUnaryRecipe_match<Op0_t, Instruction::ZExt>,
+ AllUnaryRecipe_match<Op0_t, Instruction::SExt>>
+m_ZExtOrSExt(const Op0_t &Op0) {
+ return m_CombineOr(m_ZExt(Op0), m_SExt(Op0));
+}
+
+template <unsigned Opcode, typename Op0_t, typename Op1_t>
+inline AllBinaryRecipe_match<Op0_t, Op1_t, Opcode> m_Binary(const Op0_t &Op0,
+ const Op1_t &Op1) {
+ return AllBinaryRecipe_match<Op0_t, Op1_t, Opcode>(Op0, Op1);
+}
+
+template <typename Op0_t, typename Op1_t>
+inline AllBinaryRecipe_match<Op0_t, Op1_t, Instruction::Mul>
+m_Mul(const Op0_t &Op0, const Op1_t &Op1) {
+ return m_Binary<Instruction::Mul, Op0_t, Op1_t>(Op0, Op1);
+}
+
} // namespace VPlanPatternMatch
} // namespace llvm
diff --git a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
index 0fc98fb69791d4..a91ccefe4b6d7d 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
@@ -814,27 +814,6 @@ void VPlanTransforms::clearReductionWrapFlags(VPlan &Plan) {
}
}
-/// Returns true is \p V is constant one.
-static bool isConstantOne(VPValue *V) {
- if (!V->isLiveIn())
- return false;
- auto *C = dyn_cast<ConstantInt>(V->getLiveInIRValue());
- return C && C->isOne();
-}
-
-/// Returns the llvm::Instruction opcode for \p R.
-static unsigned getOpcodeForRecipe(VPRecipeBase &R) {
- if (auto *WidenR = dyn_cast<VPWidenRecipe>(&R))
- return WidenR->getUnderlyingInstr()->getOpcode();
- if (auto *WidenC = dyn_cast<VPWidenCastRecipe>(&R))
- return WidenC->getOpcode();
- if (auto *RepR = dyn_cast<VPReplicateRecipe>(&R))
- return RepR->getUnderlyingInstr()->getOpcode();
- if (auto *VPI = dyn_cast<VPInstruction>(&R))
- return VPI->getOpcode();
- return 0;
-}
-
/// Try to simplify recipe \p R.
static void simplifyRecipe(VPRecipeBase &R, VPTypeAnalysis &TypeInfo) {
// Try to remove redundant blend recipes.
@@ -848,24 +827,9 @@ static void simplifyRecipe(VPRecipeBase &R, VPTypeAnalysis &TypeInfo) {
return;
}
- switch (getOpcodeForRecipe(R)) {
- case Instruction::Mul: {
- VPValue *A = R.getOperand(0);
- VPValue *B = R.getOperand(1);
- if (isConstantOne(A))
- return R.getVPSingleValue()->replaceAllUsesWith(B);
- if (isConstantOne(B))
- return R.getVPSingleValue()->replaceAllUsesWith(A);
- break;
- }
- case Instruction::Trunc: {
- VPRecipeBase *Ext = R.getOperand(0)->getDefiningRecipe();
- if (!Ext)
- break;
- unsigned ExtOpcode = getOpcodeForRecipe(*Ext);
- if (ExtOpcode != Instruction::ZExt && ExtOpcode != Instruction::SExt)
- break;
- VPValue *A = Ext->getOperand(0);
+ using namespace llvm::VPlanPatternMatch;
+ VPValue *A;
+ if (match(&R, m_Trunc(m_ZExtOrSExt(m_VPValue(A))))) {
VPValue *Trunc = R.getVPSingleValue();
Type *TruncTy = TypeInfo.inferScalarType(Trunc);
Type *ATy = TypeInfo.inferScalarType(A);
@@ -874,8 +838,12 @@ static void simplifyRecipe(VPRecipeBase &R, VPTypeAnalysis &TypeInfo) {
} else {
// Don't replace a scalarizing recipe with a widened cast.
if (isa<VPReplicateRecipe>(&R))
- break;
+ return;
if (ATy->getScalarSizeInBits() < TruncTy->getScalarSizeInBits()) {
+
+ unsigned ExtOpcode = match(R.getOperand(0), m_SExt(m_VPValue()))
+ ? Instruction::SExt
+ : Instruction::ZExt;
auto *VPC =
new VPWidenCastRecipe(Instruction::CastOps(ExtOpcode), A, TruncTy);
VPC->insertBefore(&R);
@@ -901,11 +869,11 @@ static void simplifyRecipe(VPRecipeBase &R, VPTypeAnalysis &TypeInfo) {
assert(TypeInfo.inferScalarType(VPV) == TypeInfo2.inferScalarType(VPV));
}
#endif
- break;
- }
- default:
- break;
}
+
+ if (match(&R, m_CombineOr(m_Mul(m_VPValue(A), m_SpecificInt(1)),
+ m_Mul(m_SpecificInt(1), m_VPValue(A)))))
+ return R.getVPSingleValue()->replaceAllUsesWith(A);
}
/// Try to simplify the recipes in \p Plan.
>From cb84f130b724f64f88f780c1731a4c6e9cba99cd Mon Sep 17 00:00:00 2001
From: Jay Foad <jay.foad at amd.com>
Date: Mon, 18 Mar 2024 14:29:53 +0000
Subject: [PATCH 12/40] [AMDGPU] Remove unneeded addr mode predicates on FLAT
Real instructions (#85641)
These predicates should be copied from the corresponding Pseudo
instruction. Previously that did not work because of a problem with
setting the right predicates on the Pseudos, but #85442 fixed that.
---
llvm/lib/Target/AMDGPU/FLATInstructions.td | 20 +++++---------------
1 file changed, 5 insertions(+), 15 deletions(-)
diff --git a/llvm/lib/Target/AMDGPU/FLATInstructions.td b/llvm/lib/Target/AMDGPU/FLATInstructions.td
index d35efaa85512a4..3c0a97e3d050c6 100644
--- a/llvm/lib/Target/AMDGPU/FLATInstructions.td
+++ b/llvm/lib/Target/AMDGPU/FLATInstructions.td
@@ -2135,9 +2135,7 @@ multiclass FLAT_Real_SADDR_RTN_gfx10<bits<7> op> {
multiclass FLAT_Real_ST_gfx10<bits<7> op> {
def _ST_gfx10 :
- FLAT_Real_gfx10<op, !cast<FLAT_Pseudo>(NAME#"_ST")> {
- let OtherPredicates = [HasFlatScratchSTMode];
- }
+ FLAT_Real_gfx10<op, !cast<FLAT_Pseudo>(NAME#"_ST")>;
}
multiclass FLAT_Real_AllAddr_gfx10<bits<7> op> :
@@ -2372,15 +2370,11 @@ multiclass FLAT_Real_SADDR_RTN_gfx11<bits<7> op, string ps, string opName> {
}
multiclass FLAT_Real_ST_gfx11<bits<7> op, string ps, string opName> {
- def _ST_gfx11 : FLAT_Real_gfx11<op, !cast<FLAT_Pseudo>(ps#"_ST"), opName> {
- let OtherPredicates = [HasFlatScratchSTMode];
- }
+ def _ST_gfx11 : FLAT_Real_gfx11<op, !cast<FLAT_Pseudo>(ps#"_ST"), opName>;
}
multiclass FLAT_Real_SVS_gfx11<bits<7> op, string ps, string opName> {
- def _SVS_gfx11 : FLAT_Real_gfx11<op, !cast<FLAT_Pseudo>(ps#"_SVS"), opName> {
- let OtherPredicates = [HasFlatScratchSVSMode];
- }
+ def _SVS_gfx11 : FLAT_Real_gfx11<op, !cast<FLAT_Pseudo>(ps#"_SVS"), opName>;
}
multiclass FLAT_Real_AllAddr_gfx11<bits<7> op, string ps, string opName, int renamed = false> :
@@ -2582,15 +2576,11 @@ multiclass VFLAT_Real_SADDR_RTN_gfx12<bits<8> op, string ps, string opName> {
}
multiclass VFLAT_Real_ST_gfx12<bits<8> op, string ps, string opName> {
- def _ST_gfx12 : VFLAT_Real_gfx12<op, !cast<FLAT_Pseudo>(ps#"_ST"), opName> {
- let OtherPredicates = [HasFlatScratchSTMode];
- }
+ def _ST_gfx12 : VFLAT_Real_gfx12<op, !cast<FLAT_Pseudo>(ps#"_ST"), opName>;
}
multiclass VFLAT_Real_SVS_gfx12<bits<8> op, string ps, string opName> {
- def _SVS_gfx12 : VFLAT_Real_gfx12<op, !cast<FLAT_Pseudo>(ps#"_SVS"), opName> {
- let OtherPredicates = [HasFlatScratchSVSMode];
- }
+ def _SVS_gfx12 : VFLAT_Real_gfx12<op, !cast<FLAT_Pseudo>(ps#"_SVS"), opName>;
}
multiclass VFLAT_Real_Atomics_gfx12<bits<8> op, string ps = NAME, string opName = !tolower(NAME),
>From e5b20c83e5ba25e6e0650df30352ce54c2f6ea2f Mon Sep 17 00:00:00 2001
From: Qiu Chaofan <qiucofan at cn.ibm.com>
Date: Mon, 18 Mar 2024 22:31:05 +0800
Subject: [PATCH 13/40] [PowerPC] Update chain uses when emitting lxsizx
(#84892)
---
llvm/lib/Target/PowerPC/PPCISelLowering.cpp | 1 +
.../CodeGen/PowerPC/scalar-double-ldst.ll | 58 +++++++++++++++++++
.../test/CodeGen/PowerPC/scalar-float-ldst.ll | 58 +++++++++++++++++++
3 files changed, 117 insertions(+)
diff --git a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
index 28e8523436bbfe..cce0efad39c75b 100644
--- a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
+++ b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
@@ -15048,6 +15048,7 @@ SDValue PPCTargetLowering::combineFPToIntToFP(SDNode *N,
SDValue Ld = DAG.getMemIntrinsicNode(PPCISD::LXSIZX, dl,
DAG.getVTList(MVT::f64, MVT::Other),
Ops, MVT::i8, LDN->getMemOperand());
+ DAG.makeEquivalentMemoryOrdering(LDN, Ld);
// For signed conversion, we need to sign-extend the value in the VSR
if (Signed) {
diff --git a/llvm/test/CodeGen/PowerPC/scalar-double-ldst.ll b/llvm/test/CodeGen/PowerPC/scalar-double-ldst.ll
index 6f68679325c579..798637b6840f1e 100644
--- a/llvm/test/CodeGen/PowerPC/scalar-double-ldst.ll
+++ b/llvm/test/CodeGen/PowerPC/scalar-double-ldst.ll
@@ -7281,3 +7281,61 @@ entry:
store double %str, ptr inttoptr (i64 1000000000000 to ptr), align 4096
ret void
}
+
+define dso_local void @st_reversed_double_from_i8(ptr %ptr) {
+; CHECK-P10-LABEL: st_reversed_double_from_i8:
+; CHECK-P10: # %bb.0: # %entry
+; CHECK-P10-NEXT: li r4, 8
+; CHECK-P10-NEXT: lxsibzx f0, 0, r3
+; CHECK-P10-NEXT: xxspltidp vs2, -1023410176
+; CHECK-P10-NEXT: lxsibzx f1, r3, r4
+; CHECK-P10-NEXT: xscvuxddp f0, f0
+; CHECK-P10-NEXT: xscvuxddp f1, f1
+; CHECK-P10-NEXT: xsadddp f0, f0, f2
+; CHECK-P10-NEXT: xsadddp f1, f1, f2
+; CHECK-P10-NEXT: stfd f1, 0(r3)
+; CHECK-P10-NEXT: stfd f0, 8(r3)
+; CHECK-P10-NEXT: blr
+;
+; CHECK-P9-LABEL: st_reversed_double_from_i8:
+; CHECK-P9: # %bb.0: # %entry
+; CHECK-P9-NEXT: li r4, 8
+; CHECK-P9-NEXT: lxsibzx f0, 0, r3
+; CHECK-P9-NEXT: lxsibzx f1, r3, r4
+; CHECK-P9-NEXT: addis r4, r2, .LCPI300_0 at toc@ha
+; CHECK-P9-NEXT: lfs f2, .LCPI300_0 at toc@l(r4)
+; CHECK-P9-NEXT: xscvuxddp f0, f0
+; CHECK-P9-NEXT: xscvuxddp f1, f1
+; CHECK-P9-NEXT: xsadddp f0, f0, f2
+; CHECK-P9-NEXT: xsadddp f1, f1, f2
+; CHECK-P9-NEXT: stfd f0, 8(r3)
+; CHECK-P9-NEXT: stfd f1, 0(r3)
+; CHECK-P9-NEXT: blr
+;
+; CHECK-P8-LABEL: st_reversed_double_from_i8:
+; CHECK-P8: # %bb.0: # %entry
+; CHECK-P8-NEXT: lbz r4, 0(r3)
+; CHECK-P8-NEXT: lbz r5, 8(r3)
+; CHECK-P8-NEXT: mtfprwz f0, r4
+; CHECK-P8-NEXT: mtfprwz f1, r5
+; CHECK-P8-NEXT: addis r4, r2, .LCPI300_0 at toc@ha
+; CHECK-P8-NEXT: lfs f2, .LCPI300_0 at toc@l(r4)
+; CHECK-P8-NEXT: xscvuxddp f0, f0
+; CHECK-P8-NEXT: xscvuxddp f1, f1
+; CHECK-P8-NEXT: xsadddp f0, f0, f2
+; CHECK-P8-NEXT: xsadddp f1, f1, f2
+; CHECK-P8-NEXT: stfd f1, 0(r3)
+; CHECK-P8-NEXT: stfd f0, 8(r3)
+; CHECK-P8-NEXT: blr
+entry:
+ %idx = getelementptr inbounds i8, ptr %ptr, i64 8
+ %i0 = load i8, ptr %ptr, align 1
+ %i1 = load i8, ptr %idx, align 1
+ %f0 = uitofp i8 %i0 to double
+ %f1 = uitofp i8 %i1 to double
+ %a0 = fadd double %f0, -1.280000e+02
+ %a1 = fadd double %f1, -1.280000e+02
+ store double %a1, ptr %ptr, align 8
+ store double %a0, ptr %idx, align 8
+ ret void
+}
diff --git a/llvm/test/CodeGen/PowerPC/scalar-float-ldst.ll b/llvm/test/CodeGen/PowerPC/scalar-float-ldst.ll
index 824dd4c4db6cb7..f3960573421298 100644
--- a/llvm/test/CodeGen/PowerPC/scalar-float-ldst.ll
+++ b/llvm/test/CodeGen/PowerPC/scalar-float-ldst.ll
@@ -7271,3 +7271,61 @@ entry:
store double %conv, ptr inttoptr (i64 1000000000000 to ptr), align 4096
ret void
}
+
+define dso_local void @st_reversed_float_from_i8(ptr %ptr) {
+; CHECK-P10-LABEL: st_reversed_float_from_i8:
+; CHECK-P10: # %bb.0: # %entry
+; CHECK-P10-NEXT: li r4, 8
+; CHECK-P10-NEXT: lxsibzx f0, 0, r3
+; CHECK-P10-NEXT: xxspltidp vs2, -1023410176
+; CHECK-P10-NEXT: lxsibzx f1, r3, r4
+; CHECK-P10-NEXT: xscvuxdsp f0, f0
+; CHECK-P10-NEXT: xscvuxdsp f1, f1
+; CHECK-P10-NEXT: xsaddsp f0, f0, f2
+; CHECK-P10-NEXT: xsaddsp f1, f1, f2
+; CHECK-P10-NEXT: stfs f0, 8(r3)
+; CHECK-P10-NEXT: stfs f1, 0(r3)
+; CHECK-P10-NEXT: blr
+;
+; CHECK-P9-LABEL: st_reversed_float_from_i8:
+; CHECK-P9: # %bb.0: # %entry
+; CHECK-P9-NEXT: li r4, 8
+; CHECK-P9-NEXT: lxsibzx f0, 0, r3
+; CHECK-P9-NEXT: lxsibzx f1, r3, r4
+; CHECK-P9-NEXT: addis r4, r2, .LCPI300_0 at toc@ha
+; CHECK-P9-NEXT: lfs f2, .LCPI300_0 at toc@l(r4)
+; CHECK-P9-NEXT: xscvuxdsp f0, f0
+; CHECK-P9-NEXT: xscvuxdsp f1, f1
+; CHECK-P9-NEXT: xsaddsp f0, f0, f2
+; CHECK-P9-NEXT: xsaddsp f1, f1, f2
+; CHECK-P9-NEXT: stfs f0, 8(r3)
+; CHECK-P9-NEXT: stfs f1, 0(r3)
+; CHECK-P9-NEXT: blr
+;
+; CHECK-P8-LABEL: st_reversed_float_from_i8:
+; CHECK-P8: # %bb.0: # %entry
+; CHECK-P8-NEXT: lbz r4, 0(r3)
+; CHECK-P8-NEXT: lbz r5, 8(r3)
+; CHECK-P8-NEXT: mtfprwz f0, r4
+; CHECK-P8-NEXT: mtfprwz f1, r5
+; CHECK-P8-NEXT: addis r4, r2, .LCPI300_0 at toc@ha
+; CHECK-P8-NEXT: lfs f2, .LCPI300_0 at toc@l(r4)
+; CHECK-P8-NEXT: xscvuxdsp f0, f0
+; CHECK-P8-NEXT: xscvuxdsp f1, f1
+; CHECK-P8-NEXT: xsaddsp f0, f0, f2
+; CHECK-P8-NEXT: xsaddsp f1, f1, f2
+; CHECK-P8-NEXT: stfs f1, 0(r3)
+; CHECK-P8-NEXT: stfs f0, 8(r3)
+; CHECK-P8-NEXT: blr
+entry:
+ %idx = getelementptr inbounds i8, ptr %ptr, i64 8
+ %i0 = load i8, ptr %ptr, align 1
+ %i1 = load i8, ptr %idx, align 1
+ %f0 = uitofp i8 %i0 to float
+ %f1 = uitofp i8 %i1 to float
+ %a0 = fadd float %f0, -1.280000e+02
+ %a1 = fadd float %f1, -1.280000e+02
+ store float %a1, ptr %ptr, align 8
+ store float %a0, ptr %idx, align 8
+ ret void
+}
>From 9cea288bf789c74146cb211a2b5a84895e6866ac Mon Sep 17 00:00:00 2001
From: Tom Stellard <tstellar at redhat.com>
Date: Mon, 18 Mar 2024 07:32:10 -0700
Subject: [PATCH 14/40] [release] Fix version extraction in export.sh (#85328)
The LLVM_VERSION_* variables were moved to a new file in
81e20472a0c5a4a8edc5ec38dc345d580681af81.
---
llvm/utils/release/export.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/utils/release/export.sh b/llvm/utils/release/export.sh
index 9fd906c49ea6e5..66bef82586a348 100755
--- a/llvm/utils/release/export.sh
+++ b/llvm/utils/release/export.sh
@@ -84,7 +84,7 @@ export_sources() {
# Determine the release by fetching the version from LLVM's CMakeLists.txt
# in the specified git ref.
if [ -n "$snapshot" ]; then
- release=$(git -C $llvm_src_dir show $snapshot:llvm/CMakeLists.txt | grep -ioP 'set\(\s*LLVM_VERSION_(MAJOR|MINOR|PATCH)\s\K[0-9]+' | paste -sd '.')
+ release=$(git -C $llvm_src_dir show $snapshot:cmake/Modules/LLVMVersion.cmake | grep -ioP 'set\(\s*LLVM_VERSION_(MAJOR|MINOR|PATCH)\s\K[0-9]+' | paste -sd '.')
fi
tag="llvmorg-$release"
>From e2e3624fae669f85de1445bf7037ff29feb30905 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Mon, 18 Mar 2024 15:34:31 +0100
Subject: [PATCH 15/40] [clang][test] Try to fix constexpr-void-cast test
The test currenlty fails:
https://lab.llvm.org/buildbot/#/builders/139/builds/61628
because it emits a C11 warning when compiling as C. Try to fix that
be defining the C standard to use.
---
clang/test/Sema/constexpr-void-cast.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/test/Sema/constexpr-void-cast.c b/clang/test/Sema/constexpr-void-cast.c
index c5caa3b9e58feb..91e4027f67fe38 100644
--- a/clang/test/Sema/constexpr-void-cast.c
+++ b/clang/test/Sema/constexpr-void-cast.c
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -x c -fsyntax-only %s -verify=c
-// RUN: %clang_cc1 -x c -fsyntax-only %s -pedantic -verify=c-pedantic
+// RUN: %clang_cc1 -x c -fsyntax-only %s -verify=c -std=c11
+// RUN: %clang_cc1 -x c -fsyntax-only %s -pedantic -verify=c-pedantic -std=c11
//
// RUN: %clang_cc1 -x c++ -fsyntax-only %s -verify=cxx
// RUN: %clang_cc1 -x c++ -fsyntax-only %s -pedantic -verify=cxx-pedantic
>From 9253950ec1690e786ba1cdaaf3234fb30b633eab Mon Sep 17 00:00:00 2001
From: Fraser Cormack <fraser at codeplay.com>
Date: Mon, 18 Mar 2024 14:37:04 +0000
Subject: [PATCH 16/40] [libclc] Convert tabs to spaces in CMake (#85634)
Having a mix of tabs and spaces makes the diff of any changes to the
build system noisier than necessary. This commit unifies them to two
spaces.
This includes some minor cosmetic changes such as with joining things on
one line where appropriate.
There are other files in libclc which have tabs but those haven't been
touched at this time. Those could come at another time if desired,
though they might be more contentious as the project isn't
clang-formatted at all and so that might invite larger discussions
around formatting.
---
libclc/CMakeLists.txt | 408 +++++++++++------------
libclc/check_external_calls.sh | 16 +-
libclc/cmake/CMakeLLAsmInformation.cmake | 4 +-
3 files changed, 206 insertions(+), 222 deletions(-)
diff --git a/libclc/CMakeLists.txt b/libclc/CMakeLists.txt
index 18f77940e76669..745b848fba4901 100644
--- a/libclc/CMakeLists.txt
+++ b/libclc/CMakeLists.txt
@@ -40,8 +40,7 @@ set( LIBCLC_MIN_LLVM "3.9.0" )
set( LIBCLC_TARGETS_TO_BUILD "all"
CACHE STRING "Semicolon-separated list of targets to build, or 'all'." )
-option( ENABLE_RUNTIME_SUBNORMAL "Enable runtime linking of subnormal support."
-OFF )
+option( ENABLE_RUNTIME_SUBNORMAL "Enable runtime linking of subnormal support." OFF )
find_package(LLVM REQUIRED HINTS "${LLVM_CMAKE_DIR}")
include(AddLLVM)
@@ -49,16 +48,16 @@ include(AddLLVM)
message( "LLVM version: ${LLVM_PACKAGE_VERSION}" )
if( ${LLVM_PACKAGE_VERSION} VERSION_LESS ${LIBCLC_MIN_LLVM} )
- message( FATAL_ERROR "libclc needs at least LLVM ${LIBCLC_MIN_LLVM}" )
+ message( FATAL_ERROR "libclc needs at least LLVM ${LIBCLC_MIN_LLVM}" )
endif()
# mesa3d environment is only available since LLVM 4.0
if( ${LLVM_PACKAGE_VERSION} VERSION_GREATER "3.9.0" )
- set( LIBCLC_TARGETS_ALL ${LIBCLC_TARGETS_ALL} amdgcn-mesa-mesa3d )
+ set( LIBCLC_TARGETS_ALL ${LIBCLC_TARGETS_ALL} amdgcn-mesa-mesa3d )
endif()
if( LIBCLC_TARGETS_TO_BUILD STREQUAL "all" )
- set( LIBCLC_TARGETS_TO_BUILD ${LIBCLC_TARGETS_ALL} )
+ set( LIBCLC_TARGETS_TO_BUILD ${LIBCLC_TARGETS_ALL} )
endif()
find_program( LLVM_CLANG clang PATHS ${LLVM_TOOLS_BINARY_DIR} NO_DEFAULT_PATH )
@@ -75,15 +74,15 @@ message( "opt: ${LLVM_OPT}" )
message( "llvm-spirv: ${LLVM_SPIRV}" )
message( "" )
if( NOT LLVM_CLANG OR NOT LLVM_OPT OR NOT LLVM_AS OR NOT LLVM_LINK )
- message( FATAL_ERROR "toolchain incomplete!" )
+ message( FATAL_ERROR "toolchain incomplete!" )
endif()
list( SORT LIBCLC_TARGETS_TO_BUILD )
if( "spirv-mesa3d-" IN_LIST LIBCLC_TARGETS_TO_BUILD OR "spirv64-mesa3d-" IN_LIST LIBCLC_TARGETS_TO_BUILD )
- if( NOT LLVM_SPIRV )
- message( FATAL_ERROR "SPIR-V targets requested, but spirv-tools is not installed" )
- endif()
+ if( NOT LLVM_SPIRV )
+ message( FATAL_ERROR "SPIR-V targets requested, but spirv-tools is not installed" )
+ endif()
endif()
set( CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake )
@@ -99,8 +98,8 @@ set( LLVM_VERSION_DEFINE "-DHAVE_LLVM=0x${LLVM_VERSION_MAJOR}0${LLVM_VERSION_MIN
# LLVM 13 enables standard includes by default
if( ${LLVM_PACKAGE_VERSION} VERSION_GREATER "12.99.99" )
- set( CMAKE_LLAsm_FLAGS "${CMAKE_LLAsm_FLAGS} -cl-no-stdinc")
- set( CMAKE_CLC_FLAGS "${CMAKE_CLC_FLAGS} -cl-no-stdinc")
+ set( CMAKE_LLAsm_FLAGS "${CMAKE_LLAsm_FLAGS} -cl-no-stdinc" )
+ set( CMAKE_CLC_FLAGS "${CMAKE_CLC_FLAGS} -cl-no-stdinc" )
endif()
enable_language( CLC LLAsm )
@@ -142,14 +141,14 @@ set( cypress_aliases hemlock )
set( barts_aliases turks caicos )
set( cayman_aliases aruba )
set( tahiti_aliases pitcairn verde oland hainan bonaire kabini kaveri hawaii
- mullins tonga tongapro iceland carrizo fiji stoney polaris10 polaris11
- gfx602 gfx705 gfx805
- gfx900 gfx902 gfx904 gfx906 gfx908 gfx909 gfx90a gfx90c gfx940 gfx941 gfx942
- gfx1010 gfx1011 gfx1012 gfx1013
- gfx1030 gfx1031 gfx1032 gfx1033 gfx1034 gfx1035 gfx1036
- gfx1100 gfx1101 gfx1102 gfx1103
- gfx1150 gfx1151
- gfx1200 gfx1201
+ mullins tonga tongapro iceland carrizo fiji stoney polaris10 polaris11
+ gfx602 gfx705 gfx805
+ gfx900 gfx902 gfx904 gfx906 gfx908 gfx909 gfx90a gfx90c gfx940 gfx941 gfx942
+ gfx1010 gfx1011 gfx1012 gfx1013
+ gfx1030 gfx1031 gfx1032 gfx1033 gfx1034 gfx1035 gfx1036
+ gfx1100 gfx1101 gfx1102 gfx1103
+ gfx1150 gfx1151
+ gfx1200 gfx1201
)
# pkg-config file
@@ -158,210 +157,195 @@ install( FILES ${CMAKE_CURRENT_BINARY_DIR}/libclc.pc DESTINATION "${CMAKE_INSTAL
install( DIRECTORY generic/include/clc DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" )
if( ENABLE_RUNTIME_SUBNORMAL )
- add_library( subnormal_use_default STATIC
- generic/lib/subnormal_use_default.ll )
- add_library( subnormal_disable STATIC
- generic/lib/subnormal_disable.ll )
- install( TARGETS subnormal_use_default subnormal_disable ARCHIVE
- DESTINATION "${CMAKE_INSTALL_DATADIR}/clc" )
+ add_library( subnormal_use_default STATIC
+ generic/lib/subnormal_use_default.ll )
+ add_library( subnormal_disable STATIC
+ generic/lib/subnormal_disable.ll )
+ install( TARGETS subnormal_use_default subnormal_disable ARCHIVE
+ DESTINATION "${CMAKE_INSTALL_DATADIR}/clc" )
endif()
find_package( Python3 REQUIRED COMPONENTS Interpreter )
file( TO_CMAKE_PATH ${CMAKE_SOURCE_DIR}/generic/lib/gen_convert.py script_loc )
add_custom_command(
- OUTPUT convert.cl
- COMMAND ${Python3_EXECUTABLE} ${script_loc} > convert.cl
- DEPENDS ${script_loc} )
+ OUTPUT convert.cl
+ COMMAND ${Python3_EXECUTABLE} ${script_loc} > convert.cl
+ DEPENDS ${script_loc} )
add_custom_target( "generate_convert.cl" DEPENDS convert.cl )
add_custom_command(
- OUTPUT clspv-convert.cl
- COMMAND ${Python3_EXECUTABLE} ${script_loc} --clspv > clspv-convert.cl
- DEPENDS ${script_loc} )
+ OUTPUT clspv-convert.cl
+ COMMAND ${Python3_EXECUTABLE} ${script_loc} --clspv > clspv-convert.cl
+ DEPENDS ${script_loc} )
add_custom_target( "clspv-generate_convert.cl" DEPENDS clspv-convert.cl )
enable_testing()
foreach( t ${LIBCLC_TARGETS_TO_BUILD} )
- message( "BUILDING ${t}" )
- string( REPLACE "-" ";" TRIPLE ${t} )
- list( GET TRIPLE 0 ARCH )
- list( GET TRIPLE 1 VENDOR )
- list( GET TRIPLE 2 OS )
-
- set( dirs )
-
- if ( NOT ${ARCH} STREQUAL spirv AND NOT ${ARCH} STREQUAL spirv64 AND
- NOT ${ARCH} STREQUAL clspv AND NOT ${ARCH} STREQUAL clspv64)
- LIST( APPEND dirs generic )
- endif()
-
- if( ${ARCH} STREQUAL r600 OR ${ARCH} STREQUAL amdgcn )
- list( APPEND dirs amdgpu )
- endif()
-
- #nvptx is special
- if( ${ARCH} STREQUAL nvptx OR ${ARCH} STREQUAL nvptx64 )
- set( DARCH ptx )
- else()
- set( DARCH ${ARCH} )
- endif()
-
- # Enumerate SOURCES* files
- set( source_list )
- foreach( l ${dirs} ${DARCH} ${DARCH}-${OS} ${DARCH}-${VENDOR}-${OS} )
- foreach( s "SOURCES" "SOURCES_${LLVM_MAJOR}.${LLVM_MINOR}" )
- file( TO_CMAKE_PATH ${l}/lib/${s} file_loc )
- file( TO_CMAKE_PATH ${CMAKE_SOURCE_DIR}/${file_loc} loc )
- # Prepend the location to give higher priority to
- # specialized implementation
- if( EXISTS ${loc} )
- set( source_list ${file_loc} ${source_list} )
- endif()
- endforeach()
- endforeach()
-
- # Add the generated convert.cl here to prevent adding
- # the one listed in SOURCES
- if( NOT ${ARCH} STREQUAL "spirv" AND NOT ${ARCH} STREQUAL "spirv64" )
- if( NOT ENABLE_RUNTIME_SUBNORMAL AND NOT ${ARCH} STREQUAL "clspv" AND
- NOT ${ARCH} STREQUAL "clspv64" )
- set( rel_files convert.cl )
- set( objects convert.cl )
- list( APPEND rel_files generic/lib/subnormal_use_default.ll )
- elseif(${ARCH} STREQUAL "clspv" OR ${ARCH} STREQUAL "clspv64")
- set( rel_files clspv-convert.cl )
- set( objects clspv-convert.cl )
- endif()
- else()
- set( rel_files )
- set( objects )
- endif()
-
- foreach( l ${source_list} )
- file( READ ${l} file_list )
- string( REPLACE "\n" ";" file_list ${file_list} )
- get_filename_component( dir ${l} DIRECTORY )
- foreach( f ${file_list} )
- list( FIND objects ${f} found )
- if( found EQUAL -1 )
- list( APPEND objects ${f} )
- list( APPEND rel_files ${dir}/${f} )
- # FIXME: This should really go away
- file( TO_CMAKE_PATH ${CMAKE_SOURCE_DIR}/${dir}/${f} src_loc )
- get_filename_component( fdir ${src_loc} DIRECTORY )
-
- set_source_files_properties( ${dir}/${f}
- PROPERTIES COMPILE_FLAGS "-I ${fdir}" )
- endif()
- endforeach()
- endforeach()
-
- foreach( d ${${t}_devices} )
- # Some targets don't have a specific GPU to target
- if( ${d} STREQUAL "none" OR ${ARCH} STREQUAL "spirv" OR ${ARCH} STREQUAL "spirv64" )
- set( mcpu )
- set( arch_suffix "${t}" )
- else()
- set( mcpu "-mcpu=${d}" )
- set( arch_suffix "${d}-${t}" )
- endif()
- message( " DEVICE: ${d} ( ${${d}_aliases} )" )
-
- if ( ${ARCH} STREQUAL "spirv" OR ${ARCH} STREQUAL "spirv64" )
- if( ${ARCH} STREQUAL "spirv" )
- set( t "spir--" )
- else()
- set( t "spir64--" )
- endif()
- set( build_flags -O0 -finline-hint-functions )
- set( opt_flags )
- set( spvflags --spirv-max-version=1.1 )
- elseif( ${ARCH} STREQUAL "clspv" )
- set( t "spir--" )
- set( build_flags "-Wno-unknown-assumption")
- set( opt_flags -O3 )
- elseif( ${ARCH} STREQUAL "clspv64" )
- set( t "spir64--" )
- set( build_flags "-Wno-unknown-assumption")
- set( opt_flags -O3 )
- else()
- set( build_flags )
- set( opt_flags -O3 )
- endif()
-
- add_library( builtins.link.${arch_suffix} STATIC ${rel_files} )
- # Make sure we depend on the pseudo target to prevent
- # multiple invocations
- add_dependencies( builtins.link.${arch_suffix}
- generate_convert.cl )
- add_dependencies( builtins.link.${arch_suffix}
- clspv-generate_convert.cl )
- # CMake will turn this include into absolute path
- target_include_directories( builtins.link.${arch_suffix} PRIVATE
- "generic/include" )
- target_compile_definitions( builtins.link.${arch_suffix} PRIVATE
- "__CLC_INTERNAL" )
- string( TOUPPER "-DCLC_${ARCH}" CLC_TARGET_DEFINE )
- target_compile_definitions( builtins.link.${arch_suffix} PRIVATE
- ${CLC_TARGET_DEFINE} )
- target_compile_options( builtins.link.${arch_suffix} PRIVATE -target
- ${t} ${mcpu} -fno-builtin -nostdlib ${build_flags} )
- set_target_properties( builtins.link.${arch_suffix} PROPERTIES
- LINKER_LANGUAGE CLC )
-
- set( obj_suffix ${arch_suffix}.bc )
-
- # Add opt target
- add_custom_command( OUTPUT "builtins.opt.${obj_suffix}"
- COMMAND ${LLVM_OPT} ${opt_flags} -o
- "builtins.opt.${obj_suffix}"
- "builtins.link.${obj_suffix}"
- DEPENDS "builtins.link.${arch_suffix}" )
- add_custom_target( "opt.${obj_suffix}" ALL
- DEPENDS "builtins.opt.${obj_suffix}" )
-
- if( ${ARCH} STREQUAL "spirv" OR ${ARCH} STREQUAL "spirv64" )
- set( spv_suffix ${arch_suffix}.spv )
- add_custom_command( OUTPUT "${spv_suffix}"
- COMMAND ${LLVM_SPIRV} ${spvflags}
- -o "${spv_suffix}"
- "builtins.link.${obj_suffix}"
- DEPENDS "builtins.link.${arch_suffix}" )
- add_custom_target( "prepare-${spv_suffix}" ALL
- DEPENDS "${spv_suffix}" )
- install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${spv_suffix}
- DESTINATION "${CMAKE_INSTALL_DATADIR}/clc" )
- else()
-
- # Add prepare target
- add_custom_command( OUTPUT "${obj_suffix}"
- COMMAND prepare_builtins -o
- "${obj_suffix}"
- "builtins.opt.${obj_suffix}"
- DEPENDS "opt.${obj_suffix}"
- "builtins.opt.${obj_suffix}"
- prepare_builtins )
- add_custom_target( "prepare-${obj_suffix}" ALL
- DEPENDS "${obj_suffix}" )
-
- # nvptx-- targets don't include workitem builtins
- if( NOT ${t} MATCHES ".*ptx.*--$" )
- add_test( NAME external-calls-${obj_suffix}
- COMMAND ./check_external_calls.sh ${CMAKE_CURRENT_BINARY_DIR}/${obj_suffix} ${LLVM_TOOLS_BINARY_DIR}
- WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} )
- endif()
-
- install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${obj_suffix} DESTINATION "${CMAKE_INSTALL_DATADIR}/clc" )
- foreach( a ${${d}_aliases} )
- set( alias_suffix "${a}-${t}.bc" )
- add_custom_target( ${alias_suffix} ALL
- COMMAND ${CMAKE_COMMAND} -E
- create_symlink ${obj_suffix}
- ${alias_suffix}
- DEPENDS "prepare-${obj_suffix}" )
- install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${alias_suffix} DESTINATION "${CMAKE_INSTALL_DATADIR}/clc" )
- endforeach( a )
- endif()
- endforeach( d )
+ message( "BUILDING ${t}" )
+ string( REPLACE "-" ";" TRIPLE ${t} )
+ list( GET TRIPLE 0 ARCH )
+ list( GET TRIPLE 1 VENDOR )
+ list( GET TRIPLE 2 OS )
+
+ set( dirs )
+
+ if ( NOT ${ARCH} STREQUAL spirv AND NOT ${ARCH} STREQUAL spirv64 AND
+ NOT ${ARCH} STREQUAL clspv AND NOT ${ARCH} STREQUAL clspv64)
+ LIST( APPEND dirs generic )
+ endif()
+
+ if( ${ARCH} STREQUAL r600 OR ${ARCH} STREQUAL amdgcn )
+ list( APPEND dirs amdgpu )
+ endif()
+
+ #nvptx is special
+ if( ${ARCH} STREQUAL nvptx OR ${ARCH} STREQUAL nvptx64 )
+ set( DARCH ptx )
+ else()
+ set( DARCH ${ARCH} )
+ endif()
+
+ # Enumerate SOURCES* files
+ set( source_list )
+ foreach( l ${dirs} ${DARCH} ${DARCH}-${OS} ${DARCH}-${VENDOR}-${OS} )
+ foreach( s "SOURCES" "SOURCES_${LLVM_MAJOR}.${LLVM_MINOR}" )
+ file( TO_CMAKE_PATH ${l}/lib/${s} file_loc )
+ file( TO_CMAKE_PATH ${CMAKE_SOURCE_DIR}/${file_loc} loc )
+ # Prepend the location to give higher priority to
+ # specialized implementation
+ if( EXISTS ${loc} )
+ set( source_list ${file_loc} ${source_list} )
+ endif()
+ endforeach()
+ endforeach()
+
+ # Add the generated convert.cl here to prevent adding
+ # the one listed in SOURCES
+ if( NOT ${ARCH} STREQUAL "spirv" AND NOT ${ARCH} STREQUAL "spirv64" )
+ if( NOT ENABLE_RUNTIME_SUBNORMAL AND NOT ${ARCH} STREQUAL "clspv" AND
+ NOT ${ARCH} STREQUAL "clspv64" )
+ set( rel_files convert.cl )
+ set( objects convert.cl )
+ list( APPEND rel_files generic/lib/subnormal_use_default.ll )
+ elseif(${ARCH} STREQUAL "clspv" OR ${ARCH} STREQUAL "clspv64")
+ set( rel_files clspv-convert.cl )
+ set( objects clspv-convert.cl )
+ endif()
+ else()
+ set( rel_files )
+ set( objects )
+ endif()
+
+ foreach( l ${source_list} )
+ file( READ ${l} file_list )
+ string( REPLACE "\n" ";" file_list ${file_list} )
+ get_filename_component( dir ${l} DIRECTORY )
+ foreach( f ${file_list} )
+ list( FIND objects ${f} found )
+ if( found EQUAL -1 )
+ list( APPEND objects ${f} )
+ list( APPEND rel_files ${dir}/${f} )
+ # FIXME: This should really go away
+ file( TO_CMAKE_PATH ${CMAKE_SOURCE_DIR}/${dir}/${f} src_loc )
+ get_filename_component( fdir ${src_loc} DIRECTORY )
+
+ set_source_files_properties( ${dir}/${f}
+ PROPERTIES COMPILE_FLAGS "-I ${fdir}" )
+ endif()
+ endforeach()
+ endforeach()
+
+ foreach( d ${${t}_devices} )
+ # Some targets don't have a specific GPU to target
+ if( ${d} STREQUAL "none" OR ${ARCH} STREQUAL "spirv" OR ${ARCH} STREQUAL "spirv64" )
+ set( mcpu )
+ set( arch_suffix "${t}" )
+ else()
+ set( mcpu "-mcpu=${d}" )
+ set( arch_suffix "${d}-${t}" )
+ endif()
+ message( " DEVICE: ${d} ( ${${d}_aliases} )" )
+
+ if ( ${ARCH} STREQUAL "spirv" OR ${ARCH} STREQUAL "spirv64" )
+ if( ${ARCH} STREQUAL "spirv" )
+ set( t "spir--" )
+ else()
+ set( t "spir64--" )
+ endif()
+ set( build_flags -O0 -finline-hint-functions )
+ set( opt_flags )
+ set( spvflags --spirv-max-version=1.1 )
+ elseif( ${ARCH} STREQUAL "clspv" )
+ set( t "spir--" )
+ set( build_flags "-Wno-unknown-assumption")
+ set( opt_flags -O3 )
+ elseif( ${ARCH} STREQUAL "clspv64" )
+ set( t "spir64--" )
+ set( build_flags "-Wno-unknown-assumption")
+ set( opt_flags -O3 )
+ else()
+ set( build_flags )
+ set( opt_flags -O3 )
+ endif()
+
+ add_library( builtins.link.${arch_suffix} STATIC ${rel_files} )
+ # Make sure we depend on the pseudo target to prevent
+ # multiple invocations
+ add_dependencies( builtins.link.${arch_suffix} generate_convert.cl )
+ add_dependencies( builtins.link.${arch_suffix} clspv-generate_convert.cl )
+ # CMake will turn this include into absolute path
+ target_include_directories( builtins.link.${arch_suffix} PRIVATE
+ "generic/include" )
+ target_compile_definitions( builtins.link.${arch_suffix} PRIVATE
+ "__CLC_INTERNAL" )
+ string( TOUPPER "-DCLC_${ARCH}" CLC_TARGET_DEFINE )
+ target_compile_definitions( builtins.link.${arch_suffix} PRIVATE
+ ${CLC_TARGET_DEFINE} )
+ target_compile_options( builtins.link.${arch_suffix} PRIVATE -target
+ ${t} ${mcpu} -fno-builtin -nostdlib ${build_flags} )
+ set_target_properties( builtins.link.${arch_suffix} PROPERTIES
+ LINKER_LANGUAGE CLC )
+
+ set( obj_suffix ${arch_suffix}.bc )
+
+ # Add opt target
+ add_custom_command( OUTPUT "builtins.opt.${obj_suffix}"
+ COMMAND ${LLVM_OPT} ${opt_flags} -o "builtins.opt.${obj_suffix}" "builtins.link.${obj_suffix}"
+ DEPENDS "builtins.link.${arch_suffix}" )
+ add_custom_target( "opt.${obj_suffix}" ALL
+ DEPENDS "builtins.opt.${obj_suffix}" )
+
+ if( ${ARCH} STREQUAL "spirv" OR ${ARCH} STREQUAL "spirv64" )
+ set( spv_suffix ${arch_suffix}.spv )
+ add_custom_command( OUTPUT "${spv_suffix}"
+ COMMAND ${LLVM_SPIRV} ${spvflags} -o "${spv_suffix}" "builtins.link.${obj_suffix}"
+ DEPENDS "builtins.link.${arch_suffix}" )
+ add_custom_target( "prepare-${spv_suffix}" ALL DEPENDS "${spv_suffix}" )
+ install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${spv_suffix}
+ DESTINATION "${CMAKE_INSTALL_DATADIR}/clc" )
+ else()
+ # Add prepare target
+ add_custom_command( OUTPUT "${obj_suffix}"
+ COMMAND prepare_builtins -o "${obj_suffix}" "builtins.opt.${obj_suffix}"
+ DEPENDS "opt.${obj_suffix}" "builtins.opt.${obj_suffix}" prepare_builtins )
+ add_custom_target( "prepare-${obj_suffix}" ALL DEPENDS "${obj_suffix}" )
+
+ # nvptx-- targets don't include workitem builtins
+ if( NOT ${t} MATCHES ".*ptx.*--$" )
+ add_test( NAME external-calls-${obj_suffix}
+ COMMAND ./check_external_calls.sh ${CMAKE_CURRENT_BINARY_DIR}/${obj_suffix} ${LLVM_TOOLS_BINARY_DIR}
+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} )
+ endif()
+
+ install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${obj_suffix} DESTINATION "${CMAKE_INSTALL_DATADIR}/clc" )
+ foreach( a ${${d}_aliases} )
+ set( alias_suffix "${a}-${t}.bc" )
+ add_custom_target( ${alias_suffix} ALL
+ COMMAND ${CMAKE_COMMAND} -E create_symlink ${obj_suffix} ${alias_suffix}
+ DEPENDS "prepare-${obj_suffix}" )
+ install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${alias_suffix} DESTINATION "${CMAKE_INSTALL_DATADIR}/clc" )
+ endforeach( a )
+ endif()
+ endforeach( d )
endforeach( t )
diff --git a/libclc/check_external_calls.sh b/libclc/check_external_calls.sh
index 4de31a220d070f..25792e249d6b66 100755
--- a/libclc/check_external_calls.sh
+++ b/libclc/check_external_calls.sh
@@ -3,15 +3,15 @@
FILE=$1
BIN_DIR=$2
if [ ! -f $FILE ]; then
- echo "ERROR: Not a file: $FILE"
- exit 3
+ echo "ERROR: Not a file: $FILE"
+ exit 3
fi
ret=0
DIS="$BIN_DIR/llvm-dis"
if [ ! -x $DIS ]; then
- echo "ERROR: Disassembler '$DIS' is not executable"
- exit 3
+ echo "ERROR: Disassembler '$DIS' is not executable"
+ exit 3
fi
TMP_FILE=$(mktemp)
@@ -21,10 +21,10 @@ $DIS < $FILE | grep ' call ' | grep -v '@llvm' > "$TMP_FILE"
COUNT=$(wc -l < "$TMP_FILE")
if [ "$COUNT" -ne "0" ]; then
- echo "ERROR: $COUNT unresolved calls detected in $FILE"
- cat $TMP_FILE
- ret=1
+ echo "ERROR: $COUNT unresolved calls detected in $FILE"
+ cat $TMP_FILE
+ ret=1
else
- echo "File $FILE is OK"
+ echo "File $FILE is OK"
fi
exit $ret
diff --git a/libclc/cmake/CMakeLLAsmInformation.cmake b/libclc/cmake/CMakeLLAsmInformation.cmake
index 218e20a52fe823..35ec3081da0f7d 100644
--- a/libclc/cmake/CMakeLLAsmInformation.cmake
+++ b/libclc/cmake/CMakeLLAsmInformation.cmake
@@ -1,7 +1,7 @@
if(NOT CMAKE_LLAsm_COMPILE_OBJECT)
set(CMAKE_LLAsm_COMPILE_OBJECT
- "${CMAKE_LLAsm_PREPROCESSOR} -E -P <DEFINES> <INCLUDES> <FLAGS> -x cl <SOURCE> -o <OBJECT>.temp"
- "<CMAKE_LLAsm_COMPILER> -o <OBJECT> <OBJECT>.temp")
+ "${CMAKE_LLAsm_PREPROCESSOR} -E -P <DEFINES> <INCLUDES> <FLAGS> -x cl <SOURCE> -o <OBJECT>.temp"
+ "<CMAKE_LLAsm_COMPILER> -o <OBJECT> <OBJECT>.temp")
endif()
if(NOT CMAKE_LLAsm_CREATE_STATIC_LIBRARY)
>From 09bc6abba6e226ad5e9d18d4365690d6f04de21a Mon Sep 17 00:00:00 2001
From: Jonas Paulsson <paulson1 at linux.ibm.com>
Date: Mon, 18 Mar 2024 10:37:59 -0400
Subject: [PATCH 17/40] [MachineFrameInfo] Refactoring around
computeMaxcallFrameSize() (NFC) (#78001)
- Use computeMaxCallFrameSize() in PEI::calculateCallFrameInfo() instead of duplicating the code.
- Set AdjustsStack in FinalizeISel instead of in computeMaxCallFrameSize().
---
llvm/include/llvm/CodeGen/MachineFrameInfo.h | 8 +++-
llvm/include/llvm/CodeGen/TargetInstrInfo.h | 1 +
llvm/lib/CodeGen/FinalizeISel.cpp | 9 +++++
llvm/lib/CodeGen/MachineFrameInfo.cpp | 15 +++----
llvm/lib/CodeGen/PrologEpilogInserter.cpp | 40 ++++++-------------
llvm/lib/Target/PowerPC/PPCTLSDynamicCall.cpp | 5 ++-
llvm/lib/Target/X86/X86ISelLowering.cpp | 1 +
llvm/test/CodeGen/AArch64/avoid-zero-copy.mir | 2 +
.../AArch64/stack-probing-no-scratch-reg.mir | 1 +
.../AArch64/stack-probing-shrink-wrap.mir | 1 +
...lee-save-size-after-livedebugvariables.mir | 1 +
.../AMDGPU/av_spill_cross_bb_usage.mir | 2 +
.../Hexagon/livephysregs-regmask-clobber.mir | 2 +
.../CodeGen/MIR/AMDGPU/stack-id-assert.mir | 2 +
llvm/test/CodeGen/Mips/avoid-zero-copy.mir | 2 +
.../test/CodeGen/Mips/msa/emergency-spill.mir | 2 +-
llvm/test/CodeGen/RISCV/live-sp.mir | 2 +-
.../RISCV/rvv/addi-rvv-stack-object.mir | 2 +-
.../CodeGen/RISCV/rvv/rvv-stack-align.mir | 6 +--
.../rvv/wrong-stack-offset-for-rvv-object.mir | 2 +-
.../CodeGen/RISCV/stack-inst-compress.mir | 3 ++
llvm/test/CodeGen/SystemZ/cond-move-04.mir | 1 +
llvm/test/CodeGen/SystemZ/cond-move-08.mir | 1 +
.../SystemZ/cond-move-regalloc-hints-02.mir | 1 +
.../SystemZ/cond-move-regalloc-hints.mir | 1 +
llvm/test/CodeGen/SystemZ/frame-28.mir | 2 +
.../fast-regalloc-live-out-debug-values.mir | 1 +
llvm/test/CodeGen/X86/heap-alloc-markers.mir | 1 +
llvm/test/CodeGen/X86/instr-symbols.mir | 1 +
.../CodeGen/X86/statepoint-fixup-undef.mir | 2 +-
llvm/test/CodeGen/X86/statepoint-vreg.mir | 2 +
llvm/test/DebugInfo/MIR/X86/debug-loc-0.mir | 2 +-
.../MIR/X86/prolog-epilog-indirection.mir | 1 +
.../DebugInfo/X86/live-debug-vars-dse.mir | 2 +-
llvm/test/DebugInfo/X86/prolog-params.mir | 2 +
35 files changed, 79 insertions(+), 50 deletions(-)
diff --git a/llvm/include/llvm/CodeGen/MachineFrameInfo.h b/llvm/include/llvm/CodeGen/MachineFrameInfo.h
index 7d11d63d4066f4..0fe73fec7ee67f 100644
--- a/llvm/include/llvm/CodeGen/MachineFrameInfo.h
+++ b/llvm/include/llvm/CodeGen/MachineFrameInfo.h
@@ -638,13 +638,17 @@ class MachineFrameInfo {
bool hasTailCall() const { return HasTailCall; }
void setHasTailCall(bool V = true) { HasTailCall = V; }
- /// Computes the maximum size of a callframe and the AdjustsStack property.
+ /// Computes the maximum size of a callframe.
/// This only works for targets defining
/// TargetInstrInfo::getCallFrameSetupOpcode(), getCallFrameDestroyOpcode(),
/// and getFrameSize().
/// This is usually computed by the prologue epilogue inserter but some
/// targets may call this to compute it earlier.
- void computeMaxCallFrameSize(const MachineFunction &MF);
+ /// If FrameSDOps is passed, the frame instructions in the MF will be
+ /// inserted into it.
+ void computeMaxCallFrameSize(
+ MachineFunction &MF,
+ std::vector<MachineBasicBlock::iterator> *FrameSDOps = nullptr);
/// Return the maximum size of a call frame that must be
/// allocated for an outgoing function call. This is only available if
diff --git a/llvm/include/llvm/CodeGen/TargetInstrInfo.h b/llvm/include/llvm/CodeGen/TargetInstrInfo.h
index be4ee5b6f9e29a..9fd0ebe6956fbe 100644
--- a/llvm/include/llvm/CodeGen/TargetInstrInfo.h
+++ b/llvm/include/llvm/CodeGen/TargetInstrInfo.h
@@ -204,6 +204,7 @@ class TargetInstrInfo : public MCInstrInfo {
/// if they exist (-1 otherwise). Some targets use pseudo instructions in
/// order to abstract away the difference between operating with a frame
/// pointer and operating without, through the use of these two instructions.
+ /// A FrameSetup MI in MF implies MFI::AdjustsStack.
///
unsigned getCallFrameSetupOpcode() const { return CallFrameSetupOpcode; }
unsigned getCallFrameDestroyOpcode() const { return CallFrameDestroyOpcode; }
diff --git a/llvm/lib/CodeGen/FinalizeISel.cpp b/llvm/lib/CodeGen/FinalizeISel.cpp
index 329c9587e32122..978355f8eb1bbf 100644
--- a/llvm/lib/CodeGen/FinalizeISel.cpp
+++ b/llvm/lib/CodeGen/FinalizeISel.cpp
@@ -14,8 +14,10 @@
//
//===----------------------------------------------------------------------===//
+#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
+#include "llvm/CodeGen/TargetInstrInfo.h"
#include "llvm/CodeGen/TargetLowering.h"
#include "llvm/CodeGen/TargetSubtargetInfo.h"
#include "llvm/InitializePasses.h"
@@ -45,6 +47,7 @@ INITIALIZE_PASS(FinalizeISel, DEBUG_TYPE,
bool FinalizeISel::runOnMachineFunction(MachineFunction &MF) {
bool Changed = false;
+ const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo();
const TargetLowering *TLI = MF.getSubtarget().getTargetLowering();
// Iterate through each instruction in the function, looking for pseudos.
@@ -54,6 +57,12 @@ bool FinalizeISel::runOnMachineFunction(MachineFunction &MF) {
MBBI != MBBE; ) {
MachineInstr &MI = *MBBI++;
+ // Set AdjustsStack to true if the instruction selector emits a stack
+ // frame setup instruction or a stack aligning inlineasm.
+ if (MI.getOpcode() == TII->getCallFrameSetupOpcode() ||
+ MI.isStackAligningInlineAsm())
+ MF.getFrameInfo().setAdjustsStack(true);
+
// If MI is a pseudo, expand it.
if (MI.usesCustomInsertionHook()) {
Changed = true;
diff --git a/llvm/lib/CodeGen/MachineFrameInfo.cpp b/llvm/lib/CodeGen/MachineFrameInfo.cpp
index 280d3a6a41edc9..853de4c88caeb7 100644
--- a/llvm/lib/CodeGen/MachineFrameInfo.cpp
+++ b/llvm/lib/CodeGen/MachineFrameInfo.cpp
@@ -184,7 +184,8 @@ uint64_t MachineFrameInfo::estimateStackSize(const MachineFunction &MF) const {
return alignTo(Offset, StackAlign);
}
-void MachineFrameInfo::computeMaxCallFrameSize(const MachineFunction &MF) {
+void MachineFrameInfo::computeMaxCallFrameSize(
+ MachineFunction &MF, std::vector<MachineBasicBlock::iterator> *FrameSDOps) {
const TargetInstrInfo &TII = *MF.getSubtarget().getInstrInfo();
unsigned FrameSetupOpcode = TII.getCallFrameSetupOpcode();
unsigned FrameDestroyOpcode = TII.getCallFrameDestroyOpcode();
@@ -192,18 +193,14 @@ void MachineFrameInfo::computeMaxCallFrameSize(const MachineFunction &MF) {
"Can only compute MaxCallFrameSize if Setup/Destroy opcode are known");
MaxCallFrameSize = 0;
- for (const MachineBasicBlock &MBB : MF) {
- for (const MachineInstr &MI : MBB) {
+ for (MachineBasicBlock &MBB : MF) {
+ for (MachineInstr &MI : MBB) {
unsigned Opcode = MI.getOpcode();
if (Opcode == FrameSetupOpcode || Opcode == FrameDestroyOpcode) {
unsigned Size = TII.getFrameSize(MI);
MaxCallFrameSize = std::max(MaxCallFrameSize, Size);
- AdjustsStack = true;
- } else if (MI.isInlineAsm()) {
- // Some inline asm's need a stack frame, as indicated by operand 1.
- unsigned ExtraInfo = MI.getOperand(InlineAsm::MIOp_ExtraInfo).getImm();
- if (ExtraInfo & InlineAsm::Extra_IsAlignStack)
- AdjustsStack = true;
+ if (FrameSDOps != nullptr)
+ FrameSDOps->push_back(&MI);
}
}
}
diff --git a/llvm/lib/CodeGen/PrologEpilogInserter.cpp b/llvm/lib/CodeGen/PrologEpilogInserter.cpp
index 8af17e63e25c75..e77d5e658962b1 100644
--- a/llvm/lib/CodeGen/PrologEpilogInserter.cpp
+++ b/llvm/lib/CodeGen/PrologEpilogInserter.cpp
@@ -228,9 +228,8 @@ bool PEI::runOnMachineFunction(MachineFunction &MF) {
FrameIndexVirtualScavenging = TRI->requiresFrameIndexScavenging(MF);
ORE = &getAnalysis<MachineOptimizationRemarkEmitterPass>().getORE();
- // Calculate the MaxCallFrameSize and AdjustsStack variables for the
- // function's frame information. Also eliminates call frame pseudo
- // instructions.
+ // Calculate the MaxCallFrameSize value for the function's frame
+ // information. Also eliminates call frame pseudo instructions.
calculateCallFrameInfo(MF);
// Determine placement of CSR spill/restore code and prolog/epilog code:
@@ -350,17 +349,13 @@ bool PEI::runOnMachineFunction(MachineFunction &MF) {
return true;
}
-/// Calculate the MaxCallFrameSize and AdjustsStack
-/// variables for the function's frame information and eliminate call frame
-/// pseudo instructions.
+/// Calculate the MaxCallFrameSize variable for the function's frame
+/// information and eliminate call frame pseudo instructions.
void PEI::calculateCallFrameInfo(MachineFunction &MF) {
const TargetInstrInfo &TII = *MF.getSubtarget().getInstrInfo();
const TargetFrameLowering *TFI = MF.getSubtarget().getFrameLowering();
MachineFrameInfo &MFI = MF.getFrameInfo();
- unsigned MaxCallFrameSize = 0;
- bool AdjustsStack = MFI.adjustsStack();
-
// Get the function call frame set-up and tear-down instruction opcode
unsigned FrameSetupOpcode = TII.getCallFrameSetupOpcode();
unsigned FrameDestroyOpcode = TII.getCallFrameDestroyOpcode();
@@ -370,26 +365,15 @@ void PEI::calculateCallFrameInfo(MachineFunction &MF) {
if (FrameSetupOpcode == ~0u && FrameDestroyOpcode == ~0u)
return;
+ // (Re-)Compute the MaxCallFrameSize.
+ uint32_t MaxCFSIn =
+ MFI.isMaxCallFrameSizeComputed() ? MFI.getMaxCallFrameSize() : UINT32_MAX;
std::vector<MachineBasicBlock::iterator> FrameSDOps;
- for (MachineBasicBlock &BB : MF)
- for (MachineBasicBlock::iterator I = BB.begin(); I != BB.end(); ++I)
- if (TII.isFrameInstr(*I)) {
- unsigned Size = TII.getFrameSize(*I);
- if (Size > MaxCallFrameSize) MaxCallFrameSize = Size;
- AdjustsStack = true;
- FrameSDOps.push_back(I);
- } else if (I->isInlineAsm()) {
- // Some inline asm's need a stack frame, as indicated by operand 1.
- unsigned ExtraInfo = I->getOperand(InlineAsm::MIOp_ExtraInfo).getImm();
- if (ExtraInfo & InlineAsm::Extra_IsAlignStack)
- AdjustsStack = true;
- }
-
- assert(!MFI.isMaxCallFrameSizeComputed() ||
- (MFI.getMaxCallFrameSize() >= MaxCallFrameSize &&
- !(AdjustsStack && !MFI.adjustsStack())));
- MFI.setAdjustsStack(AdjustsStack);
- MFI.setMaxCallFrameSize(MaxCallFrameSize);
+ MFI.computeMaxCallFrameSize(MF, &FrameSDOps);
+ assert(MFI.getMaxCallFrameSize() <= MaxCFSIn &&
+ "Recomputing MaxCFS gave a larger value.");
+ assert((FrameSDOps.empty() || MF.getFrameInfo().adjustsStack()) &&
+ "AdjustsStack not set in presence of a frame pseudo instruction.");
if (TFI->canSimplifyCallFramePseudos(MF)) {
// If call frames are not being included as part of the stack frame, and
diff --git a/llvm/lib/Target/PowerPC/PPCTLSDynamicCall.cpp b/llvm/lib/Target/PowerPC/PPCTLSDynamicCall.cpp
index 147438dfedd87d..9f680ef5046dad 100644
--- a/llvm/lib/Target/PowerPC/PPCTLSDynamicCall.cpp
+++ b/llvm/lib/Target/PowerPC/PPCTLSDynamicCall.cpp
@@ -25,6 +25,7 @@
#include "PPCInstrInfo.h"
#include "PPCTargetMachine.h"
#include "llvm/CodeGen/LiveIntervals.h"
+#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/InitializePasses.h"
@@ -159,9 +160,11 @@ namespace {
// We don't really need to save data to the stack - the clobbered
// registers are already saved when the SDNode (e.g. PPCaddiTlsgdLAddr)
// gets translated to the pseudo instruction (e.g. ADDItlsgdLADDR).
- if (NeedFence)
+ if (NeedFence) {
+ MBB.getParent()->getFrameInfo().setAdjustsStack(true);
BuildMI(MBB, I, DL, TII->get(PPC::ADJCALLSTACKDOWN)).addImm(0)
.addImm(0);
+ }
if (IsAIX) {
if (IsTLSLDAIXMI) {
diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp
index 9b88611073f01d..8a6594230f0a2c 100644
--- a/llvm/lib/Target/X86/X86ISelLowering.cpp
+++ b/llvm/lib/Target/X86/X86ISelLowering.cpp
@@ -35228,6 +35228,7 @@ X86TargetLowering::EmitLoweredTLSAddr(MachineInstr &MI,
MachineFunction &MF = *BB->getParent();
// Emit CALLSEQ_START right before the instruction.
+ BB->getParent()->getFrameInfo().setAdjustsStack(true);
unsigned AdjStackDown = TII.getCallFrameSetupOpcode();
MachineInstrBuilder CallseqStart =
BuildMI(MF, MIMD, TII.get(AdjStackDown)).addImm(0).addImm(0).addImm(0);
diff --git a/llvm/test/CodeGen/AArch64/avoid-zero-copy.mir b/llvm/test/CodeGen/AArch64/avoid-zero-copy.mir
index 859be2d3374762..b940734c6988cb 100644
--- a/llvm/test/CodeGen/AArch64/avoid-zero-copy.mir
+++ b/llvm/test/CodeGen/AArch64/avoid-zero-copy.mir
@@ -19,6 +19,8 @@
...
---
name: foo
+frameInfo:
+ adjustsStack: true
body: |
bb.0 (%ir-block.0):
; CHECK-LABEL: name: foo
diff --git a/llvm/test/CodeGen/AArch64/stack-probing-no-scratch-reg.mir b/llvm/test/CodeGen/AArch64/stack-probing-no-scratch-reg.mir
index f2d79bd7206908..a9c9b5ff60e451 100644
--- a/llvm/test/CodeGen/AArch64/stack-probing-no-scratch-reg.mir
+++ b/llvm/test/CodeGen/AArch64/stack-probing-no-scratch-reg.mir
@@ -29,6 +29,7 @@ tracksRegLiveness: true
liveins:
- { reg: '$w0', virtual-reg: '' }
frameInfo:
+ adjustsStack: true
localFrameSize: 150000
stack:
- { id: 0, name: a, type: default, offset: 0, size: 150000, alignment: 8,
diff --git a/llvm/test/CodeGen/AArch64/stack-probing-shrink-wrap.mir b/llvm/test/CodeGen/AArch64/stack-probing-shrink-wrap.mir
index 83aa90d389a4a2..985ec352139708 100644
--- a/llvm/test/CodeGen/AArch64/stack-probing-shrink-wrap.mir
+++ b/llvm/test/CodeGen/AArch64/stack-probing-shrink-wrap.mir
@@ -31,6 +31,7 @@ tracksRegLiveness: true
liveins:
- { reg: '$w0', virtual-reg: '' }
frameInfo:
+ adjustsStack: true
localFrameSize: 150000
stack:
- { id: 0, name: a, type: default, offset: 0, size: 150000, alignment: 8,
diff --git a/llvm/test/CodeGen/AArch64/wrong-callee-save-size-after-livedebugvariables.mir b/llvm/test/CodeGen/AArch64/wrong-callee-save-size-after-livedebugvariables.mir
index 53a8612a7fae76..8e1142474447ed 100644
--- a/llvm/test/CodeGen/AArch64/wrong-callee-save-size-after-livedebugvariables.mir
+++ b/llvm/test/CodeGen/AArch64/wrong-callee-save-size-after-livedebugvariables.mir
@@ -64,6 +64,7 @@
name: foo
tracksRegLiveness: true
frameInfo:
+ adjustsStack: true
hasCalls: true
fixedStack: []
stack:
diff --git a/llvm/test/CodeGen/AMDGPU/av_spill_cross_bb_usage.mir b/llvm/test/CodeGen/AMDGPU/av_spill_cross_bb_usage.mir
index c1da29ecc2c2f5..3228962ed01f78 100644
--- a/llvm/test/CodeGen/AMDGPU/av_spill_cross_bb_usage.mir
+++ b/llvm/test/CodeGen/AMDGPU/av_spill_cross_bb_usage.mir
@@ -14,6 +14,8 @@
---
name: test_av_spill_cross_bb_usage
tracksRegLiveness: true
+frameInfo:
+ adjustsStack: true
stack:
- { id: 0, name: '', type: spill-slot, offset: 0, size: 4, alignment: 4 }
machineFunctionInfo:
diff --git a/llvm/test/CodeGen/Hexagon/livephysregs-regmask-clobber.mir b/llvm/test/CodeGen/Hexagon/livephysregs-regmask-clobber.mir
index 8f1cb42b96a6fa..52213070f53567 100644
--- a/llvm/test/CodeGen/Hexagon/livephysregs-regmask-clobber.mir
+++ b/llvm/test/CodeGen/Hexagon/livephysregs-regmask-clobber.mir
@@ -17,6 +17,8 @@
name: f0
tracksRegLiveness: true
+frameInfo:
+ adjustsStack: true
stack:
- { id: 0, offset: 0, size: 128, alignment: 128 }
- { id: 1, offset: 128, size: 128, alignment: 128 }
diff --git a/llvm/test/CodeGen/MIR/AMDGPU/stack-id-assert.mir b/llvm/test/CodeGen/MIR/AMDGPU/stack-id-assert.mir
index e40d1879399cef..9831f786b847d3 100644
--- a/llvm/test/CodeGen/MIR/AMDGPU/stack-id-assert.mir
+++ b/llvm/test/CodeGen/MIR/AMDGPU/stack-id-assert.mir
@@ -29,6 +29,8 @@ liveins:
- { reg: '$vgpr0', virtual-reg: '' }
- { reg: '$vgpr1', virtual-reg: '' }
- { reg: '$vgpr2', virtual-reg: '' }
+frameInfo:
+ adjustsStack: true
stack:
- { id: 0, name: '', type: spill-slot, offset: 0, size: 8, alignment: 4,
stack-id: sgpr-spill, callee-saved-register: '', callee-saved-restored: true,
diff --git a/llvm/test/CodeGen/Mips/avoid-zero-copy.mir b/llvm/test/CodeGen/Mips/avoid-zero-copy.mir
index 5c7cffd109ea62..e3990bdf9bc3fb 100644
--- a/llvm/test/CodeGen/Mips/avoid-zero-copy.mir
+++ b/llvm/test/CodeGen/Mips/avoid-zero-copy.mir
@@ -19,6 +19,8 @@
...
---
name: a
+frameInfo:
+ adjustsStack: true
body: |
bb.0 (%ir-block.0):
liveins: $a0_64, $t9_64, $ra_64, $fp_64, $gp_64
diff --git a/llvm/test/CodeGen/Mips/msa/emergency-spill.mir b/llvm/test/CodeGen/Mips/msa/emergency-spill.mir
index e1c7b2158d6174..20894645286618 100644
--- a/llvm/test/CodeGen/Mips/msa/emergency-spill.mir
+++ b/llvm/test/CodeGen/Mips/msa/emergency-spill.mir
@@ -90,7 +90,7 @@ frameInfo:
stackSize: 0
offsetAdjustment: 0
maxAlignment: 16
- adjustsStack: false
+ adjustsStack: true
hasCalls: true
stackProtector: ''
maxCallFrameSize: 4294967295
diff --git a/llvm/test/CodeGen/RISCV/live-sp.mir b/llvm/test/CodeGen/RISCV/live-sp.mir
index 8dd307f521f5bd..fa6297a3913a9b 100644
--- a/llvm/test/CodeGen/RISCV/live-sp.mir
+++ b/llvm/test/CodeGen/RISCV/live-sp.mir
@@ -44,7 +44,7 @@ frameInfo:
stackSize: 0
offsetAdjustment: 0
maxAlignment: 4
- adjustsStack: false
+ adjustsStack: true
hasCalls: true
stackProtector: ''
maxCallFrameSize: 4294967295
diff --git a/llvm/test/CodeGen/RISCV/rvv/addi-rvv-stack-object.mir b/llvm/test/CodeGen/RISCV/rvv/addi-rvv-stack-object.mir
index 52557288210394..080a89e41f0d54 100644
--- a/llvm/test/CodeGen/RISCV/rvv/addi-rvv-stack-object.mir
+++ b/llvm/test/CodeGen/RISCV/rvv/addi-rvv-stack-object.mir
@@ -22,7 +22,7 @@ frameInfo:
stackSize: 0
offsetAdjustment: 0
maxAlignment: 16
- adjustsStack: false
+ adjustsStack: true
hasCalls: true
stackProtector: ''
functionContext: ''
diff --git a/llvm/test/CodeGen/RISCV/rvv/rvv-stack-align.mir b/llvm/test/CodeGen/RISCV/rvv/rvv-stack-align.mir
index 6ea6fb183a7fdf..749bd4c13879b6 100644
--- a/llvm/test/CodeGen/RISCV/rvv/rvv-stack-align.mir
+++ b/llvm/test/CodeGen/RISCV/rvv/rvv-stack-align.mir
@@ -159,7 +159,7 @@ frameInfo:
stackSize: 0
offsetAdjustment: 0
maxAlignment: 8
- adjustsStack: false
+ adjustsStack: true
hasCalls: true
stackProtector: ''
maxCallFrameSize: 4294967295
@@ -204,7 +204,7 @@ frameInfo:
stackSize: 0
offsetAdjustment: 0
maxAlignment: 16
- adjustsStack: false
+ adjustsStack: true
hasCalls: true
stackProtector: ''
maxCallFrameSize: 4294967295
@@ -249,7 +249,7 @@ frameInfo:
stackSize: 0
offsetAdjustment: 0
maxAlignment: 32
- adjustsStack: false
+ adjustsStack: true
hasCalls: true
stackProtector: ''
maxCallFrameSize: 4294967295
diff --git a/llvm/test/CodeGen/RISCV/rvv/wrong-stack-offset-for-rvv-object.mir b/llvm/test/CodeGen/RISCV/rvv/wrong-stack-offset-for-rvv-object.mir
index 06ed46f291a832..8248c26636793e 100644
--- a/llvm/test/CodeGen/RISCV/rvv/wrong-stack-offset-for-rvv-object.mir
+++ b/llvm/test/CodeGen/RISCV/rvv/wrong-stack-offset-for-rvv-object.mir
@@ -83,7 +83,7 @@ frameInfo:
stackSize: 0
offsetAdjustment: 0
maxAlignment: 8
- adjustsStack: false
+ adjustsStack: true
hasCalls: true
stackProtector: ''
maxCallFrameSize: 4294967295
diff --git a/llvm/test/CodeGen/RISCV/stack-inst-compress.mir b/llvm/test/CodeGen/RISCV/stack-inst-compress.mir
index 6721ff11d99b7b..5cc4615bb64a13 100644
--- a/llvm/test/CodeGen/RISCV/stack-inst-compress.mir
+++ b/llvm/test/CodeGen/RISCV/stack-inst-compress.mir
@@ -32,6 +32,7 @@ alignment: 2
tracksRegLiveness: true
frameInfo:
maxAlignment: 4
+ adjustsStack: true
hasCalls: true
localFrameSize: 2048
stack:
@@ -117,6 +118,7 @@ alignment: 2
tracksRegLiveness: true
frameInfo:
maxAlignment: 4
+ adjustsStack: true
hasCalls: true
localFrameSize: 4096
stack:
@@ -210,6 +212,7 @@ alignment: 2
tracksRegLiveness: true
frameInfo:
maxAlignment: 4
+ adjustsStack: true
hasCalls: true
localFrameSize: 8192
stack:
diff --git a/llvm/test/CodeGen/SystemZ/cond-move-04.mir b/llvm/test/CodeGen/SystemZ/cond-move-04.mir
index 97aa00f582921d..23fd2739698a40 100644
--- a/llvm/test/CodeGen/SystemZ/cond-move-04.mir
+++ b/llvm/test/CodeGen/SystemZ/cond-move-04.mir
@@ -53,6 +53,7 @@ registers:
- { id: 10, class: gr64bit }
- { id: 11, class: gr32bit }
frameInfo:
+ adjustsStack: true
hasCalls: true
body: |
bb.0 (%ir-block.1):
diff --git a/llvm/test/CodeGen/SystemZ/cond-move-08.mir b/llvm/test/CodeGen/SystemZ/cond-move-08.mir
index 93aa5626b8e894..64c6d069799282 100644
--- a/llvm/test/CodeGen/SystemZ/cond-move-08.mir
+++ b/llvm/test/CodeGen/SystemZ/cond-move-08.mir
@@ -116,6 +116,7 @@ registers:
- { id: 27, class: grx32bit }
- { id: 28, class: addr64bit }
frameInfo:
+ adjustsStack: true
hasCalls: true
body: |
bb.0.bb5:
diff --git a/llvm/test/CodeGen/SystemZ/cond-move-regalloc-hints-02.mir b/llvm/test/CodeGen/SystemZ/cond-move-regalloc-hints-02.mir
index 37e29800fb1d62..2701a1dc034a22 100644
--- a/llvm/test/CodeGen/SystemZ/cond-move-regalloc-hints-02.mir
+++ b/llvm/test/CodeGen/SystemZ/cond-move-regalloc-hints-02.mir
@@ -30,6 +30,7 @@ registers:
- { id: 11, class: gr32bit }
frameInfo:
maxAlignment: 1
+ adjustsStack: true
hasCalls: true
machineFunctionInfo: {}
body: |
diff --git a/llvm/test/CodeGen/SystemZ/cond-move-regalloc-hints.mir b/llvm/test/CodeGen/SystemZ/cond-move-regalloc-hints.mir
index e7e1eaf8f8fdcd..c98ffda8372721 100644
--- a/llvm/test/CodeGen/SystemZ/cond-move-regalloc-hints.mir
+++ b/llvm/test/CodeGen/SystemZ/cond-move-regalloc-hints.mir
@@ -192,6 +192,7 @@ liveins:
- { reg: '$r2d', virtual-reg: '%31' }
- { reg: '$r3d', virtual-reg: '%32' }
frameInfo:
+ adjustsStack: true
hasCalls: true
body: |
bb.0.bb:
diff --git a/llvm/test/CodeGen/SystemZ/frame-28.mir b/llvm/test/CodeGen/SystemZ/frame-28.mir
index dd5933a9c7b4b4..13337dba6ec53f 100644
--- a/llvm/test/CodeGen/SystemZ/frame-28.mir
+++ b/llvm/test/CodeGen/SystemZ/frame-28.mir
@@ -162,6 +162,8 @@ body: |
---
name: fun4
tracksRegLiveness: true
+frameInfo:
+ adjustsStack: true
stack:
- { id: 0, size: 5000 }
- { id: 1, size: 2500 }
diff --git a/llvm/test/CodeGen/X86/fast-regalloc-live-out-debug-values.mir b/llvm/test/CodeGen/X86/fast-regalloc-live-out-debug-values.mir
index 56cbe3f7b56388..37a90a2f16d671 100644
--- a/llvm/test/CodeGen/X86/fast-regalloc-live-out-debug-values.mir
+++ b/llvm/test/CodeGen/X86/fast-regalloc-live-out-debug-values.mir
@@ -119,6 +119,7 @@
name: foo
tracksRegLiveness: true
frameInfo:
+ adjustsStack: true
hasCalls: true
stack:
- { id: 0, name: a.addr, size: 4, alignment: 4, debug-info-variable: '!11',
diff --git a/llvm/test/CodeGen/X86/heap-alloc-markers.mir b/llvm/test/CodeGen/X86/heap-alloc-markers.mir
index 0bf83657cb06c0..6e0dc50bac0e19 100644
--- a/llvm/test/CodeGen/X86/heap-alloc-markers.mir
+++ b/llvm/test/CodeGen/X86/heap-alloc-markers.mir
@@ -34,6 +34,7 @@ name: test
# CHECK-LABEL: {{^}}test:
tracksRegLiveness: true
frameInfo:
+ adjustsStack: true
hasCalls: true
body: |
bb.0.entry:
diff --git a/llvm/test/CodeGen/X86/instr-symbols.mir b/llvm/test/CodeGen/X86/instr-symbols.mir
index a900288d70869a..7af6ca81810123 100644
--- a/llvm/test/CodeGen/X86/instr-symbols.mir
+++ b/llvm/test/CodeGen/X86/instr-symbols.mir
@@ -23,6 +23,7 @@ name: test
# CHECK-LABEL: {{^}}test:
tracksRegLiveness: true
frameInfo:
+ adjustsStack: true
hasCalls: true
body: |
bb.0.entry:
diff --git a/llvm/test/CodeGen/X86/statepoint-fixup-undef.mir b/llvm/test/CodeGen/X86/statepoint-fixup-undef.mir
index 30a68e6c2efd2a..4a18351bde493d 100644
--- a/llvm/test/CodeGen/X86/statepoint-fixup-undef.mir
+++ b/llvm/test/CodeGen/X86/statepoint-fixup-undef.mir
@@ -61,7 +61,7 @@ frameInfo:
stackSize: 0
offsetAdjustment: 0
maxAlignment: 8
- adjustsStack: false
+ adjustsStack: true
hasCalls: true
stackProtector: ''
maxCallFrameSize: 4294967295
diff --git a/llvm/test/CodeGen/X86/statepoint-vreg.mir b/llvm/test/CodeGen/X86/statepoint-vreg.mir
index bfeadfc93da8f6..a0c596f249931c 100644
--- a/llvm/test/CodeGen/X86/statepoint-vreg.mir
+++ b/llvm/test/CodeGen/X86/statepoint-vreg.mir
@@ -134,6 +134,8 @@ registers:
liveins:
- { reg: '$rdi', virtual-reg: '%0' }
- { reg: '$rsi', virtual-reg: '%1' }
+frameInfo:
+ adjustsStack: true
fixedStack: []
stack: []
callSites: []
diff --git a/llvm/test/DebugInfo/MIR/X86/debug-loc-0.mir b/llvm/test/DebugInfo/MIR/X86/debug-loc-0.mir
index 56a4d835aaa597..0b007456be1e6f 100644
--- a/llvm/test/DebugInfo/MIR/X86/debug-loc-0.mir
+++ b/llvm/test/DebugInfo/MIR/X86/debug-loc-0.mir
@@ -75,7 +75,7 @@ frameInfo:
stackSize: 0
offsetAdjustment: 0
maxAlignment: 8
- adjustsStack: false
+ adjustsStack: true
hasCalls: true
stackProtector: ''
maxCallFrameSize: 4294967295
diff --git a/llvm/test/DebugInfo/MIR/X86/prolog-epilog-indirection.mir b/llvm/test/DebugInfo/MIR/X86/prolog-epilog-indirection.mir
index 6941467fe0e4a3..4df967ce034939 100644
--- a/llvm/test/DebugInfo/MIR/X86/prolog-epilog-indirection.mir
+++ b/llvm/test/DebugInfo/MIR/X86/prolog-epilog-indirection.mir
@@ -104,6 +104,7 @@ alignment: 16
tracksRegLiveness: true
frameInfo:
maxAlignment: 4
+ adjustsStack: true
hasCalls: true
stack:
- { id: 0, name: l_1081, type: default, offset: 0, size: 4, alignment: 4,
diff --git a/llvm/test/DebugInfo/X86/live-debug-vars-dse.mir b/llvm/test/DebugInfo/X86/live-debug-vars-dse.mir
index 9443ed5e332c13..908889063584c2 100644
--- a/llvm/test/DebugInfo/X86/live-debug-vars-dse.mir
+++ b/llvm/test/DebugInfo/X86/live-debug-vars-dse.mir
@@ -107,7 +107,7 @@ frameInfo:
stackSize: 0
offsetAdjustment: 0
maxAlignment: 8
- adjustsStack: false
+ adjustsStack: true
hasCalls: true
stackProtector: ''
maxCallFrameSize: 4294967295
diff --git a/llvm/test/DebugInfo/X86/prolog-params.mir b/llvm/test/DebugInfo/X86/prolog-params.mir
index af21bc85ccd746..6629dca810f954 100644
--- a/llvm/test/DebugInfo/X86/prolog-params.mir
+++ b/llvm/test/DebugInfo/X86/prolog-params.mir
@@ -98,6 +98,8 @@ fixedStack:
isImmutable: true, isAliased: false, callee-saved-register: '', callee-saved-restored: true }
- { id: 2, type: default, offset: 0, size: 4, alignment: 16, stack-id: default,
isImmutable: true, isAliased: false, callee-saved-register: '', callee-saved-restored: true }
+frameInfo:
+ adjustsStack: true
stack:
- { id: 0, name: arr, type: default, offset: 0, size: 8, alignment: 4,
stack-id: default, callee-saved-register: '', callee-saved-restored: true }
>From c5177f149b43dcc5a39c2c1aefaf1bba8518fd2e Mon Sep 17 00:00:00 2001
From: Martin Wehking <martin.wehking at codeplay.com>
Date: Mon, 18 Mar 2024 14:38:27 +0000
Subject: [PATCH 18/40] Silence potential overflow warning (#83272)
Cast Offset variable to int64_t type directly inside a multiplication
and function call to utilize 64-bit arithmetic.
Ensure that the multiplication will not overflow.
A static analyzer warned about this since the function expects a 64-bit
argument, but the multiplication is evaluated inside a 32-bit context.
---
llvm/lib/Target/AMDGPU/SIRegisterInfo.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Target/AMDGPU/SIRegisterInfo.cpp b/llvm/lib/Target/AMDGPU/SIRegisterInfo.cpp
index 5cd10aa0c3a707..79a7d1cf66c4d3 100644
--- a/llvm/lib/Target/AMDGPU/SIRegisterInfo.cpp
+++ b/llvm/lib/Target/AMDGPU/SIRegisterInfo.cpp
@@ -1753,12 +1753,12 @@ void SIRegisterInfo::buildVGPRSpillLoadStore(SGPRSpillBuilder &SB, int Index,
unsigned Opc = ST.enableFlatScratch() ? AMDGPU::SCRATCH_LOAD_DWORD_SADDR
: AMDGPU::BUFFER_LOAD_DWORD_OFFSET;
buildSpillLoadStore(*SB.MBB, SB.MI, SB.DL, Opc, Index, SB.TmpVGPR, false,
- FrameReg, Offset * SB.EltSize, MMO, SB.RS);
+ FrameReg, (int64_t)Offset * SB.EltSize, MMO, SB.RS);
} else {
unsigned Opc = ST.enableFlatScratch() ? AMDGPU::SCRATCH_STORE_DWORD_SADDR
: AMDGPU::BUFFER_STORE_DWORD_OFFSET;
buildSpillLoadStore(*SB.MBB, SB.MI, SB.DL, Opc, Index, SB.TmpVGPR, IsKill,
- FrameReg, Offset * SB.EltSize, MMO, SB.RS);
+ FrameReg, (int64_t)Offset * SB.EltSize, MMO, SB.RS);
// This only ever adds one VGPR spill
SB.MFI.addToSpilledVGPRs(1);
}
>From 487f356b20860a3eeb29b836483c639735f9393c Mon Sep 17 00:00:00 2001
From: Orlando Cazalet-Hyams <orlando.hyams at sony.com>
Date: Mon, 18 Mar 2024 14:41:32 +0000
Subject: [PATCH 19/40] [RemoveDIs][AsmWriter] Add empty-metadata operands to
the SlotTracker (#85636)
---
llvm/lib/IR/AsmWriter.cpp | 8 +++++++-
llvm/test/DebugInfo/print-non-instruction-debug-info.ll | 4 ++++
2 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp
index 11383ea6214bf3..19acc89f73fb7e 100644
--- a/llvm/lib/IR/AsmWriter.cpp
+++ b/llvm/lib/IR/AsmWriter.cpp
@@ -1143,9 +1143,15 @@ void SlotTracker::processDbgRecordMetadata(const DbgRecord &DR) {
// Process metadata used by DbgRecords; we only specifically care about the
// DILocalVariable, DILocation, and DIAssignID fields, as the Value and
// Expression fields should only be printed inline and so do not use a slot.
+ // Note: The above doesn't apply for empty-metadata operands.
+ if (auto *Empty = dyn_cast<MDNode>(DPV->getRawLocation()))
+ CreateMetadataSlot(Empty);
CreateMetadataSlot(DPV->getRawVariable());
- if (DPV->isDbgAssign())
+ if (DPV->isDbgAssign()) {
CreateMetadataSlot(cast<MDNode>(DPV->getRawAssignID()));
+ if (auto *Empty = dyn_cast<MDNode>(DPV->getRawAddress()))
+ CreateMetadataSlot(Empty);
+ }
} else if (const DPLabel *DPL = dyn_cast<const DPLabel>(&DR)) {
CreateMetadataSlot(DPL->getRawLabel());
} else {
diff --git a/llvm/test/DebugInfo/print-non-instruction-debug-info.ll b/llvm/test/DebugInfo/print-non-instruction-debug-info.ll
index 2e765619fcb896..490f24ff76ff56 100644
--- a/llvm/test/DebugInfo/print-non-instruction-debug-info.ll
+++ b/llvm/test/DebugInfo/print-non-instruction-debug-info.ll
@@ -26,6 +26,8 @@
; CHECK-NEXT: {{^}} store i32 %[[VAL_ADD]]{{.+}}, !DIAssignID ![[ASSIGNID:[0-9]+]]
; OLDDBG-NEXT: call void @llvm.dbg.assign(metadata i32 %[[VAL_ADD]], metadata ![[VAR_B]], metadata !DIExpression(), metadata ![[ASSIGNID]], metadata ptr %[[VAL_B]], metadata !DIExpression()), !dbg ![[LOC_4:[0-9]+]]
; NEWDBG-NEXT: {{^}} #dbg_assign(i32 %[[VAL_ADD]], ![[VAR_B]], !DIExpression(), ![[ASSIGNID]], ptr %[[VAL_B]], !DIExpression(), ![[LOC_4:[0-9]+]])
+; OLDDBG-NEXT: call void @llvm.dbg.assign(metadata ![[EMPTY:[0-9]+]], metadata ![[VAR_B]], metadata !DIExpression(), metadata ![[ASSIGNID]], metadata ![[EMPTY]], metadata !DIExpression()), !dbg ![[LOC_4]]
+; NEWDBG-NEXT: {{^}} #dbg_assign(![[EMPTY:[0-9]+]], ![[VAR_B]], !DIExpression(), ![[ASSIGNID]], ![[EMPTY]], !DIExpression(), ![[LOC_4]])
; CHECK-NEXT: {{^}} ret i32
; OLDDBG-DAG: declare void @llvm.dbg.value
@@ -40,6 +42,7 @@
; CHECK-DAG: ![[LOC_3]] = !DILocation(line: 3, column: 25
; CHECK-DAG: ![[LOC_4]] = !DILocation(line: 3, column: 30
; CHECK-DAG: ![[LABEL_ID]] = !DILabel(
+; CHECK-DAG: ![[EMPTY]] = !{}
define dso_local i32 @f(i32 %a) !dbg !7 {
entry:
@@ -51,6 +54,7 @@ entry:
call void @llvm.dbg.label(metadata !50), !dbg !32
store i32 %add, ptr %b, !dbg !32, !DIAssignID !40
call void @llvm.dbg.assign(metadata i32 %add, metadata !21, metadata !DIExpression(), metadata !40, metadata ptr %b, metadata !DIExpression()), !dbg !33
+ call void @llvm.dbg.assign(metadata !2, metadata !21, metadata !DIExpression(), metadata !40, metadata !2, metadata !DIExpression()), !dbg !33
ret i32 %add, !dbg !33
}
>From 0c21377aeafc523bd4a8c40bd27e33498f3199f7 Mon Sep 17 00:00:00 2001
From: Kelvin Li <kkwli at users.noreply.github.com>
Date: Mon, 18 Mar 2024 10:59:47 -0400
Subject: [PATCH 20/40] [flang] Diagnose the impure procedure reference in
finalization according to the rank of the entity (#85475)
Use the rank of the array section to determine which final procedure
would be called in diagnosing whether that procedure is impure or not.
---
flang/include/flang/Semantics/tools.h | 3 +-
flang/lib/Semantics/check-do-forall.cpp | 7 +++-
flang/lib/Semantics/tools.cpp | 13 ++++---
flang/test/Semantics/doconcurrent08.f90 | 49 ++++++++++++++++++++++---
4 files changed, 59 insertions(+), 13 deletions(-)
diff --git a/flang/include/flang/Semantics/tools.h b/flang/include/flang/Semantics/tools.h
index df66e1adb55023..dc3cd6c894a2c2 100644
--- a/flang/include/flang/Semantics/tools.h
+++ b/flang/include/flang/Semantics/tools.h
@@ -180,7 +180,8 @@ const Symbol *IsFinalizable(const Symbol &,
const Symbol *IsFinalizable(const DerivedTypeSpec &,
std::set<const DerivedTypeSpec *> * = nullptr,
bool withImpureFinalizer = false, std::optional<int> rank = std::nullopt);
-const Symbol *HasImpureFinal(const Symbol &);
+const Symbol *HasImpureFinal(
+ const Symbol &, std::optional<int> rank = std::nullopt);
// Is this type finalizable or does it contain any polymorphic allocatable
// ultimate components?
bool MayRequireFinalization(const DerivedTypeSpec &derived);
diff --git a/flang/lib/Semantics/check-do-forall.cpp b/flang/lib/Semantics/check-do-forall.cpp
index 4e8578d0e1daff..36340a4c5259a7 100644
--- a/flang/lib/Semantics/check-do-forall.cpp
+++ b/flang/lib/Semantics/check-do-forall.cpp
@@ -220,8 +220,11 @@ class DoConcurrentBodyEnforce {
if (MightDeallocatePolymorphic(*entity, DeallocateNonCoarray)) {
SayDeallocateOfPolymorph(variable.GetSource(), *entity, reason);
}
- if (const Symbol * impure{HasImpureFinal(*entity)}) {
- SayDeallocateWithImpureFinal(*entity, reason, *impure);
+ if (const auto *assignment{GetAssignment(stmt)}) {
+ const auto &lhs{assignment->lhs};
+ if (const Symbol * impure{HasImpureFinal(*entity, lhs.Rank())}) {
+ SayDeallocateWithImpureFinal(*entity, reason, *impure);
+ }
}
}
if (const auto *assignment{GetAssignment(stmt)}) {
diff --git a/flang/lib/Semantics/tools.cpp b/flang/lib/Semantics/tools.cpp
index bf999b090419c6..0484baae93cd59 100644
--- a/flang/lib/Semantics/tools.cpp
+++ b/flang/lib/Semantics/tools.cpp
@@ -827,15 +827,18 @@ static const Symbol *HasImpureFinal(
return IsFinalizable(derived, nullptr, /*withImpureFinalizer=*/true, rank);
}
-const Symbol *HasImpureFinal(const Symbol &original) {
+const Symbol *HasImpureFinal(const Symbol &original, std::optional<int> rank) {
const Symbol &symbol{ResolveAssociations(original)};
if (symbol.has<ObjectEntityDetails>()) {
if (const DeclTypeSpec * symType{symbol.GetType()}) {
if (const DerivedTypeSpec * derived{symType->AsDerived()}) {
- // finalizable assumed-rank not allowed (C839)
- return evaluate::IsAssumedRank(symbol)
- ? nullptr
- : HasImpureFinal(*derived, symbol.Rank());
+ if (evaluate::IsAssumedRank(symbol)) {
+ // finalizable assumed-rank not allowed (C839)
+ return nullptr;
+ } else {
+ int actualRank{rank.value_or(symbol.Rank())};
+ return HasImpureFinal(*derived, actualRank);
+ }
}
}
}
diff --git a/flang/test/Semantics/doconcurrent08.f90 b/flang/test/Semantics/doconcurrent08.f90
index 41cd71e233d0d3..52b382741d0731 100644
--- a/flang/test/Semantics/doconcurrent08.f90
+++ b/flang/test/Semantics/doconcurrent08.f90
@@ -209,6 +209,8 @@ module m2
type :: impureFinal
contains
final :: impureSub
+ final :: impureSubRank1
+ final :: impureSubRank2
end type
type :: pureFinal
@@ -222,16 +224,27 @@ impure subroutine impureSub(x)
type(impureFinal), intent(in) :: x
end subroutine
+ impure subroutine impureSubRank1(x)
+ type(impureFinal), intent(in) :: x(:)
+ end subroutine
+
+ impure subroutine impureSubRank2(x)
+ type(impureFinal), intent(in) :: x(:,:)
+ end subroutine
+
pure subroutine pureSub(x)
type(pureFinal), intent(in) :: x
end subroutine
subroutine s4()
type(impureFinal), allocatable :: ifVar, ifvar1
+ type(impureFinal), allocatable :: ifArr1(:), ifArr2(:,:)
+ type(impureFinal) :: if0
type(pureFinal), allocatable :: pfVar
allocate(ifVar)
allocate(ifVar1)
allocate(pfVar)
+ allocate(ifArr1(5), ifArr2(5,5))
! OK for an ordinary DO loop
do i = 1,10
@@ -239,11 +252,9 @@ subroutine s4()
end do
! OK to invoke a PURE FINAL procedure in a DO CONCURRENT
- ! This case does not work currently because the compiler's test for
- ! HasImpureFinal() in .../lib/Semantics/tools.cc doesn't work correctly
-! do concurrent (i = 1:10)
-! if (i .eq. 1) deallocate(pfVar)
-! end do
+ do concurrent (i = 1:10)
+ if (i .eq. 1) deallocate(pfVar)
+ end do
! Error to invoke an IMPURE FINAL procedure in a DO CONCURRENT
do concurrent (i = 1:10)
@@ -271,6 +282,34 @@ subroutine s4()
ifvar = ifvar1
end if
end do
+
+ do concurrent (i = 1:5)
+ if (i .eq. 1) then
+ !ERROR: Deallocation of an entity with an IMPURE FINAL procedure 'impuresub' caused by assignment not allowed in DO CONCURRENT
+ ifArr1(i) = if0
+ end if
+ end do
+
+ do concurrent (i = 1:5)
+ if (i .eq. 1) then
+ !ERROR: Deallocation of an entity with an IMPURE FINAL procedure 'impuresubrank1' caused by assignment not allowed in DO CONCURRENT
+ ifArr1 = if0
+ end if
+ end do
+
+ do concurrent (i = 1:5)
+ if (i .eq. 1) then
+ !ERROR: Deallocation of an entity with an IMPURE FINAL procedure 'impuresubrank1' caused by assignment not allowed in DO CONCURRENT
+ ifArr2(i,:) = if0
+ end if
+ end do
+
+ do concurrent (i = 1:5)
+ if (i .eq. 1) then
+ !ERROR: Deallocation of an entity with an IMPURE FINAL procedure 'impuresubrank2' caused by assignment not allowed in DO CONCURRENT
+ ifArr2(:,:) = if0
+ end if
+ end do
end subroutine s4
end module m2
>From d56110fa025b58e57602a254c841e6e41ea46a42 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Mon, 18 Mar 2024 14:55:29 +0100
Subject: [PATCH 21/40] [clang][Interp] Fix _Complex comma operators
Handle them before shelling out to visitComplexBinOp().
---
clang/lib/AST/Interp/ByteCodeExprGen.cpp | 21 +++++++++++----------
clang/test/AST/Interp/complex.cpp | 3 +++
2 files changed, 14 insertions(+), 10 deletions(-)
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index d943dcbe06507b..af214d4a8577c6 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -401,6 +401,17 @@ bool ByteCodeExprGen<Emitter>::VisitBinaryOperator(const BinaryOperator *BO) {
const Expr *LHS = BO->getLHS();
const Expr *RHS = BO->getRHS();
+ // Handle comma operators. Just discard the LHS
+ // and delegate to RHS.
+ if (BO->isCommaOp()) {
+ if (!this->discard(LHS))
+ return false;
+ if (RHS->getType()->isVoidType())
+ return this->discard(RHS);
+
+ return this->delegate(RHS);
+ }
+
if (BO->getType()->isAnyComplexType())
return this->VisitComplexBinOp(BO);
if ((LHS->getType()->isAnyComplexType() ||
@@ -416,16 +427,6 @@ bool ByteCodeExprGen<Emitter>::VisitBinaryOperator(const BinaryOperator *BO) {
std::optional<PrimType> RT = classify(RHS->getType());
std::optional<PrimType> T = classify(BO->getType());
- // Deal with operations which have composite or void types.
- if (BO->isCommaOp()) {
- if (!this->discard(LHS))
- return false;
- if (RHS->getType()->isVoidType())
- return this->discard(RHS);
-
- return this->delegate(RHS);
- }
-
// Special case for C++'s three-way/spaceship operator <=>, which
// returns a std::{strong,weak,partial}_ordering (which is a class, so doesn't
// have a PrimType).
diff --git a/clang/test/AST/Interp/complex.cpp b/clang/test/AST/Interp/complex.cpp
index d4e3d5a46a64fb..09cb620d7b7c39 100644
--- a/clang/test/AST/Interp/complex.cpp
+++ b/clang/test/AST/Interp/complex.cpp
@@ -9,6 +9,9 @@ static_assert(&__imag z1 == &__real z1 + 1, "");
static_assert((*(&__imag z1)) == __imag z1, "");
static_assert((*(&__real z1)) == __real z1, "");
+constexpr _Complex int Comma1 = {1, 2};
+constexpr _Complex int Comma2 = (0, Comma1);
+static_assert(Comma1 == Comma1, "");
constexpr double setter() {
_Complex float d = {1.0, 2.0};
>From 73381a8df80674bd5198a89e8e879c64c48121b8 Mon Sep 17 00:00:00 2001
From: Jie Fu <jiefu at tencent.com>
Date: Mon, 18 Mar 2024 23:09:54 +0800
Subject: [PATCH 22/40] [CodeGen] Fix -Wunused-variable in
PrologEpilogInserter.cpp (NFC)
llvm-project/llvm/lib/CodeGen/PrologEpilogInserter.cpp:369:12:
error: unused variable 'MaxCFSIn' [-Werror,-Wunused-variable]
uint32_t MaxCFSIn =
^
1 error generated.
---
llvm/lib/CodeGen/PrologEpilogInserter.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/CodeGen/PrologEpilogInserter.cpp b/llvm/lib/CodeGen/PrologEpilogInserter.cpp
index e77d5e658962b1..eaf96ec5cbde8c 100644
--- a/llvm/lib/CodeGen/PrologEpilogInserter.cpp
+++ b/llvm/lib/CodeGen/PrologEpilogInserter.cpp
@@ -366,7 +366,7 @@ void PEI::calculateCallFrameInfo(MachineFunction &MF) {
return;
// (Re-)Compute the MaxCallFrameSize.
- uint32_t MaxCFSIn =
+ [[maybe_unused]] uint32_t MaxCFSIn =
MFI.isMaxCallFrameSizeComputed() ? MFI.getMaxCallFrameSize() : UINT32_MAX;
std::vector<MachineBasicBlock::iterator> FrameSDOps;
MFI.computeMaxCallFrameSize(MF, &FrameSDOps);
>From 0c423af59c971ddf1aa12d94529edf8293608157 Mon Sep 17 00:00:00 2001
From: Hirofumi Nakamura <k.nakamura.hirofumi at gmail.com>
Date: Tue, 19 Mar 2024 00:13:59 +0900
Subject: [PATCH 23/40] [clang-format] Add Options to break inside the TableGen
DAGArg. (#83149)
Add two options to control the line break inside TableGen DAGArg.
- TableGenBreakInsideDAGArg
- TableGenBreakingDAGArgOperators
---
clang/docs/ClangFormatStyleOptions.rst | 64 +++++++++++++++
clang/include/clang/Format/Format.h | 63 ++++++++++++++-
clang/lib/Format/ContinuationIndenter.cpp | 19 ++++-
clang/lib/Format/Format.cpp | 14 ++++
clang/lib/Format/FormatToken.h | 4 +
clang/lib/Format/TokenAnnotator.cpp | 64 ++++++++++++++-
clang/unittests/Format/FormatTestTableGen.cpp | 79 +++++++++++++++++++
clang/unittests/Format/TokenAnnotatorTest.cpp | 70 ++++++++++++++++
8 files changed, 371 insertions(+), 6 deletions(-)
diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst
index 5b00a8f4c00fb8..35b6d0a2b52b67 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -6158,6 +6158,70 @@ the configuration (without a prefix: ``Auto``).
**TabWidth** (``Unsigned``) :versionbadge:`clang-format 3.7` :ref:`¶ <TabWidth>`
The number of columns used for tab stops.
+.. _TableGenBreakInsideDAGArg:
+
+**TableGenBreakInsideDAGArg** (``DAGArgStyle``) :versionbadge:`clang-format 19` :ref:`¶ <TableGenBreakInsideDAGArg>`
+ The styles of the line break inside the DAGArg in TableGen.
+
+ Possible values:
+
+ * ``DAS_DontBreak`` (in configuration: ``DontBreak``)
+ Never break inside DAGArg.
+
+ .. code-block:: c++
+
+ let DAGArgIns = (ins i32:$src1, i32:$src2);
+
+ * ``DAS_BreakElements`` (in configuration: ``BreakElements``)
+ Break inside DAGArg after each list element but for the last.
+ This aligns to the first element.
+
+ .. code-block:: c++
+
+ let DAGArgIns = (ins i32:$src1,
+ i32:$src2);
+
+ * ``DAS_BreakAll`` (in configuration: ``BreakAll``)
+ Break inside DAGArg after the operator and the all elements.
+
+ .. code-block:: c++
+
+ let DAGArgIns = (ins
+ i32:$src1,
+ i32:$src2
+ );
+
+
+
+.. _TableGenBreakingDAGArgOperators:
+
+**TableGenBreakingDAGArgOperators** (``List of Strings``) :versionbadge:`clang-format 19` :ref:`¶ <TableGenBreakingDAGArgOperators>`
+ Works only when TableGenBreakInsideDAGArg is not DontBreak.
+ The string list needs to consist of identifiers in TableGen.
+ If any identifier is specified, this limits the line breaks by
+ TableGenBreakInsideDAGArg option only on DAGArg values beginning with
+ the specified identifiers.
+
+ For example the configuration,
+
+ .. code-block:: c++
+
+ TableGenBreakInsideDAGArg: BreakAll
+ TableGenBreakingDAGArgOperators: ['ins', 'outs']
+
+ makes the line break only occurs inside DAGArgs beginning with the
+ specified identifiers 'ins' and 'outs'.
+
+
+ .. code-block:: c++
+
+ let DAGArgIns = (ins
+ i32:$src1,
+ i32:$src2
+ );
+ let DAGArgOtherID = (other i32:$other1, i32:$other2);
+ let DAGArgBang = (!cast<SomeType>("Some") i32:$src1, i32:$src2)
+
.. _TypeNames:
**TypeNames** (``List of Strings``) :versionbadge:`clang-format 17` :ref:`¶ <TypeNames>`
diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index 590297fd89a398..54861a66889e22 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -4728,6 +4728,60 @@ struct FormatStyle {
/// \version 8
std::vector<std::string> StatementMacros;
+ /// Works only when TableGenBreakInsideDAGArg is not DontBreak.
+ /// The string list needs to consist of identifiers in TableGen.
+ /// If any identifier is specified, this limits the line breaks by
+ /// TableGenBreakInsideDAGArg option only on DAGArg values beginning with
+ /// the specified identifiers.
+ ///
+ /// For example the configuration,
+ /// \code
+ /// TableGenBreakInsideDAGArg: BreakAll
+ /// TableGenBreakingDAGArgOperators: ['ins', 'outs']
+ /// \endcode
+ ///
+ /// makes the line break only occurs inside DAGArgs beginning with the
+ /// specified identifiers 'ins' and 'outs'.
+ ///
+ /// \code
+ /// let DAGArgIns = (ins
+ /// i32:$src1,
+ /// i32:$src2
+ /// );
+ /// let DAGArgOtherID = (other i32:$other1, i32:$other2);
+ /// let DAGArgBang = (!cast<SomeType>("Some") i32:$src1, i32:$src2)
+ /// \endcode
+ /// \version 19
+ std::vector<std::string> TableGenBreakingDAGArgOperators;
+
+ /// Different ways to control the format inside TableGen DAGArg.
+ enum DAGArgStyle : int8_t {
+ /// Never break inside DAGArg.
+ /// \code
+ /// let DAGArgIns = (ins i32:$src1, i32:$src2);
+ /// \endcode
+ DAS_DontBreak,
+ /// Break inside DAGArg after each list element but for the last.
+ /// This aligns to the first element.
+ /// \code
+ /// let DAGArgIns = (ins i32:$src1,
+ /// i32:$src2);
+ /// \endcode
+ DAS_BreakElements,
+ /// Break inside DAGArg after the operator and the all elements.
+ /// \code
+ /// let DAGArgIns = (ins
+ /// i32:$src1,
+ /// i32:$src2
+ /// );
+ /// \endcode
+ DAS_BreakAll,
+ };
+
+ /// The styles of the line break inside the DAGArg in TableGen.
+ /// \version 19
+ DAGArgStyle TableGenBreakInsideDAGArg;
+
/// The number of columns used for tab stops.
/// \version 3.7
unsigned TabWidth;
@@ -4980,9 +5034,12 @@ struct FormatStyle {
SpacesInSquareBrackets == R.SpacesInSquareBrackets &&
Standard == R.Standard &&
StatementAttributeLikeMacros == R.StatementAttributeLikeMacros &&
- StatementMacros == R.StatementMacros && TabWidth == R.TabWidth &&
- TypeNames == R.TypeNames && TypenameMacros == R.TypenameMacros &&
- UseTab == R.UseTab &&
+ StatementMacros == R.StatementMacros &&
+ TableGenBreakingDAGArgOperators ==
+ R.TableGenBreakingDAGArgOperators &&
+ TableGenBreakInsideDAGArg == R.TableGenBreakInsideDAGArg &&
+ TabWidth == R.TabWidth && TypeNames == R.TypeNames &&
+ TypenameMacros == R.TypenameMacros && UseTab == R.UseTab &&
VerilogBreakBetweenInstancePorts ==
R.VerilogBreakBetweenInstancePorts &&
WhitespaceSensitiveMacros == R.WhitespaceSensitiveMacros;
diff --git a/clang/lib/Format/ContinuationIndenter.cpp b/clang/lib/Format/ContinuationIndenter.cpp
index 3987f2185450ec..b3de317f16336c 100644
--- a/clang/lib/Format/ContinuationIndenter.cpp
+++ b/clang/lib/Format/ContinuationIndenter.cpp
@@ -815,6 +815,7 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun,
!CurrentState.IsCSharpGenericTypeConstraint && Previous.opensScope() &&
Previous.isNot(TT_ObjCMethodExpr) && Previous.isNot(TT_RequiresClause) &&
Previous.isNot(TT_TableGenDAGArgOpener) &&
+ Previous.isNot(TT_TableGenDAGArgOpenerToBreak) &&
!(Current.MacroParent && Previous.MacroParent) &&
(Current.isNot(TT_LineComment) ||
Previous.isOneOf(BK_BracedInit, TT_VerilogMultiLineListLParen))) {
@@ -1437,7 +1438,9 @@ unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) {
Style.BreakInheritanceList == FormatStyle::BILS_AfterColon) {
return CurrentState.Indent;
}
- if (Previous.is(tok::r_paren) && !Current.isBinaryOperator() &&
+ if (Previous.is(tok::r_paren) &&
+ Previous.isNot(TT_TableGenDAGArgOperatorToBreak) &&
+ !Current.isBinaryOperator() &&
!Current.isOneOf(tok::colon, tok::comment)) {
return ContinuationIndent;
}
@@ -1698,7 +1701,8 @@ void ContinuationIndenter::moveStatePastFakeLParens(LineState &State,
(Style.AlignAfterOpenBracket != FormatStyle::BAS_DontAlign ||
PrecedenceLevel != prec::Comma || Current.NestingLevel == 0) &&
(!Style.isTableGen() ||
- (Previous && Previous->is(TT_TableGenDAGArgListComma)))) {
+ (Previous && Previous->isOneOf(TT_TableGenDAGArgListComma,
+ TT_TableGenDAGArgListCommaToBreak)))) {
NewParenState.Indent = std::max(
std::max(State.Column, NewParenState.Indent), CurrentState.LastSpace);
}
@@ -1835,6 +1839,17 @@ void ContinuationIndenter::moveStatePastScopeOpener(LineState &State,
Style.ContinuationIndentWidth +
std::max(CurrentState.LastSpace, CurrentState.StartOfFunctionCall);
+ if (Style.isTableGen() && Current.is(TT_TableGenDAGArgOpenerToBreak) &&
+ Style.TableGenBreakInsideDAGArg == FormatStyle::DAS_BreakElements) {
+ // For the case the next token is a TableGen DAGArg operator identifier
+ // that is not marked to have a line break after it.
+ // In this case the option DAS_BreakElements requires to align the
+ // DAGArg elements to the operator.
+ const FormatToken *Next = Current.Next;
+ if (Next && Next->is(TT_TableGenDAGArgOperatorID))
+ NewIndent = State.Column + Next->TokenText.size() + 2;
+ }
+
// Ensure that different different brackets force relative alignment, e.g.:
// void SomeFunction(vector< // break
// int> v);
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index 470e79660b5db6..89813badc8ec20 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -279,6 +279,14 @@ struct ScalarEnumerationTraits<FormatStyle::BreakTemplateDeclarationsStyle> {
}
};
+template <> struct ScalarEnumerationTraits<FormatStyle::DAGArgStyle> {
+ static void enumeration(IO &IO, FormatStyle::DAGArgStyle &Value) {
+ IO.enumCase(Value, "DontBreak", FormatStyle::DAS_DontBreak);
+ IO.enumCase(Value, "BreakElements", FormatStyle::DAS_BreakElements);
+ IO.enumCase(Value, "BreakAll", FormatStyle::DAS_BreakAll);
+ }
+};
+
template <>
struct ScalarEnumerationTraits<FormatStyle::DefinitionReturnTypeBreakingStyle> {
static void
@@ -1092,6 +1100,10 @@ template <> struct MappingTraits<FormatStyle> {
IO.mapOptional("StatementAttributeLikeMacros",
Style.StatementAttributeLikeMacros);
IO.mapOptional("StatementMacros", Style.StatementMacros);
+ IO.mapOptional("TableGenBreakingDAGArgOperators",
+ Style.TableGenBreakingDAGArgOperators);
+ IO.mapOptional("TableGenBreakInsideDAGArg",
+ Style.TableGenBreakInsideDAGArg);
IO.mapOptional("TabWidth", Style.TabWidth);
IO.mapOptional("TypeNames", Style.TypeNames);
IO.mapOptional("TypenameMacros", Style.TypenameMacros);
@@ -1552,6 +1564,8 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
LLVMStyle.StatementAttributeLikeMacros.push_back("Q_EMIT");
LLVMStyle.StatementMacros.push_back("Q_UNUSED");
LLVMStyle.StatementMacros.push_back("QT_REQUIRE_VERSION");
+ LLVMStyle.TableGenBreakingDAGArgOperators = {};
+ LLVMStyle.TableGenBreakInsideDAGArg = FormatStyle::DAS_DontBreak;
LLVMStyle.TabWidth = 8;
LLVMStyle.UseTab = FormatStyle::UT_Never;
LLVMStyle.VerilogBreakBetweenInstancePorts = true;
diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h
index 2d6116c43cfad0..a141db3d41d7b9 100644
--- a/clang/lib/Format/FormatToken.h
+++ b/clang/lib/Format/FormatToken.h
@@ -156,7 +156,11 @@ extern bool IsCpp;
TYPE(TableGenDAGArgCloser) \
TYPE(TableGenDAGArgListColon) \
TYPE(TableGenDAGArgListComma) \
+ TYPE(TableGenDAGArgListCommaToBreak) \
TYPE(TableGenDAGArgOpener) \
+ TYPE(TableGenDAGArgOpenerToBreak) \
+ TYPE(TableGenDAGArgOperatorID) \
+ TYPE(TableGenDAGArgOperatorToBreak) \
TYPE(TableGenListCloser) \
TYPE(TableGenListOpener) \
TYPE(TableGenMultiLineString) \
diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp
index 66af8cdd498cab..911f7cca470a32 100644
--- a/clang/lib/Format/TokenAnnotator.cpp
+++ b/clang/lib/Format/TokenAnnotator.cpp
@@ -986,16 +986,59 @@ class AnnotatingParser {
return false;
}
+ // Judge if the token is a operator ID to insert line break in DAGArg.
+ // That is, TableGenBreakingDAGArgOperators is empty (by the definition of the
+ // option) or the token is in the list.
+ bool isTableGenDAGArgBreakingOperator(const FormatToken &Tok) {
+ auto &Opes = Style.TableGenBreakingDAGArgOperators;
+ // If the list is empty, all operators are breaking operators.
+ if (Opes.empty())
+ return true;
+ // Otherwise, the operator is limited to normal identifiers.
+ if (Tok.isNot(tok::identifier) ||
+ Tok.isOneOf(TT_TableGenBangOperator, TT_TableGenCondOperator)) {
+ return false;
+ }
+ // The case next is colon, it is not a operator of identifier.
+ if (!Tok.Next || Tok.Next->is(tok::colon))
+ return false;
+ return std::find(Opes.begin(), Opes.end(), Tok.TokenText.str()) !=
+ Opes.end();
+ }
+
// SimpleValue6 ::= "(" DagArg [DagArgList] ")"
// This parses SimpleValue 6's inside part of "(" ")"
bool parseTableGenDAGArgAndList(FormatToken *Opener) {
+ FormatToken *FirstTok = CurrentToken;
if (!parseTableGenDAGArg())
return false;
+ bool BreakInside = false;
+ if (Style.TableGenBreakInsideDAGArg != FormatStyle::DAS_DontBreak) {
+ // Specialized detection for DAGArgOperator, that determines the way of
+ // line break for this DAGArg elements.
+ if (isTableGenDAGArgBreakingOperator(*FirstTok)) {
+ // Special case for identifier DAGArg operator.
+ BreakInside = true;
+ Opener->setType(TT_TableGenDAGArgOpenerToBreak);
+ if (FirstTok->isOneOf(TT_TableGenBangOperator,
+ TT_TableGenCondOperator)) {
+ // Special case for bang/cond operators. Set the whole operator as
+ // the DAGArg operator. Always break after it.
+ CurrentToken->Previous->setType(TT_TableGenDAGArgOperatorToBreak);
+ } else if (FirstTok->is(tok::identifier)) {
+ if (Style.TableGenBreakInsideDAGArg == FormatStyle::DAS_BreakAll)
+ FirstTok->setType(TT_TableGenDAGArgOperatorToBreak);
+ else
+ FirstTok->setType(TT_TableGenDAGArgOperatorID);
+ }
+ }
+ }
// Parse the [DagArgList] part
bool FirstDAGArgListElm = true;
while (CurrentToken) {
if (!FirstDAGArgListElm && CurrentToken->is(tok::comma)) {
- CurrentToken->setType(TT_TableGenDAGArgListComma);
+ CurrentToken->setType(BreakInside ? TT_TableGenDAGArgListCommaToBreak
+ : TT_TableGenDAGArgListComma);
skipToNextNonComment();
}
if (CurrentToken && CurrentToken->is(tok::r_paren)) {
@@ -5086,6 +5129,11 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line,
}
if (Right.is(TT_TableGenCondOperatorColon))
return false;
+ if (Left.isOneOf(TT_TableGenDAGArgOperatorID,
+ TT_TableGenDAGArgOperatorToBreak) &&
+ Right.isNot(TT_TableGenDAGArgCloser)) {
+ return true;
+ }
// Do not insert bang operators and consequent openers.
if (Right.isOneOf(tok::l_paren, tok::less) &&
Left.isOneOf(TT_TableGenBangOperator, TT_TableGenCondOperator)) {
@@ -5462,6 +5510,18 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line,
// case2:0);
if (Left.is(TT_TableGenCondOperatorComma))
return true;
+ if (Left.is(TT_TableGenDAGArgOperatorToBreak) &&
+ Right.isNot(TT_TableGenDAGArgCloser)) {
+ return true;
+ }
+ if (Left.is(TT_TableGenDAGArgListCommaToBreak))
+ return true;
+ if (Right.is(TT_TableGenDAGArgCloser) && Right.MatchingParen &&
+ Right.MatchingParen->is(TT_TableGenDAGArgOpenerToBreak) &&
+ &Left != Right.MatchingParen->Next) {
+ // Check to avoid empty DAGArg such as (ins).
+ return Style.TableGenBreakInsideDAGArg == FormatStyle::DAS_BreakAll;
+ }
}
if (Line.startsWith(tok::kw_asm) && Right.is(TT_InlineASMColon) &&
@@ -5876,6 +5936,8 @@ bool TokenAnnotator::canBreakBefore(const AnnotatedLine &Line,
// Avoid to break around paste operator.
if (Left.is(tok::hash) || Right.is(tok::hash))
return false;
+ if (Left.isOneOf(TT_TableGenBangOperator, TT_TableGenCondOperator))
+ return false;
}
if (Left.is(tok::at))
diff --git a/clang/unittests/Format/FormatTestTableGen.cpp b/clang/unittests/Format/FormatTestTableGen.cpp
index 76b871e2e1a522..c96866f0840f00 100644
--- a/clang/unittests/Format/FormatTestTableGen.cpp
+++ b/clang/unittests/Format/FormatTestTableGen.cpp
@@ -332,6 +332,85 @@ TEST_F(FormatTestTableGen, Assert) {
verifyFormat("assert !le(DefVar1, 0), \"Assert1\";\n");
}
+TEST_F(FormatTestTableGen, DAGArgBreakElements) {
+ FormatStyle Style = getGoogleStyle(FormatStyle::LK_TableGen);
+ Style.ColumnLimit = 60;
+ // By default, the DAGArg does not have a break inside.
+ ASSERT_EQ(Style.TableGenBreakInsideDAGArg, FormatStyle::DAS_DontBreak);
+ verifyFormat("def Def : Parent {\n"
+ " let dagarg = (ins a:$src1, aa:$src2, aaa:$src3)\n"
+ "}\n",
+ Style);
+ // This option forces to break inside the DAGArg.
+ Style.TableGenBreakInsideDAGArg = FormatStyle::DAS_BreakElements;
+ verifyFormat("def Def : Parent {\n"
+ " let dagarg = (ins a:$src1,\n"
+ " aa:$src2,\n"
+ " aaa:$src3);\n"
+ "}\n",
+ Style);
+ verifyFormat("def Def : Parent {\n"
+ " let dagarg = (other a:$src1,\n"
+ " aa:$src2,\n"
+ " aaa:$src3);\n"
+ "}\n",
+ Style);
+ // Then, limit the DAGArg operator only to "ins".
+ Style.TableGenBreakingDAGArgOperators = {"ins"};
+ verifyFormat("def Def : Parent {\n"
+ " let dagarg = (ins a:$src1,\n"
+ " aa:$src2,\n"
+ " aaa:$src3);\n"
+ "}\n",
+ Style);
+ verifyFormat("def Def : Parent {\n"
+ " let dagarg = (other a:$src1, aa:$src2, aaa:$src3)\n"
+ "}\n",
+ Style);
+}
+
+TEST_F(FormatTestTableGen, DAGArgBreakAll) {
+ FormatStyle Style = getGoogleStyle(FormatStyle::LK_TableGen);
+ Style.ColumnLimit = 60;
+ // By default, the DAGArg does not have a break inside.
+ verifyFormat("def Def : Parent {\n"
+ " let dagarg = (ins a:$src1, aa:$src2, aaa:$src3)\n"
+ "}\n",
+ Style);
+ // This option forces to break inside the DAGArg.
+ Style.TableGenBreakInsideDAGArg = FormatStyle::DAS_BreakAll;
+ verifyFormat("def Def : Parent {\n"
+ " let dagarg = (ins\n"
+ " a:$src1,\n"
+ " aa:$src2,\n"
+ " aaa:$src3\n"
+ " );\n"
+ "}\n",
+ Style);
+ verifyFormat("def Def : Parent {\n"
+ " let dagarg = (other\n"
+ " a:$src1,\n"
+ " aa:$src2,\n"
+ " aaa:$src3\n"
+ " );\n"
+ "}\n",
+ Style);
+ // Then, limit the DAGArg operator only to "ins".
+ Style.TableGenBreakingDAGArgOperators = {"ins"};
+ verifyFormat("def Def : Parent {\n"
+ " let dagarg = (ins\n"
+ " a:$src1,\n"
+ " aa:$src2,\n"
+ " aaa:$src3\n"
+ " );\n"
+ "}\n",
+ Style);
+ verifyFormat("def Def : Parent {\n"
+ " let dagarg = (other a:$src1, aa:$src2, aaa:$src3);\n"
+ "}\n",
+ Style);
+}
+
TEST_F(FormatTestTableGen, CondOperatorAlignment) {
FormatStyle Style = getGoogleStyle(FormatStyle::LK_TableGen);
Style.ColumnLimit = 60;
diff --git a/clang/unittests/Format/TokenAnnotatorTest.cpp b/clang/unittests/Format/TokenAnnotatorTest.cpp
index b30ea64201bf8d..bfb71d1f7e97ea 100644
--- a/clang/unittests/Format/TokenAnnotatorTest.cpp
+++ b/clang/unittests/Format/TokenAnnotatorTest.cpp
@@ -2367,6 +2367,76 @@ TEST_F(TokenAnnotatorTest, UnderstandTableGenTokens) {
EXPECT_TOKEN(Tokens[4], tok::less, TT_TemplateOpener);
EXPECT_TOKEN(Tokens[6], tok::greater, TT_TemplateCloser);
EXPECT_TOKEN(Tokens[7], tok::l_brace, TT_FunctionLBrace);
+
+ // DAGArg breaking options. They use different token types depending on what
+ // is specified.
+ Style.TableGenBreakInsideDAGArg = FormatStyle::DAS_BreakElements;
+
+ // When TableGenBreakInsideDAGArg is DAS_BreakElements and
+ // TableGenBreakingDAGArgOperators is not specified, it makes all the DAGArg
+ // elements to have line break.
+ Tokens = AnnotateValue("(ins type1:$src1, type2:$src2)");
+ ASSERT_EQ(Tokens.size(), 10u) << Tokens;
+ EXPECT_TOKEN(Tokens[0], tok::l_paren, TT_TableGenDAGArgOpenerToBreak);
+ EXPECT_TOKEN(Tokens[1], tok::identifier,
+ TT_TableGenDAGArgOperatorID); // ins
+ EXPECT_TOKEN(Tokens[5], tok::comma, TT_TableGenDAGArgListCommaToBreak);
+ EXPECT_TOKEN(Tokens[9], tok::r_paren, TT_TableGenDAGArgCloser);
+
+ Tokens = AnnotateValue("(other type1:$src1, type2:$src2)");
+ ASSERT_EQ(Tokens.size(), 10u) << Tokens;
+ EXPECT_TOKEN(Tokens[0], tok::l_paren, TT_TableGenDAGArgOpenerToBreak);
+ EXPECT_TOKEN(Tokens[1], tok::identifier,
+ TT_TableGenDAGArgOperatorID); // other
+ EXPECT_TOKEN(Tokens[5], tok::comma, TT_TableGenDAGArgListCommaToBreak);
+ EXPECT_TOKEN(Tokens[9], tok::r_paren, TT_TableGenDAGArgCloser);
+
+ // For non-identifier operators, breaks after the operator.
+ Tokens = AnnotateValue("(!cast<Type>(\"Name\") type1:$src1, type2:$src2)");
+ ASSERT_EQ(Tokens.size(), 16u) << Tokens;
+ EXPECT_TOKEN(Tokens[0], tok::l_paren, TT_TableGenDAGArgOpenerToBreak);
+ EXPECT_TOKEN(Tokens[7], tok::r_paren, TT_TableGenDAGArgOperatorToBreak);
+ EXPECT_TOKEN(Tokens[11], tok::comma, TT_TableGenDAGArgListCommaToBreak);
+ EXPECT_TOKEN(Tokens[15], tok::r_paren, TT_TableGenDAGArgCloser);
+
+ Style.TableGenBreakInsideDAGArg = FormatStyle::DAS_BreakAll;
+
+ // When TableGenBreakInsideDAGArg is DAS_BreakAll and
+ // TableGenBreakingDAGArgOperators is not specified, it makes all the DAGArg
+ // to have line break inside it.
+ Tokens = AnnotateValue("(ins type1:$src1, type2:$src2)");
+ ASSERT_EQ(Tokens.size(), 10u) << Tokens;
+ EXPECT_TOKEN(Tokens[0], tok::l_paren, TT_TableGenDAGArgOpenerToBreak);
+ EXPECT_TOKEN(Tokens[1], tok::identifier,
+ TT_TableGenDAGArgOperatorToBreak); // ins
+ EXPECT_TOKEN(Tokens[5], tok::comma, TT_TableGenDAGArgListCommaToBreak);
+ EXPECT_TOKEN(Tokens[9], tok::r_paren, TT_TableGenDAGArgCloser);
+
+ Tokens = AnnotateValue("(other type1:$src1, type2:$src2)");
+ ASSERT_EQ(Tokens.size(), 10u) << Tokens;
+ EXPECT_TOKEN(Tokens[0], tok::l_paren, TT_TableGenDAGArgOpenerToBreak);
+ EXPECT_TOKEN(Tokens[1], tok::identifier,
+ TT_TableGenDAGArgOperatorToBreak); // other
+ EXPECT_TOKEN(Tokens[5], tok::comma, TT_TableGenDAGArgListCommaToBreak);
+ EXPECT_TOKEN(Tokens[9], tok::r_paren, TT_TableGenDAGArgCloser);
+
+ // If TableGenBreakingDAGArgOperators is specified, it is limited to the
+ // specified operators.
+ Style.TableGenBreakingDAGArgOperators = {"ins", "outs"};
+ Tokens = AnnotateValue("(ins type1:$src1, type2:$src2)");
+ ASSERT_EQ(Tokens.size(), 10u) << Tokens;
+ EXPECT_TOKEN(Tokens[0], tok::l_paren, TT_TableGenDAGArgOpenerToBreak);
+ EXPECT_TOKEN(Tokens[1], tok::identifier,
+ TT_TableGenDAGArgOperatorToBreak); // ins
+ EXPECT_TOKEN(Tokens[5], tok::comma, TT_TableGenDAGArgListCommaToBreak);
+ EXPECT_TOKEN(Tokens[9], tok::r_paren, TT_TableGenDAGArgCloser);
+
+ Tokens = AnnotateValue("(other type1:$src1, type2:$src2)");
+ ASSERT_EQ(Tokens.size(), 10u) << Tokens;
+ EXPECT_TOKEN(Tokens[0], tok::l_paren, TT_TableGenDAGArgOpener);
+ EXPECT_TOKEN(Tokens[1], tok::identifier, TT_Unknown); // other
+ EXPECT_TOKEN(Tokens[5], tok::comma, TT_TableGenDAGArgListComma);
+ EXPECT_TOKEN(Tokens[9], tok::r_paren, TT_TableGenDAGArgCloser);
}
TEST_F(TokenAnnotatorTest, UnderstandConstructors) {
>From f6f42af06f6fe6a78f044686a36e4995d4f42ac5 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Mon, 18 Mar 2024 11:40:07 -0400
Subject: [PATCH 24/40] [libc] Add `shm_open/shm_unlink` (#84974)
---
libc/config/linux/aarch64/entrypoints.txt | 2 +
libc/config/linux/api.td | 2 +-
libc/config/linux/riscv/entrypoints.txt | 2 +
libc/config/linux/x86_64/entrypoints.txt | 2 +
libc/docs/dev/undefined_behavior.rst | 4 +
libc/spec/posix.td | 11 ++
libc/src/__support/CPP/string_view.h | 21 +++-
libc/src/sys/mman/CMakeLists.txt | 14 +++
libc/src/sys/mman/linux/CMakeLists.txt | 37 ++++++
libc/src/sys/mman/linux/shm_common.h | 53 +++++++++
libc/src/sys/mman/linux/shm_open.cpp | 25 ++++
libc/src/sys/mman/linux/shm_unlink.cpp | 22 ++++
libc/src/sys/mman/shm_open.h | 20 ++++
libc/src/sys/mman/shm_unlink.h | 18 +++
.../src/__support/CPP/stringview_test.cpp | 108 ++++++++++++++++++
libc/test/src/sys/mman/linux/CMakeLists.txt | 20 ++++
libc/test/src/sys/mman/linux/shm_test.cpp | 77 +++++++++++++
17 files changed, 435 insertions(+), 3 deletions(-)
create mode 100644 libc/src/sys/mman/linux/shm_common.h
create mode 100644 libc/src/sys/mman/linux/shm_open.cpp
create mode 100644 libc/src/sys/mman/linux/shm_unlink.cpp
create mode 100644 libc/src/sys/mman/shm_open.h
create mode 100644 libc/src/sys/mman/shm_unlink.h
create mode 100644 libc/test/src/sys/mman/linux/shm_test.cpp
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index 43c9e81f17833e..a48a94f715eb7e 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -216,6 +216,8 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.sys.mman.mlockall
libc.src.sys.mman.munlockall
libc.src.sys.mman.msync
+ libc.src.sys.mman.shm_open
+ libc.src.sys.mman.shm_unlink
# sys/random.h entrypoints
libc.src.sys.random.getrandom
diff --git a/libc/config/linux/api.td b/libc/config/linux/api.td
index 04d720daf9509c..e9e82c5d478945 100644
--- a/libc/config/linux/api.td
+++ b/libc/config/linux/api.td
@@ -117,7 +117,7 @@ def SchedAPI : PublicAPI<"sched.h"> {
}
def SysMManAPI : PublicAPI<"sys/mman.h"> {
- let Types = ["off_t", "size_t"];
+ let Types = ["off_t", "size_t", "mode_t"];
}
def SignalAPI : PublicAPI<"signal.h"> {
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 99ef84d3f73974..5e28378b403a79 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -221,6 +221,8 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.sys.mman.mlockall
libc.src.sys.mman.munlockall
libc.src.sys.mman.msync
+ libc.src.sys.mman.shm_open
+ libc.src.sys.mman.shm_unlink
# sys/random.h entrypoints
libc.src.sys.random.getrandom
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 99182e7f92ac09..ee02488ca7cd7c 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -229,6 +229,8 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.sys.mman.mlockall
libc.src.sys.mman.munlockall
libc.src.sys.mman.msync
+ libc.src.sys.mman.shm_open
+ libc.src.sys.mman.shm_unlink
# sys/random.h entrypoints
libc.src.sys.random.getrandom
diff --git a/libc/docs/dev/undefined_behavior.rst b/libc/docs/dev/undefined_behavior.rst
index 6e73a305e8e054..3a06b7007febd4 100644
--- a/libc/docs/dev/undefined_behavior.rst
+++ b/libc/docs/dev/undefined_behavior.rst
@@ -70,3 +70,7 @@ Design Decisions
Resizable Tables for hsearch
----------------------------
The POSIX.1 standard does not delineate the behavior consequent to invoking hsearch or hdestroy without prior initialization of the hash table via hcreate. Furthermore, the standard does not specify the outcomes of successive invocations of hsearch absent intervening hdestroy calls. Libraries such as MUSL and Glibc do not apply checks to these scenarios, potentially leading to memory corruption or leakage. Conversely, FreeBSD's libc and Bionic automatically initialize the hash table to a minimal size if it is found uninitialized, and proceeding to destroy the table only if initialization has occurred. This approach also avoids redundant table allocation if an initialized hash table is already present. Given that the hash table starts with a minimal size, resizing becomes necessary to accommodate additional user insertions. LLVM's libc mirrors the approach of FreeBSD's libc and Bionic, owing to its enhanced robustness and user-friendliness. Notably, such resizing behavior itself aligns with POSIX.1 standards, which explicitly permit implementations to modify the capacity of the hash table.
+
+Path without Leading Slashs in shm_open
+----------------------------------------
+POSIX.1 leaves that when the name of a shared memory object does not begin with a slash, the behavior is implementation defined. In such cases, the shm_open in LLVM libc is implemented to behave as if the name began with a slash.
diff --git a/libc/spec/posix.td b/libc/spec/posix.td
index 591919aac95dd5..26f41b61dc0015 100644
--- a/libc/spec/posix.td
+++ b/libc/spec/posix.td
@@ -246,6 +246,7 @@ def POSIX : StandardSpec<"POSIX"> {
[
SizeTType,
OffTType,
+ ModeTType,
],
[], // Enumerations
[
@@ -310,6 +311,16 @@ def POSIX : StandardSpec<"POSIX"> {
RetValSpec<IntType>,
[ArgSpec<VoidPtr>, ArgSpec<SizeTType>, ArgSpec<IntType>]
>,
+ FunctionSpec<
+ "shm_open",
+ RetValSpec<IntType>,
+ [ArgSpec<ConstCharPtr>, ArgSpec<IntType>, ArgSpec<ModeTType>]
+ >,
+ FunctionSpec<
+ "shm_unlink",
+ RetValSpec<IntType>,
+ [ArgSpec<ConstCharPtr>]
+ >,
]
>;
diff --git a/libc/src/__support/CPP/string_view.h b/libc/src/__support/CPP/string_view.h
index d23aa261ca0614..8aa96fa87f6986 100644
--- a/libc/src/__support/CPP/string_view.h
+++ b/libc/src/__support/CPP/string_view.h
@@ -179,7 +179,8 @@ class string_view {
LIBC_INLINE char back() const { return Data[Len - 1]; }
// Finds the first occurence of c in this view, starting at position From.
- LIBC_INLINE size_t find_first_of(const char c, size_t From = 0) const {
+ LIBC_INLINE constexpr size_t find_first_of(const char c,
+ size_t From = 0) const {
for (size_t Pos = From; Pos < size(); ++Pos)
if ((*this)[Pos] == c)
return Pos;
@@ -187,13 +188,29 @@ class string_view {
}
// Finds the last occurence of c in this view, ending at position End.
- LIBC_INLINE size_t find_last_of(const char c, size_t End = npos) const {
+ LIBC_INLINE constexpr size_t find_last_of(const char c,
+ size_t End = npos) const {
End = End >= size() ? size() : End + 1;
for (; End > 0; --End)
if ((*this)[End - 1] == c)
return End - 1;
return npos;
}
+
+ // Finds the first character not equal to c in this view, starting at position
+ // From.
+ LIBC_INLINE constexpr size_t find_first_not_of(const char c,
+ size_t From = 0) const {
+ for (size_t Pos = From; Pos < size(); ++Pos)
+ if ((*this)[Pos] != c)
+ return Pos;
+ return npos;
+ }
+
+ // Check if this view contains the given character.
+ LIBC_INLINE constexpr bool contains(char c) const {
+ return find_first_of(c) != npos;
+ }
};
} // namespace cpp
diff --git a/libc/src/sys/mman/CMakeLists.txt b/libc/src/sys/mman/CMakeLists.txt
index b49f73873c2006..9c74202a09f035 100644
--- a/libc/src/sys/mman/CMakeLists.txt
+++ b/libc/src/sys/mman/CMakeLists.txt
@@ -85,3 +85,17 @@ add_entrypoint_object(
DEPENDS
.${LIBC_TARGET_OS}.msync
)
+
+add_entrypoint_object(
+ shm_open
+ ALIAS
+ DEPENDS
+ .${LIBC_TARGET_OS}.shm_open
+)
+
+add_entrypoint_object(
+ shm_unlink
+ ALIAS
+ DEPENDS
+ .${LIBC_TARGET_OS}.shm_unlink
+)
diff --git a/libc/src/sys/mman/linux/CMakeLists.txt b/libc/src/sys/mman/linux/CMakeLists.txt
index 04086ee5d33254..00f4f0e64ec06b 100644
--- a/libc/src/sys/mman/linux/CMakeLists.txt
+++ b/libc/src/sys/mman/linux/CMakeLists.txt
@@ -152,3 +152,40 @@ add_entrypoint_object(
libc.src.__support.OSUtil.osutil
libc.src.errno.errno
)
+
+add_header_library(
+ shm_common
+ HDRS
+ shm_common.h
+ DEPENDS
+ libc.src.__support.CPP.array
+ libc.src.__support.CPP.string_view
+ libc.src.__support.CPP.optional
+ libc.src.__support.common
+ libc.src.errno.errno
+ libc.src.string.memory_utils.inline_memcpy
+)
+
+add_entrypoint_object(
+ shm_open
+ SRCS
+ shm_open.cpp
+ HDRS
+ ../shm_open.h
+ DEPENDS
+ libc.src.fcntl.open
+ libc.include.llvm-libc-macros.fcntl_macros
+ libc.include.llvm-libc-types.mode_t
+ .shm_common
+)
+
+add_entrypoint_object(
+ shm_unlink
+ SRCS
+ shm_unlink.cpp
+ HDRS
+ ../shm_unlink.h
+ DEPENDS
+ libc.src.unistd.unlink
+ .shm_common
+)
diff --git a/libc/src/sys/mman/linux/shm_common.h b/libc/src/sys/mman/linux/shm_common.h
new file mode 100644
index 00000000000000..6f2a3fdc71b647
--- /dev/null
+++ b/libc/src/sys/mman/linux/shm_common.h
@@ -0,0 +1,53 @@
+//===---------- Shared implementations for shm_open/shm_unlink ------------===//
+//
+// 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 "src/__support/CPP/array.h"
+#include "src/__support/CPP/optional.h"
+#include "src/__support/CPP/string_view.h"
+#include "src/errno/libc_errno.h"
+#include "src/string/memory_utils/inline_memcpy.h"
+
+// TODO: Get PATH_MAX via https://github.com/llvm/llvm-project/issues/85121
+#include <linux/limits.h>
+
+namespace LIBC_NAMESPACE {
+
+namespace shm_common {
+
+LIBC_INLINE_VAR constexpr cpp::string_view SHM_PREFIX = "/dev/shm/";
+using SHMPath = cpp::array<char, NAME_MAX + SHM_PREFIX.size() + 1>;
+
+LIBC_INLINE cpp::optional<SHMPath> translate_name(cpp::string_view name) {
+ // trim leading slashes
+ size_t offset = name.find_first_not_of('/');
+ if (offset == cpp::string_view::npos) {
+ libc_errno = EINVAL;
+ return cpp::nullopt;
+ }
+ name = name.substr(offset);
+
+ // check the name
+ if (name.size() > NAME_MAX) {
+ libc_errno = ENAMETOOLONG;
+ return cpp::nullopt;
+ }
+ if (name == "." || name == ".." || name.contains('/')) {
+ libc_errno = EINVAL;
+ return cpp::nullopt;
+ }
+
+ // prepend the prefix
+ SHMPath buffer;
+ inline_memcpy(buffer.data(), SHM_PREFIX.data(), SHM_PREFIX.size());
+ inline_memcpy(buffer.data() + SHM_PREFIX.size(), name.data(), name.size());
+ buffer[SHM_PREFIX.size() + name.size()] = '\0';
+ return buffer;
+}
+} // namespace shm_common
+
+} // namespace LIBC_NAMESPACE
diff --git a/libc/src/sys/mman/linux/shm_open.cpp b/libc/src/sys/mman/linux/shm_open.cpp
new file mode 100644
index 00000000000000..0d39b8b4e53dbf
--- /dev/null
+++ b/libc/src/sys/mman/linux/shm_open.cpp
@@ -0,0 +1,25 @@
+//===---------- Linux implementation of the shm_open function -------------===//
+//
+// 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 "src/sys/mman/shm_open.h"
+#include "llvm-libc-macros/fcntl-macros.h"
+#include "src/fcntl/open.h"
+#include "src/sys/mman/linux/shm_common.h"
+
+namespace LIBC_NAMESPACE {
+
+static constexpr int DEFAULT_OFLAGS = O_NOFOLLOW | O_CLOEXEC | O_NONBLOCK;
+
+LLVM_LIBC_FUNCTION(int, shm_open, (const char *name, int oflags, mode_t mode)) {
+ using namespace shm_common;
+ if (cpp::optional<SHMPath> buffer = translate_name(name))
+ return open(buffer->data(), oflags | DEFAULT_OFLAGS, mode);
+ return -1;
+}
+
+} // namespace LIBC_NAMESPACE
diff --git a/libc/src/sys/mman/linux/shm_unlink.cpp b/libc/src/sys/mman/linux/shm_unlink.cpp
new file mode 100644
index 00000000000000..32f48d3e3e7187
--- /dev/null
+++ b/libc/src/sys/mman/linux/shm_unlink.cpp
@@ -0,0 +1,22 @@
+//===---------- Linux implementation of the shm_unlink function -----------===//
+//
+// 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 "src/sys/mman/shm_unlink.h"
+#include "src/sys/mman/linux/shm_common.h"
+#include "src/unistd/unlink.h"
+
+namespace LIBC_NAMESPACE {
+
+LLVM_LIBC_FUNCTION(int, shm_unlink, (const char *name)) {
+ using namespace shm_common;
+ if (cpp::optional<SHMPath> buffer = translate_name(name))
+ return unlink(buffer->data());
+ return -1;
+}
+
+} // namespace LIBC_NAMESPACE
diff --git a/libc/src/sys/mman/shm_open.h b/libc/src/sys/mman/shm_open.h
new file mode 100644
index 00000000000000..91796d7b5c0501
--- /dev/null
+++ b/libc/src/sys/mman/shm_open.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for shm_open function -------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_SYS_MMAN_SHM_OPEN_H
+#define LLVM_LIBC_SRC_SYS_MMAN_SHM_OPEN_H
+
+#include <llvm-libc-types/mode_t.h>
+
+namespace LIBC_NAMESPACE {
+
+int shm_open(const char *name, int oflag, mode_t mode);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_SYS_MMAN_SHM_OPEN_H
diff --git a/libc/src/sys/mman/shm_unlink.h b/libc/src/sys/mman/shm_unlink.h
new file mode 100644
index 00000000000000..c38c06adbaa292
--- /dev/null
+++ b/libc/src/sys/mman/shm_unlink.h
@@ -0,0 +1,18 @@
+//===-- Implementation header for shm_unlink function ------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_SYS_MMAN_SHM_UNLINK_H
+#define LLVM_LIBC_SRC_SYS_MMAN_SHM_UNLINK_H
+
+namespace LIBC_NAMESPACE {
+
+int shm_unlink(const char *name);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_SYS_MMAN_SHM_UNLINK_H
diff --git a/libc/test/src/__support/CPP/stringview_test.cpp b/libc/test/src/__support/CPP/stringview_test.cpp
index 33eb8ab4765728..6b68f2a1c47a9d 100644
--- a/libc/test/src/__support/CPP/stringview_test.cpp
+++ b/libc/test/src/__support/CPP/stringview_test.cpp
@@ -174,3 +174,111 @@ TEST(LlvmLibcStringViewTest, FindLastOf) {
ASSERT_EQ(Empty1.find_last_of('a', 0), string_view::npos);
ASSERT_EQ(Empty1.find_last_of('a', 123), string_view::npos);
}
+
+TEST(LlvmLibcStringViewTest, FindFirstNotOf) {
+ string_view Tmp("abada");
+
+ EXPECT_EQ(Tmp.find_first_not_of('a'), size_t(1));
+ EXPECT_EQ(Tmp.find_first_not_of('a', 123), string_view::npos);
+ EXPECT_EQ(Tmp.find_first_not_of('a', 5), string_view::npos);
+ EXPECT_EQ(Tmp.find_first_not_of('a', 4), string_view::npos);
+ EXPECT_EQ(Tmp.find_first_not_of('a', 3), size_t(3));
+ EXPECT_EQ(Tmp.find_first_not_of('a', 2), size_t(3));
+ EXPECT_EQ(Tmp.find_first_not_of('a', 1), size_t(1));
+ EXPECT_EQ(Tmp.find_first_not_of('a', 0), size_t(1));
+
+ EXPECT_EQ(Tmp.find_first_not_of('b'), size_t(0));
+ EXPECT_EQ(Tmp.find_first_not_of('b', 123), string_view::npos);
+ EXPECT_EQ(Tmp.find_first_not_of('b', 5), string_view::npos);
+ EXPECT_EQ(Tmp.find_first_not_of('b', 4), size_t(4));
+ EXPECT_EQ(Tmp.find_first_not_of('b', 3), size_t(3));
+ EXPECT_EQ(Tmp.find_first_not_of('b', 2), size_t(2));
+ EXPECT_EQ(Tmp.find_first_not_of('b', 1), size_t(2));
+ EXPECT_EQ(Tmp.find_first_not_of('b', 0), size_t(0));
+
+ EXPECT_EQ(Tmp.find_first_not_of('d'), size_t(0));
+ EXPECT_EQ(Tmp.find_first_not_of('d', 123), string_view::npos);
+ EXPECT_EQ(Tmp.find_first_not_of('d', 5), string_view::npos);
+ EXPECT_EQ(Tmp.find_first_not_of('d', 4), size_t(4));
+ EXPECT_EQ(Tmp.find_first_not_of('d', 3), size_t(4));
+ EXPECT_EQ(Tmp.find_first_not_of('d', 2), size_t(2));
+ EXPECT_EQ(Tmp.find_first_not_of('d', 1), size_t(1));
+ EXPECT_EQ(Tmp.find_first_not_of('d', 0), size_t(0));
+
+ EXPECT_EQ(Tmp.find_first_not_of('e'), size_t(0));
+ EXPECT_EQ(Tmp.find_first_not_of('e', 123), string_view::npos);
+ EXPECT_EQ(Tmp.find_first_not_of('e', 5), string_view::npos);
+ EXPECT_EQ(Tmp.find_first_not_of('e', 4), size_t(4));
+ EXPECT_EQ(Tmp.find_first_not_of('e', 3), size_t(3));
+ EXPECT_EQ(Tmp.find_first_not_of('e', 2), size_t(2));
+ EXPECT_EQ(Tmp.find_first_not_of('e', 1), size_t(1));
+ EXPECT_EQ(Tmp.find_first_not_of('e', 0), size_t(0));
+
+ string_view Empty;
+ EXPECT_EQ(Empty.find_first_not_of('a'), string_view::npos);
+ EXPECT_EQ(Empty.find_first_not_of('a', 0), string_view::npos);
+ EXPECT_EQ(Empty.find_first_not_of('a', 123), string_view::npos);
+
+ string_view Empty1("");
+ EXPECT_EQ(Empty1.find_first_not_of('a'), string_view::npos);
+ EXPECT_EQ(Empty1.find_first_not_of('a', 0), string_view::npos);
+ EXPECT_EQ(Empty1.find_first_not_of('a', 123), string_view::npos);
+
+ string_view Full("aaaaaaa");
+ EXPECT_EQ(Full.find_first_not_of('a'), string_view::npos);
+ EXPECT_EQ(Full.find_first_not_of('a', 0), string_view::npos);
+ EXPECT_EQ(Full.find_first_not_of('a', 123), string_view::npos);
+
+ EXPECT_EQ(Full.find_first_not_of('b'), size_t(0));
+ EXPECT_EQ(Full.find_first_not_of('b', 0), size_t(0));
+ EXPECT_EQ(Full.find_first_not_of('b', 123), string_view::npos);
+}
+
+TEST(LlvmLibcStringViewTest, Contains) {
+ string_view Empty;
+ for (char c = 'a'; c < 'z'; ++c)
+ EXPECT_FALSE(Empty.contains(c));
+
+ string_view Tmp("abada");
+ EXPECT_TRUE(Tmp.contains('a'));
+ EXPECT_TRUE(Tmp.contains('b'));
+ EXPECT_FALSE(Tmp.contains('c'));
+ EXPECT_TRUE(Tmp.contains('d'));
+ EXPECT_FALSE(Tmp.contains('e'));
+
+ EXPECT_TRUE(Tmp.substr(1).contains('a'));
+ EXPECT_TRUE(Tmp.substr(1).contains('b'));
+ EXPECT_FALSE(Tmp.substr(1).contains('c'));
+ EXPECT_TRUE(Tmp.substr(1).contains('d'));
+ EXPECT_FALSE(Tmp.substr(1).contains('e'));
+
+ EXPECT_TRUE(Tmp.substr(2).contains('a'));
+ EXPECT_FALSE(Tmp.substr(2).contains('b'));
+ EXPECT_FALSE(Tmp.substr(2).contains('c'));
+ EXPECT_TRUE(Tmp.substr(2).contains('d'));
+ EXPECT_FALSE(Tmp.substr(2).contains('e'));
+
+ EXPECT_TRUE(Tmp.substr(3).contains('a'));
+ EXPECT_FALSE(Tmp.substr(3).contains('b'));
+ EXPECT_FALSE(Tmp.substr(3).contains('c'));
+ EXPECT_TRUE(Tmp.substr(3).contains('d'));
+ EXPECT_FALSE(Tmp.substr(3).contains('e'));
+
+ EXPECT_TRUE(Tmp.substr(4).contains('a'));
+ EXPECT_FALSE(Tmp.substr(4).contains('b'));
+ EXPECT_FALSE(Tmp.substr(4).contains('c'));
+ EXPECT_FALSE(Tmp.substr(4).contains('d'));
+ EXPECT_FALSE(Tmp.substr(4).contains('e'));
+
+ EXPECT_FALSE(Tmp.substr(5).contains('a'));
+ EXPECT_FALSE(Tmp.substr(5).contains('b'));
+ EXPECT_FALSE(Tmp.substr(5).contains('c'));
+ EXPECT_FALSE(Tmp.substr(5).contains('d'));
+ EXPECT_FALSE(Tmp.substr(5).contains('e'));
+
+ EXPECT_FALSE(Tmp.substr(6).contains('a'));
+ EXPECT_FALSE(Tmp.substr(6).contains('b'));
+ EXPECT_FALSE(Tmp.substr(6).contains('c'));
+ EXPECT_FALSE(Tmp.substr(6).contains('d'));
+ EXPECT_FALSE(Tmp.substr(6).contains('e'));
+}
diff --git a/libc/test/src/sys/mman/linux/CMakeLists.txt b/libc/test/src/sys/mman/linux/CMakeLists.txt
index 6f7fc34d8d3ce3..0762a2d846a8e0 100644
--- a/libc/test/src/sys/mman/linux/CMakeLists.txt
+++ b/libc/test/src/sys/mman/linux/CMakeLists.txt
@@ -127,3 +127,23 @@ add_libc_unittest(
libc.src.unistd.sysconf
libc.test.UnitTest.ErrnoSetterMatcher
)
+
+add_libc_unittest(
+ shm_test
+ SUITE
+ libc_sys_mman_unittests
+ SRCS
+ shm_test.cpp
+ DEPENDS
+ libc.include.sys_mman
+ libc.include.sys_syscall
+ libc.src.errno.errno
+ libc.src.sys.mman.shm_open
+ libc.src.sys.mman.shm_unlink
+ libc.src.sys.mman.mmap
+ libc.src.sys.mman.munmap
+ libc.src.unistd.ftruncate
+ libc.src.unistd.close
+ libc.src.__support.OSUtil.osutil
+ libc.test.UnitTest.ErrnoSetterMatcher
+)
diff --git a/libc/test/src/sys/mman/linux/shm_test.cpp b/libc/test/src/sys/mman/linux/shm_test.cpp
new file mode 100644
index 00000000000000..3b1a2aa33b56ad
--- /dev/null
+++ b/libc/test/src/sys/mman/linux/shm_test.cpp
@@ -0,0 +1,77 @@
+//===-- Unittests for shm_open/shm_unlink ---------------------------------===//
+//
+// 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 "src/__support/OSUtil/syscall.h"
+#include "src/sys/mman/mmap.h"
+#include "src/sys/mman/munmap.h"
+#include "src/sys/mman/shm_open.h"
+#include "src/sys/mman/shm_unlink.h"
+#include "src/unistd/close.h"
+#include "src/unistd/ftruncate.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
+#include "test/UnitTest/LibcTest.h"
+#include <asm-generic/fcntl.h>
+#include <sys/syscall.h>
+
+using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;
+// since shm_open/shm_unlink are wrappers around open/unlink, we only focus on
+// testing basic cases and name conversions.
+
+TEST(LlvmLibcShmTest, Basic) {
+ const char *name = "/test_shm_open";
+ int fd;
+ ASSERT_THAT(fd = LIBC_NAMESPACE::shm_open(name, O_CREAT | O_RDWR, 0666),
+ returns(GE(0)).with_errno(EQ(0)));
+
+ // check that FD_CLOEXEC is set by default.
+ // TODO: use fcntl when implemented.
+ // https://github.com/llvm/llvm-project/issues/84968
+ long flag = LIBC_NAMESPACE::syscall_impl(SYS_fcntl, fd, F_GETFD);
+ ASSERT_GE(static_cast<int>(flag), 0);
+ EXPECT_NE(static_cast<int>(flag) & FD_CLOEXEC, 0);
+
+ // allocate space using ftruncate
+ ASSERT_THAT(LIBC_NAMESPACE::ftruncate(fd, 4096), Succeeds());
+ // map the shared memory
+ void *addr = LIBC_NAMESPACE::mmap(nullptr, 4096, PROT_READ | PROT_WRITE,
+ MAP_SHARED, fd, 0);
+ ASSERT_NE(addr, MAP_FAILED);
+ // just write random data to the shared memory
+ char data[] = "Despite its name, LLVM has little to do with traditional "
+ "virtual machines.";
+ for (size_t i = 0; i < sizeof(data); ++i)
+ static_cast<char *>(addr)[i] = data[i];
+
+ // close fd does not affect the mapping
+ ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds());
+ for (size_t i = 0; i < sizeof(data); ++i)
+ EXPECT_EQ(static_cast<char *>(addr)[i], data[i]);
+
+ // unmap the shared memory
+ ASSERT_THAT(LIBC_NAMESPACE::munmap(addr, 4096), Succeeds());
+ // remove the shared memory
+ ASSERT_THAT(LIBC_NAMESPACE::shm_unlink(name), Succeeds());
+}
+
+TEST(LlvmLibcShmTest, NameConversion) {
+ const char *name = "////test_shm_open";
+ int fd;
+ ASSERT_THAT(fd = LIBC_NAMESPACE::shm_open(name, O_CREAT | O_RDWR, 0666),
+ returns(GE(0)).with_errno(EQ(0)));
+ ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds());
+ ASSERT_THAT(LIBC_NAMESPACE::shm_unlink(name), Succeeds());
+
+ ASSERT_THAT(LIBC_NAMESPACE::shm_open("/123/123", O_CREAT | O_RDWR, 0666),
+ Fails(EINVAL));
+
+ ASSERT_THAT(LIBC_NAMESPACE::shm_open("/.", O_CREAT | O_RDWR, 0666),
+ Fails(EINVAL));
+
+ ASSERT_THAT(LIBC_NAMESPACE::shm_open("/..", O_CREAT | O_RDWR, 0666),
+ Fails(EINVAL));
+}
>From 57914f647e2551ea19758038345bb8bc2c4762c1 Mon Sep 17 00:00:00 2001
From: Mike Rice <michael.p.rice at intel.com>
Date: Mon, 18 Mar 2024 08:44:55 -0700
Subject: [PATCH 25/40] [clang-tidy][NFC] Remove unnecessary nullptr check on
cast subexpr (#85473)
The value of SubExpr is not null since getSubExpr would assert in that
case. Remove the nullptr check. This avoids confusion since SubExpr is
used without check later in the function.
---
.../clang-tidy/readability/ImplicitBoolConversionCheck.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/readability/ImplicitBoolConversionCheck.cpp b/clang-tools-extra/clang-tidy/readability/ImplicitBoolConversionCheck.cpp
index 4f02950e7794cb..74152c6034510b 100644
--- a/clang-tools-extra/clang-tidy/readability/ImplicitBoolConversionCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/ImplicitBoolConversionCheck.cpp
@@ -81,8 +81,7 @@ void fixGenericExprCastToBool(DiagnosticBuilder &Diag,
const Expr *SubExpr = Cast->getSubExpr();
- bool NeedInnerParens =
- SubExpr != nullptr && utils::fixit::areParensNeededForStatement(*SubExpr);
+ bool NeedInnerParens = utils::fixit::areParensNeededForStatement(*SubExpr);
bool NeedOuterParens =
Parent != nullptr && utils::fixit::areParensNeededForStatement(*Parent);
>From 12b802ac0bc6ddf0742aa3fe8caecd8204d70ca5 Mon Sep 17 00:00:00 2001
From: Congcong Cai <congcongcai0907 at 163.com>
Date: Mon, 18 Mar 2024 23:56:36 +0800
Subject: [PATCH 26/40] [clang-tidy]bugprone-unused-return-value ignore `++`
and `--` operator overloading (#84922)
Fixes: #84705
Further fix for #84489
---
.../bugprone/UnusedReturnValueCheck.cpp | 41 +++++++++--------
.../unused-return-value-avoid-assignment.cpp | 45 ++++++++++++++-----
2 files changed, 57 insertions(+), 29 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/bugprone/UnusedReturnValueCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UnusedReturnValueCheck.cpp
index 243fe47c2036b6..73373147e96fc9 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UnusedReturnValueCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/UnusedReturnValueCheck.cpp
@@ -31,9 +31,16 @@ AST_MATCHER_P(FunctionDecl, isInstantiatedFrom, Matcher<FunctionDecl>,
Finder, Builder);
}
-AST_MATCHER_P(CXXMethodDecl, isOperatorOverloading,
- llvm::SmallVector<OverloadedOperatorKind>, Kinds) {
- return llvm::is_contained(Kinds, Node.getOverloadedOperator());
+constexpr std::initializer_list<OverloadedOperatorKind>
+ AssignmentOverloadedOperatorKinds = {
+ OO_Equal, OO_PlusEqual, OO_MinusEqual, OO_StarEqual,
+ OO_SlashEqual, OO_PercentEqual, OO_CaretEqual, OO_AmpEqual,
+ OO_PipeEqual, OO_LessLessEqual, OO_GreaterGreaterEqual, OO_PlusPlus,
+ OO_MinusMinus};
+
+AST_MATCHER(FunctionDecl, isAssignmentOverloadedOperator) {
+ return llvm::is_contained(AssignmentOverloadedOperatorKinds,
+ Node.getOverloadedOperator());
}
} // namespace
@@ -164,22 +171,18 @@ void UnusedReturnValueCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
}
void UnusedReturnValueCheck::registerMatchers(MatchFinder *Finder) {
- auto MatchedDirectCallExpr = expr(
- callExpr(
- callee(functionDecl(
- // Don't match void overloads of checked functions.
- unless(returns(voidType())),
- // Don't match copy or move assignment operator.
- unless(cxxMethodDecl(isOperatorOverloading(
- {OO_Equal, OO_PlusEqual, OO_MinusEqual, OO_StarEqual,
- OO_SlashEqual, OO_PercentEqual, OO_CaretEqual, OO_AmpEqual,
- OO_PipeEqual, OO_LessLessEqual, OO_GreaterGreaterEqual}))),
- anyOf(
- isInstantiatedFrom(
- matchers::matchesAnyListedName(CheckedFunctions)),
- returns(hasCanonicalType(hasDeclaration(namedDecl(
- matchers::matchesAnyListedName(CheckedReturnTypes)))))))))
- .bind("match"));
+ auto MatchedDirectCallExpr =
+ expr(callExpr(callee(functionDecl(
+ // Don't match copy or move assignment operator.
+ unless(isAssignmentOverloadedOperator()),
+ // Don't match void overloads of checked functions.
+ unless(returns(voidType())),
+ anyOf(isInstantiatedFrom(matchers::matchesAnyListedName(
+ CheckedFunctions)),
+ returns(hasCanonicalType(hasDeclaration(
+ namedDecl(matchers::matchesAnyListedName(
+ CheckedReturnTypes)))))))))
+ .bind("match"));
auto CheckCastToVoid =
AllowCastToVoid ? castExpr(unless(hasCastKind(CK_ToVoid))) : castExpr();
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/unused-return-value-avoid-assignment.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unused-return-value-avoid-assignment.cpp
index b4a41004adf894..564c07a724ccde 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/unused-return-value-avoid-assignment.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unused-return-value-avoid-assignment.cpp
@@ -3,23 +3,37 @@
// RUN: {bugprone-unused-return-value.CheckedFunctions: "::*"}}' \
// RUN: --
-struct S {
- S(){};
- S(S const &);
- S(S &&);
- S &operator=(S const &);
- S &operator=(S &&);
- S &operator+=(S);
+struct S1 {
+ S1(){};
+ S1(S1 const &);
+ S1(S1 &&);
+ S1 &operator=(S1 const &);
+ S1 &operator=(S1 &&);
+ S1 &operator+=(S1);
+ S1 &operator++();
+ S1 &operator++(int);
+ S1 &operator--();
+ S1 &operator--(int);
};
-S returnValue();
-S const &returnRef();
+struct S2 {
+ S2(){};
+ S2(S2 const &);
+ S2(S2 &&);
+};
+
+S2 &operator-=(S2&, int);
+S2 &operator++(S2 &);
+S2 &operator++(S2 &, int);
+
+S1 returnValue();
+S1 const &returnRef();
void bar() {
returnValue();
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should not be disregarded; neglecting it may lead to errors
- S a{};
+ S1 a{};
a = returnValue();
a.operator=(returnValue());
@@ -27,4 +41,15 @@ void bar() {
a.operator=(returnRef());
a += returnRef();
+
+ a++;
+ ++a;
+ a--;
+ --a;
+
+ S2 b{};
+
+ b -= 1;
+ b++;
+ ++b;
}
>From 39c739eec9148a2f4e04c7fa9ca11f23db79bb15 Mon Sep 17 00:00:00 2001
From: Andrei Golubev <andrey.golubev at intel.com>
Date: Mon, 18 Mar 2024 18:05:05 +0200
Subject: [PATCH 27/40] [mlir][OpInterfacesGen][NFC] Add newline after traits
declaration (#85633)
Slightly improve the readability of the tablegen-generated code.
Co-authored-by: Orest Chura <orest.chura at intel.com>
---
mlir/tools/mlir-tblgen/OpInterfacesGen.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/mlir/tools/mlir-tblgen/OpInterfacesGen.cpp b/mlir/tools/mlir-tblgen/OpInterfacesGen.cpp
index 9672a02cc08f68..2a7406f42f34b5 100644
--- a/mlir/tools/mlir-tblgen/OpInterfacesGen.cpp
+++ b/mlir/tools/mlir-tblgen/OpInterfacesGen.cpp
@@ -533,7 +533,7 @@ void InterfaceGenerator::emitInterfaceDecl(const Interface &interface) {
<< "struct " << interfaceTraitsName << " {\n";
emitConceptDecl(interface);
emitModelDecl(interface);
- os << "};";
+ os << "};\n";
// Emit the derived trait for the interface.
os << "template <typename " << valueTemplate << ">\n";
>From f8042171552ca16e77a5e9367188e7f218474380 Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Mon, 18 Mar 2024 16:53:37 +0100
Subject: [PATCH 28/40] Revert "[libc++][CMake] Removes
LIBCXX_ENABLE_CLANG_TIDY. (#85262)"
This reverts commit 4109b18ee5de1346c2b89a5c89b86bae5c8631d3.
It looks like the automatic detection has false positives. This broke
the following build https://github.com/llvm/llvm-project/pull/85262
---
libcxx/CMakeLists.txt | 5 +++++
libcxx/docs/ReleaseNotes/19.rst | 4 +---
libcxx/test/tools/CMakeLists.txt | 12 ++++++++----
.../test/tools/clang_tidy_checks/CMakeLists.txt | 17 +++++------------
libcxx/utils/ci/buildkite-pipeline.yml | 1 +
libcxx/utils/ci/run-buildbot | 8 ++++++++
6 files changed, 28 insertions(+), 19 deletions(-)
diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index 043d5a8295c1a6..e565c47c76687a 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -123,6 +123,7 @@ option(LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS
to provide compile-time errors when using features unavailable on some version of
the shared library they shipped should turn this on and see `include/__availability`
for more details." OFF)
+option(LIBCXX_ENABLE_CLANG_TIDY "Whether to compile and run clang-tidy checks" OFF)
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(LIBCXX_DEFAULT_TEST_CONFIG "llvm-libc++-shared-gcc.cfg.in")
@@ -862,6 +863,10 @@ add_subdirectory(modules)
set(LIBCXX_TEST_DEPS "cxx_experimental")
+if (LIBCXX_ENABLE_CLANG_TIDY)
+ list(APPEND LIBCXX_TEST_DEPS cxx-tidy)
+endif()
+
list(APPEND LIBCXX_TEST_DEPS generate-cxx-modules)
if (LIBCXX_INCLUDE_BENCHMARKS)
diff --git a/libcxx/docs/ReleaseNotes/19.rst b/libcxx/docs/ReleaseNotes/19.rst
index 1a46789da58443..c70ae477fafc1d 100644
--- a/libcxx/docs/ReleaseNotes/19.rst
+++ b/libcxx/docs/ReleaseNotes/19.rst
@@ -82,6 +82,7 @@ Deprecations and Removals
libatomic is not available. If you are one such user, please reach out to the libc++ developers so we can collaborate
on a path for supporting atomics properly on freestanding platforms.
+
Upcoming Deprecations and Removals
----------------------------------
@@ -106,6 +107,3 @@ Build System Changes
- The ``LIBCXX_EXECUTOR`` and ``LIBCXXABI_EXECUTOR`` CMake variables have been removed. Please
set ``LIBCXX_TEST_PARAMS`` to ``executor=<...>`` instead.
-
-- The Cmake variable ``LIBCXX_ENABLE_CLANG_TIDY`` has been removed. The build system has been changed
- to automatically detect the presence of ``clang-tidy`` and the required ``Clang`` libraries.
diff --git a/libcxx/test/tools/CMakeLists.txt b/libcxx/test/tools/CMakeLists.txt
index 6d99c53ad46d9f..e30ad6cdd8201f 100644
--- a/libcxx/test/tools/CMakeLists.txt
+++ b/libcxx/test/tools/CMakeLists.txt
@@ -1,8 +1,12 @@
set(LIBCXX_TEST_TOOLS_PATH ${CMAKE_CURRENT_BINARY_DIR} PARENT_SCOPE)
-if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
- message(STATUS "Clang-tidy tests are disabled due to non-clang based compiler.")
- return()
+# TODO: Remove LIBCXX_ENABLE_CLANG_TIDY
+if(LIBCXX_ENABLE_CLANG_TIDY)
+ if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
+ message(STATUS "Clang-tidy can only be used when building libc++ with "
+ "a clang compiler.")
+ return()
+ endif()
+ add_subdirectory(clang_tidy_checks)
endif()
-add_subdirectory(clang_tidy_checks)
diff --git a/libcxx/test/tools/clang_tidy_checks/CMakeLists.txt b/libcxx/test/tools/clang_tidy_checks/CMakeLists.txt
index 2e5d8aa2fce74b..74905a0c3ed1c4 100644
--- a/libcxx/test/tools/clang_tidy_checks/CMakeLists.txt
+++ b/libcxx/test/tools/clang_tidy_checks/CMakeLists.txt
@@ -9,17 +9,6 @@ set(Clang_DIR_SAVE ${Clang_DIR})
# versions must match. Otherwise there likely will be ODR-violations. This had
# led to crashes and incorrect output of the clang-tidy based checks.
find_package(Clang ${CMAKE_CXX_COMPILER_VERSION})
-if(NOT Clang_FOUND)
- message(STATUS "Clang-tidy tests are disabled since the "
- "Clang development package is unavailable.")
- return()
-endif()
-if(NOT TARGET clangTidy)
- message(STATUS "Clang-tidy tests are disabled since the "
- "Clang development package has no clangTidy target.")
- return()
-endif()
-message(STATUS "Clang-tidy tests are enabled.")
set(SOURCES
abi_tag_on_virtual.cpp
@@ -33,7 +22,11 @@ set(SOURCES
libcpp_module.cpp
)
-list(APPEND LIBCXX_TEST_DEPS cxx-tidy)
+if(NOT Clang_FOUND)
+ message(STATUS "Could not find a suitable version of the Clang development package;
+ custom libc++ clang-tidy checks will not be available.")
+ return()
+endif()
set(LLVM_DIR "${LLVM_DIR_SAVE}" CACHE PATH "The directory containing a CMake configuration file for LLVM." FORCE)
set(Clang_DIR "${Clang_DIR_SAVE}" CACHE PATH "The directory containing a CMake configuration file for Clang." FORCE)
diff --git a/libcxx/utils/ci/buildkite-pipeline.yml b/libcxx/utils/ci/buildkite-pipeline.yml
index 0761b40c784d42..e42262620d5fb0 100644
--- a/libcxx/utils/ci/buildkite-pipeline.yml
+++ b/libcxx/utils/ci/buildkite-pipeline.yml
@@ -43,6 +43,7 @@ definitions:
environment_definitions:
_common_env: &common_env
+ ENABLE_CLANG_TIDY: "On"
LLVM_SYMBOLIZER_PATH: "/usr/bin/llvm-symbolizer-${LLVM_HEAD_VERSION}"
CLANG_CRASH_DIAGNOSTICS_DIR: "crash_diagnostics"
CC: clang-${LLVM_HEAD_VERSION}
diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot
index 10b0ed607bce79..2905745355b68e 100755
--- a/libcxx/utils/ci/run-buildbot
+++ b/libcxx/utils/ci/run-buildbot
@@ -44,6 +44,9 @@ CMAKE The CMake binary to use. This variable is optional.
CLANG_FORMAT The clang-format binary to use when generating the format
ignore list.
+ENABLE_CLANG_TIDY Whether to compile and run clang-tidy checks. This variable
+ is optional.
+
EOF
}
@@ -108,6 +111,10 @@ function clean() {
rm -rf "${BUILD_DIR}"
}
+if [ -z "${ENABLE_CLANG_TIDY}" ]; then
+ ENABLE_CLANG_TIDY=Off
+fi
+
function generate-cmake-base() {
echo "--- Generating CMake"
${CMAKE} \
@@ -119,6 +126,7 @@ function generate-cmake-base() {
-DLIBCXX_ENABLE_WERROR=YES \
-DLIBCXXABI_ENABLE_WERROR=YES \
-DLIBUNWIND_ENABLE_WERROR=YES \
+ -DLIBCXX_ENABLE_CLANG_TIDY=${ENABLE_CLANG_TIDY} \
-DLLVM_LIT_ARGS="-sv --xunit-xml-output test-results.xml --timeout=1500 --time-tests" \
"${@}"
}
>From ca04b56a8b26cbe9327eaab113e1e4af096d902a Mon Sep 17 00:00:00 2001
From: Shourya Goel <shouryagoel10000 at gmail.com>
Date: Mon, 18 Mar 2024 21:56:30 +0530
Subject: [PATCH 29/40] [libc] Implement fileno (#85628)
fixes: #85150
---
libc/config/linux/x86_64/entrypoints.txt | 1 +
libc/src/stdio/CMakeLists.txt | 10 ++++++++++
libc/src/stdio/fileno.h | 21 +++++++++++++++++++++
libc/src/stdio/generic/CMakeLists.txt | 12 ++++++++++++
libc/src/stdio/generic/fileno.cpp | 21 +++++++++++++++++++++
libc/test/src/stdio/fileop_test.cpp | 1 +
6 files changed, 66 insertions(+)
create mode 100644 libc/src/stdio/fileno.h
create mode 100644 libc/src/stdio/generic/fileno.cpp
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index ee02488ca7cd7c..f81d334e9e788d 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -208,6 +208,7 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.stdio.sscanf
libc.src.stdio.scanf
libc.src.stdio.fscanf
+ libc.src.stdio.fileno
# sys/epoll.h entrypoints
libc.src.sys.epoll.epoll_wait
diff --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt
index bb8e41606c5dfb..ece93fd56ef0c5 100644
--- a/libc/src/stdio/CMakeLists.txt
+++ b/libc/src/stdio/CMakeLists.txt
@@ -236,6 +236,16 @@ add_entrypoint_object(
libc.src.stdio.printf_core.vfprintf_internal
)
+add_stdio_entrypoint_object(
+ fileno
+ SRCS
+ fileno.cpp
+ HDRS
+ fileno.h
+ DEPENDS
+ libc.src.stdio.fileno
+)
+
add_subdirectory(printf_core)
add_subdirectory(scanf_core)
diff --git a/libc/src/stdio/fileno.h b/libc/src/stdio/fileno.h
new file mode 100644
index 00000000000000..d41f112226c512
--- /dev/null
+++ b/libc/src/stdio/fileno.h
@@ -0,0 +1,21 @@
+//===-- Implementation header of fileno --------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_STDIO_FILENO_H
+#define LLVM_LIBC_SRC_STDIO_FILENO_H
+
+#include "include/llvm-libc-types/FILE.h"
+
+namespace LIBC_NAMESPACE {
+
+int fileno(::FILE *f);
+
+} // namespace LIBC_NAMESPACE
+
+#endif // LLVM_LIBC_SRC_STDIO_FILENO_H
diff --git a/libc/src/stdio/generic/CMakeLists.txt b/libc/src/stdio/generic/CMakeLists.txt
index 4e4a709e94061b..0aa213caba7b8a 100644
--- a/libc/src/stdio/generic/CMakeLists.txt
+++ b/libc/src/stdio/generic/CMakeLists.txt
@@ -70,6 +70,18 @@ add_entrypoint_object(
libc.src.__support.File.platform_file
)
+add_entrypoint_object(
+ fileno
+ SRCS
+ fileno.cpp
+ HDRS
+ ../fileno.h
+ DEPENDS
+ libc.include.stdio
+ libc.src.__support.File.file
+ libc.src.__support.File.platform_file
+)
+
add_entrypoint_object(
fflush
SRCS
diff --git a/libc/src/stdio/generic/fileno.cpp b/libc/src/stdio/generic/fileno.cpp
new file mode 100644
index 00000000000000..663ba926637622
--- /dev/null
+++ b/libc/src/stdio/generic/fileno.cpp
@@ -0,0 +1,21 @@
+//===-- Implementation of fileno
+//-------------------------------------------===//
+//
+// 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 "src/stdio/fileno.h"
+
+#include "include/llvm-libc-types/FILE.h"
+#include "src/__support/File/file.h"
+
+namespace LIBC_NAMESPACE {
+
+LLVM_LIBC_FUNCTION(int, fileno, (::FILE * stream)) {
+ return get_fileno(reinterpret_cast<LIBC_NAMESPACE::File *>(stream));
+}
+
+} // namespace LIBC_NAMESPACE
diff --git a/libc/test/src/stdio/fileop_test.cpp b/libc/test/src/stdio/fileop_test.cpp
index f5dbc49818390d..2f2e63eaf75d4c 100644
--- a/libc/test/src/stdio/fileop_test.cpp
+++ b/libc/test/src/stdio/fileop_test.cpp
@@ -30,6 +30,7 @@ TEST(LlvmLibcFILETest, SimpleFileOperations) {
constexpr char FILENAME[] = "testdata/simple_operations.test";
::FILE *file = LIBC_NAMESPACE::fopen(FILENAME, "w");
ASSERT_FALSE(file == nullptr);
+ ASSERT_EQ(LIBC_NAMESPACE::fileno(file), 3);
constexpr char CONTENT[] = "1234567890987654321";
ASSERT_EQ(sizeof(CONTENT) - 1,
LIBC_NAMESPACE::fwrite(CONTENT, 1, sizeof(CONTENT) - 1, file));
>From 8d142043e8c4c1144cd94f39f3cc7c88da5cec3f Mon Sep 17 00:00:00 2001
From: "Balaji V. Iyer" <43187390+bviyer at users.noreply.github.com>
Date: Mon, 18 Mar 2024 11:27:58 -0500
Subject: [PATCH 30/40] Revert "[MLIR][XeGPU] Adding XeGPU 2d block operators
(#84692)" (#85653)
This reverts commit daebe5c4f27ba140ac8d13abf41e3fe4db72b91a.
This commit causes the following asan issue:
```
<snip>/llvm-project/build/bin/mlir-opt <snip>/llvm-project/mlir/test/Dialect/XeGPU/XeGPUOps.mlir | <snip>/llvm-project/build/bin/FileCheck <snip>/llvm-project/mlir/test/Dialect/XeGPU/XeGPUOps.mlir
# executed command: <snip>/llvm-project/build/bin/mlir-opt <snip>/llvm-project/mlir/test/Dialect/XeGPU/XeGPUOps.mlir
# .---command stderr------------
# | =================================================================
# | ==2772558==ERROR: AddressSanitizer: stack-use-after-return on address 0x7fd2c2c42b90 at pc 0x55e406d54614 bp 0x7ffc810e4070 sp 0x7ffc810e4068
# | READ of size 8 at 0x7fd2c2c42b90 thread T0
# | #0 0x55e406d54613 in operator()<long int const*> /usr/include/c++/13/bits/predefined_ops.h:318
# | #1 0x55e406d54613 in __count_if<long int const*, __gnu_cxx::__ops::_Iter_pred<mlir::verifyListOfOperandsOrIntegers(Operation*, llvm::StringRef, unsigned int, llvm::ArrayRef<long int>, ValueRange)::<lambda(int64_t)> > > /usr/include/c++/13/bits/stl_algobase.h:2125
# | #2 0x55e406d54613 in count_if<long int const*, mlir::verifyListOfOperandsOrIntegers(Operation*,
...
```
---
mlir/include/mlir/Dialect/XeGPU/IR/XeGPU.h | 7 +-
.../mlir/Dialect/XeGPU/IR/XeGPUAttrs.td | 61 ----
.../mlir/Dialect/XeGPU/IR/XeGPUDialect.td | 4 +-
.../include/mlir/Dialect/XeGPU/IR/XeGPUOps.td | 291 +-----------------
.../mlir/Dialect/XeGPU/IR/XeGPUTypes.td | 104 +------
mlir/lib/Dialect/XeGPU/IR/XeGPUDialect.cpp | 73 +----
mlir/lib/Dialect/XeGPU/IR/XeGPUOps.cpp | 177 +----------
mlir/test/Dialect/XeGPU/XeGPUOps.mlir | 62 ----
8 files changed, 12 insertions(+), 767 deletions(-)
delete mode 100644 mlir/test/Dialect/XeGPU/XeGPUOps.mlir
diff --git a/mlir/include/mlir/Dialect/XeGPU/IR/XeGPU.h b/mlir/include/mlir/Dialect/XeGPU/IR/XeGPU.h
index 87aabdc015fea5..7aaa4ecc7ee77a 100644
--- a/mlir/include/mlir/Dialect/XeGPU/IR/XeGPU.h
+++ b/mlir/include/mlir/Dialect/XeGPU/IR/XeGPU.h
@@ -9,12 +9,7 @@
#ifndef MLIR_DIALECT_XEGPU_IR_XEGPU_H
#define MLIR_DIALECT_XEGPU_IR_XEGPU_H
-#include "mlir/Bytecode/BytecodeOpInterface.h"
-#include "mlir/IR/BuiltinTypes.h"
-#include "mlir/IR/Dialect.h"
-#include "mlir/Interfaces/ShapedOpInterfaces.h"
-#include "mlir/Interfaces/SideEffectInterfaces.h"
-#include "mlir/Interfaces/ViewLikeInterface.h"
+#include <mlir/IR/Dialect.h>
namespace mlir {
namespace xegpu {
diff --git a/mlir/include/mlir/Dialect/XeGPU/IR/XeGPUAttrs.td b/mlir/include/mlir/Dialect/XeGPU/IR/XeGPUAttrs.td
index cd38549f1ccf43..bb325c272e3324 100644
--- a/mlir/include/mlir/Dialect/XeGPU/IR/XeGPUAttrs.td
+++ b/mlir/include/mlir/Dialect/XeGPU/IR/XeGPUAttrs.td
@@ -10,7 +10,6 @@
#define MLIR_DIALECT_XEGPU_IR_XEGPUATTRS_TD
include "mlir/Dialect/XeGPU/IR/XeGPUDialect.td"
-include "mlir/IR/EnumAttr.td"
class XeGPUAttr<string name, string attrMnemonic, list<Trait> traits = [],
string baseCppClass = "::mlir::Attribute">
@@ -18,64 +17,4 @@ class XeGPUAttr<string name, string attrMnemonic, list<Trait> traits = [],
let mnemonic = attrMnemonic;
}
-def XeGPU_TensorDescAttr: XeGPUAttr<"TensorDesc", "tdesc_attr"> {
- let parameters = (ins
- OptionalParameter<"MemoryScopeAttr">: $memory_scope,
- OptionalParameter<"IntegerAttr", "1">: $array_length,
- OptionalParameter<"BoolAttr", "true">: $boundary_check
- );
-
- let builders = [
- AttrBuilder<(ins
- CArg<"xegpu::MemoryScope", "xegpu::MemoryScope::Global">:$memory_scope,
- CArg<"int", "1">:$array_length,
- CArg<"bool", "true">: $boundary_check
- )>
- ];
-
- let assemblyFormat = "`<` struct(params) `>`";
-}
-
-//===----------------------------------------------------------------------===//
-// XeGPU Memory Scope Enums.
-//===----------------------------------------------------------------------===//
-def XeGPU_MemoryScopeGlobal: I32EnumAttrCase<"Global", 0, "global">;
-def XeGPU_MemoryScopeShared: I32EnumAttrCase<"SLM", 1, "slm">;
-def XeGPU_MemoryScope: I32EnumAttr<"MemoryScope",
- "The address space of the memory the tensor descritor is created for",
- [XeGPU_MemoryScopeGlobal, XeGPU_MemoryScopeShared]> {
- let genSpecializedAttr = 0;
- let cppNamespace = "::mlir::xegpu";
-}
-
-def XeGPU_MemoryScopeAttr:
- EnumAttr<XeGPU_Dialect, XeGPU_MemoryScope, "memory_scope"> {
- let assemblyFormat = "$value";
-}
-
-//===----------------------------------------------------------------------===//
-// XeGPU Cache Enums.
-//===----------------------------------------------------------------------===//
-def XeGPU_CachePolicyCached: I32EnumAttrCase<"CACHED", 0, "cached">; // valid for read and write
-def XeGPU_CachePolicyUncached: I32EnumAttrCase<"UNCACHED", 1, "uncached">; // valid for read and write
-def XeGPU_CachePolicyStreaming: I32EnumAttrCase<"STREAMING", 2, "streaming">; // valid for read only
-def XeGPU_CachePolicyInvalid: I32EnumAttrCase<"READ_INVALIDATE", 3, "read_invalidate">; // valid for read only
-def XeGPU_CachePolicyWriteBack: I32EnumAttrCase<"WRITE_BACK", 4, "write_back">; // valid for write only
-def XeGPU_CachePolicyWriteThrough: I32EnumAttrCase<"WRITE_THROUGH", 5, "write_through">; // valid for write only
-
-def XeGPU_CachePolicyEnums : I32EnumAttr<"CachePolicy", "Cache policy",
- [XeGPU_CachePolicyCached, XeGPU_CachePolicyUncached,
- XeGPU_CachePolicyStreaming, XeGPU_CachePolicyInvalid,
- XeGPU_CachePolicyWriteBack, XeGPU_CachePolicyWriteThrough]> {
- let genSpecializedAttr = 0;
- let cppNamespace = "::mlir::xegpu";
-}
-
-def XeGPU_CacheHintAttr
- : EnumAttr<XeGPU_Dialect, XeGPU_CachePolicyEnums, "cache_hint"> {
- let assemblyFormat = "`<` $value `>`";
-}
-
-
-
#endif // MLIR_DIALECT_XEGPU_IR_XEGPUATTRS_TD
diff --git a/mlir/include/mlir/Dialect/XeGPU/IR/XeGPUDialect.td b/mlir/include/mlir/Dialect/XeGPU/IR/XeGPUDialect.td
index c2f09319c790e0..3851275ad30a0a 100644
--- a/mlir/include/mlir/Dialect/XeGPU/IR/XeGPUDialect.td
+++ b/mlir/include/mlir/Dialect/XeGPU/IR/XeGPUDialect.td
@@ -23,8 +23,8 @@ def XeGPU_Dialect : Dialect {
the lower-level GPU compiler.
}];
- let useDefaultTypePrinterParser = true;
- let useDefaultAttributePrinterParser = true;
+ // let useDefaultTypePrinterParser = true;
+ // let useDefaultAttributePrinterParser = true;
}
#endif // MLIR_DIALECT_XEGPU_IR_XEGPUDIALECT_TD
diff --git a/mlir/include/mlir/Dialect/XeGPU/IR/XeGPUOps.td b/mlir/include/mlir/Dialect/XeGPU/IR/XeGPUOps.td
index 1f90dcb4bf55ad..5825ef9195b03f 100644
--- a/mlir/include/mlir/Dialect/XeGPU/IR/XeGPUOps.td
+++ b/mlir/include/mlir/Dialect/XeGPU/IR/XeGPUOps.td
@@ -9,13 +9,10 @@
#ifndef MLIR_DIALECT_XEGPU_IR_XEGPUOPS_TD
#define MLIR_DIALECT_XEGPU_IR_XEGPUOPS_TD
-include "mlir/IR/AttrTypeBase.td"
include "mlir/Dialect/XeGPU/IR/XeGPUAttrs.td"
include "mlir/Dialect/XeGPU/IR/XeGPUDialect.td"
include "mlir/Dialect/XeGPU/IR/XeGPUTypes.td"
-include "mlir/Interfaces/ShapedOpInterfaces.td"
-include "mlir/Interfaces/SideEffectInterfaces.td"
-include "mlir/Interfaces/ViewLikeInterface.td"
+
// Base class for dialect operations. This operation inherits from the base
// `Op` class in OpBase.td, and provides:
@@ -23,291 +20,7 @@ include "mlir/Interfaces/ViewLikeInterface.td"
// * The mnemonic for the operation, or the name without the dialect prefix.
// * A list of traits for the operation.
class XeGPU_Op<string mnemonic, list<Trait> traits = []>:
- Op<XeGPU_Dialect, mnemonic, traits> {
-
- code extraBaseClassDeclaration = [{
- void printProperties(::mlir::MLIRContext *ctx,
- ::mlir::OpAsmPrinter &p, const Properties &prop) {
- Attribute propAttr = getPropertiesAsAttr(ctx, prop);
- if (propAttr)
- p << "<" << propAttr << ">";
- }
-
- static ::mlir::ParseResult parseProperties(::mlir::OpAsmParser &parser,
- ::mlir::OperationState &result) {
- if (mlir::succeeded(parser.parseLess())) {
- if (parser.parseAttribute(result.propertiesAttr) || parser.parseGreater())
- return failure();
- }
- return success();
- }
-
- }];
-}
-
-
-def XeGPU_CreateNdDescOp: XeGPU_Op<"create_nd_tdesc", [Pure, ViewLikeOpInterface,
- AttrSizedOperandSegments, OffsetSizeAndStrideOpInterface]> {
-
- let summary = "Create nd-tensor descriptor operation";
- let description = [{
- The "create_nd_tdesc" operation creates a TensorDescType which represents
- a sub-view of a 2D memory region (It can be extended to support n-D memory
- region if needed in future). Elements in the subview continuous in each
- dimention. It encodes the following important information for supporting
- Intel hardware features:
-
- * source: an object representing (starting address/pointer of) a 2D memory region.
- It can be either a 2D memref object, or simply a pointer represented by uint64_t type.
- for the later case, the shape and layout information of the 2D memory region should
- be explicitly passed via `dynamic_shape` and `dynamic_strides` parameters.
- * offsets: two index values represents offsets from the "source" at the each dimension
- at which the subview of the target memory will be created. It is encoded via two
- variables, including "dynamic_offsets" and "static_offsets", such that it can
- accept various forms, such as, operands (e.g., [%c0, %c]) and attributes (e.g., [2, 4])).
- * shape: the shape information of the memory region pointed by the "source". It is
- typically encoded via the MemRefType of the source, e.g., memref<4096x4096xf16>.
- But if "source" is simply a pointer represented as uint64_t type, or a memref
- type without shape information e.g., memref<?x?xf16>, the shape information has
- to be explicitly passed via the "dynamic_shape" argument. Currently "dynamic_shape"
- only accepts operands(e.g., [%c4096, %c4096]), not attributes(e.g., [4096, 4096]).
- * strides: the strides of the memory region pointed by the "source". Similar to shape,
- it is typically encoded via the MemRefType of the source too. But if "source" is
- simply a pointer represented as uint64_t type, or a memref type without shape
- information e.g., memref<?x?xf16>, the strides information has to be explicitly
- passed via the "dynamic_strides" argument. And it currently only accepts operands two.
-
- Example 1 (suppose the tensor shape inferred by the compiler is 8x16):
- %0 = memref.alloc() : memref<1024x1024xf32>
- %c0 = arith.constant 0 : index
- %c1 = arith.constant 1 : index
- %1 = xegpu.create_nd_tdesc %0[%c0, %c0]: memref<1024x1024xf32> -> TensorDesc<8x16xf32>
-
- Example 2 (suppose the tensor shape inferred by the compiler is 8x16):
- %0 = memref.alloc(%h, %w) : memref<?x?xf32>
- %c0 = arith.constant 0 : index
- %c1 = arith.constant 1 : index
- %1 = xegpu.create_nd_tdesc %0[%c0, %c0], [%h, %w], [%w, %c1]: memref<?x?xf32> -> TensorDesc<8x16xf32>
-
- Example 3 (suppose the tensor shape inferred by the compiler is 8x16):
- %0 = ... : ui64
- %c0 = arith.constant 0 : index
- %c1 = arith.constant 1 : index
- %1 = xegpu.create_nd_tdesc %0[%c0, %c0], [%h, %w], [%w, %c1]: ui64 -> TensorDesc<8x16xf32>
- }];
-
- let arguments = (ins
- XeGPU_BaseAddrType: $source,
- Variadic<Index>: $offsets,
- Variadic<Index>: $shape,
- Variadic<Index>: $strides,
- DenseI64ArrayAttr: $static_offsets
- );
- let results = (outs XeGPU_TensorDesc: $TensorDesc);
-
- let assemblyFormat = [{
- $source ``
- custom<DynamicIndexList>($offsets, $static_offsets)
- (`,` `[` $shape^ `]` `,` `[` $strides `]`)?
- attr-dict `:` type($source) `->` qualified(type($TensorDesc))
- }];
-
- let hasVerifier = 1;
-
- let builders = [
- OpBuilder<(ins "Type": $tdesc, "TypedValue<MemRefType>": $source,
- "llvm::ArrayRef<OpFoldResult>": $offsets)>,
-
- OpBuilder<(ins "Type": $tdesc, "TypedValue<IntegerType> ": $source,
- "llvm::ArrayRef<OpFoldResult>": $offsets,
- "ValueRange": $shape, "ValueRange": $stride)>
- ];
-
- let extraClassDeclaration = extraBaseClassDeclaration # [{
- /// Returns the type of the source memref operand.
- Type getSourceType() {
- return getSource().getType();
- }
-
- /// Returns the type of the result TensorDesc.
- xegpu::TensorDescType getType() {
- return getTensorDesc().getType();
- }
-
- /// Return the element type of the TensorDesc
- Type getElementType() {
- return getType().getElementType();
- }
-
- /// Return the shape of the TensorDesc
- llvm::ArrayRef<int64_t> getTensorDescShape() {
- return getType().getShape();
- }
-
- /// wrapper for matching with OffsetSizeAndStrideOpInterface
- OperandRange getSizes() {
- return getShape();
- }
-
- /// wrapper for matching with OffsetSizeAndStrideOpInterface
- /// If source is IntegerType and `shape` is filled, it will
- /// return an array of ShapedType::kDynamic representing dynamic
- /// shape encoded in the `shape` argument will be used. Presence
- /// of `shape` overides static shape from source memref type.
- SmallVector<int64_t> getStaticSizes() {
- if (getSourceType().isa<IntegerType>() || getShape().size()) {
- auto dims = getMixedOffsets().size();
- return SmallVector<int64_t>(dims, ShapedType::kDynamic);
- }
- auto memrefType = getSourceType().dyn_cast<MemRefType>();
- return SmallVector<int64_t>(memrefType.getShape());
- }
-
- /// wrapper for matching with OffsetSizeAndStrideOpInterface
- /// If source is IntegerType or `strides` is filled, it will
- /// return an array of ShapedType::kDynamic representing dynamic
- /// strides encoded in the `strides` argument will be used. Presence
- /// of `strides` overides static strides from source memref type.
- SmallVector<int64_t> getStaticStrides() {
- if (getSourceType().isa<IntegerType>() || getStrides().size()) {
- auto dims = getMixedOffsets().size();
- return SmallVector<int64_t>(dims, ShapedType::kDynamic);
- }
- auto memrefType = getSourceType().dyn_cast<MemRefType>();
- auto [strides, offset] = getStridesAndOffset(memrefType);
- return strides;
- }
-
- /// Return the expected rank of each of the`static_offsets`,
- /// `static_shape` and `static_strides` attributes.
- std::array<unsigned, 3> getArrayAttrMaxRanks() {
- unsigned rank;
- if (auto ty = getSourceType().dyn_cast<MemRefType>()) {
- rank = ty.getRank();
- } else {
- rank = (unsigned)getMixedOffsets().size();
- }
- return {rank, rank, rank};
- }
-
- /// Return the number of leading operands before the `offsets`,
- /// `shape` and `strides` operands.
- static unsigned getOffsetSizeAndStrideStartOperandIndex() { return 1; }
-
- mlir::Value getViewSource() { return getSource(); }
- }];
-}
-
-def XeGPU_PrefetchNdOp : XeGPU_Op<"prefetch_nd", []> {
- let summary = "prefetches a nD block to cache";
- let description = [{
- It issues an instruction to prefetch the data from memory to each
- level of the cache based on their cache policy.
-
- Example:
- ```
- xegpu.prefetch_nd %tdesc {l1_hint = #xegpu.cache_hint<cached>,
- l2_hint = #xegpu.cache_hint<cached>,
- l3_hint = #xegpu.cache_hint<cached>}
- : !xegpu.tensor_desc<8x16xf16>
- ```
-
- }];
-
- let arguments = (ins XeGPU_TensorDesc: $TensorDesc,
- OptionalAttr<XeGPU_CacheHintAttr>: $l1_hint,
- OptionalAttr<XeGPU_CacheHintAttr>: $l2_hint,
- OptionalAttr<XeGPU_CacheHintAttr>: $l3_hint);
-
- let extraClassDeclaration = extraBaseClassDeclaration;
-
- let assemblyFormat = "$TensorDesc prop-dict attr-dict `:` qualified(type($TensorDesc))";
-}
-
-
-def XeGPU_LoadNdOp : XeGPU_Op<"load_nd"> {
- let summary = "loads a n-D block from memory (represented by TensorDesc)"
- "to registers (represented by vector)";
- let description = [{
- LoadNdOp essentially mimics the hardware block read instruction to read
- a block of data from memory to register. It takes a set of optional cache
- hints for each level of cache, L1, L2 and L3. If hardware does not have a
- correspoding cache, Corresponding cache hint attribute will be masked.
- vnni transform is an hardware feature for Intel GPU, which is used to
- do data packing during the load for B operand of matrix operation, if
- the bit width of the data type is less then 32 bits, e.g., fp16. And
- transpose is another Intel hardware feature, which will do transpose
- operation when loading the data if the bit width of the data type is
- fp32 or fp64. It implies that vnni and transpose cannot exit at the
- same time.
-
- Example:
- ```
- xegpu.load_nd %1 {transpose = [1, 0],
- l1_hint = #xegpu.cache_hint<cached>,
- l2_hint = #xegpu.cache_hint<uncached>,
- l3_hint = #xegpu.cache_hint<streaming>}
- : !xegpu.tensor_desc<8x16xf32> -> vector<16x8xf32>
- ```
-
-
- }];
-
- let arguments = (ins XeGPU_TensorDesc: $TensorDesc,
- OptionalAttr<I64Attr>: $vnni_axis,
- OptionalAttr<DenseI64ArrayAttr>: $transpose,
- OptionalAttr<XeGPU_CacheHintAttr>: $l1_hint,
- OptionalAttr<XeGPU_CacheHintAttr>: $l2_hint,
- OptionalAttr<XeGPU_CacheHintAttr>: $l3_hint);
-
- let results = (outs XeGPU_ValueType: $value);
-
- let extraClassDeclaration = extraBaseClassDeclaration # [{
- VectorType getType() {
- return llvm::dyn_cast<VectorType>(getValue().getType());
- }
-
- xegpu::TensorDescType getTensorDescType() {
- return getTensorDesc().getType();
- }
- }];
-
- let assemblyFormat = "$TensorDesc prop-dict attr-dict `:` qualified(type($TensorDesc)) `->` type($value)";
- let hasVerifier = 1;
-}
-
-def XeGPU_StoreNdOp : XeGPU_Op<"store_nd", []> {
- let summary = "stores a n-D block register region back to memory, currently only supports 2D";
-
- let description = [{
- StoreNdOp essentially mimics the hardware block write instruction io
- write a block of data from register into the memory region as described
- by the TensorDesc. It takes a set of optional cache hints for each level
- of cache, L1, L2 and L3. If hardware does not have a correspoding cache,
- Corresponding cache hint attribute will be masked.
-
- Example:
- ```
- xegpu.store_nd %3, %2 {l1_hint = #xegpu.cache_hint<uncached>,
- l2_hint = #xegpu.cache_hint<write_back>,
- l3_hint = #xegpu.cache_hint<write_through>}
- : vector<8x16xf16>, !xegpu.tensor_desc<8x16xf16>
- ```
-
-
- }];
-
- let arguments = (ins XeGPU_ValueType: $value,
- XeGPU_TensorDesc: $TensorDesc,
- OptionalAttr<XeGPU_CacheHintAttr>: $l1_hint,
- OptionalAttr<XeGPU_CacheHintAttr>: $l2_hint,
- OptionalAttr<XeGPU_CacheHintAttr>: $l3_hint);
-
- let extraClassDeclaration = extraBaseClassDeclaration;
+ Op<XeGPU_Dialect, mnemonic, traits>;
- let assemblyFormat = [{$value `,` $TensorDesc prop-dict attr-dict
- `:` type($value) `,` qualified(type($TensorDesc))}];
- let hasVerifier = 1;
-}
#endif // MLIR_DIALECT_XEGPU_IR_XEGPUOPS_TD
diff --git a/mlir/include/mlir/Dialect/XeGPU/IR/XeGPUTypes.td b/mlir/include/mlir/Dialect/XeGPU/IR/XeGPUTypes.td
index 19ac1693712dd8..1d75bb4e2906fe 100644
--- a/mlir/include/mlir/Dialect/XeGPU/IR/XeGPUTypes.td
+++ b/mlir/include/mlir/Dialect/XeGPU/IR/XeGPUTypes.td
@@ -9,9 +9,9 @@
#ifndef MLIR_DIALECT_XEGPU_IR_XEGPUTYPES_TD
#define MLIR_DIALECT_XEGPU_IR_XEGPUTYPES_TD
+include "mlir/IR/BuiltinTypes.td"
include "mlir/Dialect/XeGPU/IR/XeGPUAttrs.td"
include "mlir/Dialect/XeGPU/IR/XeGPUDialect.td"
-include "mlir/IR/BuiltinTypes.td"
def XeGPU_IntType: AnyTypeOf<[I1, I8, I16, I32, I64, SI1, SI8, SI16, SI32, SI64, UI1, UI8, UI16, UI32, UI64]>;
def XeGPU_FloatType: AnyTypeOf<[F16, F32, F64, BF16, TF32]>;
@@ -30,106 +30,4 @@ class XeGPUTypeDef<string name, string typeMnemonic, list<Trait> traits = [],
let mnemonic = typeMnemonic;
}
-def XeGPU_TensorDesc: XeGPUTypeDef<"TensorDesc", "tensor_desc",
- [ShapedTypeInterface], "::mlir::TensorType"> {
- let summary = "TensorDesc describing regions of interested data.";
- let description = [{
- TensorDesc is a type designed to describe regions of the interested data as well as some
- features that are unique to Intel hardware. Different with the builtin tensor type in MLIR,
- it essentially only contains the meta data, and doesn't hold the data by itself. It is designed
- to mainly support 2D block load/store and DPAS (matrix multiplication instruction) on Intel GPU.
- It encodes the following information:
-
- * shape: the sizes/shape of the intereted data block, e.g., 8x16 means 8 rows
- and each row contains 16 contiguous data element. The rows could be
- either contiguous or not, depends on whether the encoding attribute
- is set or not.
- * element_type: the data type of the data element, e.g., f16, f32.
-
- Similar to the builtin tensor, it also provides an optinal attribute to encoding
- the following information via the TensorDescAttr object:
- * memory_scope (xegpu::MemoryScope): [optional] where the data is located,
- global memory or shared memory. It is default to Global.
- * array_length (int): [optional] The number of contiguous blocks with size as `shape`,
- that will be loaded by block load at a time. It is default to 1.
- * boundary_check (bool): [optional] indicates whether the operation detects the boundary
- and pads with zero for out-of-boundary access. It is default to do boundary check.
-
-
- Syntax:
-
- ```
- TensorDesc-type ::= `tensor_desc` `<` dim-list element-type (attr-list)? `>`
- element-type ::= float-type | integer-type | index-type
- dim-list := (static-dim-list `x`)?
- static-dim-list ::= decimal-literal `x` decimal-literal
- attr-list = (, memory_scope = value)? (, arr_len = value)? (, boundary_check = value)?
- ```
-
- Examples:
-
- ```mlir
- // A block TensorDesc with 8x16 i32 elements
- xegpu.tensor_desc<8x16xi32>
-
- // A block TensorDesc with 8x16 f32 elements
- xegpu.tensor_desc<8x16xf32>
-
- // A TensorDesc with 8x16 f32 elements for a memory region in shared memory space.
- xegpu.tensor_desc<8x16xf32, #xegpu.tdesc_attr<memory_scope = slm>>
- ```
- }];
-
- let parameters = (ins ArrayRefParameter<"int64_t">: $shape,
- "mlir::Type": $elementType,
- OptionalParameter<"mlir::Attribute">: $encoding);
-
- let extraClassDeclaration = [{
- using TensorType::clone;
- using mlir::ShapedType::Trait<TensorDescType>::getElementTypeBitWidth;
- using mlir::ShapedType::Trait<TensorDescType>::getRank;
- using mlir::ShapedType::Trait<TensorDescType>::getNumElements;
- using mlir::ShapedType::Trait<TensorDescType>::isDynamicDim;
- using mlir::ShapedType::Trait<TensorDescType>::hasStaticShape;
- using mlir::ShapedType::Trait<TensorDescType>::getNumDynamicDims;
- using mlir::ShapedType::Trait<TensorDescType>::getDimSize;
- using mlir::ShapedType::Trait<TensorDescType>::getDynamicDimIndex;
-
- TensorDescType clone(::mlir::Type elementType) {
- return llvm::cast<TensorDescType>(cloneWith(getShape(), elementType));
- }
-
- TensorDescAttr getEncodingAsTensorDescAttr() const {
- return llvm::dyn_cast_if_present<TensorDescAttr>(getEncoding());
- }
-
- xegpu::MemoryScope getMemoryScope() const {
- auto attr = getEncodingAsTensorDescAttr();
- if (attr && attr.getMemoryScope())
- return attr.getMemoryScope().getValue();
- // return default value
- return MemoryScope::Global;
- }
-
- int getArrayLength() {
- auto attr = getEncodingAsTensorDescAttr();
- if (attr && attr.getArrayLength())
- return attr.getArrayLength().getInt();
- // return default value
- return 1;
- }
-
- bool getBoundaryCheck() {
- auto attr = getEncodingAsTensorDescAttr();
- if (attr && attr.getBoundaryCheck())
- return attr.getBoundaryCheck().getValue();
- // return default value
- return true;
- }
- }];
-
- let hasCustomAssemblyFormat = true;
-
-}
-
#endif // MLIR_DIALECT_XEGPU_IR_XEGPUTYPES_TD
diff --git a/mlir/lib/Dialect/XeGPU/IR/XeGPUDialect.cpp b/mlir/lib/Dialect/XeGPU/IR/XeGPUDialect.cpp
index 0b3f4b9c9dbeae..4f839ee773476b 100644
--- a/mlir/lib/Dialect/XeGPU/IR/XeGPUDialect.cpp
+++ b/mlir/lib/Dialect/XeGPU/IR/XeGPUDialect.cpp
@@ -6,10 +6,7 @@
//
//===----------------------------------------------------------------------===//
-#include "mlir/Dialect/XeGPU/IR/XeGPU.h"
-#include "mlir/IR/Builders.h"
-#include "mlir/IR/DialectImplementation.h"
-#include "llvm/ADT/TypeSwitch.h"
+#include <mlir/Dialect/XeGPU/IR/XeGPU.h>
namespace mlir {
namespace xegpu {
@@ -29,72 +26,8 @@ void XeGPUDialect::initialize() {
>();
}
-//===----------------------------------------------------------------------===//
-// XeGPU_TensorDescAttr
-//===----------------------------------------------------------------------===//
-
-//===----------------------------------------------------------------------===//
-// XeGPU_TensorDescType
-//===----------------------------------------------------------------------===//
-mlir::Type TensorDescType::parse(::mlir::AsmParser &parser) {
- llvm::SmallVector<int64_t> shape;
- mlir::Type elementType;
- mlir::FailureOr<mlir::Attribute> encoding;
-
- // Parse literal '<'
- if (parser.parseLess())
- return {};
-
- auto shapeLoc = parser.getCurrentLocation();
- if (mlir::failed(parser.parseDimensionList(shape))) {
- parser.emitError(shapeLoc, "failed to parse parameter 'shape'");
- return {};
- }
-
- auto elemTypeLoc = parser.getCurrentLocation();
- if (mlir::failed(parser.parseType(elementType))) {
- parser.emitError(elemTypeLoc, "failed to parse parameter 'elementType'");
- return {};
- }
-
- // parse optional attributes
- if (mlir::succeeded(parser.parseOptionalComma())) {
- encoding = mlir::FieldParser<mlir::Attribute>::parse(parser);
- if (mlir::failed(encoding)) {
- parser.emitError(
- parser.getCurrentLocation(),
- "Failed to parse the attribute field for TensorDescType.\n");
- return {};
- }
- }
-
- // Parse literal '>'
- if (parser.parseGreater())
- return {};
-
- return TensorDescType::get(parser.getContext(), shape, elementType,
- encoding.value_or(mlir::Attribute()));
-}
-
-void TensorDescType::print(::mlir::AsmPrinter &printer) const {
- printer << "<";
-
- auto shape = getShape();
- for (int64_t dim : shape) {
- if (mlir::ShapedType::isDynamic(dim))
- printer << '?';
- else
- printer << dim;
- printer << 'x';
- }
-
- printer << getElementType();
-
- if (auto encoding = getEncoding())
- printer << ", " << encoding;
-
- printer << ">";
-}
+// this file is for position occupation,
+// we will add functions in following PRs.
} // namespace xegpu
} // namespace mlir
diff --git a/mlir/lib/Dialect/XeGPU/IR/XeGPUOps.cpp b/mlir/lib/Dialect/XeGPU/IR/XeGPUOps.cpp
index e6ecf26c224818..b356c397fb8369 100644
--- a/mlir/lib/Dialect/XeGPU/IR/XeGPUOps.cpp
+++ b/mlir/lib/Dialect/XeGPU/IR/XeGPUOps.cpp
@@ -6,186 +6,15 @@
//
//===----------------------------------------------------------------------===//
-#include "mlir/Dialect/Utils/StaticValueUtils.h"
-#include "mlir/Dialect/XeGPU/IR/XeGPU.h"
-#include "mlir/IR/Builders.h"
+#include <mlir/Dialect/XeGPU/IR/XeGPU.h>
#define DEBUG_TYPE "xegpu"
namespace mlir {
namespace xegpu {
-static void transpose(llvm::ArrayRef<int64_t> trans,
- std::vector<int64_t> &shape) {
- std::vector<int64_t> old = shape;
- for (size_t i = 0; i < trans.size(); i++)
- shape[i] = old[trans[i]];
-}
-
-template <typename T>
-static std::string makeString(T array, bool breakline = false) {
- std::string buf;
- buf.clear();
- llvm::raw_string_ostream os(buf);
- os << "[";
- for (size_t i = 1; i < array.size(); i++) {
- os << array[i - 1] << ", ";
- if (breakline)
- os << "\n\t\t";
- }
- os << array.back() << "]";
- os.flush();
- return buf;
-}
-
-//===----------------------------------------------------------------------===//
-// XeGPU_CreateNdDescOp
-//===----------------------------------------------------------------------===//
-void CreateNdDescOp::build(OpBuilder &builder, OperationState &state,
- Type tdesc, TypedValue<MemRefType> source,
- llvm::ArrayRef<OpFoldResult> offsets) {
- auto ty = source.getType();
- (void)ty;
- assert(ty && ty.hasStaticShape() && offsets.size() == (size_t)ty.getRank());
-
- llvm::SmallVector<int64_t> staticOffsets;
- llvm::SmallVector<Value> dynamicOffsets;
- dispatchIndexOpFoldResults(offsets, dynamicOffsets, staticOffsets);
-
- build(builder, state, tdesc, source, dynamicOffsets /* dynamic offsets */,
- ValueRange({}) /* empty dynamic shape */,
- ValueRange({}) /* empty dynamic strides */,
- staticOffsets /* static offsets */);
-}
-
-void CreateNdDescOp::build(OpBuilder &builder, OperationState &state,
- Type tdesc, TypedValue<IntegerType> source,
- llvm::ArrayRef<OpFoldResult> offsets,
- ValueRange shape, ValueRange stride) {
- assert(shape.size() && offsets.size() && stride.size() &&
- shape.size() == stride.size() && shape.size() == offsets.size());
-
- llvm::SmallVector<int64_t> staticOffsets;
- llvm::SmallVector<Value> dynamicOffsets;
-
- dispatchIndexOpFoldResults(offsets, dynamicOffsets, staticOffsets);
-
- build(builder, state, tdesc, source, /* dynamic_offsets = */ dynamicOffsets,
- /* dynamic shape = */ shape, /* dynamic strides = */ stride,
- /* static offsets = */ staticOffsets);
-}
-
-LogicalResult CreateNdDescOp::verify() {
- auto rank = (int64_t)getMixedOffsets().size();
- bool invalidRank = (rank != 2);
- bool invalidElemTy = false;
-
- // check source type matches the rank if it is a memref.
- // It also should have the same ElementType as TensorDesc.
- auto memrefTy = getSourceType().dyn_cast<MemRefType>();
- if (memrefTy) {
- invalidRank |= (memrefTy.getRank() != rank);
- invalidElemTy |= memrefTy.getElementType() != getElementType();
- }
-
- // check result type matches the rank
- invalidRank = (getType().getRank() != rank);
-
- // mismatches among shape, strides, and offsets are
- // already handeled by OffsetSizeAndStrideOpInterface.
- // So they are not check here.
- if (invalidRank)
- return emitOpError(
- "Expecting the rank of shape, strides, offsets, "
- "source memref type (if source is a memref) and TensorDesc "
- "should match with each other. They currenlty are 2D.");
-
- if (invalidElemTy)
- return emitOpError("TensorDesc should have the same element "
- "type with the source if it is a memref.\n");
-
- return success();
-}
-
-//===----------------------------------------------------------------------===//
-// XeGPU_LoadNdOp
-//===----------------------------------------------------------------------===//
-LogicalResult LoadNdOp::verify() {
- auto tdescTy = getTensorDescType();
- auto valueTy = getType();
-
- if (tdescTy.getRank() != 2)
- return emitOpError(
- "The TensorDesc for LoadNdOp should be a 2D TensorDesc.");
-
- if (!valueTy)
- return emitOpError("Invalid result, it should be a VectorType.\n");
-
- auto tdescElemTy = tdescTy.getElementType();
- auto valueElemTy = valueTy.getElementType();
-
- if (tdescElemTy != valueElemTy)
- return emitOpError(
- "Value should have the same element type as TensorDesc.");
-
- auto array_len = tdescTy.getArrayLength();
- auto tdescShape = tdescTy.getShape().vec();
- auto valueShape = valueTy.getShape().vec();
-
- if (getTranspose()) {
- auto trans = getTranspose().value();
- if (tdescShape.size() >= trans.size())
- transpose(trans, tdescShape);
- else
- emitWarning("Invalid transpose attr. It is ignored.");
- }
-
- if (getVnniAxis()) {
- auto axis = getVnniAxis().value();
- auto vnni_factor = valueShape.back();
- tdescShape[axis] /= vnni_factor;
- tdescShape.push_back(vnni_factor);
- }
-
- if (array_len > 1) {
- auto it = tdescShape.begin();
- tdescShape.insert(it, array_len);
- }
-
- if (tdescShape != valueShape)
- return emitOpError() << "Result shape doesn't match TensorDesc shape."
- << "The expected shape is " << makeString(tdescShape)
- << ". But the given shape is "
- << makeString(valueShape) << ".\n";
- return success();
-}
-
-//===----------------------------------------------------------------------===//
-// XeGPU_StoreNdOp
-//===----------------------------------------------------------------------===//
-LogicalResult StoreNdOp::verify() {
- auto dstTy = getTensorDesc().getType(); // Tile
- auto valTy = getValue().getType().cast<VectorType>(); // Vector
-
- if (dstTy.getRank() != 2)
- return emitOpError("Expecting a 2D TensorDesc shape.\n");
-
- if (!valTy)
- return emitOpError("Exepcting a VectorType result.\n");
-
- auto dstElemTy = dstTy.getElementType();
- auto valElemTy = valTy.getElementType();
-
- if (dstElemTy != valElemTy) {
- return emitOpError() << "The element type of the value should "
- "match the elementtype of the TensorDesc.\n";
- }
-
- if (dstTy.getShape() != valTy.getShape())
- return emitOpError()
- << "The result shape should match the TensorDesc shape.\n";
- return success();
-}
+// this file is for position occupation,
+// we will add functions in following PRs.
} // namespace xegpu
} // namespace mlir
diff --git a/mlir/test/Dialect/XeGPU/XeGPUOps.mlir b/mlir/test/Dialect/XeGPU/XeGPUOps.mlir
deleted file mode 100644
index 039346adbb851c..00000000000000
--- a/mlir/test/Dialect/XeGPU/XeGPUOps.mlir
+++ /dev/null
@@ -1,62 +0,0 @@
-// RUN: mlir-opt %s | FileCheck %s
-// Verify the printed output can be parsed.
-// RUN: mlir-opt %s | mlir-opt | FileCheck %s
-// Verify the generic form can be parsed.
-// RUN: mlir-opt -mlir-print-op-generic %s | mlir-opt | FileCheck %s
-
-// CHECK-LABEL: gpu.module @test {
-gpu.module @test {
-// CHECK: gpu.func @test_create_nd_tdesc_vc_1(%[[arg0:.*]]: memref<24x32xf32>) {
-gpu.func @test_create_nd_tdesc_vc_1(%src: memref<24x32xf32>) {
- // CHECK: %[[REG:.*]] = xegpu.create_nd_tdesc %arg0[0, 0] : memref<24x32xf32> -> !xegpu.tensor_desc<8x16xf32>
- %1 = xegpu.create_nd_tdesc %src[0, 0] : memref<24x32xf32> -> !xegpu.tensor_desc<8x16xf32>
- gpu.return
-}
-
-// CHECK: gpu.func @test_create_nd_tdesc_vc_2(%[[arg0:.*]]: ui64, %[[arg1:.*]]: index, %[[arg2:.*]]: index, %[[arg3:.*]]: index, %[[arg4:.*]]: index) {
-gpu.func @test_create_nd_tdesc_vc_2(%src: ui64, %w : index, %h : index, %x : index, %y : index) {
- //CHECK: %[[C:.*]] = arith.constant 1 : index
- %c1 = arith.constant 1 : index
- // CHECK: %[[REG:.*]] = xegpu.create_nd_tdesc %[[arg0]][%[[arg3]], %[[arg4]]], [%[[arg2]], %[[arg1]]], [%[[arg1]], %[[C]]] : ui64 -> !xegpu.tensor_desc<8x16xf32>
- %1 = xegpu.create_nd_tdesc %src[%x, %y], [%h, %w], [%w, %c1] : ui64 -> !xegpu.tensor_desc<8x16xf32>
- gpu.return
-}
-
-// CHECK: gpu.func @test_create_nd_tdesc_vc_3(%[[arg0:.*]]: memref<24x32xf32>) {
-gpu.func @test_create_nd_tdesc_vc_3(%src: memref<24x32xf32>) {
- // CHECK: %[[REG:.*]] = xegpu.create_nd_tdesc %[[arg0]][0, 0] : memref<24x32xf32> -> !xegpu.tensor_desc<24x16xf32, #xegpu.tdesc_attr<array_length = 2 : i64>
- %1 = xegpu.create_nd_tdesc %src[0, 0] : memref<24x32xf32> -> !xegpu.tensor_desc<24x16xf32, #xegpu.tdesc_attr<array_length = 2>>
- gpu.return
-}
-
-// CHECK: gpu.func @test_prefetch_nd_vc(%[[arg0:.*]]: memref<24x32xf16>) {
-gpu.func @test_prefetch_nd_vc(%src: memref<24x32xf16>) {
- // CHECK: %[[R0:.*]] = xegpu.create_nd_tdesc %[[arg0]][0, 0] : memref<24x32xf16> -> !xegpu.tensor_desc<8x16xf16>
- %1 = xegpu.create_nd_tdesc %src[0, 0] : memref<24x32xf16> -> !xegpu.tensor_desc<8x16xf16>
- // CHECK: xegpu.prefetch_nd %[[R0]] <{l1_hint = #xegpu.cache_hint<cached>, l2_hint = #xegpu.cache_hint<uncached>}> : !xegpu.tensor_desc<8x16xf16>
- xegpu.prefetch_nd %1 <{l1_hint = #xegpu.cache_hint<cached>, l2_hint = #xegpu.cache_hint<uncached>}>: !xegpu.tensor_desc<8x16xf16>
- gpu.return
-}
-
-// CHECK: func @test_load_nd_vc(%[[arg0:.*]]: memref<8x16xf16>) {
-gpu.func @test_load_nd_vc(%src: memref<8x16xf16>) {
- // CHECK: %[[R0:.*]] = xegpu.create_nd_tdesc %arg0[0, 0] : memref<8x16xf16> -> !xegpu.tensor_desc<8x16xf16>
- %1 = xegpu.create_nd_tdesc %src[0, 0] : memref<8x16xf16> -> !xegpu.tensor_desc<8x16xf16>
- // CHECK: %[[R1:.*]] = xegpu.load_nd %[[R0]] <{l1_hint = #xegpu.cache_hint<cached>, l2_hint = #xegpu.cache_hint<uncached>, vnni_axis = 0 : i64}> : !xegpu.tensor_desc<8x16xf16> -> vector<4x16x2xf16>
- %2 = xegpu.load_nd %1 <{vnni_axis = 0, l1_hint = #xegpu.cache_hint<cached>, l2_hint = #xegpu.cache_hint<uncached>}>
- : !xegpu.tensor_desc<8x16xf16> -> vector<4x16x2xf16>
- gpu.return
-}
-
-// CHECK: func @test_store_nd_vc(%[[arg0:.*]]: memref<24x32xf16>) {
-gpu.func @test_store_nd_vc(%dst: memref<24x32xf16>) {
- // CHECK: %[[C:.*]] = arith.constant dense<1.000000e+00> : vector<24x32xf16>
- %1 = arith.constant dense<1.0>: vector<24x32xf16>
- // CHECK: %[[R0:.*]] = xegpu.create_nd_tdesc %[[arg0]][0, 0] : memref<24x32xf16> -> !xegpu.tensor_desc<24x32xf16>
- %2 = xegpu.create_nd_tdesc %dst[0, 0] : memref<24x32xf16> -> !xegpu.tensor_desc<24x32xf16>
- // CHECK: xegpu.store_nd %[[C]], %[[R0]] <{l1_hint = #xegpu.cache_hint<write_back>, l2_hint = #xegpu.cache_hint<uncached>}> : vector<24x32xf16>, !xegpu.tensor_desc<24x32xf16>
- xegpu.store_nd %1, %2 <{l1_hint = #xegpu.cache_hint<write_back>, l2_hint = #xegpu.cache_hint<uncached>}>: vector<24x32xf16>, !xegpu.tensor_desc<24x32xf16>
- gpu.return
-}
-
-}
\ No newline at end of file
>From bd9a2afa8015a3f0bcc214a053816f4275f8d891 Mon Sep 17 00:00:00 2001
From: zhongyunde 00443407 <zhongyunde at huawei.com>
Date: Mon, 18 Mar 2024 17:50:45 +0100
Subject: [PATCH 31/40] [InstCombine] Add tests for selects with same
conditions (NFC)
---
llvm/test/Transforms/InstCombine/select.ll | 56 ++++++++++++++++++++++
1 file changed, 56 insertions(+)
diff --git a/llvm/test/Transforms/InstCombine/select.ll b/llvm/test/Transforms/InstCombine/select.ll
index d9734242a86891..278cabdff9ed3e 100644
--- a/llvm/test/Transforms/InstCombine/select.ll
+++ b/llvm/test/Transforms/InstCombine/select.ll
@@ -3706,3 +3706,59 @@ define i32 @src_select_xxory_eq0_xorxy_y(i32 %x, i32 %y) {
%cond = select i1 %xor0, i32 %xor, i32 %y
ret i32 %cond
}
+
+define i32 @sequence_select_with_same_cond_false(i1 %c1, i1 %c2){
+; CHECK-LABEL: @sequence_select_with_same_cond_false(
+; CHECK-NEXT: [[S1:%.*]] = select i1 [[C1:%.*]], i32 23, i32 45
+; CHECK-NEXT: [[S2:%.*]] = select i1 [[C2:%.*]], i32 666, i32 [[S1]]
+; CHECK-NEXT: [[S3:%.*]] = select i1 [[C1]], i32 789, i32 [[S2]]
+; CHECK-NEXT: ret i32 [[S3]]
+;
+ %s1 = select i1 %c1, i32 23, i32 45
+ %s2 = select i1 %c2, i32 666, i32 %s1
+ %s3 = select i1 %c1, i32 789, i32 %s2
+ ret i32 %s3
+}
+
+define i32 @sequence_select_with_same_cond_true(i1 %c1, i1 %c2){
+; CHECK-LABEL: @sequence_select_with_same_cond_true(
+; CHECK-NEXT: [[S1:%.*]] = select i1 [[C1:%.*]], i32 45, i32 23
+; CHECK-NEXT: [[S2:%.*]] = select i1 [[C2:%.*]], i32 [[S1]], i32 666
+; CHECK-NEXT: [[S3:%.*]] = select i1 [[C1]], i32 [[S2]], i32 789
+; CHECK-NEXT: ret i32 [[S3]]
+;
+ %s1 = select i1 %c1, i32 45, i32 23
+ %s2 = select i1 %c2, i32 %s1, i32 666
+ %s3 = select i1 %c1, i32 %s2, i32 789
+ ret i32 %s3
+}
+
+define double @sequence_select_with_same_cond_double(double %a, i1 %c1, i1 %c2, double %r1, double %r2){
+; CHECK-LABEL: @sequence_select_with_same_cond_double(
+; CHECK-NEXT: [[S1:%.*]] = select i1 [[C1:%.*]], double 1.000000e+00, double 0.000000e+00
+; CHECK-NEXT: [[S2:%.*]] = select i1 [[C2:%.*]], double [[S1]], double 2.000000e+00
+; CHECK-NEXT: [[S3:%.*]] = select i1 [[C1]], double [[S2]], double 3.000000e+00
+; CHECK-NEXT: ret double [[S3]]
+;
+ %s1 = select i1 %c1, double 1.0, double 0.0
+ %s2 = select i1 %c2, double %s1, double 2.0
+ %s3 = select i1 %c1, double %s2, double 3.0
+ ret double %s3
+}
+
+declare void @use32(i32)
+
+define i32 @sequence_select_with_same_cond_extra_use(i1 %c1, i1 %c2){
+; CHECK-LABEL: @sequence_select_with_same_cond_extra_use(
+; CHECK-NEXT: [[S1:%.*]] = select i1 [[C1:%.*]], i32 23, i32 45
+; CHECK-NEXT: call void @use32(i32 [[S1]])
+; CHECK-NEXT: [[S2:%.*]] = select i1 [[C2:%.*]], i32 666, i32 [[S1]]
+; CHECK-NEXT: [[S3:%.*]] = select i1 [[C1]], i32 789, i32 [[S2]]
+; CHECK-NEXT: ret i32 [[S3]]
+;
+ %s1 = select i1 %c1, i32 23, i32 45
+ call void @use32(i32 %s1)
+ %s2 = select i1 %c2, i32 666, i32 %s1
+ %s3 = select i1 %c1, i32 789, i32 %s2
+ ret i32 %s3
+}
>From 705788c84623b4f1dab72a108e039a0de2d53cf6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fulop at sigmatechnology.com>
Date: Mon, 18 Mar 2024 17:56:15 +0100
Subject: [PATCH 32/40] [clang][analyzer] Improve
BlockInCriticalSectionsChecker (#80029)
* Add support for multiple, potentially overlapping critical sections:
The checker can now simultaneously handle several mutex's critical
sections without confusing them.
* Implement the handling of recursive mutexes:
By identifying the lock events, recursive mutexes are now supported.
A lock event is a pair of a lock expression, and the SVal of the mutex
that it locks, so even multiple locks of the same mutex (and even by
the same expression) is now supported.
* Refine the note tags generated by the checker:
The note tags now correctly show just for mutexes that are
active at the point of error, and multiple acquisitions of the same mutex
are also noted.
---
.../BlockInCriticalSectionChecker.cpp | 386 ++++++++++++++----
.../Analysis/block-in-critical-section.cpp | 270 +++++++++---
2 files changed, 510 insertions(+), 146 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
index 66e080adb1382b..e4373915410fb2 100644
--- a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
@@ -20,48 +20,178 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringExtras.h"
+
+#include <iterator>
+#include <utility>
+#include <variant>
using namespace clang;
using namespace ento;
namespace {
+
+struct CritSectionMarker {
+ const Expr *LockExpr{};
+ const MemRegion *LockReg{};
+
+ void Profile(llvm::FoldingSetNodeID &ID) const {
+ ID.Add(LockExpr);
+ ID.Add(LockReg);
+ }
+
+ [[nodiscard]] constexpr bool
+ operator==(const CritSectionMarker &Other) const noexcept {
+ return LockExpr == Other.LockExpr && LockReg == Other.LockReg;
+ }
+ [[nodiscard]] constexpr bool
+ operator!=(const CritSectionMarker &Other) const noexcept {
+ return !(*this == Other);
+ }
+};
+
+class CallDescriptionBasedMatcher {
+ CallDescription LockFn;
+ CallDescription UnlockFn;
+
+public:
+ CallDescriptionBasedMatcher(CallDescription &&LockFn,
+ CallDescription &&UnlockFn)
+ : LockFn(std::move(LockFn)), UnlockFn(std::move(UnlockFn)) {}
+ [[nodiscard]] bool matches(const CallEvent &Call, bool IsLock) const {
+ if (IsLock) {
+ return LockFn.matches(Call);
+ }
+ return UnlockFn.matches(Call);
+ }
+};
+
+class FirstArgMutexDescriptor : public CallDescriptionBasedMatcher {
+public:
+ FirstArgMutexDescriptor(CallDescription &&LockFn, CallDescription &&UnlockFn)
+ : CallDescriptionBasedMatcher(std::move(LockFn), std::move(UnlockFn)) {}
+
+ [[nodiscard]] const MemRegion *getRegion(const CallEvent &Call, bool) const {
+ return Call.getArgSVal(0).getAsRegion();
+ }
+};
+
+class MemberMutexDescriptor : public CallDescriptionBasedMatcher {
+public:
+ MemberMutexDescriptor(CallDescription &&LockFn, CallDescription &&UnlockFn)
+ : CallDescriptionBasedMatcher(std::move(LockFn), std::move(UnlockFn)) {}
+
+ [[nodiscard]] const MemRegion *getRegion(const CallEvent &Call, bool) const {
+ return cast<CXXMemberCall>(Call).getCXXThisVal().getAsRegion();
+ }
+};
+
+class RAIIMutexDescriptor {
+ mutable const IdentifierInfo *Guard{};
+ mutable bool IdentifierInfoInitialized{};
+ mutable llvm::SmallString<32> GuardName{};
+
+ void initIdentifierInfo(const CallEvent &Call) const {
+ if (!IdentifierInfoInitialized) {
+ // In case of checking C code, or when the corresponding headers are not
+ // included, we might end up query the identifier table every time when
+ // this function is called instead of early returning it. To avoid this, a
+ // bool variable (IdentifierInfoInitialized) is used and the function will
+ // be run only once.
+ Guard = &Call.getCalleeAnalysisDeclContext()->getASTContext().Idents.get(
+ GuardName);
+ IdentifierInfoInitialized = true;
+ }
+ }
+
+ template <typename T> bool matchesImpl(const CallEvent &Call) const {
+ const T *C = dyn_cast<T>(&Call);
+ if (!C)
+ return false;
+ const IdentifierInfo *II =
+ cast<CXXRecordDecl>(C->getDecl()->getParent())->getIdentifier();
+ return II == Guard;
+ }
+
+public:
+ RAIIMutexDescriptor(StringRef GuardName) : GuardName(GuardName) {}
+ [[nodiscard]] bool matches(const CallEvent &Call, bool IsLock) const {
+ initIdentifierInfo(Call);
+ if (IsLock) {
+ return matchesImpl<CXXConstructorCall>(Call);
+ }
+ return matchesImpl<CXXDestructorCall>(Call);
+ }
+ [[nodiscard]] const MemRegion *getRegion(const CallEvent &Call,
+ bool IsLock) const {
+ const MemRegion *LockRegion = nullptr;
+ if (IsLock) {
+ if (std::optional<SVal> Object = Call.getReturnValueUnderConstruction()) {
+ LockRegion = Object->getAsRegion();
+ }
+ } else {
+ LockRegion = cast<CXXDestructorCall>(Call).getCXXThisVal().getAsRegion();
+ }
+ return LockRegion;
+ }
+};
+
+using MutexDescriptor =
+ std::variant<FirstArgMutexDescriptor, MemberMutexDescriptor,
+ RAIIMutexDescriptor>;
+
class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
- mutable IdentifierInfo *IILockGuard = nullptr;
- mutable IdentifierInfo *IIUniqueLock = nullptr;
- mutable bool IdentifierInfoInitialized = false;
-
- const CallDescription LockFn{{"lock"}};
- const CallDescription UnlockFn{{"unlock"}};
- const CallDescription SleepFn{{"sleep"}};
- const CallDescription GetcFn{{"getc"}};
- const CallDescription FgetsFn{{"fgets"}};
- const CallDescription ReadFn{{"read"}};
- const CallDescription RecvFn{{"recv"}};
- const CallDescription PthreadLockFn{{"pthread_mutex_lock"}};
- const CallDescription PthreadTryLockFn{{"pthread_mutex_trylock"}};
- const CallDescription PthreadUnlockFn{{"pthread_mutex_unlock"}};
- const CallDescription MtxLock{{"mtx_lock"}};
- const CallDescription MtxTimedLock{{"mtx_timedlock"}};
- const CallDescription MtxTryLock{{"mtx_trylock"}};
- const CallDescription MtxUnlock{{"mtx_unlock"}};
-
- const llvm::StringLiteral ClassLockGuard{"lock_guard"};
- const llvm::StringLiteral ClassUniqueLock{"unique_lock"};
+private:
+ const std::array<MutexDescriptor, 8> MutexDescriptors{
+ MemberMutexDescriptor(
+ CallDescription(/*QualifiedName=*/{"std", "mutex", "lock"},
+ /*RequiredArgs=*/0),
+ CallDescription({"std", "mutex", "unlock"}, 0)),
+ FirstArgMutexDescriptor(CallDescription({"pthread_mutex_lock"}, 1),
+ CallDescription({"pthread_mutex_unlock"}, 1)),
+ FirstArgMutexDescriptor(CallDescription({"mtx_lock"}, 1),
+ CallDescription({"mtx_unlock"}, 1)),
+ FirstArgMutexDescriptor(CallDescription({"pthread_mutex_trylock"}, 1),
+ CallDescription({"pthread_mutex_unlock"}, 1)),
+ FirstArgMutexDescriptor(CallDescription({"mtx_trylock"}, 1),
+ CallDescription({"mtx_unlock"}, 1)),
+ FirstArgMutexDescriptor(CallDescription({"mtx_timedlock"}, 1),
+ CallDescription({"mtx_unlock"}, 1)),
+ RAIIMutexDescriptor("lock_guard"),
+ RAIIMutexDescriptor("unique_lock")};
+
+ const std::array<CallDescription, 5> BlockingFunctions{
+ ArrayRef{StringRef{"sleep"}}, ArrayRef{StringRef{"getc"}},
+ ArrayRef{StringRef{"fgets"}}, ArrayRef{StringRef{"read"}},
+ ArrayRef{StringRef{"recv"}}};
const BugType BlockInCritSectionBugType{
this, "Call to blocking function in critical section", "Blocking Error"};
- void initIdentifierInfo(ASTContext &Ctx) const;
+ void reportBlockInCritSection(const CallEvent &call, CheckerContext &C) const;
- void reportBlockInCritSection(SymbolRef FileDescSym,
- const CallEvent &call,
- CheckerContext &C) const;
+ [[nodiscard]] const NoteTag *createCritSectionNote(CritSectionMarker M,
+ CheckerContext &C) const;
-public:
- bool isBlockingFunction(const CallEvent &Call) const;
- bool isLockFunction(const CallEvent &Call) const;
- bool isUnlockFunction(const CallEvent &Call) const;
+ [[nodiscard]] std::optional<MutexDescriptor>
+ checkDescriptorMatch(const CallEvent &Call, CheckerContext &C,
+ bool IsLock) const;
+
+ void handleLock(const MutexDescriptor &Mutex, const CallEvent &Call,
+ CheckerContext &C) const;
+ void handleUnlock(const MutexDescriptor &Mutex, const CallEvent &Call,
+ CheckerContext &C) const;
+
+ [[nodiscard]] bool isBlockingInCritSection(const CallEvent &Call,
+ CheckerContext &C) const;
+
+public:
/// Process unlock.
/// Process lock.
/// Process blocking functions (sleep, getc, fgets, read, recv)
@@ -70,73 +200,118 @@ class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
} // end anonymous namespace
-REGISTER_TRAIT_WITH_PROGRAMSTATE(MutexCounter, unsigned)
-
-void BlockInCriticalSectionChecker::initIdentifierInfo(ASTContext &Ctx) const {
- if (!IdentifierInfoInitialized) {
- /* In case of checking C code, or when the corresponding headers are not
- * included, we might end up query the identifier table every time when this
- * function is called instead of early returning it. To avoid this, a bool
- * variable (IdentifierInfoInitialized) is used and the function will be run
- * only once. */
- IILockGuard = &Ctx.Idents.get(ClassLockGuard);
- IIUniqueLock = &Ctx.Idents.get(ClassUniqueLock);
- IdentifierInfoInitialized = true;
- }
+REGISTER_LIST_WITH_PROGRAMSTATE(ActiveCritSections, CritSectionMarker)
+
+namespace std {
+// Iterator traits for ImmutableList data structure
+// that enable the use of STL algorithms.
+// TODO: Move these to llvm::ImmutableList when overhauling immutable data
+// structures for proper iterator concept support.
+template <>
+struct iterator_traits<
+ typename llvm::ImmutableList<CritSectionMarker>::iterator> {
+ using iterator_category = std::forward_iterator_tag;
+ using value_type = CritSectionMarker;
+ using difference_type = std::ptrdiff_t;
+ using reference = CritSectionMarker &;
+ using pointer = CritSectionMarker *;
+};
+} // namespace std
+
+std::optional<MutexDescriptor>
+BlockInCriticalSectionChecker::checkDescriptorMatch(const CallEvent &Call,
+ CheckerContext &C,
+ bool IsLock) const {
+ const auto Descriptor =
+ llvm::find_if(MutexDescriptors, [&Call, IsLock](auto &&Descriptor) {
+ return std::visit(
+ [&Call, IsLock](auto &&DescriptorImpl) {
+ return DescriptorImpl.matches(Call, IsLock);
+ },
+ Descriptor);
+ });
+ if (Descriptor != MutexDescriptors.end())
+ return *Descriptor;
+ return std::nullopt;
}
-bool BlockInCriticalSectionChecker::isBlockingFunction(const CallEvent &Call) const {
- return matchesAny(Call, SleepFn, GetcFn, FgetsFn, ReadFn, RecvFn);
+static const MemRegion *getRegion(const CallEvent &Call,
+ const MutexDescriptor &Descriptor,
+ bool IsLock) {
+ return std::visit(
+ [&Call, IsLock](auto &&Descriptor) {
+ return Descriptor.getRegion(Call, IsLock);
+ },
+ Descriptor);
}
-bool BlockInCriticalSectionChecker::isLockFunction(const CallEvent &Call) const {
- if (const auto *Ctor = dyn_cast<CXXConstructorCall>(&Call)) {
- auto IdentifierInfo = Ctor->getDecl()->getParent()->getIdentifier();
- if (IdentifierInfo == IILockGuard || IdentifierInfo == IIUniqueLock)
- return true;
- }
+void BlockInCriticalSectionChecker::handleLock(
+ const MutexDescriptor &LockDescriptor, const CallEvent &Call,
+ CheckerContext &C) const {
+ const MemRegion *MutexRegion =
+ getRegion(Call, LockDescriptor, /*IsLock=*/true);
+ if (!MutexRegion)
+ return;
- return matchesAny(Call, LockFn, PthreadLockFn, PthreadTryLockFn, MtxLock,
- MtxTimedLock, MtxTryLock);
+ const CritSectionMarker MarkToAdd{Call.getOriginExpr(), MutexRegion};
+ ProgramStateRef StateWithLockEvent =
+ C.getState()->add<ActiveCritSections>(MarkToAdd);
+ C.addTransition(StateWithLockEvent, createCritSectionNote(MarkToAdd, C));
}
-bool BlockInCriticalSectionChecker::isUnlockFunction(const CallEvent &Call) const {
- if (const auto *Dtor = dyn_cast<CXXDestructorCall>(&Call)) {
- const auto *DRecordDecl = cast<CXXRecordDecl>(Dtor->getDecl()->getParent());
- auto IdentifierInfo = DRecordDecl->getIdentifier();
- if (IdentifierInfo == IILockGuard || IdentifierInfo == IIUniqueLock)
- return true;
+void BlockInCriticalSectionChecker::handleUnlock(
+ const MutexDescriptor &UnlockDescriptor, const CallEvent &Call,
+ CheckerContext &C) const {
+ const MemRegion *MutexRegion =
+ getRegion(Call, UnlockDescriptor, /*IsLock=*/false);
+ if (!MutexRegion)
+ return;
+
+ ProgramStateRef State = C.getState();
+ const auto ActiveSections = State->get<ActiveCritSections>();
+ const auto MostRecentLock =
+ llvm::find_if(ActiveSections, [MutexRegion](auto &&Marker) {
+ return Marker.LockReg == MutexRegion;
+ });
+ if (MostRecentLock == ActiveSections.end())
+ return;
+
+ // Build a new ImmutableList without this element.
+ auto &Factory = State->get_context<ActiveCritSections>();
+ llvm::ImmutableList<CritSectionMarker> NewList = Factory.getEmptyList();
+ for (auto It = ActiveSections.begin(), End = ActiveSections.end(); It != End;
+ ++It) {
+ if (It != MostRecentLock)
+ NewList = Factory.add(*It, NewList);
}
- return matchesAny(Call, UnlockFn, PthreadUnlockFn, MtxUnlock);
+ State = State->set<ActiveCritSections>(NewList);
+ C.addTransition(State);
+}
+
+bool BlockInCriticalSectionChecker::isBlockingInCritSection(
+ const CallEvent &Call, CheckerContext &C) const {
+ return llvm::any_of(BlockingFunctions,
+ [&Call](auto &&Fn) { return Fn.matches(Call); }) &&
+ !C.getState()->get<ActiveCritSections>().isEmpty();
}
void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call,
CheckerContext &C) const {
- initIdentifierInfo(C.getASTContext());
-
- if (!isBlockingFunction(Call)
- && !isLockFunction(Call)
- && !isUnlockFunction(Call))
- return;
-
- ProgramStateRef State = C.getState();
- unsigned mutexCount = State->get<MutexCounter>();
- if (isUnlockFunction(Call) && mutexCount > 0) {
- State = State->set<MutexCounter>(--mutexCount);
- C.addTransition(State);
- } else if (isLockFunction(Call)) {
- State = State->set<MutexCounter>(++mutexCount);
- C.addTransition(State);
- } else if (mutexCount > 0) {
- SymbolRef BlockDesc = Call.getReturnValue().getAsSymbol();
- reportBlockInCritSection(BlockDesc, Call, C);
+ if (isBlockingInCritSection(Call, C)) {
+ reportBlockInCritSection(Call, C);
+ } else if (std::optional<MutexDescriptor> LockDesc =
+ checkDescriptorMatch(Call, C, /*IsLock=*/true)) {
+ handleLock(*LockDesc, Call, C);
+ } else if (std::optional<MutexDescriptor> UnlockDesc =
+ checkDescriptorMatch(Call, C, /*IsLock=*/false)) {
+ handleUnlock(*UnlockDesc, Call, C);
}
}
void BlockInCriticalSectionChecker::reportBlockInCritSection(
- SymbolRef BlockDescSym, const CallEvent &Call, CheckerContext &C) const {
- ExplodedNode *ErrNode = C.generateNonFatalErrorNode();
+ const CallEvent &Call, CheckerContext &C) const {
+ ExplodedNode *ErrNode = C.generateNonFatalErrorNode(C.getState());
if (!ErrNode)
return;
@@ -147,14 +322,63 @@ void BlockInCriticalSectionChecker::reportBlockInCritSection(
auto R = std::make_unique<PathSensitiveBugReport>(BlockInCritSectionBugType,
os.str(), ErrNode);
R->addRange(Call.getSourceRange());
- R->markInteresting(BlockDescSym);
+ R->markInteresting(Call.getReturnValue());
C.emitReport(std::move(R));
}
+const NoteTag *
+BlockInCriticalSectionChecker::createCritSectionNote(CritSectionMarker M,
+ CheckerContext &C) const {
+ const BugType *BT = &this->BlockInCritSectionBugType;
+ return C.getNoteTag([M, BT](PathSensitiveBugReport &BR,
+ llvm::raw_ostream &OS) {
+ if (&BR.getBugType() != BT)
+ return;
+
+ // Get the lock events for the mutex of the current line's lock event.
+ const auto CritSectionBegins =
+ BR.getErrorNode()->getState()->get<ActiveCritSections>();
+ llvm::SmallVector<CritSectionMarker, 4> LocksForMutex;
+ llvm::copy_if(
+ CritSectionBegins, std::back_inserter(LocksForMutex),
+ [M](const auto &Marker) { return Marker.LockReg == M.LockReg; });
+ if (LocksForMutex.empty())
+ return;
+
+ // As the ImmutableList builds the locks by prepending them, we
+ // reverse the list to get the correct order.
+ std::reverse(LocksForMutex.begin(), LocksForMutex.end());
+
+ // Find the index of the lock expression in the list of all locks for a
+ // given mutex (in acquisition order).
+ const auto Position =
+ llvm::find_if(std::as_const(LocksForMutex), [M](const auto &Marker) {
+ return Marker.LockExpr == M.LockExpr;
+ });
+ if (Position == LocksForMutex.end())
+ return;
+
+ // If there is only one lock event, we don't need to specify how many times
+ // the critical section was entered.
+ if (LocksForMutex.size() == 1) {
+ OS << "Entering critical section here";
+ return;
+ }
+
+ const auto IndexOfLock =
+ std::distance(std::as_const(LocksForMutex).begin(), Position);
+
+ const auto OrdinalOfLock = IndexOfLock + 1;
+ OS << "Entering critical section for the " << OrdinalOfLock
+ << llvm::getOrdinalSuffix(OrdinalOfLock) << " time here";
+ });
+}
+
void ento::registerBlockInCriticalSectionChecker(CheckerManager &mgr) {
mgr.registerChecker<BlockInCriticalSectionChecker>();
}
-bool ento::shouldRegisterBlockInCriticalSectionChecker(const CheckerManager &mgr) {
+bool ento::shouldRegisterBlockInCriticalSectionChecker(
+ const CheckerManager &mgr) {
return true;
}
diff --git a/clang/test/Analysis/block-in-critical-section.cpp b/clang/test/Analysis/block-in-critical-section.cpp
index fcf6188fc033ec..87c26b9f1b5209 100644
--- a/clang/test/Analysis/block-in-critical-section.cpp
+++ b/clang/test/Analysis/block-in-critical-section.cpp
@@ -1,4 +1,8 @@
-// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.unix.BlockInCriticalSection -std=c++11 -verify %s
+// RUN: %clang_analyze_cc1 \
+// RUN: -analyzer-checker=alpha.unix.BlockInCriticalSection \
+// RUN: -std=c++11 \
+// RUN: -analyzer-output text \
+// RUN: -verify %s
void sleep(int x) {}
@@ -21,108 +25,242 @@ template<typename T>
struct not_real_lock {
not_real_lock<T>(std::mutex) {}
};
-}
+} // namespace std
+
+struct FILE;
+int getc(FILE *stream);
+char* fgets(char *str, FILE *stream);
+using ssize_t = long long;
+using size_t = unsigned long long;
+ssize_t read(int fd, void *buf, size_t count);
+ssize_t recv(int sockfd, void *buf, size_t len, int flags);
-void getc() {}
-void fgets() {}
-void read() {}
-void recv() {}
+struct pthread_mutex_t;
+void pthread_mutex_lock(pthread_mutex_t *mutex);
+void pthread_mutex_trylock(pthread_mutex_t *mutex);
+void pthread_mutex_unlock(pthread_mutex_t *mutex);
-void pthread_mutex_lock() {}
-void pthread_mutex_trylock() {}
-void pthread_mutex_unlock() {}
+struct mtx_t;
+void mtx_lock(mtx_t *mutex);
+void mtx_timedlock(mtx_t *mutex);
+void mtx_trylock(mtx_t *mutex);
+void mtx_unlock(mtx_t *mutex);
-void mtx_lock() {}
-void mtx_timedlock() {}
-void mtx_trylock() {}
-void mtx_unlock() {}
+// global params for dummy function calls
+FILE *stream;
+char *str;
+int fd;
+void *buf;
+size_t count;
+int sockfd;
+size_t len;
+int flags;
void testBlockInCriticalSectionWithStdMutex() {
std::mutex m;
- m.lock();
+ m.lock(); // expected-note 5{{Entering critical section here}}
sleep(3); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
- getc(); // expected-warning {{Call to blocking function 'getc' inside of critical section}}
- fgets(); // expected-warning {{Call to blocking function 'fgets' inside of critical section}}
- read(); // expected-warning {{Call to blocking function 'read' inside of critical section}}
- recv(); // expected-warning {{Call to blocking function 'recv' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
+ getc(stream); // expected-warning {{Call to blocking function 'getc' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'getc' inside of critical section}}
+ fgets(str, stream); // expected-warning {{Call to blocking function 'fgets' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'fgets' inside of critical section}}
+ read(fd, buf, count); // expected-warning {{Call to blocking function 'read' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'read' inside of critical section}}
+ recv(sockfd, buf, count, flags); // expected-warning {{Call to blocking function 'recv' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'recv' inside of critical section}}
m.unlock();
}
-void testBlockInCriticalSectionWithPthreadMutex() {
- pthread_mutex_lock();
+void testBlockInCriticalSectionWithPthreadMutex(pthread_mutex_t *mutex) {
+ pthread_mutex_lock(mutex); // expected-note 5{{Entering critical section here}}
sleep(3); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
- getc(); // expected-warning {{Call to blocking function 'getc' inside of critical section}}
- fgets(); // expected-warning {{Call to blocking function 'fgets' inside of critical section}}
- read(); // expected-warning {{Call to blocking function 'read' inside of critical section}}
- recv(); // expected-warning {{Call to blocking function 'recv' inside of critical section}}
- pthread_mutex_unlock();
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
+ getc(stream); // expected-warning {{Call to blocking function 'getc' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'getc' inside of critical section}}
+ fgets(str, stream); // expected-warning {{Call to blocking function 'fgets' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'fgets' inside of critical section}}
+ read(fd, buf, count); // expected-warning {{Call to blocking function 'read' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'read' inside of critical section}}
+ recv(sockfd, buf, count, flags); // expected-warning {{Call to blocking function 'recv' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'recv' inside of critical section}}
+ pthread_mutex_unlock(mutex);
- pthread_mutex_trylock();
+ pthread_mutex_trylock(mutex); // expected-note 5{{Entering critical section here}}
sleep(3); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
- getc(); // expected-warning {{Call to blocking function 'getc' inside of critical section}}
- fgets(); // expected-warning {{Call to blocking function 'fgets' inside of critical section}}
- read(); // expected-warning {{Call to blocking function 'read' inside of critical section}}
- recv(); // expected-warning {{Call to blocking function 'recv' inside of critical section}}
- pthread_mutex_unlock();
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
+ getc(stream); // expected-warning {{Call to blocking function 'getc' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'getc' inside of critical section}}
+ fgets(str, stream); // expected-warning {{Call to blocking function 'fgets' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'fgets' inside of critical section}}
+ read(fd, buf, count); // expected-warning {{Call to blocking function 'read' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'read' inside of critical section}}
+ recv(sockfd, buf, count, flags); // expected-warning {{Call to blocking function 'recv' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'recv' inside of critical section}}
+ pthread_mutex_unlock(mutex);
}
-void testBlockInCriticalSectionC11Locks() {
- mtx_lock();
+void testBlockInCriticalSectionC11Locks(mtx_t *mutex) {
+ mtx_lock(mutex); // expected-note 5{{Entering critical section here}}
sleep(3); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
- getc(); // expected-warning {{Call to blocking function 'getc' inside of critical section}}
- fgets(); // expected-warning {{Call to blocking function 'fgets' inside of critical section}}
- read(); // expected-warning {{Call to blocking function 'read' inside of critical section}}
- recv(); // expected-warning {{Call to blocking function 'recv' inside of critical section}}
- mtx_unlock();
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
+ getc(stream); // expected-warning {{Call to blocking function 'getc' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'getc' inside of critical section}}
+ fgets(str, stream); // expected-warning {{Call to blocking function 'fgets' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'fgets' inside of critical section}}
+ read(fd, buf, count); // expected-warning {{Call to blocking function 'read' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'read' inside of critical section}}
+ recv(sockfd, buf, count, flags); // expected-warning {{Call to blocking function 'recv' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'recv' inside of critical section}}
+ mtx_unlock(mutex);
- mtx_timedlock();
+ mtx_timedlock(mutex); // expected-note 5{{Entering critical section here}}
sleep(3); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
- getc(); // expected-warning {{Call to blocking function 'getc' inside of critical section}}
- fgets(); // expected-warning {{Call to blocking function 'fgets' inside of critical section}}
- read(); // expected-warning {{Call to blocking function 'read' inside of critical section}}
- recv(); // expected-warning {{Call to blocking function 'recv' inside of critical section}}
- mtx_unlock();
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
+ getc(stream); // expected-warning {{Call to blocking function 'getc' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'getc' inside of critical section}}
+ fgets(str, stream); // expected-warning {{Call to blocking function 'fgets' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'fgets' inside of critical section}}
+ read(fd, buf, count); // expected-warning {{Call to blocking function 'read' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'read' inside of critical section}}
+ recv(sockfd, buf, count, flags); // expected-warning {{Call to blocking function 'recv' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'recv' inside of critical section}}
+ mtx_unlock(mutex);
- mtx_trylock();
+ mtx_trylock(mutex); // expected-note 5{{Entering critical section here}}
sleep(3); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
- getc(); // expected-warning {{Call to blocking function 'getc' inside of critical section}}
- fgets(); // expected-warning {{Call to blocking function 'fgets' inside of critical section}}
- read(); // expected-warning {{Call to blocking function 'read' inside of critical section}}
- recv(); // expected-warning {{Call to blocking function 'recv' inside of critical section}}
- mtx_unlock();
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
+ getc(stream); // expected-warning {{Call to blocking function 'getc' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'getc' inside of critical section}}
+ fgets(str, stream); // expected-warning {{Call to blocking function 'fgets' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'fgets' inside of critical section}}
+ read(fd, buf, count); // expected-warning {{Call to blocking function 'read' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'read' inside of critical section}}
+ recv(sockfd, buf, count, flags); // expected-warning {{Call to blocking function 'recv' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'recv' inside of critical section}}
+ mtx_unlock(mutex);
+}
+
+void testMultipleBlockingCalls() {
+ std::mutex m;
+ m.lock(); // expected-note 1{{Entering critical section here}}
+ sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
+ m.unlock();
+ sleep(2); // no-warning
}
-void testBlockInCriticalSectionWithNestedMutexes() {
+void testMultipleMutexesMultipleBlockingCalls() {
std::mutex m, n, k;
- m.lock();
- n.lock();
- k.lock();
- sleep(3); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ m.lock(); // expected-note 2{{Entering critical section here}}
+ n.lock(); // expected-note 2{{Entering critical section here}}
+ k.lock(); // expected-note 1{{Entering critical section here}}
+ sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
k.unlock();
- sleep(5); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ sleep(2); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
+}
+
+
+void testRecursiveAcquisition() {
+ std::mutex m;
+ m.lock(); // expected-note {{Entering critical section for the 1st time here}}
+ m.lock(); // expected-note {{Entering critical section for the 2nd time here}}
+ sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
+ m.unlock();
+ m.unlock();
+}
+
+void testRecursiveAcquisitionWithMultipleBlockingCalls() {
+ std::mutex m;
+ m.lock(); // expected-note 1{{Entering critical section for the 1st time here}}
+ // expected-note at -1 {{Entering critical section here}}
+ m.lock(); // expected-note 1{{Entering critical section for the 2nd time here}}
+ sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
+ m.unlock();
+ // this next 'sleep' call is only in the critical section of the first lock
+ sleep(2); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
+ m.unlock();
+}
+
+void testRecursiveAcquisitionWithMultipleMutexes() {
+ std::mutex m, n;
+ m.lock(); // expected-note 1{{Entering critical section here}}
+ n.lock(); // expected-note 2{{Entering critical section here}}
+ sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
+ m.unlock();
+ // this next 'sleep' call is only in the critical section of mutex 'n'
+ sleep(2); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
+ n.unlock();
+}
+
+
+void testNestedMutexes() {
+ std::mutex m, n, k;
+ m.lock(); // expected-note 3{{Entering critical section here}}
+ n.lock(); // expected-note 2{{Entering critical section here}}
+ k.lock(); // expected-note 1{{Entering critical section here}}
+ sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
+ k.unlock();
+ sleep(2); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
n.unlock();
sleep(3); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
+ m.unlock();
+ sleep(4); // no-warning
+}
+
+void testNonOverlappingMutexes() {
+ std::mutex m;
+ m.lock(); // There should be no warning here
+ m.unlock();
+ m.lock(); // expected-note {{Entering critical section here}}
+ sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
+ m.unlock();
+}
+
+void testMixedMutexLocksWithIntermittentUnlock() {
+ std::mutex m, n, k;
+ m.lock(); // expected-note {{Entering critical section here}}
+ n.lock(); // the problem is not is this lock's critical section
+ n.unlock();
+ k.lock(); // same as for n.lock()
+ k.unlock();
+ sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
m.unlock();
- sleep(3); // no-warning
}
void f() {
sleep(1000); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
}
void testBlockInCriticalSectionInterProcedural() {
std::mutex m;
- m.lock();
- f();
+ m.lock(); // expected-note {{Entering critical section here}}
+ f(); // expected-note {{Calling 'f'}}
m.unlock();
}
+void unknown_function_that_may_lock(std::mutex &);
void testBlockInCriticalSectionUnexpectedUnlock() {
std::mutex m;
+ unknown_function_that_may_lock(m);
m.unlock();
sleep(1); // no-warning
- m.lock();
- sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ m.lock(); // expected-note {{Entering critical section here}}
+ sleep(2); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
}
void testBlockInCriticalSectionLockGuard() {
@@ -130,12 +268,13 @@ void testBlockInCriticalSectionLockGuard() {
std::not_real_lock<std::mutex> not_real_lock(g_mutex);
sleep(1); // no-warning
- std::lock_guard<std::mutex> lock(g_mutex);
+ std::lock_guard<std::mutex> lock(g_mutex); // expected-note {{Entering critical section here}}
sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
}
void testBlockInCriticalSectionLockGuardNested() {
- testBlockInCriticalSectionLockGuard();
+ testBlockInCriticalSectionLockGuard(); // expected-note {{Calling 'testBlockInCriticalSectionLockGuard'}}
sleep(1); // no-warning
}
@@ -144,11 +283,12 @@ void testBlockInCriticalSectionUniqueLock() {
std::not_real_lock<std::mutex> not_real_lock(g_mutex);
sleep(1); // no-warning
- std::unique_lock<std::mutex> lock(g_mutex);
+ std::unique_lock<std::mutex> lock(g_mutex); // expected-note {{Entering critical section here}}
sleep(1); // expected-warning {{Call to blocking function 'sleep' inside of critical section}}
+ // expected-note at -1 {{Call to blocking function 'sleep' inside of critical section}}
}
void testBlockInCriticalSectionUniqueLockNested() {
- testBlockInCriticalSectionUniqueLock();
+ testBlockInCriticalSectionUniqueLock(); // expected-note {{Calling 'testBlockInCriticalSectionUniqueLock'}}
sleep(1); // no-warning
}
>From 9a784303a3666e9d9b93088a1519e1dc2ba4d015 Mon Sep 17 00:00:00 2001
From: David Green <david.green at arm.com>
Date: Mon, 18 Mar 2024 17:04:31 +0000
Subject: [PATCH 33/40] [AArch64][GlobalISel] Legalize small G_TRUNC (#85625)
This is an alternative to #85610, that moreElement's small G_TRUNC
vectors to widen the vectors. It needs to disable one of the existing
Unmerge(Trunc(..)) combines, and some of the code is not as optimal as
it could be. I believe with some extra optimizations it could look
better (I was thinking combining trunc(buildvector) -> buildvector and
possibly improving buildvector lowering by generating
insert_vector_element earlier).
---
.../GlobalISel/LegalizationArtifactCombiner.h | 6 +-
.../AArch64/GISel/AArch64LegalizerInfo.cpp | 4 +-
.../GlobalISel/legalize-load-store.mir | 8 +-
.../AArch64/GlobalISel/legalize-xtn.mir | 14 +++-
llvm/test/CodeGen/AArch64/bitcast.ll | 74 ++++++++++++-------
llvm/test/CodeGen/AArch64/itofp.ll | 16 ++--
6 files changed, 80 insertions(+), 42 deletions(-)
diff --git a/llvm/include/llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h b/llvm/include/llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h
index da330b517c2801..ca62f38061b115 100644
--- a/llvm/include/llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h
+++ b/llvm/include/llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h
@@ -432,9 +432,13 @@ class LegalizationArtifactCombiner {
DestTy.isVector() ? CastSrcTy.getNumElements() / NumDefs : 1;
LLT UnmergeTy = CastSrcTy.changeElementCount(
ElementCount::getFixed(UnmergeNumElts));
+ LLT SrcWideTy =
+ SrcTy.changeElementCount(ElementCount::getFixed(UnmergeNumElts));
if (isInstUnsupported(
- {TargetOpcode::G_UNMERGE_VALUES, {UnmergeTy, CastSrcTy}}))
+ {TargetOpcode::G_UNMERGE_VALUES, {UnmergeTy, CastSrcTy}}) ||
+ LI.getAction({TargetOpcode::G_TRUNC, {SrcWideTy, UnmergeTy}})
+ .Action == LegalizeActions::MoreElements)
return false;
Builder.setInstr(MI);
diff --git a/llvm/lib/Target/AArch64/GISel/AArch64LegalizerInfo.cpp b/llvm/lib/Target/AArch64/GISel/AArch64LegalizerInfo.cpp
index 2ae2923dfb353e..996abe8e47396b 100644
--- a/llvm/lib/Target/AArch64/GISel/AArch64LegalizerInfo.cpp
+++ b/llvm/lib/Target/AArch64/GISel/AArch64LegalizerInfo.cpp
@@ -628,7 +628,9 @@ AArch64LegalizerInfo::AArch64LegalizerInfo(const AArch64Subtarget &ST)
return DstTy.isVector() && SrcTy.getSizeInBits() > 128 &&
DstTy.getScalarSizeInBits() * 2 <= SrcTy.getScalarSizeInBits();
})
-
+ .clampMinNumElements(0, s8, 8)
+ .clampMinNumElements(0, s16, 4)
+ .clampMinNumElements(0, s32, 2)
.alwaysLegal();
getActionDefinitionsBuilder(G_SEXT_INREG)
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/legalize-load-store.mir b/llvm/test/CodeGen/AArch64/GlobalISel/legalize-load-store.mir
index aa152aea81ff9c..b8328eda9a6618 100644
--- a/llvm/test/CodeGen/AArch64/GlobalISel/legalize-load-store.mir
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/legalize-load-store.mir
@@ -607,9 +607,11 @@ body: |
; CHECK-NEXT: [[LOAD1:%[0-9]+]]:_(s16) = G_LOAD [[PTR_ADD]](p0) :: (load (s16) from unknown-address + 2)
; CHECK-NEXT: [[ANYEXT:%[0-9]+]]:_(s32) = G_ANYEXT [[LOAD]](s16)
; CHECK-NEXT: [[ANYEXT1:%[0-9]+]]:_(s32) = G_ANYEXT [[LOAD1]](s16)
- ; CHECK-NEXT: [[BUILD_VECTOR:%[0-9]+]]:_(<2 x s32>) = G_BUILD_VECTOR [[ANYEXT]](s32), [[ANYEXT1]](s32)
- ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(<2 x s16>) = G_TRUNC [[BUILD_VECTOR]](<2 x s32>)
- ; CHECK-NEXT: $s0 = COPY [[TRUNC]](<2 x s16>)
+ ; CHECK-NEXT: [[DEF:%[0-9]+]]:_(s32) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: [[BUILD_VECTOR:%[0-9]+]]:_(<4 x s32>) = G_BUILD_VECTOR [[ANYEXT]](s32), [[ANYEXT1]](s32), [[DEF]](s32), [[DEF]](s32)
+ ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(<4 x s16>) = G_TRUNC [[BUILD_VECTOR]](<4 x s32>)
+ ; CHECK-NEXT: [[UV:%[0-9]+]]:_(<2 x s16>), [[UV1:%[0-9]+]]:_(<2 x s16>) = G_UNMERGE_VALUES [[TRUNC]](<4 x s16>)
+ ; CHECK-NEXT: $s0 = COPY [[UV]](<2 x s16>)
; CHECK-NEXT: RET_ReallyLR
%0:_(p0) = COPY $x0
%1(<2 x s16>) = G_LOAD %0(p0) :: (load (<2 x s16>))
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/legalize-xtn.mir b/llvm/test/CodeGen/AArch64/GlobalISel/legalize-xtn.mir
index 661265173ae82b..ed40a2ff7ea70f 100644
--- a/llvm/test/CodeGen/AArch64/GlobalISel/legalize-xtn.mir
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/legalize-xtn.mir
@@ -540,9 +540,17 @@ body: |
; CHECK: liveins: $d0
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: [[COPY:%[0-9]+]]:_(<2 x s32>) = COPY $d0
- ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(<2 x s8>) = G_TRUNC [[COPY]](<2 x s32>)
- ; CHECK-NEXT: [[CONCAT_VECTORS:%[0-9]+]]:_(<4 x s8>) = G_CONCAT_VECTORS [[TRUNC]](<2 x s8>), [[TRUNC]](<2 x s8>)
- ; CHECK-NEXT: [[ANYEXT:%[0-9]+]]:_(<4 x s16>) = G_ANYEXT [[CONCAT_VECTORS]](<4 x s8>)
+ ; CHECK-NEXT: [[UV:%[0-9]+]]:_(s32), [[UV1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[COPY]](<2 x s32>)
+ ; CHECK-NEXT: [[DEF:%[0-9]+]]:_(s32) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: [[BUILD_VECTOR:%[0-9]+]]:_(<4 x s32>) = G_BUILD_VECTOR [[UV]](s32), [[UV1]](s32), [[DEF]](s32), [[DEF]](s32)
+ ; CHECK-NEXT: [[BUILD_VECTOR1:%[0-9]+]]:_(<4 x s32>) = G_BUILD_VECTOR [[DEF]](s32), [[DEF]](s32), [[DEF]](s32), [[DEF]](s32)
+ ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(<4 x s16>) = G_TRUNC [[BUILD_VECTOR]](<4 x s32>)
+ ; CHECK-NEXT: [[TRUNC1:%[0-9]+]]:_(<4 x s16>) = G_TRUNC [[BUILD_VECTOR1]](<4 x s32>)
+ ; CHECK-NEXT: [[CONCAT_VECTORS:%[0-9]+]]:_(<8 x s16>) = G_CONCAT_VECTORS [[TRUNC]](<4 x s16>), [[TRUNC1]](<4 x s16>)
+ ; CHECK-NEXT: [[TRUNC2:%[0-9]+]]:_(<8 x s8>) = G_TRUNC [[CONCAT_VECTORS]](<8 x s16>)
+ ; CHECK-NEXT: [[UV2:%[0-9]+]]:_(<2 x s8>), [[UV3:%[0-9]+]]:_(<2 x s8>), [[UV4:%[0-9]+]]:_(<2 x s8>), [[UV5:%[0-9]+]]:_(<2 x s8>) = G_UNMERGE_VALUES [[TRUNC2]](<8 x s8>)
+ ; CHECK-NEXT: [[CONCAT_VECTORS1:%[0-9]+]]:_(<4 x s8>) = G_CONCAT_VECTORS [[UV2]](<2 x s8>), [[UV2]](<2 x s8>)
+ ; CHECK-NEXT: [[ANYEXT:%[0-9]+]]:_(<4 x s16>) = G_ANYEXT [[CONCAT_VECTORS1]](<4 x s8>)
; CHECK-NEXT: $d0 = COPY [[ANYEXT]](<4 x s16>)
; CHECK-NEXT: RET_ReallyLR implicit $d0
%0:_(<2 x s32>) = COPY $d0
diff --git a/llvm/test/CodeGen/AArch64/bitcast.ll b/llvm/test/CodeGen/AArch64/bitcast.ll
index 9ebd570e687a01..b7c02d6855a9e9 100644
--- a/llvm/test/CodeGen/AArch64/bitcast.ll
+++ b/llvm/test/CodeGen/AArch64/bitcast.ll
@@ -4,12 +4,10 @@
; PR23065: SCALAR_TO_VECTOR implies the top elements 1 to N-1 of the N-element vector are undefined.
-; CHECK-GI: warning: Instruction selection used fallback path for bitcast_v4i8_i32
-; CHECK-GI-NEXT: warning: Instruction selection used fallback path for bitcast_i32_v4i8
-; CHECK-GI-NEXT: warning: Instruction selection used fallback path for bitcast_v2i16_i32
-; CHECK-GI-NEXT: warning: Instruction selection used fallback path for bitcast_i32_v2i16
-; CHECK-GI-NEXT: warning: Instruction selection used fallback path for bitcast_v2i16_v4i8
-; CHECK-GI-NEXT: warning: Instruction selection used fallback path for bitcast_v4i8_v2i16
+; CHECK-GI: warning: Instruction selection used fallback path for bitcast_i32_v4i8
+; CHECK-GI-NEXT: warning: Instruction selection used fallback path for bitcast_i32_v2i16
+; CHECK-GI-NEXT: warning: Instruction selection used fallback path for bitcast_v2i16_v4i8
+; CHECK-GI-NEXT: warning: Instruction selection used fallback path for bitcast_v4i8_v2i16
define <4 x i16> @foo1(<2 x i32> %a) {
; CHECK-SD-LABEL: foo1:
@@ -54,15 +52,28 @@ define <4 x i16> @foo2(<2 x i32> %a) {
; ===== To and From Scalar Types =====
define i32 @bitcast_v4i8_i32(<4 x i8> %a, <4 x i8> %b){
-; CHECK-LABEL: bitcast_v4i8_i32:
-; CHECK: // %bb.0:
-; CHECK-NEXT: sub sp, sp, #16
-; CHECK-NEXT: .cfi_def_cfa_offset 16
-; CHECK-NEXT: add v0.4h, v0.4h, v1.4h
-; CHECK-NEXT: uzp1 v0.8b, v0.8b, v0.8b
-; CHECK-NEXT: fmov w0, s0
-; CHECK-NEXT: add sp, sp, #16
-; CHECK-NEXT: ret
+; CHECK-SD-LABEL: bitcast_v4i8_i32:
+; CHECK-SD: // %bb.0:
+; CHECK-SD-NEXT: sub sp, sp, #16
+; CHECK-SD-NEXT: .cfi_def_cfa_offset 16
+; CHECK-SD-NEXT: add v0.4h, v0.4h, v1.4h
+; CHECK-SD-NEXT: uzp1 v0.8b, v0.8b, v0.8b
+; CHECK-SD-NEXT: fmov w0, s0
+; CHECK-SD-NEXT: add sp, sp, #16
+; CHECK-SD-NEXT: ret
+;
+; CHECK-GI-LABEL: bitcast_v4i8_i32:
+; CHECK-GI: // %bb.0:
+; CHECK-GI-NEXT: add v0.4h, v0.4h, v1.4h
+; CHECK-GI-NEXT: mov h1, v0.h[1]
+; CHECK-GI-NEXT: mov h2, v0.h[2]
+; CHECK-GI-NEXT: mov h3, v0.h[3]
+; CHECK-GI-NEXT: mov v0.h[1], v1.h[0]
+; CHECK-GI-NEXT: mov v0.h[2], v2.h[0]
+; CHECK-GI-NEXT: mov v0.h[3], v3.h[0]
+; CHECK-GI-NEXT: xtn v0.8b, v0.8h
+; CHECK-GI-NEXT: fmov w0, s0
+; CHECK-GI-NEXT: ret
%c = add <4 x i8> %a, %b
%d = bitcast <4 x i8> %c to i32
ret i32 %d
@@ -81,18 +92,27 @@ define <4 x i8> @bitcast_i32_v4i8(i32 %a, i32 %b){
}
define i32 @bitcast_v2i16_i32(<2 x i16> %a, <2 x i16> %b){
-; CHECK-LABEL: bitcast_v2i16_i32:
-; CHECK: // %bb.0:
-; CHECK-NEXT: sub sp, sp, #16
-; CHECK-NEXT: .cfi_def_cfa_offset 16
-; CHECK-NEXT: add v0.2s, v0.2s, v1.2s
-; CHECK-NEXT: mov w8, v0.s[1]
-; CHECK-NEXT: fmov w9, s0
-; CHECK-NEXT: strh w9, [sp, #12]
-; CHECK-NEXT: strh w8, [sp, #14]
-; CHECK-NEXT: ldr w0, [sp, #12]
-; CHECK-NEXT: add sp, sp, #16
-; CHECK-NEXT: ret
+; CHECK-SD-LABEL: bitcast_v2i16_i32:
+; CHECK-SD: // %bb.0:
+; CHECK-SD-NEXT: sub sp, sp, #16
+; CHECK-SD-NEXT: .cfi_def_cfa_offset 16
+; CHECK-SD-NEXT: add v0.2s, v0.2s, v1.2s
+; CHECK-SD-NEXT: mov w8, v0.s[1]
+; CHECK-SD-NEXT: fmov w9, s0
+; CHECK-SD-NEXT: strh w9, [sp, #12]
+; CHECK-SD-NEXT: strh w8, [sp, #14]
+; CHECK-SD-NEXT: ldr w0, [sp, #12]
+; CHECK-SD-NEXT: add sp, sp, #16
+; CHECK-SD-NEXT: ret
+;
+; CHECK-GI-LABEL: bitcast_v2i16_i32:
+; CHECK-GI: // %bb.0:
+; CHECK-GI-NEXT: add v0.2s, v0.2s, v1.2s
+; CHECK-GI-NEXT: mov s1, v0.s[1]
+; CHECK-GI-NEXT: mov v0.s[1], v1.s[0]
+; CHECK-GI-NEXT: xtn v0.4h, v0.4s
+; CHECK-GI-NEXT: fmov w0, s0
+; CHECK-GI-NEXT: ret
%c = add <2 x i16> %a, %b
%d = bitcast <2 x i16> %c to i32
ret i32 %d
diff --git a/llvm/test/CodeGen/AArch64/itofp.ll b/llvm/test/CodeGen/AArch64/itofp.ll
index 2164c2aad20111..ceac37f91238a8 100644
--- a/llvm/test/CodeGen/AArch64/itofp.ll
+++ b/llvm/test/CodeGen/AArch64/itofp.ll
@@ -5521,7 +5521,8 @@ define <2 x half> @stofp_v2i8_v2f16(<2 x i8> %a) {
; CHECK-GI-FP16: // %bb.0: // %entry
; CHECK-GI-FP16-NEXT: // kill: def $d0 killed $d0 def $q0
; CHECK-GI-FP16-NEXT: mov s1, v0.s[1]
-; CHECK-GI-FP16-NEXT: mov v0.h[1], v1.h[0]
+; CHECK-GI-FP16-NEXT: mov v0.s[1], v1.s[0]
+; CHECK-GI-FP16-NEXT: xtn v0.4h, v0.4s
; CHECK-GI-FP16-NEXT: shl v0.4h, v0.4h, #8
; CHECK-GI-FP16-NEXT: sshr v0.4h, v0.4h, #8
; CHECK-GI-FP16-NEXT: mov h1, v0.h[1]
@@ -5580,12 +5581,13 @@ define <2 x half> @utofp_v2i8_v2f16(<2 x i8> %a) {
;
; CHECK-GI-FP16-LABEL: utofp_v2i8_v2f16:
; CHECK-GI-FP16: // %bb.0: // %entry
-; CHECK-GI-FP16-NEXT: movi d1, #0x0000ff000000ff
-; CHECK-GI-FP16-NEXT: and v0.8b, v0.8b, v1.8b
-; CHECK-GI-FP16-NEXT: mov s1, v0.s[1]
-; CHECK-GI-FP16-NEXT: mov v0.h[1], v1.h[0]
-; CHECK-GI-FP16-NEXT: ucvtf v0.4h, v0.4h
-; CHECK-GI-FP16-NEXT: mov h1, v0.h[1]
+; CHECK-GI-FP16-NEXT: // kill: def $d0 killed $d0 def $q0
+; CHECK-GI-FP16-NEXT: mov w8, v0.s[1]
+; CHECK-GI-FP16-NEXT: fmov w9, s0
+; CHECK-GI-FP16-NEXT: and w9, w9, #0xff
+; CHECK-GI-FP16-NEXT: and w8, w8, #0xff
+; CHECK-GI-FP16-NEXT: ucvtf h0, w9
+; CHECK-GI-FP16-NEXT: ucvtf h1, w8
; CHECK-GI-FP16-NEXT: mov v0.h[1], v1.h[0]
; CHECK-GI-FP16-NEXT: // kill: def $d0 killed $d0 killed $q0
; CHECK-GI-FP16-NEXT: ret
>From ece2903ce730392e5236d27f1f387fa8067fcb1b Mon Sep 17 00:00:00 2001
From: alx32 <103613512+alx32 at users.noreply.github.com>
Date: Mon, 18 Mar 2024 10:08:18 -0700
Subject: [PATCH 34/40] [lld-macho] Implement ObjC category merging
(-objc_category_merging) (#82928)
This change adds a flag to lld to enable category merging for MachoO +
ObjC.
It adds the '-objc_category_merging' flag for enabling this option and
uses the existing '-no_objc_category_merging' flag for disabling it.
In ld64, this optimization is enabled by default, but in lld, for now,
we require explicitly passing the '-objc_category_merging' flag in order
to enable it.
Behavior: if in the same link unit, multiple categories are extending
the same class, then they get merged into a single category.
Ex: `Cat1(method1+method2,protocol1) + Cat2(method3+method4,protocol2,
property1) = Cat1_2(method1+method2+method3+method4,
protocol1+protocol2, property1)`
Notes on implementation decisions made in this diff:
1. There is a possibility to further improve the current implementation
by directly merging the category data into the base class (if the base
class is present in the link unit) - this improvement may be done as a
follow-up. This improved functionality is already present in ld64.
2. We do the merging on the raw inputSections - after dead-stripping
(categories can't be dead stripped anyway).
3. The changes are mostly self-contained to ObjC.cpp, except for adding
a new flag (linkerOptimizeReason) to ConcatInputSection and StringPiece
to mark that this data has been optimized away. Another way to do it
would have been to just mark the pieces as not 'live' but this would
cause the old symbols to show up in the linker map as being
dead-stripped - even if dead-stripping is disabled. This flag allows us
to match the ld64 behavior.
---------
Co-authored-by: Alex B <alexborcan at meta.com>
---
lld/MachO/Driver.cpp | 9 +
lld/MachO/InputSection.h | 2 +-
lld/MachO/ObjC.cpp | 920 +++++++++++++++++-
lld/MachO/ObjC.h | 12 +
lld/MachO/Options.td | 10 +-
.../objc-category-merging-complete-test.s | 762 +++++++++++++++
...jc-category-merging-extern-class-minimal.s | 155 +++
7 files changed, 1862 insertions(+), 8 deletions(-)
create mode 100644 lld/test/MachO/objc-category-merging-complete-test.s
create mode 100644 lld/test/MachO/objc-category-merging-extern-class-minimal.s
diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp
index 9edb6b9c60a1fe..36248925d65ad2 100644
--- a/lld/MachO/Driver.cpp
+++ b/lld/MachO/Driver.cpp
@@ -1437,6 +1437,8 @@ bool link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
resetOutputSegments();
resetWriter();
InputFile::resetIdCount();
+
+ objc::doCleanup();
};
ctx->e.logName = args::getFilenameWithoutExe(argsArr[0]);
@@ -1979,9 +1981,16 @@ bool link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
if (config->deadStrip)
markLive();
+ // Categories are not subject to dead-strip. The __objc_catlist section is
+ // marked as NO_DEAD_STRIP and that propagates into all category data.
if (args.hasArg(OPT_check_category_conflicts))
objc::checkCategories();
+ // Category merging uses "->live = false" to erase old category data, so
+ // it has to run after dead-stripping (markLive).
+ if (args.hasArg(OPT_objc_category_merging, OPT_no_objc_category_merging))
+ objc::mergeCategories();
+
// ICF assumes that all literals have been folded already, so we must run
// foldIdenticalLiterals before foldIdenticalSections.
foldIdenticalLiterals();
diff --git a/lld/MachO/InputSection.h b/lld/MachO/InputSection.h
index becb01017d633a..b25f0638f4c6cb 100644
--- a/lld/MachO/InputSection.h
+++ b/lld/MachO/InputSection.h
@@ -93,9 +93,9 @@ class InputSection {
// .subsections_via_symbols, there is typically only one element here.
llvm::TinyPtrVector<Defined *> symbols;
-protected:
const Section §ion;
+protected:
const Defined *getContainingSymbol(uint64_t off) const;
};
diff --git a/lld/MachO/ObjC.cpp b/lld/MachO/ObjC.cpp
index 67254ec53a2145..9b2446360e4f7f 100644
--- a/lld/MachO/ObjC.cpp
+++ b/lld/MachO/ObjC.cpp
@@ -7,16 +7,19 @@
//===----------------------------------------------------------------------===//
#include "ObjC.h"
+#include "ConcatOutputSection.h"
#include "InputFiles.h"
#include "InputSection.h"
#include "Layout.h"
#include "OutputSegment.h"
+#include "SyntheticSections.h"
#include "Target.h"
#include "lld/Common/ErrorHandler.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/BinaryFormat/MachO.h"
#include "llvm/Bitcode/BitcodeReader.h"
+#include "llvm/Support/TimeProfiler.h"
using namespace llvm;
using namespace llvm::MachO;
@@ -78,7 +81,8 @@ namespace {
DO(Ptr, classMethods) \
DO(Ptr, protocols) \
DO(Ptr, instanceProps) \
- DO(Ptr, classProps)
+ DO(Ptr, classProps) \
+ DO(uint32_t, size)
CREATE_LAYOUT_CLASS(Category, FOR_EACH_CATEGORY_FIELD);
@@ -112,13 +116,19 @@ CREATE_LAYOUT_CLASS(ROClass, FOR_EACH_RO_CLASS_FIELD);
#undef FOR_EACH_RO_CLASS_FIELD
#define FOR_EACH_LIST_HEADER(DO) \
- DO(uint32_t, size) \
- DO(uint32_t, count)
+ DO(uint32_t, structSize) \
+ DO(uint32_t, structCount)
CREATE_LAYOUT_CLASS(ListHeader, FOR_EACH_LIST_HEADER);
#undef FOR_EACH_LIST_HEADER
+#define FOR_EACH_PROTOCOL_LIST_HEADER(DO) DO(Ptr, protocolCount)
+
+CREATE_LAYOUT_CLASS(ProtocolListHeader, FOR_EACH_PROTOCOL_LIST_HEADER);
+
+#undef FOR_EACH_PROTOCOL_LIST_HEADER
+
#define FOR_EACH_METHOD(DO) \
DO(Ptr, name) \
DO(Ptr, type) \
@@ -311,6 +321,8 @@ void ObjcCategoryChecker::parseClass(const Defined *classSym) {
}
void objc::checkCategories() {
+ TimeTraceScope timeScope("ObjcCategoryChecker");
+
ObjcCategoryChecker checker;
for (const InputSection *isec : inputSections) {
if (isec->getName() == section_names::objcCatList)
@@ -320,3 +332,905 @@ void objc::checkCategories() {
}
}
}
+
+namespace {
+
+class ObjcCategoryMerger {
+ // Information about an input category
+ struct InfoInputCategory {
+ ConcatInputSection *catListIsec;
+ ConcatInputSection *catBodyIsec;
+ uint32_t offCatListIsec = 0;
+
+ bool wasMerged = false;
+ };
+
+ // To write new (merged) categories or classes, we will try make limited
+ // assumptions about the alignment and the sections the various class/category
+ // info are stored in and . So we'll just reuse the same sections and
+ // alignment as already used in existing (input) categories. To do this we
+ // have InfoCategoryWriter which contains the various sections that the
+ // generated categories will be written to.
+ template <typename T> struct InfoWriteSection {
+ bool valid = false; // Data has been successfully collected from input
+ uint32_t align = 0;
+ Section *inputSection;
+ Reloc relocTemplate;
+ T *outputSection;
+ };
+
+ struct InfoCategoryWriter {
+ InfoWriteSection<ConcatOutputSection> catListInfo;
+ InfoWriteSection<ConcatOutputSection> catBodyInfo;
+ InfoWriteSection<CStringSection> catNameInfo;
+ InfoWriteSection<ConcatOutputSection> catPtrListInfo;
+ };
+
+ // Information about a pointer list in the original categories (method lists,
+ // protocol lists, etc)
+ struct PointerListInfo {
+ PointerListInfo(const char *_categoryPrefix, uint32_t _categoryOffset,
+ uint32_t _pointersPerStruct)
+ : categoryPrefix(_categoryPrefix), categoryOffset(_categoryOffset),
+ pointersPerStruct(_pointersPerStruct) {}
+ const char *categoryPrefix;
+ uint32_t categoryOffset = 0;
+
+ uint32_t pointersPerStruct = 0;
+
+ uint32_t structSize = 0;
+ uint32_t structCount = 0;
+
+ std::vector<Symbol *> allPtrs;
+ };
+
+ // Full information about all the categories that extend a class. This will
+ // include all the additional methods, protocols, and properties that are
+ // contained in all the categories that extend a particular class.
+ struct ClassExtensionInfo {
+ ClassExtensionInfo(CategoryLayout &_catLayout) : catLayout(_catLayout){};
+
+ // Merged names of containers. Ex: base|firstCategory|secondCategory|...
+ std::string mergedContainerName;
+ std::string baseClassName;
+ Symbol *baseClass = nullptr;
+ CategoryLayout &catLayout;
+
+ // In case we generate new data, mark the new data as belonging to this file
+ ObjFile *objFileForMergeData = nullptr;
+
+ PointerListInfo instanceMethods = {
+ objc::symbol_names::categoryInstanceMethods,
+ /*_categoryOffset=*/catLayout.instanceMethodsOffset,
+ /*pointersPerStruct=*/3};
+ PointerListInfo classMethods = {
+ objc::symbol_names::categoryClassMethods,
+ /*_categoryOffset=*/catLayout.classMethodsOffset,
+ /*pointersPerStruct=*/3};
+ PointerListInfo protocols = {objc::symbol_names::categoryProtocols,
+ /*_categoryOffset=*/catLayout.protocolsOffset,
+ /*pointersPerStruct=*/0};
+ PointerListInfo instanceProps = {
+ objc::symbol_names::listProprieties,
+ /*_categoryOffset=*/catLayout.instancePropsOffset,
+ /*pointersPerStruct=*/2};
+ PointerListInfo classProps = {
+ objc::symbol_names::klassPropList,
+ /*_categoryOffset=*/catLayout.classPropsOffset,
+ /*pointersPerStruct=*/2};
+ };
+
+public:
+ ObjcCategoryMerger(std::vector<ConcatInputSection *> &_allInputSections);
+ void doMerge();
+ static void doCleanup();
+
+private:
+ void collectAndValidateCategoriesData();
+ void
+ mergeCategoriesIntoSingleCategory(std::vector<InfoInputCategory> &categories);
+
+ void eraseISec(ConcatInputSection *isec);
+ void eraseMergedCategories();
+
+ void generateCatListForNonErasedCategories(
+ std::map<ConcatInputSection *, std::set<uint64_t>>
+ catListToErasedOffsets);
+ template <typename T>
+ void collectSectionWriteInfoFromIsec(const InputSection *isec,
+ InfoWriteSection<T> &catWriteInfo);
+ void collectCategoryWriterInfoFromCategory(const InfoInputCategory &catInfo);
+ void parseCatInfoToExtInfo(const InfoInputCategory &catInfo,
+ ClassExtensionInfo &extInfo);
+
+ void parseProtocolListInfo(const ConcatInputSection *isec, uint32_t secOffset,
+ PointerListInfo &ptrList);
+
+ void parsePointerListInfo(const ConcatInputSection *isec, uint32_t secOffset,
+ PointerListInfo &ptrList);
+
+ void emitAndLinkPointerList(Defined *parentSym, uint32_t linkAtOffset,
+ const ClassExtensionInfo &extInfo,
+ const PointerListInfo &ptrList);
+
+ void emitAndLinkProtocolList(Defined *parentSym, uint32_t linkAtOffset,
+ const ClassExtensionInfo &extInfo,
+ const PointerListInfo &ptrList);
+
+ Defined *emitCategory(const ClassExtensionInfo &extInfo);
+ Defined *emitCatListEntrySec(const std::string &forCateogryName,
+ const std::string &forBaseClassName,
+ ObjFile *objFile);
+ Defined *emitCategoryBody(const std::string &name, const Defined *nameSym,
+ const Symbol *baseClassSym,
+ const std::string &baseClassName, ObjFile *objFile);
+ Defined *emitCategoryName(const std::string &name, ObjFile *objFile);
+ void createSymbolReference(Defined *refFrom, const Symbol *refTo,
+ uint32_t offset, const Reloc &relocTemplate);
+ Symbol *tryGetSymbolAtIsecOffset(const ConcatInputSection *isec,
+ uint32_t offset);
+ Defined *tryGetDefinedAtIsecOffset(const ConcatInputSection *isec,
+ uint32_t offset);
+ void tryEraseDefinedAtIsecOffset(const ConcatInputSection *isec,
+ uint32_t offset);
+
+ // Allocate a null-terminated StringRef backed by generatedSectionData
+ StringRef newStringData(const char *str);
+ // Allocate section data, backed by generatedSectionData
+ SmallVector<uint8_t> &newSectionData(uint32_t size);
+
+ CategoryLayout catLayout;
+ ClassLayout classLayout;
+ ROClassLayout roClassLayout;
+ ListHeaderLayout listHeaderLayout;
+ MethodLayout methodLayout;
+ ProtocolListHeaderLayout protocolListHeaderLayout;
+
+ InfoCategoryWriter infoCategoryWriter;
+ std::vector<ConcatInputSection *> &allInputSections;
+ // Map of base class Symbol to list of InfoInputCategory's for it
+ DenseMap<const Symbol *, std::vector<InfoInputCategory>> categoryMap;
+
+ // Normally, the binary data comes from the input files, but since we're
+ // generating binary data ourselves, we use the below array to store it in.
+ // Need this to be 'static' so the data survives past the ObjcCategoryMerger
+ // object, as the data will be read by the Writer when the final binary is
+ // generated.
+ static SmallVector<SmallVector<uint8_t>> generatedSectionData;
+};
+
+SmallVector<SmallVector<uint8_t>> ObjcCategoryMerger::generatedSectionData;
+
+ObjcCategoryMerger::ObjcCategoryMerger(
+ std::vector<ConcatInputSection *> &_allInputSections)
+ : catLayout(target->wordSize), classLayout(target->wordSize),
+ roClassLayout(target->wordSize), listHeaderLayout(target->wordSize),
+ methodLayout(target->wordSize),
+ protocolListHeaderLayout(target->wordSize),
+ allInputSections(_allInputSections) {}
+
+// This is a template so that it can be used both for CStringSection and
+// ConcatOutputSection
+template <typename T>
+void ObjcCategoryMerger::collectSectionWriteInfoFromIsec(
+ const InputSection *isec, InfoWriteSection<T> &catWriteInfo) {
+
+ catWriteInfo.inputSection = const_cast<Section *>(&isec->section);
+ catWriteInfo.align = isec->align;
+ catWriteInfo.outputSection = dyn_cast_or_null<T>(isec->parent);
+
+ assert(catWriteInfo.outputSection &&
+ "outputSection may not be null in collectSectionWriteInfoFromIsec.");
+
+ if (isec->relocs.size())
+ catWriteInfo.relocTemplate = isec->relocs[0];
+
+ catWriteInfo.valid = true;
+}
+
+Symbol *
+ObjcCategoryMerger::tryGetSymbolAtIsecOffset(const ConcatInputSection *isec,
+ uint32_t offset) {
+ const Reloc *reloc = isec->getRelocAt(offset);
+
+ if (!reloc)
+ return nullptr;
+
+ return reloc->referent.get<Symbol *>();
+}
+
+Defined *
+ObjcCategoryMerger::tryGetDefinedAtIsecOffset(const ConcatInputSection *isec,
+ uint32_t offset) {
+ Symbol *sym = tryGetSymbolAtIsecOffset(isec, offset);
+ return dyn_cast_or_null<Defined>(sym);
+}
+
+// Given an ConcatInputSection or CStringInputSection and an offset, if there is
+// a symbol(Defined) at that offset, then erase the symbol (mark it not live)
+void ObjcCategoryMerger::tryEraseDefinedAtIsecOffset(
+ const ConcatInputSection *isec, uint32_t offset) {
+ const Reloc *reloc = isec->getRelocAt(offset);
+
+ if (!reloc)
+ return;
+
+ Defined *sym = dyn_cast_or_null<Defined>(reloc->referent.get<Symbol *>());
+ if (!sym)
+ return;
+
+ if (auto *cisec = dyn_cast_or_null<ConcatInputSection>(sym->isec))
+ eraseISec(cisec);
+ else if (auto *csisec = dyn_cast_or_null<CStringInputSection>(sym->isec)) {
+ uint32_t totalOffset = sym->value + reloc->addend;
+ StringPiece &piece = csisec->getStringPiece(totalOffset);
+ piece.live = false;
+ } else {
+ llvm_unreachable("erased symbol has to be Defined or CStringInputSection");
+ }
+}
+
+void ObjcCategoryMerger::collectCategoryWriterInfoFromCategory(
+ const InfoInputCategory &catInfo) {
+
+ if (!infoCategoryWriter.catListInfo.valid)
+ collectSectionWriteInfoFromIsec<ConcatOutputSection>(
+ catInfo.catListIsec, infoCategoryWriter.catListInfo);
+ if (!infoCategoryWriter.catBodyInfo.valid)
+ collectSectionWriteInfoFromIsec<ConcatOutputSection>(
+ catInfo.catBodyIsec, infoCategoryWriter.catBodyInfo);
+
+ if (!infoCategoryWriter.catNameInfo.valid) {
+ lld::macho::Defined *catNameSym =
+ tryGetDefinedAtIsecOffset(catInfo.catBodyIsec, catLayout.nameOffset);
+ assert(catNameSym && "Category does not have a valid name Symbol");
+
+ collectSectionWriteInfoFromIsec<CStringSection>(
+ catNameSym->isec, infoCategoryWriter.catNameInfo);
+ }
+
+ // Collect writer info from all the category lists (we're assuming they all
+ // would provide the same info)
+ if (!infoCategoryWriter.catPtrListInfo.valid) {
+ for (uint32_t off = catLayout.instanceMethodsOffset;
+ off <= catLayout.classPropsOffset; off += target->wordSize) {
+ if (Defined *ptrList =
+ tryGetDefinedAtIsecOffset(catInfo.catBodyIsec, off)) {
+ collectSectionWriteInfoFromIsec<ConcatOutputSection>(
+ ptrList->isec, infoCategoryWriter.catPtrListInfo);
+ // we've successfully collected data, so we can break
+ break;
+ }
+ }
+ }
+}
+
+// Parse a protocol list that might be linked to ConcatInputSection at a given
+// offset. The format of the protocol list is different than other lists (prop
+// lists, method lists) so we need to parse it differently
+void ObjcCategoryMerger::parseProtocolListInfo(const ConcatInputSection *isec,
+ uint32_t secOffset,
+ PointerListInfo &ptrList) {
+ if (!isec || (secOffset + target->wordSize > isec->data.size()))
+ assert("Tried to read pointer list beyond protocol section end");
+
+ const Reloc *reloc = isec->getRelocAt(secOffset);
+ if (!reloc)
+ return;
+
+ auto *ptrListSym = dyn_cast_or_null<Defined>(reloc->referent.get<Symbol *>());
+ assert(ptrListSym && "Protocol list reloc does not have a valid Defined");
+
+ // Theoretically protocol count can be either 32b or 64b, depending on
+ // platform pointer size, but to simplify implementation we always just read
+ // the lower 32b which should be good enough.
+ uint32_t protocolCount = *reinterpret_cast<const uint32_t *>(
+ ptrListSym->isec->data.data() + listHeaderLayout.structSizeOffset);
+
+ ptrList.structCount += protocolCount;
+ ptrList.structSize = target->wordSize;
+
+ uint32_t expectedListSize =
+ (protocolCount * target->wordSize) +
+ /*header(count)*/ protocolListHeaderLayout.totalSize +
+ /*extra null value*/ target->wordSize;
+ assert(expectedListSize == ptrListSym->isec->data.size() &&
+ "Protocol list does not match expected size");
+
+ uint32_t off = protocolListHeaderLayout.totalSize;
+ for (uint32_t inx = 0; inx < protocolCount; ++inx) {
+ const Reloc *reloc = ptrListSym->isec->getRelocAt(off);
+ assert(reloc && "No reloc found at protocol list offset");
+
+ auto *listSym = dyn_cast_or_null<Defined>(reloc->referent.get<Symbol *>());
+ assert(listSym && "Protocol list reloc does not have a valid Defined");
+
+ ptrList.allPtrs.push_back(listSym);
+ off += target->wordSize;
+ }
+ assert((ptrListSym->isec->getRelocAt(off) == nullptr) &&
+ "expected null terminating protocol");
+ assert(off + /*extra null value*/ target->wordSize == expectedListSize &&
+ "Protocol list end offset does not match expected size");
+}
+
+// Parse a pointer list that might be linked to ConcatInputSection at a given
+// offset. This can be used for instance methods, class methods, instance props
+// and class props since they have the same format.
+void ObjcCategoryMerger::parsePointerListInfo(const ConcatInputSection *isec,
+ uint32_t secOffset,
+ PointerListInfo &ptrList) {
+ assert(ptrList.pointersPerStruct == 2 || ptrList.pointersPerStruct == 3);
+ assert(isec && "Trying to parse pointer list from null isec");
+ assert(secOffset + target->wordSize <= isec->data.size() &&
+ "Trying to read pointer list beyond section end");
+
+ const Reloc *reloc = isec->getRelocAt(secOffset);
+ if (!reloc)
+ return;
+
+ auto *ptrListSym = dyn_cast_or_null<Defined>(reloc->referent.get<Symbol *>());
+ assert(ptrListSym && "Reloc does not have a valid Defined");
+
+ uint32_t thisStructSize = *reinterpret_cast<const uint32_t *>(
+ ptrListSym->isec->data.data() + listHeaderLayout.structSizeOffset);
+ uint32_t thisStructCount = *reinterpret_cast<const uint32_t *>(
+ ptrListSym->isec->data.data() + listHeaderLayout.structCountOffset);
+ assert(thisStructSize == ptrList.pointersPerStruct * target->wordSize);
+
+ assert(!ptrList.structSize || (thisStructSize == ptrList.structSize));
+
+ ptrList.structCount += thisStructCount;
+ ptrList.structSize = thisStructSize;
+
+ uint32_t expectedListSize =
+ listHeaderLayout.totalSize + (thisStructSize * thisStructCount);
+ assert(expectedListSize == ptrListSym->isec->data.size() &&
+ "Pointer list does not match expected size");
+
+ for (uint32_t off = listHeaderLayout.totalSize; off < expectedListSize;
+ off += target->wordSize) {
+ const Reloc *reloc = ptrListSym->isec->getRelocAt(off);
+ assert(reloc && "No reloc found at pointer list offset");
+
+ auto *listSym = dyn_cast_or_null<Defined>(reloc->referent.get<Symbol *>());
+ assert(listSym && "Reloc does not have a valid Defined");
+
+ ptrList.allPtrs.push_back(listSym);
+ }
+}
+
+// Here we parse all the information of an input category (catInfo) and
+// append the parsed info into the structure which will contain all the
+// information about how a class is extended (extInfo)
+void ObjcCategoryMerger::parseCatInfoToExtInfo(const InfoInputCategory &catInfo,
+ ClassExtensionInfo &extInfo) {
+ const Reloc *catNameReloc =
+ catInfo.catBodyIsec->getRelocAt(catLayout.nameOffset);
+
+ // Parse name
+ assert(catNameReloc && "Category does not have a reloc at 'nameOffset'");
+
+ // is this the first category we are parsing?
+ if (extInfo.mergedContainerName.empty())
+ extInfo.objFileForMergeData =
+ dyn_cast_or_null<ObjFile>(catInfo.catBodyIsec->getFile());
+ else
+ extInfo.mergedContainerName += "|";
+
+ assert(extInfo.objFileForMergeData &&
+ "Expected to already have valid objextInfo.objFileForMergeData");
+
+ StringRef catName = getReferentString(*catNameReloc);
+ extInfo.mergedContainerName += catName.str();
+
+ // Parse base class
+ if (!extInfo.baseClass) {
+ Symbol *classSym =
+ tryGetSymbolAtIsecOffset(catInfo.catBodyIsec, catLayout.klassOffset);
+ assert(extInfo.baseClassName.empty());
+ extInfo.baseClass = classSym;
+ llvm::StringRef classPrefix(objc::symbol_names::klass);
+ assert(classSym->getName().starts_with(classPrefix) &&
+ "Base class symbol does not start with expected prefix");
+ extInfo.baseClassName = classSym->getName().substr(classPrefix.size());
+ } else {
+ assert((extInfo.baseClass ==
+ tryGetSymbolAtIsecOffset(catInfo.catBodyIsec,
+ catLayout.klassOffset)) &&
+ "Trying to parse category info into container with different base "
+ "class");
+ }
+
+ parsePointerListInfo(catInfo.catBodyIsec, catLayout.instanceMethodsOffset,
+ extInfo.instanceMethods);
+
+ parsePointerListInfo(catInfo.catBodyIsec, catLayout.classMethodsOffset,
+ extInfo.classMethods);
+
+ parseProtocolListInfo(catInfo.catBodyIsec, catLayout.protocolsOffset,
+ extInfo.protocols);
+
+ parsePointerListInfo(catInfo.catBodyIsec, catLayout.instancePropsOffset,
+ extInfo.instanceProps);
+
+ parsePointerListInfo(catInfo.catBodyIsec, catLayout.classPropsOffset,
+ extInfo.classProps);
+}
+
+// Generate a protocol list (including header) and link it into the parent at
+// the specified offset.
+void ObjcCategoryMerger::emitAndLinkProtocolList(
+ Defined *parentSym, uint32_t linkAtOffset,
+ const ClassExtensionInfo &extInfo, const PointerListInfo &ptrList) {
+ if (ptrList.allPtrs.empty())
+ return;
+
+ assert(ptrList.allPtrs.size() == ptrList.structCount);
+
+ uint32_t bodySize = (ptrList.structCount * target->wordSize) +
+ /*header(count)*/ protocolListHeaderLayout.totalSize +
+ /*extra null value*/ target->wordSize;
+ llvm::ArrayRef<uint8_t> bodyData = newSectionData(bodySize);
+
+ // This theoretically can be either 32b or 64b, but writing just the first 32b
+ // is good enough
+ const uint32_t *ptrProtoCount = reinterpret_cast<const uint32_t *>(
+ bodyData.data() + protocolListHeaderLayout.protocolCountOffset);
+
+ *const_cast<uint32_t *>(ptrProtoCount) = ptrList.allPtrs.size();
+
+ ConcatInputSection *listSec = make<ConcatInputSection>(
+ *infoCategoryWriter.catPtrListInfo.inputSection, bodyData,
+ infoCategoryWriter.catPtrListInfo.align);
+ listSec->parent = infoCategoryWriter.catPtrListInfo.outputSection;
+ listSec->live = true;
+ allInputSections.push_back(listSec);
+
+ listSec->parent = infoCategoryWriter.catPtrListInfo.outputSection;
+
+ std::string symName = ptrList.categoryPrefix;
+ symName += extInfo.baseClassName + "_$_(" + extInfo.mergedContainerName + ")";
+
+ Defined *ptrListSym = make<Defined>(
+ newStringData(symName.c_str()), /*file=*/parentSym->getObjectFile(),
+ listSec, /*value=*/0, bodyData.size(), /*isWeakDef=*/false,
+ /*isExternal=*/false, /*isPrivateExtern=*/false, /*includeInSymtab=*/true,
+ /*isReferencedDynamically=*/false, /*noDeadStrip=*/false,
+ /*isWeakDefCanBeHidden=*/false);
+
+ ptrListSym->used = true;
+ parentSym->getObjectFile()->symbols.push_back(ptrListSym);
+
+ createSymbolReference(parentSym, ptrListSym, linkAtOffset,
+ infoCategoryWriter.catBodyInfo.relocTemplate);
+
+ uint32_t offset = protocolListHeaderLayout.totalSize;
+ for (Symbol *symbol : ptrList.allPtrs) {
+ createSymbolReference(ptrListSym, symbol, offset,
+ infoCategoryWriter.catPtrListInfo.relocTemplate);
+ offset += target->wordSize;
+ }
+}
+
+// Generate a pointer list (including header) and link it into the parent at the
+// specified offset. This is used for instance and class methods and
+// proprieties.
+void ObjcCategoryMerger::emitAndLinkPointerList(
+ Defined *parentSym, uint32_t linkAtOffset,
+ const ClassExtensionInfo &extInfo, const PointerListInfo &ptrList) {
+ if (ptrList.allPtrs.empty())
+ return;
+
+ assert(ptrList.allPtrs.size() * target->wordSize ==
+ ptrList.structCount * ptrList.structSize);
+
+ // Generate body
+ uint32_t bodySize =
+ listHeaderLayout.totalSize + (ptrList.structSize * ptrList.structCount);
+ llvm::ArrayRef<uint8_t> bodyData = newSectionData(bodySize);
+
+ const uint32_t *ptrStructSize = reinterpret_cast<const uint32_t *>(
+ bodyData.data() + listHeaderLayout.structSizeOffset);
+ const uint32_t *ptrStructCount = reinterpret_cast<const uint32_t *>(
+ bodyData.data() + listHeaderLayout.structCountOffset);
+
+ *const_cast<uint32_t *>(ptrStructSize) = ptrList.structSize;
+ *const_cast<uint32_t *>(ptrStructCount) = ptrList.structCount;
+
+ ConcatInputSection *listSec = make<ConcatInputSection>(
+ *infoCategoryWriter.catPtrListInfo.inputSection, bodyData,
+ infoCategoryWriter.catPtrListInfo.align);
+ listSec->parent = infoCategoryWriter.catPtrListInfo.outputSection;
+ listSec->live = true;
+ allInputSections.push_back(listSec);
+
+ listSec->parent = infoCategoryWriter.catPtrListInfo.outputSection;
+
+ std::string symName = ptrList.categoryPrefix;
+ symName += extInfo.baseClassName + "_$_" + extInfo.mergedContainerName;
+
+ Defined *ptrListSym = make<Defined>(
+ newStringData(symName.c_str()), /*file=*/parentSym->getObjectFile(),
+ listSec, /*value=*/0, bodyData.size(), /*isWeakDef=*/false,
+ /*isExternal=*/false, /*isPrivateExtern=*/false, /*includeInSymtab=*/true,
+ /*isReferencedDynamically=*/false, /*noDeadStrip=*/false,
+ /*isWeakDefCanBeHidden=*/false);
+
+ ptrListSym->used = true;
+ parentSym->getObjectFile()->symbols.push_back(ptrListSym);
+
+ createSymbolReference(parentSym, ptrListSym, linkAtOffset,
+ infoCategoryWriter.catBodyInfo.relocTemplate);
+
+ uint32_t offset = listHeaderLayout.totalSize;
+ for (Symbol *symbol : ptrList.allPtrs) {
+ createSymbolReference(ptrListSym, symbol, offset,
+ infoCategoryWriter.catPtrListInfo.relocTemplate);
+ offset += target->wordSize;
+ }
+}
+
+// This method creates an __objc_catlist ConcatInputSection with a single slot
+Defined *
+ObjcCategoryMerger::emitCatListEntrySec(const std::string &forCateogryName,
+ const std::string &forBaseClassName,
+ ObjFile *objFile) {
+ uint32_t sectionSize = target->wordSize;
+ llvm::ArrayRef<uint8_t> bodyData = newSectionData(sectionSize);
+
+ ConcatInputSection *newCatList =
+ make<ConcatInputSection>(*infoCategoryWriter.catListInfo.inputSection,
+ bodyData, infoCategoryWriter.catListInfo.align);
+ newCatList->parent = infoCategoryWriter.catListInfo.outputSection;
+ newCatList->live = true;
+ allInputSections.push_back(newCatList);
+
+ newCatList->parent = infoCategoryWriter.catListInfo.outputSection;
+
+ std::string catSymName = "<__objc_catlist slot for merged category ";
+ catSymName += forBaseClassName + "(" + forCateogryName + ")>";
+
+ Defined *catListSym = make<Defined>(
+ newStringData(catSymName.c_str()), /*file=*/objFile, newCatList,
+ /*value=*/0, bodyData.size(), /*isWeakDef=*/false, /*isExternal=*/false,
+ /*isPrivateExtern=*/false, /*includeInSymtab=*/false,
+ /*isReferencedDynamically=*/false, /*noDeadStrip=*/false,
+ /*isWeakDefCanBeHidden=*/false);
+
+ catListSym->used = true;
+ objFile->symbols.push_back(catListSym);
+ return catListSym;
+}
+
+// Here we generate the main category body and link the name and base class into
+// it. We don't link any other info yet like the protocol and class/instance
+// methods/props.
+Defined *ObjcCategoryMerger::emitCategoryBody(const std::string &name,
+ const Defined *nameSym,
+ const Symbol *baseClassSym,
+ const std::string &baseClassName,
+ ObjFile *objFile) {
+ llvm::ArrayRef<uint8_t> bodyData = newSectionData(catLayout.totalSize);
+
+ uint32_t *ptrSize = (uint32_t *)(const_cast<uint8_t *>(bodyData.data()) +
+ catLayout.sizeOffset);
+ *ptrSize = catLayout.totalSize;
+
+ ConcatInputSection *newBodySec =
+ make<ConcatInputSection>(*infoCategoryWriter.catBodyInfo.inputSection,
+ bodyData, infoCategoryWriter.catBodyInfo.align);
+ newBodySec->parent = infoCategoryWriter.catBodyInfo.outputSection;
+ newBodySec->live = true;
+ allInputSections.push_back(newBodySec);
+
+ std::string symName =
+ objc::symbol_names::category + baseClassName + "_$_(" + name + ")";
+ Defined *catBodySym = make<Defined>(
+ newStringData(symName.c_str()), /*file=*/objFile, newBodySec,
+ /*value=*/0, bodyData.size(), /*isWeakDef=*/false, /*isExternal=*/false,
+ /*isPrivateExtern=*/false, /*includeInSymtab=*/true,
+ /*isReferencedDynamically=*/false, /*noDeadStrip=*/false,
+ /*isWeakDefCanBeHidden=*/false);
+
+ catBodySym->used = true;
+ objFile->symbols.push_back(catBodySym);
+
+ createSymbolReference(catBodySym, nameSym, catLayout.nameOffset,
+ infoCategoryWriter.catBodyInfo.relocTemplate);
+
+ // Create a reloc to the base class (either external or internal)
+ createSymbolReference(catBodySym, baseClassSym, catLayout.klassOffset,
+ infoCategoryWriter.catBodyInfo.relocTemplate);
+
+ return catBodySym;
+}
+
+// This writes the new category name (for the merged category) into the binary
+// and returns the sybmol for it.
+Defined *ObjcCategoryMerger::emitCategoryName(const std::string &name,
+ ObjFile *objFile) {
+ StringRef nameStrData = newStringData(name.c_str());
+ // We use +1 below to include the null terminator
+ llvm::ArrayRef<uint8_t> nameData(
+ reinterpret_cast<const uint8_t *>(nameStrData.data()),
+ nameStrData.size() + 1);
+
+ auto *parentSection = infoCategoryWriter.catNameInfo.inputSection;
+ CStringInputSection *newStringSec = make<CStringInputSection>(
+ *infoCategoryWriter.catNameInfo.inputSection, nameData,
+ infoCategoryWriter.catNameInfo.align, /*dedupLiterals=*/true);
+
+ parentSection->subsections.push_back({0, newStringSec});
+
+ newStringSec->splitIntoPieces();
+ newStringSec->pieces[0].live = true;
+ newStringSec->parent = infoCategoryWriter.catNameInfo.outputSection;
+ in.cStringSection->addInput(newStringSec);
+ assert(newStringSec->pieces.size() == 1);
+
+ Defined *catNameSym = make<Defined>(
+ "<merged category name>", /*file=*/objFile, newStringSec,
+ /*value=*/0, nameData.size(),
+ /*isWeakDef=*/false, /*isExternal=*/false, /*isPrivateExtern=*/false,
+ /*includeInSymtab=*/false, /*isReferencedDynamically=*/false,
+ /*noDeadStrip=*/false, /*isWeakDefCanBeHidden=*/false);
+
+ catNameSym->used = true;
+ objFile->symbols.push_back(catNameSym);
+ return catNameSym;
+}
+
+// This method fully creates a new category from the given ClassExtensionInfo.
+// It creates the category name, body and method/protocol/prop lists and links
+// them all together. Then it creates a new __objc_catlist entry and adds the
+// category to it. Calling this method will fully generate a category which will
+// be available in the final binary.
+Defined *ObjcCategoryMerger::emitCategory(const ClassExtensionInfo &extInfo) {
+ Defined *catNameSym = emitCategoryName(extInfo.mergedContainerName,
+ extInfo.objFileForMergeData);
+
+ Defined *catBodySym = emitCategoryBody(
+ extInfo.mergedContainerName, catNameSym, extInfo.baseClass,
+ extInfo.baseClassName, extInfo.objFileForMergeData);
+
+ Defined *catListSym =
+ emitCatListEntrySec(extInfo.mergedContainerName, extInfo.baseClassName,
+ extInfo.objFileForMergeData);
+
+ // Add the single category body to the category list at the offset 0.
+ createSymbolReference(catListSym, catBodySym, /*offset=*/0,
+ infoCategoryWriter.catListInfo.relocTemplate);
+
+ emitAndLinkPointerList(catBodySym, catLayout.instanceMethodsOffset, extInfo,
+ extInfo.instanceMethods);
+
+ emitAndLinkPointerList(catBodySym, catLayout.classMethodsOffset, extInfo,
+ extInfo.classMethods);
+
+ emitAndLinkProtocolList(catBodySym, catLayout.protocolsOffset, extInfo,
+ extInfo.protocols);
+
+ emitAndLinkPointerList(catBodySym, catLayout.instancePropsOffset, extInfo,
+ extInfo.instanceProps);
+
+ emitAndLinkPointerList(catBodySym, catLayout.classPropsOffset, extInfo,
+ extInfo.classProps);
+
+ return catBodySym;
+}
+
+// This method merges all the categories (sharing a base class) into a single
+// category.
+void ObjcCategoryMerger::mergeCategoriesIntoSingleCategory(
+ std::vector<InfoInputCategory> &categories) {
+ assert(categories.size() > 1 && "Expected at least 2 categories");
+
+ ClassExtensionInfo extInfo(catLayout);
+
+ for (auto &catInfo : categories)
+ parseCatInfoToExtInfo(catInfo, extInfo);
+
+ Defined *newCatDef = emitCategory(extInfo);
+ assert(newCatDef && "Failed to create a new category");
+
+ for (auto &catInfo : categories)
+ catInfo.wasMerged = true;
+}
+
+void ObjcCategoryMerger::createSymbolReference(Defined *refFrom,
+ const Symbol *refTo,
+ uint32_t offset,
+ const Reloc &relocTemplate) {
+ Reloc r = relocTemplate;
+ r.offset = offset;
+ r.addend = 0;
+ r.referent = const_cast<Symbol *>(refTo);
+ refFrom->isec->relocs.push_back(r);
+}
+
+void ObjcCategoryMerger::collectAndValidateCategoriesData() {
+ for (InputSection *sec : allInputSections) {
+ if (sec->getName() != section_names::objcCatList)
+ continue;
+ ConcatInputSection *catListCisec = dyn_cast<ConcatInputSection>(sec);
+ assert(catListCisec &&
+ "__objc_catList InputSection is not a ConcatInputSection");
+
+ for (uint32_t off = 0; off < catListCisec->getSize();
+ off += target->wordSize) {
+ Defined *categorySym = tryGetDefinedAtIsecOffset(catListCisec, off);
+ assert(categorySym &&
+ "Failed to get a valid cateogry at __objc_catlit offset");
+
+ // We only support ObjC categories (no swift + @objc)
+ // TODO: Support swift + @objc categories also
+ if (!categorySym->getName().starts_with(objc::symbol_names::category))
+ continue;
+
+ auto *catBodyIsec = dyn_cast<ConcatInputSection>(categorySym->isec);
+ assert(catBodyIsec &&
+ "Category data section is not an ConcatInputSection");
+
+ // Check that the category has a reloc at 'klassOffset' (which is
+ // a pointer to the class symbol)
+
+ Symbol *classSym =
+ tryGetSymbolAtIsecOffset(catBodyIsec, catLayout.klassOffset);
+ assert(classSym && "Category does not have a valid base class");
+
+ InfoInputCategory catInputInfo{catListCisec, catBodyIsec, off};
+ categoryMap[classSym].push_back(catInputInfo);
+
+ collectCategoryWriterInfoFromCategory(catInputInfo);
+ }
+ }
+}
+
+// In the input we have multiple __objc_catlist InputSection, each of which may
+// contain links to multiple categories. Of these categories, we will merge (and
+// erase) only some. There will be some categories that will remain untouched
+// (not erased). For these not erased categories, we generate new __objc_catlist
+// entries since the parent __objc_catlist entry will be erased
+void ObjcCategoryMerger::generateCatListForNonErasedCategories(
+ const std::map<ConcatInputSection *, std::set<uint64_t>>
+ catListToErasedOffsets) {
+
+ // Go through all offsets of all __objc_catlist's that we process and if there
+ // are categories that we didn't process - generate a new __objc_catlist for
+ // each.
+ for (auto &mapEntry : catListToErasedOffsets) {
+ ConcatInputSection *catListIsec = mapEntry.first;
+ for (uint32_t catListIsecOffset = 0;
+ catListIsecOffset < catListIsec->data.size();
+ catListIsecOffset += target->wordSize) {
+ // This slot was erased, we can just skip it
+ if (mapEntry.second.count(catListIsecOffset))
+ continue;
+
+ Defined *nonErasedCatBody =
+ tryGetDefinedAtIsecOffset(catListIsec, catListIsecOffset);
+ assert(nonErasedCatBody && "Failed to relocate non-deleted category");
+
+ // Allocate data for the new __objc_catlist slot
+ auto bodyData = newSectionData(target->wordSize);
+
+ // We mark the __objc_catlist slot as belonging to the same file as the
+ // category
+ ObjFile *objFile = dyn_cast<ObjFile>(nonErasedCatBody->getFile());
+
+ ConcatInputSection *listSec = make<ConcatInputSection>(
+ *infoCategoryWriter.catListInfo.inputSection, bodyData,
+ infoCategoryWriter.catListInfo.align);
+ listSec->parent = infoCategoryWriter.catListInfo.outputSection;
+ listSec->live = true;
+ allInputSections.push_back(listSec);
+
+ std::string slotSymName = "<__objc_catlist slot for category ";
+ slotSymName += nonErasedCatBody->getName();
+ slotSymName += ">";
+
+ Defined *catListSlotSym = make<Defined>(
+ newStringData(slotSymName.c_str()), /*file=*/objFile, listSec,
+ /*value=*/0, bodyData.size(),
+ /*isWeakDef=*/false, /*isExternal=*/false, /*isPrivateExtern=*/false,
+ /*includeInSymtab=*/false, /*isReferencedDynamically=*/false,
+ /*noDeadStrip=*/false, /*isWeakDefCanBeHidden=*/false);
+
+ catListSlotSym->used = true;
+ objFile->symbols.push_back(catListSlotSym);
+
+ // Now link the category body into the newly created slot
+ createSymbolReference(catListSlotSym, nonErasedCatBody, 0,
+ infoCategoryWriter.catListInfo.relocTemplate);
+ }
+ }
+}
+
+void ObjcCategoryMerger::eraseISec(ConcatInputSection *isec) {
+ isec->live = false;
+ for (auto &sym : isec->symbols)
+ sym->used = false;
+}
+
+// This fully erases the merged categories, including their body, their names,
+// their method/protocol/prop lists and the __objc_catlist entries that link to
+// them.
+void ObjcCategoryMerger::eraseMergedCategories() {
+ // Map of InputSection to a set of offsets of the categories that were merged
+ std::map<ConcatInputSection *, std::set<uint64_t>> catListToErasedOffsets;
+
+ for (auto &mapEntry : categoryMap) {
+ for (InfoInputCategory &catInfo : mapEntry.second) {
+ if (catInfo.wasMerged) {
+ eraseISec(catInfo.catListIsec);
+ catListToErasedOffsets[catInfo.catListIsec].insert(
+ catInfo.offCatListIsec);
+ }
+ }
+ }
+
+ // If there were categories that we did not erase, we need to generate a new
+ // __objc_catList that contains only the un-merged categories, and get rid of
+ // the references to the ones we merged.
+ generateCatListForNonErasedCategories(catListToErasedOffsets);
+
+ // Erase the old method lists & names of the categories that were merged
+ for (auto &mapEntry : categoryMap) {
+ for (InfoInputCategory &catInfo : mapEntry.second) {
+ if (!catInfo.wasMerged)
+ continue;
+
+ eraseISec(catInfo.catBodyIsec);
+ tryEraseDefinedAtIsecOffset(catInfo.catBodyIsec, catLayout.nameOffset);
+ tryEraseDefinedAtIsecOffset(catInfo.catBodyIsec,
+ catLayout.instanceMethodsOffset);
+ tryEraseDefinedAtIsecOffset(catInfo.catBodyIsec,
+ catLayout.classMethodsOffset);
+ tryEraseDefinedAtIsecOffset(catInfo.catBodyIsec,
+ catLayout.protocolsOffset);
+ tryEraseDefinedAtIsecOffset(catInfo.catBodyIsec,
+ catLayout.classPropsOffset);
+ tryEraseDefinedAtIsecOffset(catInfo.catBodyIsec,
+ catLayout.instancePropsOffset);
+ }
+ }
+}
+
+void ObjcCategoryMerger::doMerge() {
+ collectAndValidateCategoriesData();
+
+ for (auto &entry : categoryMap)
+ if (entry.second.size() > 1)
+ // Merge all categories into a new, single category
+ mergeCategoriesIntoSingleCategory(entry.second);
+
+ // Erase all categories that were merged
+ eraseMergedCategories();
+}
+
+void ObjcCategoryMerger::doCleanup() { generatedSectionData.clear(); }
+
+StringRef ObjcCategoryMerger::newStringData(const char *str) {
+ uint32_t len = strlen(str);
+ auto &data = newSectionData(len + 1);
+ char *strData = reinterpret_cast<char *>(data.data());
+ strncpy(strData, str, len);
+ return StringRef(strData, len);
+}
+
+SmallVector<uint8_t> &ObjcCategoryMerger::newSectionData(uint32_t size) {
+ generatedSectionData.push_back(SmallVector<uint8_t>(size, 0));
+ return generatedSectionData.back();
+}
+
+} // namespace
+
+void objc::mergeCategories() {
+ TimeTraceScope timeScope("ObjcCategoryMerger");
+
+ ObjcCategoryMerger merger(inputSections);
+ merger.doMerge();
+}
+
+void objc::doCleanup() { ObjcCategoryMerger::doCleanup(); }
diff --git a/lld/MachO/ObjC.h b/lld/MachO/ObjC.h
index 4c65f9a1f78812..9fbe984e6223ec 100644
--- a/lld/MachO/ObjC.h
+++ b/lld/MachO/ObjC.h
@@ -17,14 +17,26 @@ namespace objc {
namespace symbol_names {
constexpr const char klass[] = "_OBJC_CLASS_$_";
+constexpr const char klassPropList[] = "__OBJC_$_CLASS_PROP_LIST_";
+
constexpr const char metaclass[] = "_OBJC_METACLASS_$_";
constexpr const char ehtype[] = "_OBJC_EHTYPE_$_";
constexpr const char ivar[] = "_OBJC_IVAR_$_";
+constexpr const char listProprieties[] = "__OBJC_$_PROP_LIST_";
+
+constexpr const char category[] = "__OBJC_$_CATEGORY_";
+constexpr const char categoryInstanceMethods[] =
+ "__OBJC_$_CATEGORY_INSTANCE_METHODS_";
+constexpr const char categoryClassMethods[] =
+ "__OBJC_$_CATEGORY_CLASS_METHODS_";
+constexpr const char categoryProtocols[] = "__OBJC_CATEGORY_PROTOCOLS_$_";
} // namespace symbol_names
// Check for duplicate method names within related categories / classes.
void checkCategories();
+void mergeCategories();
+void doCleanup();
} // namespace objc
bool hasObjCSection(llvm::MemoryBufferRef);
diff --git a/lld/MachO/Options.td b/lld/MachO/Options.td
index a524e4a4c50841..0d8ee2a0926be2 100644
--- a/lld/MachO/Options.td
+++ b/lld/MachO/Options.td
@@ -129,6 +129,12 @@ def strict_auto_link : Flag<["--"], "strict-auto-link">,
def check_category_conflicts : Flag<["--"], "check-category-conflicts">,
HelpText<"Check for conflicts between category & class methods">,
Group<grp_lld>;
+def objc_category_merging : Flag<["-"], "objc_category_merging">,
+ HelpText<"Merge Objective-C categories that share the same base class">,
+ Group<grp_lld>;
+def no_objc_category_merging : Flag<["-"], "no_objc_category_merging">,
+ HelpText<"Do not merge Objective-C categories">,
+ Group<grp_lld>;
def lto_debug_pass_manager: Flag<["--"], "lto-debug-pass-manager">,
HelpText<"Debug new pass manager">, Group<grp_lld>;
def cs_profile_generate: Flag<["--"], "cs-profile-generate">,
@@ -966,10 +972,6 @@ def interposable_list : Separate<["-"], "interposable_list">,
def no_function_starts : Flag<["-"], "no_function_starts">,
HelpText<"Do not create table of function start addresses">,
Group<grp_rare>;
-def no_objc_category_merging : Flag<["-"], "no_objc_category_merging">,
- HelpText<"Do not merge Objective-C categories into their classes">,
- Flags<[HelpHidden]>,
- Group<grp_rare>;
def object_path_lto : Separate<["-"], "object_path_lto">,
MetaVarName<"<path>">,
HelpText<"Retain any temporary mach-o file in <path> that would otherwise be deleted during LTO">,
diff --git a/lld/test/MachO/objc-category-merging-complete-test.s b/lld/test/MachO/objc-category-merging-complete-test.s
new file mode 100644
index 00000000000000..3bc3ca26b6ae6c
--- /dev/null
+++ b/lld/test/MachO/objc-category-merging-complete-test.s
@@ -0,0 +1,762 @@
+# REQUIRES: aarch64
+# RUN: rm -rf %t; split-file %s %t && cd %t
+
+## Create a dylib to link against(a64_file1.dylib) and merge categories in the main binary (file2_merge_a64.exe)
+# RUN: llvm-mc -filetype=obj -triple=arm64-apple-macos -o a64_file1.o a64_file1.s
+# RUN: %lld -arch arm64 a64_file1.o -o a64_file1.dylib -dylib
+
+# RUN: llvm-mc -filetype=obj -triple=arm64-apple-macos -o a64_file2.o a64_file2.s
+# RUN: %lld -arch arm64 -o a64_file2_no_merge.exe a64_file1.dylib a64_file2.o
+# RUN: %lld -arch arm64 -o a64_file2_merge.exe -objc_category_merging a64_file1.dylib a64_file2.o
+
+# RUN: llvm-objdump --objc-meta-data --macho a64_file2_no_merge.exe | FileCheck %s --check-prefixes=NO_MERGE_CATS
+# RUN: llvm-objdump --objc-meta-data --macho a64_file2_merge.exe | FileCheck %s --check-prefixes=MERGE_CATS
+
+
+MERGE_CATS: __OBJC_$_CATEGORY_MyBaseClass_$_(Category02|Category03)
+MERGE_CATS-NEXT: name {{.*}} Category02|Category03
+MERGE_CATS: instanceMethods
+MERGE_CATS-NEXT: entsize 24
+MERGE_CATS-NEXT: count 4
+MERGE_CATS-NEXT: name {{.*}} class02InstanceMethod
+MERGE_CATS-NEXT: types {{.*}} v16 at 0:8
+MERGE_CATS-NEXT: imp -[MyBaseClass(Category02) class02InstanceMethod]
+MERGE_CATS-NEXT: name {{.*}} myProtocol02Method
+MERGE_CATS-NEXT: types {{.*}} v16 at 0:8
+MERGE_CATS-NEXT: imp -[MyBaseClass(Category02) myProtocol02Method]
+MERGE_CATS-NEXT: name {{.*}} class03InstanceMethod
+MERGE_CATS-NEXT: types {{.*}} v16 at 0:8
+MERGE_CATS-NEXT: imp -[MyBaseClass(Category03) class03InstanceMethod]
+MERGE_CATS-NEXT: name {{.*}} myProtocol03Method
+MERGE_CATS-NEXT: types {{.*}} v16 at 0:8
+MERGE_CATS-NEXT: imp -[MyBaseClass(Category03) myProtocol03Method]
+MERGE_CATS-NEXT: classMethods {{.*}}
+MERGE_CATS-NEXT: entsize 24
+MERGE_CATS-NEXT: count 4
+MERGE_CATS-NEXT: name {{.*}} class02ClassMethod
+MERGE_CATS-NEXT: types {{.*}} v16 at 0:8
+MERGE_CATS-NEXT: imp +[MyBaseClass(Category02) class02ClassMethod]
+MERGE_CATS-NEXT: name {{.*}} MyProtocol02Prop
+MERGE_CATS-NEXT: types {{.*}} i16 at 0:8
+MERGE_CATS-NEXT: imp +[MyBaseClass(Category02) MyProtocol02Prop]
+MERGE_CATS-NEXT: name {{.*}} class03ClassMethod
+MERGE_CATS-NEXT: types {{.*}} v16 at 0:8
+MERGE_CATS-NEXT: imp +[MyBaseClass(Category03) class03ClassMethod]
+MERGE_CATS-NEXT: name {{.*}} MyProtocol03Prop
+MERGE_CATS-NEXT: types {{.*}} i16 at 0:8
+MERGE_CATS-NEXT: imp +[MyBaseClass(Category03) MyProtocol03Prop]
+MERGE_CATS-NEXT: protocols
+MERGE_CATS-NEXT: count 2
+MERGE_CATS-NEXT: list[0] {{.*}} (struct protocol_t *)
+MERGE_CATS-NEXT: isa 0x0
+MERGE_CATS-NEXT: name {{.*}} MyProtocol02
+MERGE_CATS-NEXT: protocols 0x0
+MERGE_CATS-NEXT: instanceMethods
+MERGE_CATS-NEXT: entsize 24
+MERGE_CATS-NEXT: count 2
+MERGE_CATS-NEXT: name {{.*}} myProtocol02Method
+MERGE_CATS-NEXT: types {{.*}} v16 at 0:8
+MERGE_CATS-NEXT: imp 0x0
+MERGE_CATS-NEXT: name {{.*}} MyProtocol02Prop
+MERGE_CATS-NEXT: types {{.*}} i16 at 0:8
+MERGE_CATS-NEXT: imp 0x0
+MERGE_CATS-NEXT: classMethods
+MERGE_CATS-NEXT: optionalInstanceMethods 0x0
+MERGE_CATS-NEXT: optionalClassMethods 0x0
+MERGE_CATS-NEXT: instanceProperties {{.*}}
+MERGE_CATS-NEXT: list[1] {{.*}}
+MERGE_CATS-NEXT: isa 0x0
+MERGE_CATS-NEXT: name {{.*}} MyProtocol03
+MERGE_CATS-NEXT: protocols 0x0
+MERGE_CATS-NEXT: instanceMethods
+MERGE_CATS-NEXT: entsize 24
+MERGE_CATS-NEXT: count 2
+MERGE_CATS-NEXT: name {{.*}} myProtocol03Method
+MERGE_CATS-NEXT: types {{.*}} v16 at 0:8
+MERGE_CATS-NEXT: imp 0x0
+MERGE_CATS-NEXT: name {{.*}} MyProtocol03Prop
+MERGE_CATS-NEXT: types {{.*}} i16 at 0:8
+MERGE_CATS-NEXT: imp 0x0
+MERGE_CATS-NEXT: classMethods 0x0
+MERGE_CATS-NEXT: optionalInstanceMethods 0x0
+MERGE_CATS-NEXT: optionalClassMethods 0x0
+MERGE_CATS-NEXT: instanceProperties {{.*}}
+MERGE_CATS-NEXT: instanceProperties
+MERGE_CATS-NEXT: entsize 16
+MERGE_CATS-NEXT: count 2
+MERGE_CATS-NEXT: name {{.*}} MyProtocol02Prop
+MERGE_CATS-NEXT: attributes {{.*}} Ti,R,D
+MERGE_CATS-NEXT: name {{.*}} MyProtocol03Prop
+MERGE_CATS-NEXT: attributes {{.*}} Ti,R,D
+
+
+NO_MERGE_CATS-NOT: __OBJC_$_CATEGORY_MyBaseClass_$_(Category02|Category03)
+NO_MERGE_CATS: __OBJC_$_CATEGORY_MyBaseClass_$_Category02
+NO_MERGE_CATS: instanceMethods
+NO_MERGE_CATS-NEXT: 24
+NO_MERGE_CATS-NEXT: 2
+NO_MERGE_CATS: classMethods
+NO_MERGE_CATS-NEXT: 24
+NO_MERGE_CATS-NEXT: 2
+
+
+#--- a64_file1.s
+
+## @protocol MyProtocol01
+## - (void)myProtocol01Method;
+## @property (nonatomic) int MyProtocol01Prop;
+## @end
+##
+## __attribute__((objc_root_class))
+## @interface MyBaseClass<MyProtocol01>
+## - (void)baseInstanceMethod;
+## - (void)myProtocol01Method;
+## + (void)baseClassMethod;
+## @end
+##
+## @implementation MyBaseClass
+## @synthesize MyProtocol01Prop;
+## - (void)baseInstanceMethod {}
+## - (void)myProtocol01Method {}
+## + (void)baseClassMethod {}
+## @end
+##
+## void *_objc_empty_cache;
+
+ .section __TEXT,__text,regular,pure_instructions
+ .p2align 2 ; -- Begin function -[MyBaseClass baseInstanceMethod]
+"-[MyBaseClass baseInstanceMethod]": ; @"\01-[MyBaseClass baseInstanceMethod]"
+ .cfi_startproc
+; %bb.0: ; %entry
+ ret
+ .cfi_endproc
+ ; -- End function
+ .p2align 2 ; -- Begin function -[MyBaseClass myProtocol01Method]
+"-[MyBaseClass myProtocol01Method]": ; @"\01-[MyBaseClass myProtocol01Method]"
+ .cfi_startproc
+; %bb.0: ; %entry
+ ret
+ .cfi_endproc
+ ; -- End function
+ .p2align 2 ; -- Begin function +[MyBaseClass baseClassMethod]
+"+[MyBaseClass baseClassMethod]": ; @"\01+[MyBaseClass baseClassMethod]"
+ .cfi_startproc
+; %bb.0: ; %entry
+ ret
+ .cfi_endproc
+ ; -- End function
+ .p2align 2 ; -- Begin function -[MyBaseClass MyProtocol01Prop]
+"-[MyBaseClass MyProtocol01Prop]": ; @"\01-[MyBaseClass MyProtocol01Prop]"
+ .cfi_startproc
+; %bb.0: ; %entry
+Lloh0:
+ adrp x8, _OBJC_IVAR_$_MyBaseClass.MyProtocol01Prop at PAGE
+Lloh1:
+ ldrsw x8, [x8, _OBJC_IVAR_$_MyBaseClass.MyProtocol01Prop at PAGEOFF]
+ ldr w0, [x0, x8]
+ ret
+ .loh AdrpLdr Lloh0, Lloh1
+ .cfi_endproc
+ ; -- End function
+ .p2align 2 ; -- Begin function -[MyBaseClass setMyProtocol01Prop:]
+"-[MyBaseClass setMyProtocol01Prop:]": ; @"\01-[MyBaseClass setMyProtocol01Prop:]"
+ .cfi_startproc
+; %bb.0: ; %entry
+Lloh2:
+ adrp x8, _OBJC_IVAR_$_MyBaseClass.MyProtocol01Prop at PAGE
+Lloh3:
+ ldrsw x8, [x8, _OBJC_IVAR_$_MyBaseClass.MyProtocol01Prop at PAGEOFF]
+ str w2, [x0, x8]
+ ret
+ .loh AdrpLdr Lloh2, Lloh3
+ .cfi_endproc
+ ; -- End function
+ .private_extern _OBJC_IVAR_$_MyBaseClass.MyProtocol01Prop ; @"OBJC_IVAR_$_MyBaseClass.MyProtocol01Prop"
+ .section __DATA,__objc_ivar
+ .globl _OBJC_IVAR_$_MyBaseClass.MyProtocol01Prop
+ .p2align 2, 0x0
+_OBJC_IVAR_$_MyBaseClass.MyProtocol01Prop:
+ .long 0 ; 0x0
+ .section __DATA,__objc_data
+ .globl _OBJC_CLASS_$_MyBaseClass ; @"OBJC_CLASS_$_MyBaseClass"
+ .p2align 3, 0x0
+_OBJC_CLASS_$_MyBaseClass:
+ .quad _OBJC_METACLASS_$_MyBaseClass
+ .quad 0
+ .quad __objc_empty_cache
+ .quad 0
+ .quad __OBJC_CLASS_RO_$_MyBaseClass
+ .globl _OBJC_METACLASS_$_MyBaseClass ; @"OBJC_METACLASS_$_MyBaseClass"
+ .p2align 3, 0x0
+_OBJC_METACLASS_$_MyBaseClass:
+ .quad _OBJC_METACLASS_$_MyBaseClass
+ .quad _OBJC_CLASS_$_MyBaseClass
+ .quad __objc_empty_cache
+ .quad 0
+ .quad __OBJC_METACLASS_RO_$_MyBaseClass
+ .section __TEXT,__objc_classname,cstring_literals
+l_OBJC_CLASS_NAME_: ; @OBJC_CLASS_NAME_
+ .asciz "MyBaseClass"
+ .section __TEXT,__objc_methname,cstring_literals
+l_OBJC_METH_VAR_NAME_: ; @OBJC_METH_VAR_NAME_
+ .asciz "baseClassMethod"
+ .section __TEXT,__objc_methtype,cstring_literals
+l_OBJC_METH_VAR_TYPE_: ; @OBJC_METH_VAR_TYPE_
+ .asciz "v16 at 0:8"
+ .section __DATA,__objc_const
+ .p2align 3, 0x0 ; @"_OBJC_$_CLASS_METHODS_MyBaseClass"
+__OBJC_$_CLASS_METHODS_MyBaseClass:
+ .long 24 ; 0x18
+ .long 1 ; 0x1
+ .quad l_OBJC_METH_VAR_NAME_
+ .quad l_OBJC_METH_VAR_TYPE_
+ .quad "+[MyBaseClass baseClassMethod]"
+ .section __TEXT,__objc_classname,cstring_literals
+l_OBJC_CLASS_NAME_.1: ; @OBJC_CLASS_NAME_.1
+ .asciz "MyProtocol01"
+ .section __TEXT,__objc_methname,cstring_literals
+l_OBJC_METH_VAR_NAME_.2: ; @OBJC_METH_VAR_NAME_.2
+ .asciz "myProtocol01Method"
+l_OBJC_METH_VAR_NAME_.3: ; @OBJC_METH_VAR_NAME_.3
+ .asciz "MyProtocol01Prop"
+ .section __TEXT,__objc_methtype,cstring_literals
+l_OBJC_METH_VAR_TYPE_.4: ; @OBJC_METH_VAR_TYPE_.4
+ .asciz "i16 at 0:8"
+ .section __TEXT,__objc_methname,cstring_literals
+l_OBJC_METH_VAR_NAME_.5: ; @OBJC_METH_VAR_NAME_.5
+ .asciz "setMyProtocol01Prop:"
+ .section __TEXT,__objc_methtype,cstring_literals
+l_OBJC_METH_VAR_TYPE_.6: ; @OBJC_METH_VAR_TYPE_.6
+ .asciz "v20 at 0:8i16"
+ .section __DATA,__objc_const
+ .p2align 3, 0x0 ; @"_OBJC_$_PROTOCOL_INSTANCE_METHODS_MyProtocol01"
+__OBJC_$_PROTOCOL_INSTANCE_METHODS_MyProtocol01:
+ .long 24 ; 0x18
+ .long 3 ; 0x3
+ .quad l_OBJC_METH_VAR_NAME_.2
+ .quad l_OBJC_METH_VAR_TYPE_
+ .quad 0
+ .quad l_OBJC_METH_VAR_NAME_.3
+ .quad l_OBJC_METH_VAR_TYPE_.4
+ .quad 0
+ .quad l_OBJC_METH_VAR_NAME_.5
+ .quad l_OBJC_METH_VAR_TYPE_.6
+ .quad 0
+ .section __TEXT,__objc_methname,cstring_literals
+l_OBJC_PROP_NAME_ATTR_: ; @OBJC_PROP_NAME_ATTR_
+ .asciz "MyProtocol01Prop"
+l_OBJC_PROP_NAME_ATTR_.7: ; @OBJC_PROP_NAME_ATTR_.7
+ .asciz "Ti,N"
+ .section __DATA,__objc_const
+ .p2align 3, 0x0 ; @"_OBJC_$_PROP_LIST_MyProtocol01"
+__OBJC_$_PROP_LIST_MyProtocol01:
+ .long 16 ; 0x10
+ .long 1 ; 0x1
+ .quad l_OBJC_PROP_NAME_ATTR_
+ .quad l_OBJC_PROP_NAME_ATTR_.7
+ .p2align 3, 0x0 ; @"_OBJC_$_PROTOCOL_METHOD_TYPES_MyProtocol01"
+__OBJC_$_PROTOCOL_METHOD_TYPES_MyProtocol01:
+ .quad l_OBJC_METH_VAR_TYPE_
+ .quad l_OBJC_METH_VAR_TYPE_.4
+ .quad l_OBJC_METH_VAR_TYPE_.6
+ .private_extern __OBJC_PROTOCOL_$_MyProtocol01 ; @"_OBJC_PROTOCOL_$_MyProtocol01"
+ .section __DATA,__data
+ .globl __OBJC_PROTOCOL_$_MyProtocol01
+ .weak_definition __OBJC_PROTOCOL_$_MyProtocol01
+ .p2align 3, 0x0
+__OBJC_PROTOCOL_$_MyProtocol01:
+ .quad 0
+ .quad l_OBJC_CLASS_NAME_.1
+ .quad 0
+ .quad __OBJC_$_PROTOCOL_INSTANCE_METHODS_MyProtocol01
+ .quad 0
+ .quad 0
+ .quad 0
+ .quad __OBJC_$_PROP_LIST_MyProtocol01
+ .long 96 ; 0x60
+ .long 0 ; 0x0
+ .quad __OBJC_$_PROTOCOL_METHOD_TYPES_MyProtocol01
+ .quad 0
+ .quad 0
+ .private_extern __OBJC_LABEL_PROTOCOL_$_MyProtocol01 ; @"_OBJC_LABEL_PROTOCOL_$_MyProtocol01"
+ .section __DATA,__objc_protolist,coalesced,no_dead_strip
+ .globl __OBJC_LABEL_PROTOCOL_$_MyProtocol01
+ .weak_definition __OBJC_LABEL_PROTOCOL_$_MyProtocol01
+ .p2align 3, 0x0
+__OBJC_LABEL_PROTOCOL_$_MyProtocol01:
+ .quad __OBJC_PROTOCOL_$_MyProtocol01
+ .section __DATA,__objc_const
+ .p2align 3, 0x0 ; @"_OBJC_CLASS_PROTOCOLS_$_MyBaseClass"
+__OBJC_CLASS_PROTOCOLS_$_MyBaseClass:
+ .quad 1 ; 0x1
+ .quad __OBJC_PROTOCOL_$_MyProtocol01
+ .quad 0
+ .p2align 3, 0x0 ; @"_OBJC_METACLASS_RO_$_MyBaseClass"
+__OBJC_METACLASS_RO_$_MyBaseClass:
+ .long 3 ; 0x3
+ .long 40 ; 0x28
+ .long 40 ; 0x28
+ .space 4
+ .quad 0
+ .quad l_OBJC_CLASS_NAME_
+ .quad __OBJC_$_CLASS_METHODS_MyBaseClass
+ .quad __OBJC_CLASS_PROTOCOLS_$_MyBaseClass
+ .quad 0
+ .quad 0
+ .quad 0
+ .section __TEXT,__objc_methname,cstring_literals
+l_OBJC_METH_VAR_NAME_.8: ; @OBJC_METH_VAR_NAME_.8
+ .asciz "baseInstanceMethod"
+ .section __DATA,__objc_const
+ .p2align 3, 0x0 ; @"_OBJC_$_INSTANCE_METHODS_MyBaseClass"
+__OBJC_$_INSTANCE_METHODS_MyBaseClass:
+ .long 24 ; 0x18
+ .long 4 ; 0x4
+ .quad l_OBJC_METH_VAR_NAME_.8
+ .quad l_OBJC_METH_VAR_TYPE_
+ .quad "-[MyBaseClass baseInstanceMethod]"
+ .quad l_OBJC_METH_VAR_NAME_.2
+ .quad l_OBJC_METH_VAR_TYPE_
+ .quad "-[MyBaseClass myProtocol01Method]"
+ .quad l_OBJC_METH_VAR_NAME_.3
+ .quad l_OBJC_METH_VAR_TYPE_.4
+ .quad "-[MyBaseClass MyProtocol01Prop]"
+ .quad l_OBJC_METH_VAR_NAME_.5
+ .quad l_OBJC_METH_VAR_TYPE_.6
+ .quad "-[MyBaseClass setMyProtocol01Prop:]"
+ .section __TEXT,__objc_methtype,cstring_literals
+l_OBJC_METH_VAR_TYPE_.9: ; @OBJC_METH_VAR_TYPE_.9
+ .asciz "i"
+ .section __DATA,__objc_const
+ .p2align 3, 0x0 ; @"_OBJC_$_INSTANCE_VARIABLES_MyBaseClass"
+__OBJC_$_INSTANCE_VARIABLES_MyBaseClass:
+ .long 32 ; 0x20
+ .long 1 ; 0x1
+ .quad _OBJC_IVAR_$_MyBaseClass.MyProtocol01Prop
+ .quad l_OBJC_METH_VAR_NAME_.3
+ .quad l_OBJC_METH_VAR_TYPE_.9
+ .long 2 ; 0x2
+ .long 4 ; 0x4
+ .section __TEXT,__objc_methname,cstring_literals
+l_OBJC_PROP_NAME_ATTR_.10: ; @OBJC_PROP_NAME_ATTR_.10
+ .asciz "Ti,N,VMyProtocol01Prop"
+ .section __DATA,__objc_const
+ .p2align 3, 0x0 ; @"_OBJC_$_PROP_LIST_MyBaseClass"
+__OBJC_$_PROP_LIST_MyBaseClass:
+ .long 16 ; 0x10
+ .long 1 ; 0x1
+ .quad l_OBJC_PROP_NAME_ATTR_
+ .quad l_OBJC_PROP_NAME_ATTR_.10
+ .p2align 3, 0x0 ; @"_OBJC_CLASS_RO_$_MyBaseClass"
+__OBJC_CLASS_RO_$_MyBaseClass:
+ .long 2 ; 0x2
+ .long 0 ; 0x0
+ .long 4 ; 0x4
+ .space 4
+ .quad 0
+ .quad l_OBJC_CLASS_NAME_
+ .quad __OBJC_$_INSTANCE_METHODS_MyBaseClass
+ .quad __OBJC_CLASS_PROTOCOLS_$_MyBaseClass
+ .quad __OBJC_$_INSTANCE_VARIABLES_MyBaseClass
+ .quad 0
+ .quad __OBJC_$_PROP_LIST_MyBaseClass
+ .globl __objc_empty_cache ; @_objc_empty_cache
+.zerofill __DATA,__common,__objc_empty_cache,8,3
+ .section __DATA,__objc_classlist,regular,no_dead_strip
+ .p2align 3, 0x0 ; @"OBJC_LABEL_CLASS_$"
+l_OBJC_LABEL_CLASS_$:
+ .quad _OBJC_CLASS_$_MyBaseClass
+ .no_dead_strip __OBJC_LABEL_PROTOCOL_$_MyProtocol01
+ .no_dead_strip __OBJC_PROTOCOL_$_MyProtocol01
+ .section __DATA,__objc_imageinfo,regular,no_dead_strip
+L_OBJC_IMAGE_INFO:
+ .long 0
+ .long 96
+.subsections_via_symbols
+
+
+#--- a64_file2.s
+
+## @protocol MyProtocol01
+## - (void)myProtocol01Method;
+## @end
+##
+## @protocol MyProtocol02
+## - (void)myProtocol02Method;
+## @property(readonly) int MyProtocol02Prop;
+## @end
+##
+## @protocol MyProtocol03
+## - (void)myProtocol03Method;
+## @property(readonly) int MyProtocol03Prop;
+## @end
+##
+##
+## __attribute__((objc_root_class))
+## @interface MyBaseClass<MyProtocol01>
+## - (void)baseInstanceMethod;
+## - (void)myProtocol01Method;
+## + (void)baseClassMethod;
+## @end
+##
+##
+##
+## @interface MyBaseClass(Category02)<MyProtocol02>
+## - (void)class02InstanceMethod;
+## - (void)myProtocol02Method;
+## + (void)class02ClassMethod;
+## + (int)MyProtocol02Prop;
+## @end
+##
+## @implementation MyBaseClass(Category02)
+## - (void)class02InstanceMethod {}
+## - (void)myProtocol02Method {}
+## + (void)class02ClassMethod {}
+## + (int)MyProtocol02Prop { return 0;}
+## @dynamic MyProtocol02Prop;
+## @end
+##
+## @interface MyBaseClass(Category03)<MyProtocol03>
+## - (void)class03InstanceMethod;
+## - (void)myProtocol03Method;
+## + (void)class03ClassMethod;
+## + (int)MyProtocol03Prop;
+## @end
+##
+## @implementation MyBaseClass(Category03)
+## - (void)class03InstanceMethod {}
+## - (void)myProtocol03Method {}
+## + (void)class03ClassMethod {}
+## + (int)MyProtocol03Prop { return 0;}
+## @dynamic MyProtocol03Prop;
+## @end
+##
+## int main() {
+## return 0;
+## }
+
+
+ .section __TEXT,__text,regular,pure_instructions
+ .p2align 2 ; -- Begin function -[MyBaseClass(Category02) class02InstanceMethod]
+"-[MyBaseClass(Category02) class02InstanceMethod]": ; @"\01-[MyBaseClass(Category02) class02InstanceMethod]"
+ .cfi_startproc
+; %bb.0: ; %entry
+ ret
+ .cfi_endproc
+ ; -- End function
+ .p2align 2 ; -- Begin function -[MyBaseClass(Category02) myProtocol02Method]
+"-[MyBaseClass(Category02) myProtocol02Method]": ; @"\01-[MyBaseClass(Category02) myProtocol02Method]"
+ .cfi_startproc
+; %bb.0: ; %entry
+ ret
+ .cfi_endproc
+ ; -- End function
+ .p2align 2 ; -- Begin function +[MyBaseClass(Category02) class02ClassMethod]
+"+[MyBaseClass(Category02) class02ClassMethod]": ; @"\01+[MyBaseClass(Category02) class02ClassMethod]"
+ .cfi_startproc
+; %bb.0: ; %entry
+ ret
+ .cfi_endproc
+ ; -- End function
+ .p2align 2 ; -- Begin function +[MyBaseClass(Category02) MyProtocol02Prop]
+"+[MyBaseClass(Category02) MyProtocol02Prop]": ; @"\01+[MyBaseClass(Category02) MyProtocol02Prop]"
+ .cfi_startproc
+; %bb.0: ; %entry
+ b _OUTLINED_FUNCTION_0
+ .cfi_endproc
+ ; -- End function
+ .p2align 2 ; -- Begin function -[MyBaseClass(Category03) class03InstanceMethod]
+"-[MyBaseClass(Category03) class03InstanceMethod]": ; @"\01-[MyBaseClass(Category03) class03InstanceMethod]"
+ .cfi_startproc
+; %bb.0: ; %entry
+ ret
+ .cfi_endproc
+ ; -- End function
+ .p2align 2 ; -- Begin function -[MyBaseClass(Category03) myProtocol03Method]
+"-[MyBaseClass(Category03) myProtocol03Method]": ; @"\01-[MyBaseClass(Category03) myProtocol03Method]"
+ .cfi_startproc
+; %bb.0: ; %entry
+ ret
+ .cfi_endproc
+ ; -- End function
+ .p2align 2 ; -- Begin function +[MyBaseClass(Category03) class03ClassMethod]
+"+[MyBaseClass(Category03) class03ClassMethod]": ; @"\01+[MyBaseClass(Category03) class03ClassMethod]"
+ .cfi_startproc
+; %bb.0: ; %entry
+ ret
+ .cfi_endproc
+ ; -- End function
+ .p2align 2 ; -- Begin function +[MyBaseClass(Category03) MyProtocol03Prop]
+"+[MyBaseClass(Category03) MyProtocol03Prop]": ; @"\01+[MyBaseClass(Category03) MyProtocol03Prop]"
+ .cfi_startproc
+; %bb.0: ; %entry
+ b _OUTLINED_FUNCTION_0
+ .cfi_endproc
+ ; -- End function
+ .globl _main ; -- Begin function main
+ .p2align 2
+_main: ; @main
+ .cfi_startproc
+; %bb.0: ; %entry
+ b _OUTLINED_FUNCTION_0
+ .cfi_endproc
+ ; -- End function
+ .p2align 2 ; -- Begin function OUTLINED_FUNCTION_0
+_OUTLINED_FUNCTION_0: ; @OUTLINED_FUNCTION_0 Tail Call
+ .cfi_startproc
+; %bb.0:
+ mov w0, #0
+ ret
+ .cfi_endproc
+ ; -- End function
+ .section __TEXT,__objc_classname,cstring_literals
+l_OBJC_CLASS_NAME_: ; @OBJC_CLASS_NAME_
+ .asciz "Category02"
+ .section __TEXT,__objc_methname,cstring_literals
+l_OBJC_METH_VAR_NAME_: ; @OBJC_METH_VAR_NAME_
+ .asciz "class02InstanceMethod"
+ .section __TEXT,__objc_methtype,cstring_literals
+l_OBJC_METH_VAR_TYPE_: ; @OBJC_METH_VAR_TYPE_
+ .asciz "v16 at 0:8"
+ .section __TEXT,__objc_methname,cstring_literals
+l_OBJC_METH_VAR_NAME_.1: ; @OBJC_METH_VAR_NAME_.1
+ .asciz "myProtocol02Method"
+ .section __DATA,__objc_const
+ .p2align 3, 0x0 ; @"_OBJC_$_CATEGORY_INSTANCE_METHODS_MyBaseClass_$_Category02"
+__OBJC_$_CATEGORY_INSTANCE_METHODS_MyBaseClass_$_Category02:
+ .long 24 ; 0x18
+ .long 2 ; 0x2
+ .quad l_OBJC_METH_VAR_NAME_
+ .quad l_OBJC_METH_VAR_TYPE_
+ .quad "-[MyBaseClass(Category02) class02InstanceMethod]"
+ .quad l_OBJC_METH_VAR_NAME_.1
+ .quad l_OBJC_METH_VAR_TYPE_
+ .quad "-[MyBaseClass(Category02) myProtocol02Method]"
+ .section __TEXT,__objc_methname,cstring_literals
+l_OBJC_METH_VAR_NAME_.2: ; @OBJC_METH_VAR_NAME_.2
+ .asciz "class02ClassMethod"
+l_OBJC_METH_VAR_NAME_.3: ; @OBJC_METH_VAR_NAME_.3
+ .asciz "MyProtocol02Prop"
+ .section __TEXT,__objc_methtype,cstring_literals
+l_OBJC_METH_VAR_TYPE_.4: ; @OBJC_METH_VAR_TYPE_.4
+ .asciz "i16 at 0:8"
+ .section __DATA,__objc_const
+ .p2align 3, 0x0 ; @"_OBJC_$_CATEGORY_CLASS_METHODS_MyBaseClass_$_Category02"
+__OBJC_$_CATEGORY_CLASS_METHODS_MyBaseClass_$_Category02:
+ .long 24 ; 0x18
+ .long 2 ; 0x2
+ .quad l_OBJC_METH_VAR_NAME_.2
+ .quad l_OBJC_METH_VAR_TYPE_
+ .quad "+[MyBaseClass(Category02) class02ClassMethod]"
+ .quad l_OBJC_METH_VAR_NAME_.3
+ .quad l_OBJC_METH_VAR_TYPE_.4
+ .quad "+[MyBaseClass(Category02) MyProtocol02Prop]"
+ .section __TEXT,__objc_classname,cstring_literals
+l_OBJC_CLASS_NAME_.5: ; @OBJC_CLASS_NAME_.5
+ .asciz "MyProtocol02"
+ .section __DATA,__objc_const
+ .p2align 3, 0x0 ; @"_OBJC_$_PROTOCOL_INSTANCE_METHODS_MyProtocol02"
+__OBJC_$_PROTOCOL_INSTANCE_METHODS_MyProtocol02:
+ .long 24 ; 0x18
+ .long 2 ; 0x2
+ .quad l_OBJC_METH_VAR_NAME_.1
+ .quad l_OBJC_METH_VAR_TYPE_
+ .quad 0
+ .quad l_OBJC_METH_VAR_NAME_.3
+ .quad l_OBJC_METH_VAR_TYPE_.4
+ .quad 0
+ .section __TEXT,__objc_methname,cstring_literals
+l_OBJC_PROP_NAME_ATTR_: ; @OBJC_PROP_NAME_ATTR_
+ .asciz "MyProtocol02Prop"
+l_OBJC_PROP_NAME_ATTR_.6: ; @OBJC_PROP_NAME_ATTR_.6
+ .asciz "Ti,R"
+ .section __DATA,__objc_const
+ .p2align 3, 0x0 ; @"_OBJC_$_PROP_LIST_MyProtocol02"
+__OBJC_$_PROP_LIST_MyProtocol02:
+ .long 16 ; 0x10
+ .long 1 ; 0x1
+ .quad l_OBJC_PROP_NAME_ATTR_
+ .quad l_OBJC_PROP_NAME_ATTR_.6
+ .p2align 3, 0x0 ; @"_OBJC_$_PROTOCOL_METHOD_TYPES_MyProtocol02"
+__OBJC_$_PROTOCOL_METHOD_TYPES_MyProtocol02:
+ .quad l_OBJC_METH_VAR_TYPE_
+ .quad l_OBJC_METH_VAR_TYPE_.4
+ .private_extern __OBJC_PROTOCOL_$_MyProtocol02 ; @"_OBJC_PROTOCOL_$_MyProtocol02"
+ .section __DATA,__data
+ .globl __OBJC_PROTOCOL_$_MyProtocol02
+ .weak_definition __OBJC_PROTOCOL_$_MyProtocol02
+ .p2align 3, 0x0
+__OBJC_PROTOCOL_$_MyProtocol02:
+ .quad 0
+ .quad l_OBJC_CLASS_NAME_.5
+ .quad 0
+ .quad __OBJC_$_PROTOCOL_INSTANCE_METHODS_MyProtocol02
+ .quad 0
+ .quad 0
+ .quad 0
+ .quad __OBJC_$_PROP_LIST_MyProtocol02
+ .long 96 ; 0x60
+ .long 0 ; 0x0
+ .quad __OBJC_$_PROTOCOL_METHOD_TYPES_MyProtocol02
+ .quad 0
+ .quad 0
+ .private_extern __OBJC_LABEL_PROTOCOL_$_MyProtocol02 ; @"_OBJC_LABEL_PROTOCOL_$_MyProtocol02"
+ .section __DATA,__objc_protolist,coalesced,no_dead_strip
+ .globl __OBJC_LABEL_PROTOCOL_$_MyProtocol02
+ .weak_definition __OBJC_LABEL_PROTOCOL_$_MyProtocol02
+ .p2align 3, 0x0
+__OBJC_LABEL_PROTOCOL_$_MyProtocol02:
+ .quad __OBJC_PROTOCOL_$_MyProtocol02
+ .section __DATA,__objc_const
+ .p2align 3, 0x0 ; @"_OBJC_CATEGORY_PROTOCOLS_$_MyBaseClass_$_Category02"
+__OBJC_CATEGORY_PROTOCOLS_$_MyBaseClass_$_Category02:
+ .quad 1 ; 0x1
+ .quad __OBJC_PROTOCOL_$_MyProtocol02
+ .quad 0
+ .section __TEXT,__objc_methname,cstring_literals
+l_OBJC_PROP_NAME_ATTR_.7: ; @OBJC_PROP_NAME_ATTR_.7
+ .asciz "Ti,R,D"
+ .section __DATA,__objc_const
+ .p2align 3, 0x0 ; @"_OBJC_$_PROP_LIST_MyBaseClass_$_Category02"
+__OBJC_$_PROP_LIST_MyBaseClass_$_Category02:
+ .long 16 ; 0x10
+ .long 1 ; 0x1
+ .quad l_OBJC_PROP_NAME_ATTR_
+ .quad l_OBJC_PROP_NAME_ATTR_.7
+ .p2align 3, 0x0 ; @"_OBJC_$_CATEGORY_MyBaseClass_$_Category02"
+__OBJC_$_CATEGORY_MyBaseClass_$_Category02:
+ .quad l_OBJC_CLASS_NAME_
+ .quad _OBJC_CLASS_$_MyBaseClass
+ .quad __OBJC_$_CATEGORY_INSTANCE_METHODS_MyBaseClass_$_Category02
+ .quad __OBJC_$_CATEGORY_CLASS_METHODS_MyBaseClass_$_Category02
+ .quad __OBJC_CATEGORY_PROTOCOLS_$_MyBaseClass_$_Category02
+ .quad __OBJC_$_PROP_LIST_MyBaseClass_$_Category02
+ .quad 0
+ .long 64 ; 0x40
+ .space 4
+ .section __TEXT,__objc_classname,cstring_literals
+l_OBJC_CLASS_NAME_.8: ; @OBJC_CLASS_NAME_.8
+ .asciz "Category03"
+ .section __TEXT,__objc_methname,cstring_literals
+l_OBJC_METH_VAR_NAME_.9: ; @OBJC_METH_VAR_NAME_.9
+ .asciz "class03InstanceMethod"
+l_OBJC_METH_VAR_NAME_.10: ; @OBJC_METH_VAR_NAME_.10
+ .asciz "myProtocol03Method"
+ .section __DATA,__objc_const
+ .p2align 3, 0x0 ; @"_OBJC_$_CATEGORY_INSTANCE_METHODS_MyBaseClass_$_Category03"
+__OBJC_$_CATEGORY_INSTANCE_METHODS_MyBaseClass_$_Category03:
+ .long 24 ; 0x18
+ .long 2 ; 0x2
+ .quad l_OBJC_METH_VAR_NAME_.9
+ .quad l_OBJC_METH_VAR_TYPE_
+ .quad "-[MyBaseClass(Category03) class03InstanceMethod]"
+ .quad l_OBJC_METH_VAR_NAME_.10
+ .quad l_OBJC_METH_VAR_TYPE_
+ .quad "-[MyBaseClass(Category03) myProtocol03Method]"
+ .section __TEXT,__objc_methname,cstring_literals
+l_OBJC_METH_VAR_NAME_.11: ; @OBJC_METH_VAR_NAME_.11
+ .asciz "class03ClassMethod"
+l_OBJC_METH_VAR_NAME_.12: ; @OBJC_METH_VAR_NAME_.12
+ .asciz "MyProtocol03Prop"
+ .section __DATA,__objc_const
+ .p2align 3, 0x0 ; @"_OBJC_$_CATEGORY_CLASS_METHODS_MyBaseClass_$_Category03"
+__OBJC_$_CATEGORY_CLASS_METHODS_MyBaseClass_$_Category03:
+ .long 24 ; 0x18
+ .long 2 ; 0x2
+ .quad l_OBJC_METH_VAR_NAME_.11
+ .quad l_OBJC_METH_VAR_TYPE_
+ .quad "+[MyBaseClass(Category03) class03ClassMethod]"
+ .quad l_OBJC_METH_VAR_NAME_.12
+ .quad l_OBJC_METH_VAR_TYPE_.4
+ .quad "+[MyBaseClass(Category03) MyProtocol03Prop]"
+ .section __TEXT,__objc_classname,cstring_literals
+l_OBJC_CLASS_NAME_.13: ; @OBJC_CLASS_NAME_.13
+ .asciz "MyProtocol03"
+ .section __DATA,__objc_const
+ .p2align 3, 0x0 ; @"_OBJC_$_PROTOCOL_INSTANCE_METHODS_MyProtocol03"
+__OBJC_$_PROTOCOL_INSTANCE_METHODS_MyProtocol03:
+ .long 24 ; 0x18
+ .long 2 ; 0x2
+ .quad l_OBJC_METH_VAR_NAME_.10
+ .quad l_OBJC_METH_VAR_TYPE_
+ .quad 0
+ .quad l_OBJC_METH_VAR_NAME_.12
+ .quad l_OBJC_METH_VAR_TYPE_.4
+ .quad 0
+ .section __TEXT,__objc_methname,cstring_literals
+l_OBJC_PROP_NAME_ATTR_.14: ; @OBJC_PROP_NAME_ATTR_.14
+ .asciz "MyProtocol03Prop"
+ .section __DATA,__objc_const
+ .p2align 3, 0x0 ; @"_OBJC_$_PROP_LIST_MyProtocol03"
+__OBJC_$_PROP_LIST_MyProtocol03:
+ .long 16 ; 0x10
+ .long 1 ; 0x1
+ .quad l_OBJC_PROP_NAME_ATTR_.14
+ .quad l_OBJC_PROP_NAME_ATTR_.6
+ .p2align 3, 0x0 ; @"_OBJC_$_PROTOCOL_METHOD_TYPES_MyProtocol03"
+__OBJC_$_PROTOCOL_METHOD_TYPES_MyProtocol03:
+ .quad l_OBJC_METH_VAR_TYPE_
+ .quad l_OBJC_METH_VAR_TYPE_.4
+ .private_extern __OBJC_PROTOCOL_$_MyProtocol03 ; @"_OBJC_PROTOCOL_$_MyProtocol03"
+ .section __DATA,__data
+ .globl __OBJC_PROTOCOL_$_MyProtocol03
+ .weak_definition __OBJC_PROTOCOL_$_MyProtocol03
+ .p2align 3, 0x0
+__OBJC_PROTOCOL_$_MyProtocol03:
+ .quad 0
+ .quad l_OBJC_CLASS_NAME_.13
+ .quad 0
+ .quad __OBJC_$_PROTOCOL_INSTANCE_METHODS_MyProtocol03
+ .quad 0
+ .quad 0
+ .quad 0
+ .quad __OBJC_$_PROP_LIST_MyProtocol03
+ .long 96 ; 0x60
+ .long 0 ; 0x0
+ .quad __OBJC_$_PROTOCOL_METHOD_TYPES_MyProtocol03
+ .quad 0
+ .quad 0
+ .private_extern __OBJC_LABEL_PROTOCOL_$_MyProtocol03 ; @"_OBJC_LABEL_PROTOCOL_$_MyProtocol03"
+ .section __DATA,__objc_protolist,coalesced,no_dead_strip
+ .globl __OBJC_LABEL_PROTOCOL_$_MyProtocol03
+ .weak_definition __OBJC_LABEL_PROTOCOL_$_MyProtocol03
+ .p2align 3, 0x0
+__OBJC_LABEL_PROTOCOL_$_MyProtocol03:
+ .quad __OBJC_PROTOCOL_$_MyProtocol03
+ .section __DATA,__objc_const
+ .p2align 3, 0x0 ; @"_OBJC_CATEGORY_PROTOCOLS_$_MyBaseClass_$_Category03"
+__OBJC_CATEGORY_PROTOCOLS_$_MyBaseClass_$_Category03:
+ .quad 1 ; 0x1
+ .quad __OBJC_PROTOCOL_$_MyProtocol03
+ .quad 0
+ .p2align 3, 0x0 ; @"_OBJC_$_PROP_LIST_MyBaseClass_$_Category03"
+__OBJC_$_PROP_LIST_MyBaseClass_$_Category03:
+ .long 16 ; 0x10
+ .long 1 ; 0x1
+ .quad l_OBJC_PROP_NAME_ATTR_.14
+ .quad l_OBJC_PROP_NAME_ATTR_.7
+ .p2align 3, 0x0 ; @"_OBJC_$_CATEGORY_MyBaseClass_$_Category03"
+__OBJC_$_CATEGORY_MyBaseClass_$_Category03:
+ .quad l_OBJC_CLASS_NAME_.8
+ .quad _OBJC_CLASS_$_MyBaseClass
+ .quad __OBJC_$_CATEGORY_INSTANCE_METHODS_MyBaseClass_$_Category03
+ .quad __OBJC_$_CATEGORY_CLASS_METHODS_MyBaseClass_$_Category03
+ .quad __OBJC_CATEGORY_PROTOCOLS_$_MyBaseClass_$_Category03
+ .quad __OBJC_$_PROP_LIST_MyBaseClass_$_Category03
+ .quad 0
+ .long 64 ; 0x40
+ .space 4
+ .section __DATA,__objc_catlist,regular,no_dead_strip
+ .p2align 3, 0x0 ; @"OBJC_LABEL_CATEGORY_$"
+l_OBJC_LABEL_CATEGORY_$:
+ .quad __OBJC_$_CATEGORY_MyBaseClass_$_Category02
+ .quad __OBJC_$_CATEGORY_MyBaseClass_$_Category03
+ .no_dead_strip __OBJC_LABEL_PROTOCOL_$_MyProtocol02
+ .no_dead_strip __OBJC_LABEL_PROTOCOL_$_MyProtocol03
+ .no_dead_strip __OBJC_PROTOCOL_$_MyProtocol02
+ .no_dead_strip __OBJC_PROTOCOL_$_MyProtocol03
+ .section __DATA,__objc_imageinfo,regular,no_dead_strip
+L_OBJC_IMAGE_INFO:
+ .long 0
+ .long 96
+.subsections_via_symbols
diff --git a/lld/test/MachO/objc-category-merging-extern-class-minimal.s b/lld/test/MachO/objc-category-merging-extern-class-minimal.s
new file mode 100644
index 00000000000000..ede7ef5d9c32d4
--- /dev/null
+++ b/lld/test/MachO/objc-category-merging-extern-class-minimal.s
@@ -0,0 +1,155 @@
+# REQUIRES: aarch64
+# RUN: rm -rf %t; split-file %s %t && cd %t
+
+## Create a dylib with a fake base class to link against
+# RUN: llvm-mc -filetype=obj -triple=arm64-apple-macos -o a64_fakedylib.o a64_fakedylib.s
+# RUN: %lld -arch arm64 a64_fakedylib.o -o a64_fakedylib.dylib -dylib
+
+## Create our main testing dylib - linking against the fake dylib above
+# RUN: llvm-mc -filetype=obj -triple=arm64-apple-macos -o merge_cat_minimal.o merge_cat_minimal.s
+# RUN: %lld -arch arm64 -dylib -o merge_cat_minimal_no_merge.dylib a64_fakedylib.dylib merge_cat_minimal.o
+# RUN: %lld -arch arm64 -dylib -o merge_cat_minimal_merge.dylib -objc_category_merging a64_fakedylib.dylib merge_cat_minimal.o
+
+## Now verify that the flag caused category merging to happen appropriatelly
+# RUN: llvm-objdump --objc-meta-data --macho merge_cat_minimal_no_merge.dylib | FileCheck %s --check-prefixes=NO_MERGE_CATS
+# RUN: llvm-objdump --objc-meta-data --macho merge_cat_minimal_merge.dylib | FileCheck %s --check-prefixes=MERGE_CATS
+
+#### Check merge categories enabled ###
+# Check that the original categories are not there
+MERGE_CATS-NOT: __OBJC_$_CATEGORY_MyBaseClass_$_Category01
+MERGE_CATS-NOT: __OBJC_$_CATEGORY_MyBaseClass_$_Category02
+
+# Check that the merged cateogry is there, in the correct format
+MERGE_CATS: __OBJC_$_CATEGORY_MyBaseClass_$_(Category01|Category02)
+MERGE_CATS-NEXT: name {{.*}} Category01|Category02
+MERGE_CATS: instanceMethods
+MERGE_CATS-NEXT: 24
+MERGE_CATS-NEXT: 2
+MERGE_CATS-NEXT: name {{.*}} cat01_InstanceMethod
+MERGE_CATS-NEXT: types {{.*}} v16 at 0:8
+MERGE_CATS-NEXT: imp -[MyBaseClass(Category01) cat01_InstanceMethod]
+MERGE_CATS-NEXT: name {{.*}} cat02_InstanceMethod
+MERGE_CATS-NEXT: types {{.*}} v16 at 0:8
+MERGE_CATS-NEXT: imp -[MyBaseClass(Category02) cat02_InstanceMethod]
+MERGE_CATS-NEXT: classMethods 0x0
+MERGE_CATS-NEXT: protocols 0x0
+MERGE_CATS-NEXT: instanceProperties 0x0
+
+#### Check merge categories disabled ###
+# Check that the merged category is not there
+NO_MERGE_CATS-NOT: __OBJC_$_CATEGORY_MyBaseClass_$_(Category01|Category02)
+
+# Check that the original categories are there
+NO_MERGE_CATS: __OBJC_$_CATEGORY_MyBaseClass_$_Category01
+NO_MERGE_CATS: __OBJC_$_CATEGORY_MyBaseClass_$_Category02
+
+
+
+#--- a64_fakedylib.s
+
+ .section __DATA,__objc_data
+ .globl _OBJC_CLASS_$_MyBaseClass
+_OBJC_CLASS_$_MyBaseClass:
+ .quad 0
+
+#--- merge_cat_minimal.s
+
+; ================== Generated from ObjC: ==================
+; __attribute__((objc_root_class))
+; @interface MyBaseClass
+; - (void)baseInstanceMethod;
+; @end
+;
+; @interface MyBaseClass(Category01)
+; - (void)cat01_InstanceMethod;
+; @end
+;
+; @implementation MyBaseClass(Category01)
+; - (void)cat01_InstanceMethod {}
+; @end
+;
+; @interface MyBaseClass(Category02)
+; - (void)cat02_InstanceMethod;
+; @end
+;
+; @implementation MyBaseClass(Category02)
+; - (void)cat02_InstanceMethod {}
+; @end
+; ================== Generated from ObjC: ==================
+
+ .section __TEXT,__text,regular,pure_instructions
+ .p2align 2 ; -- Begin function -[MyBaseClass(Category01) cat01_InstanceMethod]
+"-[MyBaseClass(Category01) cat01_InstanceMethod]": ; @"\01-[MyBaseClass(Category01) cat01_InstanceMethod]"
+ .cfi_startproc
+ ret
+ .cfi_endproc
+ ; -- End function
+ .p2align 2 ; -- Begin function -[MyBaseClass(Category02) cat02_InstanceMethod]
+"-[MyBaseClass(Category02) cat02_InstanceMethod]": ; @"\01-[MyBaseClass(Category02) cat02_InstanceMethod]"
+ .cfi_startproc
+ ret
+ .cfi_endproc
+ ; -- End function
+ .section __TEXT,__objc_classname,cstring_literals
+l_OBJC_CLASS_NAME_: ; @OBJC_CLASS_NAME_
+ .asciz "Category01"
+ .section __TEXT,__objc_methname,cstring_literals
+l_OBJC_METH_VAR_NAME_: ; @OBJC_METH_VAR_NAME_
+ .asciz "cat01_InstanceMethod"
+ .section __TEXT,__objc_methtype,cstring_literals
+l_OBJC_METH_VAR_TYPE_: ; @OBJC_METH_VAR_TYPE_
+ .asciz "v16 at 0:8"
+ .section __DATA,__objc_const
+ .p2align 3, 0x0 ; @"_OBJC_$_CATEGORY_INSTANCE_METHODS_MyBaseClass_$_Category01"
+__OBJC_$_CATEGORY_INSTANCE_METHODS_MyBaseClass_$_Category01:
+ .long 24 ; 0x18
+ .long 1 ; 0x1
+ .quad l_OBJC_METH_VAR_NAME_
+ .quad l_OBJC_METH_VAR_TYPE_
+ .quad "-[MyBaseClass(Category01) cat01_InstanceMethod]"
+ .p2align 3, 0x0 ; @"_OBJC_$_CATEGORY_MyBaseClass_$_Category01"
+__OBJC_$_CATEGORY_MyBaseClass_$_Category01:
+ .quad l_OBJC_CLASS_NAME_
+ .quad _OBJC_CLASS_$_MyBaseClass
+ .quad __OBJC_$_CATEGORY_INSTANCE_METHODS_MyBaseClass_$_Category01
+ .quad 0
+ .quad 0
+ .quad 0
+ .quad 0
+ .long 64 ; 0x40
+ .space 4
+ .section __TEXT,__objc_classname,cstring_literals
+l_OBJC_CLASS_NAME_.1: ; @OBJC_CLASS_NAME_.1
+ .asciz "Category02"
+ .section __TEXT,__objc_methname,cstring_literals
+l_OBJC_METH_VAR_NAME_.2: ; @OBJC_METH_VAR_NAME_.2
+ .asciz "cat02_InstanceMethod"
+ .section __DATA,__objc_const
+ .p2align 3, 0x0 ; @"_OBJC_$_CATEGORY_INSTANCE_METHODS_MyBaseClass_$_Category02"
+__OBJC_$_CATEGORY_INSTANCE_METHODS_MyBaseClass_$_Category02:
+ .long 24 ; 0x18
+ .long 1 ; 0x1
+ .quad l_OBJC_METH_VAR_NAME_.2
+ .quad l_OBJC_METH_VAR_TYPE_
+ .quad "-[MyBaseClass(Category02) cat02_InstanceMethod]"
+ .p2align 3, 0x0 ; @"_OBJC_$_CATEGORY_MyBaseClass_$_Category02"
+__OBJC_$_CATEGORY_MyBaseClass_$_Category02:
+ .quad l_OBJC_CLASS_NAME_.1
+ .quad _OBJC_CLASS_$_MyBaseClass
+ .quad __OBJC_$_CATEGORY_INSTANCE_METHODS_MyBaseClass_$_Category02
+ .quad 0
+ .quad 0
+ .quad 0
+ .quad 0
+ .long 64 ; 0x40
+ .space 4
+ .section __DATA,__objc_catlist,regular,no_dead_strip
+ .p2align 3, 0x0 ; @"OBJC_LABEL_CATEGORY_$"
+l_OBJC_LABEL_CATEGORY_$:
+ .quad __OBJC_$_CATEGORY_MyBaseClass_$_Category01
+ .quad __OBJC_$_CATEGORY_MyBaseClass_$_Category02
+ .section __DATA,__objc_imageinfo,regular,no_dead_strip
+L_OBJC_IMAGE_INFO:
+ .long 0
+ .long 96
+.subsections_via_symbols
>From 280c7a9526a9ae7f959117c9cec94f8c8887f15c Mon Sep 17 00:00:00 2001
From: Joseph Huber <huberjn at outlook.com>
Date: Mon, 18 Mar 2024 12:10:19 -0500
Subject: [PATCH 35/40] [Clang] Fix preprocessing device only in HIP mode
Summary:
A recent change made the HIP compilation bundle by default. However we
don't want to do this for `-E`, which silently broke some handling.
---
clang/lib/Driver/Driver.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index 5015ce9f6d68e0..1daf588142b3b4 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -4647,7 +4647,8 @@ Action *Driver::BuildOffloadingActions(Compilation &C,
// All kinds exit now in device-only mode except for non-RDC mode HIP.
if (offloadDeviceOnly() &&
- (!C.isOffloadingHostKind(Action::OFK_HIP) ||
+ (getFinalPhase(Args) == phases::Preprocess ||
+ !C.isOffloadingHostKind(Action::OFK_HIP) ||
!Args.hasFlag(options::OPT_gpu_bundle_output,
options::OPT_no_gpu_bundle_output, true) ||
Args.hasFlag(options::OPT_fgpu_rdc, options::OPT_fno_gpu_rdc, false)))
>From 0db2e1e91a9124c276fd00f674b71f038a53ddec Mon Sep 17 00:00:00 2001
From: Alex Richardson <alexrichardson at google.com>
Date: Mon, 18 Mar 2024 09:17:46 -0700
Subject: [PATCH 36/40] Reland "[compiler-rt] Avoid generating coredumps when
piped to a tool"
Updated the corelimit.cpp test to handle buildbots with RLIMIT_CORE
rlim_max already set to zero.
Original commit message:
I was trying to debug why `ninja check-compiler-rt` was taking so long
to run on my system and after some debugging it turned out that most of
the time was being spent generating core dumps.
On many current Linux systems, coredumps are no longer dumped in the CWD
but instead piped to a utility such as systemd-coredumpd that stores
them in a deterministic location. This can be done by setting the
kernel.core_pattern sysctl to start with a '|'. However, when using such
a setup the kernel ignores a coredump limit of 0 (since there is no file
being written) and we can end up piping many gigabytes of data to
systemd-coredumpd which causes the test suite to freeze for a long time.
While most piped coredump handlers do respect the crashing processes'
RLIMIT_CORE, this is notable not the case for Debian's systemd-coredump
due to a local patch that changes sysctl.d/50-coredump.conf to ignore
the specified limit and instead use RLIM_INFINITY
(https://salsa.debian.org/systemd-team/systemd/-/commit/64599ffe44f0d).
Fortunately there is a workaround: the kernel recognizes the magic value
of 1 for RLIMIT_CORE to disable coredumps when piping. One byte is also
too small to generate any coredump, so it effectively behaves as if we
had set the value to zero.
The alternative to using RLIMIT_CORE=1 would be to use prctl() with the
PR_SET_DUMPABLE flag, however that also prevents ptrace(), so makes it
impossible to attach a debugger.
Fixes: https://github.com/llvm/llvm-project/issues/45797
This reverts commit 0b9f19a9880eb786871194af116f223d2ad30c52.
---
.../sanitizer_posix_libcdep.cpp | 22 ++++++++++++++++++-
.../sanitizer_common/TestCases/corelimit.cpp | 4 +++-
2 files changed, 24 insertions(+), 2 deletions(-)
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cpp
index ef1fc354974396..ece2d7d63dd619 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_posix_libcdep.cpp
@@ -104,7 +104,27 @@ static void setlim(int res, rlim_t lim) {
void DisableCoreDumperIfNecessary() {
if (common_flags()->disable_coredump) {
- setlim(RLIMIT_CORE, 0);
+ rlimit rlim;
+ CHECK_EQ(0, getrlimit(RLIMIT_CORE, &rlim));
+ // On Linux, if the kernel.core_pattern sysctl starts with a '|' (i.e. it
+ // is being piped to a coredump handler such as systemd-coredumpd), the
+ // kernel ignores RLIMIT_CORE (since we aren't creating a file in the file
+ // system) except for the magic value of 1, which disables coredumps when
+ // piping. 1 byte is too small for any kind of valid core dump, so it
+ // also disables coredumps if kernel.core_pattern creates files directly.
+ // While most piped coredump handlers do respect the crashing processes'
+ // RLIMIT_CORE, this is notable not the case for Debian's systemd-coredump
+ // due to a local patch that changes sysctl.d/50-coredump.conf to ignore
+ // the specified limit and instead use RLIM_INFINITY.
+ //
+ // The alternative to using RLIMIT_CORE=1 would be to use prctl() with the
+ // PR_SET_DUMPABLE flag, however that also prevents ptrace(), so makes it
+ // impossible to attach a debugger.
+ //
+ // Note: we use rlim_max in the Min() call here since that is the upper
+ // limit for what can be set without getting an EINVAL error.
+ rlim.rlim_cur = Min<rlim_t>(SANITIZER_LINUX ? 1 : 0, rlim.rlim_max);
+ CHECK_EQ(0, setrlimit(RLIMIT_CORE, &rlim));
}
}
diff --git a/compiler-rt/test/sanitizer_common/TestCases/corelimit.cpp b/compiler-rt/test/sanitizer_common/TestCases/corelimit.cpp
index 2378a4cfdced12..9b56471905aaf4 100644
--- a/compiler-rt/test/sanitizer_common/TestCases/corelimit.cpp
+++ b/compiler-rt/test/sanitizer_common/TestCases/corelimit.cpp
@@ -10,7 +10,9 @@ int main() {
getrlimit(RLIMIT_CORE, &lim_core);
void *p;
if (sizeof(p) == 8) {
- assert(0 == lim_core.rlim_cur);
+ // rlim_cur will be set to zero or one depending on the target OS and
+ // initial core limits. See comments in DisableCoreDumperIfNecessary().
+ assert(lim_core.rlim_cur <= 1u);
}
return 0;
}
>From 67c5a98caea419a9720712d3977d487ab95f6356 Mon Sep 17 00:00:00 2001
From: Fraser Cormack <fraser at codeplay.com>
Date: Mon, 18 Mar 2024 17:16:35 +0000
Subject: [PATCH 37/40] [IR][NFC] Suppress warnings in ternary operators
Just doing this the same way as in AMDGPUPromoteAlloca.cpp
---
llvm/lib/IR/AutoUpgrade.cpp | 26 +++++++++++++++-----------
1 file changed, 15 insertions(+), 11 deletions(-)
diff --git a/llvm/lib/IR/AutoUpgrade.cpp b/llvm/lib/IR/AutoUpgrade.cpp
index be0abb4b71dae2..dbea4529456c58 100644
--- a/llvm/lib/IR/AutoUpgrade.cpp
+++ b/llvm/lib/IR/AutoUpgrade.cpp
@@ -647,11 +647,12 @@ static bool upgradeArmOrAarch64IntrinsicFunction(bool IsArm, Function *F,
// v16i8 respectively.
if (Name.consume_front("bfdot.")) {
// (arm|aarch64).neon.bfdot.*'.
- Intrinsic::ID ID = StringSwitch<Intrinsic::ID>(Name)
- .Cases("v2f32.v8i8", "v4f32.v16i8",
- IsArm ? Intrinsic::arm_neon_bfdot
- : Intrinsic::aarch64_neon_bfdot)
- .Default(Intrinsic::not_intrinsic);
+ Intrinsic::ID ID =
+ StringSwitch<Intrinsic::ID>(Name)
+ .Cases("v2f32.v8i8", "v4f32.v16i8",
+ IsArm ? (Intrinsic::ID)Intrinsic::arm_neon_bfdot
+ : (Intrinsic::ID)Intrinsic::aarch64_neon_bfdot)
+ .Default(Intrinsic::not_intrinsic);
if (ID != Intrinsic::not_intrinsic) {
size_t OperandWidth = F->getReturnType()->getPrimitiveSizeInBits();
assert((OperandWidth == 64 || OperandWidth == 128) &&
@@ -674,12 +675,15 @@ static bool upgradeArmOrAarch64IntrinsicFunction(bool IsArm, Function *F,
// (arm|aarch64).neon.bfm*.v4f32.v16i8'.
Intrinsic::ID ID =
StringSwitch<Intrinsic::ID>(Name)
- .Case("mla", IsArm ? Intrinsic::arm_neon_bfmmla
- : Intrinsic::aarch64_neon_bfmmla)
- .Case("lalb", IsArm ? Intrinsic::arm_neon_bfmlalb
- : Intrinsic::aarch64_neon_bfmlalb)
- .Case("lalt", IsArm ? Intrinsic::arm_neon_bfmlalt
- : Intrinsic::aarch64_neon_bfmlalt)
+ .Case("mla",
+ IsArm ? (Intrinsic::ID)Intrinsic::arm_neon_bfmmla
+ : (Intrinsic::ID)Intrinsic::aarch64_neon_bfmmla)
+ .Case("lalb",
+ IsArm ? (Intrinsic::ID)Intrinsic::arm_neon_bfmlalb
+ : (Intrinsic::ID)Intrinsic::aarch64_neon_bfmlalb)
+ .Case("lalt",
+ IsArm ? (Intrinsic::ID)Intrinsic::arm_neon_bfmlalt
+ : (Intrinsic::ID)Intrinsic::aarch64_neon_bfmlalt)
.Default(Intrinsic::not_intrinsic);
if (ID != Intrinsic::not_intrinsic) {
NewFn = Intrinsic::getDeclaration(F->getParent(), ID);
>From 44c579f5b56e89c44ad508805f742601ce3db2b1 Mon Sep 17 00:00:00 2001
From: Alexey Bataev <a.bataev at outlook.com>
Date: Mon, 18 Mar 2024 10:00:38 -0700
Subject: [PATCH 38/40] [SLP][NFC]Add a test with minbitwidth operand, but not
a user.
---
.../X86/minbitwidth-user-not-min.ll | 49 +++++++++++++++++++
1 file changed, 49 insertions(+)
create mode 100644 llvm/test/Transforms/SLPVectorizer/X86/minbitwidth-user-not-min.ll
diff --git a/llvm/test/Transforms/SLPVectorizer/X86/minbitwidth-user-not-min.ll b/llvm/test/Transforms/SLPVectorizer/X86/minbitwidth-user-not-min.ll
new file mode 100644
index 00000000000000..6922df8991b831
--- /dev/null
+++ b/llvm/test/Transforms/SLPVectorizer/X86/minbitwidth-user-not-min.ll
@@ -0,0 +1,49 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
+; RUN: opt -S --passes=slp-vectorizer -mtriple=x86_64-unknown-linux-gnu < %s | FileCheck %s
+
+define void @test(ptr %block, ptr noalias %pixels, i1 %b) {
+; CHECK-LABEL: define void @test(
+; CHECK-SAME: ptr [[BLOCK:%.*]], ptr noalias [[PIXELS:%.*]], i1 [[B:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = insertelement <4 x i1> <i1 true, i1 poison, i1 false, i1 false>, i1 [[B]], i32 1
+; CHECK-NEXT: [[TMP1:%.*]] = sext <4 x i1> [[TMP0]] to <4 x i8>
+; CHECK-NEXT: [[TMP2:%.*]] = load <4 x i16>, ptr [[BLOCK]], align 2
+; CHECK-NEXT: [[TMP3:%.*]] = icmp ult <4 x i16> [[TMP2]], zeroinitializer
+; CHECK-NEXT: [[TMP4:%.*]] = trunc <4 x i16> [[TMP2]] to <4 x i8>
+; CHECK-NEXT: [[TMP5:%.*]] = select <4 x i1> [[TMP3]], <4 x i8> [[TMP4]], <4 x i8> [[TMP1]]
+; CHECK-NEXT: store <4 x i8> [[TMP5]], ptr [[PIXELS]], align 1
+; CHECK-NEXT: ret void
+;
+entry:
+ %0 = load i16, ptr %block, align 2
+ %tobool.not.i78 = icmp ult i16 %0, 0
+ %conv.i80 = sext i1 true to i8
+ %conv1.i81 = trunc i16 %0 to i8
+ %retval.0.i82 = select i1 %tobool.not.i78, i8 %conv1.i81, i8 %conv.i80
+ store i8 %retval.0.i82, ptr %pixels, align 1
+ %arrayidx2 = getelementptr i8, ptr %block, i64 2
+ %1 = load i16, ptr %arrayidx2, align 2
+ %tobool.not.i73 = icmp ult i16 %1, 0
+ %conv.i75 = sext i1 %b to i8
+ %conv1.i76 = trunc i16 %1 to i8
+ %retval.0.i77 = select i1 %tobool.not.i73, i8 %conv1.i76, i8 %conv.i75
+ %arrayidx5 = getelementptr i8, ptr %pixels, i64 1
+ store i8 %retval.0.i77, ptr %arrayidx5, align 1
+ %arrayidx6 = getelementptr i8, ptr %block, i64 4
+ %2 = load i16, ptr %arrayidx6, align 2
+ %tobool.not.i68 = icmp ult i16 %2, 0
+ %conv.i70 = sext i1 false to i8
+ %conv1.i71 = trunc i16 %2 to i8
+ %retval.0.i72 = select i1 %tobool.not.i68, i8 %conv1.i71, i8 %conv.i70
+ %arrayidx9 = getelementptr i8, ptr %pixels, i64 2
+ store i8 %retval.0.i72, ptr %arrayidx9, align 1
+ %arrayidx10 = getelementptr i8, ptr %block, i64 6
+ %3 = load i16, ptr %arrayidx10, align 2
+ %tobool.not.i63 = icmp ult i16 %3, 0
+ %conv.i65 = sext i1 false to i8
+ %conv1.i66 = trunc i16 %3 to i8
+ %retval.0.i67 = select i1 %tobool.not.i63, i8 %conv1.i66, i8 %conv.i65
+ %arrayidx13 = getelementptr i8, ptr %pixels, i64 3
+ store i8 %retval.0.i67, ptr %arrayidx13, align 1
+ ret void
+}
>From 42fff172c8700ce2208a58df53e261a83180503f Mon Sep 17 00:00:00 2001
From: Roberto Bampi <bampi at google.com>
Date: Thu, 7 Mar 2024 18:10:56 +0100
Subject: [PATCH 39/40] [clang-format] Add --fail-on-incomplete-format.
At the moment clang-format will return exit code 0 on incomplete
results. In scripts it would sometimes be useful if clang-format would
instead fail in those cases, signalling that there was something wrong
with the code being formatted.
---
clang/docs/ClangFormat.rst | 1 +
clang/test/Format/fail-on-incomplete.cpp | 5 +++++
clang/tools/clang-format/ClangFormat.cpp | 14 +++++++++++---
3 files changed, 17 insertions(+), 3 deletions(-)
create mode 100644 clang/test/Format/fail-on-incomplete.cpp
diff --git a/clang/docs/ClangFormat.rst b/clang/docs/ClangFormat.rst
index 819d9ee9f9cde1..80dc38a075c8fc 100644
--- a/clang/docs/ClangFormat.rst
+++ b/clang/docs/ClangFormat.rst
@@ -61,6 +61,7 @@ to format C/C++/Java/JavaScript/JSON/Objective-C/Protobuf/C# code.
--dry-run - If set, do not actually make the formatting changes
--dump-config - Dump configuration options to stdout and exit.
Can be used with -style option.
+ --fail-on-incomplete-format - If set, fail with exit code 1 on incomplete format.
--fallback-style=<string> - The name of the predefined style used as a
fallback in case clang-format is invoked with
-style=file, but can not find the .clang-format
diff --git a/clang/test/Format/fail-on-incomplete.cpp b/clang/test/Format/fail-on-incomplete.cpp
new file mode 100644
index 00000000000000..23220e654e4625
--- /dev/null
+++ b/clang/test/Format/fail-on-incomplete.cpp
@@ -0,0 +1,5 @@
+// RUN: cat %s | not clang-format --fail-on-incomplete-format | FileCheck %s
+// RUN: cat %s | clang-format | FileCheck %s
+int a([) {}
+
+// CHECK: int a([) {}
\ No newline at end of file
diff --git a/clang/tools/clang-format/ClangFormat.cpp b/clang/tools/clang-format/ClangFormat.cpp
index 60b0f5d1a0f414..e2600435b0a620 100644
--- a/clang/tools/clang-format/ClangFormat.cpp
+++ b/clang/tools/clang-format/ClangFormat.cpp
@@ -199,6 +199,11 @@ static cl::list<std::string> FileNames(cl::Positional,
cl::desc("[@<file>] [<file> ...]"),
cl::cat(ClangFormatCategory));
+static cl::opt<bool> FailOnIncompleteFormat(
+ "fail-on-incomplete-format",
+ cl::desc("If set, fail with exit code 1 on incomplete format."),
+ cl::init(false), cl::cat(ClangFormatCategory));
+
namespace clang {
namespace format {
@@ -393,7 +398,7 @@ class ClangFormatDiagConsumer : public DiagnosticConsumer {
};
// Returns true on error.
-static bool format(StringRef FileName) {
+static bool format(StringRef FileName, bool ErrorOnIncompleteFormat = false) {
const bool IsSTDIN = FileName == "-";
if (!OutputXML && Inplace && IsSTDIN) {
errs() << "error: cannot use -i when reading from stdin.\n";
@@ -529,6 +534,9 @@ static bool format(StringRef FileName) {
Rewrite.getEditBuffer(ID).write(outs());
}
}
+ if (ErrorOnIncompleteFormat && !Status.FormatComplete)
+ return true;
+
return false;
}
@@ -693,7 +701,7 @@ int main(int argc, const char **argv) {
}
if (FileNames.empty())
- return clang::format::format("-");
+ return clang::format::format("-", FailOnIncompleteFormat);
if (FileNames.size() > 1 &&
(!Offsets.empty() || !Lengths.empty() || !LineRanges.empty())) {
@@ -711,7 +719,7 @@ int main(int argc, const char **argv) {
errs() << "Formatting [" << FileNo++ << "/" << FileNames.size() << "] "
<< FileName << "\n";
}
- Error |= clang::format::format(FileName);
+ Error |= clang::format::format(FileName, FailOnIncompleteFormat);
}
return Error ? 1 : 0;
}
>From c240e4d7d0a8e79ec62413fbc3c294ef132cc2fe Mon Sep 17 00:00:00 2001
From: Roberto Bampi <gigaroby at users.noreply.github.com>
Date: Fri, 8 Mar 2024 14:51:49 +0100
Subject: [PATCH 40/40] Update clang/tools/clang-format/ClangFormat.cpp
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Björn Schäpers <github at hazardy.de>
---
clang/tools/clang-format/ClangFormat.cpp | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/clang/tools/clang-format/ClangFormat.cpp b/clang/tools/clang-format/ClangFormat.cpp
index e2600435b0a620..0e54a478f94fd3 100644
--- a/clang/tools/clang-format/ClangFormat.cpp
+++ b/clang/tools/clang-format/ClangFormat.cpp
@@ -534,10 +534,7 @@ static bool format(StringRef FileName, bool ErrorOnIncompleteFormat = false) {
Rewrite.getEditBuffer(ID).write(outs());
}
}
- if (ErrorOnIncompleteFormat && !Status.FormatComplete)
- return true;
-
- return false;
+ return ErrorOnIncompleteFormat && !Status.FormatComplete;
}
} // namespace format
More information about the Mlir-commits
mailing list