r285664 - Implement ABI proposal for throwing noexcept function pointers, per discussion

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Mon Oct 31 18:34:46 PDT 2016


Author: rsmith
Date: Mon Oct 31 20:34:46 2016
New Revision: 285664

URL: http://llvm.org/viewvc/llvm-project?rev=285664&view=rev
Log:
Implement ABI proposal for throwing noexcept function pointers, per discussion
on cxx-abi-dev (thread starting 2016-10-11). This is currently hidden behind a
cc1-only -m flag, pending discussion of how best to deal with language changes
that require use of new symbols from the ABI library.

Added:
    cfe/trunk/test/CodeGenCXX/rtti-qualfn.cpp
Modified:
    cfe/trunk/include/clang/Driver/CC1Options.td
    cfe/trunk/include/clang/Frontend/CodeGenOptions.def
    cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp
    cfe/trunk/lib/Frontend/CompilerInvocation.cpp
    cfe/trunk/www/cxx_status.html

Modified: cfe/trunk/include/clang/Driver/CC1Options.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Driver/CC1Options.td?rev=285664&r1=285663&r2=285664&view=diff
==============================================================================
--- cfe/trunk/include/clang/Driver/CC1Options.td (original)
+++ cfe/trunk/include/clang/Driver/CC1Options.td Mon Oct 31 20:34:46 2016
@@ -251,6 +251,8 @@ def munwind_tables : Flag<["-"], "munwin
   HelpText<"Generate unwinding tables for all functions">;
 def mconstructor_aliases : Flag<["-"], "mconstructor-aliases">,
   HelpText<"Emit complete constructors and destructors as aliases when possible">;
+def mqualified_function_type_info : Flag<["-"], "mqualified-function-type-info">,
+  HelpText<"Emit __qualified_function_type_info for qualified function types">;
 def mlink_bitcode_file : Separate<["-"], "mlink-bitcode-file">,
   HelpText<"Link the given bitcode file before performing optimizations.">;
 def mlink_cuda_bitcode : Separate<["-"], "mlink-cuda-bitcode">,

Modified: cfe/trunk/include/clang/Frontend/CodeGenOptions.def
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Frontend/CodeGenOptions.def?rev=285664&r1=285663&r2=285664&view=diff
==============================================================================
--- cfe/trunk/include/clang/Frontend/CodeGenOptions.def (original)
+++ cfe/trunk/include/clang/Frontend/CodeGenOptions.def Mon Oct 31 20:34:46 2016
@@ -133,6 +133,7 @@ CODEGENOPT(DumpCoverageMapping , 1, 0) /
   /// If -fpcc-struct-return or -freg-struct-return is specified.
 ENUM_CODEGENOPT(StructReturnConvention, StructReturnConventionKind, 2, SRCK_Default)
 
+CODEGENOPT(QualifiedFunctionTypeInfo, 1, 0) ///< Use __qualified_function_type_info.
 CODEGENOPT(RelaxAll          , 1, 0) ///< Relax all machine code instructions.
 CODEGENOPT(RelaxedAliasing   , 1, 0) ///< Set when -fno-strict-aliasing is enabled.
 CODEGENOPT(StructPathTBAA    , 1, 0) ///< Whether or not to use struct-path TBAA.

Modified: cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp?rev=285664&r1=285663&r2=285664&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp (original)
+++ cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp Mon Oct 31 20:34:46 2016
@@ -46,6 +46,7 @@ protected:
   bool UseARMMethodPtrABI;
   bool UseARMGuardVarABI;
   bool Use32BitVTableOffsetABI;
+  bool UseQualifiedFunctionTypeInfoABI;
 
   ItaniumMangleContext &getMangleContext() {
     return cast<ItaniumMangleContext>(CodeGen::CGCXXABI::getMangleContext());
@@ -57,7 +58,8 @@ public:
                 bool UseARMGuardVarABI = false) :
     CGCXXABI(CGM), UseARMMethodPtrABI(UseARMMethodPtrABI),
     UseARMGuardVarABI(UseARMGuardVarABI),
-    Use32BitVTableOffsetABI(false) { }
+    Use32BitVTableOffsetABI(false),
+    UseQualifiedFunctionTypeInfoABI(CGM.getCodeGenOpts().QualifiedFunctionTypeInfo) { }
 
   bool classifyReturnType(CGFunctionInfo &FI) const override;
 
@@ -2427,6 +2429,9 @@ class ItaniumRTTIBuilder {
   /// descriptor of the given type.
   llvm::Constant *GetAddrOfExternalRTTIDescriptor(QualType Ty);
 
+  /// Determine whether FnTy should be emitted as a qualified function type.
+  bool EmitAsQualifiedFunctionType(const FunctionType *FnTy);
+
   /// BuildVTablePointer - Build the vtable pointer for the given type.
   void BuildVTablePointer(const Type *Ty);
 
@@ -2439,6 +2444,10 @@ class ItaniumRTTIBuilder {
   /// constraints, according ti the Itanium C++ ABI, 2.9.5p5c.
   void BuildVMIClassTypeInfo(const CXXRecordDecl *RD);
 
+  /// Build an abi::__qualified_function_type_info struct, used for function
+  /// types with various kinds of qualifiers.
+  void BuildQualifiedFunctionTypeInfo(const FunctionType *FnTy);
+
   /// BuildPointerTypeInfo - Build an abi::__pointer_type_info struct, used
   /// for pointer types.
   void BuildPointerTypeInfo(QualType PointeeTy);
@@ -2455,6 +2464,27 @@ public:
   ItaniumRTTIBuilder(const ItaniumCXXABI &ABI)
       : CGM(ABI.CGM), VMContext(CGM.getModule().getContext()), CXXABI(ABI) {}
 
+  // Function type info flags.
+  enum {
+    /// Qualifiers for 'this' pointer of member function type.
+    //@{
+    QFTI_Const = 0x1,
+    QFTI_Volatile = 0x2,
+    QFTI_Restrict = 0x4,
+    QFTI_LValRef = 0x8,
+    QFTI_RValRef = 0x10,
+    //@}
+
+    /// Noexcept function qualifier (C++17 onwards).
+    QFTI_Noexcept = 0x20,
+
+    // Transaction-safe function qualifier (Transactional Memory TS).
+    //QFTI_TxSafe = 0x40,
+
+    /// Noreturn function type qualifier (GNU/Clang extension).
+    QFTI_Noreturn = 0x80
+  };
+
   // Pointer type info flags.
   enum {
     /// PTI_Const - Type has const qualifier.
@@ -2806,8 +2836,12 @@ void ItaniumRTTIBuilder::BuildVTablePoin
 
   case Type::FunctionNoProto:
   case Type::FunctionProto:
-    // abi::__function_type_info.
-    VTableName = "_ZTVN10__cxxabiv120__function_type_infoE";
+    if (EmitAsQualifiedFunctionType(cast<FunctionType>(Ty)))
+      // abi::__qualified_function_type_info.
+      VTableName = "_ZTVN10__cxxabiv130__qualified_function_type_infoE";
+    else
+      // abi::__function_type_info.
+      VTableName = "_ZTVN10__cxxabiv120__function_type_infoE";
     break;
 
   case Type::Enum:
@@ -3021,10 +3055,15 @@ llvm::Constant *ItaniumRTTIBuilder::Buil
     break;
 
   case Type::FunctionNoProto:
-  case Type::FunctionProto:
+  case Type::FunctionProto: {
+    auto *FnTy = cast<FunctionType>(Ty);
     // Itanium C++ ABI 2.9.5p5:
     // abi::__function_type_info adds no data members to std::type_info.
+    if (EmitAsQualifiedFunctionType(FnTy))
+      // abi::__qualified_type_info adds a base function type and qualifiers.
+      BuildQualifiedFunctionTypeInfo(FnTy);
     break;
+  }
 
   case Type::Enum:
     // Itanium C++ ABI 2.9.5p5:
@@ -3319,6 +3358,72 @@ void ItaniumRTTIBuilder::BuildVMIClassTy
   }
 }
 
+bool ItaniumRTTIBuilder::EmitAsQualifiedFunctionType(const FunctionType *FnTy) {
+  if (!CXXABI.UseQualifiedFunctionTypeInfoABI)
+    return false;
+
+  auto *FPT = dyn_cast<FunctionProtoType>(FnTy);
+  if (!FPT)
+    return false;
+  return FPT->getTypeQuals() || FPT->getRefQualifier() != RQ_None ||
+         FPT->isNothrow(CXXABI.getContext()) || FPT->getNoReturnAttr();
+}
+
+void ItaniumRTTIBuilder::BuildQualifiedFunctionTypeInfo(
+    const FunctionType *FnTy) {
+  unsigned int Qualifiers = 0;
+
+  auto ExtInfo = FnTy->getExtInfo();
+  if (ExtInfo.getNoReturn()) {
+    Qualifiers |= QFTI_Noreturn;
+    ExtInfo = ExtInfo.withNoReturn(false);
+  }
+
+  QualType BaseType;
+  if (auto *FPT = dyn_cast<FunctionProtoType>(FnTy)) {
+    auto EPI = FPT->getExtProtoInfo();
+    EPI.ExtInfo = ExtInfo;
+
+    if (EPI.TypeQuals & Qualifiers::Const)
+      Qualifiers |= QFTI_Const;
+    if (EPI.TypeQuals & Qualifiers::Volatile)
+      Qualifiers |= QFTI_Volatile;
+    if (EPI.TypeQuals & Qualifiers::Restrict)
+      Qualifiers |= QFTI_Restrict;
+    EPI.TypeQuals = 0;
+
+    if (EPI.RefQualifier == RQ_LValue)
+      Qualifiers |= QFTI_LValRef;
+    else if (EPI.RefQualifier == RQ_RValue)
+      Qualifiers |= QFTI_RValRef;
+    EPI.RefQualifier = RQ_None;
+
+    if (EPI.ExceptionSpec.Type == EST_BasicNoexcept)
+      Qualifiers |= QFTI_Noexcept;
+    else
+      assert(EPI.ExceptionSpec.Type == EST_None &&
+             "unexpected canonical non-dependent exception spec");
+    EPI.ExceptionSpec.Type = EST_None;
+
+    BaseType = CXXABI.getContext().getFunctionType(FPT->getReturnType(),
+                                                   FPT->getParamTypes(), EPI);
+  } else {
+    BaseType =
+        QualType(CXXABI.getContext().adjustFunctionType(FnTy, ExtInfo), 0);
+  }
+
+  assert(Qualifiers && "should not have created qualified type info");
+
+  // __base_type is a pointer to the std::type_info derivation for the
+  // unqualified version of the function type.
+  Fields.push_back(ItaniumRTTIBuilder(CXXABI).BuildTypeInfo(BaseType));
+
+  // __qualifiers is a flag word describing the qualifiers of the function type.
+  llvm::Type *UnsignedIntLTy =
+    CGM.getTypes().ConvertType(CGM.getContext().UnsignedIntTy);
+  Fields.push_back(llvm::ConstantInt::get(UnsignedIntLTy, Qualifiers));
+}
+
 /// BuildPointerTypeInfo - Build an abi::__pointer_type_info struct,
 /// used for pointer types.
 void ItaniumRTTIBuilder::BuildPointerTypeInfo(QualType PointeeTy) {

Modified: cfe/trunk/lib/Frontend/CompilerInvocation.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/CompilerInvocation.cpp?rev=285664&r1=285663&r2=285664&view=diff
==============================================================================
--- cfe/trunk/lib/Frontend/CompilerInvocation.cpp (original)
+++ cfe/trunk/lib/Frontend/CompilerInvocation.cpp Mon Oct 31 20:34:46 2016
@@ -553,6 +553,7 @@ static bool ParseCodeGenArgs(CodeGenOpti
   Opts.ObjCAutoRefCountExceptions = Args.hasArg(OPT_fobjc_arc_exceptions);
   Opts.CXAAtExit = !Args.hasArg(OPT_fno_use_cxa_atexit);
   Opts.CXXCtorDtorAliases = Args.hasArg(OPT_mconstructor_aliases);
+  Opts.QualifiedFunctionTypeInfo = Args.hasArg(OPT_mqualified_function_type_info);
   Opts.CodeModel = getCodeModel(Args, Diags);
   Opts.DebugPass = Args.getLastArgValue(OPT_mdebug_pass);
   Opts.DisableFPElim =

Added: cfe/trunk/test/CodeGenCXX/rtti-qualfn.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/rtti-qualfn.cpp?rev=285664&view=auto
==============================================================================
--- cfe/trunk/test/CodeGenCXX/rtti-qualfn.cpp (added)
+++ cfe/trunk/test/CodeGenCXX/rtti-qualfn.cpp Mon Oct 31 20:34:46 2016
@@ -0,0 +1,51 @@
+// RUN: %clang_cc1 -std=c++1z -mqualified-function-type-info -I%S %s -triple x86_64-linux-gnu -emit-llvm -o - -fcxx-exceptions | FileCheck %s
+
+#include "typeinfo"
+
+struct A {};
+
+// CHECK-DAG: @_ZTIKFvvE = [[QFTI:linkonce_odr constant { i8\*, i8\*, i8\*, i32 } { i8\* bitcast \(i8\*\* getelementptr inbounds \(i8\*, i8\*\* @_ZTVN10__cxxabiv130__qualified_function_type_infoE, i64 2\) to i8\*\),]] i8* getelementptr inbounds ([6 x i8], [6 x i8]* @_ZTSKFvvE, i32 0, i32 0), i8* bitcast ({ i8*, i8* }* @_ZTIFvvE to i8*), i32 1 }, comdat
+// CHECK-DAG: @_ZTIM1AKFvvE = [[PMFTI:linkonce_odr constant { i8\*, i8\*, i32, i8\*, i8\* } { i8\* bitcast \(i8\*\* getelementptr inbounds \(i8\*, i8\*\* @_ZTVN10__cxxabiv129__pointer_to_member_type_infoE, i64 2\) to i8\*\),]] i8* getelementptr inbounds ([9 x i8], [9 x i8]* @_ZTSM1AKFvvE, i32 0, i32 0), i32 0, i8* bitcast ({ i8*, i8*, i8*, i32 }* @_ZTIKFvvE to i8*), i8* bitcast ({ i8*, i8* }* @_ZTI1A to i8*) }, comdat
+auto &ti_const = typeid(void (A::*)() const);
+
+// CHECK-DAG: @_ZTIVFvvE = [[QFTI]] {{.*}} @_ZTIFvvE {{.*}}, i32 2 }, comdat
+// CHECK-DAG: @_ZTIM1AVFvvE = [[PMFTI]] {{.*}}), i32 0, {{.*}} @_ZTIVFvvE
+auto &ti_volatile = typeid(void (A::*)() volatile);
+
+// CHECK-DAG: @_ZTIrFvvE = [[QFTI]] {{.*}} @_ZTIFvvE {{.*}}, i32 4 }, comdat
+// CHECK-DAG: @_ZTIM1ArFvvE = [[PMFTI]] {{.*}}), i32 0, {{.*}} @_ZTIrFvvE
+auto &ti_restrict = typeid(void (A::*)() __restrict);
+
+// CHECK-DAG: @_ZTIFvvRE = [[QFTI]] {{.*}} @_ZTIFvvE {{.*}}, i32 8 }, comdat
+// CHECK-DAG: @_ZTIM1AFvvRE = [[PMFTI]] {{.*}}), i32 0, {{.*}} @_ZTIFvvRE
+auto &ti_lref = typeid(void (A::*)() &);
+
+// CHECK-DAG: @_ZTIFvvOE = [[QFTI]] {{.*}} @_ZTIFvvE {{.*}}, i32 16 }, comdat
+// CHECK-DAG: @_ZTIM1AFvvOE = [[PMFTI]] {{.*}}), i32 0, {{.*}} @_ZTIFvvOE
+auto &ti_rref = typeid(void (A::*)() &&);
+
+// CHECK-DAG: @_ZTInxFvvE = [[QFTI]] {{.*}} @_ZTIFvvE {{.*}}, i32 32 }, comdat
+// CHECK-DAG: @_ZTIM1AnxFvvE = [[PMFTI]] {{.*}}), i32 0, {{.*}} @_ZTInxFvvE
+auto &ti_noexcept = typeid(void (A::*)() noexcept);
+
+//auto &ti_txsafe = typeid(void (A::*)() transaction_safe);
+
+// FIXME: Produce the typeinfo for a noreturn function type here?
+// CHECK-DAG: @_ZTIM1AFvvE = [[PMFTI]] {{.*}}), i32 0, {{.*}} @_ZTIFvvE
+auto &ti_noreturn = typeid(void __attribute__((noreturn)) (A::*)());
+
+// CHECK-DAG: @_ZTIrVKnxFvvRE = [[QFTI]] {{.*}} @_ZTIFvvE {{.*}}, i32 47 }, comdat
+// CHECK-DAG: @_ZTIM1ArVKnxFvvRE = [[PMFTI]] {{.*}}), i32 0, {{.*}} @_ZTIrVKnxFvvRE
+auto &ti_rainbow = typeid(void (A::*)() const volatile __restrict & noexcept);
+
+// CHECK-LABEL: define void @_Z1fv(
+__attribute__((noreturn)) void f() noexcept {
+  // CHECK: call void @__cxa_throw({{.*}}@_ZTIPnxFvvE
+  throw f;
+}
+
+// CHECK-LABEL: define void @_Z1gM1AnxFvvE(
+void g(__attribute__((noreturn)) void (A::*p)() noexcept) {
+  // CHECK: call void @__cxa_throw({{.*}}@_ZTIM1AnxFvvE
+  throw p;
+}

Modified: cfe/trunk/www/cxx_status.html
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/www/cxx_status.html?rev=285664&r1=285663&r2=285664&view=diff
==============================================================================
--- cfe/trunk/www/cxx_status.html (original)
+++ cfe/trunk/www/cxx_status.html Mon Oct 31 20:34:46 2016
@@ -612,9 +612,7 @@ as the draft C++1z standard evolves.
     <tr>
       <td>Make exception specifications part of the type system</td>
       <td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0012r1.html">P0012R1</a></td>
-      <td class="partial" align="center">Partial</td>
-      <!-- We don't correctly support throwing noexcept function types and
-           catching as non-noexcept yet. -->
+      <td class="svn" align="center">SVN <a href="#p0012">(9)</a></td>
     </tr>
     <tr>
       <td><tt>__has_include</tt> in preprocessor conditionals</td>
@@ -624,7 +622,7 @@ as the draft C++1z standard evolves.
     <tr>
       <td>New specification for inheriting constructors (<a href="cxx_dr_status.html#1941">DR1941</a> et al)</td>
       <td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0136r1.html">P0136R1</a></td>
-      <td class="full" align="center">Clang 3.9 <a href="#p0136">(9)</a></td>
+      <td class="full" align="center">Clang 3.9 <a href="#p0136">(10)</a></td>
     </tr>
     <!-- Jacksonville papers -->
     <tr>
@@ -701,7 +699,7 @@ as the draft C++1z standard evolves.
     <tr>
       <td rowspan=2>Stricter expression evaluation order</td>
       <td><a href="http://wg21.link/p0145r3">P0145R3</a></td>
-      <td class="svn" align="center" rowspan=2>SVN <a href="#p0145">(10)</a></td>
+      <td class="svn" align="center" rowspan=2>SVN <a href="#p0145">(11)</a></td>
     </tr>
     <tr>
       <td><a href="http://wg21.link/p0400r0">P0400R0</a></td>
@@ -741,10 +739,18 @@ all language versions that allow type de
 (per the request of the C++ committee).
 In Clang 3.7, a warning is emitted for all cases that would change meaning.
 </span><br>
-<span id="p0136">(9): This is the resolution to a Defect Report, so is applied
+<span id="p0012">(9): Support for throwing a noexcept function pointer and
+catching it as a non-noexcept function pointer requires an ABI library with
+C++17 support. Currently, only libc++abi 4.0 provides this support, so this
+portion of the feature is disabled by default. If you are using a sufficiently
+recent ABI library, you can enable support for this feature with the
+<code>-Xclang -mqualified-function-type-info</code> flag. This flag is likely
+to be removed or replaced in future Clang releases.
+</span><br>
+<span id="p0136">(10): This is the resolution to a Defect Report, so is applied
 to all language versions supporting inheriting constructors.
 </span><br>
-<span id="p0145">(10): Under the MS ABI, function parameters are destroyed from
+<span id="p0145">(11): Under the MS ABI, function parameters are destroyed from
 left to right in the callee. As a result, function parameters in calls to
 <tt>operator<<</tt>, <tt>operator>></tt>, <tt>operator->*</tt>,
 <tt>operator&&</tt>, <tt>operator||</tt>, and <tt>operator,</tt>




More information about the cfe-commits mailing list