243 changes: 7 additions & 236 deletions clang/test/SemaTemplate/cwg2398.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,12 @@ namespace type_pack3 {
template<class T3> struct B;

template<template<class T4 > class TT1, class T5 > struct B<TT1<T5 >>;

template<template<class T6, class ...T7s> class TT2, class T8, class ...T9s> struct B<TT2<T8, T9s...>> {};
// new-note@-1 {{template is declared here}}
template<template<class T6, class ...T7s> class TT2, class T8, class ...T9s> struct B<TT2<T8, T9s...>>;
// old-note@-1 {{template is declared here}}

template struct B<A<int>>;
// expected-error@-1 {{explicit instantiation of undefined template}}
} // namespace type_pack3

namespace gcc_issue {
Expand Down Expand Up @@ -156,14 +158,16 @@ namespace ttp_defaults {
namespace ttp_only {
template <template <class... > class TT1> struct A { static constexpr int V = 0; };
template <template <class > class TT2> struct A<TT2> { static constexpr int V = 1; };
// new-note@-1 {{partial specialization matches}}
template <template <class, class> class TT3> struct A<TT3> { static constexpr int V = 2; };
// new-note@-1 {{partial specialization matches}}

template <class ... > struct B;
template <class > struct C;
template <class, class > struct D;
template <class, class, class> struct E;

static_assert(A<B>::V == 0);
static_assert(A<B>::V == 0); // new-error {{ambiguous partial specializations}}
static_assert(A<C>::V == 1);
static_assert(A<D>::V == 2);
static_assert(A<E>::V == 0);
Expand Down Expand Up @@ -360,152 +364,6 @@ namespace classes {
} // namespace defaulted
} // namespace classes

namespace packs {
namespace t1 {
// FIXME: This should be rejected
template<template<int, int...> class> struct A {};
// old-note@-1 {{previous non-type template parameter with type 'int' is here}}

template<char> struct B;
// old-note@-1 {{template non-type parameter has a different type 'char' in template argument}}

template struct A<B>;
// old-error@-1 {{has different template parameters}}
} // namespace t1
namespace t2 {
template<template<char, int...> class> struct A {};
// old-note@-1 {{previous non-type template parameter with type 'char' is here}}

template<int> struct B;
// old-note@-1 {{template non-type parameter has a different type 'int' in template argument}}

template struct A<B>;
// old-error@-1 {{has different template parameters}}
} // namespace t2
namespace t3 {
// FIXME: This should be rejected
template<template<int...> class> struct A {};
// old-note@-1 {{previous non-type template parameter with type 'int' is here}}

template<char> struct B;
// old-note@-1 {{template non-type parameter has a different type 'char' in template argument}}

template struct A<B>;
// old-error@-1 {{has different template parameters}}
} // namespace t3
namespace t4 {
template<template<char...> class> struct A {};
// old-note@-1 {{previous non-type template parameter with type 'char' is here}}

template<int> struct B;
// old-note@-1 {{template non-type parameter has a different type 'int' in template argument}}

template struct A<B>;
// old-error@-1 {{has different template parameters}}
} // namespace t4
} // namespace packs

namespace fun_tmpl_call {
namespace match_func {
template <template <class> class TT> void f(TT<int>) {};
// old-note@-1 {{has different template parameters}}
template <class...> struct A {};
void test() { f(A<int>()); }
// old-error@-1 {{no matching function for call to 'f'}}
} // namespace match_func
namespace order_func_nonpack {
template <template <class> class TT> void f(TT<int>) {}
template <template <class...> class TT> void f(TT<int>) = delete;

template <class> struct A {};
void test() { f(A<int>()); }
} // namespace order_func_nonpack
namespace order_func_pack {
template <template <class> class TT> void f(TT<int>) = delete;
template <template <class...> class TT> void f(TT<int>) {}

template <class...> struct A {};
void test() { f(A<int>()); }
} // namespace order_func_pack
namespace match_method {
struct A {
template <template <class> class TT> void f(TT<int>) {};
// old-note@-1 {{has different template parameters}}
};
template <class...> struct B {};
void test() { A().f(B<int>()); }
// old-error@-1 {{no matching member function for call to 'f'}}
} // namespace t2
namespace order_method_nonpack {
struct A {
template <template <class> class TT> void f(TT<int>) {}
template <template <class...> class TT> void f(TT<int>) = delete;
};
template <class> struct B {};
void test() { A().f(B<int>()); }
} // namespace order_method_nonpack
namespace order_method_pack {
struct A {
template <template <class> class TT> void f(TT<int>) = delete;
template <template <class...> class TT> void f(TT<int>) {}
};
template <class...> struct B {};
void test() { A().f(B<int>()); }
} // namespace order_method_pack
namespace match_conv {
struct A {
template <template <class> class TT> operator TT<int>() { return {}; }
// old-note@-1 {{different template parameters}}
};
template <class...> struct B {};
// old-note@-1 2{{not viable}}
void test() { B<int> b = A(); }
// old-error@-1 {{no viable conversion from 'A' to 'B<int>'}}
} // namespace match_conv
namespace order_conv_nonpack {
struct A {
template <template <class> class TT> operator TT<int>() { return {}; };
template <template <class...> class TT> operator TT<int>() = delete;
};
template <class> struct B {};
void test() { B<int> b = A(); }
} // namespace order_conv_nonpack
namespace order_conv_pack {
struct A {
template <template <class> class TT> operator TT<int>() = delete;
template <template <class...> class TT> operator TT<int>() { return {}; }
};
template <class...> struct B {};
void test() { B<int> b = A(); }
} // namespace order_conv_pack
namespace regression1 {
template <template <class, class...> class TT, class T1, class... T2s>
void f(TT<T1, T2s...>) {}
template <class> struct A {};
void test() { f(A<int>()); }
} // namespace regression1
} // namespace fun_tmpl_packs

namespace partial {
namespace t1 {
template<template<class... T1s> class TT1> struct A {};

template<template<class T2> class TT2> struct A<TT2>;

template<class... T3s> struct B;
template struct A<B>;
} // namespace t1
namespace t2 {
template<template<class... T1s> class TT1> struct A;

template<template<class T2> class TT2> struct A<TT2> {};

template<class T3> struct B;
template struct A<B>;
} // namespace t1

} // namespace partial

namespace regression1 {
template <typename T, typename Y> struct map {};
template <typename T> class foo {};
Expand All @@ -522,93 +380,6 @@ namespace regression1 {
}
} // namespace regression1

namespace constraints {
template <class T> concept C1 = true;
// new-note@-1 {{similar constraint expression here}}
// new-note@-2 2{{similar constraint expressions not considered equivalent}}

template <class T> concept C2 = C1<T> && true;
// new-note@-1 2{{similar constraint expression here}}

template <class T> concept D1 = true;
// new-note@-1 {{similar constraint expressions not considered equivalent}}

namespace t1 {
template<template<C1, class... T1s> class TT1> // new-note {{TT1' declared here}}
struct A {};
template<D1, class T2> struct B {}; // new-note {{'B' declared here}}
template struct A<B>;
// new-error@-1 {{'B' is more constrained than template template parameter 'TT1'}}
} // namespace t1
namespace t2 {
template<template<C2, class... T1s> class TT1> struct A {};
template<C1, class T2> struct B {};
template struct A<B>;
} // namespace t2
namespace t3 {
template<template<C1, class... T1s> class TT1> // new-note {{'TT1' declared here}}
struct A {};
template<C2, class T2> struct B {}; // new-note {{'B' declared here}}
template struct A<B>;
// new-error@-1 {{'B' is more constrained than template template parameter 'TT1'}}
} // namespace t2
namespace t4 {
// FIXME: This should be accepted.
template<template<C1... T1s> class TT1> // new-note {{'TT1' declared here}}
struct A {};
template<C1 T2> struct B {}; // new-note {{'B' declared here}}
template struct A<B>;
// new-error@-1 {{'B' is more constrained than template template parameter 'TT1'}}
} // namespace t4
namespace t5 {
// FIXME: This should be accepted
template<template<C2... T1s> class TT1> // new-note {{'TT1' declared here}}
struct A {};
template<C1 T2> struct B {}; // new-note {{'B' declared here}}
template struct A<B>;
// new-error@-1 {{'B' is more constrained than template template parameter 'TT1'}}
} // namespace t5
namespace t6 {
template<template<C1... T1s> class TT1> // new-note {{'TT1' declared here}}
struct A {};
template<C2 T2> struct B {}; // new-note {{'B' declared here}}
template struct A<B>;
// new-error@-1 {{'B' is more constrained than template template parameter 'TT1'}}
} // namespace t6
namespace t7 {
template<template<class... T1s> class TT1>
struct A {};
template<C1 T2> struct B {};
template struct A<B>;
} // namespace t7
namespace t8 {
template<template<C1... T1s> class TT1>
struct A {};
template<class T2> struct B {};
template struct A<B>;
} // namespace t8
namespace t9 {
template<template<C1... T1s> class TT1> // new-note {{'TT1' declared here}}
struct A {};
template<D1 T2> struct B {}; // new-note {{'B' declared here}}
template struct A<B>;
// new-error@-1 {{'B' is more constrained than template template parameter 'TT1'}}
} // namespace t9
namespace t10 {
template<template<class...> requires C1<int> class TT1> // new-note {{'TT1' declared here}}
struct A {};

template<class> requires C2<int> struct B {}; // new-note {{'B' declared here}}
template struct A<B>;
// new-error@-1 {{'B' is more constrained than template template parameter 'TT1'}}
} // namespace t10
namespace t11 {
template<template<class...> requires C2<int> class TT1> struct A {};
template<class> requires C1<int> struct B {};
template struct A<B>;
} // namespace t11
} // namespace constraints

namespace regression2 {
template <class> struct D {};

Expand Down
26 changes: 8 additions & 18 deletions clang/test/SemaTemplate/temp_arg_nontype.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -387,11 +387,12 @@ namespace PR17696 {

namespace partial_order_different_types {
template<int, int, typename T, typename, T> struct A;
// expected-note@-1 {{template is declared here}}
template<int N, typename T, typename U, T V> struct A<0, N, T, U, V> {};
template<int N, typename T, typename U, U V> struct A<0, N, T, U, V>;
// expected-error@-1 {{class template partial specialization is not more specialized than the primary template}}
A<0, 0, int, int, 0> a;
template<int N, typename T, typename U, T V> struct A<0, N, T, U, V>; // expected-note {{matches}}
// FIXME: It appears that this partial specialization should be ill-formed as
// it is not more specialized than the primary template. V is not deducible
// because it does not have the same type as the corresponding parameter.
template<int N, typename T, typename U, U V> struct A<0, N, T, U, V> {}; // expected-note {{matches}}
A<0, 0, int, int, 0> a; // expected-error {{ambiguous}}
}

namespace partial_order_references {
Expand Down Expand Up @@ -457,24 +458,13 @@ namespace dependent_nested_partial_specialization {
namespace nondependent_default_arg_ordering {
int n, m;
template<typename A, A B = &n> struct X {};

template<typename A> void f(X<A>);
// expected-note@-1 {{candidate function}}
template<typename A> void f(X<A, &m>);
// expected-note@-1 {{candidate function}}
template<typename A, A B> void f(X<A, B>);
// expected-note@-1 2{{candidate function}}
template<template<typename U, U> class T, typename A, int *B> void f(T<A, B>);
// expected-note@-1 2{{candidate function}}

// FIXME: When partial ordering, we get an inconsistent deduction between
// `A` (type-parameter-0-0) and `int *`, when deducing the first parameter.
// The deduction mechanism needs to be extended to be able to correctly
// handle these cases where the argument's template parameters appear in
// the result.
void g() {
X<int *, &n> x; f(x); // expected-error {{call to 'f' is ambiguous}}
X<int *, &m> y; f(y); // expected-error {{call to 'f' is ambiguous}}
X<int *, &n> x; f(x);
X<int *, &m> y; f(y);
}
}

Expand Down
38 changes: 15 additions & 23 deletions clang/test/SemaTemplate/temp_arg_template.cpp
Original file line number Diff line number Diff line change
@@ -1,40 +1,33 @@
// RUN: %clang_cc1 -fsyntax-only -verify=expected,precxx17 %std_cxx98-14 %s
// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx17 -std=c++17 %s

template<template<typename T> class X> struct A; // #A
// expected-note@-1 2{{previous template template parameter is here}}
template<template<typename T> class X> struct A; // expected-note 2{{previous template template parameter is here}}

template<template<typename T, int I> class X> struct B; // expected-note{{previous template template parameter is here}}

template<template<int I> class X> struct C;
// precxx17-error@-1 {{deduced non-type template argument does not have the same type as the corresponding template parameter ('int' vs 'const int &')}}
// cxx17-error@-2 {{conversion from 'int' to 'const int &' in converted constant expression would bind reference to a temporary}}
// expected-note@-3 {{previous template template parameter is here}}
template<template<int I> class X> struct C; // expected-note {{previous non-type template parameter with type 'int' is here}}

template<class> struct X; // expected-note {{template is declared here}}
template<int N> struct Y; // expected-note {{template parameter is declared here}}
template<class> struct X; // expected-note{{too few template parameters in template template argument}}
template<int N> struct Y; // expected-note{{template parameter has a different kind in template argument}}
template<long N> struct Ylong;
template<const int &N> struct Yref; // precxx17-note {{template parameter is declared here}}
template<const int &N> struct Yref; // expected-note{{template non-type parameter has a different type 'const int &' in template argument}}

namespace N {
template<class> struct Z;
}
template<class, class> struct TooMany; // expected-note{{template is declared here}}
template<class, class> struct TooMany; // expected-note{{too many template parameters in template template argument}}


A<X> *a1;
A<N::Z> *a2;
A< ::N::Z> *a3;

A<Y> *a4; // expected-error@#A {{template argument for non-type template parameter must be an expression}}
// expected-note@-1 {{different template parameters}}
A<TooMany> *a5; // expected-error {{too few template arguments for class template 'TooMany'}}
// expected-note@-1 {{different template parameters}}
B<X> *a6; // expected-error {{too many template arguments for class template 'X'}}
// expected-note@-1 {{different template parameters}}
A<Y> *a4; // expected-error{{template template argument has different template parameters than its corresponding template template parameter}}
A<TooMany> *a5; // expected-error{{template template argument has different template parameters than its corresponding template template parameter}}
B<X> *a6; // expected-error{{template template argument has different template parameters than its corresponding template template parameter}}
C<Y> *a7;
C<Ylong> *a8;
C<Yref> *a9; // expected-note {{different template parameters}}
C<Yref> *a9; // expected-error{{template template argument has different template parameters than its corresponding template template parameter}}

template<typename T> void f(int);

Expand Down Expand Up @@ -110,9 +103,9 @@ void foo() {

namespace CheckDependentNonTypeParamTypes {
template<template<typename T, typename U, T v> class X> struct A {
// expected-note@-1 {{previous template template parameter is here}}
void f() {
X<int, void*, 3> x;
X<int, void*, 3> x; // precxx17-error {{does not refer to any declaration}} \
cxx17-error {{value of type 'int' is not implicitly convertible to 'void *'}}
}
void g() {
X<int, long, 3> x;
Expand All @@ -131,16 +124,15 @@ namespace CheckDependentNonTypeParamTypes {
}
};

template<typename T, typename U, U v> struct B {
// expected-error@-1 {{conflicting deduction 'U' against 'T' for parameter}}
template<typename T, typename U, U v> struct B { // precxx17-note {{parameter}}
static const U value = v;
};

// FIXME: This should probably be rejected, but the rules are at best unclear.
A<B> ab; // expected-note {{different template parameters}}
A<B> ab;

void use() {
ab.f();
ab.f(); // expected-note {{instantiation of}}
ab.g();
ab.h();
}
Expand Down
82 changes: 30 additions & 52 deletions clang/test/SemaTemplate/temp_arg_template_p0522.cpp
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s

// expected-note@temp_arg_template_p0522.cpp:* 1+{{template is declared here}}
// expected-note@temp_arg_template_p0522.cpp:* 1+{{template parameter is declared here}}
// expected-note@temp_arg_template_p0522.cpp:* 1+{{previous template template parameter is here}}
// expected-note@temp_arg_template_p0522.cpp:* 1+{{}}

template<template<int> typename> struct Ti; // #Ti
template<template<int...> typename> struct TPi; // #TPi
template<template<int> typename> struct Ti;
template<template<int...> typename> struct TPi;
template<template<int, int...> typename> struct TiPi;
template<template<int..., int...> typename> struct TPiPi; // FIXME: Why is this not ill-formed?

template<typename T, template<T> typename> struct tT0; // #tT0
template<template<typename T, T> typename> struct Tt0; // #Tt0
template<typename T, template<T> typename> struct tT0;
template<template<typename T, T> typename> struct Tt0;

template<template<typename> typename> struct Tt;
template<template<typename, typename...> typename> struct TtPt;
Expand All @@ -21,8 +19,8 @@ template<int, int> struct ii;
template<int...> struct Pi;
template<int, int, int...> struct iiPi;

template<int, typename = int> struct iDt; // #iDt
template<int, typename> struct it; // #it
template<int, typename = int> struct iDt;
template<int, typename> struct it;

template<typename T, T v> struct t0;

Expand All @@ -33,27 +31,20 @@ namespace IntParam {
Ti<iDi>,
Ti<Pi>,
Ti<iDt>>;
using err1 = Ti<ii>; // expected-error {{too few template arguments for class template 'ii'}}
// expected-note@-1 {{different template parameters}}
using err2 = Ti<iiPi>; // expected-error {{too few template arguments for class template 'iiPi'}}
// expected-note@-1 {{different template parameters}}
using err3 = Ti<t0>; // expected-error@#Ti {{template argument for template type parameter must be a type}}
// expected-note@-1 {{different template parameters}}
using err4 = Ti<it>; // expected-error {{too few template arguments for class template 'it'}}
// expected-note@-1 {{different template parameters}}
using err1 = Ti<ii>; // expected-error {{different template parameters}}
using err2 = Ti<iiPi>; // expected-error {{different template parameters}}
using err3 = Ti<t0>; // expected-error {{different template parameters}}
using err4 = Ti<it>; // expected-error {{different template parameters}}
}

// These are accepted by the backwards-compatibility "parameter pack in
// parameter matches any number of parameters in arguments" rule.
namespace IntPackParam {
using ok = TPi<Pi>;
using ok_compat = Pt<TPi<i>, TPi<iDi>, TPi<ii>, TPi<iiPi>>;
using err1 = TPi<t0>; // expected-error@#TPi {{template argument for template type parameter must be a type}}
// expected-note@-1 {{different template parameters}}
using err2 = TPi<iDt>; // expected-error@#iDt {{could not match 'type-parameter-0-1' against}}
// expected-note@-1 {{different template parameters}}
using err3 = TPi<it>; // expected-error@#it {{could not match 'type-parameter-0-1' against}}
// expected-note@-1 {{different template parameters}}
using err1 = TPi<t0>; // expected-error {{different template parameters}}
using err2 = TPi<iDt>; // expected-error {{different template parameters}}
using err3 = TPi<it>; // expected-error {{different template parameters}}
}

namespace IntAndPackParam {
Expand All @@ -64,50 +55,42 @@ namespace IntAndPackParam {

namespace DependentType {
using ok = Pt<tT0<int, i>, tT0<int, iDi>>;
using err1 = tT0<int, ii>; // expected-error {{too few template arguments for class template 'ii'}}
// expected-note@-1 {{different template parameters}}
using err1 = tT0<int, ii>; // expected-error {{different template parameters}}
using err2 = tT0<short, i>; // FIXME: should this be OK?
using err2a = tT0<long long, i>; // FIXME: should this be OK (if long long is larger than int)?
using err2b = tT0<void*, i>; // expected-error@#tT0 {{value of type 'void *' is not implicitly convertible to 'int'}}
// expected-note@-1 {{different template parameters}}
using err3 = tT0<short, t0>; // expected-error@#tT0 {{template argument for template type parameter must be a type}}
// expected-note@-1 {{different template parameters}}
using err2b = tT0<void*, i>; // expected-error {{different template parameters}}
using err3 = tT0<short, t0>; // expected-error {{different template parameters}}

using ok2 = Tt0<t0>;
using err4 = Tt0<it>; // expected-error@#Tt0 {{template argument for non-type template parameter must be an expression}}
// expected-note@-1 {{different template parameters}}
using err4 = Tt0<it>; // expected-error {{different template parameters}}
}

namespace Auto {
template<template<int> typename T> struct TInt {}; // #TInt
template<template<int*> typename T> struct TIntPtr {}; // #TIntPtr
template<template<int> typename T> struct TInt {};
template<template<int*> typename T> struct TIntPtr {};
template<template<auto> typename T> struct TAuto {};
template<template<auto*> typename T> struct TAutoPtr {};
template<template<decltype(auto)> typename T> struct TDecltypeAuto {};
template<auto> struct Auto;
template<auto*> struct AutoPtr; // #AutoPtr
template<auto*> struct AutoPtr;
template<decltype(auto)> struct DecltypeAuto;
template<int> struct Int;
template<int*> struct IntPtr;

TInt<Auto> ia;
TInt<AutoPtr> iap; // expected-error@#TInt {{non-type template parameter '' with type 'auto *' has incompatible initializer of type 'int'}}
// expected-note@-1 {{different template parameters}}
TInt<AutoPtr> iap; // FIXME: ill-formed (?)
TInt<DecltypeAuto> ida;
TInt<Int> ii;
TInt<IntPtr> iip; // expected-error@#TInt {{conversion from 'int' to 'int *' is not allowed in a converted constant expression}}
// expected-note@-1 {{different template parameters}}
TInt<IntPtr> iip; // expected-error {{different template parameters}}

TIntPtr<Auto> ipa;
TIntPtr<AutoPtr> ipap;
TIntPtr<DecltypeAuto> ipda;
TIntPtr<Int> ipi; // expected-error@#TIntPtr {{value of type 'int *' is not implicitly convertible to 'int'}}
// expected-note@-1 {{different template parameters}}
TIntPtr<Int> ipi; // expected-error {{different template parameters}}
TIntPtr<IntPtr> ipip;

TAuto<Auto> aa;
TAuto<AutoPtr> aap; // expected-error@#AutoPtr {{could not match 'auto *' against 'auto'}}
// expected-note@-1 {{different template parameters}}
TAuto<AutoPtr> aap; // FIXME: ill-formed (?)
TAuto<Int> ai; // FIXME: ill-formed (?)
TAuto<IntPtr> aip; // FIXME: ill-formed (?)

Expand All @@ -128,8 +111,7 @@ namespace Auto {
// parameters (such as 'user-defined-type &') that are not valid 'auto'
// parameters.
TDecltypeAuto<Auto> daa;
TDecltypeAuto<AutoPtr> daap; // expected-error@#AutoPtr {{could not match 'auto *' against 'decltype(auto)'}}
// expected-note@-1 {{different template parameters}}
TDecltypeAuto<AutoPtr> daap; // FIXME: should probably be ill-formed

int n;
template<auto A, decltype(A) B = &n> struct SubstFailure;
Expand All @@ -146,7 +128,7 @@ namespace GH62529 {
} // namespace GH62529

namespace GH101394 {
struct X {}; // #X
struct X {};
struct Y {
constexpr Y(const X &) {}
};
Expand All @@ -157,12 +139,8 @@ namespace GH101394 {
template struct A<B>;
} // namespace t1
namespace t2 {
template<template<Y> class> struct A {}; // #A
template<X> struct B; // #B
template struct A<B>;
// expected-error@#A {{no viable conversion from 'const Y' to 'X'}}
// expected-note@-2 {{different template parameters}}
// expected-note@#X 2{{not viable}}
// expected-note@#B {{passing argument to parameter here}}
template<template<Y> class> struct A {};
template<X> struct B;
template struct A<B>; // expected-error {{different template parameters}}
} // namespace t2
} // namespace GH101394
12 changes: 0 additions & 12 deletions clang/test/Templight/templight-empty-entries-fix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -314,18 +314,6 @@ void foo() {
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:289:35'$}}
// CHECK: {{^poi:[ ]+''$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+unnamed template template parameter 0 of d$}}
// CHECK: {{^kind:[ ]+PartialOrderingTTP$}}
// CHECK: {{^event:[ ]+Begin$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:289:35'$}}
// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:295:5'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+unnamed template template parameter 0 of d$}}
// CHECK: {{^kind:[ ]+PartialOrderingTTP$}}
// CHECK: {{^event:[ ]+End$}}
// CHECK: {{^orig:[ ]+'.*templight-empty-entries-fix.cpp:289:35'$}}
// CHECK: {{^poi:[ ]+'.*templight-empty-entries-fix.cpp:295:5'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+d$}}
// CHECK: {{^kind:[ ]+ExplicitTemplateArgumentSubstitution$}}
// CHECK: {{^event:[ ]+End$}}
Expand Down
33 changes: 10 additions & 23 deletions clang/test/Templight/templight-prior-template-arg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,76 +10,63 @@ class B {};
// CHECK: {{^kind:[ ]+PriorTemplateArgumentSubstitution$}}
// CHECK: {{^event:[ ]+Begin$}}
// CHECK: {{^orig:[ ]+'.*templight-prior-template-arg.cpp:5:40'}}
// CHECK: {{^poi:[ ]+'.*templight-prior-template-arg.cpp:85:1'$}}
// CHECK: {{^poi:[ ]+'.*templight-prior-template-arg.cpp:72:1'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+'B::Outer'$}}
// CHECK: {{^kind:[ ]+PriorTemplateArgumentSubstitution$}}
// CHECK: {{^event:[ ]+End$}}
// CHECK: {{^orig:[ ]+'.*templight-prior-template-arg.cpp:5:40'}}
// CHECK: {{^poi:[ ]+'.*templight-prior-template-arg.cpp:85:1'$}}
//
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+'B::Outer'$}}
// CHECK: {{^kind:[ ]+PartialOrderingTTP$}}
// CHECK: {{^event:[ ]+Begin$}}
// CHECK: {{^orig:[ ]+'.*templight-prior-template-arg.cpp:5:40'}}
// CHECK: {{^poi:[ ]+'.*templight-prior-template-arg.cpp:85:3'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+'B::Outer'$}}
// CHECK: {{^kind:[ ]+PartialOrderingTTP$}}
// CHECK: {{^event:[ ]+End$}}
// CHECK: {{^orig:[ ]+'.*templight-prior-template-arg.cpp:5:40'}}
// CHECK: {{^poi:[ ]+'.*templight-prior-template-arg.cpp:85:3'$}}
// CHECK: {{^poi:[ ]+'.*templight-prior-template-arg.cpp:72:1'$}}
//
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+'B<A>'$}}
// CHECK: {{^kind:[ ]+TemplateInstantiation$}}
// CHECK: {{^event:[ ]+Begin$}}
// CHECK: {{^orig:[ ]+'.*templight-prior-template-arg.cpp:6:7'}}
// CHECK: {{^poi:[ ]+'.*templight-prior-template-arg.cpp:85:6'$}}
// CHECK: {{^poi:[ ]+'.*templight-prior-template-arg.cpp:72:6'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+'B<A>'$}}
// CHECK: {{^kind:[ ]+TemplateInstantiation$}}
// CHECK: {{^event:[ ]+End$}}
// CHECK: {{^orig:[ ]+'.*templight-prior-template-arg.cpp:6:7'}}
// CHECK: {{^poi:[ ]+'.*templight-prior-template-arg.cpp:85:6'$}}
// CHECK: {{^poi:[ ]+'.*templight-prior-template-arg.cpp:72:6'$}}
//
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+'B<A>'$}}
// CHECK: {{^kind:[ ]+TemplateInstantiation$}}
// CHECK: {{^event:[ ]+Begin$}}
// CHECK: {{^orig:[ ]+'.*templight-prior-template-arg.cpp:6:7'}}
// CHECK: {{^poi:[ ]+'.*templight-prior-template-arg.cpp:85:6'$}}
// CHECK: {{^poi:[ ]+'.*templight-prior-template-arg.cpp:72:6'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+'B<A>'$}}
// CHECK: {{^kind:[ ]+TemplateInstantiation$}}
// CHECK: {{^event:[ ]+End$}}
// CHECK: {{^orig:[ ]+'.*templight-prior-template-arg.cpp:6:7'}}
// CHECK: {{^poi:[ ]+'.*templight-prior-template-arg.cpp:85:6'$}}
// CHECK: {{^poi:[ ]+'.*templight-prior-template-arg.cpp:72:6'$}}
//
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+'B<A>'$}}
// CHECK: {{^kind:[ ]+Memoization$}}
// CHECK: {{^event:[ ]+Begin$}}
// CHECK: {{^orig:[ ]+'.*templight-prior-template-arg.cpp:6:7'}}
// CHECK: {{^poi:[ ]+'.*templight-prior-template-arg.cpp:85:6'$}}
// CHECK: {{^poi:[ ]+'.*templight-prior-template-arg.cpp:72:6'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+'B<A>'$}}
// CHECK: {{^kind:[ ]+Memoization$}}
// CHECK: {{^event:[ ]+End$}}
// CHECK: {{^orig:[ ]+'.*templight-prior-template-arg.cpp:6:7'}}
// CHECK: {{^poi:[ ]+'.*templight-prior-template-arg.cpp:85:6'$}}
// CHECK: {{^poi:[ ]+'.*templight-prior-template-arg.cpp:72:6'$}}
//
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+'B<A>'$}}
// CHECK: {{^kind:[ ]+Memoization$}}
// CHECK: {{^event:[ ]+Begin$}}
// CHECK: {{^orig:[ ]+'.*templight-prior-template-arg.cpp:6:7'}}
// CHECK: {{^poi:[ ]+'.*templight-prior-template-arg.cpp:85:6'$}}
// CHECK: {{^poi:[ ]+'.*templight-prior-template-arg.cpp:72:6'$}}
// CHECK-LABEL: {{^---$}}
// CHECK: {{^name:[ ]+'B<A>'$}}
// CHECK: {{^kind:[ ]+Memoization$}}
// CHECK: {{^event:[ ]+End$}}
// CHECK: {{^orig:[ ]+'.*templight-prior-template-arg.cpp:6:7'}}
// CHECK: {{^poi:[ ]+'.*templight-prior-template-arg.cpp:85:6'$}}
// CHECK: {{^poi:[ ]+'.*templight-prior-template-arg.cpp:72:6'$}}
B<A> b;
29 changes: 29 additions & 0 deletions clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2599,6 +2599,35 @@ TEST_P(ASTMatchersTest, HasName_MatchesInlinedNamespaces) {
EXPECT_TRUE(matches(code, recordDecl(hasName("::a::C"))));
}

TEST_P(ASTMatchersTest, HasName_MatchesSpecializedInlinedNamespace) {
if (!GetParam().isCXX11OrLater()) {
return;
}

StringRef code = R"(
namespace a {
inline namespace v1 {
template<typename T> T foo(T);
}
}
namespace a {
enum Tag{T1, T2};
template <Tag, typename T> T foo(T);
}
auto v1 = a::foo(1);
auto v2 = a::foo<a::T1>(1);
)";
EXPECT_TRUE(matches(
code, varDecl(hasName("v1"), hasDescendant(callExpr(callee(
functionDecl(hasName("::a::foo"))))))));
EXPECT_TRUE(matches(
code, varDecl(hasName("v2"), hasDescendant(callExpr(callee(
functionDecl(hasName("::a::foo"))))))));
}

TEST_P(ASTMatchersTest, HasName_MatchesAnonymousNamespaces) {
if (!GetParam().isCXX()) {
return;
Expand Down
22 changes: 11 additions & 11 deletions clang/www/cxx_dr_status.html
Original file line number Diff line number Diff line change
Expand Up @@ -17128,11 +17128,11 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<td>Undesired outcomes with <TT>const_cast</TT></td>
<td align="center">Not resolved</td>
</tr>
<tr class="open" id="2880">
<tr id="2880">
<td><a href="https://cplusplus.github.io/CWG/issues/2880.html">2880</a></td>
<td>open</td>
<td>accepted</td>
<td>Accessibility check for destructor of incomplete class type</td>
<td align="center">Not resolved</td>
<td class="unknown" align="center">Unknown</td>
</tr>
<tr id="2881">
<td><a href="https://cplusplus.github.io/CWG/issues/2881.html">2881</a></td>
Expand Down Expand Up @@ -17260,7 +17260,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
</tr>
<tr class="open" id="2901">
<td><a href="https://cplusplus.github.io/CWG/issues/2901.html">2901</a></td>
<td>review</td>
<td>tentatively ready</td>
<td>Unclear semantics for near-match aliased access</td>
<td align="center">Not resolved</td>
</tr>
Expand Down Expand Up @@ -17408,31 +17408,31 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
</tr>
<tr class="open" id="2923">
<td><a href="https://cplusplus.github.io/CWG/issues/2923.html">2923</a></td>
<td>open</td>
<td>tentatively ready</td>
<td>Note about infinite loops and execution steps</td>
<td align="center">Not resolved</td>
</tr>
<tr class="open" id="2924">
<td><a href="https://cplusplus.github.io/CWG/issues/2924.html">2924</a></td>
<td>open</td>
<td>review</td>
<td>Undefined behavior during constant evaluation</td>
<td align="center">Not resolved</td>
</tr>
<tr class="open" id="2925">
<tr id="2925">
<td><a href="https://cplusplus.github.io/CWG/issues/2925.html">2925</a></td>
<td>open</td>
<td>NAD</td>
<td>Deleting a pointer to an incomplete enumeration type</td>
<td align="center">Not resolved</td>
<td class="unknown" align="center">Unknown</td>
</tr>
<tr class="open" id="2926">
<td><a href="https://cplusplus.github.io/CWG/issues/2926.html">2926</a></td>
<td>open</td>
<td>tentatively ready</td>
<td>Lookup context for dependent qualified names</td>
<td align="center">Not resolved</td>
</tr>
<tr class="open" id="2927">
<td><a href="https://cplusplus.github.io/CWG/issues/2927.html">2927</a></td>
<td>open</td>
<td>review</td>
<td>Unclear status of translation unit with <TT>module</TT> keyword</td>
<td align="center">Not resolved</td>
</tr>
Expand Down
26 changes: 11 additions & 15 deletions compiler-rt/lib/asan/asan_descriptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,20 @@
namespace __asan {

AsanThreadIdAndName::AsanThreadIdAndName(AsanThreadContext *t) {
Init(t->tid, t->name);
}

AsanThreadIdAndName::AsanThreadIdAndName(u32 tid) {
if (tid == kInvalidTid) {
Init(tid, "");
} else {
asanThreadRegistry().CheckLocked();
AsanThreadContext *t = GetThreadContextByTidLocked(tid);
Init(tid, t->name);
if (!t) {
internal_snprintf(name, sizeof(name), "T-1");
return;
}
int len = internal_snprintf(name, sizeof(name), "T%llu", t->unique_id);
CHECK(((unsigned int)len) < sizeof(name));
if (internal_strlen(t->name))
internal_snprintf(&name[len], sizeof(name) - len, " (%s)", t->name);
}

void AsanThreadIdAndName::Init(u32 tid, const char *tname) {
int len = internal_snprintf(name, sizeof(name), "T%d", tid);
CHECK(((unsigned int)len) < sizeof(name));
if (tname[0] != '\0')
internal_snprintf(&name[len], sizeof(name) - len, " (%s)", tname);
AsanThreadIdAndName::AsanThreadIdAndName(u32 tid)
: AsanThreadIdAndName(
tid == kInvalidTid ? nullptr : GetThreadContextByTidLocked(tid)) {
asanThreadRegistry().CheckLocked();
}

void DescribeThread(AsanThreadContext *context) {
Expand Down
2 changes: 0 additions & 2 deletions compiler-rt/lib/asan/asan_descriptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@ class AsanThreadIdAndName {
const char *c_str() const { return &name[0]; }

private:
void Init(u32 tid, const char *tname);

char name[128];
};

Expand Down
7 changes: 4 additions & 3 deletions compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1027,7 +1027,6 @@ bool internal_sigismember(__sanitizer_sigset_t *set, int signum) {
// ThreadLister implementation.
ThreadLister::ThreadLister(pid_t pid) : buffer_(4096) {
task_path_.AppendF("/proc/%d/task", pid);
status_path_.AppendF("%s/status", task_path_.data());
}

ThreadLister::Result ThreadLister::ListThreads(
Expand Down Expand Up @@ -1086,7 +1085,9 @@ ThreadLister::Result ThreadLister::ListThreads(
}
}

const char *ThreadLister::LoadStatus(int tid) {
const char *ThreadLister::LoadStatus(tid_t tid) {
status_path_.clear();
status_path_.AppendF("%s/%llu/status", task_path_.data(), tid);
auto cleanup = at_scope_exit([&] {
// Resize back to capacity if it is downsized by `ReadFileToVector`.
buffer_.resize(buffer_.capacity());
Expand All @@ -1097,7 +1098,7 @@ const char *ThreadLister::LoadStatus(int tid) {
return buffer_.data();
}

bool ThreadLister::IsAlive(int tid) {
bool ThreadLister::IsAlive(tid_t tid) {
// /proc/%d/task/%d/status uses same call to detect alive threads as
// proc_task_readdir. See task_state implementation in Linux.
static const char kPrefix[] = "\nPPid:";
Expand Down
4 changes: 2 additions & 2 deletions compiler-rt/lib/sanitizer_common/sanitizer_linux.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,10 @@ class ThreadLister {
Ok,
};
Result ListThreads(InternalMmapVector<tid_t> *threads);
const char *LoadStatus(int tid);
const char *LoadStatus(tid_t tid);

private:
bool IsAlive(int tid);
bool IsAlive(tid_t tid);

InternalScopedString task_path_;
InternalScopedString status_path_;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,6 @@ class ThreadSuspender {
};

bool ThreadSuspender::SuspendThread(tid_t tid) {
// Are we already attached to this thread?
// Currently this check takes linear time, however the number of threads is
// usually small.
if (suspended_threads_list_.ContainsTid(tid)) return false;
int pterrno;
if (internal_iserror(internal_ptrace(PTRACE_ATTACH, tid, nullptr, nullptr),
&pterrno)) {
Expand Down Expand Up @@ -220,12 +216,18 @@ bool ThreadSuspender::SuspendAllThreads() {
VReport(1, "Failed to list threads\n");
return false;
case ThreadLister::Incomplete:
VReport(1, "Incomplete list\n");
retry = true;
break;
case ThreadLister::Ok:
break;
}
for (tid_t tid : threads) {
// Are we already attached to this thread?
// Currently this check takes linear time, however the number of threads
// is usually small.
if (suspended_threads_list_.ContainsTid(tid))
continue;
if (SuspendThread(tid))
retry = true;
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ class SANITIZER_MUTEX ThreadRegistry {

// Should be guarded by ThreadRegistryLock.
ThreadContextBase *GetThreadLocked(u32 tid) {
return threads_.empty() ? nullptr : threads_[tid];
return tid < threads_.size() ? threads_[tid] : nullptr;
}

u32 NumThreadsLocked() const { return threads_.size(); }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@ TEST_F(ThreadListerTest, ThreadListerSeesAllSpawnedThreads) {
std::vector<tid_t> listed_tids = ReadTidsToVector(&thread_lister);
ASSERT_TRUE(HasElement(listed_tids, self_tid));
ASSERT_TRUE(Includes(listed_tids, tids_));

ASSERT_NE(nullptr, thread_lister.LoadStatus(self_tid));
for (auto tid : tids_) ASSERT_NE(nullptr, thread_lister.LoadStatus(tid));
}

TEST_F(ThreadListerTest, DoNotForgetThreads) {
Expand Down
9 changes: 6 additions & 3 deletions flang/lib/Lower/OpenMP/OpenMP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1310,8 +1310,8 @@ static void genTaskClauses(lower::AbstractConverter &converter,
cp.processUntied(clauseOps);
// TODO Support delayed privatization.

cp.processTODO<clause::Affinity, clause::Detach, clause::InReduction>(
loc, llvm::omp::Directive::OMPD_task);
cp.processTODO<clause::Affinity, clause::Detach, clause::InReduction,
clause::Mergeable>(loc, llvm::omp::Directive::OMPD_task);
}

static void genTaskgroupClauses(lower::AbstractConverter &converter,
Expand Down Expand Up @@ -2780,7 +2780,10 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
!std::holds_alternative<clause::ThreadLimit>(clause.u) &&
!std::holds_alternative<clause::Threads>(clause.u) &&
!std::holds_alternative<clause::UseDeviceAddr>(clause.u) &&
!std::holds_alternative<clause::UseDevicePtr>(clause.u)) {
!std::holds_alternative<clause::UseDevicePtr>(clause.u) &&
!std::holds_alternative<clause::InReduction>(clause.u) &&
!std::holds_alternative<clause::Mergeable>(clause.u) &&
!std::holds_alternative<clause::TaskReduction>(clause.u)) {
TODO(clauseLocation, "OpenMP Block construct clause");
}
}
Expand Down
29 changes: 29 additions & 0 deletions flang/lib/Optimizer/Analysis/AliasAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#include "flang/Optimizer/Dialect/FortranVariableInterface.h"
#include "flang/Optimizer/HLFIR/HLFIROps.h"
#include "mlir/Analysis/AliasAnalysis.h"
#include "mlir/Dialect/OpenMP/OpenMPDialect.h"
#include "mlir/Dialect/OpenMP/OpenMPInterfaces.h"
#include "mlir/IR/BuiltinOps.h"
#include "mlir/IR/Value.h"
#include "mlir/Interfaces/SideEffectInterfaces.h"
Expand Down Expand Up @@ -296,6 +298,17 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v,
defOp = v.getDefiningOp();
return;
}
// If load is inside target and it points to mapped item,
// continue tracking.
Operation *loadMemrefOp = op.getMemref().getDefiningOp();
bool isDeclareOp = llvm::isa<fir::DeclareOp>(loadMemrefOp) ||
llvm::isa<hlfir::DeclareOp>(loadMemrefOp);
if (isDeclareOp &&
llvm::isa<omp::TargetOp>(loadMemrefOp->getParentOp())) {
v = op.getMemref();
defOp = v.getDefiningOp();
return;
}
// No further tracking for addresses loaded from memory for now.
type = SourceKind::Indirect;
breakFromLoop = true;
Expand All @@ -319,6 +332,22 @@ AliasAnalysis::Source AliasAnalysis::getSource(mlir::Value v,
breakFromLoop = true;
})
.Case<hlfir::DeclareOp, fir::DeclareOp>([&](auto op) {
// If declare operation is inside omp target region,
// continue alias analysis outside the target region
if (auto targetOp =
llvm::dyn_cast<omp::TargetOp>(op->getParentOp())) {
auto argIface = cast<omp::BlockArgOpenMPOpInterface>(*targetOp);
for (auto [opArg, blockArg] : llvm::zip_equal(
targetOp.getMapVars(), argIface.getMapBlockArgs())) {
if (blockArg == op.getMemref()) {
omp::MapInfoOp mapInfo =
llvm::cast<omp::MapInfoOp>(opArg.getDefiningOp());
v = mapInfo.getVarPtr();
defOp = v.getDefiningOp();
return;
}
}
}
auto varIf = llvm::cast<fir::FortranVariableOpInterface>(defOp);
// While going through a declare operation collect
// the variable attributes from it. Right now, some
Expand Down
2 changes: 2 additions & 0 deletions flang/lib/Optimizer/Analysis/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ add_flang_library(FIRAnalysis
FIRDialect
HLFIRDialect
MLIRIR
MLIROpenMPDialect

LINK_LIBS
FIRBuilder
Expand All @@ -14,5 +15,6 @@ add_flang_library(FIRAnalysis
MLIRFuncDialect
MLIRLLVMDialect
MLIRMathTransforms
MLIROpenMPDialect
FIRSupport
)
24 changes: 13 additions & 11 deletions flang/lib/Optimizer/Transforms/StackArrays.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ mlir::LogicalResult AllocationAnalysis::visitOperation(
}
} else if (mlir::isa<fir::ResultOp>(op)) {
mlir::Operation *parent = op->getParentOp();
LatticePoint *parentLattice = getLattice(parent);
LatticePoint *parentLattice = getLattice(getProgramPointAfter(parent));
assert(parentLattice);
mlir::ChangeResult parentChanged = parentLattice->join(*after);
propagateIfChanged(parentLattice, parentChanged);
Expand All @@ -396,28 +396,29 @@ void AllocationAnalysis::setToEntryState(LatticePoint *lattice) {
/// Mostly a copy of AbstractDenseLattice::processOperation - the difference
/// being that call operations are passed through to the transfer function
mlir::LogicalResult AllocationAnalysis::processOperation(mlir::Operation *op) {
mlir::ProgramPoint *point = getProgramPointAfter(op);
// If the containing block is not executable, bail out.
if (!getOrCreateFor<mlir::dataflow::Executable>(op, op->getBlock())->isLive())
if (op->getBlock() != nullptr &&
!getOrCreateFor<mlir::dataflow::Executable>(
point, getProgramPointBefore(op->getBlock()))
->isLive())
return mlir::success();

// Get the dense lattice to update
mlir::dataflow::AbstractDenseLattice *after = getLattice(op);
mlir::dataflow::AbstractDenseLattice *after = getLattice(point);

// If this op implements region control-flow, then control-flow dictates its
// transfer function.
if (auto branch = mlir::dyn_cast<mlir::RegionBranchOpInterface>(op)) {
visitRegionBranchOperation(op, branch, after);
visitRegionBranchOperation(point, branch, after);
return mlir::success();
}

// pass call operations through to the transfer function

// Get the dense state before the execution of the op.
const mlir::dataflow::AbstractDenseLattice *before;
if (mlir::Operation *prev = op->getPrevNode())
before = getLatticeFor(op, prev);
else
before = getLatticeFor(op, op->getBlock());
const mlir::dataflow::AbstractDenseLattice *before =
getLatticeFor(point, getProgramPointBefore(op));

/// Invoke the operation transfer function
return visitOperationImpl(op, *before, after);
Expand Down Expand Up @@ -452,9 +453,10 @@ StackArraysAnalysisWrapper::analyseFunction(mlir::Operation *func) {
return mlir::failure();
}

LatticePoint point{func};
LatticePoint point{solver.getProgramPointAfter(func)};
auto joinOperationLattice = [&](mlir::Operation *op) {
const LatticePoint *lattice = solver.lookupState<LatticePoint>(op);
const LatticePoint *lattice =
solver.lookupState<LatticePoint>(solver.getProgramPointAfter(op));
// there will be no lattice for an unreachable block
if (lattice)
(void)point.join(*lattice);
Expand Down
66 changes: 66 additions & 0 deletions flang/test/Analysis/AliasAnalysis/alias-analysis-omp-target-1.fir
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Use --mlir-disable-threading so that the AA queries are serialized
// as well as its diagnostic output.
// RUN: fir-opt %s -pass-pipeline='builtin.module(func.func(test-fir-alias-analysis))' -split-input-file --mlir-disable-threading 2>&1 | FileCheck %s

// Fortran source code:
//
// program TestAllocatableArray
// real(kind=8), allocatable :: A(:)
// real(kind=8), allocatable :: B(:)
// !$omp target
// A(0) = B(0)
// !$omp end target
// end TestAllocatableArray

// CHECK-LABEL: Testing : "_QPTestAllocatableArray"
// CHECK-DAG: targetArrayB#0 <-> targetArrayA#0: NoAlias
func.func @_QPTestAllocatableArray() {
%0 = fir.address_of(@_QFEa) : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>
%1:2 = hlfir.declare %0 {fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "ArrayA" } : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>) -> (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>)
%2 = fir.address_of(@_QFEb) : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>
%3:2 = hlfir.declare %2 {fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "ArrayB" } : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>) -> (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>)
%4 = fir.load %1#0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>
%c1 = arith.constant 1 : index
%c0 = arith.constant 0 : index
%5 = fir.load %1#1 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>
%c0_0 = arith.constant 0 : index
%6:3 = fir.box_dims %5, %c0_0 : (!fir.box<!fir.heap<!fir.array<?xf64>>>, index) -> (index, index, index)
%7:3 = fir.box_dims %4, %c0 : (!fir.box<!fir.heap<!fir.array<?xf64>>>, index) -> (index, index, index)
%c0_1 = arith.constant 0 : index
%8 = arith.subi %7#1, %c1 : index
%9 = omp.map.bounds lower_bound(%c0_1 : index) upper_bound(%8 : index) extent(%7#1 : index) stride(%7#2 : index) start_idx(%6#0 : index) {stride_in_bytes = true}
%10 = fir.box_offset %1#1 base_addr : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>) -> !fir.llvm_ptr<!fir.ref<!fir.array<?xf64>>>
%11 = omp.map.info var_ptr(%1#1 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>, !fir.array<?xf64>) var_ptr_ptr(%10 : !fir.llvm_ptr<!fir.ref<!fir.array<?xf64>>>) map_clauses(implicit, tofrom) capture(ByRef) bounds(%9) -> !fir.llvm_ptr<!fir.ref<!fir.array<?xf64>>> {name = ""}
%12 = omp.map.info var_ptr(%1#1 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>, !fir.box<!fir.heap<!fir.array<?xf64>>>) map_clauses(implicit, tofrom) capture(ByRef) members(%11 : [0] : !fir.llvm_ptr<!fir.ref<!fir.array<?xf64>>>) -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>> {name = "a"}
%13 = fir.load %3#0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>
%c1_2 = arith.constant 1 : index
%c0_3 = arith.constant 0 : index
%14 = fir.load %3#1 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>
%c0_4 = arith.constant 0 : index
%15:3 = fir.box_dims %14, %c0_4 : (!fir.box<!fir.heap<!fir.array<?xf64>>>, index) -> (index, index, index)
%16:3 = fir.box_dims %13, %c0_3 : (!fir.box<!fir.heap<!fir.array<?xf64>>>, index) -> (index, index, index)
%c0_5 = arith.constant 0 : index
%17 = arith.subi %16#1, %c1_2 : index
%18 = omp.map.bounds lower_bound(%c0_5 : index) upper_bound(%17 : index) extent(%16#1 : index) stride(%16#2 : index) start_idx(%15#0 : index) {stride_in_bytes = true}
%19 = fir.box_offset %3#1 base_addr : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>) -> !fir.llvm_ptr<!fir.ref<!fir.array<?xf64>>>
%20 = omp.map.info var_ptr(%3#1 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>, !fir.array<?xf64>) var_ptr_ptr(%19 : !fir.llvm_ptr<!fir.ref<!fir.array<?xf64>>>) map_clauses(implicit, tofrom) capture(ByRef) bounds(%18) -> !fir.llvm_ptr<!fir.ref<!fir.array<?xf64>>> {name = ""}
%21 = omp.map.info var_ptr(%3#1 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>, !fir.box<!fir.heap<!fir.array<?xf64>>>) map_clauses(implicit, tofrom) capture(ByRef) members(%20 : [0] : !fir.llvm_ptr<!fir.ref<!fir.array<?xf64>>>) -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>> {name = "b"}
omp.target map_entries(%11 -> %arg0, %12 -> %arg1, %20 -> %arg2, %21 -> %arg3 : !fir.llvm_ptr<!fir.ref<!fir.array<?xf64>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>, !fir.llvm_ptr<!fir.ref<!fir.array<?xf64>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>) {
%22:2 = hlfir.declare %arg1 {fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFEa"} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>) -> (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>)
%23:2 = hlfir.declare %arg3 {fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFEb"} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>) -> (!fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>)
%24 = fir.load %23#0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>
%c0_6 = arith.constant 0 : index
%25 = hlfir.designate %24 (%c0_6) {test.ptr = "targetArrayB"} : (!fir.box<!fir.heap<!fir.array<?xf64>>>, index) -> !fir.ref<f64>
%26 = fir.load %25 : !fir.ref<f64>
%27 = fir.load %22#0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf64>>>>
%c0_7 = arith.constant 0 : index
%28 = hlfir.designate %27 (%c0_7) {test.ptr = "targetArrayA"} : (!fir.box<!fir.heap<!fir.array<?xf64>>>, index) -> !fir.ref<f64>
hlfir.assign %26 to %28 : f64, !fir.ref<f64>
omp.terminator
}
return
}
fir.global internal @_QFEa : !fir.box<!fir.heap<!fir.array<?xf64>>> {
}
fir.global internal @_QFEb : !fir.box<!fir.heap<!fir.array<?xf64>>> {
}
96 changes: 96 additions & 0 deletions flang/test/Analysis/AliasAnalysis/alias-analysis-omp-target-2.fir
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Use --mlir-disable-threading so that the AA queries are serialized
// as well as its diagnostic output.
// RUN: fir-opt %s -pass-pipeline='builtin.module(func.func(test-fir-alias-analysis))' -split-input-file --mlir-disable-threading 2>&1 | FileCheck %s

// Fortran source code:
//
// subroutine TestTargetData(p, a, b)
// real :: p(10), a(10), b(10)
// !$omp target data map(from: p)
// !$omp target map(to: a )
// p(1) = a(1)
// !$omp end target
// !$omp target map(to: b )
// p(1) = b(1)
// !$omp end target
// !$omp end target data
// end subroutine

// CHECK-LABEL: Testing : "_QPTestTargetData"

// CHECK-DAG: targetArrayA#0 <-> targetArrayP#0: NoAlias
// CHECK-DAG: targetArrayA#0 <-> targetArrayB#0: NoAlias
// CHECK-DAG: targetArrayP#0 <-> targetArrayB#0: NoAlias

func.func @_QPTestTargetData(%arg0: !fir.ref<!fir.array<10xf32>> {fir.bindc_name = "p"}, %arg1: !fir.ref<!fir.array<10xf32>> {fir.bindc_name = "a"}, %arg2: !fir.ref<!fir.array<10xf32>> {fir.bindc_name = "b"}) {
%0 = fir.dummy_scope : !fir.dscope
%c10 = arith.constant 10 : index
%1 = fir.shape %c10 : (index) -> !fir.shape<1>
%2:2 = hlfir.declare %arg1(%1) dummy_scope %0 {uniq_name = "_QFtest_target_dataEa"} : (!fir.ref<!fir.array<10xf32>>, !fir.shape<1>, !fir.dscope) -> (!fir.ref<!fir.array<10xf32>>, !fir.ref<!fir.array<10xf32>>)
%c10_0 = arith.constant 10 : index
%3 = fir.shape %c10_0 : (index) -> !fir.shape<1>
%4:2 = hlfir.declare %arg2(%3) dummy_scope %0 {uniq_name = "_QFtest_target_dataEb"} : (!fir.ref<!fir.array<10xf32>>, !fir.shape<1>, !fir.dscope) -> (!fir.ref<!fir.array<10xf32>>, !fir.ref<!fir.array<10xf32>>)
%c10_1 = arith.constant 10 : index
%5 = fir.shape %c10_1 : (index) -> !fir.shape<1>
%6:2 = hlfir.declare %arg0(%5) dummy_scope %0 {uniq_name = "_QFtest_target_dataEp"} : (!fir.ref<!fir.array<10xf32>>, !fir.shape<1>, !fir.dscope) -> (!fir.ref<!fir.array<10xf32>>, !fir.ref<!fir.array<10xf32>>)
%c1 = arith.constant 1 : index
%c0 = arith.constant 0 : index
%7 = arith.subi %c10_1, %c1 : index
%8 = omp.map.bounds lower_bound(%c0 : index) upper_bound(%7 : index) extent(%c10_1 : index) stride(%c1 : index) start_idx(%c1 : index)
%9 = omp.map.info var_ptr(%6#1 : !fir.ref<!fir.array<10xf32>>, !fir.array<10xf32>) map_clauses(from) capture(ByRef) bounds(%8) -> !fir.ref<!fir.array<10xf32>> {name = "p"}
omp.target_data map_entries(%9 : !fir.ref<!fir.array<10xf32>>) {
%c1_2 = arith.constant 1 : index
%c0_3 = arith.constant 0 : index
%10 = arith.subi %c10, %c1_2 : index
%11 = omp.map.bounds lower_bound(%c0_3 : index) upper_bound(%10 : index) extent(%c10 : index) stride(%c1_2 : index) start_idx(%c1_2 : index)
%12 = omp.map.info var_ptr(%2#1 : !fir.ref<!fir.array<10xf32>>, !fir.array<10xf32>) map_clauses(to) capture(ByRef) bounds(%11) -> !fir.ref<!fir.array<10xf32>> {name = "a"}
%c1_4 = arith.constant 1 : index
%c0_5 = arith.constant 0 : index
%13 = arith.subi %c10_1, %c1_4 : index
%14 = omp.map.bounds lower_bound(%c0_5 : index) upper_bound(%13 : index) extent(%c10_1 : index) stride(%c1_4 : index) start_idx(%c1_4 : index)
%15 = omp.map.info var_ptr(%6#1 : !fir.ref<!fir.array<10xf32>>, !fir.array<10xf32>) map_clauses(implicit, tofrom) capture(ByRef) bounds(%14) -> !fir.ref<!fir.array<10xf32>> {name = "p"}
omp.target map_entries(%12 -> %arg3, %15 -> %arg4 : !fir.ref<!fir.array<10xf32>>, !fir.ref<!fir.array<10xf32>>) {
%c10_10 = arith.constant 10 : index
%22 = fir.shape %c10_10 : (index) -> !fir.shape<1>
%23:2 = hlfir.declare %arg3(%22) {uniq_name = "_QFtest_target_dataEa"} : (!fir.ref<!fir.array<10xf32>>, !fir.shape<1>) -> (!fir.ref<!fir.array<10xf32>>, !fir.ref<!fir.array<10xf32>>)
%c10_11 = arith.constant 10 : index
%24 = fir.shape %c10_11 : (index) -> !fir.shape<1>
%25:2 = hlfir.declare %arg4(%24) {uniq_name = "_QFtest_target_dataEp"} : (!fir.ref<!fir.array<10xf32>>, !fir.shape<1>) -> (!fir.ref<!fir.array<10xf32>>, !fir.ref<!fir.array<10xf32>>)
%c1_12 = arith.constant 1 : index
%26 = hlfir.designate %23#0 (%c1_12) {test.ptr = "targetArrayA"} : (!fir.ref<!fir.array<10xf32>>, index) -> !fir.ref<f32>
%27 = fir.load %26 : !fir.ref<f32>
%c1_13 = arith.constant 1 : index
%28 = hlfir.designate %25#0 (%c1_13) {test.ptr = "targetArrayP"} : (!fir.ref<!fir.array<10xf32>>, index) -> !fir.ref<f32>
hlfir.assign %27 to %28 : f32, !fir.ref<f32>
omp.terminator
}
%c1_6 = arith.constant 1 : index
%c0_7 = arith.constant 0 : index
%16 = arith.subi %c10_0, %c1_6 : index
%17 = omp.map.bounds lower_bound(%c0_7 : index) upper_bound(%16 : index) extent(%c10_0 : index) stride(%c1_6 : index) start_idx(%c1_6 : index)
%18 = omp.map.info var_ptr(%4#1 : !fir.ref<!fir.array<10xf32>>, !fir.array<10xf32>) map_clauses(to) capture(ByRef) bounds(%17) -> !fir.ref<!fir.array<10xf32>> {name = "b"}
%c1_8 = arith.constant 1 : index
%c0_9 = arith.constant 0 : index
%19 = arith.subi %c10_1, %c1_8 : index
%20 = omp.map.bounds lower_bound(%c0_9 : index) upper_bound(%19 : index) extent(%c10_1 : index) stride(%c1_8 : index) start_idx(%c1_8 : index)
%21 = omp.map.info var_ptr(%6#1 : !fir.ref<!fir.array<10xf32>>, !fir.array<10xf32>) map_clauses(implicit, tofrom) capture(ByRef) bounds(%20) -> !fir.ref<!fir.array<10xf32>> {name = "p"}
omp.target map_entries(%18 -> %arg3, %21 -> %arg4 : !fir.ref<!fir.array<10xf32>>, !fir.ref<!fir.array<10xf32>>) {
%c10_10 = arith.constant 10 : index
%22 = fir.shape %c10_10 : (index) -> !fir.shape<1>
%23:2 = hlfir.declare %arg3(%22) {uniq_name = "_QFtest_target_dataEb"} : (!fir.ref<!fir.array<10xf32>>, !fir.shape<1>) -> (!fir.ref<!fir.array<10xf32>>, !fir.ref<!fir.array<10xf32>>)
%c10_11 = arith.constant 10 : index
%24 = fir.shape %c10_11 : (index) -> !fir.shape<1>
%25:2 = hlfir.declare %arg4(%24) {uniq_name = "_QFtest_target_dataEp"} : (!fir.ref<!fir.array<10xf32>>, !fir.shape<1>) -> (!fir.ref<!fir.array<10xf32>>, !fir.ref<!fir.array<10xf32>>)
%c1_12 = arith.constant 1 : index
%26 = hlfir.designate %23#0 (%c1_12) {test.ptr = "targetArrayB"} : (!fir.ref<!fir.array<10xf32>>, index) -> !fir.ref<f32>
%27 = fir.load %26 : !fir.ref<f32>
%c1_13 = arith.constant 1 : index
%28 = hlfir.designate %25#0 (%c1_13) {test.ptr = "targetArrayP"} : (!fir.ref<!fir.array<10xf32>>, index) -> !fir.ref<f32>
hlfir.assign %27 to %28 : f32, !fir.ref<f32>
omp.terminator
}
omp.terminator
}
return
}

14 changes: 14 additions & 0 deletions flang/test/Lower/OpenMP/Todo/reduction-inscan.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
! RUN: %not_todo_cmd bbc -emit-fir -fopenmp -o - %s 2>&1 | FileCheck %s
! RUN: %not_todo_cmd %flang_fc1 -emit-fir -fopenmp -o - %s 2>&1 | FileCheck %s

! CHECK: not yet implemented: Reduction modifiers are not supported
subroutine reduction_inscan()
integer :: i,j
i = 0

!$omp do reduction(inscan, +:i)
do j=1,10
i = i + 1
end do
!$omp end do
end subroutine reduction_inscan
12 changes: 12 additions & 0 deletions flang/test/Lower/OpenMP/Todo/reduction-task.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
! RUN: %not_todo_cmd bbc -emit-fir -fopenmp -o - %s 2>&1 | FileCheck %s
! RUN: %not_todo_cmd %flang_fc1 -emit-fir -fopenmp -o - %s 2>&1 | FileCheck %s

! CHECK: not yet implemented: Reduction modifiers are not supported
subroutine reduction_task()
integer :: i
i = 0

!$omp parallel reduction(task, +:i)
i = i + 1
!$omp end parallel
end subroutine reduction_task
15 changes: 15 additions & 0 deletions flang/test/Lower/OpenMP/Todo/target-inreduction.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
! RUN: %not_todo_cmd bbc -emit-fir -fopenmp -fopenmp-version=50 -o - %s 2>&1 | FileCheck %s
! RUN: %not_todo_cmd %flang_fc1 -emit-fir -fopenmp -fopenmp-version=50 -o - %s 2>&1 | FileCheck %s

!===============================================================================
! `mergeable` clause
!===============================================================================

! CHECK: not yet implemented: Unhandled clause IN_REDUCTION in TARGET construct
subroutine omp_target_inreduction()
integer i
i = 0
!$omp target in_reduction(+:i)
i = i + 1
!$omp end target
end subroutine omp_target_inreduction
15 changes: 15 additions & 0 deletions flang/test/Lower/OpenMP/Todo/task-inreduction.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
! RUN: %not_todo_cmd bbc -emit-fir -fopenmp -o - %s 2>&1 | FileCheck %s
! RUN: %not_todo_cmd %flang_fc1 -emit-fir -fopenmp -o - %s 2>&1 | FileCheck %s

!===============================================================================
! `mergeable` clause
!===============================================================================

! CHECK: not yet implemented: Unhandled clause IN_REDUCTION in TASK construct
subroutine omp_task_in_reduction()
integer i
i = 0
!$omp task in_reduction(+:i)
i = i + 1
!$omp end task
end subroutine omp_task_in_reduction
2 changes: 1 addition & 1 deletion flang/test/Lower/OpenMP/Todo/task_mergeable.f90
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
! `mergeable` clause
!===============================================================================

! CHECK: not yet implemented: OpenMP Block construct clause
! CHECK: not yet implemented: Unhandled clause MERGEABLE in TASK construct
subroutine omp_task_mergeable()
!$omp task mergeable
call foo()
Expand Down
10 changes: 10 additions & 0 deletions flang/test/Lower/OpenMP/Todo/taskgroup-task-reduction.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
! RUN: %not_todo_cmd bbc -emit-fir -fopenmp -o - %s -fopenmp-version=50 2>&1 | FileCheck %s
! RUN: %not_todo_cmd %flang_fc1 -emit-fir -fopenmp -o - %s -fopenmp-version=50 2>&1 | FileCheck %s

! CHECK: not yet implemented: Unhandled clause TASK_REDUCTION in TASKGROUP construct
subroutine omp_taskgroup_task_reduction
integer :: res
!$omp taskgroup task_reduction(+:res)
res = res + 1
!$omp end taskgroup
end subroutine omp_taskgroup_task_reduction
13 changes: 13 additions & 0 deletions flang/test/Lower/OpenMP/Todo/taskloop.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
! RUN: %not_todo_cmd bbc -emit-fir -fopenmp -o - %s -fopenmp-version=50 2>&1 | FileCheck %s
! RUN: %not_todo_cmd %flang_fc1 -emit-fir -fopenmp -o - %s -fopenmp-version=50 2>&1 | FileCheck %s

! CHECK: not yet implemented: Taskloop construct
subroutine omp_taskloop
integer :: res, i
!$omp taskloop
do i = 1, 10
res = res + 1
end do
!$omp end taskloop
end subroutine omp_taskloop

10 changes: 10 additions & 0 deletions flang/test/Lower/OpenMP/Todo/taskwait-depend.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
! RUN: %not_todo_cmd bbc -emit-fir -fopenmp -o - %s -fopenmp-version=50 2>&1 | FileCheck %s
! RUN: %not_todo_cmd %flang_fc1 -emit-fir -fopenmp -o - %s -fopenmp-version=50 2>&1 | FileCheck %s

! CHECK: not yet implemented: Unhandled clause DEPEND in TASKWAIT construct
subroutine omp_tw_depend
integer :: res
!$omp taskwait depend(out: res)
res = res + 1
end subroutine omp_tw_depend

8 changes: 8 additions & 0 deletions flang/test/Lower/OpenMP/Todo/taskwait-nowait.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
! RUN: %not_todo_cmd bbc -emit-fir -fopenmp -o - %s -fopenmp-version=51 2>&1 | FileCheck %s
! RUN: %not_todo_cmd %flang_fc1 -emit-fir -fopenmp -o - %s -fopenmp-version=51 2>&1 | FileCheck %s

! CHECK: not yet implemented: Unhandled clause NOWAIT in TASKWAIT construct
subroutine omp_tw_nowait
!$omp taskwait nowait
end subroutine omp_tw_nowait

2 changes: 1 addition & 1 deletion libc/src/math/generic/cos.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ LLVM_LIBC_FUNCTION(double, cos, (double x)) {
}
return ans;
};
DoubleDouble sin_k = get_idx_dd(k + 128);
DoubleDouble msin_k = get_idx_dd(k + 128);
DoubleDouble cos_k = get_idx_dd(k + 64);
#else
// Fast look up version, but needs 256-entry table.
Expand Down
2 changes: 1 addition & 1 deletion libc/src/math/generic/range_reduction_double_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ struct LargeRangeReduction {
DoubleDouble y_mid;
};

#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS
static Float128 range_reduction_small_f128(double x) {
constexpr Float128 PI_OVER_128_F128 = {
Sign::POS, -133, 0xc90f'daa2'2168'c234'c4c6'628b'80dc'1cd1_u128};
Expand All @@ -300,7 +301,6 @@ static Float128 range_reduction_small_f128(double x) {
return fputil::quick_mul(y, PI_OVER_128_F128);
}

#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS
static constexpr Float128 SIN_K_PI_OVER_128_F128[65] = {
{Sign::POS, 0, 0},
{Sign::POS, -133, 0xc90a'afbd'1b33'efc9'c539'edcb'fda0'cf2c_u128},
Expand Down
107 changes: 73 additions & 34 deletions libcxx/utils/ci/run-buildbot
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#
# ===----------------------------------------------------------------------===##

set -ex
set -e
set -o pipefail
unset LANG
unset LC_ALL
Expand Down Expand Up @@ -96,12 +96,37 @@ if [ -z "${CMAKE}" ]; then
fi
fi

function step() {
endstep
set +x
if [[ ! -z ${GITHUB_ACTIONS+x} ]]; then
echo "::group::$1"
export IN_GROUP=1
else
echo "--- $1"
fi
set -x
}

function endstep() {
set +x
if [[ ! -z ${GITHUB_ACTIONS+x} ]] && [[ ! -z ${IN_GROUP+x} ]]; then
echo "::endgroup::"
unset IN_GROUP
fi
set -x
}

function error() {
echo "::error::$1"
}

function clean() {
rm -rf "${BUILD_DIR}"
}

function generate-cmake-base() {
echo "--- Generating CMake"
step "Generating CMake"
${CMAKE} \
-S "${MONOREPO_ROOT}/runtimes" \
-B "${BUILD_DIR}" \
Expand Down Expand Up @@ -138,29 +163,32 @@ function generate-cmake-android() {
}

function check-runtimes() {
echo "+++ Running the libc++ tests"
step "Building libc++ test dependencies"
${NINJA} -vC "${BUILD_DIR}" cxx-test-depends

step "Running the libc++ tests"
${NINJA} -vC "${BUILD_DIR}" check-cxx

echo "+++ Running the libc++abi tests"
step "Running the libc++abi tests"
${NINJA} -vC "${BUILD_DIR}" check-cxxabi

echo "+++ Running the libunwind tests"
step "Running the libunwind tests"
${NINJA} -vC "${BUILD_DIR}" check-unwind
}

# TODO: The goal is to test this against all configurations. We should also move
# this to the Lit test suite instead of being a separate CMake target.
function check-abi-list() {
echo "+++ Running the libc++ ABI list test"
step "Running the libc++ ABI list test"
${NINJA} -vC "${BUILD_DIR}" check-cxx-abilist || (
echo "+++ Generating the libc++ ABI list after failed check"
error "Generating the libc++ ABI list after failed check"
${NINJA} -vC "${BUILD_DIR}" generate-cxx-abilist
false
)
}

function check-cxx-benchmarks() {
echo "--- Running the benchmarks"
step "Running the benchmarks"
${NINJA} -vC "${BUILD_DIR}" check-cxx-benchmarks
}

Expand All @@ -170,12 +198,13 @@ function test-armv7m-picolibc() {
# To make it easier to get this builder up and running, build picolibc
# from scratch. Anecdotally, the build-picolibc script takes about 16 seconds.
# This could be optimised by building picolibc into the Docker container.
step "Building picolibc from source"
${MONOREPO_ROOT}/libcxx/utils/ci/build-picolibc.sh \
--build-dir "${BUILD_DIR}" \
--install-dir "${INSTALL_DIR}" \
--target armv7m-none-eabi

echo "--- Generating CMake"
step "Generating CMake for compiler-rt"
flags="--sysroot=${INSTALL_DIR}"
${CMAKE} \
-S "${MONOREPO_ROOT}/compiler-rt" \
Expand All @@ -187,6 +216,8 @@ function test-armv7m-picolibc() {
-DCMAKE_CXX_FLAGS="${flags}" \
-DLLVM_ENABLE_PER_TARGET_RUNTIME_DIR=ON \
"${@}"

step "Generating CMake for libc++"
generate-cmake \
-DLIBCXX_TEST_CONFIG="armv7m-picolibc-libc++.cfg.in" \
-DLIBCXXABI_TEST_CONFIG="armv7m-picolibc-libc++abi.cfg.in" \
Expand All @@ -195,6 +226,7 @@ function test-armv7m-picolibc() {
-DCMAKE_CXX_FLAGS="${flags}" \
"${@}"

step "Installing compiler-rt"
${NINJA} -vC "${BUILD_DIR}/compiler-rt" install

# Prior to clang 19, armv7m-none-eabi normalised to armv7m-none-unknown-eabi.
Expand All @@ -208,9 +240,9 @@ function test-armv7m-picolibc() {
}

# Print the version of a few tools to aid diagnostics in some cases
step "Diagnose tools in use"
${CMAKE} --version
${NINJA} --version

if [ ! -z "${CXX}" ]; then ${CXX} --version; fi

case "${BUILDER}" in
Expand All @@ -220,10 +252,9 @@ check-generated-output)
clean
generate-cmake

set +x # Printing all the commands below just creates extremely confusing output

# Reject patches that forgot to re-run the generator scripts.
echo "+++ Making sure the generator scripts were run"
step "Making sure the generator scripts were run"
set +x # Printing all the commands below just creates extremely confusing output
${NINJA} -vC "${BUILD_DIR}" libcxx-generate-files
git diff | tee ${BUILD_DIR}/generated_output.patch
git ls-files -o --exclude-standard | tee ${BUILD_DIR}/generated_output.status
Expand All @@ -235,9 +266,8 @@ check-generated-output)
false
fi

# Reject patches that introduce non-ASCII characters or hard tabs.
# Depends on LC_COLLATE set at the top of this script.
set -x
# This depends on LC_COLLATE set at the top of this script.
step "Reject patches that introduce non-ASCII characters or hard tabs."
! grep -rn '[^ -~]' libcxx/include libcxx/src libcxx/test \
--exclude '*.dat' \
--exclude '*unicode*.cpp' \
Expand Down Expand Up @@ -345,7 +375,7 @@ generic-ubsan)
bootstrapping-build)
clean

echo "--- Generating CMake"
step "Generating CMake"
${CMAKE} \
-S "${MONOREPO_ROOT}/llvm" \
-B "${BUILD_DIR}" \
Expand All @@ -362,14 +392,14 @@ bootstrapping-build)
-DLLVM_ENABLE_ASSERTIONS=ON \
-DLLVM_LIT_ARGS="-sv --xunit-xml-output test-results.xml --timeout=1500 --time-tests"

echo "+++ Running the LLDB libc++ data formatter tests"
step "Running the LLDB libc++ data formatter tests"
${NINJA} -vC "${BUILD_DIR}" lldb-api-test-deps
${BUILD_DIR}/bin/llvm-lit -sv --param dotest-args='--category libc++' "${MONOREPO_ROOT}/lldb/test/API"

echo "--- Running the libc++ and libc++abi tests"
step "Running the libc++ and libc++abi tests"
${NINJA} -vC "${BUILD_DIR}" check-runtimes

echo "+++ Installing libc++ and libc++abi to a fake location"
step "Installing libc++ and libc++abi to a fake location"
${NINJA} -vC "${BUILD_DIR}" install-runtimes

ccache -s
Expand Down Expand Up @@ -502,6 +532,7 @@ generic-optimized-speed)
apple-configuration)
clean

step "Installing libc++ with the Apple system configuration"
arch="$(uname -m)"
xcrun --sdk macosx \
${MONOREPO_ROOT}/libcxx/utils/ci/apple-install-libcxx.sh \
Expand All @@ -512,6 +543,7 @@ apple-configuration)
--architectures "${arch}" \
--version "999.99"

step "Running tests against Apple-configured libc++"
# TODO: It would be better to run the tests against the fake-installed version of libc++ instead
xcrun --sdk macosx ninja -vC "${BUILD_DIR}/${arch}" check-cxx check-cxxabi check-cxx-abilist
;;
Expand All @@ -524,6 +556,7 @@ apple-system-hardened)
params+=";hardening_mode=fast"

# In the Apple system configuration, we build libc++ and libunwind separately.
step "Installing libc++ and libc++abi in Apple-system configuration"
${CMAKE} \
-S "${MONOREPO_ROOT}/runtimes" \
-B "${BUILD_DIR}/cxx" \
Expand All @@ -539,6 +572,7 @@ apple-system-hardened)
-DLIBCXX_TEST_PARAMS="${params}" \
-DLIBCXXABI_TEST_PARAMS="${params}"

step "Installing libunwind in Apple-system configuration"
${CMAKE} \
-S "${MONOREPO_ROOT}/runtimes" \
-B "${BUILD_DIR}/unwind" \
Expand All @@ -551,13 +585,13 @@ apple-system-hardened)
-DLIBUNWIND_TEST_PARAMS="${params}" \
-DCMAKE_INSTALL_NAME_DIR="/usr/lib/system"

echo "+++ Running the libc++ tests"
step "Running the libc++ tests"
${NINJA} -vC "${BUILD_DIR}/cxx" check-cxx

echo "+++ Running the libc++abi tests"
step "Running the libc++abi tests"
${NINJA} -vC "${BUILD_DIR}/cxx" check-cxxabi

echo "+++ Running the libunwind tests"
step "Running the libunwind tests"
${NINJA} -vC "${BUILD_DIR}/unwind" check-unwind
;;
apple-system)
Expand All @@ -568,6 +602,7 @@ apple-system)
params="target_triple=${arch}-apple-macosx${version}"

# In the Apple system configuration, we build libc++ and libunwind separately.
step "Installing libc++ and libc++abi in Apple-system configuration"
${CMAKE} \
-S "${MONOREPO_ROOT}/runtimes" \
-B "${BUILD_DIR}/cxx" \
Expand All @@ -583,6 +618,7 @@ apple-system)
-DLIBCXX_TEST_PARAMS="${params}" \
-DLIBCXXABI_TEST_PARAMS="${params}"

step "Installing libunwind in Apple-system configuration"
${CMAKE} \
-S "${MONOREPO_ROOT}/runtimes" \
-B "${BUILD_DIR}/unwind" \
Expand All @@ -595,13 +631,13 @@ apple-system)
-DLIBUNWIND_TEST_PARAMS="${params}" \
-DCMAKE_INSTALL_NAME_DIR="/usr/lib/system"

echo "+++ Running the libc++ tests"
step "Running the libc++ tests"
${NINJA} -vC "${BUILD_DIR}/cxx" check-cxx

echo "+++ Running the libc++abi tests"
step "Running the libc++abi tests"
${NINJA} -vC "${BUILD_DIR}/cxx" check-cxxabi

echo "+++ Running the libunwind tests"
step "Running the libunwind tests"
${NINJA} -vC "${BUILD_DIR}/unwind" check-unwind
;;
benchmarks)
Expand Down Expand Up @@ -664,13 +700,13 @@ clang-cl-dll)
# anyway), thus just disable the experimental library. Remove this
# setting when cmake and the test driver does the right thing automatically.
generate-cmake-libcxx-win -DLIBCXX_TEST_PARAMS="enable_experimental=False"
echo "+++ Running the libc++ tests"
step "Running the libc++ tests"
${NINJA} -vC "${BUILD_DIR}" check-cxx
;;
clang-cl-static)
clean
generate-cmake-libcxx-win -DLIBCXX_ENABLE_SHARED=OFF
echo "+++ Running the libc++ tests"
step "Running the libc++ tests"
${NINJA} -vC "${BUILD_DIR}" check-cxx
;;
clang-cl-no-vcruntime)
Expand All @@ -681,14 +717,14 @@ clang-cl-no-vcruntime)
# exceptions enabled.
generate-cmake-libcxx-win -DLIBCXX_TEST_PARAMS="enable_experimental=False" \
-DLIBCXX_TEST_CONFIG="llvm-libc++-shared-no-vcruntime-clangcl.cfg.in"
echo "+++ Running the libc++ tests"
step "Running the libc++ tests"
${NINJA} -vC "${BUILD_DIR}" check-cxx
;;
clang-cl-debug)
clean
generate-cmake-libcxx-win -DLIBCXX_TEST_PARAMS="enable_experimental=False" \
-DCMAKE_BUILD_TYPE=Debug
echo "+++ Running the libc++ tests"
step "Running the libc++ tests"
${NINJA} -vC "${BUILD_DIR}" check-cxx
;;
clang-cl-static-crt)
Expand All @@ -697,7 +733,7 @@ clang-cl-static-crt)
# the static CRT, as opposed to "MultiThreadedDLL" which is the default).
generate-cmake-libcxx-win -DLIBCXX_ENABLE_SHARED=OFF \
-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded
echo "+++ Running the libc++ tests"
step "Running the libc++ tests"
${NINJA} -vC "${BUILD_DIR}" check-cxx
;;
mingw-dll)
Expand Down Expand Up @@ -743,6 +779,7 @@ mingw-incomplete-sysroot)
-C "${MONOREPO_ROOT}/libcxx/cmake/caches/MinGW.cmake"
# Only test that building succeeds; there's not much extra value in running
# the tests here, as it would be equivalent to the mingw-dll config above.
step "Building the runtimes"
${NINJA} -vC "${BUILD_DIR}"
;;
aix)
Expand All @@ -760,7 +797,7 @@ android-ndk-*)
ANDROID_EMU_IMG="${BUILDER#android-ndk-}"
. "${MONOREPO_ROOT}/libcxx/utils/ci/vendor/android/emulator-functions.sh"
if ! validate_emu_img "${ANDROID_EMU_IMG}"; then
echo "error: android-ndk suffix must be a valid emulator image (${ANDROID_EMU_IMG})" >&2
error "android-ndk suffix must be a valid emulator image (${ANDROID_EMU_IMG})" >&2
exit 1
fi
ARCH=$(arch_of_emu_img ${ANDROID_EMU_IMG})
Expand Down Expand Up @@ -792,9 +829,9 @@ android-ndk-*)
# directories.
adb shell mkdir -p /data/local/tmp/adb_run
adb push "${BUILD_DIR}/lib/libc++_shared.so" /data/local/tmp/libc++/libc++_shared.so
echo "+++ Running the libc++ tests"
step "Running the libc++ tests"
${NINJA} -vC "${BUILD_DIR}" check-cxx
echo "+++ Running the libc++abi tests"
step "Running the libc++abi tests"
${NINJA} -vC "${BUILD_DIR}" check-cxxabi
;;
#################################################################
Expand All @@ -810,3 +847,5 @@ android-ndk-*)
exit 1
;;
esac

endstep # Make sure we close any still-open output group
5 changes: 0 additions & 5 deletions libcxxabi/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -131,11 +131,6 @@ if (NOT LIBCXXABI_ENABLE_SHARED AND NOT LIBCXXABI_ENABLE_STATIC)
message(FATAL_ERROR "libc++abi must be built as either a shared or static library.")
endif()

# TODO: Remove this, which shouldn't be necessary since we know we're being built
# side-by-side with libc++.
set(LIBCXXABI_LIBCXX_INCLUDES "" CACHE PATH
"Specify path to libc++ includes.")

set(LIBCXXABI_HERMETIC_STATIC_LIBRARY_DEFAULT OFF)
if (WIN32)
set(LIBCXXABI_HERMETIC_STATIC_LIBRARY_DEFAULT ON)
Expand Down
10 changes: 5 additions & 5 deletions lld/ELF/AArch64ErrataFix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -374,9 +374,9 @@ class elf::Patch843419Section final : public SyntheticSection {
public:
Patch843419Section(Ctx &, InputSection *p, uint64_t off);

void writeTo(Ctx &, uint8_t *buf) override;
void writeTo(uint8_t *buf) override;

size_t getSize(Ctx &) const override { return 8; }
size_t getSize() const override { return 8; }

uint64_t getLDSTAddr() const;

Expand All @@ -393,21 +393,21 @@ class elf::Patch843419Section final : public SyntheticSection {
};

Patch843419Section::Patch843419Section(Ctx &ctx, InputSection *p, uint64_t off)
: SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 4,
: SyntheticSection(ctx, SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 4,
".text.patch"),
patchee(p), patcheeOffset(off) {
this->parent = p->getParent();
patchSym = addSyntheticLocal(
saver().save("__CortexA53843419_" + utohexstr(getLDSTAddr())), STT_FUNC,
0, getSize(ctx), *this);
0, getSize(), *this);
addSyntheticLocal(saver().save("$x"), STT_NOTYPE, 0, 0, *this);
}

uint64_t Patch843419Section::getLDSTAddr() const {
return patchee->getVA(patcheeOffset);
}

void Patch843419Section::writeTo(Ctx &ctx, uint8_t *buf) {
void Patch843419Section::writeTo(uint8_t *buf) {
// Copy the instruction that we will be replacing with a branch in the
// patchee Section.
write32le(buf, read32le(patchee->content().begin() + patcheeOffset));
Expand Down
12 changes: 6 additions & 6 deletions lld/ELF/ARMErrataFix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,9 @@ class elf::Patch657417Section final : public SyntheticSection {
Patch657417Section(Ctx &, InputSection *p, uint64_t off, uint32_t instr,
bool isARM);

void writeTo(Ctx &, uint8_t *buf) override;
void writeTo(uint8_t *buf) override;

size_t getSize(Ctx &) const override { return 4; }
size_t getSize() const override { return 4; }

// Get the virtual address of the branch instruction at patcheeOffset.
uint64_t getBranchAddr() const;
Expand Down Expand Up @@ -136,13 +136,13 @@ static bool is32bitBranch(uint32_t instr) {

Patch657417Section::Patch657417Section(Ctx &ctx, InputSection *p, uint64_t off,
uint32_t instr, bool isARM)
: SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 4,
: SyntheticSection(ctx, SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 4,
".text.patch"),
patchee(p), patcheeOffset(off), instr(instr), isARM(isARM) {
parent = p->getParent();
patchSym = addSyntheticLocal(
saver().save("__CortexA8657417_" + utohexstr(getBranchAddr())), STT_FUNC,
isARM ? 0 : 1, getSize(ctx), *this);
isARM ? 0 : 1, getSize(), *this);
addSyntheticLocal(saver().save(isARM ? "$a" : "$t"), STT_NOTYPE, 0, 0, *this);
}

Expand Down Expand Up @@ -176,7 +176,7 @@ static uint64_t getThumbDestAddr(Ctx &ctx, uint64_t sourceAddr,
return sourceAddr + offset + 4;
}

void Patch657417Section::writeTo(Ctx &ctx, uint8_t *buf) {
void Patch657417Section::writeTo(uint8_t *buf) {
// The base instruction of the patch is always a 32-bit unconditional branch.
if (isARM)
write32le(buf, 0xea000000);
Expand Down Expand Up @@ -417,7 +417,7 @@ void ARMErr657417Patcher::insertPatches(
// isec so the branch we are patching always goes forwards.
static void implementPatch(ScanResult sr, InputSection *isec,
std::vector<Patch657417Section *> &patches) {

Ctx &ctx = isec->getCtx();
log("detected cortex-a8-657419 erratum sequence starting at " +
utohexstr(isec->getVA(sr.off)) + " in unpatched output.");
Patch657417Section *psec;
Expand Down
30 changes: 17 additions & 13 deletions lld/ELF/Arch/ARM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ class ARM final : public TargetInfo {
bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override;
void relocate(uint8_t *loc, const Relocation &rel,
uint64_t val) const override;

private:
void encodeAluGroup(uint8_t *loc, const Relocation &rel, uint64_t val,
int group, bool check) const;
};
enum class CodeState { Data = 0, Thumb = 2, Arm = 4 };
} // namespace
Expand Down Expand Up @@ -534,8 +538,8 @@ static std::pair<uint32_t, uint32_t> getRemAndLZForGroup(unsigned group,
return {rem, lz};
}

static void encodeAluGroup(uint8_t *loc, const Relocation &rel, uint64_t val,
int group, bool check) {
void ARM::encodeAluGroup(uint8_t *loc, const Relocation &rel, uint64_t val,
int group, bool check) const {
// ADD/SUB (immediate) add = bit23, sub = bit22
// immediate field carries is a 12-bit modified immediate, made up of a 4-bit
// even rotate right and an 8-bit immediate.
Expand Down Expand Up @@ -1327,10 +1331,9 @@ class elf::ArmCmseSGVeneer {
};

ArmCmseSGSection::ArmCmseSGSection(Ctx &ctx)
: SyntheticSection(llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_EXECINSTR,
: SyntheticSection(ctx, llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_EXECINSTR,
llvm::ELF::SHT_PROGBITS,
/*alignment=*/32, ".gnu.sgstubs"),
ctx(ctx) {
/*alignment=*/32, ".gnu.sgstubs") {
entsize = ACLESESYM_SIZE;
// The range of addresses used in the CMSE import library should be fixed.
for (auto &[_, sym] : ctx.symtab->cmseImportLib) {
Expand Down Expand Up @@ -1380,7 +1383,7 @@ void ArmCmseSGSection::addSGVeneer(Symbol *acleSeSym, Symbol *sym) {
sgVeneers.emplace_back(ss);
}

void ArmCmseSGSection::writeTo(Ctx &ctx, uint8_t *buf) {
void ArmCmseSGSection::writeTo(uint8_t *buf) {
for (ArmCmseSGVeneer *s : sgVeneers) {
uint8_t *p = buf + s->offset;
write16(p + 0, 0xe97f); // SG
Expand All @@ -1397,14 +1400,14 @@ void ArmCmseSGSection::addMappingSymbol() {
addSyntheticLocal("$t", STT_NOTYPE, /*off=*/0, /*size=*/0, *this);
}

size_t ArmCmseSGSection::getSize(Ctx &) const {
size_t ArmCmseSGSection::getSize() const {
if (sgVeneers.empty())
return (impLibMaxAddr ? impLibMaxAddr - getVA() : 0) + newEntries * entsize;

return entries.size() * entsize;
}

void ArmCmseSGSection::finalizeContents(Ctx &) {
void ArmCmseSGSection::finalizeContents() {
if (sgVeneers.empty())
return;

Expand Down Expand Up @@ -1442,10 +1445,11 @@ void ArmCmseSGSection::finalizeContents(Ctx &) {
// https://developer.arm.com/documentation/ecm0359818/latest
template <typename ELFT> void elf::writeARMCmseImportLib(Ctx &ctx) {
StringTableSection *shstrtab =
make<StringTableSection>(".shstrtab", /*dynamic=*/false);
make<StringTableSection>(ctx, ".shstrtab", /*dynamic=*/false);
StringTableSection *strtab =
make<StringTableSection>(".strtab", /*dynamic=*/false);
SymbolTableBaseSection *impSymTab = make<SymbolTableSection<ELFT>>(*strtab);
make<StringTableSection>(ctx, ".strtab", /*dynamic=*/false);
SymbolTableBaseSection *impSymTab =
make<SymbolTableSection<ELFT>>(ctx, *strtab);

SmallVector<std::pair<OutputSection *, SyntheticSection *>, 0> osIsPairs;
osIsPairs.emplace_back(make<OutputSection>(strtab->name, 0, 0), strtab);
Expand All @@ -1471,8 +1475,8 @@ template <typename ELFT> void elf::writeARMCmseImportLib(Ctx &ctx) {
osec->recordSection(isec);
osec->finalizeInputSections(ctx);
osec->shName = shstrtab->addString(osec->name);
osec->size = isec->getSize(ctx);
isec->finalizeContents(ctx);
osec->size = isec->getSize();
isec->finalizeContents();
osec->offset = alignToPowerOf2(off, osec->addralign);
off = osec->offset + osec->size;
}
Expand Down
6 changes: 3 additions & 3 deletions lld/ELF/Arch/LoongArch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ static bool isJirl(uint32_t insn) {
return (insn & 0xfc000000) == JIRL;
}

static void handleUleb128(uint8_t *loc, uint64_t val) {
static void handleUleb128(Ctx &ctx, uint8_t *loc, uint64_t val) {
const uint32_t maxcount = 1 + 64 / 7;
uint32_t count;
const char *error = nullptr;
Expand Down Expand Up @@ -700,7 +700,7 @@ void LoongArch::relocate(uint8_t *loc, const Relocation &rel,
write64le(loc, read64le(loc) + val);
return;
case R_LARCH_ADD_ULEB128:
handleUleb128(loc, val);
handleUleb128(ctx, loc, val);
return;
case R_LARCH_SUB6:
*loc = (*loc & 0xc0) | ((*loc - val) & 0x3f);
Expand All @@ -718,7 +718,7 @@ void LoongArch::relocate(uint8_t *loc, const Relocation &rel,
write64le(loc, read64le(loc) - val);
return;
case R_LARCH_SUB_ULEB128:
handleUleb128(loc, -val);
handleUleb128(ctx, loc, -val);
return;

case R_LARCH_MARK_LA:
Expand Down
4 changes: 2 additions & 2 deletions lld/ELF/Arch/Mips.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ int64_t MIPS<ELFT>::getImplicitAddend(const uint8_t *buf, RelType type) const {
}

static std::pair<uint32_t, uint64_t>
calculateMipsRelChain(uint8_t *loc, RelType type, uint64_t val) {
calculateMipsRelChain(Ctx &ctx, uint8_t *loc, RelType type, uint64_t val) {
// MIPS N64 ABI packs multiple relocations into the single relocation
// record. In general, all up to three relocations can have arbitrary
// types. In fact, Clang and GCC uses only a few combinations. For now,
Expand Down Expand Up @@ -572,7 +572,7 @@ void MIPS<ELFT>::relocate(uint8_t *loc, const Relocation &rel,
RelType type = rel.type;

if (ELFT::Is64Bits || ctx.arg.mipsN32Abi)
std::tie(type, val) = calculateMipsRelChain(loc, type, val);
std::tie(type, val) = calculateMipsRelChain(ctx, loc, type, val);

// Detect cross-mode jump/branch and fix instruction.
val = fixupCrossModeJump<ELFT>(loc, type, val);
Expand Down
15 changes: 8 additions & 7 deletions lld/ELF/Arch/RISCV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1044,11 +1044,12 @@ namespace {
// extension.
class RISCVAttributesSection final : public SyntheticSection {
public:
RISCVAttributesSection()
: SyntheticSection(0, SHT_RISCV_ATTRIBUTES, 1, ".riscv.attributes") {}
RISCVAttributesSection(Ctx &ctx)
: SyntheticSection(ctx, 0, SHT_RISCV_ATTRIBUTES, 1, ".riscv.attributes") {
}

size_t getSize(Ctx &) const override { return size; }
void writeTo(Ctx &, uint8_t *buf) override;
size_t getSize() const override { return size; }
void writeTo(uint8_t *buf) override;

static constexpr StringRef vendor = "riscv";
DenseMap<unsigned, unsigned> intAttr;
Expand Down Expand Up @@ -1179,7 +1180,7 @@ mergeAttributesSection(Ctx &ctx,
unsigned firstStackAlignValue = 0, xlen = 0;
bool hasArch = false;

ctx.in.riscvAttributes = std::make_unique<RISCVAttributesSection>();
ctx.in.riscvAttributes = std::make_unique<RISCVAttributesSection>(ctx);
auto &merged = static_cast<RISCVAttributesSection &>(*ctx.in.riscvAttributes);

// Collect all tags values from attributes section.
Expand Down Expand Up @@ -1277,8 +1278,8 @@ mergeAttributesSection(Ctx &ctx,
return &merged;
}

void RISCVAttributesSection::writeTo(Ctx &ctx, uint8_t *buf) {
const size_t size = getSize(ctx);
void RISCVAttributesSection::writeTo(uint8_t *buf) {
const size_t size = getSize();
uint8_t *const end = buf + size;
*buf = ELFAttrs::Format_Version;
write32(buf + 1, size - 1);
Expand Down
18 changes: 14 additions & 4 deletions lld/ELF/Arch/X86.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ class X86 : public TargetInfo {

RelExpr adjustTlsExpr(RelType type, RelExpr expr) const override;
void relocateAlloc(InputSectionBase &sec, uint8_t *buf) const override;

private:
void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
};
} // namespace

Expand Down Expand Up @@ -344,7 +350,8 @@ void X86::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
}
}

static void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) {
void X86::relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
uint64_t val) const {
if (rel.type == R_386_TLS_GD) {
// Convert (loc[-2] == 0x04)
// leal x@tlsgd(, %ebx, 1), %eax
Expand Down Expand Up @@ -379,7 +386,8 @@ static void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) {
}
}

static void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, uint64_t val) {
void X86::relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
uint64_t val) const {
if (rel.type == R_386_TLS_GD) {
// Convert (loc[-2] == 0x04)
// leal x@tlsgd(, %ebx, 1), %eax
Expand Down Expand Up @@ -413,7 +421,8 @@ static void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, uint64_t val) {

// In some conditions, relocations can be optimized to avoid using GOT.
// This function does that for Initial Exec to Local Exec case.
static void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, uint64_t val) {
void X86::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
uint64_t val) const {
// Ulrich's document section 6.2 says that @gotntpoff can
// be used with MOVL or ADDL instructions.
// @indntpoff is similar to @gotntpoff, but for use in
Expand Down Expand Up @@ -450,7 +459,8 @@ static void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, uint64_t val) {
write32le(loc, val);
}

static void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) {
void X86::relaxTlsLdToLe(uint8_t *loc, const Relocation &rel,
uint64_t val) const {
if (rel.type == R_386_TLS_LDO_32) {
write32le(loc, val);
return;
Expand Down
18 changes: 14 additions & 4 deletions lld/ELF/Arch/X86_64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ class X86_64 : public TargetInfo {
bool deleteFallThruJmpInsn(InputSection &is, InputFile *file,
InputSection *nextIS) const override;
bool relaxOnce(int pass) const override;

private:
void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel, uint64_t val) const;
};
} // namespace

Expand Down Expand Up @@ -460,7 +466,8 @@ RelType X86_64::getDynRel(RelType type) const {
return R_X86_64_NONE;
}

static void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) {
void X86_64::relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
uint64_t val) const {
if (rel.type == R_X86_64_TLSGD) {
// Convert
// .byte 0x66
Expand Down Expand Up @@ -500,7 +507,8 @@ static void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) {
}
}

static void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, uint64_t val) {
void X86_64::relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
uint64_t val) const {
if (rel.type == R_X86_64_TLSGD) {
// Convert
// .byte 0x66
Expand Down Expand Up @@ -541,7 +549,8 @@ static void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, uint64_t val) {

// In some conditions, R_X86_64_GOTTPOFF relocation can be optimized to
// R_X86_64_TPOFF32 so that it does not use GOT.
static void relaxTlsIeToLe(uint8_t *loc, const Relocation &, uint64_t val) {
void X86_64::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
uint64_t val) const {
uint8_t *inst = loc - 3;
uint8_t reg = loc[-1] >> 3;
uint8_t *regSlot = loc - 1;
Expand Down Expand Up @@ -582,7 +591,8 @@ static void relaxTlsIeToLe(uint8_t *loc, const Relocation &, uint64_t val) {
write32le(loc, val + 4);
}

static void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) {
void X86_64::relaxTlsLdToLe(uint8_t *loc, const Relocation &rel,
uint64_t val) const {
const uint8_t inst[] = {
0x66, 0x66, // .word 0x6666
0x66, // .byte 0x66
Expand Down
1 change: 1 addition & 0 deletions lld/ELF/DWARF.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ LLDDwarfObj<ELFT>::findAux(const InputSectionBase &sec, uint64_t pos,
const RelTy &rel = *it;

const ObjFile<ELFT> *file = sec.getFile<ELFT>();
Ctx &ctx = sec.getCtx();
uint32_t symIndex = rel.getSymbol(ctx.arg.isMips64EL);
const typename ELFT::Sym &sym = file->template getELFSyms<ELFT>()[symIndex];
uint32_t secIndex = file->getSectionIndex(sym);
Expand Down
2 changes: 1 addition & 1 deletion lld/ELF/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2370,7 +2370,7 @@ static void replaceCommonSymbols(Ctx &ctx) {
if (!s)
continue;

auto *bss = make<BssSection>("COMMON", s->size, s->alignment);
auto *bss = make<BssSection>(ctx, "COMMON", s->size, s->alignment);
bss->file = s->file;
ctx.inputSections.push_back(bss);
Defined(s->file, StringRef(), s->binding, s->stOther, s->type,
Expand Down
2 changes: 1 addition & 1 deletion lld/ELF/EhFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ void EhReader::skipAugP() {
uint8_t enc = readByte();
if ((enc & 0xf0) == DW_EH_PE_aligned)
failOn(d.data() - 1, "DW_EH_PE_aligned encoding is not supported");
size_t size = getAugPSize(ctx, enc);
size_t size = getAugPSize(isec->getCtx(), enc);
if (size == 0)
failOn(d.data() - 1, "unknown FDE encoding");
if (size >= d.size())
Expand Down
4 changes: 3 additions & 1 deletion lld/ELF/InputFiles.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,10 @@ void parseFiles(Ctx &, const std::vector<InputFile *> &files);

// The root class of input files.
class InputFile {
protected:
public:
Ctx &ctx;

protected:
std::unique_ptr<Symbol *[]> symbols;
uint32_t numSymbols = 0;
SmallVector<InputSectionBase *, 0> sections;
Expand Down
12 changes: 7 additions & 5 deletions lld/ELF/InputSection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ InputSectionBase::InputSectionBase(InputFile *file, uint64_t flags,
uint32_t link, uint32_t info,
uint32_t addralign, ArrayRef<uint8_t> data,
StringRef name, Kind sectionKind)
: SectionBase(sectionKind, name, flags, entsize, addralign, type, info,
link),
file(file), content_(data.data()), size(data.size()) {
: SectionBase(sectionKind, file, name, flags, entsize, addralign, type,
info, link),
content_(data.data()), size(data.size()) {
// In order to reduce memory allocation, we assume that mergeable
// sections are smaller than 4 GiB, which is not an unreasonable
// assumption as of 2017.
Expand Down Expand Up @@ -88,7 +88,7 @@ template <class ELFT>
InputSectionBase::InputSectionBase(ObjFile<ELFT> &file,
const typename ELFT::Shdr &hdr,
StringRef name, Kind sectionKind)
: InputSectionBase(&file, getFlags(ctx, hdr.sh_flags), hdr.sh_type,
: InputSectionBase(&file, getFlags(file.ctx, hdr.sh_flags), hdr.sh_type,
hdr.sh_entsize, hdr.sh_link, hdr.sh_info,
hdr.sh_addralign, getSectionContents(file, hdr), name,
sectionKind) {
Expand All @@ -101,7 +101,7 @@ InputSectionBase::InputSectionBase(ObjFile<ELFT> &file,

size_t InputSectionBase::getSize() const {
if (auto *s = dyn_cast<SyntheticSection>(this))
return s->getSize(ctx);
return s->getSize();
return size - bytesDropped;
}

Expand Down Expand Up @@ -185,6 +185,8 @@ RelsOrRelas<ELFT> InputSectionBase::relsOrRelas(bool supportsCrel) const {
return ret;
}

Ctx &SectionBase::getCtx() const { return file->ctx; }

uint64_t SectionBase::getOffset(uint64_t offset) const {
switch (kind()) {
case Output: {
Expand Down
36 changes: 20 additions & 16 deletions lld/ELF/InputSection.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ class SectionBase {

uint8_t partition = 1;
uint32_t type;

// The file which contains this section. For InputSectionBase, its dynamic
// type is usually ObjFile<ELFT>, but may be an InputFile of InternalKind
// (for a synthetic section).
InputFile *file;

StringRef name;

// The 1-indexed partition that this section is assigned to by the garbage
Expand All @@ -92,6 +98,7 @@ class SectionBase {
uint32_t link;
uint32_t info;

Ctx &getCtx() const;
OutputSection *getOutputSection();
const OutputSection *getOutputSection() const {
return const_cast<SectionBase *>(this)->getOutputSection();
Expand All @@ -108,12 +115,12 @@ class SectionBase {
void markDead() { partition = 0; }

protected:
constexpr SectionBase(Kind sectionKind, StringRef name, uint64_t flags,
uint32_t entsize, uint32_t addralign, uint32_t type,
uint32_t info, uint32_t link)
constexpr SectionBase(Kind sectionKind, InputFile *file, StringRef name,
uint64_t flags, uint32_t entsize, uint32_t addralign,
uint32_t type, uint32_t info, uint32_t link)
: sectionKind(sectionKind), bss(false), keepUnique(false), type(type),
name(name), flags(flags), addralign(addralign), entsize(entsize),
link(link), info(info) {}
file(file), name(name), flags(flags), addralign(addralign),
entsize(entsize), link(link), info(info) {}
};

struct SymbolAnchor {
Expand Down Expand Up @@ -150,11 +157,6 @@ class InputSectionBase : public SectionBase {
return s->kind() != Output && s->kind() != Class;
}

// The file which contains this section. Its dynamic type is usually
// ObjFile<ELFT>, but may be an InputFile of InternalKind (for a synthetic
// section).
InputFile *file;

// Input sections are part of an output section. Special sections
// like .eh_frame and merge sections are first combined into a
// synthetic section that is then added to an output section. In all
Expand Down Expand Up @@ -470,19 +472,21 @@ static_assert(sizeof(InputSection) <= 160, "InputSection is too big");

class SyntheticSection : public InputSection {
public:
SyntheticSection(uint64_t flags, uint32_t type, uint32_t addralign,
Ctx &ctx;
SyntheticSection(Ctx &ctx, uint64_t flags, uint32_t type, uint32_t addralign,
StringRef name)
: InputSection(ctx.internalFile, flags, type, addralign, {}, name,
InputSectionBase::Synthetic) {}
InputSectionBase::Synthetic),
ctx(ctx) {}

virtual ~SyntheticSection() = default;
virtual size_t getSize(Ctx &) const = 0;
virtual size_t getSize() const = 0;
virtual bool updateAllocSize(Ctx &) { return false; }
// If the section has the SHF_ALLOC flag and the size may be changed if
// thunks are added, update the section size.
virtual bool isNeeded(Ctx &) const { return true; }
virtual void finalizeContents(Ctx &) {}
virtual void writeTo(Ctx &, uint8_t *buf) = 0;
virtual bool isNeeded() const { return true; }
virtual void finalizeContents() {}
virtual void writeTo(uint8_t *buf) = 0;

static bool classof(const SectionBase *sec) {
return sec->kind() == InputSectionBase::Synthetic;
Expand Down
23 changes: 14 additions & 9 deletions lld/ELF/LinkerScript.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ static void sortSections(MutableArrayRef<InputSectionBase *> vec,
// --sort-section is handled as an inner SORT command.
// 3. If one SORT command is given, and if it is SORT_NONE, don't sort.
// 4. If no SORT command is given, sort according to --sort-section.
static void sortInputSections(MutableArrayRef<InputSectionBase *> vec,
static void sortInputSections(Ctx &ctx, MutableArrayRef<InputSectionBase *> vec,
SortSectionPolicy outer,
SortSectionPolicy inner) {
if (outer == SortSectionPolicy::None)
Expand Down Expand Up @@ -517,6 +517,7 @@ LinkerScript::computeInputSections(const InputSectionDescription *cmd,
for (size_t i = begin; i != end; ++i)
ret[i] = sections[indexes[i]];
sortInputSections(
ctx,
MutableArrayRef<InputSectionBase *>(ret).slice(begin, end - begin),
ctx.arg.sortSection, SortSectionPolicy::None);
};
Expand Down Expand Up @@ -584,6 +585,7 @@ LinkerScript::computeInputSections(const InputSectionDescription *cmd,
// ret[sizeBeforeCurrPat,ret.size()) are already in the input order, so we
// just sort by sortOuter and sortInner.
sortInputSections(
ctx,
MutableArrayRef<InputSectionBase *>(ret).slice(sizeBeforeCurrPat),
pat.sortOuter, pat.sortInner);
sizeAfterPrevSort = ret.size();
Expand Down Expand Up @@ -865,7 +867,8 @@ static OutputDesc *createSection(InputSectionBase *isec, StringRef outsecName) {
return osd;
}

static OutputDesc *addInputSec(StringMap<TinyPtrVector<OutputSection *>> &map,
static OutputDesc *addInputSec(Ctx &ctx,
StringMap<TinyPtrVector<OutputSection *>> &map,
InputSectionBase *isec, StringRef outsecName) {
// Sections with SHT_GROUP or SHF_GROUP attributes reach here only when the -r
// option is given. A section with SHT_GROUP defines a "section group", and
Expand Down Expand Up @@ -983,7 +986,7 @@ void LinkerScript::addOrphanSections() {
} else if (OutputSection *sec = findByName(sectionCommands, name)) {
sec->recordSection(s);
} else {
if (OutputDesc *osd = addInputSec(map, s, name))
if (OutputDesc *osd = addInputSec(ctx, map, s, name))
v.push_back(osd);
assert(isa<MergeInputSection>(s) ||
s->getOutputSection()->sectionIndex == UINT32_MAX);
Expand Down Expand Up @@ -1055,7 +1058,7 @@ void LinkerScript::diagnoseOrphanHandling() const {
}

void LinkerScript::diagnoseMissingSGSectionAddress() const {
if (!ctx.arg.cmseImplib || !ctx.in.armCmseSGSection->isNeeded(ctx))
if (!ctx.arg.cmseImplib || !ctx.in.armCmseSGSection->isNeeded())
return;

OutputSection *sec = findByName(sectionCommands, ".gnu.sgstubs");
Expand Down Expand Up @@ -1114,7 +1117,7 @@ LinkerScript::findMemoryRegion(OutputSection *sec, MemoryRegion *hint) {
return {nullptr, nullptr};
}

static OutputSection *findFirstSection(PhdrEntry *load) {
static OutputSection *findFirstSection(Ctx &ctx, PhdrEntry *load) {
for (OutputSection *sec : ctx.outputSections)
if (sec->ptLoad == load)
return sec;
Expand Down Expand Up @@ -1187,7 +1190,7 @@ bool LinkerScript::assignOffsets(OutputSection *sec) {

// Propagate state->lmaOffset to the first "non-header" section.
if (PhdrEntry *l = sec->ptLoad)
if (sec == findFirstSection(l))
if (sec == findFirstSection(ctx, l))
l->lmaOffset = state->lmaOffset;

// We can call this method multiple times during the creation of
Expand Down Expand Up @@ -1462,7 +1465,7 @@ void LinkerScript::allocateHeaders(SmallVector<PhdrEntry *, 0> &phdrs) {

ctx.out.elfHeader->ptLoad = nullptr;
ctx.out.programHeaders->ptLoad = nullptr;
firstPTLoad->firstSec = findFirstSection(firstPTLoad);
firstPTLoad->firstSec = findFirstSection(ctx, firstPTLoad);

llvm::erase_if(phdrs,
[](const PhdrEntry *e) { return e->p_type == PT_PHDR; });
Expand Down Expand Up @@ -1811,8 +1814,10 @@ void LinkerScript::addScriptReferencedSymbolsToSymTable() {
}

bool LinkerScript::shouldAddProvideSym(StringRef symName) {
// A Defined may be demoted to Undefined but the return value must stay the
// same. Use isUsedInRegularObj to ensure this. The exportDynamic condition,
// This function is called before and after garbage collection. To prevent
// undefined references from the RHS, the result of this function for a
// symbol must be the same for each call. We use isUsedInRegularObj to not
// change the return value of a demoted symbol. The exportDynamic condition,
// while not so accurate, allows PROVIDE to define a symbol referenced by a
// DSO.
Symbol *sym = elf::ctx.symtab->find(symName);
Expand Down
Loading