45 changes: 14 additions & 31 deletions libcxx/include/__memory/unique_ptr.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include <__type_traits/integral_constant.h>
#include <__type_traits/is_array.h>
#include <__type_traits/is_assignable.h>
#include <__type_traits/is_bounded_array.h>
#include <__type_traits/is_constant_evaluated.h>
#include <__type_traits/is_constructible.h>
#include <__type_traits/is_convertible.h>
Expand All @@ -41,6 +42,7 @@
#include <__type_traits/is_same.h>
#include <__type_traits/is_swappable.h>
#include <__type_traits/is_trivially_relocatable.h>
#include <__type_traits/is_unbounded_array.h>
#include <__type_traits/is_void.h>
#include <__type_traits/remove_extent.h>
#include <__type_traits/type_identity.h>
Expand Down Expand Up @@ -758,55 +760,36 @@ operator<=>(const unique_ptr<_T1, _D1>& __x, nullptr_t) {

#if _LIBCPP_STD_VER >= 14

template <class _Tp>
struct __unique_if {
typedef unique_ptr<_Tp> __unique_single;
};

template <class _Tp>
struct __unique_if<_Tp[]> {
typedef unique_ptr<_Tp[]> __unique_array_unknown_bound;
};

template <class _Tp, size_t _Np>
struct __unique_if<_Tp[_Np]> {
typedef void __unique_array_known_bound;
};

template <class _Tp, class... _Args>
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 typename __unique_if<_Tp>::__unique_single
make_unique(_Args&&... __args) {
template <class _Tp, class... _Args, enable_if_t<!is_array<_Tp>::value, int> = 0>
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unique_ptr<_Tp> make_unique(_Args&&... __args) {
return unique_ptr<_Tp>(new _Tp(std::forward<_Args>(__args)...));
}

template <class _Tp>
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 typename __unique_if<_Tp>::__unique_array_unknown_bound
make_unique(size_t __n) {
template <class _Tp, enable_if_t<__is_unbounded_array_v<_Tp>, int> = 0>
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unique_ptr<_Tp> make_unique(size_t __n) {
typedef __remove_extent_t<_Tp> _Up;
return unique_ptr<_Tp>(__private_constructor_tag(), new _Up[__n](), __n);
}

template <class _Tp, class... _Args>
typename __unique_if<_Tp>::__unique_array_known_bound make_unique(_Args&&...) = delete;
template <class _Tp, class... _Args, enable_if_t<__is_bounded_array_v<_Tp>, int> = 0>
void make_unique(_Args&&...) = delete;

#endif // _LIBCPP_STD_VER >= 14

#if _LIBCPP_STD_VER >= 20

template <class _Tp>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 typename __unique_if<_Tp>::__unique_single
make_unique_for_overwrite() {
template <class _Tp, enable_if_t<!is_array_v<_Tp>, int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unique_ptr<_Tp> make_unique_for_overwrite() {
return unique_ptr<_Tp>(new _Tp);
}

template <class _Tp>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 typename __unique_if<_Tp>::__unique_array_unknown_bound
make_unique_for_overwrite(size_t __n) {
template <class _Tp, enable_if_t<is_unbounded_array_v<_Tp>, int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 unique_ptr<_Tp> make_unique_for_overwrite(size_t __n) {
return unique_ptr<_Tp>(__private_constructor_tag(), new __remove_extent_t<_Tp>[__n], __n);
}

template <class _Tp, class... _Args>
typename __unique_if<_Tp>::__unique_array_known_bound make_unique_for_overwrite(_Args&&...) = delete;
template <class _Tp, class... _Args, enable_if_t<is_bounded_array_v<_Tp>, int> = 0>
void make_unique_for_overwrite(_Args&&...) = delete;

#endif // _LIBCPP_STD_VER >= 20

Expand Down
4 changes: 2 additions & 2 deletions libcxx/include/__type_traits/is_bounded_array.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@
_LIBCPP_BEGIN_NAMESPACE_STD

template <class>
struct _LIBCPP_TEMPLATE_VIS __libcpp_is_bounded_array : false_type {};
inline const bool __is_bounded_array_v = false;
template <class _Tp, size_t _Np>
struct _LIBCPP_TEMPLATE_VIS __libcpp_is_bounded_array<_Tp[_Np]> : true_type {};
inline const bool __is_bounded_array_v<_Tp[_Np]> = true;

#if _LIBCPP_STD_VER >= 20

Expand Down
4 changes: 2 additions & 2 deletions libcxx/include/__type_traits/is_unbounded_array.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
_LIBCPP_BEGIN_NAMESPACE_STD

template <class>
struct _LIBCPP_TEMPLATE_VIS __libcpp_is_unbounded_array : false_type {};
inline const bool __is_unbounded_array_v = false;
template <class _Tp>
struct _LIBCPP_TEMPLATE_VIS __libcpp_is_unbounded_array<_Tp[]> : true_type {};
inline const bool __is_unbounded_array_v<_Tp[]> = true;

#if _LIBCPP_STD_VER >= 20

Expand Down
8 changes: 8 additions & 0 deletions libcxx/include/__utility/is_pointer_in_range.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ __is_pointer_in_range(const _Tp* __begin, const _Tp* __end, const _Up* __ptr) {
reinterpret_cast<const char*>(__ptr) < reinterpret_cast<const char*>(__end);
}

template <class _Tp, class _Up>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool
__is_overlapping_range(const _Tp* __begin, const _Tp* __end, const _Up* __begin2) {
auto __size = __end - __begin;
auto __end2 = __begin2 + __size;
return std::__is_pointer_in_range(__begin, __end, __begin2) || std::__is_pointer_in_range(__begin2, __end2, __begin);
}

_LIBCPP_END_NAMESPACE_STD

#endif // _LIBCPP___UTILITY_IS_POINTER_IN_RANGE_H
11 changes: 11 additions & 0 deletions libcxx/include/string
Original file line number Diff line number Diff line change
Expand Up @@ -1912,6 +1912,9 @@ private:
#ifndef _LIBCPP_CXX03_LANG
if constexpr (__libcpp_is_contiguous_iterator<_ForwardIter>::value &&
is_same<value_type, __iter_value_type<_ForwardIter>>::value && is_same<_ForwardIter, _Sent>::value) {
_LIBCPP_ASSERT_INTERNAL(
!std::__is_overlapping_range(std::__to_address(__first), std::__to_address(__last), __dest),
"__copy_non_overlapping_range called with an overlapping range!");
traits_type::copy(__dest, std::__to_address(__first), __last - __first);
return __dest + (__last - __first);
}
Expand Down Expand Up @@ -1966,9 +1969,12 @@ private:
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __set_long_size(size_type __s) _NOEXCEPT {
__rep_.__l.__size_ = __s;
}

_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type __get_long_size() const _NOEXCEPT {
_LIBCPP_ASSERT_INTERNAL(__rep_.__l.__is_long_, "String has to be long when trying to get the long size");
return __rep_.__l.__size_;
}

_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __set_size(size_type __s) _NOEXCEPT {
if (__is_long())
__set_long_size(__s);
Expand All @@ -1977,11 +1983,13 @@ private:
}

_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __set_long_cap(size_type __s) _NOEXCEPT {
_LIBCPP_ASSERT_INTERNAL(!__fits_in_sso(__s), "Long capacity should always be larger than the SSO");
__rep_.__l.__cap_ = __s / __endian_factor;
__rep_.__l.__is_long_ = true;
}

_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type __get_long_cap() const _NOEXCEPT {
_LIBCPP_ASSERT_INTERNAL(__rep_.__l.__is_long_, "String has to be long when trying to get the long capacity");
return __rep_.__l.__cap_ * __endian_factor;
}

Expand All @@ -1990,10 +1998,12 @@ private:
}

_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 pointer __get_long_pointer() _NOEXCEPT {
_LIBCPP_ASSERT_INTERNAL(__rep_.__l.__is_long_, "String has to be long when trying to get the long pointer");
return _LIBCPP_ASAN_VOLATILE_WRAPPER(__rep_.__l.__data_);
}

_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 const_pointer __get_long_pointer() const _NOEXCEPT {
_LIBCPP_ASSERT_INTERNAL(__rep_.__l.__is_long_, "String has to be long when trying to get the long pointer");
return _LIBCPP_ASAN_VOLATILE_WRAPPER(__rep_.__l.__data_);
}

Expand Down Expand Up @@ -2137,6 +2147,7 @@ private:
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_NOINLINE basic_string& __assign_no_alias(const value_type* __s, size_type __n);

_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __erase_to_end(size_type __pos) {
_LIBCPP_ASSERT_INTERNAL(__pos <= capacity(), "Trying to erase at position outside the strings capacity!");
__null_terminate_at(std::__to_address(__get_pointer()), __pos);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,81 @@ static_assert(!CanEmplace<Map, Emplaceable>);
static_assert(!CanEmplace<Map, int, double>);

template <class KeyContainer, class ValueContainer>
void test_simple() {
void test() {
using Key = typename KeyContainer::value_type;
using Value = typename ValueContainer::value_type;
using M = std::flat_map<Key, Value, std::less<Key>, KeyContainer, ValueContainer>;
using R = std::pair<typename M::iterator, bool>;
M m;
ASSERT_SAME_TYPE(decltype(m.emplace()), R);
R r = m.emplace(typename M::value_type(2, 3.5));
assert(r.second);
assert(r.first == m.begin());
assert(m.size() == 1);
assert(m.begin()->first == 2);
assert(m.begin()->second == 3.5);
{
// was empty
M m;
std::same_as<R> decltype(auto) r = m.emplace(typename M::value_type(2, 3.5));
assert(r.second);
assert(r.first == m.begin());
assert(m.size() == 1);
assert(r.first->first == 2);
assert(r.first->second == 3.5);
}
{
// key does not exist and inserted at the begin
M m = {{3, 4.0}, {5, 3.0}, {6, 1.0}, {7, 0.0}};
std::same_as<R> decltype(auto) r = m.emplace(typename M::value_type(2, 2.0));
assert(r.second);
assert(r.first == m.begin());
assert(m.size() == 5);
assert(r.first->first == 2);
assert(r.first->second == 2.0);
}
{
// key does not exist and inserted in the middle
M m = {{0, 4.0}, {1, 3.0}, {3, 1.0}, {4, 0.0}};
std::same_as<R> decltype(auto) r = m.emplace(typename M::value_type(2, 2.0));
assert(r.second);
assert(r.first == m.begin() + 2);
assert(m.size() == 5);
assert(r.first->first == 2);
assert(r.first->second == 2.0);
}
{
// key does not exist and inserted at the end
M m = {{0, 4.0}, {1, 3.0}};
std::same_as<R> decltype(auto) r = m.emplace(typename M::value_type(2, 2.0));
assert(r.second);
assert(r.first == m.begin() + 2);
assert(m.size() == 3);
assert(r.first->first == 2);
assert(r.first->second == 2.0);
}
{
// key already exists and original at the begin
M m = {{2, 4.0}, {3, 3.0}, {5, 1.0}, {6, 0.0}};
std::same_as<R> decltype(auto) r = m.emplace(typename M::value_type(2, 2.0));
assert(!r.second);
assert(r.first == m.begin());
assert(m.size() == 4);
assert(r.first->first == 2);
assert(r.first->second == 4.0);
}
{
// key already exists and original in the middle
M m = {{0, 4.0}, {2, 3.0}, {3, 1.0}, {4, 0.0}};
std::same_as<R> decltype(auto) r = m.emplace(typename M::value_type(2, 2.0));
assert(!r.second);
assert(r.first == m.begin() + 1);
assert(m.size() == 4);
assert(r.first->first == 2);
assert(r.first->second == 3.0);
}
{
// key already exists and original at the end
M m = {{0, 4.0}, {1, 3.0}, {2, 1.0}};
std::same_as<R> decltype(auto) r = m.emplace(typename M::value_type(2, 2.0));
assert(!r.second);
assert(r.first == m.begin() + 2);
assert(m.size() == 3);
assert(r.first->first == 2);
assert(r.first->second == 1.0);
}
}

template <class KeyContainer, class ValueContainer>
Expand Down Expand Up @@ -82,10 +144,10 @@ void test_emplaceable() {
}

int main(int, char**) {
test_simple<std::vector<int>, std::vector<double>>();
test_simple<std::deque<int>, std::vector<double>>();
test_simple<MinSequenceContainer<int>, MinSequenceContainer<double>>();
test_simple<std::vector<int, min_allocator<int>>, std::vector<double, min_allocator<double>>>();
test<std::vector<int>, std::vector<double>>();
test<std::deque<int>, std::vector<double>>();
test<MinSequenceContainer<int>, MinSequenceContainer<double>>();
test<std::vector<int, min_allocator<int>>, std::vector<double, min_allocator<double>>>();

test_emplaceable<std::vector<int>, std::vector<Emplaceable>>();
test_emplaceable<std::deque<int>, std::vector<Emplaceable>>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,100 @@ static_assert(!CanEmplaceHint<Map, int, double>);
#endif

template <class KeyContainer, class ValueContainer>
void test_simple() {
void test() {
using Key = typename KeyContainer::value_type;
using Value = typename ValueContainer::value_type;
using M = std::flat_map<Key, Value, std::less<Key>, KeyContainer, ValueContainer>;
using R = M::iterator;
M m;
ASSERT_SAME_TYPE(decltype(m.emplace_hint(m.cbegin())), R);
R r = m.emplace_hint(m.end(), typename M::value_type(2, 3.5));
assert(r == m.begin());
assert(m.size() == 1);
assert(m.begin()->first == 2);
assert(m.begin()->second == 3.5);
{
// was empty
M m;
std::same_as<R> decltype(auto) r = m.emplace_hint(m.end(), typename M::value_type(2, 3.5));
assert(r == m.begin());
assert(m.size() == 1);
assert(r->first == 2);
assert(r->second == 3.5);
}
{
// hints correct at the begin
M m = {{3, 3.0}, {4, 4.0}};
auto hint = m.begin();
std::same_as<R> decltype(auto) r = m.emplace_hint(hint, typename M::value_type(2, 2.0));
assert(r == m.begin());
assert(m.size() == 3);
assert(r->first == 2);
assert(r->second == 2.0);
}
{
// hints correct in the middle
M m = {{0, 0.0}, {1, 1.0}, {3, 3.0}, {4, 4.0}};
auto hint = m.begin() + 2;
std::same_as<R> decltype(auto) r = m.emplace_hint(hint, typename M::value_type(2, 2.0));
assert(r == m.begin() + 2);
assert(m.size() == 5);
assert(r->first == 2);
assert(r->second == 2.0);
}
{
// hints correct at the end
M m = {{0, 0.0}, {1, 1.0}};
auto hint = m.end();
std::same_as<R> decltype(auto) r = m.emplace_hint(hint, typename M::value_type(2, 2.0));
assert(r == m.begin() + 2);
assert(m.size() == 3);
assert(r->first == 2);
assert(r->second == 2.0);
}
{
// hints correct but key already exists
M m = {{0, 0.0}, {1, 1.0}, {2, 1.9}, {3, 3.0}, {4, 4.0}};
auto hint = m.begin() + 2;
std::same_as<R> decltype(auto) r = m.emplace_hint(hint, typename M::value_type(2, 2.0));
assert(r == m.begin() + 2);
assert(m.size() == 5);
assert(r->first == 2);
assert(r->second == 1.9);
}
{
// hints incorrectly at the begin
M m = {{1, 1.0}, {4, 4.0}};
auto hint = m.begin();
std::same_as<R> decltype(auto) r = m.emplace_hint(hint, typename M::value_type(2, 2.0));
assert(r == m.begin() + 1);
assert(m.size() == 3);
assert(r->first == 2);
assert(r->second == 2.0);
}
{
// hints incorrectly in the middle
M m = {{0, 0.0}, {1, 1.0}, {3, 3.0}, {4, 4.0}};
auto hint = m.begin() + 1;
std::same_as<R> decltype(auto) r = m.emplace_hint(hint, typename M::value_type(2, 2.0));
assert(r == m.begin() + 2);
assert(m.size() == 5);
assert(r->first == 2);
assert(r->second == 2.0);
}
{
// hints incorrectly at the end
M m = {{0, 0.0}, {3, 3.0}};
auto hint = m.end();
std::same_as<R> decltype(auto) r = m.emplace_hint(hint, typename M::value_type(2, 2.0));
assert(r == m.begin() + 1);
assert(m.size() == 3);
assert(r->first == 2);
assert(r->second == 2.0);
}
{
// hints incorrect and key already exists
M m = {{0, 0.0}, {1, 1.0}, {2, 1.9}, {3, 3.0}, {4, 4.0}};
auto hint = m.begin();
std::same_as<R> decltype(auto) r = m.emplace_hint(hint, typename M::value_type(2, 2.0));
assert(r == m.begin() + 2);
assert(m.size() == 5);
assert(r->first == 2);
assert(r->second == 1.9);
}
}

template <class KeyContainer, class ValueContainer>
Expand Down Expand Up @@ -81,10 +163,10 @@ void test_emplaceable() {
}

int main(int, char**) {
test_simple<std::vector<int>, std::vector<double>>();
test_simple<std::deque<int>, std::vector<double>>();
test_simple<MinSequenceContainer<int>, MinSequenceContainer<double>>();
test_simple<std::vector<int, min_allocator<int>>, std::vector<double, min_allocator<double>>>();
test<std::vector<int>, std::vector<double>>();
test<std::deque<int>, std::vector<double>>();
test<MinSequenceContainer<int>, MinSequenceContainer<double>>();
test<std::vector<int, min_allocator<int>>, std::vector<double, min_allocator<double>>>();

test_emplaceable<std::vector<int>, std::vector<Emplaceable>>();
test_emplaceable<std::deque<int>, std::vector<Emplaceable>>();
Expand Down
2 changes: 2 additions & 0 deletions lld/test/wasm/lto/Inputs/thinlto_empty.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-unknown"
94 changes: 94 additions & 0 deletions lld/test/wasm/lto/obj-path.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
;; Copied from testr/ELF/lto/obj-path.ll
;; Test --lto-obj-path= for regular LTO.

; RUN: rm -rf %t && split-file %s %t && cd %t
; RUN: mkdir d
; RUN: opt 1.ll -o 1.bc
; RUN: opt 2.ll -o d/2.bc

; RUN: rm -f objpath.o
; RUN: wasm-ld --lto-obj-path=objpath.o -shared 1.bc d/2.bc -o 3
; RUN: llvm-nm 3 | FileCheck %s --check-prefix=NM
; RUN: llvm-objdump -d objpath.o | FileCheck %s
; RUN: ls 3* objpath* | count 2

; RUN: rm -f 3 objpath.o
; RUN: wasm-ld --thinlto-index-only=3.txt --lto-obj-path=objpath.o -shared 1.bc d/2.bc -o 3
; RUN: llvm-objdump -d objpath.o | FileCheck %s
; RUN: not ls 3

; NM: T f
; NM: T g

; CHECK: file format wasm
; CHECK: <f>:
; CHECK: <g>:

;; Test --lto-obj-path= for ThinLTO.
; RUN: opt -module-summary 1.ll -o 1.bc
; RUN: opt -module-summary 2.ll -o d/2.bc

; RUN: wasm-ld --lto-obj-path=objpath.o -shared 1.bc d/2.bc -o 3
; RUN: llvm-nm 3 | FileCheck %s --check-prefix=NM3
; RUN: llvm-objdump -d objpath.o1 | FileCheck %s --check-prefix=CHECK1
; RUN: llvm-objdump -d objpath.o2 | FileCheck %s --check-prefix=CHECK2

; NM3: T f
; NM3-NEXT: T g

; CHECK1: file format wasm
; CHECK1-EMPTY:
; CHECK1-NEXT: Disassembly of section CODE:
; CHECK1: <f>:
; CHECK1-EMPTY:
; CHECK1-NEXT: end
; CHECK1-NOT: {{.}}

; CHECK2: file format wasm
; CHECK2-EMPTY:
; CHECK2-NEXT: Disassembly of section CODE:
; CHECK2: <g>:
; CHECK2-EMPTY:
; CHECK2-NEXT: end
; CHECK2-NOT: {{.}}

;; With --thinlto-index-only, --lto-obj-path= creates just one file.
; RUN: rm -f objpath.o objpath.o1 objpath.o2
; RUN: wasm-ld --thinlto-index-only --lto-obj-path=objpath.o -shared 1.bc d/2.bc -o /dev/null
; RUN: llvm-objdump -d objpath.o | FileCheck %s --check-prefix=EMPTY
; RUN: not ls objpath.o1
; RUN: not ls objpath.o2

;; Ensure lld emits empty combined module if specific obj-path.
; RUN: mkdir obj
; RUN: wasm-ld --lto-obj-path=objpath.o -shared 1.bc d/2.bc -o obj/out --save-temps
; RUN: ls obj/out.lto.o out.lto.1.o d/out.lto.2.o

;; Ensure lld does not emit empty combined module by default.
; RUN: rm -fr obj && mkdir obj
; RUN: wasm-ld -shared 1.bc d/2.bc -o obj/out --save-temps
; RUN: not test -e obj/out.lto.o

; EMPTY: file format wasm
; EMPTY-NOT: {{.}}

;--- 1.ll
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-unknown"

declare void @g(...)

define void @f() {
entry:
call void (...) @g()
ret void
}

;--- 2.ll
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-unknown"

define void @g() {
entry:
ret void
}
10 changes: 5 additions & 5 deletions lld/test/wasm/lto/parallel.ll
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
; RUN: llvm-as -o %t.bc %s
; RUN: rm -f %t.lto.o %t1.lto.o
; RUN: wasm-ld --lto-partitions=2 -save-temps -o %t %t.bc -r
; RUN: llvm-nm %t.lto.o | FileCheck --check-prefix=CHECK0 %s
; RUN: llvm-nm %t1.lto.o | FileCheck --check-prefix=CHECK1 %s
; RUN: rm -rf %t && mkdir %t && cd %t
; RUN: llvm-as -o a.bc %s
; RUN: wasm-ld --lto-partitions=2 -save-temps -o out a.bc -r
; RUN: llvm-nm out.lto.o | FileCheck --check-prefix=CHECK0 %s
; RUN: llvm-nm out.lto.1.o | FileCheck --check-prefix=CHECK1 %s

target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-unknown-wasm"
Expand Down
145 changes: 145 additions & 0 deletions lld/test/wasm/lto/thinlto-index-only.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
; RUN: rm -rf %t && split-file %s %t && cd %t
; RUN: mkdir d

;; First ensure that the ThinLTO handling in lld handles
;; bitcode without summary sections gracefully and generates index file.
; RUN: llvm-as 1.ll -o 1.o
; RUN: llvm-as %p/Inputs/thinlto.ll -o d/2.o
; RUN: wasm-ld --thinlto-index-only -shared 1.o d/2.o -o 3
; RUN: ls d/2.o.thinlto.bc
; RUN: not test -e 3
; RUN: wasm-ld -shared 1.o d/2.o -o 3
; RUN: llvm-nm 3 | FileCheck %s --check-prefix=NM

;; Basic ThinLTO tests.
; RUN: llvm-as 0.ll -o 0.o
; RUN: opt -module-summary 1.ll -o 1.o
; RUN: opt -module-summary %p/Inputs/thinlto.ll -o d/2.o
; RUN: opt -module-summary %p/Inputs/thinlto_empty.ll -o 3.o
; RUN: cp 3.o 4.o

;; Ensure lld doesn't generates index files when --thinlto-index-only is not enabled.
; RUN: rm -f 1.o.thinlto.bc d/2.o.thinlto.bc
; RUN: wasm-ld -shared 1.o d/2.o -o /dev/null
; RUN: not ls 1.o.thinlto.bc
; RUN: not ls d/2.o.thinlto.bc

;; Ensure lld generates an index and not a binary if requested.
; RUN: wasm-ld --thinlto-index-only -shared 1.o --start-lib d/2.o 3.o --end-lib 4.o -o 4
; RUN: not test -e 4
; RUN: llvm-bcanalyzer -dump 1.o.thinlto.bc | FileCheck %s --check-prefix=BACKEND1
; RUN: llvm-bcanalyzer -dump d/2.o.thinlto.bc | FileCheck %s --check-prefix=BACKEND2
; RUN: llvm-dis < 3.o.thinlto.bc | FileCheck %s --check-prefix=BACKEND3
; RUN: llvm-dis < 4.o.thinlto.bc | FileCheck %s --check-prefix=BACKEND4

; RUN: rm -f 1.o.thinlto.bc d/2.o.thinlto.bc 3.o.thinlto.bc 4.o.thinlto.bc
; RUN: wasm-ld --thinlto-index-only=4.txt --thinlto-emit-imports-files -shared 1.o --start-lib d/2.o 3.o --end-lib 4.o -o 4
; RUN: not test -e 4
; RUN: FileCheck %s --check-prefix=RSP --implicit-check-not={{.}} < 4.txt
; RUN: llvm-bcanalyzer -dump 1.o.thinlto.bc | FileCheck %s --check-prefix=BACKEND1
; RUN: llvm-bcanalyzer -dump d/2.o.thinlto.bc | FileCheck %s --check-prefix=BACKEND2
; RUN: llvm-dis < 3.o.thinlto.bc | FileCheck %s --check-prefix=BACKEND3
; RUN: llvm-dis < 4.o.thinlto.bc | FileCheck %s --check-prefix=BACKEND4
; RUN: FileCheck %s --check-prefix=IMPORTS1 --implicit-check-not={{.}} < 1.o.imports
; RUN: count 0 < d/2.o.imports
;; Test that LLD generates an empty index even for lazy object file that is not added to link.
; RUN: count 0 < 3.o.imports
; RUN: count 0 < 4.o.imports

;; Test interaction with --save-temps.
; RUN: rm -f 4.txt 1.o.thinlto.bc d/2.o.thinlto.bc 3.o.thinlto.bc 4.o.thinlto.bc
; RUN: wasm-ld --thinlto-index-only=4.txt --thinlto-emit-imports-files --save-temps -shared 0.o 1.o --start-lib d/2.o 3.o --end-lib 4.o -o t
; RUN: not test -e 4
; RUN: FileCheck %s --check-prefix=RSP --implicit-check-not={{.}} < 4.txt
; RUN: llvm-bcanalyzer -dump 1.o.thinlto.bc | FileCheck %s --check-prefix=BACKEND1
; RUN: FileCheck %s --check-prefix=IMPORTS1 --implicit-check-not={{.}} < 1.o.imports
; RUN: FileCheck %s --check-prefix=RESOLUTION < t.resolution.txt
; RUN: llvm-dis < t.index.bc | FileCheck %s --check-prefix=INDEX-BC

; RSP: 1.o
; RSP-NEXT: d/2.o
; RSP-NEXT: 4.o

; IMPORTS1: d/2.o

; RESOLUTION: 0.o
; RESOLUTION-NEXT: -r=0.o,foo,px
; RESOLUTION-NEXT: 1.o

; INDEX-BC: ^0 = module: (path: "1.o", hash: (0, 0, 0, 0, 0))
; INDEX-BC-NEXT: ^1 = module: (path: "4.o", hash: (0, 0, 0, 0, 0))
; INDEX-BC-NEXT: ^2 = module: (path: "d/2.o", hash: (0, 0, 0, 0, 0))

;; Ensure LLD generates an empty index for each bitcode file even if all bitcode files are lazy.
; RUN: rm -f 1.o.thinlto.bc
; RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown /dev/null -o dummy.o
; RUN: wasm-ld --thinlto-index-only -shared dummy.o --start-lib 1.o --end-lib -o /dev/null
; RUN: ls 1.o.thinlto.bc

;; Ensure when the same bitcode object is given as both lazy and non-lazy,
;; LLD does not generate an empty index for the lazy object.
; RUN: rm -f d/2.o.thinlto.bc
; RUN: wasm-ld --thinlto-index-only -shared 1.o d/2.o --start-lib d/2.o --end-lib -o /dev/null
; RUN: llvm-dis < d/2.o.thinlto.bc | grep -q '\^0 = module:'
; RUN: rm -f d/2.o.thinlto.bc
; RUN: wasm-ld --thinlto-index-only -shared --start-lib d/2.o --end-lib d/2.o 1.o -o /dev/null
; RUN: llvm-dis < d/2.o.thinlto.bc | grep -q '\^0 = module:'

;; Ensure when the same lazy bitcode object is given multiple times,
;; no empty index file is generated if one of the copies is linked.
; RUN: rm -f d/2.o.thinlto.bc
; RUN: wasm-ld --thinlto-index-only -shared 1.o --start-lib d/2.o --end-lib --start-lib d/2.o --end-lib -o /dev/null
; RUN: llvm-dis < d/2.o.thinlto.bc | grep -q '\^0 = module:'

; NM: T f

;; The backend index for this module contains summaries from itself and
;; Inputs/thinlto.ll, as it imports from the latter.
; BACKEND1: <MODULE_STRTAB_BLOCK
; BACKEND1-NEXT: <ENTRY {{.*}} record string = '1.o'
; BACKEND1-NEXT: <ENTRY {{.*}} record string = 'd/2.o'
; BACKEND1-NEXT: </MODULE_STRTAB_BLOCK
; BACKEND1: <GLOBALVAL_SUMMARY_BLOCK
; BACKEND1: <VERSION
; BACKEND1: <FLAGS
; BACKEND1: <VALUE_GUID {{.*}} op0={{1|2}} {{op1=3060885059 op2=1207956914|op1=3432075125 op2=3712786831}}
; BACKEND1: <VALUE_GUID {{.*}} op0={{1|2}} {{op1=3060885059 op2=1207956914|op1=3432075125 op2=3712786831}}
; BACKEND1: <COMBINED
; BACKEND1: <COMBINED
; BACKEND1: </GLOBALVAL_SUMMARY_BLOCK

;; The backend index for Input/thinlto.ll contains summaries from itself only,
;; as it does not import anything.
; BACKEND2: <MODULE_STRTAB_BLOCK
; BACKEND2-NEXT: <ENTRY {{.*}} record string = 'd/2.o'
; BACKEND2-NEXT: </MODULE_STRTAB_BLOCK
; BACKEND2-NEXT: <GLOBALVAL_SUMMARY_BLOCK
; BACKEND2-NEXT: <VERSION
; BACKEND2-NEXT: <FLAGS
; BACKEND2-NEXT: <VALUE_GUID {{.*}} op0=1 op1=3060885059 op2=1207956914
; BACKEND2-NEXT: <COMBINED
; BACKEND2-NEXT: </GLOBALVAL_SUMMARY_BLOCK

; BACKEND3: ^0 = flags:

; BACKEND4: ^0 = module: (path: "4.o", hash: (0, 0, 0, 0, 0))

;--- 0.ll
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-unknown"

define void @foo() {
ret void
}

;--- 1.ll
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
target triple = "wasm32-unknown-unknown"

declare void @g(...)

define void @f() {
entry:
call void (...) @g()
ret void
}
67 changes: 35 additions & 32 deletions lld/test/wasm/lto/thinlto.ll
Original file line number Diff line number Diff line change
@@ -1,53 +1,56 @@
; Basic ThinLTO tests.
; RUN: opt -module-summary %s -o %t1.o
; RUN: opt -module-summary %p/Inputs/thinlto.ll -o %t2.o
; RUN: rm -rf %t && mkdir %t && cd %t
; RUN: mkdir d e

; RUN: opt -module-summary %s -o a.o
; RUN: opt -module-summary %p/Inputs/thinlto.ll -o d/b.o

; First force single-threaded mode
; RUN: rm -f %t31.lto.o %t32.lto.o
; RUN: wasm-ld -r -save-temps --thinlto-jobs=1 %t1.o %t2.o -o %t3
; RUN: llvm-nm %t31.lto.o | FileCheck %s --check-prefix=NM1
; RUN: llvm-nm %t32.lto.o | FileCheck %s --check-prefix=NM2
; RUN: rm -f out.lto.a.o d/out.lto.b.o
; RUN: wasm-ld -r -save-temps --thinlto-jobs=1 a.o d/b.o -o e/out
; RUN: llvm-nm out.lto.a.o | FileCheck %s --check-prefix=NM1
; RUN: llvm-nm d/out.lto.b.o | FileCheck %s --check-prefix=NM2

; Next force multi-threaded mode
; RUN: rm -f %t31.lto.o %t32.lto.o
; RUN: wasm-ld -r -save-temps --thinlto-jobs=2 %t1.o %t2.o -o %t3
; RUN: llvm-nm %t31.lto.o | FileCheck %s --check-prefix=NM1
; RUN: llvm-nm %t32.lto.o | FileCheck %s --check-prefix=NM2
; RUN: rm -f out.lto.a.o d/out.lto.b.o
; RUN: wasm-ld -r -save-temps --thinlto-jobs=2 a.o d/b.o -o e/out
; RUN: llvm-nm out.lto.a.o | FileCheck %s --check-prefix=NM1
; RUN: llvm-nm d/out.lto.b.o | FileCheck %s --check-prefix=NM2

;; --thinlto-jobs= defaults to --threads=.
; RUN: rm -f %t31.lto.o %t32.lto.o
; RUN: wasm-ld -r -save-temps --threads=2 %t1.o %t2.o -o %t3
; RUN: llvm-nm %t31.lto.o | FileCheck %s --check-prefix=NM1
; RUN: llvm-nm %t32.lto.o | FileCheck %s --check-prefix=NM2
; RUN: rm -f out.lto.a.o d/out.lto.b.o
; RUN: wasm-ld -r -save-temps --threads=2 a.o d/b.o -o e/out
; RUN: llvm-nm out.lto.a.o | FileCheck %s --check-prefix=NM1
; RUN: llvm-nm d/out.lto.b.o | FileCheck %s --check-prefix=NM2

;; --thinlto-jobs= overrides --threads=.
; RUN: rm -f %t31.lto.o %t32.lto.o
; RUN: wasm-ld -r -save-temps --threads=1 --thinlto-jobs=2 %t1.o %t2.o -o %t3
; RUN: llvm-nm %t31.lto.o | FileCheck %s --check-prefix=NM1
; RUN: llvm-nm %t32.lto.o | FileCheck %s --check-prefix=NM2
; RUN: rm -f out.lto.a.o d/out.lto.b.o
; RUN: wasm-ld -r -save-temps --threads=1 --thinlto-jobs=2 a.o d/b.o -o e/out
; RUN: llvm-nm out.lto.a.o | FileCheck %s --check-prefix=NM1
; RUN: llvm-nm d/out.lto.b.o | FileCheck %s --check-prefix=NM2

; Test with all threads, on all cores, on all CPU sockets
; RUN: rm -f %t31.lto.o %t32.lto.o
; RUN: wasm-ld -r -save-temps --thinlto-jobs=all %t1.o %t2.o -o %t3
; RUN: llvm-nm %t31.lto.o | FileCheck %s --check-prefix=NM1
; RUN: llvm-nm %t32.lto.o | FileCheck %s --check-prefix=NM2
; RUN: rm -f out.lto.a.o d/out.lto.b.o
; RUN: wasm-ld -r -save-temps --thinlto-jobs=all a.o d/b.o -o e/out
; RUN: llvm-nm out.lto.a.o | FileCheck %s --check-prefix=NM1
; RUN: llvm-nm d/out.lto.b.o | FileCheck %s --check-prefix=NM2

; Test with many more threads than the system has
; RUN: rm -f %t31.lto.o %t32.lto.o
; RUN: wasm-ld -r -save-temps --thinlto-jobs=100 %t1.o %t2.o -o %t3
; RUN: llvm-nm %t31.lto.o | FileCheck %s --check-prefix=NM1
; RUN: llvm-nm %t32.lto.o | FileCheck %s --check-prefix=NM2
; RUN: rm -f out.lto.a.o d/out.lto.b.o
; RUN: wasm-ld -r -save-temps --thinlto-jobs=100 a.o d/b.o -o e/out
; RUN: llvm-nm out.lto.a.o | FileCheck %s --check-prefix=NM1
; RUN: llvm-nm d/out.lto.b.o | FileCheck %s --check-prefix=NM2

; Test with a bad value
; RUN: rm -f %t31.lto.o %t32.lto.o
; RUN: not wasm-ld -r -save-temps --thinlto-jobs=foo %t1.o %t2.o -o %t3 2>&1 | FileCheck %s --check-prefix=BAD-JOBS
; RUN: rm -f out.lto.a.o d/out.lto.b.o
; RUN: not wasm-ld -r -save-temps --thinlto-jobs=foo a.o d/b.o -o e/out 2>&1 | FileCheck %s --check-prefix=BAD-JOBS
; BAD-JOBS: error: --thinlto-jobs: invalid job count: foo

; Check without --thinlto-jobs (which currently defaults to heavyweight_hardware_concurrency, meanning one thread per hardware core -- not SMT)
; RUN: rm -f %t31.lto.o %t32.lto.o
; RUN: wasm-ld -r -save-temps %t1.o %t2.o -o %t3
; RUN: llvm-nm %t31.lto.o | FileCheck %s --check-prefix=NM1
; RUN: llvm-nm %t32.lto.o | FileCheck %s --check-prefix=NM2
; RUN: rm -f out.lto.a.o d/out.lto.b.o
; RUN: wasm-ld -r -save-temps a.o d/b.o -o e/out
; RUN: llvm-nm out.lto.a.o | FileCheck %s --check-prefix=NM1
; RUN: llvm-nm d/out.lto.b.o | FileCheck %s --check-prefix=NM2

; NM1: T f
; NM2: T g
Expand Down
1 change: 1 addition & 0 deletions lld/wasm/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ add_lld_library(lldWasm
LINK_COMPONENTS
${LLVM_TARGETS_TO_BUILD}
BinaryFormat
BitWriter
Core
Demangle
LTO
Expand Down
8 changes: 7 additions & 1 deletion lld/wasm/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ struct Configuration {
// Because dyamanic linking under Wasm is still experimental we default to
// static linking
bool isStatic = true;
bool thinLTOEmitImportsFiles;
bool thinLTOEmitIndexFiles;
bool thinLTOIndexOnly;
bool trace;
uint64_t globalBase;
uint64_t initialHeap;
Expand All @@ -95,16 +98,18 @@ struct Configuration {
unsigned ltoo;
llvm::CodeGenOptLevel ltoCgo;
unsigned optimize;
llvm::StringRef thinLTOJobs;
bool ltoDebugPassManager;
UnresolvedPolicy unresolvedSymbols;
BuildIdKind buildId = BuildIdKind::None;

llvm::StringRef entry;
llvm::StringRef ltoObjPath;
llvm::StringRef mapFile;
llvm::StringRef outputFile;
llvm::StringRef soName;
llvm::StringRef thinLTOCacheDir;
llvm::StringRef thinLTOJobs;
llvm::StringRef thinLTOIndexOnlyArg;
llvm::StringRef whyExtract;

llvm::StringSet<> allowUndefinedSymbols;
Expand All @@ -126,6 +131,7 @@ struct Ctx {
llvm::SmallVector<StubFile *, 0> stubFiles;
llvm::SmallVector<SharedFile *, 0> sharedFiles;
llvm::SmallVector<BitcodeFile *, 0> bitcodeFiles;
llvm::SmallVector<BitcodeFile *, 0> lazyBitcodeFiles;
llvm::SmallVector<InputFunction *, 0> syntheticFunctions;
llvm::SmallVector<InputGlobal *, 0> syntheticGlobals;
llvm::SmallVector<InputTable *, 0> syntheticTables;
Expand Down
12 changes: 12 additions & 0 deletions lld/wasm/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,7 @@ static void readConfigs(opt::InputArgList &args) {
else
error("invalid codegen optimization level for LTO: " + Twine(ltoCgo));
config->ltoPartitions = args::getInteger(args, OPT_lto_partitions, 1);
config->ltoObjPath = args.getLastArgValue(OPT_lto_obj_path_eq);
config->ltoDebugPassManager = args.hasArg(OPT_lto_debug_pass_manager);
config->mapFile = args.getLastArgValue(OPT_Map);
config->optimize = args::getInteger(args, OPT_O, 1);
Expand Down Expand Up @@ -569,6 +570,13 @@ static void readConfigs(opt::InputArgList &args) {
config->thinLTOCachePolicy = CHECK(
parseCachePruningPolicy(args.getLastArgValue(OPT_thinlto_cache_policy)),
"--thinlto-cache-policy: invalid cache policy");
config->thinLTOEmitImportsFiles = args.hasArg(OPT_thinlto_emit_imports_files);
config->thinLTOEmitIndexFiles = args.hasArg(OPT_thinlto_emit_index_files) ||
args.hasArg(OPT_thinlto_index_only) ||
args.hasArg(OPT_thinlto_index_only_eq);
config->thinLTOIndexOnly = args.hasArg(OPT_thinlto_index_only) ||
args.hasArg(OPT_thinlto_index_only_eq);
config->thinLTOIndexOnlyArg = args.getLastArgValue(OPT_thinlto_index_only_eq);
config->unresolvedSymbols = getUnresolvedSymbolPolicy(args);
config->whyExtract = args.getLastArgValue(OPT_why_extract);
errorHandler().verbose = args.hasArg(OPT_verbose);
Expand Down Expand Up @@ -1379,6 +1387,10 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {

writeWhyExtract();

// Bail out if normal linked output is skipped due to LTO.
if (config->thinLTOIndexOnly)
return;

createOptionalSymbols();

// Resolve any variant symbols that were created due to signature
Expand Down
138 changes: 123 additions & 15 deletions lld/wasm/LTO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,16 @@
#include "InputFiles.h"
#include "Symbols.h"
#include "lld/Common/Args.h"
#include "lld/Common/CommonLinkerContext.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Filesystem.h"
#include "lld/Common/Strings.h"
#include "lld/Common/TargetOptionsCommandFlags.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Bitcode/BitcodeWriter.h"
#include "llvm/IR/DiagnosticPrinter.h"
#include "llvm/LTO/Config.h"
#include "llvm/LTO/LTO.h"
Expand All @@ -27,6 +30,7 @@
#include "llvm/Support/Error.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cstddef>
Expand All @@ -36,9 +40,10 @@
#include <vector>

using namespace llvm;
using namespace lld::wasm;
using namespace lld;

namespace lld::wasm {
static std::unique_ptr<lto::LTO> createLTO() {
static lto::Config createConfig() {
lto::Config c;
c.Options = initTargetOptionsFromCodeGenFlags();

Expand All @@ -52,6 +57,7 @@ static std::unique_ptr<lto::LTO> createLTO() {
c.MAttrs = getMAttrs();
c.CGOptLevel = config->ltoCgo;
c.DebugPassManager = config->ltoDebugPassManager;
c.AlwaysEmitRegularLTOObj = !config->ltoObjPath.empty();

if (config->relocatable)
c.RelocModel = std::nullopt;
Expand All @@ -63,13 +69,32 @@ static std::unique_ptr<lto::LTO> createLTO() {
if (config->saveTemps)
checkError(c.addSaveTemps(config->outputFile.str() + ".",
/*UseInputModulePath*/ true));
lto::ThinBackend backend = lto::createInProcessThinBackend(
llvm::heavyweight_hardware_concurrency(config->thinLTOJobs));
return std::make_unique<lto::LTO>(std::move(c), backend,
config->ltoPartitions);
return c;
}

BitcodeCompiler::BitcodeCompiler() : ltoObj(createLTO()) {}
namespace lld::wasm {

BitcodeCompiler::BitcodeCompiler() {
// Initialize indexFile.
if (!config->thinLTOIndexOnlyArg.empty())
indexFile = openFile(config->thinLTOIndexOnlyArg);

// Initialize ltoObj.
lto::ThinBackend backend;
auto onIndexWrite = [&](StringRef s) { thinIndices.erase(s); };
if (config->thinLTOIndexOnly) {
backend = lto::createWriteIndexesThinBackend(
llvm::hardware_concurrency(config->thinLTOJobs), "", "", "",
config->thinLTOEmitImportsFiles, indexFile.get(), onIndexWrite);
} else {
backend = lto::createInProcessThinBackend(
llvm::heavyweight_hardware_concurrency(config->thinLTOJobs),
onIndexWrite, config->thinLTOEmitIndexFiles,
config->thinLTOEmitImportsFiles);
}
ltoObj = std::make_unique<lto::LTO>(createConfig(), backend,
config->ltoPartitions);
}

BitcodeCompiler::~BitcodeCompiler() = default;

Expand All @@ -90,6 +115,10 @@ void BitcodeCompiler::add(BitcodeFile &f) {
ArrayRef<Symbol *> syms = f.getSymbols();
std::vector<lto::SymbolResolution> resols(syms.size());

if (config->thinLTOEmitIndexFiles) {
thinIndices.insert(obj.getName());
}

// Provide a resolution to the LTO API for each symbol.
for (const lto::InputFile::Symbol &objSym : obj.symbols()) {
Symbol *sym = syms[symNum];
Expand All @@ -116,6 +145,32 @@ void BitcodeCompiler::add(BitcodeFile &f) {
checkError(ltoObj->add(std::move(f.obj), resols));
}

// If LazyObjFile has not been added to link, emit empty index files.
// This is needed because this is what GNU gold plugin does and we have a
// distributed build system that depends on that behavior.
static void thinLTOCreateEmptyIndexFiles() {
DenseSet<StringRef> linkedBitCodeFiles;
for (BitcodeFile *f : ctx.bitcodeFiles)
linkedBitCodeFiles.insert(f->getName());

for (BitcodeFile *f : ctx.lazyBitcodeFiles) {
if (!f->lazy)
continue;
if (linkedBitCodeFiles.contains(f->getName()))
continue;
std::string path(f->obj->getName());
std::unique_ptr<raw_fd_ostream> os = openFile(path + ".thinlto.bc");
if (!os)
continue;

ModuleSummaryIndex m(/*HaveGVs*/ false);
m.setSkipModuleByDistributedBackend();
writeIndexToFile(m, *os);
if (config->thinLTOEmitImportsFiles)
openFile(path + ".imports");
}
}

// Merge all the bitcode files we have seen, codegen the result
// and return the resulting objects.
std::vector<StringRef> BitcodeCompiler::compile() {
Expand All @@ -136,25 +191,78 @@ std::vector<StringRef> BitcodeCompiler::compile() {

checkError(ltoObj->run(
[&](size_t task, const Twine &moduleName) {
buf[task].first = moduleName.str();
return std::make_unique<CachedFileStream>(
std::make_unique<raw_svector_ostream>(buf[task]));
std::make_unique<raw_svector_ostream>(buf[task].second));
},
cache));

// Emit empty index files for non-indexed files but not in single-module mode.
for (StringRef s : thinIndices) {
std::string path(s);
openFile(path + ".thinlto.bc");
if (config->thinLTOEmitImportsFiles)
openFile(path + ".imports");
}

if (config->thinLTOEmitIndexFiles)
thinLTOCreateEmptyIndexFiles();

if (config->thinLTOIndexOnly) {
if (!config->ltoObjPath.empty())
saveBuffer(buf[0].second, config->ltoObjPath);

// ThinLTO with index only option is required to generate only the index
// files. After that, we exit from linker and ThinLTO backend runs in a
// distributed environment.
if (indexFile)
indexFile->close();
return {};
}

if (!config->thinLTOCacheDir.empty())
pruneCache(config->thinLTOCacheDir, config->thinLTOCachePolicy, files);

std::vector<StringRef> ret;
for (unsigned i = 0; i != maxTasks; ++i) {
if (buf[i].empty())
StringRef objBuf = buf[i].second;
StringRef bitcodeFilePath = buf[i].first;
if (objBuf.empty())
continue;
ret.emplace_back(objBuf.data(), objBuf.size());
if (!config->saveTemps)
continue;
if (config->saveTemps) {
if (i == 0)
saveBuffer(buf[i], config->outputFile + ".lto.o");
else
saveBuffer(buf[i], config->outputFile + Twine(i) + ".lto.o");

// If the input bitcode file is path/to/x.o and -o specifies a.out, the
// corresponding native relocatable file path will look like:
// path/to/a.out.lto.x.o.
StringRef ltoObjName;
if (bitcodeFilePath == "ld-temp.o") {
ltoObjName =
saver().save(Twine(config->outputFile) + ".lto" +
(i == 0 ? Twine("") : Twine('.') + Twine(i)) + ".o");
} else {
StringRef directory = sys::path::parent_path(bitcodeFilePath);
// For an archive member, which has an identifier like "d/a.a(coll.o at
// 8)" (see BitcodeFile::BitcodeFile), use the filename; otherwise, use
// the stem (d/a.o => a).
StringRef baseName = bitcodeFilePath.ends_with(")")
? sys::path::filename(bitcodeFilePath)
: sys::path::stem(bitcodeFilePath);
StringRef outputFileBaseName = sys::path::filename(config->outputFile);
SmallString<256> path;
sys::path::append(path, directory,
outputFileBaseName + ".lto." + baseName + ".o");
sys::path::remove_dots(path, true);
ltoObjName = saver().save(path.str());
}
ret.emplace_back(buf[i].data(), buf[i].size());
saveBuffer(objBuf, ltoObjName);
}

if (!config->ltoObjPath.empty()) {
saveBuffer(buf[0].second, config->ltoObjPath);
for (unsigned i = 1; i != maxTasks; ++i)
saveBuffer(buf[i].second, config->ltoObjPath + Twine(i));
}

for (std::unique_ptr<MemoryBuffer> &file : files)
Expand Down
9 changes: 7 additions & 2 deletions lld/wasm/LTO.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@
#ifndef LLD_WASM_LTO_H
#define LLD_WASM_LTO_H

#include "Writer.h"
#include "lld/Common/LLVM.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallString.h"
#include "Writer.h"
#include "llvm/Support/raw_ostream.h"
#include <memory>
#include <vector>

Expand All @@ -47,8 +49,11 @@ class BitcodeCompiler {

private:
std::unique_ptr<llvm::lto::LTO> ltoObj;
std::vector<SmallString<0>> buf;
// An array of (module name, native relocatable file content) pairs.
SmallVector<std::pair<std::string, SmallString<0>>, 0> buf;
std::vector<std::unique_ptr<MemoryBuffer>> files;
std::unique_ptr<llvm::raw_fd_ostream> indexFile;
llvm::DenseSet<StringRef> thinIndices;
};
} // namespace lld::wasm

Expand Down
5 changes: 5 additions & 0 deletions lld/wasm/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -297,11 +297,16 @@ def lto_CGO: JJ<"lto-CGO">, MetaVarName<"<cgopt-level>">,
HelpText<"Codegen optimization level for LTO">;
def lto_partitions: JJ<"lto-partitions=">,
HelpText<"Number of LTO codegen partitions">;
def lto_obj_path_eq: JJ<"lto-obj-path=">;
def disable_verify: F<"disable-verify">;
def save_temps: F<"save-temps">, HelpText<"Save intermediate LTO compilation results">;
def thinlto_cache_dir: JJ<"thinlto-cache-dir=">,
HelpText<"Path to ThinLTO cached object file directory">;
defm thinlto_cache_policy: EEq<"thinlto-cache-policy", "Pruning policy for the ThinLTO cache">;
def thinlto_emit_index_files: FF<"thinlto-emit-index-files">;
def thinlto_emit_imports_files: FF<"thinlto-emit-imports-files">;
def thinlto_index_only: FF<"thinlto-index-only">;
def thinlto_index_only_eq: JJ<"thinlto-index-only=">;
def thinlto_jobs: JJ<"thinlto-jobs=">,
HelpText<"Number of ThinLTO jobs. Default to --threads=">;
def lto_debug_pass_manager: FF<"lto-debug-pass-manager">,
Expand Down
4 changes: 1 addition & 3 deletions lld/wasm/SymbolTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ void SymbolTable::addFile(InputFile *file, StringRef symName) {
// Lazy object file
if (file->lazy) {
if (auto *f = dyn_cast<BitcodeFile>(file)) {
ctx.lazyBitcodeFiles.push_back(f);
f->parseLazy();
} else {
cast<ObjFile>(file)->parseLazy();
Expand Down Expand Up @@ -81,9 +82,6 @@ void SymbolTable::compileBitcodeFiles() {
// Prevent further LTO objects being included
BitcodeFile::doneLTO = true;

if (ctx.bitcodeFiles.empty())
return;

// Compile bitcode files and replace bitcode symbols.
lto.reset(new BitcodeCompiler);
for (BitcodeFile *f : ctx.bitcodeFiles)
Expand Down
10 changes: 10 additions & 0 deletions lldb/include/lldb/Interpreter/CommandReturnObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ class CommandReturnObject {
void AppendMessageWithFormat(const char *format, ...)
__attribute__((format(printf, 2, 3)));

void AppendNote(llvm::StringRef in_string);

void AppendNoteWithFormat(const char *format, ...)
__attribute__((format(printf, 2, 3)));

void AppendWarning(llvm::StringRef in_string);

void AppendWarningWithFormat(const char *format, ...)
Expand All @@ -127,6 +132,11 @@ class CommandReturnObject {
AppendMessage(llvm::formatv(format, std::forward<Args>(args)...).str());
}

template <typename... Args>
void AppendNoteWithFormatv(const char *format, Args &&...args) {
AppendNote(llvm::formatv(format, std::forward<Args>(args)...).str());
}

template <typename... Args>
void AppendWarningWithFormatv(const char *format, Args &&... args) {
AppendWarning(llvm::formatv(format, std::forward<Args>(args)...).str());
Expand Down
15 changes: 7 additions & 8 deletions lldb/source/Commands/CommandObjectDWIMPrint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,10 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command,
if (note_shown)
return;

result.GetOutputStream()
<< "note: object description requested, but type doesn't implement "
"a custom object description. Consider using \"p\" instead of "
"\"po\" (this note will only be shown once per debug session).\n";
result.AppendNote(
"object description requested, but type doesn't implement "
"a custom object description. Consider using \"p\" instead of "
"\"po\" (this note will only be shown once per debug session).\n");
note_shown = true;
}
};
Expand Down Expand Up @@ -164,8 +164,8 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command,
StringRef flags;
if (args.HasArgs())
flags = args.GetArgString();
result.AppendMessageWithFormatv("note: ran `frame variable {0}{1}`",
flags, expr);
result.AppendNoteWithFormatv("ran `frame variable {0}{1}`", flags,
expr);
}

dump_val_object(*valobj_sp);
Expand Down Expand Up @@ -224,8 +224,7 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command,
StringRef flags;
if (args.HasArgs())
flags = args.GetArgStringWithDelimiter();
result.AppendMessageWithFormatv("note: ran `expression {0}{1}`", flags,
expr);
result.AppendNoteWithFormatv("ran `expression {0}{1}`", flags, expr);
}

if (valobj_sp->GetError().GetError() != UserExpression::kNoResult)
Expand Down
24 changes: 24 additions & 0 deletions lldb/source/Interpreter/CommandReturnObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ static llvm::raw_ostream &warning(Stream &strm) {
<< "warning: ";
}

static llvm::raw_ostream &note(Stream &strm) {
return llvm::WithColor(strm.AsRawOstream(), llvm::HighlightColor::Note,
llvm::ColorMode::Enable)
<< "note: ";
}

static void DumpStringToStreamWithNewline(Stream &strm, const std::string &s) {
bool add_newline = false;
if (!s.empty()) {
Expand Down Expand Up @@ -74,6 +80,18 @@ void CommandReturnObject::AppendMessageWithFormat(const char *format, ...) {
GetOutputStream() << sstrm.GetString();
}

void CommandReturnObject::AppendNoteWithFormat(const char *format, ...) {
if (!format)
return;
va_list args;
va_start(args, format);
StreamString sstrm;
sstrm.PrintfVarArg(format, args);
va_end(args);

note(GetOutputStream()) << sstrm.GetString();
}

void CommandReturnObject::AppendWarningWithFormat(const char *format, ...) {
if (!format)
return;
Expand All @@ -92,6 +110,12 @@ void CommandReturnObject::AppendMessage(llvm::StringRef in_string) {
GetOutputStream() << in_string.rtrim() << '\n';
}

void CommandReturnObject::AppendNote(llvm::StringRef in_string) {
if (in_string.empty())
return;
note(GetOutputStream()) << in_string.rtrim() << '\n';
}

void CommandReturnObject::AppendWarning(llvm::StringRef in_string) {
if (in_string.empty())
return;
Expand Down
14 changes: 8 additions & 6 deletions llvm/docs/LangRef.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1161,15 +1161,16 @@ parameters of a function. Parameter attributes are considered to be part
of the function, not of the function type, so functions with different
parameter attributes can have the same function type.

Parameter attributes are simple keywords that follow the type specified.
If multiple parameter attributes are needed, they are space separated.
For example:
Parameter attributes are either simple keywords or strings that follow the
specified type. Multiple parameter attributes, when required, are separated by
spaces. For example:

.. code-block:: llvm

declare i32 @printf(ptr noalias nocapture, ...)
declare i32 @atoi(i8 zeroext)
declare signext i8 @returns_signed_char()
define void @baz(i32 "amdgpu-flat-work-group-size"="1,256" %x)

Note that any attributes for the function result (``nonnull``,
``signext``) come before the result type.
Expand Down Expand Up @@ -1843,16 +1844,17 @@ a function. Function attributes are considered to be part of the
function, not of the function type, so functions with different function
attributes can have the same function type.

Function attributes are simple keywords that follow the type specified.
If multiple attributes are needed, they are space separated. For
example:
Function attributes are simple keywords or strings that follow the specified
type. Multiple attributes, when required, are separated by spaces.
For example:

.. code-block:: llvm

define void @f() noinline { ... }
define void @f() alwaysinline { ... }
define void @f() alwaysinline optsize { ... }
define void @f() optsize { ... }
define void @f() "no-sse" { ... }

``alignstack(<n>)``
This attribute indicates that, when emitting the prologue and
Expand Down
53 changes: 53 additions & 0 deletions llvm/docs/NVPTXUsage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,59 @@ right, and the least significant bits are extracted to produce a result that is
the same size as the original arguments. The shift amount is the minimum of the
value of %n and the bit width of the integer type.

'``llvm.nvvm.flo.u.*``' Intrinsic
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Syntax:
"""""""

.. code-block:: llvm
declare i32 @llvm.nvvm.flo.u.i32(i32 %a, i1 %shiftamt)
declare i32 @llvm.nvvm.flo.u.i64(i64 %a, i1 %shiftamt)
Overview:
"""""""""

The '``llvm.nvvm.flo.u``' family of intrinsics identifies the bit position of the
leading one, returning either it's offset from the most or least significant bit.

Semantics:
""""""""""

The '``llvm.nvvm.flo.u``' family of intrinsics returns the bit position of the
most significant 1. If %shiftamt is true, The result is the shift amount needed
to left-shift the found bit into the most-significant bit position, otherwise
the result is the shift amount needed to right-shift the found bit into the
least-significant bit position. 0xffffffff is returned if no 1 bit is found.

'``llvm.nvvm.flo.s.*``' Intrinsic
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Syntax:
"""""""

.. code-block:: llvm
declare i32 @llvm.nvvm.flo.s.i32(i32 %a, i1 %shiftamt)
declare i32 @llvm.nvvm.flo.s.i64(i64 %a, i1 %shiftamt)
Overview:
"""""""""

The '``llvm.nvvm.flo.s``' family of intrinsics identifies the bit position of the
leading non-sign bit, returning either it's offset from the most or least
significant bit.

Semantics:
""""""""""

The '``llvm.nvvm.flo.s``' family of intrinsics returns the bit position of the
most significant 0 for negative inputs and the most significant 1 for
non-negative inputs. If %shiftamt is true, The result is the shift amount needed
to left-shift the found bit into the most-significant bit position, otherwise
the result is the shift amount needed to right-shift the found bit into the
least-significant bit position. 0xffffffff is returned if no 1 bit is found.

Other Intrinsics
----------------
Expand Down
26 changes: 17 additions & 9 deletions llvm/include/llvm/ADT/StringMapEntry.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
#define LLVM_ADT_STRINGMAPENTRY_H

#include "llvm/ADT/StringRef.h"
#include <optional>
#include <utility>

namespace llvm {

Expand Down Expand Up @@ -147,25 +147,33 @@ class StringMapEntry final : public StringMapEntryStorage<ValueTy> {
};

// Allow structured bindings on StringMapEntry.

template <std::size_t Index, typename ValueTy>
decltype(auto) get(StringMapEntry<ValueTy> &E) {
static_assert(Index < 2);
if constexpr (Index == 0)
return E.getKey();
else
return E.getValue();
}

template <std::size_t Index, typename ValueTy>
decltype(auto) get(const StringMapEntry<ValueTy> &E) {
static_assert(Index < 2);
if constexpr (Index == 0)
return E.first();
return E.getKey();
else
return E.second;
return E.getValue();
}

} // end namespace llvm

namespace std {
template <typename ValueTy>
struct tuple_size<llvm::StringMapEntry<ValueTy>>
struct std::tuple_size<llvm::StringMapEntry<ValueTy>>
: std::integral_constant<std::size_t, 2> {};

template <std::size_t I, typename ValueTy>
struct tuple_element<I, llvm::StringMapEntry<ValueTy>>
: std::conditional<I == 0, llvm::StringRef, ValueTy> {};
} // namespace std
template <std::size_t Index, typename ValueTy>
struct std::tuple_element<Index, llvm::StringMapEntry<ValueTy>>
: std::tuple_element<Index, std::pair<llvm::StringRef, ValueTy>> {};

#endif // LLVM_ADT_STRINGMAPENTRY_H
71 changes: 47 additions & 24 deletions llvm/include/llvm/Analysis/MemoryBuiltins.h
Original file line number Diff line number Diff line change
Expand Up @@ -221,21 +221,43 @@ struct SizeOffsetAPInt : public SizeOffsetType<APInt, SizeOffsetAPInt> {
static bool known(const APInt &V) { return V.getBitWidth() > 1; }
};

/// OffsetSpan - Used internally by \p ObjectSizeOffsetVisitor. Represents a
/// point in memory as a pair of allocated bytes before and after it.
struct OffsetSpan {
APInt Before; /// Number of allocated bytes before this point.
APInt After; /// Number of allocated bytes after this point.

OffsetSpan() = default;
OffsetSpan(APInt Before, APInt After) : Before(Before), After(After) {}

bool knownBefore() const { return known(Before); }
bool knownAfter() const { return known(After); }
bool anyKnown() const { return knownBefore() || knownAfter(); }
bool bothKnown() const { return knownBefore() && knownAfter(); }

bool operator==(const OffsetSpan &RHS) const {
return Before == RHS.Before && After == RHS.After;
}
bool operator!=(const OffsetSpan &RHS) const { return !(*this == RHS); }

static bool known(const APInt &V) { return V.getBitWidth() > 1; }
};

/// Evaluate the size and offset of an object pointed to by a Value*
/// statically. Fails if size or offset are not known at compile time.
class ObjectSizeOffsetVisitor
: public InstVisitor<ObjectSizeOffsetVisitor, SizeOffsetAPInt> {
: public InstVisitor<ObjectSizeOffsetVisitor, OffsetSpan> {
const DataLayout &DL;
const TargetLibraryInfo *TLI;
ObjectSizeOpts Options;
unsigned IntTyBits;
APInt Zero;
SmallDenseMap<Instruction *, SizeOffsetAPInt, 8> SeenInsts;
SmallDenseMap<Instruction *, OffsetSpan, 8> SeenInsts;
unsigned InstructionsVisited;

APInt align(APInt Size, MaybeAlign Align);

static SizeOffsetAPInt unknown() { return SizeOffsetAPInt(); }
static OffsetSpan unknown() { return OffsetSpan(); }

public:
ObjectSizeOffsetVisitor(const DataLayout &DL, const TargetLibraryInfo *TLI,
Expand All @@ -245,29 +267,30 @@ class ObjectSizeOffsetVisitor

// These are "private", except they can't actually be made private. Only
// compute() should be used by external users.
SizeOffsetAPInt visitAllocaInst(AllocaInst &I);
SizeOffsetAPInt visitArgument(Argument &A);
SizeOffsetAPInt visitCallBase(CallBase &CB);
SizeOffsetAPInt visitConstantPointerNull(ConstantPointerNull &);
SizeOffsetAPInt visitExtractElementInst(ExtractElementInst &I);
SizeOffsetAPInt visitExtractValueInst(ExtractValueInst &I);
SizeOffsetAPInt visitGlobalAlias(GlobalAlias &GA);
SizeOffsetAPInt visitGlobalVariable(GlobalVariable &GV);
SizeOffsetAPInt visitIntToPtrInst(IntToPtrInst &);
SizeOffsetAPInt visitLoadInst(LoadInst &I);
SizeOffsetAPInt visitPHINode(PHINode &);
SizeOffsetAPInt visitSelectInst(SelectInst &I);
SizeOffsetAPInt visitUndefValue(UndefValue &);
SizeOffsetAPInt visitInstruction(Instruction &I);
OffsetSpan visitAllocaInst(AllocaInst &I);
OffsetSpan visitArgument(Argument &A);
OffsetSpan visitCallBase(CallBase &CB);
OffsetSpan visitConstantPointerNull(ConstantPointerNull &);
OffsetSpan visitExtractElementInst(ExtractElementInst &I);
OffsetSpan visitExtractValueInst(ExtractValueInst &I);
OffsetSpan visitGlobalAlias(GlobalAlias &GA);
OffsetSpan visitGlobalVariable(GlobalVariable &GV);
OffsetSpan visitIntToPtrInst(IntToPtrInst &);
OffsetSpan visitLoadInst(LoadInst &I);
OffsetSpan visitPHINode(PHINode &);
OffsetSpan visitSelectInst(SelectInst &I);
OffsetSpan visitUndefValue(UndefValue &);
OffsetSpan visitInstruction(Instruction &I);

private:
SizeOffsetAPInt findLoadSizeOffset(
LoadInst &LoadFrom, BasicBlock &BB, BasicBlock::iterator From,
SmallDenseMap<BasicBlock *, SizeOffsetAPInt, 8> &VisitedBlocks,
unsigned &ScannedInstCount);
SizeOffsetAPInt combineSizeOffset(SizeOffsetAPInt LHS, SizeOffsetAPInt RHS);
SizeOffsetAPInt computeImpl(Value *V);
SizeOffsetAPInt computeValue(Value *V);
OffsetSpan
findLoadOffsetRange(LoadInst &LoadFrom, BasicBlock &BB,
BasicBlock::iterator From,
SmallDenseMap<BasicBlock *, OffsetSpan, 8> &VisitedBlocks,
unsigned &ScannedInstCount);
OffsetSpan combineOffsetRange(OffsetSpan LHS, OffsetSpan RHS);
OffsetSpan computeImpl(Value *V);
OffsetSpan computeValue(Value *V);
bool CheckedZextOrTrunc(APInt &I);
};

Expand Down
2 changes: 1 addition & 1 deletion llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -1267,7 +1267,7 @@ class MachineIRBuilder {
/// \return a MachineInstrBuilder for the newly created instruction.
MachineInstrBuilder buildICmp(CmpInst::Predicate Pred, const DstOp &Res,
const SrcOp &Op0, const SrcOp &Op1,
std::optional<unsigned> Flgs = std::nullopt);
std::optional<unsigned> Flags = std::nullopt);

/// Build and insert a \p Res = G_FCMP \p Pred\p Op0, \p Op1
///
Expand Down
8 changes: 6 additions & 2 deletions llvm/include/llvm/CodeGen/SelectionDAGNodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -411,12 +411,14 @@ struct SDNodeFlags {
NoFPExcept = 1 << 12,
// Instructions with attached 'unpredictable' metadata on IR level.
Unpredictable = 1 << 13,
// Compare instructions which may carry the samesign flag.
SameSign = 1 << 14,

// NOTE: Please update LargestValue in LLVM_DECLARE_ENUM_AS_BITMASK below
// the class definition when adding new flags.

PoisonGeneratingFlags = NoUnsignedWrap | NoSignedWrap | Exact | Disjoint |
NonNeg | NoNaNs | NoInfs,
NonNeg | NoNaNs | NoInfs | SameSign,
};

/// Default constructor turns off all optimization flags.
Expand All @@ -438,6 +440,7 @@ struct SDNodeFlags {
void setNoSignedWrap(bool b) { setFlag<NoSignedWrap>(b); }
void setExact(bool b) { setFlag<Exact>(b); }
void setDisjoint(bool b) { setFlag<Disjoint>(b); }
void setSameSign(bool b) { setFlag<SameSign>(b); }
void setNonNeg(bool b) { setFlag<NonNeg>(b); }
void setNoNaNs(bool b) { setFlag<NoNaNs>(b); }
void setNoInfs(bool b) { setFlag<NoInfs>(b); }
Expand All @@ -454,6 +457,7 @@ struct SDNodeFlags {
bool hasNoSignedWrap() const { return Flags & NoSignedWrap; }
bool hasExact() const { return Flags & Exact; }
bool hasDisjoint() const { return Flags & Disjoint; }
bool hasSameSign() const { return Flags & SameSign; }
bool hasNonNeg() const { return Flags & NonNeg; }
bool hasNoNaNs() const { return Flags & NoNaNs; }
bool hasNoInfs() const { return Flags & NoInfs; }
Expand All @@ -473,7 +477,7 @@ struct SDNodeFlags {
};

LLVM_DECLARE_ENUM_AS_BITMASK(decltype(SDNodeFlags::None),
SDNodeFlags::Unpredictable);
SDNodeFlags::SameSign);

inline SDNodeFlags operator|(SDNodeFlags LHS, SDNodeFlags RHS) {
LHS |= RHS;
Expand Down
18 changes: 14 additions & 4 deletions llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h
Original file line number Diff line number Diff line change
Expand Up @@ -1077,6 +1077,16 @@ class LinkGraph {
return MutableArrayRef<char>(AllocatedBuffer, SourceStr.size());
}

/// Allocate a copy of the given string using the LinkGraph's allocator
/// and return it as a StringRef.
///
/// This is a convenience wrapper around allocateContent(Twine) that is
/// handy when creating new symbol names within the graph.
StringRef allocateName(Twine Source) {
auto Buf = allocateContent(Source);
return {Buf.data(), Buf.size()};
}

/// Allocate a copy of the given string using the LinkGraph's allocator.
///
/// The allocated string will be terminated with a null character, and the
Expand Down Expand Up @@ -1931,9 +1941,9 @@ Error makeAlignmentError(llvm::orc::ExecutorAddr Loc, uint64_t Value, int N,
/// alignment: PointerSize
/// alignment-offset: 0
/// address: highest allowable
using AnonymousPointerCreator = unique_function<Expected<Symbol &>(
LinkGraph &G, Section &PointerSection, Symbol *InitialTarget,
uint64_t InitialAddend)>;
using AnonymousPointerCreator =
unique_function<Symbol &(LinkGraph &G, Section &PointerSection,
Symbol *InitialTarget, uint64_t InitialAddend)>;

/// Get target-specific AnonymousPointerCreator
AnonymousPointerCreator getAnonymousPointerCreator(const Triple &TT);
Expand All @@ -1942,7 +1952,7 @@ AnonymousPointerCreator getAnonymousPointerCreator(const Triple &TT);
/// an anonymous symbol pointing to it. Return the anonymous symbol.
///
/// The stub block will be created by createPointerJumpStubBlock.
using PointerJumpStubCreator = unique_function<Expected<Symbol &>(
using PointerJumpStubCreator = unique_function<Symbol &(
LinkGraph &G, Section &StubSection, Symbol &PointerSymbol)>;

/// Get target-specific PointerJumpStubCreator
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,16 @@
#include "llvm/ExecutionEngine/Orc/RedirectionManager.h"
#include "llvm/Support/StringSaver.h"

#include <atomic>

namespace llvm {
namespace orc {

class JITLinkRedirectableSymbolManager : public RedirectableSymbolManager,
public ResourceManager {
class JITLinkRedirectableSymbolManager : public RedirectableSymbolManager {
public:
/// Create redirection manager that uses JITLink based implementaion.
static Expected<std::unique_ptr<RedirectableSymbolManager>>
Create(ObjectLinkingLayer &ObjLinkingLayer, JITDylib &JD) {
Create(ObjectLinkingLayer &ObjLinkingLayer) {
auto AnonymousPtrCreator(jitlink::getAnonymousPointerCreator(
ObjLinkingLayer.getExecutionSession().getTargetTriple()));
auto PtrJumpStubCreator(jitlink::getPointerJumpStubCreator(
Expand All @@ -35,69 +36,27 @@ class JITLinkRedirectableSymbolManager : public RedirectableSymbolManager,
inconvertibleErrorCode());
return std::unique_ptr<RedirectableSymbolManager>(
new JITLinkRedirectableSymbolManager(
ObjLinkingLayer, JD, AnonymousPtrCreator, PtrJumpStubCreator));
ObjLinkingLayer, AnonymousPtrCreator, PtrJumpStubCreator));
}

void emitRedirectableSymbols(std::unique_ptr<MaterializationResponsibility> R,
const SymbolAddrMap &InitialDests) override;

Error redirect(JITDylib &TargetJD, const SymbolAddrMap &NewDests) override;

Error handleRemoveResources(JITDylib &TargetJD, ResourceKey K) override;

void handleTransferResources(JITDylib &TargetJD, ResourceKey DstK,
ResourceKey SrcK) override;
Error redirect(JITDylib &JD, const SymbolAddrMap &NewDests) override;

private:
using StubHandle = unsigned;
constexpr static unsigned StubBlockSize = 256;
constexpr static StringRef JumpStubPrefix = "$__IND_JUMP_STUBS";
constexpr static StringRef StubPtrPrefix = "$IND_JUMP_PTR_";
constexpr static StringRef JumpStubTableName = "$IND_JUMP_";
constexpr static StringRef StubPtrTableName = "$__IND_JUMP_PTRS";

JITLinkRedirectableSymbolManager(
ObjectLinkingLayer &ObjLinkingLayer, JITDylib &JD,
ObjectLinkingLayer &ObjLinkingLayer,
jitlink::AnonymousPointerCreator &AnonymousPtrCreator,
jitlink::PointerJumpStubCreator &PtrJumpStubCreator)
: ObjLinkingLayer(ObjLinkingLayer), JD(JD),
: ObjLinkingLayer(ObjLinkingLayer),
AnonymousPtrCreator(std::move(AnonymousPtrCreator)),
PtrJumpStubCreator(std::move(PtrJumpStubCreator)) {
ObjLinkingLayer.getExecutionSession().registerResourceManager(*this);
}

~JITLinkRedirectableSymbolManager() {
ObjLinkingLayer.getExecutionSession().deregisterResourceManager(*this);
}

StringRef JumpStubSymbolName(unsigned I) {
return *ObjLinkingLayer.getExecutionSession().intern(
(JumpStubPrefix + Twine(I)).str());
}

StringRef StubPtrSymbolName(unsigned I) {
return *ObjLinkingLayer.getExecutionSession().intern(
(StubPtrPrefix + Twine(I)).str());
}

unsigned GetNumAvailableStubs() const { return AvailableStubs.size(); }

Error redirectInner(JITDylib &TargetJD, const SymbolAddrMap &NewDests);
Error grow(unsigned Need);
PtrJumpStubCreator(std::move(PtrJumpStubCreator)) {}

ObjectLinkingLayer &ObjLinkingLayer;
JITDylib &JD;
jitlink::AnonymousPointerCreator AnonymousPtrCreator;
jitlink::PointerJumpStubCreator PtrJumpStubCreator;

std::vector<StubHandle> AvailableStubs;
using SymbolToStubMap = DenseMap<SymbolStringPtr, StubHandle>;
DenseMap<JITDylib *, SymbolToStubMap> SymbolToStubs;
std::vector<ExecutorSymbolDef> JumpStubs;
std::vector<ExecutorSymbolDef> StubPointers;
DenseMap<ResourceKey, std::vector<SymbolStringPtr>> TrackedResources;

std::mutex Mutex;
std::atomic_size_t StubGraphIdx{0};
};

} // namespace orc
Expand Down
8 changes: 8 additions & 0 deletions llvm/include/llvm/IR/IntrinsicsNVVM.td
Original file line number Diff line number Diff line change
Expand Up @@ -1092,6 +1092,14 @@ let TargetPrefix = "nvvm" in {
[LLVMMatchType<0>, LLVMMatchType<0>, LLVMMatchType<0>],
[IntrNoMem, IntrSpeculatable, IntrWillReturn]>;

//
// FLO - Find Leading One
//
foreach sign = ["s", "u"] in
def int_nvvm_flo_ # sign :
DefaultAttrsIntrinsic<[llvm_i32_ty],
[llvm_anyint_ty, llvm_i1_ty],
[IntrNoMem, IntrSpeculatable, IntrWillReturn, ImmArg<ArgIndex<1>>]>;

//
// Convert
Expand Down
3 changes: 3 additions & 0 deletions llvm/include/llvm/IR/Operator.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ class OverflowingBinaryOperator : public Operator {
return NoWrapKind;
}

/// Return true if the instruction is commutative
bool isCommutative() const { return Instruction::isCommutative(getOpcode()); }

static bool classof(const Instruction *I) {
return I->getOpcode() == Instruction::Add ||
I->getOpcode() == Instruction::Sub ||
Expand Down
24 changes: 16 additions & 8 deletions llvm/include/llvm/MC/MCDXContainerWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#define LLVM_MC_MCDXCONTAINERWRITER_H

#include "llvm/MC/MCObjectWriter.h"
#include "llvm/MC/MCValue.h"
#include "llvm/Support/EndianStream.h"
#include "llvm/TargetParser/Triple.h"

namespace llvm {
Expand All @@ -31,15 +33,21 @@ class MCDXContainerTargetWriter : public MCObjectTargetWriter {
}
};

/// Construct a new DXContainer writer instance.
///
/// \param MOTW - The target specific DXContainer writer subclass.
/// \param OS - The stream to write to.
/// \returns The constructed object writer.
std::unique_ptr<MCObjectWriter>
createDXContainerObjectWriter(std::unique_ptr<MCDXContainerTargetWriter> MOTW,
raw_pwrite_stream &OS);
class DXContainerObjectWriter final : public MCObjectWriter {
support::endian::Writer W;
std::unique_ptr<MCDXContainerTargetWriter> TargetObjectWriter;

public:
DXContainerObjectWriter(std::unique_ptr<MCDXContainerTargetWriter> MOTW,
raw_pwrite_stream &OS)
: W(OS, llvm::endianness::little), TargetObjectWriter(std::move(MOTW)) {}

void recordRelocation(MCAssembler &Asm, const MCFragment *Fragment,
const MCFixup &Fixup, MCValue Target,
uint64_t &FixedValue) override {}

uint64_t writeObject(MCAssembler &Asm) override;
};
} // end namespace llvm

#endif // LLVM_MC_MCDXCONTAINERWRITER_H
3 changes: 2 additions & 1 deletion llvm/include/llvm/MC/MCInstPrinter.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,8 @@ class MCInstPrinter {

/// Returns a pair containing the mnemonic for \p MI and the number of bits
/// left for further processing by printInstruction (generated by tablegen).
virtual std::pair<const char *, uint64_t> getMnemonic(const MCInst *MI) = 0;
virtual std::pair<const char *, uint64_t>
getMnemonic(const MCInst &MI) const = 0;

/// Print the specified MCInst to the specified raw_ostream.
///
Expand Down
2 changes: 1 addition & 1 deletion llvm/include/llvm/MC/MCStreamer.h
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ class MCStreamer {

/// Returns the mnemonic for \p MI, if the streamer has access to a
/// instruction printer and returns an empty string otherwise.
virtual StringRef getMnemonic(MCInst &MI) { return ""; }
virtual StringRef getMnemonic(const MCInst &MI) const { return ""; }

/// Emit a label for \p Symbol into the current section.
///
Expand Down
22 changes: 22 additions & 0 deletions llvm/include/llvm/Object/Minidump.h
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,28 @@ Expected<ArrayRef<T>> MinidumpFile::getDataSliceAs(ArrayRef<uint8_t> Data,
return ArrayRef<T>(reinterpret_cast<const T *>(Slice->data()), Count);
}

template <typename T>
Expected<ArrayRef<T>>
MinidumpFile::getListStream(minidump::StreamType Type) const {
std::optional<ArrayRef<uint8_t>> Stream = getRawStream(Type);
if (!Stream)
return createError("No such stream");
auto ExpectedSize = getDataSliceAs<support::ulittle32_t>(*Stream, 0, 1);
if (!ExpectedSize)
return ExpectedSize.takeError();

size_t ListSize = ExpectedSize.get()[0];

size_t ListOffset = 4;
// Some producers insert additional padding bytes to align the list to an
// 8-byte boundary. Check for that by comparing the list size with the overall
// stream size.
if (ListOffset + sizeof(T) * ListSize < Stream->size())
ListOffset = 8;

return getDataSliceAs<T>(*Stream, ListOffset, ListSize);
}

} // end namespace object
} // end namespace llvm

Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/Analysis/ConstantFolding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2407,6 +2407,9 @@ static Constant *ConstantFoldScalarCall1(StringRef Name,
break;
case LibFunc_log1p:
case LibFunc_log1pf:
// Implement optional behavior from C's Annex F for +/-0.0.
if (U.isZero())
return ConstantFP::get(Ty->getContext(), U);
if (APF > APFloat::getOne(APF.getSemantics(), true) && TLI->has(Func))
return ConstantFoldFP(log1p, APF, Ty);
break;
Expand Down
145 changes: 80 additions & 65 deletions llvm/lib/Analysis/MemoryBuiltins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -686,10 +686,21 @@ ObjectSizeOffsetVisitor::ObjectSizeOffsetVisitor(const DataLayout &DL,

SizeOffsetAPInt ObjectSizeOffsetVisitor::compute(Value *V) {
InstructionsVisited = 0;
return computeImpl(V);
OffsetSpan Span = computeImpl(V);

// In ExactSizeFromOffset mode, we don't care about the Before Field, so allow
// us to overwrite it if needs be.
if (Span.knownAfter() && !Span.knownBefore() &&
Options.EvalMode == ObjectSizeOpts::Mode::ExactSizeFromOffset)
Span.Before = APInt::getZero(Span.After.getBitWidth());

if (!Span.bothKnown())
return {};

return {Span.Before + Span.After, Span.Before};
}

SizeOffsetAPInt ObjectSizeOffsetVisitor::computeImpl(Value *V) {
OffsetSpan ObjectSizeOffsetVisitor::computeImpl(Value *V) {
unsigned InitialIntTyBits = DL.getIndexTypeSizeInBits(V->getType());

// Stripping pointer casts can strip address space casts which can change the
Expand All @@ -706,28 +717,28 @@ SizeOffsetAPInt ObjectSizeOffsetVisitor::computeImpl(Value *V) {
IntTyBits = DL.getIndexTypeSizeInBits(V->getType());
Zero = APInt::getZero(IntTyBits);

SizeOffsetAPInt SOT = computeValue(V);
OffsetSpan ORT = computeValue(V);

bool IndexTypeSizeChanged = InitialIntTyBits != IntTyBits;
if (!IndexTypeSizeChanged && Offset.isZero())
return SOT;
return ORT;

// We stripped an address space cast that changed the index type size or we
// accumulated some constant offset (or both). Readjust the bit width to match
// the argument index type size and apply the offset, as required.
if (IndexTypeSizeChanged) {
if (SOT.knownSize() && !::CheckedZextOrTrunc(SOT.Size, InitialIntTyBits))
SOT.Size = APInt();
if (SOT.knownOffset() &&
!::CheckedZextOrTrunc(SOT.Offset, InitialIntTyBits))
SOT.Offset = APInt();
if (ORT.knownBefore() &&
!::CheckedZextOrTrunc(ORT.Before, InitialIntTyBits))
ORT.Before = APInt();
if (ORT.knownAfter() && !::CheckedZextOrTrunc(ORT.After, InitialIntTyBits))
ORT.After = APInt();
}
// If the computed offset is "unknown" we cannot add the stripped offset.
return {SOT.Size,
SOT.Offset.getBitWidth() > 1 ? SOT.Offset + Offset : SOT.Offset};
// If the computed bound is "unknown" we cannot add the stripped offset.
return {(ORT.knownBefore() ? ORT.Before + Offset : ORT.Before),
(ORT.knownAfter() ? ORT.After - Offset : ORT.After)};
}

SizeOffsetAPInt ObjectSizeOffsetVisitor::computeValue(Value *V) {
OffsetSpan ObjectSizeOffsetVisitor::computeValue(Value *V) {
if (Instruction *I = dyn_cast<Instruction>(V)) {
// If we have already seen this instruction, bail out. Cycles can happen in
// unreachable code after constant propagation.
Expand All @@ -737,7 +748,7 @@ SizeOffsetAPInt ObjectSizeOffsetVisitor::computeValue(Value *V) {
++InstructionsVisited;
if (InstructionsVisited > ObjectSizeOffsetVisitorMaxVisitInstructions)
return ObjectSizeOffsetVisitor::unknown();
SizeOffsetAPInt Res = visit(*I);
OffsetSpan Res = visit(*I);
// Cache the result for later visits. If we happened to visit this during
// the above recursion, we would consider it unknown until now.
SeenInsts[I] = Res;
Expand All @@ -763,15 +774,15 @@ bool ObjectSizeOffsetVisitor::CheckedZextOrTrunc(APInt &I) {
return ::CheckedZextOrTrunc(I, IntTyBits);
}

SizeOffsetAPInt ObjectSizeOffsetVisitor::visitAllocaInst(AllocaInst &I) {
OffsetSpan ObjectSizeOffsetVisitor::visitAllocaInst(AllocaInst &I) {
TypeSize ElemSize = DL.getTypeAllocSize(I.getAllocatedType());
if (ElemSize.isScalable() && Options.EvalMode != ObjectSizeOpts::Mode::Min)
return ObjectSizeOffsetVisitor::unknown();
if (!isUIntN(IntTyBits, ElemSize.getKnownMinValue()))
return ObjectSizeOffsetVisitor::unknown();
APInt Size(IntTyBits, ElemSize.getKnownMinValue());
if (!I.isArrayAllocation())
return SizeOffsetAPInt(align(Size, I.getAlign()), Zero);
return OffsetSpan(Zero, align(Size, I.getAlign()));

Value *ArraySize = I.getArraySize();
if (const ConstantInt *C = dyn_cast<ConstantInt>(ArraySize)) {
Expand All @@ -782,12 +793,12 @@ SizeOffsetAPInt ObjectSizeOffsetVisitor::visitAllocaInst(AllocaInst &I) {
bool Overflow;
Size = Size.umul_ov(NumElems, Overflow);
return Overflow ? ObjectSizeOffsetVisitor::unknown()
: SizeOffsetAPInt(align(Size, I.getAlign()), Zero);
: OffsetSpan(Zero, align(Size, I.getAlign()));
}
return ObjectSizeOffsetVisitor::unknown();
}

SizeOffsetAPInt ObjectSizeOffsetVisitor::visitArgument(Argument &A) {
OffsetSpan ObjectSizeOffsetVisitor::visitArgument(Argument &A) {
Type *MemoryTy = A.getPointeeInMemoryValueType();
// No interprocedural analysis is done at the moment.
if (!MemoryTy|| !MemoryTy->isSized()) {
Expand All @@ -796,16 +807,16 @@ SizeOffsetAPInt ObjectSizeOffsetVisitor::visitArgument(Argument &A) {
}

APInt Size(IntTyBits, DL.getTypeAllocSize(MemoryTy));
return SizeOffsetAPInt(align(Size, A.getParamAlign()), Zero);
return OffsetSpan(Zero, align(Size, A.getParamAlign()));
}

SizeOffsetAPInt ObjectSizeOffsetVisitor::visitCallBase(CallBase &CB) {
OffsetSpan ObjectSizeOffsetVisitor::visitCallBase(CallBase &CB) {
if (std::optional<APInt> Size = getAllocSize(&CB, TLI))
return SizeOffsetAPInt(*Size, Zero);
return OffsetSpan(Zero, *Size);
return ObjectSizeOffsetVisitor::unknown();
}

SizeOffsetAPInt
OffsetSpan
ObjectSizeOffsetVisitor::visitConstantPointerNull(ConstantPointerNull &CPN) {
// If null is unknown, there's nothing we can do. Additionally, non-zero
// address spaces can make use of null, so we don't presume to know anything
Expand All @@ -816,45 +827,43 @@ ObjectSizeOffsetVisitor::visitConstantPointerNull(ConstantPointerNull &CPN) {
// addrspace(1) gets casted to addrspace(0) (or vice-versa).
if (Options.NullIsUnknownSize || CPN.getType()->getAddressSpace())
return ObjectSizeOffsetVisitor::unknown();
return SizeOffsetAPInt(Zero, Zero);
return OffsetSpan(Zero, Zero);
}

SizeOffsetAPInt
OffsetSpan
ObjectSizeOffsetVisitor::visitExtractElementInst(ExtractElementInst &) {
return ObjectSizeOffsetVisitor::unknown();
}

SizeOffsetAPInt
ObjectSizeOffsetVisitor::visitExtractValueInst(ExtractValueInst &) {
OffsetSpan ObjectSizeOffsetVisitor::visitExtractValueInst(ExtractValueInst &) {
// Easy cases were already folded by previous passes.
return ObjectSizeOffsetVisitor::unknown();
}

SizeOffsetAPInt ObjectSizeOffsetVisitor::visitGlobalAlias(GlobalAlias &GA) {
OffsetSpan ObjectSizeOffsetVisitor::visitGlobalAlias(GlobalAlias &GA) {
if (GA.isInterposable())
return ObjectSizeOffsetVisitor::unknown();
return computeImpl(GA.getAliasee());
}

SizeOffsetAPInt
ObjectSizeOffsetVisitor::visitGlobalVariable(GlobalVariable &GV) {
OffsetSpan ObjectSizeOffsetVisitor::visitGlobalVariable(GlobalVariable &GV) {
if (!GV.getValueType()->isSized() || GV.hasExternalWeakLinkage() ||
((!GV.hasInitializer() || GV.isInterposable()) &&
Options.EvalMode != ObjectSizeOpts::Mode::Min))
return ObjectSizeOffsetVisitor::unknown();

APInt Size(IntTyBits, DL.getTypeAllocSize(GV.getValueType()));
return SizeOffsetAPInt(align(Size, GV.getAlign()), Zero);
return OffsetSpan(Zero, align(Size, GV.getAlign()));
}

SizeOffsetAPInt ObjectSizeOffsetVisitor::visitIntToPtrInst(IntToPtrInst &) {
OffsetSpan ObjectSizeOffsetVisitor::visitIntToPtrInst(IntToPtrInst &) {
// clueless
return ObjectSizeOffsetVisitor::unknown();
}

SizeOffsetAPInt ObjectSizeOffsetVisitor::findLoadSizeOffset(
OffsetSpan ObjectSizeOffsetVisitor::findLoadOffsetRange(
LoadInst &Load, BasicBlock &BB, BasicBlock::iterator From,
SmallDenseMap<BasicBlock *, SizeOffsetAPInt, 8> &VisitedBlocks,
SmallDenseMap<BasicBlock *, OffsetSpan, 8> &VisitedBlocks,
unsigned &ScannedInstCount) {
constexpr unsigned MaxInstsToScan = 128;

Expand All @@ -865,7 +874,7 @@ SizeOffsetAPInt ObjectSizeOffsetVisitor::findLoadSizeOffset(
auto Unknown = [&BB, &VisitedBlocks]() {
return VisitedBlocks[&BB] = ObjectSizeOffsetVisitor::unknown();
};
auto Known = [&BB, &VisitedBlocks](SizeOffsetAPInt SO) {
auto Known = [&BB, &VisitedBlocks](OffsetSpan SO) {
return VisitedBlocks[&BB] = SO;
};

Expand Down Expand Up @@ -936,15 +945,15 @@ SizeOffsetAPInt ObjectSizeOffsetVisitor::findLoadSizeOffset(
if (!C)
return Unknown();

return Known({C->getValue(), APInt(C->getValue().getBitWidth(), 0)});
return Known({APInt(C->getValue().getBitWidth(), 0), C->getValue()});
}

return Unknown();
} while (From-- != BB.begin());

SmallVector<SizeOffsetAPInt> PredecessorSizeOffsets;
SmallVector<OffsetSpan> PredecessorSizeOffsets;
for (auto *PredBB : predecessors(&BB)) {
PredecessorSizeOffsets.push_back(findLoadSizeOffset(
PredecessorSizeOffsets.push_back(findLoadOffsetRange(
Load, *PredBB, BasicBlock::iterator(PredBB->getTerminator()),
VisitedBlocks, ScannedInstCount));
if (!PredecessorSizeOffsets.back().bothKnown())
Expand All @@ -956,70 +965,70 @@ SizeOffsetAPInt ObjectSizeOffsetVisitor::findLoadSizeOffset(

return Known(std::accumulate(
PredecessorSizeOffsets.begin() + 1, PredecessorSizeOffsets.end(),
PredecessorSizeOffsets.front(),
[this](SizeOffsetAPInt LHS, SizeOffsetAPInt RHS) {
return combineSizeOffset(LHS, RHS);
PredecessorSizeOffsets.front(), [this](OffsetSpan LHS, OffsetSpan RHS) {
return combineOffsetRange(LHS, RHS);
}));
}

SizeOffsetAPInt ObjectSizeOffsetVisitor::visitLoadInst(LoadInst &LI) {
OffsetSpan ObjectSizeOffsetVisitor::visitLoadInst(LoadInst &LI) {
if (!Options.AA) {
++ObjectVisitorLoad;
return ObjectSizeOffsetVisitor::unknown();
}

SmallDenseMap<BasicBlock *, SizeOffsetAPInt, 8> VisitedBlocks;
SmallDenseMap<BasicBlock *, OffsetSpan, 8> VisitedBlocks;
unsigned ScannedInstCount = 0;
SizeOffsetAPInt SO =
findLoadSizeOffset(LI, *LI.getParent(), BasicBlock::iterator(LI),
VisitedBlocks, ScannedInstCount);
OffsetSpan SO =
findLoadOffsetRange(LI, *LI.getParent(), BasicBlock::iterator(LI),
VisitedBlocks, ScannedInstCount);
if (!SO.bothKnown())
++ObjectVisitorLoad;
return SO;
}

SizeOffsetAPInt
ObjectSizeOffsetVisitor::combineSizeOffset(SizeOffsetAPInt LHS,
SizeOffsetAPInt RHS) {
OffsetSpan ObjectSizeOffsetVisitor::combineOffsetRange(OffsetSpan LHS,
OffsetSpan RHS) {
if (!LHS.bothKnown() || !RHS.bothKnown())
return ObjectSizeOffsetVisitor::unknown();

switch (Options.EvalMode) {
case ObjectSizeOpts::Mode::Min:
return (getSizeWithOverflow(LHS).slt(getSizeWithOverflow(RHS))) ? LHS : RHS;
case ObjectSizeOpts::Mode::Max:
return (getSizeWithOverflow(LHS).sgt(getSizeWithOverflow(RHS))) ? LHS : RHS;
return {LHS.Before.slt(RHS.Before) ? LHS.Before : RHS.Before,
LHS.After.slt(RHS.After) ? LHS.After : RHS.After};
case ObjectSizeOpts::Mode::Max: {
return {LHS.Before.sgt(RHS.Before) ? LHS.Before : RHS.Before,
LHS.After.sgt(RHS.After) ? LHS.After : RHS.After};
}
case ObjectSizeOpts::Mode::ExactSizeFromOffset:
return (getSizeWithOverflow(LHS).eq(getSizeWithOverflow(RHS)))
? LHS
: ObjectSizeOffsetVisitor::unknown();
return {LHS.Before.eq(RHS.Before) ? LHS.Before : APInt(),
LHS.After.eq(RHS.After) ? LHS.After : APInt()};
case ObjectSizeOpts::Mode::ExactUnderlyingSizeAndOffset:
return LHS == RHS ? LHS : ObjectSizeOffsetVisitor::unknown();
return (LHS == RHS) ? LHS : ObjectSizeOffsetVisitor::unknown();
}
llvm_unreachable("missing an eval mode");
}

SizeOffsetAPInt ObjectSizeOffsetVisitor::visitPHINode(PHINode &PN) {
OffsetSpan ObjectSizeOffsetVisitor::visitPHINode(PHINode &PN) {
if (PN.getNumIncomingValues() == 0)
return ObjectSizeOffsetVisitor::unknown();
auto IncomingValues = PN.incoming_values();
return std::accumulate(IncomingValues.begin() + 1, IncomingValues.end(),
computeImpl(*IncomingValues.begin()),
[this](SizeOffsetAPInt LHS, Value *VRHS) {
return combineSizeOffset(LHS, computeImpl(VRHS));
[this](OffsetSpan LHS, Value *VRHS) {
return combineOffsetRange(LHS, computeImpl(VRHS));
});
}

SizeOffsetAPInt ObjectSizeOffsetVisitor::visitSelectInst(SelectInst &I) {
return combineSizeOffset(computeImpl(I.getTrueValue()),
computeImpl(I.getFalseValue()));
OffsetSpan ObjectSizeOffsetVisitor::visitSelectInst(SelectInst &I) {
return combineOffsetRange(computeImpl(I.getTrueValue()),
computeImpl(I.getFalseValue()));
}

SizeOffsetAPInt ObjectSizeOffsetVisitor::visitUndefValue(UndefValue &) {
return SizeOffsetAPInt(Zero, Zero);
OffsetSpan ObjectSizeOffsetVisitor::visitUndefValue(UndefValue &) {
return OffsetSpan(Zero, Zero);
}

SizeOffsetAPInt ObjectSizeOffsetVisitor::visitInstruction(Instruction &I) {
OffsetSpan ObjectSizeOffsetVisitor::visitInstruction(Instruction &I) {
LLVM_DEBUG(dbgs() << "ObjectSizeOffsetVisitor unknown instruction:" << I
<< '\n');
return ObjectSizeOffsetVisitor::unknown();
Expand Down Expand Up @@ -1072,7 +1081,13 @@ SizeOffsetValue ObjectSizeOffsetEvaluator::compute(Value *V) {
}

SizeOffsetValue ObjectSizeOffsetEvaluator::compute_(Value *V) {
ObjectSizeOffsetVisitor Visitor(DL, TLI, Context, EvalOpts);

// Only trust ObjectSizeOffsetVisitor in exact mode, otherwise fallback on
// dynamic computation.
ObjectSizeOpts VisitorEvalOpts(EvalOpts);
VisitorEvalOpts.EvalMode = ObjectSizeOpts::Mode::ExactUnderlyingSizeAndOffset;
ObjectSizeOffsetVisitor Visitor(DL, TLI, Context, VisitorEvalOpts);

SizeOffsetAPInt Const = Visitor.compute(V);
if (Const.bothKnown())
return SizeOffsetValue(ConstantInt::get(Context, Const.Size),
Expand Down
3 changes: 2 additions & 1 deletion llvm/lib/Analysis/MemoryProfileInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ AllocationType llvm::memprof::getAllocType(uint64_t TotalLifetimeAccessDensity,

MDNode *llvm::memprof::buildCallstackMetadata(ArrayRef<uint64_t> CallStack,
LLVMContext &Ctx) {
std::vector<Metadata *> StackVals;
SmallVector<Metadata *, 8> StackVals;
StackVals.reserve(CallStack.size());
for (auto Id : CallStack) {
auto *StackValMD =
ValueAsMetadata::get(ConstantInt::get(Type::getInt64Ty(Ctx), Id));
Expand Down
24 changes: 17 additions & 7 deletions llvm/lib/Analysis/ValueTracking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1566,6 +1566,22 @@ static void computeKnownBitsFromOperator(const Operator *I,
// Skip direct self references.
if (IncValue == P) continue;

// Recurse, but cap the recursion to one level, because we don't
// want to waste time spinning around in loops.
// TODO: See if we can base recursion limiter on number of incoming phi
// edges so we don't overly clamp analysis.
unsigned IncDepth = MaxAnalysisRecursionDepth - 1;

// If the Use is a select of this phi, use the knownbit of the other
// operand to break the recursion.
if (auto *SI = dyn_cast<SelectInst>(IncValue)) {
if (SI->getTrueValue() == P || SI->getFalseValue() == P) {
IncValue = SI->getTrueValue() == P ? SI->getFalseValue()
: SI->getTrueValue();
IncDepth = Depth + 1;
}
}

// Change the context instruction to the "edge" that flows into the
// phi. This is important because that is where the value is actually
// "evaluated" even though it is used later somewhere else. (see also
Expand All @@ -1574,13 +1590,7 @@ static void computeKnownBitsFromOperator(const Operator *I,
RecQ.CxtI = P->getIncomingBlock(u)->getTerminator();

Known2 = KnownBits(BitWidth);

// Recurse, but cap the recursion to one level, because we don't
// want to waste time spinning around in loops.
// TODO: See if we can base recursion limiter on number of incoming phi
// edges so we don't overly clamp analysis.
computeKnownBits(IncValue, DemandedElts, Known2,
MaxAnalysisRecursionDepth - 1, RecQ);
computeKnownBits(IncValue, DemandedElts, Known2, IncDepth, RecQ);

// See if we can further use a conditional branch into the phi
// to help us determine the range of the value.
Expand Down
1 change: 0 additions & 1 deletion llvm/lib/CodeGen/AsmPrinter/AccelTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
#include "llvm/MC/MCSymbol.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetLoweringObjectFile.h"
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <limits>
Expand Down
1 change: 0 additions & 1 deletion llvm/lib/CodeGen/AsmPrinter/AddressPool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#include "AddressPool.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/CodeGen/AsmPrinter.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/Target/TargetLoweringObjectFile.h"
Expand Down
1 change: 0 additions & 1 deletion llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/MC/MCSymbol.h"
#include "llvm/MC/MCSymbolCOFF.h"
#include "llvm/MC/MCSymbolELF.h"
#include "llvm/MC/MCTargetOptions.h"
#include "llvm/MC/MCValue.h"
Expand Down
1 change: 0 additions & 1 deletion llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
#include "llvm/CodeGen/AsmPrinter.h"
#include "llvm/CodeGen/DIE.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCDwarf.h"
#include "llvm/MC/MCSection.h"
Expand Down
2 changes: 0 additions & 2 deletions llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/SMLoc.h"
#include "llvm/Support/ScopedPrinter.h"
#include "llvm/Target/TargetLoweringObjectFile.h"
Expand All @@ -70,7 +69,6 @@
#include <cassert>
#include <cctype>
#include <cstddef>
#include <iterator>
#include <limits>

using namespace llvm;
Expand Down
1 change: 0 additions & 1 deletion llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
#include "llvm/Target/TargetLoweringObjectFile.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetOptions.h"
#include <iterator>
#include <optional>
#include <string>
#include <utility>
Expand Down
1 change: 0 additions & 1 deletion llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@
#include "llvm/MC/MCSymbol.h"
#include "llvm/MC/MCTargetOptions.h"
#include "llvm/MC/MachineLocation.h"
#include "llvm/MC/SectionKind.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
Expand Down
21 changes: 15 additions & 6 deletions llvm/lib/CodeGen/LiveRangeShrink.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,15 +124,24 @@ bool LiveRangeShrink::runOnMachineFunction(MachineFunction &MF) {
for (MachineBasicBlock &MBB : MF) {
if (MBB.empty())
continue;
bool SawStore = false;
BuildInstOrderMap(MBB.begin(), IOM);

MachineBasicBlock::iterator Next = MBB.begin();
if (MBB.isEHPad()) {
// Do not track PHIs in IOM when handling EHPads.
// Otherwise their uses may be hoisted outside a landingpad range.
Next = MBB.SkipPHIsLabelsAndDebug(Next);
if (Next == MBB.end())
continue;
}

BuildInstOrderMap(Next, IOM);
Next = MBB.SkipPHIsLabelsAndDebug(Next);
UseMap.clear();
bool SawStore = false;

for (MachineBasicBlock::iterator Next = MBB.begin(); Next != MBB.end();) {
while (Next != MBB.end()) {
MachineInstr &MI = *Next;
++Next;
if (MI.isPHI() || MI.isDebugOrPseudoInstr())
continue;
Next = MBB.SkipPHIsLabelsAndDebug(++Next);
if (MI.mayStore())
SawStore = true;

Expand Down
1 change: 0 additions & 1 deletion llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
#include "llvm/CodeGen/ISDOpcodes.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineMemOperand.h"
#include "llvm/CodeGen/RuntimeLibcallUtil.h"
#include "llvm/CodeGen/SDPatternMatch.h"
#include "llvm/CodeGen/SelectionDAG.h"
#include "llvm/CodeGen/SelectionDAGAddressAnalysis.h"
Expand Down
1 change: 0 additions & 1 deletion llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetOptions.h"
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <iterator>
Expand Down
1 change: 0 additions & 1 deletion llvm/lib/CodeGen/SelectionDAG/FunctionLoweringInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1105,6 +1105,9 @@ EmitMachineNode(SDNode *Node, bool IsClone, bool IsCloned,

if (Flags.hasDisjoint())
MI->setFlag(MachineInstr::MIFlag::Disjoint);

if (Flags.hasSameSign())
MI->setFlag(MachineInstr::MIFlag::SameSign);
}

// Emit all of the actual operands of this instruction, adding them to the
Expand Down
8 changes: 6 additions & 2 deletions llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10318,8 +10318,10 @@ SDValue SelectionDAG::getNode(unsigned Opcode, const SDLoc &DL, EVT VT,
AddNodeIDNode(ID, Opcode, VTs, Ops);
void *IP = nullptr;

if (SDNode *E = FindNodeOrInsertPos(ID, DL, IP))
if (SDNode *E = FindNodeOrInsertPos(ID, DL, IP)) {
E->intersectFlagsWith(Flags);
return SDValue(E, 0);
}

N = newSDNode<SDNode>(Opcode, DL.getIROrder(), DL.getDebugLoc(), VTs);
createOperands(N, Ops);
Expand Down Expand Up @@ -10524,8 +10526,10 @@ SDValue SelectionDAG::getNode(unsigned Opcode, const SDLoc &DL, SDVTList VTList,
FoldingSetNodeID ID;
AddNodeIDNode(ID, Opcode, VTList, Ops);
void *IP = nullptr;
if (SDNode *E = FindNodeOrInsertPos(ID, DL, IP))
if (SDNode *E = FindNodeOrInsertPos(ID, DL, IP)) {
E->intersectFlagsWith(Flags);
return SDValue(E, 0);
}

N = newSDNode<SDNode>(Opcode, DL.getIROrder(), DL.getDebugLoc(), VTList);
createOperands(N, Ops);
Expand Down
7 changes: 4 additions & 3 deletions llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@
#include "llvm/CodeGen/MachineModuleInfo.h"
#include "llvm/CodeGen/MachineOperand.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/RuntimeLibcallUtil.h"
#include "llvm/CodeGen/SelectionDAG.h"
#include "llvm/CodeGen/SelectionDAGTargetInfo.h"
#include "llvm/CodeGen/StackMaps.h"
Expand Down Expand Up @@ -104,8 +103,6 @@
#include "llvm/TargetParser/Triple.h"
#include "llvm/Transforms/Utils/Local.h"
#include <cstddef>
#include <deque>
#include <iterator>
#include <limits>
#include <optional>
#include <tuple>
Expand Down Expand Up @@ -3652,6 +3649,10 @@ void SelectionDAGBuilder::visitICmp(const ICmpInst &I) {
Op2 = DAG.getPtrExtOrTrunc(Op2, getCurSDLoc(), MemVT);
}

SDNodeFlags Flags;
Flags.setSameSign(I.hasSameSign());
SelectionDAG::FlagInserter FlagsInserter(DAG, Flags);

EVT DestVT = DAG.getTargetLoweringInfo().getValueType(DAG.getDataLayout(),
I.getType());
setValue(&I, DAG.getSetCC(getCurSDLoc(), DestVT, Op1, Op2, Opcode));
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,9 @@ void SDNode::print_details(raw_ostream &OS, const SelectionDAG *G) const {
if (getFlags().hasDisjoint())
OS << " disjoint";

if (getFlags().hasSameSign())
OS << " samesign";

if (getFlags().hasNonNeg())
OS << " nneg";

Expand Down
1 change: 0 additions & 1 deletion llvm/lib/CodeGen/SelectionDAG/StatepointLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineMemOperand.h"
#include "llvm/CodeGen/RuntimeLibcallUtil.h"
#include "llvm/CodeGen/SelectionDAG.h"
#include "llvm/CodeGen/SelectionDAGNodes.h"
#include "llvm/CodeGen/StackMaps.h"
Expand Down
8 changes: 6 additions & 2 deletions llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineJumpTableInfo.h"
#include "llvm/CodeGen/MachineModuleInfoImpls.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/SelectionDAG.h"
#include "llvm/CodeGen/TargetRegisterInfo.h"
Expand Down Expand Up @@ -605,10 +604,15 @@ bool TargetLowering::ShrinkDemandedOp(SDValue Op, unsigned BitWidth,
EVT SmallVT = EVT::getIntegerVT(*DAG.getContext(), SmallVTBits);
if (isTruncateFree(VT, SmallVT) && isZExtFree(SmallVT, VT)) {
// We found a type with free casts.

// If the operation has the 'disjoint' flag, then the
// operands on the new node are also disjoint.
SDNodeFlags Flags(Op->getFlags().hasDisjoint() ? SDNodeFlags::Disjoint
: SDNodeFlags::None);
SDValue X = DAG.getNode(
Op.getOpcode(), dl, SmallVT,
DAG.getNode(ISD::TRUNCATE, dl, SmallVT, Op.getOperand(0)),
DAG.getNode(ISD::TRUNCATE, dl, SmallVT, Op.getOperand(1)));
DAG.getNode(ISD::TRUNCATE, dl, SmallVT, Op.getOperand(1)), Flags);
assert(DemandedSize <= SmallVTBits && "Narrowed below demanded bits?");
SDValue Z = DAG.getNode(ISD::ANY_EXTEND, dl, VT, X);
return TLO.CombineTo(Op, Z);
Expand Down
Loading