Commit 83748cc5 authored by Marco Antognini's avatar Marco Antognini
Browse files

[OpenCL] Improve destructor support in C++ for OpenCL

Summary:
This patch does mainly three things:
 1. It fixes a false positive error detection in Sema that is similar to
    D62156. The error happens when explicitly calling an overloaded
    destructor for different address spaces.
 2. It selects the correct destructor when multiple overloads for
    address spaces are available.
 3. It inserts the expected address space cast when invoking a
    destructor, if needed, and therefore fixes a crash due to the unmet
    assertion in llvm::CastInst::Create.

The following is a reproducer of the three issues:

    struct MyType {
      ~MyType() {}
      ~MyType() __constant {}
    };

    __constant MyType myGlobal{};

    kernel void foo() {
      myGlobal.~MyType(); // 1 and 2.
      // 1. error: cannot initialize object parameter of type
      //    '__generic MyType' with an expression of type '__constant MyType'
      // 2. error: no matching member function for call to '~MyType'
    }

    kernel void bar() {
      // 3. The implicit call to the destructor crashes due to:
      //    Assertion `castIsValid(op, S, Ty) && "Invalid cast!"' failed.
      //    in llvm::CastInst::Create.
      MyType myLocal;
    }

The added test depends on D62413 and covers a few more things than the
above reproducer.

Subscribers: yaxunl, Anastasia, cfe-commits

Tags: #clang

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

llvm-svn: 366422
parent 46b55fa5
Loading
Loading
Loading
Loading
+11 −3
Original line number Diff line number Diff line
@@ -2232,20 +2232,20 @@ public:

  overridden_method_range overridden_methods() const;

  /// Returns the parent of this method declaration, which
  /// Return the parent of this method declaration, which
  /// is the class in which this method is defined.
  const CXXRecordDecl *getParent() const {
    return cast<CXXRecordDecl>(FunctionDecl::getParent());
  }

  /// Returns the parent of this method declaration, which
  /// Return the parent of this method declaration, which
  /// is the class in which this method is defined.
  CXXRecordDecl *getParent() {
    return const_cast<CXXRecordDecl *>(
             cast<CXXRecordDecl>(FunctionDecl::getParent()));
  }

  /// Returns the type of the \c this pointer.
  /// Return the type of the \c this pointer.
  ///
  /// Should only be called for instance (i.e., non-static) methods. Note
  /// that for the call operator of a lambda closure type, this returns the
@@ -2253,9 +2253,17 @@ public:
  /// 'this' type.
  QualType getThisType() const;

  /// Return the type of the object pointed by \c this.
  ///
  /// See getThisType() for usage restriction.
  QualType getThisObjectType() const;

  static QualType getThisType(const FunctionProtoType *FPT,
                              const CXXRecordDecl *Decl);

  static QualType getThisObjectType(const FunctionProtoType *FPT,
                                    const CXXRecordDecl *Decl);

  Qualifiers getMethodQualifiers() const {
    return getType()->getAs<FunctionProtoType>()->getMethodQuals();
  }
+22 −3
Original line number Diff line number Diff line
@@ -2253,12 +2253,23 @@ CXXMethodDecl::overridden_methods() const {
  return getASTContext().overridden_methods(this);
}

static QualType getThisObjectType(ASTContext &C, const FunctionProtoType *FPT,
                                  const CXXRecordDecl *Decl) {
  QualType ClassTy = C.getTypeDeclType(Decl);
  return C.getQualifiedType(ClassTy, FPT->getMethodQuals());
}

QualType CXXMethodDecl::getThisType(const FunctionProtoType *FPT,
                                    const CXXRecordDecl *Decl) {
  ASTContext &C = Decl->getASTContext();
  QualType ClassTy = C.getTypeDeclType(Decl);
  ClassTy = C.getQualifiedType(ClassTy, FPT->getMethodQuals());
  return C.getPointerType(ClassTy);
  QualType ObjectTy = ::getThisObjectType(C, FPT, Decl);
  return C.getPointerType(ObjectTy);
}

QualType CXXMethodDecl::getThisObjectType(const FunctionProtoType *FPT,
                                          const CXXRecordDecl *Decl) {
  ASTContext &C = Decl->getASTContext();
  return ::getThisObjectType(C, FPT, Decl);
}

QualType CXXMethodDecl::getThisType() const {
@@ -2273,6 +2284,14 @@ QualType CXXMethodDecl::getThisType() const {
                                    getParent());
}

QualType CXXMethodDecl::getThisObjectType() const {
  // Ditto getThisType.
  assert(isInstance() && "No 'this' for static methods!");

  return CXXMethodDecl::getThisObjectType(getType()->getAs<FunctionProtoType>(),
                                          getParent());
}

bool CXXMethodDecl::hasInlineBody() const {
  // If this function is a template instantiation, look at the template from
  // which it was instantiated.
+9 −5
Original line number Diff line number Diff line
@@ -378,7 +378,7 @@ public:
  virtual void EmitDestructorCall(CodeGenFunction &CGF,
                                  const CXXDestructorDecl *DD, CXXDtorType Type,
                                  bool ForVirtualBase, bool Delegating,
                                  Address This) = 0;
                                  Address This, QualType ThisTy) = 0;

  /// Emits the VTable definitions required for the given record type.
  virtual void emitVTableDefinitions(CodeGenVTables &CGVT,
@@ -421,11 +421,15 @@ public:
                                             llvm::Type *Ty,
                                             SourceLocation Loc) = 0;

  using DeleteOrMemberCallExpr =
      llvm::PointerUnion<const CXXDeleteExpr *, const CXXMemberCallExpr *>;

  /// Emit the ABI-specific virtual destructor call.
  virtual llvm::Value *
  EmitVirtualDestructorCall(CodeGenFunction &CGF, const CXXDestructorDecl *Dtor,
                            CXXDtorType DtorType, Address This,
                            const CXXMemberCallExpr *CE) = 0;
  virtual llvm::Value *EmitVirtualDestructorCall(CodeGenFunction &CGF,
                                                 const CXXDestructorDecl *Dtor,
                                                 CXXDtorType DtorType,
                                                 Address This,
                                                 DeleteOrMemberCallExpr E) = 0;

  virtual void adjustCallArgsForDestructorThunk(CodeGenFunction &CGF,
                                                GlobalDecl GD,
+1 −1
Original line number Diff line number Diff line
@@ -3502,7 +3502,7 @@ struct DestroyUnpassedArg final : EHScopeStack::Cleanup {
      const CXXDestructorDecl *Dtor = Ty->getAsCXXRecordDecl()->getDestructor();
      assert(!Dtor->isTrivial());
      CGF.EmitCXXDestructorCall(Dtor, Dtor_Complete, /*for vbase*/ false,
                                /*Delegating=*/false, Addr);
                                /*Delegating=*/false, Addr, Ty);
    } else {
      CGF.callCStructDestructor(CGF.MakeAddrLValue(Addr, Ty));
    }
+25 −15
Original line number Diff line number Diff line
@@ -491,12 +491,15 @@ namespace {
        cast<CXXMethodDecl>(CGF.CurCodeDecl)->getParent();

      const CXXDestructorDecl *D = BaseClass->getDestructor();
      // We are already inside a destructor, so presumably the object being
      // destroyed should have the expected type.
      QualType ThisTy = D->getThisObjectType();
      Address Addr =
        CGF.GetAddressOfDirectBaseInCompleteClass(CGF.LoadCXXThisAddress(),
                                                  DerivedClass, BaseClass,
                                                  BaseIsVirtual);
      CGF.EmitCXXDestructorCall(D, Dtor_Base, BaseIsVirtual,
                                /*Delegating=*/false, Addr);
                                /*Delegating=*/false, Addr, ThisTy);
    }
  };

@@ -1440,9 +1443,11 @@ void CodeGenFunction::EmitDestructorBody(FunctionArgList &Args) {
  if (DtorType == Dtor_Deleting) {
    RunCleanupsScope DtorEpilogue(*this);
    EnterDtorCleanups(Dtor, Dtor_Deleting);
    if (HaveInsertPoint())
    if (HaveInsertPoint()) {
      QualType ThisTy = Dtor->getThisObjectType();
      EmitCXXDestructorCall(Dtor, Dtor_Complete, /*ForVirtualBase=*/false,
                            /*Delegating=*/false, LoadCXXThisAddress());
                            /*Delegating=*/false, LoadCXXThisAddress(), ThisTy);
    }
    return;
  }

@@ -1473,8 +1478,9 @@ void CodeGenFunction::EmitDestructorBody(FunctionArgList &Args) {
    EnterDtorCleanups(Dtor, Dtor_Complete);

    if (!isTryBody) {
      QualType ThisTy = Dtor->getThisObjectType();
      EmitCXXDestructorCall(Dtor, Dtor_Base, /*ForVirtualBase=*/false,
                            /*Delegating=*/false, LoadCXXThisAddress());
                            /*Delegating=*/false, LoadCXXThisAddress(), ThisTy);
      break;
    }

@@ -2013,7 +2019,7 @@ void CodeGenFunction::destroyCXXObject(CodeGenFunction &CGF,
  const CXXDestructorDecl *dtor = record->getDestructor();
  assert(!dtor->isTrivial());
  CGF.EmitCXXDestructorCall(dtor, Dtor_Complete, /*for vbase*/ false,
                            /*Delegating=*/false, addr);
                            /*Delegating=*/false, addr, type);
}

void CodeGenFunction::EmitCXXConstructorCall(const CXXConstructorDecl *D,
@@ -2363,8 +2369,11 @@ namespace {
      : Dtor(D), Addr(Addr), Type(Type) {}

    void Emit(CodeGenFunction &CGF, Flags flags) override {
      // We are calling the destructor from within the constructor.
      // Therefore, "this" should have the expected type.
      QualType ThisTy = Dtor->getThisObjectType();
      CGF.EmitCXXDestructorCall(Dtor, Type, /*ForVirtualBase=*/false,
                                /*Delegating=*/true, Addr);
                                /*Delegating=*/true, Addr, ThisTy);
    }
  };
} // end anonymous namespace
@@ -2402,31 +2411,32 @@ CodeGenFunction::EmitDelegatingCXXConstructorCall(const CXXConstructorDecl *Ctor
void CodeGenFunction::EmitCXXDestructorCall(const CXXDestructorDecl *DD,
                                            CXXDtorType Type,
                                            bool ForVirtualBase,
                                            bool Delegating,
                                            Address This) {
                                            bool Delegating, Address This,
                                            QualType ThisTy) {
  CGM.getCXXABI().EmitDestructorCall(*this, DD, Type, ForVirtualBase,
                                     Delegating, This);
                                     Delegating, This, ThisTy);
}

namespace {
  struct CallLocalDtor final : EHScopeStack::Cleanup {
    const CXXDestructorDecl *Dtor;
    Address Addr;
    QualType Ty;

    CallLocalDtor(const CXXDestructorDecl *D, Address Addr)
      : Dtor(D), Addr(Addr) {}
    CallLocalDtor(const CXXDestructorDecl *D, Address Addr, QualType Ty)
        : Dtor(D), Addr(Addr), Ty(Ty) {}

    void Emit(CodeGenFunction &CGF, Flags flags) override {
      CGF.EmitCXXDestructorCall(Dtor, Dtor_Complete,
                                /*ForVirtualBase=*/false,
                                /*Delegating=*/false, Addr);
                                /*Delegating=*/false, Addr, Ty);
    }
  };
} // end anonymous namespace

void CodeGenFunction::PushDestructorCleanup(const CXXDestructorDecl *D,
                                            Address Addr) {
  EHStack.pushCleanup<CallLocalDtor>(NormalAndEHCleanup, D, Addr);
                                            QualType T, Address Addr) {
  EHStack.pushCleanup<CallLocalDtor>(NormalAndEHCleanup, D, Addr, T);
}

void CodeGenFunction::PushDestructorCleanup(QualType T, Address Addr) {
@@ -2436,7 +2446,7 @@ void CodeGenFunction::PushDestructorCleanup(QualType T, Address Addr) {

  const CXXDestructorDecl *D = ClassDecl->getDestructor();
  assert(D && D->isUsed() && "destructor not marked as used!");
  PushDestructorCleanup(D, Addr);
  PushDestructorCleanup(D, T, Addr);
}

void CodeGenFunction::InitializeVTablePointer(const VPtr &Vptr) {
Loading