[clang] fixes named return of variables with dependent alignment
authorMatheus Izvekov <mizvekov@gmail.com>
Fri, 2 Jul 2021 22:56:16 +0000 (00:56 +0200)
committerMatheus Izvekov <mizvekov@gmail.com>
Tue, 6 Jul 2021 00:30:44 +0000 (02:30 +0200)
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

clang/include/clang/AST/Decl.h
clang/lib/AST/Decl.cpp
clang/lib/Sema/SemaDecl.cpp
clang/lib/Sema/SemaStmt.cpp
clang/test/CodeGen/nrvo-tracking.cpp

index 020df62..d22594a 100644 (file)
@@ -1494,6 +1494,9 @@ public:
     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;
index 5047dc1..a92d372 100644 (file)
@@ -2534,6 +2534,13 @@ bool VarDecl::isNonEscapingByref() const {
   return hasAttr<BlocksAttr>() && !NonParmVarDeclBits.EscapingByref;
 }
 
+bool VarDecl::hasDependentAlignment() const {
+  return getType()->isDependentType() ||
+         llvm::any_of(specific_attrs<AlignedAttr>(), [](const AlignedAttr *AA) {
+           return AA->isAlignmentDependent();
+         });
+}
+
 VarDecl *VarDecl::getTemplateInstantiationPattern() const {
   const VarDecl *VD = this;
 
index 0075464..9e1f42a 100644 (file)
@@ -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) {
@@ -13408,7 +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) &&
+    if (VD->getTLSKind() && !VD->hasDependentAlignment() &&
         !VD->isInvalidDecl()) {
       CharUnits MaxAlignChars = Context.toCharUnitsFromBits(MaxAlign);
       if (Context.getDeclAlign(VD) > MaxAlignChars) {
index 1e86f38..506c06b 100644 (file)
@@ -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;
 
index 7893140..2d6eb9e 100644 (file)
@@ -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() {    \
@@ -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