Skip to content

Commit

Permalink
Assume __cxa_allocate_exception returns an under-aligned memory on
Browse files Browse the repository at this point in the history
Darwin if the version of libc++abi isn't new enough to include the fix
in r319123

This patch resurrects r264998, which was committed to work around a bug
in libc++abi that was causing _cxa_allocate_exception to return a memory
that wasn't double-word aligned.

http://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20160328/154332.html

In addition, this patch makes clang issue a warning if the type of the
thrown object requires an alignment that is larger than the minimum
guaranteed by the target C++ runtime.

rdar://problem/49864414

Differential Revision: https://reviews.llvm.org/D61667

llvm-svn: 360404
  • Loading branch information
ahatanaka authored and MrSidims committed May 17, 2019
1 parent 0b5b5ab commit 4e76bbd
Show file tree
Hide file tree
Showing 9 changed files with 148 additions and 18 deletions.
7 changes: 7 additions & 0 deletions clang/include/clang/AST/ASTContext.h
Expand Up @@ -2166,6 +2166,13 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// pointers and large arrays get extra alignment.
CharUnits getDeclAlign(const Decl *D, bool ForAlignof = false) const;

/// Return the alignment (in bytes) of the thrown exception object. This is
/// only meaningful for targets that allocate C++ exceptions in a system
/// runtime, such as those using the Itanium C++ ABI.
CharUnits getExnObjectAlignment() const {
return toCharUnitsFromBits(Target->getExnObjectAlignment());
}

/// Get or compute information about the layout of the specified
/// record (struct/union/class) \p D, which indicates its size and field
/// position information.
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/DiagnosticGroups.td
Expand Up @@ -408,6 +408,7 @@ def ObjCMultipleMethodNames : DiagGroup<"objc-multiple-method-names">;
def ObjCFlexibleArray : DiagGroup<"objc-flexible-array">;
def ObjCBoxing : DiagGroup<"objc-boxing">;
def OpenCLUnsupportedRGBA: DiagGroup<"opencl-unsupported-rgba">;
def UnderalignedExceptionObject : DiagGroup<"underaligned-exception-object">;
def DeprecatedObjCIsaUsage : DiagGroup<"deprecated-objc-isa-usage">;
def ExplicitInitializeCall : DiagGroup<"explicit-initialize-call">;
def Packed : DiagGroup<"packed">;
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Expand Up @@ -6584,6 +6584,12 @@ def err_throw_incomplete : Error<
"cannot throw object of incomplete type %0">;
def err_throw_incomplete_ptr : Error<
"cannot throw pointer to object of incomplete type %0">;
def warn_throw_underaligned_obj : Warning<
"underaligned exception object thrown">,
InGroup<UnderalignedExceptionObject>;
def note_throw_underaligned_obj : Note<
"required alignment of type %0 (%1 bytes) is larger than the supported "
"alignment of C++ exception objects on this target (%2 bytes)">;
def err_return_in_constructor_handler : Error<
"return in the catch of a function try block of a constructor is illegal">;
def warn_cdtor_function_try_handler_mem_expr : Warning<
Expand Down
15 changes: 15 additions & 0 deletions clang/include/clang/Basic/TargetInfo.h
Expand Up @@ -637,6 +637,21 @@ class TargetInfo : public virtual TransferrableTargetInfo,
/// types for the given target.
unsigned getSimdDefaultAlign() const { return SimdDefaultAlign; }

/// Return the alignment (in bits) of the thrown exception object. This is
/// only meaningful for targets that allocate C++ exceptions in a system
/// runtime, such as those using the Itanium C++ ABI.
virtual unsigned getExnObjectAlignment() const {
// Itanium says that an _Unwind_Exception has to be "double-word"
// aligned (and thus the end of it is also so-aligned), meaning 16
// bytes. Of course, that was written for the actual Itanium,
// which is a 64-bit platform. Classically, the ABI doesn't really
// specify the alignment on other platforms, but in practice
// libUnwind declares the struct with __attribute__((aligned)), so
// we assume that alignment here. (It's generally 16 bytes, but
// some targets overwrite it.)
return getDefaultAlignForAttributeAligned();
}

/// Return the size of intmax_t and uintmax_t for this target, in bits.
unsigned getIntMaxTWidth() const {
return getTypeWidth(IntMaxType);
Expand Down
31 changes: 31 additions & 0 deletions clang/lib/Basic/Targets/OSTargets.h
Expand Up @@ -133,6 +133,37 @@ class LLVM_LIBRARY_VISIBILITY DarwinTargetInfo : public OSTargetInfo<Target> {
/// attribute on declarations that can be dynamically replaced.
bool hasProtectedVisibility() const override { return false; }

unsigned getExnObjectAlignment() const override {
// Older versions of libc++abi guarantee an alignment of only 8-bytes for
// exception objects because of a bug in __cxa_exception that was
// eventually fixed in r319123.
llvm::VersionTuple MinVersion;
const llvm::Triple &T = this->getTriple();

// Compute the earliest OS versions that have the fix to libc++abi.
switch (T.getOS()) {
case llvm::Triple::Darwin:
case llvm::Triple::MacOSX: // Earliest supporting version is 10.14.
MinVersion = llvm::VersionTuple(10U, 14U);
break;
case llvm::Triple::IOS:
case llvm::Triple::TvOS: // Earliest supporting version is 12.0.0.
MinVersion = llvm::VersionTuple(12U);
break;
case llvm::Triple::WatchOS: // Earliest supporting version is 5.0.0.
MinVersion = llvm::VersionTuple(5U);
break;
default:
llvm_unreachable("Unexpected OS");
}

unsigned Major, Minor, Micro;
T.getOSVersion(Major, Minor, Micro);
if (llvm::VersionTuple(Major, Minor, Micro) < MinVersion)
return 64;
return OSTargetInfo<Target>::getExnObjectAlignment();
}

TargetInfo::IntType getLeastIntTypeByWidth(unsigned BitWidth,
bool IsSigned) const final {
// Darwin uses `long long` for `int_least64_t` and `int_fast64_t`.
Expand Down
15 changes: 1 addition & 14 deletions clang/lib/CodeGen/ItaniumCXXABI.cpp
Expand Up @@ -154,19 +154,6 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI {
Address Ptr, QualType ElementType,
const CXXDestructorDecl *Dtor) override;

/// Itanium says that an _Unwind_Exception has to be "double-word"
/// aligned (and thus the end of it is also so-aligned), meaning 16
/// bytes. Of course, that was written for the actual Itanium,
/// which is a 64-bit platform. Classically, the ABI doesn't really
/// specify the alignment on other platforms, but in practice
/// libUnwind declares the struct with __attribute__((aligned)), so
/// we assume that alignment here. (It's generally 16 bytes, but
/// some targets overwrite it.)
CharUnits getAlignmentOfExnObject() {
auto align = CGM.getContext().getTargetDefaultAlignForAttributeAligned();
return CGM.getContext().toCharUnitsFromBits(align);
}

void emitRethrow(CodeGenFunction &CGF, bool isNoReturn) override;
void emitThrow(CodeGenFunction &CGF, const CXXThrowExpr *E) override;

Expand Down Expand Up @@ -1191,7 +1178,7 @@ void ItaniumCXXABI::emitThrow(CodeGenFunction &CGF, const CXXThrowExpr *E) {
llvm::CallInst *ExceptionPtr = CGF.EmitNounwindRuntimeCall(
AllocExceptionFn, llvm::ConstantInt::get(SizeTy, TypeSize), "exception");

CharUnits ExnAlign = getAlignmentOfExnObject();
CharUnits ExnAlign = CGF.getContext().getExnObjectAlignment();
CGF.EmitAnyExprToExn(E->getSubExpr(), Address(ExceptionPtr, ExnAlign));

// Now throw the exception.
Expand Down
15 changes: 15 additions & 0 deletions clang/lib/Sema/SemaExprCXX.cpp
Expand Up @@ -941,6 +941,21 @@ bool Sema::CheckCXXThrowOperand(SourceLocation ThrowLoc,
}
}

// Under the Itanium C++ ABI, memory for the exception object is allocated by
// the runtime with no ability for the compiler to request additional
// alignment. Warn if the exception type requires alignment beyond the minimum
// guaranteed by the target C++ runtime.
if (Context.getTargetInfo().getCXXABI().isItaniumFamily()) {
CharUnits TypeAlign = Context.getTypeAlignInChars(Ty);
CharUnits ExnObjAlign = Context.getExnObjectAlignment();
if (ExnObjAlign < TypeAlign) {
Diag(ThrowLoc, diag::warn_throw_underaligned_obj);
Diag(ThrowLoc, diag::note_throw_underaligned_obj)
<< Ty << (unsigned)TypeAlign.getQuantity()
<< (unsigned)ExnObjAlign.getQuantity();
}
}

return false;
}

Expand Down
10 changes: 6 additions & 4 deletions clang/test/CodeGenCXX/eh.cpp
@@ -1,5 +1,5 @@
// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -triple x86_64-apple-darwin -std=c++11 -emit-llvm %s -o - \
// RUN: | FileCheck %s
// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -triple x86_64-apple-macosx10.13.99 -std=c++11 -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=UNALIGNED %s
// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -triple x86_64-apple-macosx10.14 -std=c++11 -emit-llvm -o - %s | FileCheck --check-prefix=CHECK --check-prefix=ALIGNED %s

struct test1_D {
double d;
Expand All @@ -13,7 +13,8 @@ void test1() {
// CHECK: [[EXNOBJ:%.*]] = call i8* @__cxa_allocate_exception(i64 8)
// CHECK-NEXT: [[EXN:%.*]] = bitcast i8* [[EXNOBJ]] to [[DSTAR:%[^*]*\*]]
// CHECK-NEXT: [[EXN2:%.*]] = bitcast [[DSTAR]] [[EXN]] to i8*
// CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 [[EXN2]], i8* align 8 bitcast ([[DSTAR]] @d1 to i8*), i64 8, i1 false)
// UNALIGNED-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 [[EXN2]], i8* align 8 bitcast ([[DSTAR]] @d1 to i8*), i64 8, i1 false)
// ALIGNED-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 [[EXN2]], i8* align 8 bitcast ([[DSTAR]] @d1 to i8*), i64 8, i1 false)
// CHECK-NEXT: call void @__cxa_throw(i8* [[EXNOBJ]], i8* bitcast ({ i8*, i8* }* @_ZTI7test1_D to i8*), i8* null) [[NR:#[0-9]+]]
// CHECK-NEXT: unreachable

Expand Down Expand Up @@ -466,7 +467,8 @@ int foo() {
// CHECK: [[T0:%.*]] = call i8* @__cxa_allocate_exception(i64 16)
// CHECK-NEXT: [[T1:%.*]] = bitcast i8* [[T0]] to %"class.test17::DerivedException"*
// CHECK-NEXT: [[T2:%.*]] = bitcast %"class.test17::DerivedException"* [[T1]] to i8*
// CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* align 16 [[T2]], i8 0, i64 16, i1 false)
// UNALIGNED-NEXT: call void @llvm.memset.p0i8.i64(i8* align 8 [[T2]], i8 0, i64 16, i1 false)
// ALIGNED-NEXT: call void @llvm.memset.p0i8.i64(i8* align 16 [[T2]], i8 0, i64 16, i1 false)
}
}

Expand Down
66 changes: 66 additions & 0 deletions clang/test/SemaCXX/warn-overaligned-type-thrown.cpp
@@ -0,0 +1,66 @@
// RUN: %clang_cc1 -triple x86_64-apple-macosx10.13.99 -verify -fsyntax-only -std=c++11 -fcxx-exceptions -fexceptions -DUNDERALIGNED %s
// RUN: %clang_cc1 -triple arm64-apple-ios10 -verify -fsyntax-only -std=c++11 -fcxx-exceptions -fexceptions -DUNDERALIGNED %s
// RUN: %clang_cc1 -triple arm64-apple-tvos10 -verify -fsyntax-only -std=c++11 -fcxx-exceptions -fexceptions -DUNDERALIGNED %s
// RUN: %clang_cc1 -triple arm64-apple-watchos4 -verify -fsyntax-only -std=c++11 -fcxx-exceptions -fexceptions -DUNDERALIGNED %s
// RUN: %clang_cc1 -triple x86_64-apple-macosx10.14 -verify -fsyntax-only -std=c++11 -fcxx-exceptions -fexceptions %s
// RUN: %clang_cc1 -triple arm64-apple-ios12 -verify -fsyntax-only -std=c++11 -fcxx-exceptions -fexceptions %s
// RUN: %clang_cc1 -triple arm64-apple-tvos12 -verify -fsyntax-only -std=c++11 -fcxx-exceptions -fexceptions %s
// RUN: %clang_cc1 -triple arm64-apple-watchos5 -verify -fsyntax-only -std=c++11 -fcxx-exceptions -fexceptions %s
// RUN: %clang_cc1 -triple arm-linux-gnueabi -verify -fsyntax-only -std=c++11 -fcxx-exceptions -fexceptions %s
// RUN: %clang_cc1 -triple aarch64-linux-gnueabi -verify -fsyntax-only -std=c++11 -fcxx-exceptions -fexceptions %s
// RUN: %clang_cc1 -triple mipsel-linux-gnu -verify -fsyntax-only -std=c++11 -fcxx-exceptions -fexceptions %s
// RUN: %clang_cc1 -triple mips64el-linux-gnu -verify -fsyntax-only -std=c++11 -fcxx-exceptions -fexceptions %s
// RUN: %clang_cc1 -triple wasm32-unknown-unknown -verify -fsyntax-only -std=c++11 -fcxx-exceptions -fexceptions %s
// RUN: %clang_cc1 -triple wasm64-unknown-unknown -verify -fsyntax-only -std=c++11 -fcxx-exceptions -fexceptions %s
// RUN: %clang_cc1 -triple x86_64-apple-macosx10.14 -verify -fsyntax-only -std=c++11 -fcxx-exceptions -fexceptions -Wno-underaligned-exception-object -DNODIAG %s
// RUN: %clang_cc1 -triple x86_64-windows-msvc -verify -fsyntax-only -std=c++11 -fcxx-exceptions -fexceptions -DNODIAG %s

struct S0 {
S0();
int m;
};

struct Overaligned1 {
Overaligned1();
int __attribute__((aligned(16))) m;
};

struct __attribute__((aligned(16))) Overaligned2 {
Overaligned2();
int m;
};

struct Overaligned3 {
Overaligned3();
int __attribute__((aligned(64))) m;
};

void test0() {
throw S0();
}

void test1() {
throw Overaligned1();
}

void test2() {
throw Overaligned2();
}

void test3() {
throw Overaligned3();
}

#if defined(NODIAG)
// expected-no-diagnostics
#elif defined(UNDERALIGNED)
// expected-warning@-14 {{underaligned exception object thrown}}
// expected-note@-15 {{(16 bytes) is larger than the supported alignment of C++ exception objects on this target (8 bytes)}}
// expected-warning@-12 {{underaligned exception object thrown}}
// expected-note@-13 {{(16 bytes) is larger than the supported alignment of C++ exception objects on this target (8 bytes)}}
// expected-warning@-10 {{underaligned exception object thrown}}
// expected-note@-11 {{(64 bytes) is larger than the supported alignment of C++ exception objects on this target (8 bytes)}}
#else
// expected-warning@-13 {{underaligned exception object thrown}}
// expected-note@-14 {{(64 bytes) is larger than the supported alignment of C++ exception objects on this target (16 bytes)}}
#endif

0 comments on commit 4e76bbd

Please sign in to comment.