Skip to content

Commit

Permalink
[clang] fixes named return of variables with dependent alignment
Browse files Browse the repository at this point in the history
Named return of a variable with aligned attribute would
trip an assert in case alignment was dependent.

Signed-off-by: Matheus Izvekov <mizvekov@gmail.com>

Reviewed By: rsmith

Differential Revision: https://reviews.llvm.org/D105380
  • Loading branch information
mizvekov committed Jul 7, 2021
1 parent 75eb43a commit f2d5fce
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 17 deletions.
3 changes: 3 additions & 0 deletions clang/include/clang/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1494,6 +1494,9 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> {
NonParmVarDeclBits.EscapingByref = true;
}

/// Determines if this variable's alignment is dependent.
bool hasDependentAlignment() const;

/// Retrieve the variable declaration from which this variable could
/// be instantiated, if it is an instantiation (rather than a non-template).
VarDecl *getTemplateInstantiationPattern() const;
Expand Down
8 changes: 8 additions & 0 deletions clang/lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2534,6 +2534,14 @@ bool VarDecl::isNonEscapingByref() const {
return hasAttr<BlocksAttr>() && !NonParmVarDeclBits.EscapingByref;
}

bool VarDecl::hasDependentAlignment() const {
QualType T = getType();
return T->isDependentType() || T->isUndeducedAutoType() ||
llvm::any_of(specific_attrs<AlignedAttr>(), [](const AlignedAttr *AA) {
return AA->isAlignmentDependent();
});
}

VarDecl *VarDecl::getTemplateInstantiationPattern() const {
const VarDecl *VD = this;

Expand Down
13 changes: 1 addition & 12 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13310,16 +13310,6 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
CheckCompleteDecompositionDeclaration(DD);
}

/// Determines if a variable's alignment is dependent.
static bool hasDependentAlignment(VarDecl *VD) {
if (VD->getType()->isDependentType())
return true;
for (auto *I : VD->specific_attrs<AlignedAttr>())
if (I->isAlignmentDependent())
return true;
return false;
}

/// Check if VD needs to be dllexport/dllimport due to being in a
/// dllexport/import function.
void Sema::CheckStaticLocalForDllExport(VarDecl *VD) {
Expand Down Expand Up @@ -13408,8 +13398,7 @@ void Sema::FinalizeDeclaration(Decl *ThisDecl) {
if (unsigned MaxAlign = Context.getTargetInfo().getMaxTLSAlign()) {
// Protect the check so that it's not performed on dependent types and
// dependent alignments (we can't determine the alignment in that case).
if (VD->getTLSKind() && !hasDependentAlignment(VD) &&
!VD->isInvalidDecl()) {
if (VD->getTLSKind() && !VD->hasDependentAlignment()) {
CharUnits MaxAlignChars = Context.toCharUnitsFromBits(MaxAlign);
if (Context.getDeclAlign(VD) > MaxAlignChars) {
Diag(VD->getLocation(), diag::err_tls_var_aligned_over_maximum)
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Sema/SemaStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3395,7 +3395,7 @@ Sema::NamedReturnInfo Sema::getNamedReturnInfo(const VarDecl *VD) {

// Variables with higher required alignment than their type's ABI
// alignment cannot use NRVO.
if (!VDType->isDependentType() && VD->hasAttr<AlignedAttr>() &&
if (!VD->hasDependentAlignment() &&
Context.getDeclAlign(VD) > Context.getTypeAlignInChars(VDType))
Info.S = NamedReturnInfo::MoveEligible;

Expand Down
26 changes: 26 additions & 0 deletions clang/test/CXX/class/class.init/class.copy.elision/p3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -487,3 +487,29 @@ void test5() try {
}

} // namespace test_simpler_implicit_move

namespace test_auto_variables {

struct S {};

template <class T> struct range {
S *begin() const;
S *end() const;
};

template <class T> S test_dependent_ranged_for() {
for (auto x : range<T>())
return x;
return S();
}
template S test_dependent_ranged_for<int>();

template <class T> struct X {};

template <class T> X<T> test_dependent_invalid_decl() {
auto x = X<T>().foo(); // expected-error {{no member named 'foo'}}
return x;
}
template X<int> test_dependent_invalid_decl<int>(); // expected-note {{requested here}}

} // namespace test_auto_variables
80 changes: 76 additions & 4 deletions clang/test/CodeGen/nrvo-tracking.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// RUN: %clang_cc1 -std=c++20 -fblocks -Wno-return-stack-address -triple x86_64-unknown-unknown-gnu -emit-llvm -O1 -fexperimental-new-pass-manager -o - %s | FileCheck %s

struct X {
X();
X(const X&);
X(X&&);
struct alignas(4) X {
X();
X(const X &);
X(X &&);
};

#define L(A, B, C) void l##A() { \
Expand Down Expand Up @@ -210,3 +210,75 @@ void b_attr() {
};
}()();
}

namespace test_alignas {

template <int A> X t1() {
X a [[gnu::aligned(A)]];
return a;
}

// CHECK-LABEL: define{{.*}} void @_ZN12test_alignas2t1ILi1EEE1Xv
// CHECK: call {{.*}} @_ZN1XC1Ev
// CHECK-NEXT: ret void
template X t1<1>();

// CHECK-LABEL: define{{.*}} void @_ZN12test_alignas2t1ILi4EEE1Xv
// CHECK: call {{.*}} @_ZN1XC1Ev
// CHECK-NEXT: ret void
template X t1<4>();

// CHECK-LABEL: define{{.*}} void @_ZN12test_alignas2t1ILi8EEE1Xv
// CHECK: call {{.*}} @_ZN1XC1Ev
// CHECK-NEXT: call {{.*}} @_ZN1XC1EOS_
// CHECK-NEXT: call void @llvm.lifetime.end
template X t1<8>();

template <int A> X t2() {
X a [[gnu::aligned(1)]] [[gnu::aligned(A)]] [[gnu::aligned(2)]];
return a;
}

// CHECK-LABEL: define{{.*}} void @_ZN12test_alignas2t2ILi1EEE1Xv
// CHECK: call {{.*}} @_ZN1XC1Ev
// CHECK-NEXT: ret void
template X t2<1>();

// CHECK-LABEL: define{{.*}} void @_ZN12test_alignas2t2ILi4EEE1Xv
// CHECK: call {{.*}} @_ZN1XC1Ev
// CHECK-NEXT: ret void
template X t2<4>();

// CHECK-LABEL: define{{.*}} void @_ZN12test_alignas2t2ILi8EEE1Xv
// CHECK: call {{.*}} @_ZN1XC1Ev
// CHECK-NEXT: call {{.*}} @_ZN1XC1EOS_
// CHECK-NEXT: call void @llvm.lifetime.end
template X t2<8>();

// CHECK-LABEL: define{{.*}} void @_ZN12test_alignas2t3Ev
// CHECK: call {{.*}} @_ZN1XC1Ev
// CHECK-NEXT: ret void
X t3() {
X a [[gnu::aligned(1)]];
return a;
}

// CHECK-LABEL: define{{.*}} void @_ZN12test_alignas2t4Ev
// CHECK: call {{.*}} @_ZN1XC1Ev
// CHECK-NEXT: call {{.*}} @_ZN1XC1EOS_
// CHECK-NEXT: call void @llvm.lifetime.end
X t4() {
X a [[gnu::aligned(8)]];
return a;
}

// CHECK-LABEL: define{{.*}} void @_ZN12test_alignas2t5Ev
// CHECK: call {{.*}} @_ZN1XC1Ev
// CHECK-NEXT: call {{.*}} @_ZN1XC1EOS_
// CHECK-NEXT: call void @llvm.lifetime.end
X t5() {
X a [[gnu::aligned(1)]] [[gnu::aligned(8)]];
return a;
}

} // namespace test_alignas

0 comments on commit f2d5fce

Please sign in to comment.