]> granicus.if.org Git - clang/commitdiff
Add the ability to use logical expressions for capability attributes. This is to...
authorAaron Ballman <aaron@aaronballman.com>
Fri, 9 May 2014 18:26:23 +0000 (18:26 +0000)
committerAaron Ballman <aaron@aaronballman.com>
Fri, 9 May 2014 18:26:23 +0000 (18:26 +0000)
void foo(void) __attribute__((requires_capability((FlightControl || Worker) && !Logger)));

This is WIP code.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@208439 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Analysis/Analyses/ThreadSafetyLogical.h [new file with mode: 0644]
include/clang/Analysis/Analyses/ThreadSafetyUtil.h
lib/Analysis/CMakeLists.txt
lib/Analysis/ThreadSafety.cpp
lib/Analysis/ThreadSafetyLogical.cpp [new file with mode: 0644]

diff --git a/include/clang/Analysis/Analyses/ThreadSafetyLogical.h b/include/clang/Analysis/Analyses/ThreadSafetyLogical.h
new file mode 100644 (file)
index 0000000..c4f4b21
--- /dev/null
@@ -0,0 +1,108 @@
+//===- ThreadSafetyLogical.h -----------------------------------*- C++ --*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// This file defines a representation for logical expressions with SExpr leaves
+// that are used as part of fact-checking capability expressions.
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_THREAD_SAFETY_LOGICAL_H
+#define LLVM_CLANG_THREAD_SAFETY_LOGICAL_H
+
+#include "clang/Analysis/Analyses/ThreadSafetyTIL.h"
+
+namespace clang {
+namespace threadSafety {
+namespace lexpr {
+
+class LExpr {
+public:
+  enum Opcode {
+    Terminal,
+    And,
+    Or,
+    Not
+  };
+  Opcode kind() const { return Kind; }
+
+  /// \brief Logical implication. Returns true if the LExpr implies RHS, i.e. if
+  /// the LExpr holds, then RHS must hold. For example, (A & B) implies A.
+  inline bool implies(const LExpr *RHS) const;
+
+protected:
+  LExpr(Opcode Kind) : Kind(Kind) {}
+
+private:
+  Opcode Kind;
+};
+
+class Terminal : public LExpr {
+  til::SExprRef Expr;
+
+public:
+  Terminal(til::SExpr *Expr) : LExpr(LExpr::Terminal), Expr(Expr) {}
+
+  const til::SExpr *expr() const { return Expr.get(); }
+  til::SExpr *expr() { return Expr.get(); }
+
+  static bool classof(const LExpr *E) { return E->kind() == LExpr::Terminal; }
+};
+
+class BinOp : public LExpr {
+  LExpr *LHS, *RHS;
+
+protected:
+  BinOp(LExpr *LHS, LExpr *RHS, Opcode Code) : LExpr(Code), LHS(LHS), RHS(RHS) {}
+
+public:
+  const LExpr *left() const { return LHS; }
+  LExpr *left() { return LHS; }
+
+  const LExpr *right() const { return RHS; }
+  LExpr *right() { return RHS; }
+};
+
+class And : public BinOp {
+public:
+  And(LExpr *LHS, LExpr *RHS) : BinOp(LHS, RHS, LExpr::And) {}
+
+  static bool classof(const LExpr *E) { return E->kind() == LExpr::And; }
+};
+
+class Or : public BinOp {
+public:
+  Or(LExpr *LHS, LExpr *RHS) : BinOp(LHS, RHS, LExpr::Or) {}
+
+  static bool classof(const LExpr *E) { return E->kind() == LExpr::Or; }
+};
+
+class Not : public LExpr {
+  LExpr *Exp;
+
+public:
+  Not(LExpr *Exp) : LExpr(LExpr::Not), Exp(Exp) {}
+
+  const LExpr *exp() const { return Exp; }
+  LExpr *exp() { return Exp; }
+
+  static bool classof(const LExpr *E) { return E->kind() == LExpr::Not; }
+};
+
+/// \brief Logical implication. Returns true if LHS implies RHS, i.e. if LHS
+/// holds, then RHS must hold. For example, (A & B) implies A.
+bool implies(const LExpr *LHS, const LExpr *RHS);
+
+bool LExpr::implies(const LExpr *RHS) const {
+  return lexpr::implies(this, RHS);
+}
+
+}
+}
+}
+
+#endif // LLVM_CLANG_THREAD_SAFETY_LOGICAL_H
+
index 3b768c5a692da072482002a9689c15f6ba64116a..ece7425c22525527c3a149250dd9af6f8d3b6f17 100644 (file)
 #ifndef LLVM_CLANG_THREAD_SAFETY_UTIL_H
 #define LLVM_CLANG_THREAD_SAFETY_UTIL_H
 
+#include "llvm/ADT/StringRef.h"
 #include "llvm/Support/AlignOf.h"
 #include "llvm/Support/Allocator.h"
 
 #include <cassert>
 #include <cstddef>
+#include <vector>
 #include <utility>
 
 namespace clang {
index f3e4358974c269f8f84fb48130e551fa38af521c..89f85933070e01f4da77fdd18ebeb84709e6d2fc 100644 (file)
@@ -22,8 +22,9 @@ add_clang_library(clangAnalysis
   PseudoConstantAnalysis.cpp
   ReachableCode.cpp
   ScanfFormatString.cpp
-  ThreadSafetyCommon.cpp
   ThreadSafety.cpp
+  ThreadSafetyCommon.cpp
+  ThreadSafetyLogical.cpp
   UninitializedValues.cpp
 
   LINK_LIBS
index b34b4814b6f6a82d4f745057106b0013a1e9731f..d19e04d630d8db040abc97f94ab21e86f0c9034c 100644 (file)
@@ -22,6 +22,7 @@
 #include "clang/AST/StmtVisitor.h"
 #include "clang/Analysis/Analyses/PostOrderCFGView.h"
 #include "clang/Analysis/Analyses/ThreadSafety.h"
+#include "clang/Analysis/Analyses/ThreadSafetyLogical.h"
 #include "clang/Analysis/Analyses/ThreadSafetyTIL.h"
 #include "clang/Analysis/Analyses/ThreadSafetyTraverse.h"
 #include "clang/Analysis/Analyses/ThreadSafetyCommon.h"
@@ -708,6 +709,52 @@ public:
   }
 };
 
+/// \brief Attempts to create an LExpr from a Clang Expr. If an LExpr cannot be
+/// constructed, returns a null pointer. Recursive function that terminates when
+/// the complete expression is handled, or when a failure to create an LExpr
+/// occurs.
+static clang::threadSafety::lexpr::LExpr *
+buildLExpr(threadSafety::til::MemRegionRef &Arena, const Expr *CurExpr) {
+  using namespace clang::threadSafety::lexpr;
+  using namespace clang::threadSafety::til;
+
+  if (const auto *DRE = dyn_cast<DeclRefExpr>(CurExpr)) {
+    // TODO: Construct the til::SExpr leaf properly.
+    return new Terminal(new (Arena) Variable());
+  } else if (const auto *ME = dyn_cast<MemberExpr>(CurExpr)) {
+    // TODO: Construct the til::SExpr leaf properly.
+    return new Terminal(new (Arena) Variable());
+  } else if (const auto *BOE = dyn_cast<BinaryOperator>(CurExpr)) {
+    switch (BOE->getOpcode()) {
+      case BO_LOr:
+      case BO_LAnd: {
+        auto *LHS = buildLExpr(Arena, BOE->getLHS());
+        auto *RHS = buildLExpr(Arena, BOE->getRHS());
+        if (!LHS || !RHS)
+          return nullptr;
+
+        if (BOE->getOpcode() == BO_LOr)
+          return new Or(LHS, RHS);
+        else
+          return new And(LHS, RHS);
+      }
+    default:
+      break;
+    }
+  } else if (const auto *UOE = dyn_cast<UnaryOperator>(CurExpr)) {
+    if (UOE->getOpcode() == UO_LNot) {
+      auto *E = buildLExpr(Arena, UOE->getSubExpr());
+      return new Not(E);
+    }
+  } else if (const auto *CE = dyn_cast<CastExpr>(CurExpr)) {
+    return buildLExpr(Arena, CE->getSubExpr());
+  } else if (const auto *PE = dyn_cast<ParenExpr>(CurExpr)) {
+    return buildLExpr(Arena, PE->getSubExpr());
+  }
+
+  return nullptr;
+}
+
 /// \brief A short list of SExprs
 class MutexIDList : public SmallVector<SExpr, 3> {
 public:
diff --git a/lib/Analysis/ThreadSafetyLogical.cpp b/lib/Analysis/ThreadSafetyLogical.cpp
new file mode 100644 (file)
index 0000000..51a8077
--- /dev/null
@@ -0,0 +1,112 @@
+//===- ThreadSafetyLogical.cpp ---------------------------------*- C++ --*-===//\r
+//\r
+//                     The LLVM Compiler Infrastructure\r
+//\r
+// This file is distributed under the University of Illinois Open Source\r
+// License. See LICENSE.TXT for details.\r
+//\r
+//===----------------------------------------------------------------------===//\r
+// This file defines a representation for logical expressions with SExpr leaves\r
+// that are used as part of fact-checking capability expressions.\r
+//===----------------------------------------------------------------------===//\r
+\r
+#include "clang/Analysis/Analyses/ThreadSafetyLogical.h"\r
+\r
+using namespace llvm;\r
+using namespace clang::threadSafety::lexpr;\r
+\r
+// Implication.  We implement De Morgan's Laws by maintaining LNeg and RNeg\r
+// to keep track of whether LHS and RHS are negated.\r
+static bool implies(const LExpr *LHS, bool LNeg, const LExpr *RHS, bool RNeg) {\r
+  // In comments below, we write => for implication.\r
+\r
+  // Calculates the logical AND implication operator.\r
+  const auto LeftAndOperator = [=](const BinOp *A) {\r
+    return implies(A->left(), LNeg, RHS, RNeg) &&\r
+           implies(A->right(), LNeg, RHS, RNeg);\r
+  };\r
+  const auto RightAndOperator = [=](const BinOp *A) {\r
+    return implies(LHS, LNeg, A->left(), RNeg) &&\r
+           implies(LHS, LNeg, A->right(), RNeg);\r
+  };\r
+\r
+  // Calculates the logical OR implication operator.\r
+  const auto LeftOrOperator = [=](const BinOp *A) {\r
+    return implies(A->left(), LNeg, RHS, RNeg) ||\r
+           implies(A->right(), LNeg, RHS, RNeg);\r
+  };\r
+  const auto RightOrOperator = [=](const BinOp *A) {\r
+    return implies(LHS, LNeg, A->left(), RNeg) ||\r
+           implies(LHS, LNeg, A->right(), RNeg);\r
+  };\r
+\r
+  // Recurse on right.\r
+  switch (RHS->kind()) {\r
+  case LExpr::And:\r
+    // When performing right recursion:\r
+    //   C => A & B  [if]  C => A and C => B\r
+    // When performing right recursion (negated):\r
+    //   C => !(A & B)  [if]  C => !A | !B  [===]  C => !A or C => !B\r
+    return RNeg ? RightOrOperator(cast<And>(RHS))\r
+                : RightAndOperator(cast<And>(RHS));\r
+  case LExpr::Or:\r
+    // When performing right recursion:\r
+    //   C => (A | B)  [if]  C => A or C => B\r
+    // When performing right recursion (negated):\r
+    //   C => !(A | B)  [if]  C => !A & !B  [===]  C => !A and C => !B\r
+    return RNeg ? RightAndOperator(cast<Or>(RHS))\r
+                : RightOrOperator(cast<Or>(RHS));\r
+  case LExpr::Not:\r
+    // Note that C => !A is very different from !(C => A). It would be incorrect\r
+    // to return !implies(LHS, RHS).\r
+    return implies(LHS, LNeg, cast<Not>(RHS)->exp(), !RNeg);\r
+  case LExpr::Terminal:\r
+    // After reaching the terminal, it's time to recurse on the left.\r
+    break;\r
+  }\r
+\r
+  // RHS is now a terminal.  Recurse on Left.\r
+  switch (LHS->kind()) {\r
+  case LExpr::And:\r
+    // When performing left recursion:\r
+    //   A & B => C  [if]  A => C or B => C\r
+    // When performing left recursion (negated):\r
+    //   !(A & B) => C  [if]  !A | !B => C  [===]  !A => C and !B => C\r
+    return LNeg ? LeftAndOperator(cast<And>(LHS))\r
+                : LeftOrOperator(cast<And>(LHS));\r
+  case LExpr::Or:\r
+    // When performing left recursion:\r
+    //   A | B => C  [if]  A => C and B => C\r
+    // When performing left recursion (negated):\r
+    //   !(A | B) => C  [if]  !A & !B => C  [===]  !A => C or !B => C\r
+    return LNeg ? LeftOrOperator(cast<Or>(LHS))\r
+                : LeftAndOperator(cast<Or>(LHS));\r
+  case LExpr::Not:\r
+    // Note that A => !C is very different from !(A => C). It would be incorrect\r
+    // to return !implies(LHS, RHS).\r
+    return implies(cast<Not>(LHS)->exp(), !LNeg, RHS, RNeg);\r
+  case LExpr::Terminal:\r
+    // After reaching the terminal, it's time to perform identity comparisons.\r
+    break;\r
+  }\r
+\r
+  // A => A\r
+  // !A => !A\r
+  if (LNeg != RNeg)\r
+    return false;\r
+\r
+  // FIXME -- this should compare SExprs for equality, not pointer equality.\r
+  return cast<Terminal>(LHS)->expr() == cast<Terminal>(RHS)->expr();\r
+}\r
+\r
+namespace clang {\r
+namespace threadSafety {\r
+namespace lexpr {\r
+\r
+bool implies(const LExpr *LHS, const LExpr *RHS) {\r
+  // Start out by assuming that LHS and RHS are not negated.\r
+  return ::implies(LHS, false, RHS, false);\r
+}\r
+}\r
+}\r
+}\r