11 changes: 11 additions & 0 deletions clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,5 +190,16 @@ std::optional<SVal> getPointeeVal(SVal PtrSVal, ProgramStateRef State) {
return std::nullopt;
}

bool isWithinStdNamespace(const Decl *D) {
const DeclContext *DC = D->getDeclContext();
while (DC) {
if (const auto *NS = dyn_cast<NamespaceDecl>(DC);
NS && NS->isStdNamespace())
return true;
DC = DC->getParent();
}
return false;
}

} // namespace ento
} // namespace clang
2 changes: 1 addition & 1 deletion clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ const Stmt *ExplodedNode::getNextStmtForDiagnostics() const {

const Stmt *ExplodedNode::getPreviousStmtForDiagnostics() const {
for (const ExplodedNode *N = getFirstPred(); N; N = N->getFirstPred())
if (const Stmt *S = N->getStmtForDiagnostics())
if (const Stmt *S = N->getStmtForDiagnostics(); S && !isa<CompoundStmt>(S))
return S;

return nullptr;
Expand Down
9 changes: 6 additions & 3 deletions clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1072,8 +1072,6 @@ void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out,
CleanedState, SFC, SymReaper);

// Process any special transfer function for dead symbols.
// A tag to track convenience transitions, which can be removed at cleanup.
static SimpleProgramPointTag cleanupTag(TagProviderName, "Clean Node");
// Call checkers with the non-cleaned state so that they could query the
// values of the soon to be dead symbols.
ExplodedNodeSet CheckedSet;
Expand Down Expand Up @@ -1102,10 +1100,15 @@ void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out,
// generate a transition to that state.
ProgramStateRef CleanedCheckerSt =
StateMgr.getPersistentStateWithGDM(CleanedState, CheckerState);
Bldr.generateNode(DiagnosticStmt, I, CleanedCheckerSt, &cleanupTag, K);
Bldr.generateNode(DiagnosticStmt, I, CleanedCheckerSt, cleanupNodeTag(), K);
}
}

const ProgramPointTag *ExprEngine::cleanupNodeTag() {
static SimpleProgramPointTag cleanupTag(TagProviderName, "Clean Node");
return &cleanupTag;
}

void ExprEngine::ProcessStmt(const Stmt *currStmt, ExplodedNode *Pred) {
// Reclaim any unnecessary nodes in the ExplodedGraph.
G.reclaimRecentlyAllocatedNodes();
Expand Down
165 changes: 165 additions & 0 deletions clang/test/Analysis/block-in-critical-section-inheritance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,168 @@ void gh_99628() {
// expected-note@-2 {{Call to blocking function 'sleep' inside of critical section}}
m.unlock();
}

void no_false_positive_gh_104241() {
std::mutex m;
m.lock();
// If inheritance not handled properly, this unlock might not match the lock
// above because technically they act on different memory regions:
// __mutex_base and mutex.
m.unlock();
sleep(10); // no-warning
}

struct TwoMutexes {
std::mutex m1;
std::mutex m2;
};

void two_mutexes_no_false_negative(TwoMutexes &tm) {
tm.m1.lock();
// expected-note@-1 {{Entering critical section here}}
tm.m2.unlock();
sleep(10);
// expected-warning@-1 {{Call to blocking function 'sleep' inside of critical section}}
// expected-note@-2 {{Call to blocking function 'sleep' inside of critical section}}
tm.m1.unlock();
}

struct MyMutexBase1 : std::mutex {
void lock1() { lock(); }
// expected-note@-1 {{Entering critical section here}}
void unlock1() { unlock(); }
};
struct MyMutexBase2 : std::mutex {
void lock2() { lock(); }
void unlock2() { unlock(); }
};
struct MyMutex : MyMutexBase1, MyMutexBase2 {};
// MyMutex has two distinct std::mutex as base classes

void custom_mutex_tp(MyMutexBase1 &mb) {
mb.lock();
// expected-note@-1 {{Entering critical section here}}
sleep(10);
// expected-warning@-1 {{Call to blocking function 'sleep' inside of critical section}}
// expected-note@-2 {{Call to blocking function 'sleep' inside of critical section}}
mb.unlock();
}

void custom_mutex_tn(MyMutexBase1 &mb) {
mb.lock();
mb.unlock();
sleep(10);
}

void custom_mutex_cast_tp(MyMutexBase1 &mb) {
static_cast<std::mutex&>(mb).lock();
// expected-note@-1 {{Entering critical section here}}
sleep(10);
// expected-warning@-1 {{Call to blocking function 'sleep' inside of critical section}}
// expected-note@-2 {{Call to blocking function 'sleep' inside of critical section}}
static_cast<std::mutex&>(mb).unlock();
}

void custom_mutex_cast_tn(MyMutexBase1 &mb) {
static_cast<std::mutex&>(mb).lock();
static_cast<std::mutex&>(mb).unlock();
sleep(10);
}

void two_custom_mutex_bases_tp(MyMutex &m) {
m.lock1();
// expected-note@-1 {{Calling 'MyMutexBase1::lock1'}}
// expected-note@-2 {{Returning from 'MyMutexBase1::lock1'}}
m.unlock2();
sleep(10);
// expected-warning@-1 {{Call to blocking function 'sleep' inside of critical section}}
// expected-note@-2 {{Call to blocking function 'sleep' inside of critical section}}
m.unlock1();
}

void two_custom_mutex_bases_tn(MyMutex &m) {
m.lock1();
m.unlock1();
sleep(10);
}

void two_custom_mutex_bases_casts_tp(MyMutex &m) {
static_cast<MyMutexBase1&>(m).lock();
// expected-note@-1 {{Entering critical section here}}
static_cast<MyMutexBase2&>(m).unlock();
sleep(10);
// expected-warning@-1 {{Call to blocking function 'sleep' inside of critical section}}
// expected-note@-2 {{Call to blocking function 'sleep' inside of critical section}}
static_cast<MyMutexBase1&>(m).unlock();
}

void two_custom_mutex_bases_casts_tn(MyMutex &m) {
static_cast<MyMutexBase1&>(m).lock();
static_cast<MyMutexBase1&>(m).unlock();
sleep(10);
}

struct MutexVirtBase1 : virtual std::mutex {
void lock1() { lock(); }
// expected-note@-1 {{Entering critical section here}}
void unlock1() { unlock(); }
};

struct MutexVirtBase2 : virtual std::mutex {
void lock2() { lock(); }
void unlock2() { unlock(); }
};

struct CombinedVirtMutex : MutexVirtBase1, MutexVirtBase2 {};

void virt_inherited_mutexes_same_base_tn1(CombinedVirtMutex &cvt) {
cvt.lock1();
cvt.unlock1();
sleep(10);
}

void virt_inherited_mutexes_different_bases_tn(CombinedVirtMutex &cvt) {
cvt.lock1();
cvt.unlock2(); // Despite a different name, unlock2 acts on the same mutex as lock1
sleep(10);
}

void virt_inherited_mutexes_different_bases_tp(CombinedVirtMutex &cvt) {
cvt.lock1();
// expected-note@-1 {{Calling 'MutexVirtBase1::lock1'}}
// expected-note@-2 {{Returning from 'MutexVirtBase1::lock1'}}
sleep(10);
// expected-warning@-1 {{Call to blocking function 'sleep' inside of critical section}}
// expected-note@-2 {{Call to blocking function 'sleep' inside of critical section}}
cvt.unlock1();
}

namespace std {
template <class... MutexTypes> struct scoped_lock {
explicit scoped_lock(MutexTypes&... m);
~scoped_lock();
};
template <class MutexType> class scoped_lock<MutexType> {
public:
explicit scoped_lock(MutexType& m) : m(m) { m.lock(); }
~scoped_lock() { m.unlock(); }
private:
MutexType& m;
};
} // namespace std

namespace gh_104241 {
int magic_number;
std::mutex m;

void fixed() {
int current;
for (int items_processed = 0; items_processed < 100; ++items_processed) {
{
std::scoped_lock<std::mutex> guard(m);
current = magic_number;
}
sleep(current); // expected no warning
}
}
} // namespace gh_104241
42 changes: 42 additions & 0 deletions clang/test/Analysis/block-in-critical-section-nested-namespace.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@

// RUN: %clang_analyze_cc1 \
// RUN: -analyzer-checker=unix.BlockInCriticalSection \
// RUN: -std=c++11 \
// RUN: -analyzer-output text \
// RUN: -verify %s

unsigned int sleep(unsigned int seconds) {return 0;}
namespace std {
namespace __detail {
class __mutex_base {
public:
void lock();
};
} // namespace __detail

class mutex : public __detail::__mutex_base{
public:
void unlock();
bool try_lock();
};
} // namespace std

void gh_99628() {
std::mutex m;
m.lock();
// expected-note@-1 {{Entering critical section here}}
sleep(10);
// expected-warning@-1 {{Call to blocking function 'sleep' inside of critical section}}
// expected-note@-2 {{Call to blocking function 'sleep' inside of critical section}}
m.unlock();
}

void no_false_positive_gh_104241() {
std::mutex m;
m.lock();
// If inheritance not handled properly, this unlock might not match the lock
// above because technically they act on different memory regions:
// __mutex_base and mutex.
m.unlock();
sleep(10); // no-warning
}
20 changes: 10 additions & 10 deletions clang/test/Analysis/copy-elision.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,19 +158,19 @@ ClassWithoutDestructor make1(AddressVector<ClassWithoutDestructor> &v) {
return ClassWithoutDestructor(v);
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
object of type 'ClassWithoutDestructor' is still \
referred to by the stack variable 'v' upon returning to the caller}}
referred to by the caller variable 'v' upon returning to the caller}}
}
ClassWithoutDestructor make2(AddressVector<ClassWithoutDestructor> &v) {
return make1(v);
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
object of type 'ClassWithoutDestructor' is still \
referred to by the stack variable 'v' upon returning to the caller}}
referred to by the caller variable 'v' upon returning to the caller}}
}
ClassWithoutDestructor make3(AddressVector<ClassWithoutDestructor> &v) {
return make2(v);
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
object of type 'ClassWithoutDestructor' is still \
referred to by the stack variable 'v' upon returning to the caller}}
referred to by the caller variable 'v' upon returning to the caller}}
}

void testMultipleReturns() {
Expand All @@ -193,7 +193,7 @@ void testMultipleReturns() {
void consume(ClassWithoutDestructor c) {
c.push();
// expected-warning@-1 {{Address of stack memory associated with local \
variable 'c' is still referred to by the stack variable 'v' upon returning \
variable 'c' is still referred to by the caller variable 'v' upon returning \
to the caller}}
}

Expand Down Expand Up @@ -267,7 +267,7 @@ struct TestCtorInitializer {
: c(ClassWithDestructor(v)) {}
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
object of type 'ClassWithDestructor' is still referred \
to by the stack variable 'v' upon returning to the caller}}
to by the caller variable 'v' upon returning to the caller}}
};

void testCtorInitializer() {
Expand Down Expand Up @@ -303,19 +303,19 @@ ClassWithDestructor make1(AddressVector<ClassWithDestructor> &v) {
return ClassWithDestructor(v);
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
object of type 'ClassWithDestructor' is still referred \
to by the stack variable 'v' upon returning to the caller}}
to by the caller variable 'v' upon returning to the caller}}
}
ClassWithDestructor make2(AddressVector<ClassWithDestructor> &v) {
return make1(v);
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
object of type 'ClassWithDestructor' is still referred \
to by the stack variable 'v' upon returning to the caller}}
to by the caller variable 'v' upon returning to the caller}}
}
ClassWithDestructor make3(AddressVector<ClassWithDestructor> &v) {
return make2(v);
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
object of type 'ClassWithDestructor' is still referred \
to by the stack variable 'v' upon returning to the caller}}
to by the caller variable 'v' upon returning to the caller}}
}

void testMultipleReturnsWithDestructors() {
Expand Down Expand Up @@ -360,7 +360,7 @@ void testMultipleReturnsWithDestructors() {
void consume(ClassWithDestructor c) {
c.push();
// expected-warning@-1 {{Address of stack memory associated with local \
variable 'c' is still referred to by the stack variable 'v' upon returning \
variable 'c' is still referred to by the caller variable 'v' upon returning \
to the caller}}
}

Expand Down Expand Up @@ -407,7 +407,7 @@ struct Foo {
Foo make1(Foo **r) {
return Foo(r);
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
object of type 'Foo' is still referred to by the stack \
object of type 'Foo' is still referred to by the caller \
variable 'z' upon returning to the caller}}
}

Expand Down
4 changes: 2 additions & 2 deletions clang/test/Analysis/incorrect-checker-names.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ char const *p;
void f0() {
char const str[] = "This will change";
p = str;
} // expected-warning{{Address of stack memory associated with local variable 'str' is still referred to by the global variable 'p' upon returning to the caller. This will be a dangling reference [core.StackAddressEscape]}}
// expected-note@-1{{Address of stack memory associated with local variable 'str' is still referred to by the global variable 'p' upon returning to the caller. This will be a dangling reference}}
} // expected-warning@-1{{Address of stack memory associated with local variable 'str' is still referred to by the global variable 'p' upon returning to the caller. This will be a dangling reference [core.StackAddressEscape]}}
// expected-note@-2{{Address of stack memory associated with local variable 'str' is still referred to by the global variable 'p' upon returning to the caller. This will be a dangling reference}}
2 changes: 1 addition & 1 deletion clang/test/Analysis/loop-block-counts.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ void callee(void **p) {
int x;
*p = &x;
// expected-warning@-1 {{Address of stack memory associated with local \
variable 'x' is still referred to by the stack variable 'arr' upon \
variable 'x' is still referred to by the caller variable 'arr' upon \
returning to the caller}}
}

Expand Down
6 changes: 3 additions & 3 deletions clang/test/Analysis/stack-addr-ps.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,13 @@ void callTestRegister(void) {

void top_level_leaking(int **out) {
int local = 42;
*out = &local; // no-warning FIXME
*out = &local; // expected-warning{{Address of stack memory associated with local variable 'local' is still referred to by the caller variable 'out'}}
}

void callee_leaking_via_param(int **out) {
int local = 1;
*out = &local;
// expected-warning@-1{{Address of stack memory associated with local variable 'local' is still referred to by the stack variable 'ptr'}}
// expected-warning@-1{{Address of stack memory associated with local variable 'local' is still referred to by the caller variable 'ptr'}}
}

void caller_for_leaking_callee() {
Expand All @@ -115,7 +115,7 @@ void caller_for_leaking_callee() {
void callee_nested_leaking(int **out) {
int local = 1;
*out = &local;
// expected-warning@-1{{Address of stack memory associated with local variable 'local' is still referred to by the stack variable 'ptr'}}
// expected-warning@-1{{Address of stack memory associated with local variable 'local' is still referred to by the caller variable 'ptr'}}
}

void caller_mid_for_nested_leaking(int **mid) {
Expand Down
117 changes: 64 additions & 53 deletions clang/test/Analysis/stack-addr-ps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ void write_stack_address_to(char **q) {
char local;
*q = &local;
// expected-warning@-1 {{Address of stack memory associated with local \
variable 'local' is still referred to by the stack variable 'p' upon \
variable 'local' is still referred to by the caller variable 'p' upon \
returning to the caller}}
}

Expand Down Expand Up @@ -188,14 +188,14 @@ void* global_ptr;

void global_direct_pointer() {
int local = 42;
global_ptr = &local;
} // expected-warning{{local variable 'local' is still referred to by the global variable 'global_ptr'}}
global_ptr = &local; // expected-warning{{local variable 'local' is still referred to by the global variable 'global_ptr'}}
}

void static_direct_pointer_top() {
int local = 42;
static int* p = &local;
(void)p;
} // expected-warning{{local variable 'local' is still referred to by the static variable 'p'}}
(void)p; // expected-warning{{local variable 'local' is still referred to by the static variable 'p'}}
}

void static_direct_pointer_callee() {
int local = 42;
Expand All @@ -219,7 +219,7 @@ void lambda_to_context_direct_pointer() {
int *p = nullptr;
auto lambda = [&] {
int local = 42;
p = &local; // expected-warning{{local variable 'local' is still referred to by the stack variable 'p'}}
p = &local; // expected-warning{{local variable 'local' is still referred to by the caller variable 'p'}}
};
lambda();
(void)p;
Expand All @@ -245,7 +245,7 @@ void lambda_to_context_direct_pointer_lifetime_extended() {
int *p = nullptr;
auto lambda = [&] {
int&& local = 42;
p = &local; // expected-warning{{'int' lifetime extended by local variable 'local' is still referred to by the stack variable 'p'}}
p = &local; // expected-warning{{'int' lifetime extended by local variable 'local' is still referred to by the caller variable 'p'}}
};
lambda();
(void)p;
Expand All @@ -254,7 +254,7 @@ void lambda_to_context_direct_pointer_lifetime_extended() {
template<typename Callback>
void lambda_param_capture_direct_pointer_callee(Callback& callee) {
int local = 42;
callee(local); // expected-warning{{'local' is still referred to by the stack variable 'p'}}
callee(local); // expected-warning{{'local' is still referred to by the caller variable 'p'}}
}

void lambda_param_capture_direct_pointer_caller() {
Expand All @@ -279,19 +279,19 @@ void** global_pp;
void global_ptr_local_to_ptr() {
int local = 42;
int* p = &local;
global_pp = (void**)&p;
} // expected-warning{{local variable 'p' is still referred to by the global variable 'global_pp'}}
global_pp = (void**)&p; // expected-warning{{local variable 'p' is still referred to by the global variable 'global_pp'}}
}

void global_ptr_to_ptr() {
int local = 42;
*global_pp = &local; // no-warning FIXME
*global_pp = &local; // expected-warning{{local variable 'local' is still referred to by the global variable 'global_pp'}}
}

void *** global_ppp;

void global_ptr_to_ptr_to_ptr() {
int local = 42;
**global_ppp = &local; // no-warning FIXME
**global_ppp = &local; // expected-warning{{local variable 'local' is still referred to by the global variable 'global_ppp'}}
}

void** get_some_pp();
Expand All @@ -304,12 +304,12 @@ void static_ptr_to_ptr() {

void param_ptr_to_ptr_top(void** pp) {
int local = 42;
*pp = &local; // no-warning FIXME
*pp = &local; // expected-warning{{local variable 'local' is still referred to by the caller variable 'pp'}}
}

void param_ptr_to_ptr_callee(void** pp) {
int local = 42;
*pp = &local; // expected-warning{{local variable 'local' is still referred to by the stack variable 'p'}}
*pp = &local; // expected-warning{{local variable 'local' is still referred to by the caller variable 'p'}}
}

void param_ptr_to_ptr_caller() {
Expand All @@ -319,12 +319,12 @@ void param_ptr_to_ptr_caller() {

void param_ptr_to_ptr_to_ptr_top(void*** ppp) {
int local = 42;
**ppp = &local; // no-warning FIXME
**ppp = &local; // expected-warning {{local variable 'local' is still referred to by the caller variable 'ppp'}}
}

void param_ptr_to_ptr_to_ptr_callee(void*** ppp) {
int local = 42;
**ppp = &local; // expected-warning{{local variable 'local' is still referred to by the stack variable 'pp'}}
**ppp = &local; // expected-warning{{local variable 'local' is still referred to by the caller variable 'pp'}}
}

void param_ptr_to_ptr_to_ptr_caller(void** pp) {
Expand All @@ -334,15 +334,15 @@ void param_ptr_to_ptr_to_ptr_caller(void** pp) {
void lambda_to_context_ptr_to_ptr(int **pp) {
auto lambda = [&] {
int local = 42;
*pp = &local; // expected-warning{{local variable 'local' is still referred to by the stack variable 'pp'}}
*pp = &local; // expected-warning{{local variable 'local' is still referred to by the caller variable 'pp'}}
};
lambda();
(void)*pp;
}

void param_ptr_to_ptr_fptr(int **pp) {
int local = 42;
*pp = &local; // expected-warning{{local variable 'local' is still referred to by the stack variable 'p'}}
*pp = &local; // expected-warning{{local variable 'local' is still referred to by the caller variable 'p'}}
}

void param_ptr_to_ptr_fptr_caller(void (*fptr)(int**)) {
Expand All @@ -363,7 +363,7 @@ void*& global_rtp = *make_ptr_to_ptr();
void global_ref_to_ptr() {
int local = 42;
int* p = &local;
global_rtp = p; // no-warning FIXME
global_rtp = p; // expected-warning{{local variable 'local' is still referred to by the global variable 'global_rtp'}}
}

void static_ref_to_ptr() {
Expand All @@ -376,13 +376,13 @@ void static_ref_to_ptr() {
void param_ref_to_ptr_top(void*& rp) {
int local = 42;
int* p = &local;
rp = p; // no-warning FIXME
rp = p; // expected-warning{{local variable 'local' is still referred to by the caller variable 'rp'}}
}

void param_ref_to_ptr_callee(void*& rp) {
int local = 42;
int* p = &local;
rp = p; // expected-warning{{local variable 'local' is still referred to by the stack variable 'p'}}
rp = p; // expected-warning{{local variable 'local' is still referred to by the caller variable 'p'}}
}

void param_ref_to_ptr_caller() {
Expand Down Expand Up @@ -418,26 +418,26 @@ void* global_aop[2];
void global_arr_of_ptr() {
int local = 42;
int* p = &local;
global_aop[1] = p;
} // expected-warning{{local variable 'local' is still referred to by the global variable 'global_aop'}}
global_aop[1] = p; // expected-warning{{local variable 'local' is still referred to by the global variable 'global_aop'}}
}

void static_arr_of_ptr() {
int local = 42;
static void* arr[2];
arr[1] = &local;
(void)arr[1];
} // expected-warning{{local variable 'local' is still referred to by the static variable 'arr'}}
(void)arr[1]; // expected-warning{{local variable 'local' is still referred to by the static variable 'arr'}}
}

void param_arr_of_ptr_top(void* arr[2]) {
int local = 42;
int* p = &local;
arr[1] = p; // no-warning FIXME
arr[1] = p; // expected-warning{{local variable 'local' is still referred to by the caller variable 'arr'}}
}

void param_arr_of_ptr_callee(void* arr[2]) {
int local = 42;
int* p = &local;
arr[1] = p; // expected-warning{{local variable 'local' is still referred to by the stack variable 'arrStack'}}
arr[1] = p; // expected-warning{{local variable 'local' is still referred to by the caller variable 'arrStack'}}
}

void param_arr_of_ptr_caller() {
Expand Down Expand Up @@ -474,26 +474,26 @@ void* global_aop[2];
void global_arr_of_ptr(int idx) {
int local = 42;
int* p = &local;
global_aop[idx] = p;
} // expected-warning{{local variable 'local' is still referred to by the global variable 'global_aop'}}
global_aop[idx] = p; // expected-warning{{local variable 'local' is still referred to by the global variable 'global_aop'}}
}

void static_arr_of_ptr(int idx) {
int local = 42;
static void* arr[2];
arr[idx] = &local;
(void)arr[idx];
} // expected-warning{{local variable 'local' is still referred to by the static variable 'arr'}}
(void)arr[idx]; // expected-warning{{local variable 'local' is still referred to by the static variable 'arr'}}
}

void param_arr_of_ptr_top(void* arr[2], int idx) {
int local = 42;
int* p = &local;
arr[idx] = p; // no-warning FIXME
arr[idx] = p; // expected-warning{{local variable 'local' is still referred to by the caller variable 'arr'}}
}

void param_arr_of_ptr_callee(void* arr[2], int idx) {
int local = 42;
int* p = &local;
arr[idx] = p; // expected-warning{{local variable 'local' is still referred to by the stack variable 'arrStack'}}
arr[idx] = p; // expected-warning{{local variable 'local' is still referred to by the caller variable 'arrStack'}}
}

void param_arr_of_ptr_caller(int idx) {
Expand All @@ -519,7 +519,7 @@ S returned_struct_with_ptr_callee() {
int local = 42;
S s;
s.p = &local;
return s; // expected-warning{{'local' is still referred to by the stack variable 's'}}
return s; // expected-warning{{'local' is still referred to by the caller variable 's'}}
}

void returned_struct_with_ptr_caller() {
Expand All @@ -531,15 +531,15 @@ S global_s;

void global_struct_with_ptr() {
int local = 42;
global_s.p = &local;
} // expected-warning{{'local' is still referred to by the global variable 'global_s'}}
global_s.p = &local; // expected-warning{{'local' is still referred to by the global variable 'global_s'}}
}

void static_struct_with_ptr() {
int local = 42;
static S s;
s.p = &local;
(void)s.p;
} // expected-warning{{'local' is still referred to by the static variable 's'}}
(void)s.p; // expected-warning{{'local' is still referred to by the static variable 's'}}
}
} // namespace leaking_via_struct_with_ptr

namespace leaking_via_ref_to_struct_with_ptr {
Expand All @@ -551,7 +551,7 @@ S &global_s = *(new S);

void global_ref_to_struct_with_ptr() {
int local = 42;
global_s.p = &local; // no-warning FIXME
global_s.p = &local; // expected-warning{{'local' is still referred to by the global variable 'global_s'}}
}

void static_ref_to_struct_with_ptr() {
Expand All @@ -563,12 +563,12 @@ void static_ref_to_struct_with_ptr() {

void param_ref_to_struct_with_ptr_top(S &s) {
int local = 42;
s.p = &local; // no-warning FIXME
s.p = &local; // expected-warning{{'local' is still referred to by the caller variable 's'}}
}

void param_ref_to_struct_with_ptr_callee(S &s) {
int local = 42;
s.p = &local; // expected-warning{{'local' is still referred to by the stack variable 'sStack'}}
s.p = &local; // expected-warning{{'local' is still referred to by the caller variable 'sStack'}}
}

void param_ref_to_struct_with_ptr_caller() {
Expand All @@ -579,7 +579,7 @@ void param_ref_to_struct_with_ptr_caller() {
template<typename Callable>
void lambda_param_capture_callee(Callable& callee) {
int local = 42;
callee(local); // expected-warning{{'local' is still referred to by the stack variable 'p'}}
callee(local); // expected-warning{{'local' is still referred to by the caller variable 'p'}}
}

void lambda_param_capture_caller() {
Expand Down Expand Up @@ -619,7 +619,7 @@ S* global_s;

void global_ptr_to_struct_with_ptr() {
int local = 42;
global_s->p = &local; // no-warning FIXME
global_s->p = &local; // expected-warning{{'local' is still referred to by the global variable 'global_s'}}
}

void static_ptr_to_struct_with_ptr_new() {
Expand All @@ -639,12 +639,12 @@ void static_ptr_to_struct_with_ptr_generated() {

void param_ptr_to_struct_with_ptr_top(S* s) {
int local = 42;
s->p = &local; // no-warning FIXME
s->p = &local; // expected-warning{{'local' is still referred to by the caller variable 's'}}
}

void param_ptr_to_struct_with_ptr_callee(S* s) {
int local = 42;
s->p = &local; // expected-warning{{'local' is still referred to by the stack variable 's'}}
s->p = &local; // expected-warning{{'local' is still referred to by the caller variable 's'}}
}

void param_ptr_to_struct_with_ptr_caller() {
Expand Down Expand Up @@ -682,8 +682,8 @@ S global_s[2];

void global_ptr_to_struct_with_ptr() {
int local = 42;
global_s[1].p = &local;
} // expected-warning{{'local' is still referred to by the global variable 'global_s'}}
global_s[1].p = &local; // expected-warning{{'local' is still referred to by the global variable 'global_s'}}
}

void static_ptr_to_struct_with_ptr_new() {
int local = 42;
Expand All @@ -702,12 +702,12 @@ void static_ptr_to_struct_with_ptr_generated() {

void param_ptr_to_struct_with_ptr_top(S s[2]) {
int local = 42;
s[1].p = &local; // no-warning FIXME
s[1].p = &local; // expected-warning{{'local' is still referred to by the caller variable 's'}}
}

void param_ptr_to_struct_with_ptr_callee(S s[2]) {
int local = 42;
s[1].p = &local; // expected-warning{{'local' is still referred to by the stack variable 's'}}
s[1].p = &local; // expected-warning{{'local' is still referred to by the caller variable 's'}}
}

void param_ptr_to_struct_with_ptr_caller() {
Expand All @@ -727,17 +727,17 @@ NestedAndTransitive global_nat;

void global_nested_and_transitive() {
int local = 42;
*global_nat.next[2]->next[1]->p = &local; // no-warning FIXME
*global_nat.next[2]->next[1]->p = &local; // expected-warning{{'local' is still referred to by the global variable 'global_nat'}}
}

void param_nested_and_transitive_top(NestedAndTransitive* nat) {
int local = 42;
*nat->next[2]->next[1]->p = &local; // no-warning FIXME
*nat->next[2]->next[1]->p = &local; // expected-warning{{'local' is still referred to by the caller variable 'nat'}}
}

void param_nested_and_transitive_callee(NestedAndTransitive* nat) {
int local = 42;
*nat->next[2]->next[1]->p = &local; // expected-warning{{local variable 'local' is still referred to by the stack variable 'natCaller'}}
*nat->next[2]->next[1]->p = &local; // expected-warning{{'local' is still referred to by the caller variable 'natCaller'}}
}

void param_nested_and_transitive_caller(NestedAndTransitive natCaller) {
Expand Down Expand Up @@ -769,7 +769,7 @@ void leaker(int ***leakerArg) {
// is no longer relevant.
// The message must refer to 'original_arg' instead, but there is no easy way to
// connect the SymRegion stored in 'original_arg' and 'original_arg' as variable.
**leakerArg = &local; // expected-warning{{ 'local' is still referred to by the stack variable 'arg'}}
**leakerArg = &local; // expected-warning{{ 'local' is still referred to by the caller variable 'arg'}}
}

int **tweak();
Expand All @@ -780,3 +780,14 @@ void foo(int **arg) {
leaker(&original_arg);
}
} // namespace origin_region_limitation

namespace leaking_via_indirect_global_invalidated {
void** global_pp;
void opaque();
void global_ptr_to_ptr() {
int local = 42;
*global_pp = &local;
opaque();
*global_pp = nullptr;
}
} // namespace leaking_via_indirect_global_invalidated
4 changes: 2 additions & 2 deletions clang/test/Analysis/stack-capture-leak-no-arc.mm
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ dispatch_block_t test_block_inside_block_async_leak() {
// called.
void output_block(dispatch_block_t * blk) {
int x = 0;
*blk = ^{ f(x); }; // expected-warning {{Address of stack-allocated block declared on line 43 is still referred to by the stack variable 'blk' upon returning to the caller. This will be a dangling reference [core.StackAddressEscape]}}
*blk = ^{ f(x); }; // expected-warning {{Address of stack-allocated block declared on line 43 is still referred to by the caller variable 'blk' upon returning to the caller. This will be a dangling reference [core.StackAddressEscape]}}
}

// The block literal captures nothing thus is treated as a constant.
Expand All @@ -54,7 +54,7 @@ void test_block_leak() {
__block dispatch_block_t blk;
int x = 0;
dispatch_block_t p = ^{
blk = ^{ // expected-warning {{Address of stack-allocated block declared on line 57 is still referred to by the stack variable 'blk' upon returning to the caller. This will be a dangling reference [core.StackAddressEscape]}}
blk = ^{ // expected-warning {{Address of stack-allocated block declared on line 57 is still referred to by the caller variable 'blk' upon returning to the caller. This will be a dangling reference [core.StackAddressEscape]}}
f(x);
};
};
Expand Down
8 changes: 4 additions & 4 deletions clang/test/Analysis/stackaddrleak.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ char const *p;
void f0(void) {
char const str[] = "This will change";
p = str;
} // expected-warning{{Address of stack memory associated with local variable 'str' is still referred to by the global variable 'p' upon returning to the caller. This will be a dangling reference}}
} // expected-warning@-1{{Address of stack memory associated with local variable 'str' is still referred to by the global variable 'p' upon returning to the caller. This will be a dangling reference}}

void f1(void) {
char const str[] = "This will change";
Expand All @@ -17,7 +17,7 @@ void f1(void) {

void f2(void) {
p = (const char *) __builtin_alloca(12);
} // expected-warning{{Address of stack memory allocated by call to alloca() on line 19 is still referred to by the global variable 'p' upon returning to the caller. This will be a dangling reference}}
} // expected-warning@-1{{Address of stack memory allocated by call to alloca() on line 19 is still referred to by the global variable 'p' upon returning to the caller. This will be a dangling reference}}

// PR 7383 - previously the stack address checker would crash on this example
// because it would attempt to do a direct load from 'pr7383_list'.
Expand All @@ -33,7 +33,7 @@ void test_multi_return(void) {
int x;
a = &x;
b = &x;
} // expected-warning{{Address of stack memory associated with local variable 'x' is still referred to by the static variable 'a' upon returning}} expected-warning{{Address of stack memory associated with local variable 'x' is still referred to by the static variable 'b' upon returning}}
} // expected-warning@-1{{Address of stack memory associated with local variable 'x' is still referred to by the static variable 'a' upon returning}} expected-warning@-1{{Address of stack memory associated with local variable 'x' is still referred to by the static variable 'b' upon returning}}

intptr_t returnAsNonLoc(void) {
int x;
Expand All @@ -49,7 +49,7 @@ void assignAsNonLoc(void) {
extern intptr_t ip;
int x;
ip = (intptr_t)&x;
} // expected-warning{{Address of stack memory associated with local variable 'x' is still referred to by the global variable 'ip' upon returning}}
} // expected-warning@-1{{Address of stack memory associated with local variable 'x' is still referred to by the global variable 'ip' upon returning}}

void assignAsBool(void) {
extern bool b;
Expand Down
39 changes: 39 additions & 0 deletions clang/test/CodeGenCoroutines/coro-dwarf-O2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Check that we can still observe the value of the coroutine frame
// with optimizations.
//
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 \
// RUN: -emit-llvm %s -debug-info-kind=limited -dwarf-version=5 \
// RUN: -O2 -o - | FileCheck %s

#include "Inputs/coroutine.h"

template <>
struct std::coroutine_traits<void> {
struct promise_type {
void get_return_object();
std::suspend_always initial_suspend();
std::suspend_always final_suspend() noexcept;
void return_void();
void unhandled_exception();
};
};

struct ScalarAwaiter {
template <typename F> void await_suspend(F);
bool await_ready();
int await_resume();
};

extern "C" void UseScalar(int);

extern "C" void f() {
UseScalar(co_await ScalarAwaiter{});

int Val = co_await ScalarAwaiter{};

co_await ScalarAwaiter{};
}

// CHECK: define {{.*}}@f.resume({{.*}} %[[ARG:.*]])
// CHECK: #dbg_value(ptr %[[ARG]], ![[CORO_NUM:[0-9]+]], !DIExpression(DW_OP_deref)
// CHECK: ![[CORO_NUM]] = !DILocalVariable(name: "__coro_frame"
21 changes: 21 additions & 0 deletions clang/test/SemaCXX/attr-lifetimebound.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,16 @@ template <class T> T *addressof(T &arg) {
&const_cast<char &>(reinterpret_cast<const volatile char &>(arg)));
}

template<typename T>
struct basic_string_view {
basic_string_view(const T *);
};

template <class T> struct span {
template<size_t _ArrayExtent>
span(const T (&__arr)[_ArrayExtent]) noexcept;
};

} // namespace foo
} // namespace std

Expand Down Expand Up @@ -266,3 +276,14 @@ namespace move_forward_et_al_examples {
S *AddressOfOk = std::addressof(X);
} // namespace move_forward_et_al_examples

namespace ctor_cases {
std::basic_string_view<char> test1() {
char abc[10];
return abc; // expected-warning {{address of stack memory associated with local variable}}
}

std::span<int> test2() {
int abc[10];
return abc; // expected-warning {{address of stack memory associated with local variable}}
}
} // namespace ctor_cases
15 changes: 11 additions & 4 deletions libcxx/include/__math/traits.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,14 +127,21 @@ _LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI boo

// isnormal

template <class _A1, __enable_if_t<is_floating_point<_A1>::value, int> = 0>
template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
_LIBCPP_NODISCARD _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnormal(_A1 __x) _NOEXCEPT {
return __x != 0;
}

_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnormal(float __x) _NOEXCEPT {
return __builtin_isnormal(__x);
}

template <class _A1, __enable_if_t<is_integral<_A1>::value, int> = 0>
_LIBCPP_NODISCARD _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnormal(_A1 __x) _NOEXCEPT {
return __x != 0;
_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnormal(double __x) _NOEXCEPT {
return __builtin_isnormal(__x);
}

_LIBCPP_NODISCARD inline _LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI bool isnormal(long double __x) _NOEXCEPT {
return __builtin_isnormal(__x);
}

// isgreater
Expand Down
14 changes: 14 additions & 0 deletions libcxx/test/std/numerics/c.math/isnormal.pass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,23 @@ struct TestInt {
}
};

template <typename T>
struct ConvertibleTo {
operator T() const { return T(1); }
};

int main(int, char**) {
types::for_each(types::floating_point_types(), TestFloat());
types::for_each(types::integral_types(), TestInt());

// Make sure we can call `std::isnormal` with convertible types. This checks
// whether overloads for all cv-unqualified floating-point types are working
// as expected.
{
assert(std::isnormal(ConvertibleTo<float>()));
assert(std::isnormal(ConvertibleTo<double>()));
assert(std::isnormal(ConvertibleTo<long double>()));
}

return 0;
}
2 changes: 1 addition & 1 deletion lldb/include/lldb/Symbol/TypeSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,7 @@ class TypeSystem : public PluginInterface,
return IsPointerOrReferenceType(type, nullptr);
}

virtual std::unique_ptr<UserExpression> GetUserExpression(
virtual UserExpression *GetUserExpression(
llvm::StringRef expr, llvm::StringRef prefix, SourceLanguage language,
Expression::ResultType desired_type,
const EvaluateExpressionOptions &options, ValueObject *ctx_obj) {
Expand Down
2 changes: 1 addition & 1 deletion lldb/include/lldb/Target/Target.h
Original file line number Diff line number Diff line change
Expand Up @@ -1174,7 +1174,7 @@ class Target : public std::enable_shared_from_this<Target>,
// parameters have the same meaning as for the UserExpression constructor.
// Returns a new-ed object which the caller owns.

std::unique_ptr<UserExpression>
UserExpression *
GetUserExpressionForLanguage(llvm::StringRef expr, llvm::StringRef prefix,
SourceLanguage language,
Expression::ResultType desired_type,
Expand Down
4 changes: 2 additions & 2 deletions lldb/source/Breakpoint/BreakpointLocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -251,9 +251,9 @@ bool BreakpointLocation::ConditionSaysStop(ExecutionContext &exe_ctx,
if (comp_unit)
language = comp_unit->GetLanguage();

m_user_expression_sp = GetTarget().GetUserExpressionForLanguage(
m_user_expression_sp.reset(GetTarget().GetUserExpressionForLanguage(
condition_text, llvm::StringRef(), language, Expression::eResultTypeAny,
EvaluateExpressionOptions(), nullptr, error);
EvaluateExpressionOptions(), nullptr, error));
if (error.Fail()) {
LLDB_LOGF(log, "Error getting condition expression: %s.",
error.AsCString());
Expand Down
4 changes: 2 additions & 2 deletions lldb/source/Breakpoint/Watchpoint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -463,9 +463,9 @@ void Watchpoint::SetCondition(const char *condition) {
} else {
// Pass nullptr for expr_prefix (no translation-unit level definitions).
Status error;
m_condition_up = m_target.GetUserExpressionForLanguage(
m_condition_up.reset(m_target.GetUserExpressionForLanguage(
condition, {}, {}, UserExpression::eResultTypeAny,
EvaluateExpressionOptions(), nullptr, error);
EvaluateExpressionOptions(), nullptr, error));
if (error.Fail()) {
// FIXME: Log something...
m_condition_up.reset();
Expand Down
6 changes: 3 additions & 3 deletions lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9741,16 +9741,16 @@ void ScratchTypeSystemClang::Dump(llvm::raw_ostream &output) {
}
}

std::unique_ptr<UserExpression> ScratchTypeSystemClang::GetUserExpression(
UserExpression *ScratchTypeSystemClang::GetUserExpression(
llvm::StringRef expr, llvm::StringRef prefix, SourceLanguage language,
Expression::ResultType desired_type,
const EvaluateExpressionOptions &options, ValueObject *ctx_obj) {
TargetSP target_sp = m_target_wp.lock();
if (!target_sp)
return nullptr;

return std::make_unique<ClangUserExpression>(
*target_sp.get(), expr, prefix, language, desired_type, options, ctx_obj);
return new ClangUserExpression(*target_sp.get(), expr, prefix, language,
desired_type, options, ctx_obj);
}

FunctionCaller *ScratchTypeSystemClang::GetFunctionCaller(
Expand Down
10 changes: 6 additions & 4 deletions lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
Original file line number Diff line number Diff line change
Expand Up @@ -1299,10 +1299,12 @@ class ScratchTypeSystemClang : public TypeSystemClang {
/// \see lldb_private::TypeSystem::Dump
void Dump(llvm::raw_ostream &output) override;

std::unique_ptr<UserExpression> GetUserExpression(
llvm::StringRef expr, llvm::StringRef prefix, SourceLanguage language,
Expression::ResultType desired_type,
const EvaluateExpressionOptions &options, ValueObject *ctx_obj) override;
UserExpression *GetUserExpression(llvm::StringRef expr,
llvm::StringRef prefix,
SourceLanguage language,
Expression::ResultType desired_type,
const EvaluateExpressionOptions &options,
ValueObject *ctx_obj) override;

FunctionCaller *GetFunctionCaller(const CompilerType &return_type,
const Address &function_address,
Expand Down
6 changes: 3 additions & 3 deletions lldb/source/Target/Target.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2521,7 +2521,7 @@ Target::GetPersistentExpressionStateForLanguage(lldb::LanguageType language) {
return nullptr;
}

std::unique_ptr<UserExpression> Target::GetUserExpressionForLanguage(
UserExpression *Target::GetUserExpressionForLanguage(
llvm::StringRef expr, llvm::StringRef prefix, SourceLanguage language,
Expression::ResultType desired_type,
const EvaluateExpressionOptions &options, ValueObject *ctx_obj,
Expand All @@ -2544,8 +2544,8 @@ std::unique_ptr<UserExpression> Target::GetUserExpressionForLanguage(
return nullptr;
}

auto user_expr = ts->GetUserExpression(expr, prefix, language, desired_type,
options, ctx_obj);
auto *user_expr = ts->GetUserExpression(expr, prefix, language, desired_type,
options, ctx_obj);
if (!user_expr)
error = Status::FromErrorStringWithFormat(
"Could not create an expression for language %s",
Expand Down
3 changes: 3 additions & 0 deletions llvm/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,9 @@ Changes to the C API
Because of backwards compatibility, ``LLVMIsAtomicSingleThread`` and
``LLVMSetAtomicSingleThread`` continue to work with any instruction type.

* The `LLVMSetPersonalityFn` and `LLVMSetInitializer` APIs now support clearing the
personality function and initializer respectively by passing a null pointer.


Changes to the CodeGen infrastructure
-------------------------------------
Expand Down
5 changes: 3 additions & 2 deletions llvm/lib/Demangle/Demangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ std::string llvm::demangle(std::string_view MangledName) {
}

static bool isItaniumEncoding(std::string_view S) {
// Itanium encoding requires 1 or 3 leading underscores, followed by 'Z'.
return starts_with(S, "_Z") || starts_with(S, "___Z");
// Itanium demangler supports prefixes with 1-4 underscores.
const size_t Pos = S.find_first_not_of('_');
return Pos > 0 && Pos <= 4 && S[Pos] == 'Z';
}

static bool isRustEncoding(std::string_view S) { return starts_with(S, "_R"); }
Expand Down
2 changes: 1 addition & 1 deletion llvm/lib/FileCheck/FileCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ Pattern::parseVariable(StringRef &Str, const SourceMgr &SM) {
++I;

if (I == Str.size())
return ErrorDiagnostic::get(SM, Str.slice(I, StringRef::npos),
return ErrorDiagnostic::get(SM, Str.substr(I),
StringRef("empty ") +
(IsPseudo ? "pseudo " : "global ") +
"variable name");
Expand Down
7 changes: 4 additions & 3 deletions llvm/lib/IR/Core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2261,8 +2261,8 @@ LLVMValueRef LLVMGetInitializer(LLVMValueRef GlobalVar) {
}

void LLVMSetInitializer(LLVMValueRef GlobalVar, LLVMValueRef ConstantVal) {
unwrap<GlobalVariable>(GlobalVar)
->setInitializer(unwrap<Constant>(ConstantVal));
unwrap<GlobalVariable>(GlobalVar)->setInitializer(
ConstantVal ? unwrap<Constant>(ConstantVal) : nullptr);
}

LLVMBool LLVMIsThreadLocal(LLVMValueRef GlobalVar) {
Expand Down Expand Up @@ -2445,7 +2445,8 @@ LLVMValueRef LLVMGetPersonalityFn(LLVMValueRef Fn) {
}

void LLVMSetPersonalityFn(LLVMValueRef Fn, LLVMValueRef PersonalityFn) {
unwrap<Function>(Fn)->setPersonalityFn(unwrap<Constant>(PersonalityFn));
unwrap<Function>(Fn)->setPersonalityFn(
PersonalityFn ? unwrap<Constant>(PersonalityFn) : nullptr);
}

unsigned LLVMGetIntrinsicID(LLVMValueRef Fn) {
Expand Down
13 changes: 13 additions & 0 deletions llvm/lib/Target/AArch64/AArch64InstrInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4521,7 +4521,20 @@ AArch64InstrInfo::getLdStAmountOp(const MachineInstr &MI) {
switch (MI.getOpcode()) {
default:
llvm_unreachable("Unexpected opcode");
case AArch64::LDRBroX:
case AArch64::LDRBBroX:
case AArch64::LDRSBXroX:
case AArch64::LDRSBWroX:
case AArch64::LDRHroX:
case AArch64::LDRHHroX:
case AArch64::LDRSHXroX:
case AArch64::LDRSHWroX:
case AArch64::LDRWroX:
case AArch64::LDRSroX:
case AArch64::LDRSWroX:
case AArch64::LDRDroX:
case AArch64::LDRXroX:
case AArch64::LDRQroX:
return MI.getOperand(4);
}
}
Expand Down
55 changes: 52 additions & 3 deletions llvm/lib/Target/AArch64/AArch64LoadStoreOptimizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -509,12 +509,38 @@ static unsigned getPreIndexedOpcode(unsigned Opc) {
}

static unsigned getBaseAddressOpcode(unsigned Opc) {
// TODO: Add more index address loads/stores.
// TODO: Add more index address stores.
switch (Opc) {
default:
llvm_unreachable("Opcode has no base address equivalent!");
case AArch64::LDRBroX:
return AArch64::LDRBui;
case AArch64::LDRBBroX:
return AArch64::LDRBBui;
case AArch64::LDRSBXroX:
return AArch64::LDRSBXui;
case AArch64::LDRSBWroX:
return AArch64::LDRSBWui;
case AArch64::LDRHroX:
return AArch64::LDRHui;
case AArch64::LDRHHroX:
return AArch64::LDRHHui;
case AArch64::LDRSHXroX:
return AArch64::LDRSHXui;
case AArch64::LDRSHWroX:
return AArch64::LDRSHWui;
case AArch64::LDRWroX:
return AArch64::LDRWui;
case AArch64::LDRSroX:
return AArch64::LDRSui;
case AArch64::LDRSWroX:
return AArch64::LDRSWui;
case AArch64::LDRDroX:
return AArch64::LDRDui;
case AArch64::LDRXroX:
return AArch64::LDRXui;
case AArch64::LDRQroX:
return AArch64::LDRQui;
}
}

Expand Down Expand Up @@ -766,10 +792,31 @@ static bool isMergeableIndexLdSt(MachineInstr &MI, int &Scale) {
default:
return false;
// Scaled instructions.
// TODO: Add more index address loads/stores.
// TODO: Add more index address stores.
case AArch64::LDRBroX:
case AArch64::LDRBBroX:
case AArch64::LDRSBXroX:
case AArch64::LDRSBWroX:
Scale = 1;
return true;
case AArch64::LDRHroX:
case AArch64::LDRHHroX:
case AArch64::LDRSHXroX:
case AArch64::LDRSHWroX:
Scale = 2;
return true;
case AArch64::LDRWroX:
case AArch64::LDRSroX:
case AArch64::LDRSWroX:
Scale = 4;
return true;
case AArch64::LDRDroX:
case AArch64::LDRXroX:
Scale = 8;
return true;
case AArch64::LDRQroX:
Scale = 16;
return true;
}
}

Expand Down Expand Up @@ -2223,7 +2270,9 @@ bool AArch64LoadStoreOpt::isMatchingMovConstInsn(MachineInstr &MemMI,
return false;
MBBI = prev_nodbg(MBBI, B);
MachineInstr &MovzMI = *MBBI;
if (MovzMI.getOpcode() == AArch64::MOVZWi) {
// Make sure the MOVKWi and MOVZWi set the same register.
if (MovzMI.getOpcode() == AArch64::MOVZWi &&
MovzMI.getOperand(0).getReg() == MI.getOperand(0).getReg()) {
unsigned Low = MovzMI.getOperand(1).getImm();
unsigned High = MI.getOperand(2).getImm() << MI.getOperand(3).getImm();
Offset = High + Low;
Expand Down
18 changes: 5 additions & 13 deletions llvm/lib/Target/AMDGPU/R600InstrInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,9 @@ void R600InstrInfo::copyPhysReg(MachineBasicBlock &MBB,
/// \returns true if \p MBBI can be moved into a new basic.
bool R600InstrInfo::isLegalToSplitMBBAt(MachineBasicBlock &MBB,
MachineBasicBlock::iterator MBBI) const {
for (MachineInstr::const_mop_iterator I = MBBI->operands_begin(),
E = MBBI->operands_end(); I != E; ++I) {
if (I->isReg() && !I->getReg().isVirtual() && I->isUse() &&
RI.isPhysRegLiveAcrossClauses(I->getReg()))
for (const MachineOperand &MO : MBBI->all_uses())
if (!MO.getReg().isVirtual() && RI.isPhysRegLiveAcrossClauses(MO.getReg()))
return false;
}
return true;
}

Expand Down Expand Up @@ -219,15 +216,10 @@ bool R600InstrInfo::readsLDSSrcReg(const MachineInstr &MI) const {
if (!isALUInstr(MI.getOpcode())) {
return false;
}
for (MachineInstr::const_mop_iterator I = MI.operands_begin(),
E = MI.operands_end();
I != E; ++I) {
if (!I->isReg() || !I->isUse() || I->getReg().isVirtual())
continue;

if (R600::R600_LDS_SRC_REGRegClass.contains(I->getReg()))
for (const MachineOperand &MO : MI.all_uses())
if (MO.getReg().isPhysical() &&
R600::R600_LDS_SRC_REGRegClass.contains(MO.getReg()))
return true;
}
return false;
}

Expand Down
2 changes: 1 addition & 1 deletion llvm/lib/Target/LoongArch/LoongArchLASXInstrInfo.td
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
//
//===----------------------------------------------------------------------===//

def loongarch_xvpermi: SDNode<"LoongArchISD::XVPERMI", SDT_loongArchV1RUimm>;
def loongarch_xvpermi: SDNode<"LoongArchISD::XVPERMI", SDT_LoongArchV1RUimm>;

def lasxsplati8
: PatFrag<(ops node:$e0),
Expand Down
20 changes: 10 additions & 10 deletions llvm/lib/Target/LoongArch/LoongArchLSXInstrInfo.td
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ def SDT_LoongArchVreplve : SDTypeProfile<1, 2, [SDTCisInt<0>, SDTCisVec<0>,
def SDT_LoongArchVecCond : SDTypeProfile<1, 1, [SDTCisInt<0>, SDTCisVec<1>]>;

def SDT_LoongArchVShuf : SDTypeProfile<1, 3, [SDTCisVec<0>,
SDTCisInt<1>, SDTCisVec<1>,
SDTCisSameAs<0, 2>,
SDTCisSameAs<2, 3>]>;
SDTCisInt<1>, SDTCisVec<1>,
SDTCisSameAs<0, 2>,
SDTCisSameAs<2, 3>]>;
def SDT_LoongArchV2R : SDTypeProfile<1, 2, [SDTCisVec<0>,
SDTCisSameAs<0, 1>, SDTCisSameAs<1, 2>]>;
def SDT_loongArchV1RUimm: SDTypeProfile<1, 2, [SDTCisVec<0>,
SDTCisSameAs<0,1>, SDTCisVT<2, i64>]>;
SDTCisSameAs<0, 1>, SDTCisSameAs<1, 2>]>;
def SDT_LoongArchV1RUimm: SDTypeProfile<1, 2, [SDTCisVec<0>,
SDTCisSameAs<0,1>, SDTCisVT<2, i64>]>;

// Target nodes.
def loongarch_vreplve : SDNode<"LoongArchISD::VREPLVE", SDT_LoongArchVreplve>;
Expand All @@ -31,9 +31,9 @@ def loongarch_vall_nonzero : SDNode<"LoongArchISD::VALL_NONZERO",
def loongarch_vany_nonzero : SDNode<"LoongArchISD::VANY_NONZERO",
SDT_LoongArchVecCond>;
def loongarch_vall_zero : SDNode<"LoongArchISD::VALL_ZERO",
SDT_LoongArchVecCond>;
SDT_LoongArchVecCond>;
def loongarch_vany_zero : SDNode<"LoongArchISD::VANY_ZERO",
SDT_LoongArchVecCond>;
SDT_LoongArchVecCond>;

def loongarch_vpick_sext_elt : SDNode<"LoongArchISD::VPICK_SEXT_ELT",
SDTypeProfile<1, 3, [SDTCisPtrTy<2>]>>;
Expand All @@ -48,8 +48,8 @@ def loongarch_vpackod: SDNode<"LoongArchISD::VPACKOD", SDT_LoongArchV2R>;
def loongarch_vilvl: SDNode<"LoongArchISD::VILVL", SDT_LoongArchV2R>;
def loongarch_vilvh: SDNode<"LoongArchISD::VILVH", SDT_LoongArchV2R>;

def loongarch_vshuf4i: SDNode<"LoongArchISD::VSHUF4I", SDT_loongArchV1RUimm>;
def loongarch_vreplvei: SDNode<"LoongArchISD::VREPLVEI", SDT_loongArchV1RUimm>;
def loongarch_vshuf4i: SDNode<"LoongArchISD::VSHUF4I", SDT_LoongArchV1RUimm>;
def loongarch_vreplvei: SDNode<"LoongArchISD::VREPLVEI", SDT_LoongArchV1RUimm>;

def immZExt1 : ImmLeaf<i64, [{return isUInt<1>(Imm);}]>;
def immZExt2 : ImmLeaf<i64, [{return isUInt<2>(Imm);}]>;
Expand Down
31 changes: 14 additions & 17 deletions llvm/lib/Transforms/Coroutines/CoroFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1947,8 +1947,7 @@ static void insertSpills(const FrameDataInfo &FrameData, coro::Shape &Shape) {
}
// This dbg.declare is for the main function entry point. It
// will be deleted in all coro-split functions.
coro::salvageDebugInfo(ArgToAllocaMap, *DDI, Shape.OptimizeFrame,
false /*UseEntryValue*/);
coro::salvageDebugInfo(ArgToAllocaMap, *DDI, false /*UseEntryValue*/);
};
for_each(DIs, SalvageOne);
for_each(DVRs, SalvageOne);
Expand Down Expand Up @@ -2885,9 +2884,8 @@ static void collectFrameAlloca(AllocaInst *AI, coro::Shape &Shape,

static std::optional<std::pair<Value &, DIExpression &>>
salvageDebugInfoImpl(SmallDenseMap<Argument *, AllocaInst *, 4> &ArgToAllocaMap,
bool OptimizeFrame, bool UseEntryValue, Function *F,
Value *Storage, DIExpression *Expr,
bool SkipOutermostLoad) {
bool UseEntryValue, Function *F, Value *Storage,
DIExpression *Expr, bool SkipOutermostLoad) {
IRBuilder<> Builder(F->getContext());
auto InsertPt = F->getEntryBlock().getFirstInsertionPt();
while (isa<IntrinsicInst>(InsertPt))
Expand Down Expand Up @@ -2939,10 +2937,9 @@ salvageDebugInfoImpl(SmallDenseMap<Argument *, AllocaInst *, 4> &ArgToAllocaMap,

// If the coroutine frame is an Argument, store it in an alloca to improve
// its availability (e.g. registers may be clobbered).
// Avoid this if optimizations are enabled (they would remove the alloca) or
// if the value is guaranteed to be available through other means (e.g. swift
// ABI guarantees).
if (StorageAsArg && !OptimizeFrame && !IsSwiftAsyncArg) {
// Avoid this if the value is guaranteed to be available through other means
// (e.g. swift ABI guarantees).
if (StorageAsArg && !IsSwiftAsyncArg) {
auto &Cached = ArgToAllocaMap[StorageAsArg];
if (!Cached) {
Cached = Builder.CreateAlloca(Storage->getType(), 0, nullptr,
Expand All @@ -2965,17 +2962,17 @@ salvageDebugInfoImpl(SmallDenseMap<Argument *, AllocaInst *, 4> &ArgToAllocaMap,

void coro::salvageDebugInfo(
SmallDenseMap<Argument *, AllocaInst *, 4> &ArgToAllocaMap,
DbgVariableIntrinsic &DVI, bool OptimizeFrame, bool UseEntryValue) {
DbgVariableIntrinsic &DVI, bool UseEntryValue) {

Function *F = DVI.getFunction();
// Follow the pointer arithmetic all the way to the incoming
// function argument and convert into a DIExpression.
bool SkipOutermostLoad = !isa<DbgValueInst>(DVI);
Value *OriginalStorage = DVI.getVariableLocationOp(0);

auto SalvagedInfo = ::salvageDebugInfoImpl(
ArgToAllocaMap, OptimizeFrame, UseEntryValue, F, OriginalStorage,
DVI.getExpression(), SkipOutermostLoad);
auto SalvagedInfo =
::salvageDebugInfoImpl(ArgToAllocaMap, UseEntryValue, F, OriginalStorage,
DVI.getExpression(), SkipOutermostLoad);
if (!SalvagedInfo)
return;

Expand Down Expand Up @@ -3007,17 +3004,17 @@ void coro::salvageDebugInfo(

void coro::salvageDebugInfo(
SmallDenseMap<Argument *, AllocaInst *, 4> &ArgToAllocaMap,
DbgVariableRecord &DVR, bool OptimizeFrame, bool UseEntryValue) {
DbgVariableRecord &DVR, bool UseEntryValue) {

Function *F = DVR.getFunction();
// Follow the pointer arithmetic all the way to the incoming
// function argument and convert into a DIExpression.
bool SkipOutermostLoad = DVR.isDbgDeclare();
Value *OriginalStorage = DVR.getVariableLocationOp(0);

auto SalvagedInfo = ::salvageDebugInfoImpl(
ArgToAllocaMap, OptimizeFrame, UseEntryValue, F, OriginalStorage,
DVR.getExpression(), SkipOutermostLoad);
auto SalvagedInfo =
::salvageDebugInfoImpl(ArgToAllocaMap, UseEntryValue, F, OriginalStorage,
DVR.getExpression(), SkipOutermostLoad);
if (!SalvagedInfo)
return;

Expand Down
8 changes: 4 additions & 4 deletions llvm/lib/Transforms/Coroutines/CoroInternal.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@ void replaceCoroFree(CoroIdInst *CoroId, bool Elide);
/// Attempts to rewrite the location operand of debug intrinsics in terms of
/// the coroutine frame pointer, folding pointer offsets into the DIExpression
/// of the intrinsic.
/// If the frame pointer is an Argument, store it into an alloca if
/// OptimizeFrame is false.
/// If the frame pointer is an Argument, store it into an alloca to enhance the
/// debugability.
void salvageDebugInfo(
SmallDenseMap<Argument *, AllocaInst *, 4> &ArgToAllocaMap,
DbgVariableIntrinsic &DVI, bool OptimizeFrame, bool IsEntryPoint);
DbgVariableIntrinsic &DVI, bool IsEntryPoint);
void salvageDebugInfo(
SmallDenseMap<Argument *, AllocaInst *, 4> &ArgToAllocaMap,
DbgVariableRecord &DVR, bool OptimizeFrame, bool UseEntryValue);
DbgVariableRecord &DVR, bool UseEntryValue);

// Keeps data and helper functions for lowering coroutine intrinsics.
struct LowererBase {
Expand Down
12 changes: 4 additions & 8 deletions llvm/lib/Transforms/Coroutines/CoroSplit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -735,11 +735,9 @@ void CoroCloner::salvageDebugInfo() {
bool UseEntryValue =
llvm::Triple(OrigF.getParent()->getTargetTriple()).isArch64Bit();
for (DbgVariableIntrinsic *DVI : Worklist)
coro::salvageDebugInfo(ArgToAllocaMap, *DVI, Shape.OptimizeFrame,
UseEntryValue);
coro::salvageDebugInfo(ArgToAllocaMap, *DVI, UseEntryValue);
for (DbgVariableRecord *DVR : DbgVariableRecords)
coro::salvageDebugInfo(ArgToAllocaMap, *DVR, Shape.OptimizeFrame,
UseEntryValue);
coro::salvageDebugInfo(ArgToAllocaMap, *DVR, UseEntryValue);

// Remove all salvaged dbg.declare intrinsics that became
// either unreachable or stale due to the CoroSplit transformation.
Expand Down Expand Up @@ -1961,11 +1959,9 @@ splitCoroutine(Function &F, SmallVectorImpl<Function *> &Clones,
SmallDenseMap<Argument *, AllocaInst *, 4> ArgToAllocaMap;
auto [DbgInsts, DbgVariableRecords] = collectDbgVariableIntrinsics(F);
for (auto *DDI : DbgInsts)
coro::salvageDebugInfo(ArgToAllocaMap, *DDI, Shape.OptimizeFrame,
false /*UseEntryValue*/);
coro::salvageDebugInfo(ArgToAllocaMap, *DDI, false /*UseEntryValue*/);
for (DbgVariableRecord *DVR : DbgVariableRecords)
coro::salvageDebugInfo(ArgToAllocaMap, *DVR, Shape.OptimizeFrame,
false /*UseEntryValue*/);
coro::salvageDebugInfo(ArgToAllocaMap, *DVR, false /*UseEntryValue*/);
return Shape;
}

Expand Down
85 changes: 34 additions & 51 deletions llvm/test/CodeGen/AArch64/arm64-addrmode.ll
Original file line number Diff line number Diff line change
Expand Up @@ -239,9 +239,8 @@ define i32 @LdOffset_i8_zext32(ptr %a) {
define i32 @LdOffset_i8_sext32(ptr %a) {
; CHECK-LABEL: LdOffset_i8_sext32:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w8, #56952 // =0xde78
; CHECK-NEXT: movk w8, #15, lsl #16
; CHECK-NEXT: ldrsb w0, [x0, x8]
; CHECK-NEXT: add x8, x0, #253, lsl #12 // =1036288
; CHECK-NEXT: ldrsb w0, [x8, #3704]
; CHECK-NEXT: ret
%arrayidx = getelementptr inbounds i8, ptr %a, i64 1039992
%val = load i8, ptr %arrayidx, align 1
Expand All @@ -266,9 +265,8 @@ define i64 @LdOffset_i8_zext64(ptr %a) {
define i64 @LdOffset_i8_sext64(ptr %a) {
; CHECK-LABEL: LdOffset_i8_sext64:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w8, #56952 // =0xde78
; CHECK-NEXT: movk w8, #15, lsl #16
; CHECK-NEXT: ldrsb x0, [x0, x8]
; CHECK-NEXT: add x8, x0, #253, lsl #12 // =1036288
; CHECK-NEXT: ldrsb x0, [x8, #3704]
; CHECK-NEXT: ret
%arrayidx = getelementptr inbounds i8, ptr %a, i64 1039992
%val = load i8, ptr %arrayidx, align 1
Expand All @@ -280,9 +278,8 @@ define i64 @LdOffset_i8_sext64(ptr %a) {
define i16 @LdOffset_i16(ptr %a) {
; CHECK-LABEL: LdOffset_i16:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w8, #48368 // =0xbcf0
; CHECK-NEXT: movk w8, #31, lsl #16
; CHECK-NEXT: ldrh w0, [x0, x8]
; CHECK-NEXT: add x8, x0, #506, lsl #12 // =2072576
; CHECK-NEXT: ldrh w0, [x8, #7408]
; CHECK-NEXT: ret
%arrayidx = getelementptr inbounds i16, ptr %a, i64 1039992
%val = load i16, ptr %arrayidx, align 2
Expand All @@ -293,9 +290,8 @@ define i16 @LdOffset_i16(ptr %a) {
define i32 @LdOffset_i16_zext32(ptr %a) {
; CHECK-LABEL: LdOffset_i16_zext32:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w8, #48368 // =0xbcf0
; CHECK-NEXT: movk w8, #31, lsl #16
; CHECK-NEXT: ldrh w0, [x0, x8]
; CHECK-NEXT: add x8, x0, #506, lsl #12 // =2072576
; CHECK-NEXT: ldrh w0, [x8, #7408]
; CHECK-NEXT: ret
%arrayidx = getelementptr inbounds i16, ptr %a, i64 1039992
%val = load i16, ptr %arrayidx, align 2
Expand All @@ -307,9 +303,8 @@ define i32 @LdOffset_i16_zext32(ptr %a) {
define i32 @LdOffset_i16_sext32(ptr %a) {
; CHECK-LABEL: LdOffset_i16_sext32:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w8, #48368 // =0xbcf0
; CHECK-NEXT: movk w8, #31, lsl #16
; CHECK-NEXT: ldrsh w0, [x0, x8]
; CHECK-NEXT: add x8, x0, #506, lsl #12 // =2072576
; CHECK-NEXT: ldrsh w0, [x8, #7408]
; CHECK-NEXT: ret
%arrayidx = getelementptr inbounds i16, ptr %a, i64 1039992
%val = load i16, ptr %arrayidx, align 2
Expand All @@ -321,9 +316,8 @@ define i32 @LdOffset_i16_sext32(ptr %a) {
define i64 @LdOffset_i16_zext64(ptr %a) {
; CHECK-LABEL: LdOffset_i16_zext64:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w8, #48368 // =0xbcf0
; CHECK-NEXT: movk w8, #31, lsl #16
; CHECK-NEXT: ldrh w0, [x0, x8]
; CHECK-NEXT: add x8, x0, #506, lsl #12 // =2072576
; CHECK-NEXT: ldrh w0, [x8, #7408]
; CHECK-NEXT: ret
%arrayidx = getelementptr inbounds i16, ptr %a, i64 1039992
%val = load i16, ptr %arrayidx, align 2
Expand All @@ -335,9 +329,8 @@ define i64 @LdOffset_i16_zext64(ptr %a) {
define i64 @LdOffset_i16_sext64(ptr %a) {
; CHECK-LABEL: LdOffset_i16_sext64:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w8, #48368 // =0xbcf0
; CHECK-NEXT: movk w8, #31, lsl #16
; CHECK-NEXT: ldrsh x0, [x0, x8]
; CHECK-NEXT: add x8, x0, #506, lsl #12 // =2072576
; CHECK-NEXT: ldrsh x0, [x8, #7408]
; CHECK-NEXT: ret
%arrayidx = getelementptr inbounds i16, ptr %a, i64 1039992
%val = load i16, ptr %arrayidx, align 2
Expand All @@ -349,9 +342,8 @@ define i64 @LdOffset_i16_sext64(ptr %a) {
define i32 @LdOffset_i32(ptr %a) {
; CHECK-LABEL: LdOffset_i32:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w8, #31200 // =0x79e0
; CHECK-NEXT: movk w8, #63, lsl #16
; CHECK-NEXT: ldr w0, [x0, x8]
; CHECK-NEXT: add x8, x0, #1012, lsl #12 // =4145152
; CHECK-NEXT: ldr w0, [x8, #14816]
; CHECK-NEXT: ret
%arrayidx = getelementptr inbounds i32, ptr %a, i64 1039992
%val = load i32, ptr %arrayidx, align 4
Expand All @@ -362,9 +354,8 @@ define i32 @LdOffset_i32(ptr %a) {
define i64 @LdOffset_i32_zext64(ptr %a) {
; CHECK-LABEL: LdOffset_i32_zext64:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w8, #31200 // =0x79e0
; CHECK-NEXT: movk w8, #63, lsl #16
; CHECK-NEXT: ldr w0, [x0, x8]
; CHECK-NEXT: add x8, x0, #1012, lsl #12 // =4145152
; CHECK-NEXT: ldr w0, [x8, #14816]
; CHECK-NEXT: ret
%arrayidx = getelementptr inbounds i32, ptr %a, i64 1039992
%val = load i32, ptr %arrayidx, align 2
Expand All @@ -376,9 +367,8 @@ define i64 @LdOffset_i32_zext64(ptr %a) {
define i64 @LdOffset_i32_sext64(ptr %a) {
; CHECK-LABEL: LdOffset_i32_sext64:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w8, #31200 // =0x79e0
; CHECK-NEXT: movk w8, #63, lsl #16
; CHECK-NEXT: ldrsw x0, [x0, x8]
; CHECK-NEXT: add x8, x0, #1012, lsl #12 // =4145152
; CHECK-NEXT: ldrsw x0, [x8, #14816]
; CHECK-NEXT: ret
%arrayidx = getelementptr inbounds i32, ptr %a, i64 1039992
%val = load i32, ptr %arrayidx, align 2
Expand All @@ -390,9 +380,8 @@ define i64 @LdOffset_i32_sext64(ptr %a) {
define i64 @LdOffset_i64(ptr %a) {
; CHECK-LABEL: LdOffset_i64:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w8, #62400 // =0xf3c0
; CHECK-NEXT: movk w8, #126, lsl #16
; CHECK-NEXT: ldr x0, [x0, x8]
; CHECK-NEXT: add x8, x0, #2024, lsl #12 // =8290304
; CHECK-NEXT: ldr x0, [x8, #29632]
; CHECK-NEXT: ret
%arrayidx = getelementptr inbounds i64, ptr %a, i64 1039992
%val = load i64, ptr %arrayidx, align 4
Expand All @@ -403,9 +392,8 @@ define i64 @LdOffset_i64(ptr %a) {
define <2 x i32> @LdOffset_v2i32(ptr %a) {
; CHECK-LABEL: LdOffset_v2i32:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w8, #62400 // =0xf3c0
; CHECK-NEXT: movk w8, #126, lsl #16
; CHECK-NEXT: ldr d0, [x0, x8]
; CHECK-NEXT: add x8, x0, #2024, lsl #12 // =8290304
; CHECK-NEXT: ldr d0, [x8, #29632]
; CHECK-NEXT: ret
%arrayidx = getelementptr inbounds <2 x i32>, ptr %a, i64 1039992
%val = load <2 x i32>, ptr %arrayidx, align 4
Expand All @@ -416,9 +404,8 @@ define <2 x i32> @LdOffset_v2i32(ptr %a) {
define <2 x i64> @LdOffset_v2i64(ptr %a) {
; CHECK-LABEL: LdOffset_v2i64:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w8, #59264 // =0xe780
; CHECK-NEXT: movk w8, #253, lsl #16
; CHECK-NEXT: ldr q0, [x0, x8]
; CHECK-NEXT: add x8, x0, #4048, lsl #12 // =16580608
; CHECK-NEXT: ldr q0, [x8, #59264]
; CHECK-NEXT: ret
%arrayidx = getelementptr inbounds <2 x i64>, ptr %a, i64 1039992
%val = load <2 x i64>, ptr %arrayidx, align 4
Expand All @@ -429,9 +416,8 @@ define <2 x i64> @LdOffset_v2i64(ptr %a) {
define double @LdOffset_i8_f64(ptr %a) {
; CHECK-LABEL: LdOffset_i8_f64:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w8, #56952 // =0xde78
; CHECK-NEXT: movk w8, #15, lsl #16
; CHECK-NEXT: ldrsb w8, [x0, x8]
; CHECK-NEXT: add x8, x0, #253, lsl #12 // =1036288
; CHECK-NEXT: ldrsb w8, [x8, #3704]
; CHECK-NEXT: scvtf d0, w8
; CHECK-NEXT: ret
%arrayidx = getelementptr inbounds i8, ptr %a, i64 1039992
Expand All @@ -444,9 +430,8 @@ define double @LdOffset_i8_f64(ptr %a) {
define double @LdOffset_i16_f64(ptr %a) {
; CHECK-LABEL: LdOffset_i16_f64:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w8, #48368 // =0xbcf0
; CHECK-NEXT: movk w8, #31, lsl #16
; CHECK-NEXT: ldrsh w8, [x0, x8]
; CHECK-NEXT: add x8, x0, #506, lsl #12 // =2072576
; CHECK-NEXT: ldrsh w8, [x8, #7408]
; CHECK-NEXT: scvtf d0, w8
; CHECK-NEXT: ret
%arrayidx = getelementptr inbounds i16, ptr %a, i64 1039992
Expand All @@ -459,9 +444,8 @@ define double @LdOffset_i16_f64(ptr %a) {
define double @LdOffset_i32_f64(ptr %a) {
; CHECK-LABEL: LdOffset_i32_f64:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w8, #31200 // =0x79e0
; CHECK-NEXT: movk w8, #63, lsl #16
; CHECK-NEXT: ldr s0, [x0, x8]
; CHECK-NEXT: add x8, x0, #1012, lsl #12 // =4145152
; CHECK-NEXT: ldr s0, [x8, #14816]
; CHECK-NEXT: ucvtf d0, d0
; CHECK-NEXT: ret
%arrayidx = getelementptr inbounds i32, ptr %a, i64 1039992
Expand All @@ -474,9 +458,8 @@ define double @LdOffset_i32_f64(ptr %a) {
define double @LdOffset_i64_f64(ptr %a) {
; CHECK-LABEL: LdOffset_i64_f64:
; CHECK: // %bb.0:
; CHECK-NEXT: mov w8, #62400 // =0xf3c0
; CHECK-NEXT: movk w8, #126, lsl #16
; CHECK-NEXT: ldr d0, [x0, x8]
; CHECK-NEXT: add x8, x0, #2024, lsl #12 // =8290304
; CHECK-NEXT: ldr d0, [x8, #29632]
; CHECK-NEXT: scvtf d0, d0
; CHECK-NEXT: ret
%arrayidx = getelementptr inbounds i64, ptr %a, i64 1039992
Expand Down
23 changes: 23 additions & 0 deletions llvm/test/CodeGen/AArch64/large-offset-ldr-merge.mir
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,26 @@ body: |
renamable $w8 = MOVKWi $w8, 15, 16, implicit-def $x8
renamable $w0 = LDRBBroX killed renamable $x0, killed renamable $x8, 0, 0
RET undef $lr, implicit $w0
...

# Negative test: MOVZWi and MOVKWi don't set the same register
---
name: LdOffset_different_register_MOVZ_MOVK
tracksRegLiveness: true
liveins:
- { reg: '$x0', virtual-reg: '' }
body: |
bb.0.entry:
liveins: $x0

; CHECK-LABEL: name: LdOffset_different_register_MOVZ_MOVK
; CHECK: liveins: $x0
; CHECK-NEXT: {{ $}}
; CHECK-NEXT: renamable $w7 = MOVZWi 56952, 0
; CHECK-NEXT: renamable $w8 = MOVKWi $w8, 15, 16, implicit-def $x8
; CHECK-NEXT: renamable $w0 = LDRBBroX killed renamable $x0, killed renamable $x8, 0, 0
; CHECK-NEXT: RET undef $lr, implicit $w0
renamable $w7 = MOVZWi 56952, 0
renamable $w8 = MOVKWi $w8, 15, 16, implicit-def $x8
renamable $w0 = LDRBBroX killed renamable $x0, killed renamable $x8, 0, 0
RET undef $lr, implicit $w0
6 changes: 4 additions & 2 deletions llvm/test/Transforms/Coroutines/coro-debug-O2.ll
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
; RUN: opt < %s -passes='module(coro-early),cgscc(coro-split<reuse-storage>),function(sroa)' -S | FileCheck %s
; RUN: opt --try-experimental-debuginfo-iterators < %s -passes='module(coro-early),cgscc(coro-split<reuse-storage>),function(sroa)' -S | FileCheck %s

; Checks whether the dbg.declare for `__promise` remains valid under O2.
; Checks the dbg informations about promise and coroutine frames under O2.

; CHECK-LABEL: define internal fastcc void @f.resume({{.*}})
; CHECK: entry.resume:
; CHECK: #dbg_declare(ptr %begin, ![[PROMISEVAR_RESUME:[0-9]+]], !DIExpression(
; CHECK: #dbg_value(ptr poison, ![[PROMISEVAR_RESUME:[0-9]+]], !DIExpression(DW_OP_deref, DW_OP_plus_uconst, 16
; CHECK: #dbg_value(ptr %begin, ![[CORO_FRAME:[0-9]+]], !DIExpression(DW_OP_deref)
;
; CHECK: ![[CORO_FRAME]] = !DILocalVariable(name: "__coro_frame"
; CHECK: ![[PROMISEVAR_RESUME]] = !DILocalVariable(name: "__promise"
%promise_type = type { i32, i32, double }

Expand Down
131 changes: 70 additions & 61 deletions llvm/test/Transforms/LoopVectorize/RISCV/inloop-reduction.ll
Original file line number Diff line number Diff line change
Expand Up @@ -314,67 +314,76 @@ define i32 @smin(ptr %a, i64 %n, i32 %start) {
; INLOOP-NEXT: [[SMIN_LCSSA:%.*]] = phi i32 [ [[SMIN]], [[FOR_BODY]] ], [ [[RDX_MINMAX]], [[MIDDLE_BLOCK]] ]
; INLOOP-NEXT: ret i32 [[SMIN_LCSSA]]
;
; IF-EVL-LABEL: @smin(
; IF-EVL-NEXT: entry:
; IF-EVL-NEXT: [[TMP0:%.*]] = sub i64 -1, [[N:%.*]]
; IF-EVL-NEXT: [[TMP1:%.*]] = call i64 @llvm.vscale.i64()
; IF-EVL-NEXT: [[TMP2:%.*]] = mul i64 [[TMP1]], 4
; IF-EVL-NEXT: [[TMP3:%.*]] = icmp ult i64 [[TMP0]], [[TMP2]]
; IF-EVL-NEXT: br i1 [[TMP3]], label [[SCALAR_PH:%.*]], label [[VECTOR_PH:%.*]]
; IF-EVL: vector.ph:
; IF-EVL-NEXT: [[TMP4:%.*]] = call i64 @llvm.vscale.i64()
; IF-EVL-NEXT: [[TMP5:%.*]] = mul i64 [[TMP4]], 4
; IF-EVL-NEXT: [[TMP6:%.*]] = sub i64 [[TMP5]], 1
; IF-EVL-NEXT: [[N_RND_UP:%.*]] = add i64 [[N]], [[TMP6]]
; IF-EVL-NEXT: [[N_MOD_VF:%.*]] = urem i64 [[N_RND_UP]], [[TMP5]]
; IF-EVL-NEXT: [[N_VEC:%.*]] = sub i64 [[N_RND_UP]], [[N_MOD_VF]]
; IF-EVL-NEXT: [[TRIP_COUNT_MINUS_1:%.*]] = sub i64 [[N]], 1
; IF-EVL-NEXT: [[TMP7:%.*]] = call i64 @llvm.vscale.i64()
; IF-EVL-NEXT: [[TMP8:%.*]] = mul i64 [[TMP7]], 4
; IF-EVL-NEXT: [[MINMAX_IDENT_SPLATINSERT:%.*]] = insertelement <vscale x 4 x i32> poison, i32 [[START:%.*]], i64 0
; IF-EVL-NEXT: [[MINMAX_IDENT_SPLAT:%.*]] = shufflevector <vscale x 4 x i32> [[MINMAX_IDENT_SPLATINSERT]], <vscale x 4 x i32> poison, <vscale x 4 x i32> zeroinitializer
; IF-EVL-NEXT: [[BROADCAST_SPLATINSERT1:%.*]] = insertelement <vscale x 4 x i64> poison, i64 [[TRIP_COUNT_MINUS_1]], i64 0
; IF-EVL-NEXT: [[BROADCAST_SPLAT2:%.*]] = shufflevector <vscale x 4 x i64> [[BROADCAST_SPLATINSERT1]], <vscale x 4 x i64> poison, <vscale x 4 x i32> zeroinitializer
; IF-EVL-NEXT: br label [[VECTOR_BODY:%.*]]
; IF-EVL: vector.body:
; IF-EVL-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], [[VECTOR_BODY]] ]
; IF-EVL-NEXT: [[VEC_PHI:%.*]] = phi <vscale x 4 x i32> [ [[MINMAX_IDENT_SPLAT]], [[VECTOR_PH]] ], [ [[TMP16:%.*]], [[VECTOR_BODY]] ]
; IF-EVL-NEXT: [[TMP9:%.*]] = add i64 [[INDEX]], 0
; IF-EVL-NEXT: [[BROADCAST_SPLATINSERT:%.*]] = insertelement <vscale x 4 x i64> poison, i64 [[INDEX]], i64 0
; IF-EVL-NEXT: [[BROADCAST_SPLAT:%.*]] = shufflevector <vscale x 4 x i64> [[BROADCAST_SPLATINSERT]], <vscale x 4 x i64> poison, <vscale x 4 x i32> zeroinitializer
; IF-EVL-NEXT: [[TMP10:%.*]] = call <vscale x 4 x i64> @llvm.experimental.stepvector.nxv4i64()
; IF-EVL-NEXT: [[TMP11:%.*]] = add <vscale x 4 x i64> zeroinitializer, [[TMP10]]
; IF-EVL-NEXT: [[VEC_IV:%.*]] = add <vscale x 4 x i64> [[BROADCAST_SPLAT]], [[TMP11]]
; IF-EVL-NEXT: [[TMP12:%.*]] = icmp ule <vscale x 4 x i64> [[VEC_IV]], [[BROADCAST_SPLAT2]]
; IF-EVL-NEXT: [[TMP13:%.*]] = getelementptr inbounds i32, ptr [[A:%.*]], i64 [[TMP9]]
; IF-EVL-NEXT: [[TMP14:%.*]] = getelementptr inbounds i32, ptr [[TMP13]], i32 0
; IF-EVL-NEXT: [[WIDE_MASKED_LOAD:%.*]] = call <vscale x 4 x i32> @llvm.masked.load.nxv4i32.p0(ptr [[TMP14]], i32 4, <vscale x 4 x i1> [[TMP12]], <vscale x 4 x i32> poison)
; IF-EVL-NEXT: [[TMP15:%.*]] = icmp slt <vscale x 4 x i32> [[WIDE_MASKED_LOAD]], [[VEC_PHI]]
; IF-EVL-NEXT: [[TMP16]] = select <vscale x 4 x i1> [[TMP15]], <vscale x 4 x i32> [[WIDE_MASKED_LOAD]], <vscale x 4 x i32> [[VEC_PHI]]
; IF-EVL-NEXT: [[TMP17:%.*]] = select <vscale x 4 x i1> [[TMP12]], <vscale x 4 x i32> [[TMP16]], <vscale x 4 x i32> [[VEC_PHI]]
; IF-EVL-NEXT: [[INDEX_NEXT]] = add i64 [[INDEX]], [[TMP8]]
; IF-EVL-NEXT: [[TMP18:%.*]] = icmp eq i64 [[INDEX_NEXT]], [[N_VEC]]
; IF-EVL-NEXT: br i1 [[TMP18]], label [[MIDDLE_BLOCK:%.*]], label [[VECTOR_BODY]], !llvm.loop [[LOOP4:![0-9]+]]
; IF-EVL: middle.block:
; IF-EVL-NEXT: [[TMP19:%.*]] = call i32 @llvm.vector.reduce.smin.nxv4i32(<vscale x 4 x i32> [[TMP17]])
; IF-EVL-NEXT: br i1 true, label [[FOR_END:%.*]], label [[SCALAR_PH]]
; IF-EVL: scalar.ph:
; IF-EVL-NEXT: [[BC_RESUME_VAL:%.*]] = phi i64 [ [[N_VEC]], [[MIDDLE_BLOCK]] ], [ 0, [[ENTRY:%.*]] ]
; IF-EVL-NEXT: [[BC_MERGE_RDX:%.*]] = phi i32 [ [[TMP19]], [[MIDDLE_BLOCK]] ], [ [[START]], [[ENTRY]] ]
; IF-EVL-NEXT: br label [[FOR_BODY:%.*]]
; IF-EVL: for.body:
; IF-EVL-NEXT: [[IV:%.*]] = phi i64 [ [[BC_RESUME_VAL]], [[SCALAR_PH]] ], [ [[IV_NEXT:%.*]], [[FOR_BODY]] ]
; IF-EVL-NEXT: [[RDX:%.*]] = phi i32 [ [[BC_MERGE_RDX]], [[SCALAR_PH]] ], [ [[SMIN:%.*]], [[FOR_BODY]] ]
; IF-EVL-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[A]], i64 [[IV]]
; IF-EVL-NEXT: [[TMP20:%.*]] = load i32, ptr [[ARRAYIDX]], align 4
; IF-EVL-NEXT: [[CMP_I:%.*]] = icmp slt i32 [[TMP20]], [[RDX]]
; IF-EVL-NEXT: [[SMIN]] = select i1 [[CMP_I]], i32 [[TMP20]], i32 [[RDX]]
; IF-EVL-NEXT: [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
; IF-EVL-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[IV_NEXT]], [[N]]
; IF-EVL-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_END]], label [[FOR_BODY]], !llvm.loop [[LOOP5:![0-9]+]]
; IF-EVL: for.end:
; IF-EVL-NEXT: [[SMIN_LCSSA:%.*]] = phi i32 [ [[SMIN]], [[FOR_BODY]] ], [ [[TMP19]], [[MIDDLE_BLOCK]] ]
; IF-EVL-NEXT: ret i32 [[SMIN_LCSSA]]
; IF-EVL-OUTLOOP-LABEL: @smin(
; IF-EVL-OUTLOOP-NEXT: entry:
; IF-EVL-OUTLOOP-NEXT: br label [[FOR_BODY:%.*]]
; IF-EVL-OUTLOOP: for.body:
; IF-EVL-OUTLOOP-NEXT: [[IV:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[FOR_BODY]] ]
; IF-EVL-OUTLOOP-NEXT: [[RDX:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY]] ], [ [[SMIN:%.*]], [[FOR_BODY]] ]
; IF-EVL-OUTLOOP-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[A:%.*]], i64 [[IV]]
; IF-EVL-OUTLOOP-NEXT: [[TMP0:%.*]] = load i32, ptr [[ARRAYIDX]], align 4
; IF-EVL-OUTLOOP-NEXT: [[CMP_I:%.*]] = icmp slt i32 [[TMP0]], [[RDX]]
; IF-EVL-OUTLOOP-NEXT: [[SMIN]] = select i1 [[CMP_I]], i32 [[TMP0]], i32 [[RDX]]
; IF-EVL-OUTLOOP-NEXT: [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
; IF-EVL-OUTLOOP-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[IV_NEXT]], [[N:%.*]]
; IF-EVL-OUTLOOP-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
; IF-EVL-OUTLOOP: for.end:
; IF-EVL-OUTLOOP-NEXT: [[SMIN_LCSSA:%.*]] = phi i32 [ [[SMIN]], [[FOR_BODY]] ]
; IF-EVL-OUTLOOP-NEXT: ret i32 [[SMIN_LCSSA]]
;
; IF-EVL-INLOOP-LABEL: @smin(
; IF-EVL-INLOOP-NEXT: entry:
; IF-EVL-INLOOP-NEXT: [[TMP0:%.*]] = sub i64 -1, [[N:%.*]]
; IF-EVL-INLOOP-NEXT: [[TMP1:%.*]] = call i64 @llvm.vscale.i64()
; IF-EVL-INLOOP-NEXT: [[TMP2:%.*]] = mul i64 [[TMP1]], 4
; IF-EVL-INLOOP-NEXT: [[TMP3:%.*]] = icmp ult i64 [[TMP0]], [[TMP2]]
; IF-EVL-INLOOP-NEXT: br i1 [[TMP3]], label [[SCALAR_PH:%.*]], label [[VECTOR_PH:%.*]]
; IF-EVL-INLOOP: vector.ph:
; IF-EVL-INLOOP-NEXT: [[TMP4:%.*]] = call i64 @llvm.vscale.i64()
; IF-EVL-INLOOP-NEXT: [[TMP5:%.*]] = mul i64 [[TMP4]], 4
; IF-EVL-INLOOP-NEXT: [[TMP6:%.*]] = sub i64 [[TMP5]], 1
; IF-EVL-INLOOP-NEXT: [[N_RND_UP:%.*]] = add i64 [[N]], [[TMP6]]
; IF-EVL-INLOOP-NEXT: [[N_MOD_VF:%.*]] = urem i64 [[N_RND_UP]], [[TMP5]]
; IF-EVL-INLOOP-NEXT: [[N_VEC:%.*]] = sub i64 [[N_RND_UP]], [[N_MOD_VF]]
; IF-EVL-INLOOP-NEXT: [[TMP7:%.*]] = call i64 @llvm.vscale.i64()
; IF-EVL-INLOOP-NEXT: [[TMP8:%.*]] = mul i64 [[TMP7]], 4
; IF-EVL-INLOOP-NEXT: br label [[VECTOR_BODY:%.*]]
; IF-EVL-INLOOP: vector.body:
; IF-EVL-INLOOP-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], [[VECTOR_BODY]] ]
; IF-EVL-INLOOP-NEXT: [[EVL_BASED_IV:%.*]] = phi i64 [ 0, [[VECTOR_PH]] ], [ [[INDEX_EVL_NEXT:%.*]], [[VECTOR_BODY]] ]
; IF-EVL-INLOOP-NEXT: [[VEC_PHI:%.*]] = phi i32 [ [[START:%.*]], [[VECTOR_PH]] ], [ [[RDX_MINMAX:%.*]], [[VECTOR_BODY]] ]
; IF-EVL-INLOOP-NEXT: [[TMP9:%.*]] = sub i64 [[N]], [[EVL_BASED_IV]]
; IF-EVL-INLOOP-NEXT: [[TMP10:%.*]] = call i32 @llvm.experimental.get.vector.length.i64(i64 [[TMP9]], i32 4, i1 true)
; IF-EVL-INLOOP-NEXT: [[TMP11:%.*]] = add i64 [[EVL_BASED_IV]], 0
; IF-EVL-INLOOP-NEXT: [[TMP12:%.*]] = getelementptr inbounds i32, ptr [[A:%.*]], i64 [[TMP11]]
; IF-EVL-INLOOP-NEXT: [[TMP13:%.*]] = getelementptr inbounds i32, ptr [[TMP12]], i32 0
; IF-EVL-INLOOP-NEXT: [[VP_OP_LOAD:%.*]] = call <vscale x 4 x i32> @llvm.vp.load.nxv4i32.p0(ptr align 4 [[TMP13]], <vscale x 4 x i1> shufflevector (<vscale x 4 x i1> insertelement (<vscale x 4 x i1> poison, i1 true, i64 0), <vscale x 4 x i1> poison, <vscale x 4 x i32> zeroinitializer), i32 [[TMP10]])
; IF-EVL-INLOOP-NEXT: [[TMP14:%.*]] = call i32 @llvm.vp.reduce.smin.nxv4i32(i32 2147483647, <vscale x 4 x i32> [[VP_OP_LOAD]], <vscale x 4 x i1> shufflevector (<vscale x 4 x i1> insertelement (<vscale x 4 x i1> poison, i1 true, i64 0), <vscale x 4 x i1> poison, <vscale x 4 x i32> zeroinitializer), i32 [[TMP10]])
; IF-EVL-INLOOP-NEXT: [[RDX_MINMAX]] = call i32 @llvm.smin.i32(i32 [[TMP14]], i32 [[VEC_PHI]])
; IF-EVL-INLOOP-NEXT: [[TMP15:%.*]] = zext i32 [[TMP10]] to i64
; IF-EVL-INLOOP-NEXT: [[INDEX_EVL_NEXT]] = add i64 [[TMP15]], [[EVL_BASED_IV]]
; IF-EVL-INLOOP-NEXT: [[INDEX_NEXT]] = add i64 [[INDEX]], [[TMP8]]
; IF-EVL-INLOOP-NEXT: [[TMP16:%.*]] = icmp eq i64 [[INDEX_NEXT]], [[N_VEC]]
; IF-EVL-INLOOP-NEXT: br i1 [[TMP16]], label [[MIDDLE_BLOCK:%.*]], label [[VECTOR_BODY]], !llvm.loop [[LOOP4:![0-9]+]]
; IF-EVL-INLOOP: middle.block:
; IF-EVL-INLOOP-NEXT: br i1 true, label [[FOR_END:%.*]], label [[SCALAR_PH]]
; IF-EVL-INLOOP: scalar.ph:
; IF-EVL-INLOOP-NEXT: [[BC_RESUME_VAL:%.*]] = phi i64 [ [[N_VEC]], [[MIDDLE_BLOCK]] ], [ 0, [[ENTRY:%.*]] ]
; IF-EVL-INLOOP-NEXT: [[BC_MERGE_RDX:%.*]] = phi i32 [ [[RDX_MINMAX]], [[MIDDLE_BLOCK]] ], [ [[START]], [[ENTRY]] ]
; IF-EVL-INLOOP-NEXT: br label [[FOR_BODY:%.*]]
; IF-EVL-INLOOP: for.body:
; IF-EVL-INLOOP-NEXT: [[IV:%.*]] = phi i64 [ [[BC_RESUME_VAL]], [[SCALAR_PH]] ], [ [[IV_NEXT:%.*]], [[FOR_BODY]] ]
; IF-EVL-INLOOP-NEXT: [[RDX:%.*]] = phi i32 [ [[BC_MERGE_RDX]], [[SCALAR_PH]] ], [ [[SMIN:%.*]], [[FOR_BODY]] ]
; IF-EVL-INLOOP-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[A]], i64 [[IV]]
; IF-EVL-INLOOP-NEXT: [[TMP17:%.*]] = load i32, ptr [[ARRAYIDX]], align 4
; IF-EVL-INLOOP-NEXT: [[CMP_I:%.*]] = icmp slt i32 [[TMP17]], [[RDX]]
; IF-EVL-INLOOP-NEXT: [[SMIN]] = select i1 [[CMP_I]], i32 [[TMP17]], i32 [[RDX]]
; IF-EVL-INLOOP-NEXT: [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
; IF-EVL-INLOOP-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[IV_NEXT]], [[N]]
; IF-EVL-INLOOP-NEXT: br i1 [[EXITCOND_NOT]], label [[FOR_END]], label [[FOR_BODY]], !llvm.loop [[LOOP5:![0-9]+]]
; IF-EVL-INLOOP: for.end:
; IF-EVL-INLOOP-NEXT: [[SMIN_LCSSA:%.*]] = phi i32 [ [[SMIN]], [[FOR_BODY]] ], [ [[RDX_MINMAX]], [[MIDDLE_BLOCK]] ]
; IF-EVL-INLOOP-NEXT: ret i32 [[SMIN_LCSSA]]
;
entry:
br label %for.body
Expand Down
6 changes: 4 additions & 2 deletions llvm/test/tools/llvm-cxxfilt/invalid.test
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
RUN: llvm-cxxfilt -n _Z1fi __Z1fi f ___ZSt1ff_block_invoke | FileCheck %s
RUN: llvm-cxxfilt -n _Z1fi __Z1fi _____Z1fi f ___ZSt1ff_block_invoke ____ZSt1ff_block_invoke | FileCheck %s

CHECK: f(int)
CHECK-NEXT: __Z1fi
CHECK-NEXT: f(int)
CHECK-NEXT: _____Z1fi
CHECK-NEXT: f
CHECK-NEXT: invocation function for block in std::f(float)
CHECK-NEXT: invocation function for block in std::f(float)

This file was deleted.

8 changes: 0 additions & 8 deletions llvm/test/tools/llvm-cxxfilt/strip-underscore-default.test

This file was deleted.

20 changes: 13 additions & 7 deletions llvm/test/tools/llvm-cxxfilt/strip-underscore.test
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
## Show the behaviour of --[no-]strip-underscore. This test does not test
## the platform-specific default behaviour. This is tested elsewhere.
## Show the behaviour of --[no-]strip-underscore.

RUN: llvm-cxxfilt -_ __ZN2ns1fE _ZSt1f _f _._Z3f.0v | FileCheck %s -check-prefix CHECK-STRIPPED
RUN: llvm-cxxfilt --strip-underscore __ZN2ns1fE _ZSt1f _f _._Z3f.0v | FileCheck %s -check-prefix CHECK-STRIPPED
RUN: llvm-cxxfilt -n __ZN2ns1fE _ZSt1f _f _._Z3f.0v | FileCheck %s -check-prefix CHECK-UNSTRIPPED
RUN: llvm-cxxfilt --no-strip-underscore __ZN2ns1fE _ZSt1f _f _._Z3f.0v | FileCheck %s -check-prefix CHECK-UNSTRIPPED
RUN: llvm-cxxfilt -_ ___ZN2ns1fE _____Z1fi_block_invoke _ZSt1f _f _._Z3f.0v | FileCheck %s -check-prefix CHECK-STRIPPED
RUN: llvm-cxxfilt --strip-underscore __ZN2ns1fE _____Z1fi_block_invoke _ZSt1f _f _._Z3f.0v | FileCheck %s -check-prefix CHECK-STRIPPED
RUN: llvm-cxxfilt -n ___ZN2ns1fE _____Z1fi_block_invoke _ZSt1f _f _._Z3f.0v | FileCheck %s -check-prefix CHECK-UNSTRIPPED
RUN: llvm-cxxfilt --no-strip-underscore ___ZN2ns1fE _____Z1fi_block_invoke _ZSt1f _f _._Z3f.0v | FileCheck %s -check-prefix CHECK-UNSTRIPPED
RUN: llvm-cxxfilt -n -_ _ZSt1f | FileCheck %s -check-prefix OVERRIDE-STRIPPED
RUN: llvm-cxxfilt -_ -n _ZSt1f | FileCheck %s -check-prefix OVERRIDE-UNSTRIPPED

CHECK-STRIPPED: ns::f
CHECK-STRIPPED: invocation function for block in f(int)
CHECK-STRIPPED: _ZSt1f
CHECK-STRIPPED: _f
CHECK-STRIPPED: ._Z3f.0v

CHECK-UNSTRIPPED: __ZN2ns1fE
CHECK-UNSTRIPPED: ___ZN2ns1fE
CHECK-UNSTRIPPED: _____Z1fi_block_invoke
CHECK-UNSTRIPPED: std::f
CHECK-UNSTRIPPED: _f
CHECK-UNSTRIPPED: _._Z3f.0v

OVERRIDE-STRIPPED: _ZSt1f
OVERRIDE-UNSTRIPPED: std::f
9 changes: 2 additions & 7 deletions llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,13 +165,8 @@ int llvm_cxxfilt_main(int argc, char **argv, const llvm::ToolContext &) {
return 0;
}

// The default value depends on the default triple. Mach-O has symbols
// prefixed with "_", so strip by default.
if (opt::Arg *A =
Args.getLastArg(OPT_strip_underscore, OPT_no_strip_underscore))
StripUnderscore = A->getOption().matches(OPT_strip_underscore);
else
StripUnderscore = Triple(sys::getProcessTriple()).isOSBinFormatMachO();
StripUnderscore =
Args.hasFlag(OPT_strip_underscore, OPT_no_strip_underscore, false);

ParseParams = !Args.hasArg(OPT_no_params);

Expand Down
6 changes: 3 additions & 3 deletions llvm/tools/llvm-objcopy/ObjcopyOptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -556,9 +556,9 @@ static Expected<int64_t> parseChangeSectionLMA(StringRef ArgValue,
StringRef OptionName) {
StringRef StringValue;
if (ArgValue.starts_with("*+")) {
StringValue = ArgValue.slice(2, StringRef::npos);
StringValue = ArgValue.substr(2);
} else if (ArgValue.starts_with("*-")) {
StringValue = ArgValue.slice(1, StringRef::npos);
StringValue = ArgValue.substr(1);
} else if (ArgValue.contains("=")) {
return createStringError(errc::invalid_argument,
"bad format for " + OptionName +
Expand Down Expand Up @@ -608,7 +608,7 @@ parseChangeSectionAddr(StringRef ArgValue, StringRef OptionName,
SectionPattern, SectionMatchStyle, ErrorCallback)))
return std::move(E);

StringRef Value = ArgValue.slice(LastSymbolIndex + 1, StringRef::npos);
StringRef Value = ArgValue.substr(LastSymbolIndex + 1);
if (Value.empty()) {
switch (UpdateSymbol) {
case '+':
Expand Down
26 changes: 26 additions & 0 deletions llvm/unittests/IR/FunctionTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//

#include "llvm/IR/Function.h"
#include "llvm-c/Core.h"
#include "llvm/AsmParser/Parser.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/SourceMgr.h"
Expand Down Expand Up @@ -601,4 +602,29 @@ TEST(FunctionTest, UWTable) {
EXPECT_FALSE(F.hasUWTable());
EXPECT_TRUE(F.getUWTableKind() == UWTableKind::None);
}

TEST(FunctionTest, Personality) {
LLVMContext Ctx;
Module M("test", Ctx);
Type *Int8Ty = Type::getInt8Ty(Ctx);
FunctionType *FTy = FunctionType::get(Int8Ty, false);
Function *F = Function::Create(FTy, GlobalValue::ExternalLinkage, "F", &M);
Function *PersonalityFn =
Function::Create(FTy, GlobalValue::ExternalLinkage, "PersonalityFn", &M);

EXPECT_FALSE(F->hasPersonalityFn());
F->setPersonalityFn(PersonalityFn);
EXPECT_TRUE(F->hasPersonalityFn());
EXPECT_EQ(F->getPersonalityFn(), PersonalityFn);
F->setPersonalityFn(nullptr);
EXPECT_FALSE(F->hasPersonalityFn());

EXPECT_FALSE(LLVMHasPersonalityFn(wrap(F)));
LLVMSetPersonalityFn(wrap(F), wrap(PersonalityFn));
EXPECT_TRUE(LLVMHasPersonalityFn(wrap(F)));
EXPECT_EQ(LLVMGetPersonalityFn(wrap(F)), wrap(PersonalityFn));
LLVMSetPersonalityFn(wrap(F), nullptr);
EXPECT_FALSE(LLVMHasPersonalityFn(wrap(F)));
}

} // end namespace
24 changes: 24 additions & 0 deletions llvm/unittests/IR/ValueTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//

#include "llvm/IR/Value.h"
#include "llvm-c/Core.h"
#include "llvm/AsmParser/Parser.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IntrinsicInst.h"
Expand Down Expand Up @@ -391,4 +392,27 @@ TEST(ValueTest, replaceUsesOutsideBlockDbgVariableRecord) {
EXPECT_TRUE(DVR2->getVariableLocationOp(0) == cast<Value>(B));
}

TEST(GlobalTest, Initializer) {
LLVMContext Ctx;
Module M("test", Ctx);
Type *Int8Ty = Type::getInt8Ty(Ctx);
Constant *Int8Null = Constant::getNullValue(Int8Ty);

GlobalVariable *GV = new GlobalVariable(
M, Int8Ty, false, GlobalValue::ExternalLinkage, nullptr, "GV");

EXPECT_FALSE(GV->hasInitializer());
GV->setInitializer(Int8Null);
EXPECT_TRUE(GV->hasInitializer());
EXPECT_EQ(GV->getInitializer(), Int8Null);
GV->setInitializer(nullptr);
EXPECT_FALSE(GV->hasInitializer());

EXPECT_EQ(LLVMGetInitializer(wrap(GV)), nullptr);
LLVMSetInitializer(wrap(GV), wrap(Int8Null));
EXPECT_EQ(LLVMGetInitializer(wrap(GV)), wrap(Int8Null));
LLVMSetInitializer(wrap(GV), nullptr);
EXPECT_EQ(LLVMGetInitializer(wrap(GV)), nullptr);
}

} // end anonymous namespace
2 changes: 1 addition & 1 deletion llvm/unittests/Support/VirtualFileSystemTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1583,7 +1583,7 @@ class VFSFromYAMLTest : public ::testing::Test {
IntrusiveRefCntPtr<vfs::FileSystem> ExternalFS = new DummyFileSystem(),
StringRef YAMLFilePath = "") {
std::string VersionPlusContent("{\n 'version':0,\n");
VersionPlusContent += Content.slice(Content.find('{') + 1, StringRef::npos);
VersionPlusContent += Content.substr(Content.find('{') + 1);
return getFromYAMLRawString(VersionPlusContent, ExternalFS, YAMLFilePath);
}

Expand Down