diff options
-rw-r--r-- | clang/include/clang/Basic/DiagnosticSemaKinds.td | 4 | ||||
-rw-r--r-- | clang/include/clang/Basic/TargetInfo.h | 9 | ||||
-rw-r--r-- | clang/lib/Basic/TargetInfo.cpp | 1 | ||||
-rw-r--r-- | clang/lib/Basic/Targets.cpp | 2 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 26 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDeclAttr.cpp | 26 | ||||
-rw-r--r-- | clang/test/Sema/tls_alignment.cpp | 75 |
7 files changed, 138 insertions, 5 deletions
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 85866b24575..fb1e3f1cb46 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2161,6 +2161,10 @@ def warn_objc_redundant_literal_use : Warning< def err_attr_tlsmodel_arg : Error<"tls_model must be \"global-dynamic\", " "\"local-dynamic\", \"initial-exec\" or \"local-exec\"">; +def err_tls_var_aligned_over_maximum : Error< + "alignment (%0) of thread-local variable %1 is greater than the maximum supported " + "alignment (%2) for a thread-local variable on this target">; + def err_only_annotate_after_access_spec : Error< "access specifier can only have annotation attributes">; diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h index fed69a8281e..39f575f90ef 100644 --- a/clang/include/clang/Basic/TargetInfo.h +++ b/clang/include/clang/Basic/TargetInfo.h @@ -70,6 +70,7 @@ protected: unsigned char MinGlobalAlign; unsigned char MaxAtomicPromoteWidth, MaxAtomicInlineWidth; unsigned short MaxVectorAlign; + unsigned short MaxTLSAlign; unsigned short SimdDefaultAlign; const char *DescriptionString; const char *UserLabelPrefix; @@ -809,6 +810,14 @@ public: return TLSSupported; } + /// \brief Return the maximum alignment (in bits) of a TLS variable + /// + /// Gets the maximum alignment (in bits) of a TLS variable on this target. + /// Returns zero if there is no such constraint. + unsigned short getMaxTLSAlign() const { + return MaxTLSAlign; + } + /// \brief Whether the target supports SEH __try. bool isSEHTrySupported() const { return getTriple().isOSWindows() && diff --git a/clang/lib/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp index 856ad50d378..dbd2f9ae995 100644 --- a/clang/lib/Basic/TargetInfo.cpp +++ b/clang/lib/Basic/TargetInfo.cpp @@ -50,6 +50,7 @@ TargetInfo::TargetInfo(const llvm::Triple &T) : TargetOpts(), Triple(T) { LargeArrayAlign = 0; MaxAtomicPromoteWidth = MaxAtomicInlineWidth = 0; MaxVectorAlign = 0; + MaxTLSAlign = 0; SimdDefaultAlign = 0; SizeType = UnsignedLong; PtrDiffType = SignedLong; diff --git a/clang/lib/Basic/Targets.cpp b/clang/lib/Basic/Targets.cpp index c5bf90fa784..3cf74bc8492 100644 --- a/clang/lib/Basic/Targets.cpp +++ b/clang/lib/Basic/Targets.cpp @@ -580,6 +580,8 @@ public: PS4OSTargetInfo(const llvm::Triple &Triple) : OSTargetInfo<Target>(Triple) { this->WCharType = this->UnsignedShort; + // On PS4, TLS variable cannot be aligned to more than 32 bytes (256 bits). + this->MaxTLSAlign = 256; this->UserLabelPrefix = ""; switch (Triple.getArch()) { diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 5646099015b..99d4a79c466 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -9828,6 +9828,16 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { FinalizeVarWithDestructor(var, recordType); } +/// \brief 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; +} + /// FinalizeDeclaration - called by ParseDeclarationAfterDeclarator to perform /// any semantic actions necessary after any initializer has been attached. void @@ -9841,6 +9851,22 @@ Sema::FinalizeDeclaration(Decl *ThisDecl) { checkAttributesAfterMerging(*this, *VD); + // Perform TLS alignment check here after attributes attached to the variable + // which may affect the alignment have been processed. Only perform the check + // if the target has a maximum TLS alignment (zero means no constraints). + 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)) { + CharUnits MaxAlignChars = Context.toCharUnitsFromBits(MaxAlign); + if (Context.getDeclAlign(VD) > MaxAlignChars) { + Diag(VD->getLocation(), diag::err_tls_var_aligned_over_maximum) + << (unsigned)Context.getDeclAlign(VD).getQuantity() << VD + << (unsigned)MaxAlignChars.getQuantity(); + } + } + } + // Static locals inherit dll attributes from their function. if (VD->isStaticLocal()) { if (FunctionDecl *FD = diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index b8d08306859..191dbd05c9b 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -2985,11 +2985,27 @@ void Sema::AddAlignedAttr(SourceRange AttrRange, Decl *D, Expr *E, // specifier shall have no effect // C11 6.7.5p6: // An alignment specification of zero has no effect. - if (!(TmpAttr.isAlignas() && !Alignment) && - !llvm::isPowerOf2_64(Alignment.getZExtValue())) { - Diag(AttrLoc, diag::err_alignment_not_power_of_two) - << E->getSourceRange(); - return; + if (!(TmpAttr.isAlignas() && !Alignment)) { + if(!llvm::isPowerOf2_64(Alignment.getZExtValue())) { + Diag(AttrLoc, diag::err_alignment_not_power_of_two) + << E->getSourceRange(); + return; + } + if (Context.getTargetInfo().isTLSSupported()) { + if (unsigned MaxAlign = Context.getTargetInfo().getMaxTLSAlign()) { + if (VarDecl *VD = dyn_cast<VarDecl>(D)) { + if (VD->getTLSKind()) { + CharUnits MaxAlignChars = Context.toCharUnitsFromBits(MaxAlign); + if (Alignment.getSExtValue() > MaxAlignChars.getQuantity()) { + Diag(VD->getLocation(), diag::err_tls_var_aligned_over_maximum) + << (unsigned)Alignment.getZExtValue() << VD + << (unsigned)MaxAlignChars.getQuantity(); + return; + } + } + } + } + } } // Alignment calculations can wrap around if it's greater than 2**28. diff --git a/clang/test/Sema/tls_alignment.cpp b/clang/test/Sema/tls_alignment.cpp new file mode 100644 index 00000000000..5a7bb2c463f --- /dev/null +++ b/clang/test/Sema/tls_alignment.cpp @@ -0,0 +1,75 @@ +// TLS variable cannot be aligned to more than 32 bytes on PS4. + +// RUN: %clang_cc1 -triple x86_64-scei-ps4 -fsyntax-only -verify %s + + +// A non-aligned type. +struct non_aligned_struct { + int some_data[16]; // 64 bytes of stuff, non aligned. +}; + +// An aligned type. +struct __attribute__(( aligned(64) )) aligned_struct { + int some_data[12]; // 48 bytes of stuff, aligned to 64. +}; + +// A type with an aligned field. +struct struct_with_aligned_field { + int some_aligned_data[12] __attribute__(( aligned(64) )); // 48 bytes of stuff, aligned to 64. +}; + +// A typedef of the aligned struct. +typedef aligned_struct another_aligned_struct; + +// A typedef to redefine a non-aligned struct as aligned. +typedef __attribute__(( aligned(64) )) non_aligned_struct yet_another_aligned_struct; + +// Non aligned variable doesn't cause an error. +__thread non_aligned_struct foo; + +// Variable aligned because of its type should cause an error. +__thread aligned_struct bar; // expected-error{{alignment (64) of thread-local variable}} + +// Variable explicitly aligned in the declaration should cause an error. +__thread non_aligned_struct bar2 __attribute__(( aligned(64) )); // expected-error{{alignment (64) of thread-local variable}} + +// Variable aligned because of one of its fields should cause an error. +__thread struct_with_aligned_field bar3; // expected-error{{alignment (64) of thread-local variable}} + +// Variable aligned because of typedef, first case. +__thread another_aligned_struct bar4; // expected-error{{alignment (64) of thread-local variable}} + +// Variable aligned because of typedef, second case. +__thread yet_another_aligned_struct bar5; // expected-error{{alignment (64) of thread-local variable}} + +int baz () +{ + return foo.some_data[0] + bar.some_data[1] + bar2.some_data[2] + + bar3.some_aligned_data[3] + bar4.some_data[4] + + bar5.some_data[5]; +} + + +// Verify alignment check where a dependent type is involved. +// The check is (correctly) not performed on "t", but the check still is +// performed on the structure as a whole once it has been instantiated. + +template<class T> struct templated_tls { + static __thread T t; + T other_t __attribute__(( aligned(64) )); +}; +__thread templated_tls<int> blah; // expected-error{{alignment (64) of thread-local variable}} + +int blag() { + return blah.other_t * 2; +} + + +// Verify alignment check where the alignment is a template parameter. +// The check is only performed during instantiation. +template <int N> +struct S { + static int __thread __attribute__((aligned(N))) x; // expected-error{{alignment (64) of thread-local variable}} +}; + +S<64> s_instance; // expected-note{{in instantiation of template class 'S<64>' requested here}} |