lvoufo created this revision.
lvoufo added reviewers: chandlerc, majnemer, dberlin, nlewycky.
lvoufo added a subscriber: cfe-commits.

After D14418, Remove restriction for writeonce semantics to objects with no 
non-trivial methods... 

http://reviews.llvm.org/D14419

Files:
  include/clang/AST/DeclCXX.h
  lib/AST/DeclCXX.cpp
  lib/AST/Type.cpp
  test/CodeGenCXX/const-invariant.cpp

Index: test/CodeGenCXX/const-invariant.cpp
===================================================================
--- test/CodeGenCXX/const-invariant.cpp
+++ test/CodeGenCXX/const-invariant.cpp
@@ -167,11 +167,11 @@
 // CHECK-NL-CO-OBJ: call void @_ZN1MC{{[0-9]+}}Ei({{.*}}* @_ZL3i_m, {{.*}})
 // CHECK-NL-CO-OBJ: call {{.*}}@llvm.invariant.start(i64 {{[0-9]+}}, i8* bitcast ({{.*}} @_ZL3i_m to i8*),
 // CHECK-NL-CO-OBJ: call void @_ZN1FC{{[0-9]+}}Ei({{.*}}* @_ZL3i_f, {{.*}})
-// CHECK-NL-CO-OBJ: call {{.*}}@llvm.invariant.start(i64 {{[0-9]+}}, i8* bitcast ({{.*}} @_ZL3i_f to i8*))
+// CHECK-NL-CO-OBJ-NOT: call {{.*}}@llvm.invariant.start(i64 {{[0-9]+}}, i8* bitcast ({{.*}} @_ZL3i_f to i8*))
 // CHECK-NL-CO-OBJ: call void @_ZN1IC{{[0-9]+}}Ei({{.*}}* @_ZL3i_i, {{.*}})
 // CHECK-NL-CO-OBJ: call {{.*}}@llvm.invariant.start(i64 {{[0-9]+}}, i8* bitcast ({{.*}} @_ZL3i_i to i8*))
 // CHECK-NL-CO-OBJ: call void @_ZN2IVC{{[0-9]+}}Ei({{.*}}* @_ZL4i_iv, {{.*}})
-// CHECK-NL-CO-OBJ: call {{.*}}@llvm.invariant.start(i64 {{[0-9]+}}, i8* bitcast ({{.*}} @_ZL4i_iv to i8*))
+// CHECK-NL-CO-OBJ-NOT: call {{.*}}@llvm.invariant.start(i64 {{[0-9]+}}, i8* bitcast ({{.*}} @_ZL4i_iv to i8*))
 // CHECK-NL-CO-OBJ: call void @_ZN3CIVC{{[0-9]+}}Ei({{.*}}* @_ZL5i_civ, {{.*}})
 // CHECK-NL-CO-OBJ: call {{.*}}@llvm.invariant.start(i64 {{[0-9]+}}, i8* bitcast ({{.*}} @_ZL5i_civ to i8*))
 
@@ -303,11 +303,11 @@
   // CHECK-L-CO-OBJ: %i_f = alloca {{.*}}
 
   // CHECK-L-CO-OBJ: call void @_ZN1FC{{[0-9]+}}Ei({{.*}}* %i_f,
-  // CHECK-L-CO-OBJ: call {{.*}}@llvm.invariant.start(
+  // CHECK-L-CO-OBJ-NOT: call {{.*}}@llvm.invariant.start(
   bar_f(i_f);
   foo_f(&i_f);  // May change i_f.
   bar_f(i_f);
-  // CHECK-L-CO-OBJ: call {{.*}}@llvm.invariant.end(
+  // CHECK-L-CO-OBJ-NOT: call {{.*}}@llvm.invariant.end(
 }
 
 // Example 1 with type inheriting non-virtual base:
@@ -337,11 +337,11 @@
   // CHECK-L-CO-OBJ: %i_iv = alloca {{.*}}
 
   // CHECK-L-CO-OBJ: call void @_ZN2IVC{{[0-9]+}}Ei({{.*}}* %i_iv,
-  // CHECK-L-CO-OBJ: call {{.*}}@llvm.invariant.start(
+  // CHECK-L-CO-OBJ-NOT: call {{.*}}@llvm.invariant.start(
   bar_iv(i_iv);
   foo_iv(&i_iv);  // Does not change i_iv.
   bar_iv(i_iv);
-  // CHECK-L-CO-OBJ: call {{.*}}@llvm.invariant.end(
+  // CHECK-L-CO-OBJ-NOT: call {{.*}}@llvm.invariant.end(
 }
 
 // Example 1 with type inheriting virtual writeonce base:
Index: lib/AST/Type.cpp
===================================================================
--- lib/AST/Type.cpp
+++ lib/AST/Type.cpp
@@ -1937,8 +1937,15 @@
 }
 
 bool QualType::isWriteOnce(ASTContext &Context) {
-  return (Context.getLangOpts().CPlusPlus && isConstant(Context) &&
-          !getTypePtr()->isReferenceType());
+  // TODO: Include C objects as well?
+  if (Context.getLangOpts().CPlusPlus && isConstant(Context) &&
+      !getTypePtr()->isReferenceType()) {
+    if (const CXXRecordDecl *Record =
+            Context.getBaseElementType(*this)->getAsCXXRecordDecl())
+      return Record->computeWriteOnceCandidacy();
+    return true;
+  }
+  return false;
 }
 
 bool QualType::isPODType(ASTContext &Context) const {
Index: lib/AST/DeclCXX.cpp
===================================================================
--- lib/AST/DeclCXX.cpp
+++ lib/AST/DeclCXX.cpp
@@ -58,6 +58,8 @@
       HasPrivateFields(false),
       HasProtectedFields(false),
       HasPublicFields(false),
+      IsWriteOnceChecked(false),
+      IsWriteOnceCandidate(false),
       HasMutableFields(false),
       HasVariantMembers(false),
       HasOnlyCMembers(true),
@@ -1348,6 +1350,73 @@
   return false;
 }
 
+/// \brief A class is a candidate for 'writeonce' semantics if
+/// none of its methods writes to memory.
+bool CXXRecordDecl::computeWriteOnceCandidacy() const {
+  // The class must be defined and concrete.
+  if (!isCompleteDefinition() || isDependentType()) return false;
+
+  // If this has already been checked, then return the stored value.
+  if (data().IsWriteOnceChecked) return data().IsWriteOnceCandidate;
+
+  // Otherwise, mark that this is checked.
+  data().IsWriteOnceChecked = true;
+  assert(!data().IsWriteOnceCandidate &&
+         "The candidacy should not have been set yet.");
+
+  // Check each method, excluding constructors and destructors.
+  // NOTE: The invariant intrinsic calls are generated right after the
+  //       construction of a given const object (which must be initialized)
+  //       and right before its destruction at the end of its lifetime.
+  //       Both constructors and destructors modify the allocated memory and
+  //       and will not be called in the middle of an invariant_start/end
+  //       pair, thus may not affect the reduction of loads. So, it does not
+  //       help to handle constructors here as well.
+  for (auto *M : methods()) {
+    if (isa<CXXConstructorDecl>(M) || isa<CXXDestructorDecl>(M)) continue;
+
+    // If we already know that this method does not write to memory, skip it.
+    // NOTE: CodeGenModule::ConstructAttributeList() marks declarations with
+    //       ConstAttr and PureAttr attributes, respectively, as 'readnone' and
+    //       'readonly' (for LLVM).
+    if (M->hasAttr<ConstAttr>() || M->hasAttr<PureAttr>()) continue;
+
+    const FunctionDecl *Def = nullptr;
+    bool IsDefined = M->isDefined(Def);
+
+    // If this is not defined and not virtual, then it may write to memory.
+    // So it's not a candidate for 'writeonce' semantics.
+    // If it is purely virtual then it must be overriden and its overriding
+    // method (in this context) must be a candidate for 'writeonce' semantics.
+    // Skip it.
+    if (!IsDefined) {
+      if (M->isPure()) {
+        assert(M->isVirtual() && "Not defined and pure implies virtual.");
+        continue;
+      }
+      return false;
+    }
+
+    // If this is trivial, skip it.
+    if (Def->hasTrivialBody()) continue;
+
+    // TODO: Any other case?
+  }
+
+  // Check bases
+  for (const auto &I : bases()) {
+    const RecordType *Ty = I.getType()->getAs<RecordType>();
+    assert(Ty && "No type?");
+    CXXRecordDecl *Base =
+        cast_or_null<CXXRecordDecl>(Ty->getDecl()->getDefinition());
+
+    if (!Base || !Base->computeWriteOnceCandidacy()) return false;
+  }
+
+  data().IsWriteOnceCandidate = true;
+  return true;
+}
+
 void CXXRecordDecl::completeDefinition() {
   completeDefinition(nullptr);
 }
Index: include/clang/AST/DeclCXX.h
===================================================================
--- include/clang/AST/DeclCXX.h
+++ include/clang/AST/DeclCXX.h
@@ -357,6 +357,14 @@
     /// \brief True when there are private non-static data members.
     bool HasPublicFields : 1;
 
+    /// \brief True is this class has already been checked as candidate
+    /// for writeonce semantics.
+    bool IsWriteOnceChecked : 1;
+
+    /// \brief True if instances of this class are candidates
+    /// for writeonce semantics.
+    bool IsWriteOnceCandidate : 1;
+
     /// \brief True if this class (or any subobject) has mutable fields.
     bool HasMutableFields : 1;
 
@@ -766,6 +774,8 @@
     return method_iterator(decls_end());
   }
 
+  bool computeWriteOnceCandidacy() const;
+
   /// Iterator access to constructor members.
   typedef specific_decl_iterator<CXXConstructorDecl> ctor_iterator;
   typedef llvm::iterator_range<specific_decl_iterator<CXXConstructorDecl>>
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to