From: DeLesley Hutchins Date: Thu, 15 May 2014 00:50:36 +0000 (+0000) Subject: Thread Safety Analysis: add new node types to thread safety TIL. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=be8977c750ba797dc880972c2ea9f6f7ef6cb9e3;p=clang Thread Safety Analysis: add new node types to thread safety TIL. This fills in a few missing gaps in functionality. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@208830 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Analysis/Analyses/ThreadSafetyCommon.h b/include/clang/Analysis/Analyses/ThreadSafetyCommon.h index c01114fd86..a83cbc5fe7 100644 --- a/include/clang/Analysis/Analyses/ThreadSafetyCommon.h +++ b/include/clang/Analysis/Analyses/ThreadSafetyCommon.h @@ -238,7 +238,8 @@ public: : Arena(A), SelfVar(nullptr), Scfg(nullptr), CurrentBB(nullptr), CurrentBlockInfo(nullptr) { // FIXME: we don't always have a self-variable. - SelfVar = new (Arena) til::Variable(til::Variable::VK_SFun); + SelfVar = new (Arena) til::Variable(); + SelfVar->setKind(til::Variable::VK_SFun); } // Translate a clang statement or expression to a TIL expression. @@ -268,9 +269,12 @@ private: CallingContext *Ctx); til::SExpr *translateUnaryOperator(const UnaryOperator *UO, CallingContext *Ctx); + til::SExpr *translateBinOp(til::TIL_BinaryOpcode Op, + const BinaryOperator *BO, + CallingContext *Ctx, bool Reverse = false); til::SExpr *translateBinAssign(til::TIL_BinaryOpcode Op, const BinaryOperator *BO, - CallingContext *Ctx); + CallingContext *Ctx, bool Assign = false); til::SExpr *translateBinaryOperator(const BinaryOperator *BO, CallingContext *Ctx); til::SExpr *translateCastExpr(const CastExpr *CE, CallingContext *Ctx); diff --git a/include/clang/Analysis/Analyses/ThreadSafetyOps.def b/include/clang/Analysis/Analyses/ThreadSafetyOps.def index 42184d8246..57b02f7c3f 100644 --- a/include/clang/Analysis/Analyses/ThreadSafetyOps.def +++ b/include/clang/Analysis/Analyses/ThreadSafetyOps.def @@ -24,6 +24,7 @@ TIL_OPCODE_DEF(Variable) TIL_OPCODE_DEF(Function) TIL_OPCODE_DEF(SFunction) TIL_OPCODE_DEF(Code) +TIL_OPCODE_DEF(Field) TIL_OPCODE_DEF(Apply) TIL_OPCODE_DEF(SApply) @@ -44,3 +45,9 @@ TIL_OPCODE_DEF(SCFG) TIL_OPCODE_DEF(Phi) TIL_OPCODE_DEF(Goto) TIL_OPCODE_DEF(Branch) + +// psuedo-terms +TIL_OPCODE_DEF(Identifier) +TIL_OPCODE_DEF(IfThenElse) +TIL_OPCODE_DEF(Let) + diff --git a/include/clang/Analysis/Analyses/ThreadSafetyTIL.h b/include/clang/Analysis/Analyses/ThreadSafetyTIL.h index 252eb7566e..93dc2ffcbf 100644 --- a/include/clang/Analysis/Analyses/ThreadSafetyTIL.h +++ b/include/clang/Analysis/Analyses/ThreadSafetyTIL.h @@ -3,13 +3,17 @@ // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// License. See LICENSE.TXT in the llvm repository for details. // //===----------------------------------------------------------------------===// // -// This file defines a simple intermediate language that is used by the -// thread safety analysis (See ThreadSafety.cpp). The thread safety analysis -// works by comparing mutex expressions, e.g. +// This file defines a simple Typed Intermediate Language, or TIL, that is used +// by the thread safety analysis (See ThreadSafety.cpp). The TIL is intended +// to be largely independent of clang, in the hope that the analysis can be +// reused for other non-C++ languages. All dependencies on clang/llvm should +// go in ThreadSafetyUtil.h. +// +// Thread safety analysis works by comparing mutex expressions, e.g. // // class A { Mutex mu; int dat GUARDED_BY(this->mu); } // class B { A a; } @@ -31,7 +35,7 @@ // (3) wildcards and pattern matching over expressions // (4) hash-based expression lookup // -// The IL is currently very experimental, is intended only for use within +// The TIL is currently very experimental, is intended only for use within // the thread safety analysis, and is subject to change without notice. // After the API stabilizes and matures, it may be appropriate to make this // more generally available to other analyses. @@ -43,11 +47,11 @@ #ifndef LLVM_CLANG_THREAD_SAFETY_TIL_H #define LLVM_CLANG_THREAD_SAFETY_TIL_H -#include "clang/Analysis/Analyses/ThreadSafetyUtil.h" -#include "clang/AST/ExprCXX.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/Support/Compiler.h" +// All clang include dependencies for this file must be put in +// ThreadSafetyUtil.h. +#include "ThreadSafetyUtil.h" +#include #include #include #include @@ -57,21 +61,189 @@ namespace clang { namespace threadSafety { namespace til { -using llvm::StringRef; -using clang::SourceLocation; - enum TIL_Opcode { #define TIL_OPCODE_DEF(X) COP_##X, -#include "clang/Analysis/Analyses/ThreadSafetyOps.def" +#include "ThreadSafetyOps.def" #undef TIL_OPCODE_DEF - COP_MAX }; +enum TIL_UnaryOpcode : unsigned char { + UOP_Minus, // - + UOP_BitNot, // ~ + UOP_LogicNot // ! +}; + +enum TIL_BinaryOpcode : unsigned char { + BOP_Mul, // * + BOP_Div, // / + BOP_Rem, // % + BOP_Add, // + + BOP_Sub, // - + BOP_Shl, // << + BOP_Shr, // >> + BOP_BitAnd, // & + BOP_BitXor, // ^ + BOP_BitOr, // | + BOP_Eq, // == + BOP_Neq, // != + BOP_Lt, // < + BOP_Leq, // <= + BOP_LogicAnd, // && + BOP_LogicOr // || +}; + +enum TIL_CastOpcode : unsigned char { + CAST_none = 0, + CAST_extendNum, // extend precision of numeric type + CAST_truncNum, // truncate precision of numeric type + CAST_toFloat, // convert to floating point type + CAST_toInt, // convert to integer type +}; + +const TIL_Opcode COP_Min = COP_Future; +const TIL_Opcode COP_Max = COP_Branch; +const TIL_UnaryOpcode UOP_Min = UOP_Minus; +const TIL_UnaryOpcode UOP_Max = UOP_LogicNot; +const TIL_BinaryOpcode BOP_Min = BOP_Mul; +const TIL_BinaryOpcode BOP_Max = BOP_LogicOr; +const TIL_CastOpcode CAST_Min = CAST_none; +const TIL_CastOpcode CAST_Max = CAST_toInt; + +StringRef getUnaryOpcodeString(TIL_UnaryOpcode Op); +StringRef getBinaryOpcodeString(TIL_BinaryOpcode Op); + + +// ValueTypes are data types that can actually be held in registers. +// All variables and expressions must have a vBNF_Nonealue type. +// Pointer types are further subdivided into the various heap-allocated +// types, such as functions, records, etc. +// Structured types that are passed by value (e.g. complex numbers) +// require special handling; they use BT_ValueRef, and size ST_0. +struct ValueType { + enum BaseType : unsigned char { + BT_Void = 0, + BT_Bool, + BT_Int, + BT_Float, + BT_String, // String literals + BT_Pointer, + BT_ValueRef + }; + + enum SizeType : unsigned char { + ST_0 = 0, + ST_1, + ST_8, + ST_16, + ST_32, + ST_64, + ST_128 + }; + + inline static SizeType getSizeType(unsigned nbytes); + + template + inline static ValueType getValueType(); + + ValueType(BaseType B, SizeType Sz, bool S, unsigned char VS) + : Base(B), Size(Sz), Signed(S), VectSize(VS) + { } + + BaseType Base; + SizeType Size; + bool Signed; + unsigned char VectSize; // 0 for scalar, otherwise num elements in vector +}; + + +inline ValueType::SizeType ValueType::getSizeType(unsigned nbytes) { + switch (nbytes) { + case 1: return ST_8; + case 2: return ST_16; + case 4: return ST_32; + case 8: return ST_64; + case 16: return ST_128; + default: return ST_0; + } +} + + +template<> +inline ValueType ValueType::getValueType() { + return ValueType(BT_Void, ST_0, false, 0); +} + +template<> +inline ValueType ValueType::getValueType() { + return ValueType(BT_Bool, ST_1, false, 0); +} + +template<> +inline ValueType ValueType::getValueType() { + return ValueType(BT_Int, ST_8, true, 0); +} + +template<> +inline ValueType ValueType::getValueType() { + return ValueType(BT_Int, ST_8, false, 0); +} + +template<> +inline ValueType ValueType::getValueType() { + return ValueType(BT_Int, ST_16, true, 0); +} + +template<> +inline ValueType ValueType::getValueType() { + return ValueType(BT_Int, ST_16, false, 0); +} + +template<> +inline ValueType ValueType::getValueType() { + return ValueType(BT_Int, ST_32, true, 0); +} + +template<> +inline ValueType ValueType::getValueType() { + return ValueType(BT_Int, ST_32, false, 0); +} + +template<> +inline ValueType ValueType::getValueType() { + return ValueType(BT_Int, ST_64, true, 0); +} + +template<> +inline ValueType ValueType::getValueType() { + return ValueType(BT_Int, ST_64, false, 0); +} + +template<> +inline ValueType ValueType::getValueType() { + return ValueType(BT_Float, ST_32, true, 0); +} + +template<> +inline ValueType ValueType::getValueType() { + return ValueType(BT_Float, ST_64, true, 0); +} + +template<> +inline ValueType ValueType::getValueType() { + return ValueType(BT_Float, ST_128, true, 0); +} + +template<> +inline ValueType ValueType::getValueType() { + return ValueType(BT_Pointer, getSizeType(sizeof(StringRef)), false, 0); +} + +template<> +inline ValueType ValueType::getValueType() { + return ValueType(BT_Pointer, getSizeType(sizeof(void*)), false, 0); +} -typedef clang::BinaryOperatorKind TIL_BinaryOpcode; -typedef clang::UnaryOperatorKind TIL_UnaryOpcode; -typedef clang::CastKind TIL_CastOpcode; enum TraversalKind { @@ -100,7 +272,7 @@ public: // compare all subexpressions, following the comparator interface // } - void *operator new(size_t S, clang::threadSafety::til::MemRegionRef &R) { + void *operator new(size_t S, MemRegionRef &R) { return ::operator new(S, R); } @@ -149,10 +321,10 @@ public: bool operator==(const SExprRef &R) const { return Ptr == R.Ptr; } bool operator!=(const SExprRef &R) const { return !operator==(R); } - bool operator==(const SExpr *P) const { return Ptr == P; } - bool operator!=(const SExpr *P) const { return !operator==(P); } - bool operator==(std::nullptr_t) const { return Ptr == nullptr; } - bool operator!=(std::nullptr_t) const { return Ptr != nullptr; } + bool operator==(const SExpr *P) const { return Ptr == P; } + bool operator!=(const SExpr *P) const { return !operator==(P); } + bool operator==(std::nullptr_t) const { return Ptr == nullptr; } + bool operator!=(std::nullptr_t) const { return Ptr != nullptr; } inline void reset(SExpr *E); @@ -172,9 +344,11 @@ namespace ThreadSafetyTIL { } } +// Nodes which declare variables class Function; class SFunction; class BasicBlock; +class Let; // A named variable, e.g. "x". @@ -201,14 +375,13 @@ public: }; // These are defined after SExprRef contructor, below - inline Variable(VariableKind K, SExpr *D = nullptr, - const clang::ValueDecl *Cvd = nullptr); + inline Variable(StringRef s, SExpr *D = nullptr); inline Variable(SExpr *D = nullptr, const clang::ValueDecl *Cvd = nullptr); inline Variable(const Variable &Vd, SExpr *D); VariableKind kind() const { return static_cast(Flags); } - const StringRef name() const { return Cvdecl ? Cvdecl->getName() : "_x"; } + const StringRef name() const { return Name; } const clang::ValueDecl *clangDecl() const { return Cvdecl; } // Returns the definition (for let vars) or type (for parameter & self vars) @@ -227,6 +400,7 @@ public: } void setClangDecl(const clang::ValueDecl *VD) { Cvdecl = VD; } void setDefinition(SExpr *E); + void setKind(VariableKind K) { Flags = K; } template typename V::R_SExpr traverse(V &Visitor) { // This routine is only called for variable references. @@ -241,11 +415,10 @@ private: friend class Function; friend class SFunction; friend class BasicBlock; + friend class Let; - // Function, SFunction, and BasicBlock will reset the kind. - void setKind(VariableKind K) { Flags = K; } - - SExprRef Definition; // The TIL type or definition + StringRef Name; // The name of the variable. + SExprRef Definition; // The TIL type or definition const clang::ValueDecl *Cvdecl; // The clang declaration for this variable. unsigned short BlockID; @@ -355,20 +528,20 @@ inline void SExprRef::reset(SExpr *P) { } -inline Variable::Variable(VariableKind K, SExpr *D, const clang::ValueDecl *Cvd) - : SExpr(COP_Variable), Definition(D), Cvdecl(Cvd), - BlockID(0), Id(0), NumUses(0) { - Flags = K; +inline Variable::Variable(StringRef s, SExpr *D) + : SExpr(COP_Variable), Name(s), Definition(D), Cvdecl(nullptr), + BlockID(0), Id(0), NumUses(0) { + Flags = VK_Let; } inline Variable::Variable(SExpr *D, const clang::ValueDecl *Cvd) - : SExpr(COP_Variable), Definition(D), Cvdecl(Cvd), - BlockID(0), Id(0), NumUses(0) { + : SExpr(COP_Variable), Name(Cvd ? Cvd->getName() : "_x"), + Definition(D), Cvdecl(Cvd), BlockID(0), Id(0), NumUses(0) { Flags = VK_Let; } inline Variable::Variable(const Variable &Vd, SExpr *D) // rewrite constructor - : SExpr(Vd), Definition(D), Cvdecl(Vd.Cvdecl), + : SExpr(Vd), Name(Vd.Name), Definition(D), Cvdecl(Vd.Cvdecl), BlockID(0), Id(0), NumUses(0) { Flags = Vd.kind(); } @@ -431,8 +604,11 @@ class Literal : public SExpr { public: static bool classof(const SExpr *E) { return E->opcode() == COP_Literal; } - Literal(const clang::Expr *C) : SExpr(COP_Literal), Cexpr(C) {} - Literal(const Literal &L) : SExpr(L), Cexpr(L.Cexpr) {} + Literal(const clang::Expr *C) + : SExpr(COP_Literal), ValType(ValueType::getValueType()) + { } + Literal(ValueType VT) : SExpr(COP_Literal), ValType(VT) {} + Literal(const Literal &L) : SExpr(L), ValType(L.ValType), Cexpr(L.Cexpr) {} // The clang expression for this literal. const clang::Expr *clangExpr() const { return Cexpr; } @@ -447,9 +623,26 @@ public: } private: + ValueType ValType; const clang::Expr *Cexpr; }; + +// Derived class for literal values, which stores the actual value. +template +class LiteralT : public Literal { +public: + LiteralT(T Dat) : Literal(ValueType::getValueType()), Val(Dat) { } + LiteralT(const LiteralT &L) : Literal(L), Val(L.Val) { } + + T value() const { return Val;} + T& value() { return Val; } + +private: + T Val; +}; + + // Literal pointer to an object allocated in memory. // At compile time, pointer literals are represented by symbolic names. class LiteralPtr : public SExpr { @@ -474,6 +667,7 @@ private: const clang::ValueDecl *Cvdecl; }; + // A function -- a.k.a. lambda abstraction. // Functions with multiple arguments are created by currying, // e.g. (function (x: Int) (function (y: Int) (add x y))) @@ -607,6 +801,40 @@ private: }; +// A typed, writable location in memory +class Field : public SExpr { +public: + static bool classof(const SExpr *E) { return E->opcode() == COP_Field; } + + Field(SExpr *R, SExpr *B) : SExpr(COP_Field), Range(R), Body(B) {} + Field(const Field &C, SExpr *R, SExpr *B) // rewrite constructor + : SExpr(C), Range(R), Body(B) {} + + SExpr *range() { return Range.get(); } + const SExpr *range() const { return Range.get(); } + + SExpr *body() { return Body.get(); } + const SExpr *body() const { return Body.get(); } + + template typename V::R_SExpr traverse(V &Visitor) { + typename V::R_SExpr Nr = Visitor.traverse(Range, TRV_Lazy); + typename V::R_SExpr Nb = Visitor.traverse(Body, TRV_Lazy); + return Visitor.reduceField(*this, Nr, Nb); + } + + template typename C::CType compare(Field* E, C& Cmp) { + typename C::CType Ct = Cmp.compare(range(), E->range()); + if (Cmp.notTrue(Ct)) + return Ct; + return Cmp.compare(body(), E->body()); + } + +private: + SExprRef Range; + SExprRef Body; +}; + + // Apply an argument to a function class Apply : public SExpr { public: @@ -683,9 +911,15 @@ class Project : public SExpr { public: static bool classof(const SExpr *E) { return E->opcode() == COP_Project; } + Project(SExpr *R, StringRef SName) + : SExpr(COP_Project), Rec(R), SlotName(SName), Cvdecl(nullptr) + { } Project(SExpr *R, clang::ValueDecl *Cvd) - : SExpr(COP_Project), Rec(R), Cvdecl(Cvd) {} - Project(const Project &P, SExpr *R) : SExpr(P), Rec(R), Cvdecl(P.Cvdecl) {} + : SExpr(COP_Project), Rec(R), SlotName(Cvd->getName()), Cvdecl(Cvd) + { } + Project(const Project &P, SExpr *R) + : SExpr(P), Rec(R), SlotName(P.SlotName), Cvdecl(P.Cvdecl) + { } SExpr *record() { return Rec.get(); } const SExpr *record() const { return Rec.get(); } @@ -708,6 +942,7 @@ public: private: SExprRef Rec; + StringRef SlotName; clang::ValueDecl *Cvdecl; }; @@ -1300,10 +1535,125 @@ private: }; -SExpr *getCanonicalVal(SExpr *E); -void simplifyIncompleteArg(Variable *V, til::Phi *Ph); +// An identifier, e.g. 'foo' or 'x'. +// This is a pseduo-term; it will be lowered to a variable or projection. +class Identifier : public SExpr { +public: + static bool classof(const SExpr *E) { return E->opcode() == COP_Identifier; } + + Identifier(StringRef Id): SExpr(COP_Identifier), Name(Id) { } + Identifier(const Identifier& I) : SExpr(I), Name(I.Name) { } + + StringRef name() const { return Name; } + + template typename V::R_SExpr traverse(V &Visitor) { + return Visitor.reduceIdentifier(*this); + } + + template typename C::CType compare(Identifier* E, C& Cmp) { + return Cmp.compareStrings(name(), E->name()); + } + +private: + StringRef Name; +}; + + +// An if-then-else expression. +// This is a pseduo-term; it will be lowered to a CFG. +class IfThenElse : public SExpr { +public: + static bool classof(const SExpr *E) { return E->opcode() == COP_IfThenElse; } + + IfThenElse(SExpr *C, SExpr *T, SExpr *E) + : SExpr(COP_IfThenElse), Condition(C), ThenExpr(T), ElseExpr(E) + { } + IfThenElse(const IfThenElse &I, SExpr *C, SExpr *T, SExpr *E) + : SExpr(I), Condition(C), ThenExpr(T), ElseExpr(E) + { } + + SExpr *condition() { return Condition.get(); } // Address to store to + const SExpr *condition() const { return Condition.get(); } + + SExpr *thenExpr() { return ThenExpr.get(); } // Value to store + const SExpr *thenExpr() const { return ThenExpr.get(); } + + SExpr *elseExpr() { return ElseExpr.get(); } // Value to store + const SExpr *elseExpr() const { return ElseExpr.get(); } + + template typename V::R_SExpr traverse(V &Visitor) { + typename V::R_SExpr Nc = Visitor.traverse(Condition); + typename V::R_SExpr Nt = Visitor.traverse(ThenExpr); + typename V::R_SExpr Ne = Visitor.traverse(ElseExpr); + return Visitor.reduceIfThenElse(*this, Nc, Nt, Ne); + } + + template typename C::CType compare(IfThenElse* E, C& Cmp) { + typename C::CType Ct = Cmp.compare(condition(), E->condition()); + if (Cmp.notTrue(Ct)) + return Ct; + Ct = Cmp.compare(thenExpr(), E->thenExpr()); + if (Cmp.notTrue(Ct)) + return Ct; + return Cmp.compare(elseExpr(), E->elseExpr()); + } + +private: + SExprRef Condition; + SExprRef ThenExpr; + SExprRef ElseExpr; +}; + + +// A let-expression, e.g. let x=t; u. +// This is a pseduo-term; it will be lowered to a CFG. +class Let : public SExpr { +public: + static bool classof(const SExpr *E) { return E->opcode() == COP_Let; } + + Let(Variable *Vd, SExpr *Bd) : SExpr(COP_Let), VarDecl(Vd), Body(Bd) { + Vd->setKind(Variable::VK_Let); + } + Let(const Let &L, Variable *Vd, SExpr *Bd) : SExpr(L), VarDecl(Vd), Body(Bd) { + Vd->setKind(Variable::VK_Let); + } + + Variable *variableDecl() { return VarDecl; } + const Variable *variableDecl() const { return VarDecl; } + SExpr *body() { return Body.get(); } + const SExpr *body() const { return Body.get(); } + + template typename V::R_SExpr traverse(V &Visitor) { + // This is a variable declaration, so traverse the definition. + typename V::R_SExpr E0 = Visitor.traverse(VarDecl->Definition, TRV_Lazy); + // Tell the rewriter to enter the scope of the let variable. + Variable *Nvd = Visitor.enterScope(*VarDecl, E0); + typename V::R_SExpr E1 = Visitor.traverse(Body); + Visitor.exitScope(*VarDecl); + return Visitor.reduceLet(*this, Nvd, E1); + } + template typename C::CType compare(Let* E, C& Cmp) { + typename C::CType Ct = + Cmp.compare(VarDecl->definition(), E->VarDecl->definition()); + if (Cmp.notTrue(Ct)) + return Ct; + Cmp.enterScope(variableDecl(), E->variableDecl()); + Ct = Cmp.compare(body(), E->body()); + Cmp.leaveScope(); + return Ct; + } + +private: + Variable *VarDecl; + SExprRef Body; +}; + + + +SExpr *getCanonicalVal(SExpr *E); +void simplifyIncompleteArg(Variable *V, til::Phi *Ph); } // end namespace til diff --git a/include/clang/Analysis/Analyses/ThreadSafetyTraverse.h b/include/clang/Analysis/Analyses/ThreadSafetyTraverse.h index f7b94d5a4a..7973e0b365 100644 --- a/include/clang/Analysis/Analyses/ThreadSafetyTraverse.h +++ b/include/clang/Analysis/Analyses/ThreadSafetyTraverse.h @@ -17,7 +17,7 @@ #ifndef LLVM_CLANG_THREAD_SAFETY_TRAVERSE_H #define LLVM_CLANG_THREAD_SAFETY_TRAVERSE_H -#include "clang/Analysis/Analyses/ThreadSafetyTIL.h" +#include "ThreadSafetyTIL.h" namespace clang { namespace threadSafety { @@ -72,10 +72,8 @@ public: #define TIL_OPCODE_DEF(X) \ case COP_##X: \ return self()->traverse##X(cast(E)); -#include "clang/Analysis/Analyses/ThreadSafetyOps.def" +#include "ThreadSafetyOps.def" #undef TIL_OPCODE_DEF - case COP_MAX: - return self()->reduceNull(); } } @@ -83,7 +81,7 @@ public: // Override these methods to do something for a particular kind of term. #define TIL_OPCODE_DEF(X) \ typename R::R_SExpr traverse##X(X *e) { return e->traverse(*self()); } -#include "clang/Analysis/Analyses/ThreadSafetyOps.def" +#include "ThreadSafetyOps.def" #undef TIL_OPCODE_DEF }; @@ -146,6 +144,9 @@ public: R_SExpr reduceCode(Code &Orig, R_SExpr E0, R_SExpr E1) { return new (Arena) Code(Orig, E0, E1); } + R_SExpr reduceField(Field &Orig, R_SExpr E0, R_SExpr E1) { + return new (Arena) Field(Orig, E0, E1); + } R_SExpr reduceApply(Apply &Orig, R_SExpr E0, R_SExpr E1) { return new (Arena) Apply(Orig, E0, E1); @@ -198,6 +199,16 @@ public: return new (Arena) Branch(O, C, B0, B1); } + R_SExpr reduceIdentifier(Identifier &Orig) { + return new (Arena) Identifier(Orig); + } + R_SExpr reduceIfThenElse(IfThenElse &Orig, R_SExpr C, R_SExpr T, R_SExpr E) { + return new (Arena) IfThenElse(Orig, C, T, E); + } + R_SExpr reduceLet(Let &Orig, Variable *Nvd, R_SExpr B) { + return new (Arena) Let(Orig, Nvd, B); + } + BasicBlock *reduceBasicBlock(BasicBlock &Orig, Container &As, Container &Is, R_SExpr T) { return new (Arena) BasicBlock(Orig, std::move(As.Elems), @@ -274,6 +285,9 @@ public: R_SExpr reduceCode(Code &Orig, R_SExpr E0, R_SExpr E1) { return E0 && E1; } + R_SExpr reduceField(Field &Orig, R_SExpr E0, R_SExpr E1) { + return E0 && E1; + } R_SExpr reduceApply(Apply &Orig, R_SExpr E0, R_SExpr E1) { return E0 && E1; } @@ -308,6 +322,16 @@ public: return C; } + R_SExpr reduceIdentifier(Identifier &Orig) { + return true; + } + R_SExpr reduceIfThenElse(IfThenElse &Orig, R_SExpr C, R_SExpr T, R_SExpr E) { + return C && T && E; + } + R_SExpr reduceLet(Let &Orig, Variable *Nvd, R_SExpr B) { + return Nvd && B; + } + BasicBlock *reduceBasicBlock(BasicBlock &Orig, Container &As, Container &Is, R_SExpr T) { return (As.Success && Is.Success && T) ? &Orig : nullptr; @@ -360,10 +384,8 @@ public: #define TIL_OPCODE_DEF(X) \ case COP_##X: \ return cast(E1)->compare(cast(E2), *self()); -#include "clang/Analysis/Analyses/ThreadSafetyOps.def" +#include "ThreadSafetyOps.def" #undef TIL_OPCODE_DEF - case COP_MAX: - return false; } } }; @@ -379,7 +401,8 @@ public: CType trueResult() { return true; } bool notTrue(CType ct) { return !ct; } - bool compareIntegers(unsigned i, unsigned j) { return i == j; } + bool compareIntegers(unsigned i, unsigned j) { return i == j; } + bool compareStrings (StringRef s, StringRef r) { return s == r; } bool comparePointers(const void* P, const void* Q) { return P == Q; } bool compare(SExpr *E1, SExpr* E2) { @@ -455,6 +478,7 @@ protected: case COP_Function: return Prec_Decl; case COP_SFunction: return Prec_Decl; case COP_Code: return Prec_Decl; + case COP_Field: return Prec_Decl; case COP_Apply: return Prec_Postfix; case COP_SApply: return Prec_Postfix; @@ -475,7 +499,10 @@ protected: case COP_Phi: return Prec_Atom; case COP_Goto: return Prec_Atom; case COP_Branch: return Prec_Atom; - case COP_MAX: return Prec_MAX; + + case COP_Identifier: return Prec_Atom; + case COP_IfThenElse: return Prec_Other; + case COP_Let: return Prec_Decl; } return Prec_MAX; } @@ -498,10 +525,8 @@ protected: case COP_##X: \ self()->print##X(cast(E), SS); \ return; -#include "clang/Analysis/Analyses/ThreadSafetyOps.def" +#include "ThreadSafetyOps.def" #undef TIL_OPCODE_DEF - case COP_MAX: - return; } } @@ -522,25 +547,7 @@ protected: } void printLiteral(Literal *E, StreamType &SS) { - const clang::Expr *CE = E->clangExpr(); - switch (CE->getStmtClass()) { - case Stmt::IntegerLiteralClass: - SS << cast(CE)->getValue().toString(10, true); - return; - case Stmt::StringLiteralClass: - SS << "\"" << cast(CE)->getString() << "\""; - return; - case Stmt::CharacterLiteralClass: - case Stmt::CXXNullPtrLiteralExprClass: - case Stmt::GNUNullExprClass: - case Stmt::CXXBoolLiteralExprClass: - case Stmt::FloatingLiteralClass: - case Stmt::ImaginaryLiteralClass: - case Stmt::ObjCStringLiteralClass: - default: - SS << "#lit"; - return; - } + SS << getSourceLiteralString(E->clangExpr()); } void printLiteralPtr(LiteralPtr *E, StreamType &SS) { @@ -596,6 +603,13 @@ protected: void printCode(Code *E, StreamType &SS) { SS << ": "; self()->printSExpr(E->returnType(), SS, Prec_Decl-1); + SS << " -> "; + self()->printSExpr(E->body(), SS, Prec_Decl); + } + + void printField(Field *E, StreamType &SS) { + SS << ": "; + self()->printSExpr(E->range(), SS, Prec_Decl-1); SS << " = "; self()->printSExpr(E->body(), SS, Prec_Decl); } @@ -659,7 +673,7 @@ protected: void printArrayFirst(ArrayFirst *E, StreamType &SS) { self()->printSExpr(E->array(), SS, Prec_Postfix); - if (ArrayAdd *A = dyn_cast_or_null(E)) { + if (ArrayAdd *A = dyn_cast_or_null(E->array())) { SS << "["; printSExpr(A->index(), SS, Prec_MAX); SS << "]"; @@ -675,17 +689,18 @@ protected: } void printUnaryOp(UnaryOp *E, StreamType &SS) { + SS << getUnaryOpcodeString(E->unaryOpcode()); self()->printSExpr(E->expr(), SS, Prec_Unary); } void printBinaryOp(BinaryOp *E, StreamType &SS) { self()->printSExpr(E->expr0(), SS, Prec_Binary-1); - SS << " " << clang::BinaryOperator::getOpcodeStr(E->binaryOpcode()) << " "; + SS << " " << getBinaryOpcodeString(E->binaryOpcode()) << " "; self()->printSExpr(E->expr1(), SS, Prec_Binary-1); } void printCast(Cast *E, StreamType &SS) { - SS << "~"; + SS << "%"; self()->printSExpr(E->expr(), SS, Prec_Unary); } @@ -752,6 +767,28 @@ protected: SS << " "; printBlockLabel(SS, E->elseBlock(), E->elseIndex()); } + + void printIdentifier(Identifier *E, StreamType &SS) { + SS << "$" << E->name(); + } + + void printIfThenElse(IfThenElse *E, StreamType &SS) { + SS << "if ("; + printSExpr(E->condition(), SS, Prec_MAX); + SS << ") then "; + printSExpr(E->thenExpr(), SS, Prec_Other); + SS << " else "; + printSExpr(E->elseExpr(), SS, Prec_Other); + } + + void printLet(Let *E, StreamType &SS) { + SS << "let "; + printVariable(E->variableDecl(), SS, true); + SS << " = "; + printSExpr(E->variableDecl()->definition(), SS, Prec_Decl-1); + SS << ";"; + printSExpr(E->body(), SS, Prec_Decl-1); + } }; diff --git a/include/clang/Analysis/Analyses/ThreadSafetyUtil.h b/include/clang/Analysis/Analyses/ThreadSafetyUtil.h index ece7425c22..986103df0f 100644 --- a/include/clang/Analysis/Analyses/ThreadSafetyUtil.h +++ b/include/clang/Analysis/Analyses/ThreadSafetyUtil.h @@ -17,6 +17,8 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Support/AlignOf.h" #include "llvm/Support/Allocator.h" +#include "llvm/Support/Compiler.h" +#include "clang/AST/ExprCXX.h" #include #include @@ -70,9 +72,14 @@ inline void *operator new(size_t Sz, namespace clang { namespace threadSafety { -namespace til { + +std::string getSourceLiteralString(const clang::Expr *CE); using llvm::StringRef; +using clang::SourceLocation; + +namespace til { + // A simple fixed size array class that does not manage its own memory, // suitable for use with bump pointer allocation. @@ -163,7 +170,7 @@ private: size_t Capacity; }; -} // end namespace til +} // end namespace til // A copy on write vector. diff --git a/lib/Analysis/CMakeLists.txt b/lib/Analysis/CMakeLists.txt index 89f8593307..5e97ce20cf 100644 --- a/lib/Analysis/CMakeLists.txt +++ b/lib/Analysis/CMakeLists.txt @@ -25,6 +25,7 @@ add_clang_library(clangAnalysis ThreadSafety.cpp ThreadSafetyCommon.cpp ThreadSafetyLogical.cpp + ThreadSafetyTIL.cpp UninitializedValues.cpp LINK_LIBS diff --git a/lib/Analysis/ThreadSafetyCommon.cpp b/lib/Analysis/ThreadSafetyCommon.cpp index fb96834b3e..91cf849536 100644 --- a/lib/Analysis/ThreadSafetyCommon.cpp +++ b/lib/Analysis/ThreadSafetyCommon.cpp @@ -37,65 +37,30 @@ namespace clang { namespace threadSafety { -namespace til { - -// If E is a variable, then trace back through any aliases or redundant -// Phi nodes to find the canonical definition. -SExpr *getCanonicalVal(SExpr *E) { - while (auto *V = dyn_cast(E)) { - SExpr *D; - do { - if (V->kind() != Variable::VK_Let) - return V; - D = V->definition(); - auto *V2 = dyn_cast(D); - if (V2) - V = V2; - else - break; - } while (true); - - if (ThreadSafetyTIL::isTrivial(D)) - return D; - - if (Phi *Ph = dyn_cast(D)) { - if (Ph->status() == Phi::PH_Incomplete) - simplifyIncompleteArg(V, Ph); - - if (Ph->status() == Phi::PH_SingleVal) { - E = Ph->values()[0]; - continue; - } - } - return V; - } - return E; -} - - -// Trace the arguments of an incomplete Phi node to see if they have the same -// canonical definition. If so, mark the Phi node as redundant. -// getCanonicalVal() will recursively call simplifyIncompletePhi(). -void simplifyIncompleteArg(Variable *V, til::Phi *Ph) { - assert(Ph && Ph->status() == Phi::PH_Incomplete); - - // eliminate infinite recursion -- assume that this node is not redundant. - Ph->setStatus(Phi::PH_MultiVal); - - SExpr *E0 = getCanonicalVal(Ph->values()[0]); - for (unsigned i=1, n=Ph->values().size(); ivalues()[i]); - if (Ei == V) - continue; // Recursive reference to itself. Don't count. - if (Ei != E0) { - return; // Status is already set to MultiVal. +// From ThreadSafetyUtil.h +std::string getSourceLiteralString(const clang::Expr *CE) { + switch (CE->getStmtClass()) { + case Stmt::IntegerLiteralClass: + return cast(CE)->getValue().toString(10, true); + case Stmt::StringLiteralClass: { + std::string ret("\""); + ret += cast(CE)->getString(); + ret += "\""; + return ret; } + case Stmt::CharacterLiteralClass: + case Stmt::CXXNullPtrLiteralExprClass: + case Stmt::GNUNullExprClass: + case Stmt::CXXBoolLiteralExprClass: + case Stmt::FloatingLiteralClass: + case Stmt::ImaginaryLiteralClass: + case Stmt::ObjCStringLiteralClass: + default: + return "#lit"; } - Ph->setStatus(Phi::PH_SingleVal); - // Eliminate Redundant Phi node. - V->setDefinition(Ph->values()[0]); } +namespace til { // Return true if E is a variable that points to an incomplete Phi node. static bool isIncompleteVar(const SExpr *E) { @@ -106,7 +71,6 @@ static bool isIncompleteVar(const SExpr *E) { return false; } - } // end namespace til @@ -281,21 +245,41 @@ til::SExpr *SExprBuilder::translateUnaryOperator(const UnaryOperator *UO, return translate(UO->getSubExpr(), Ctx); case UO_Minus: + return new (Arena) + til::UnaryOp(til::UOP_Minus, translate(UO->getSubExpr(), Ctx)); case UO_Not: + return new (Arena) + til::UnaryOp(til::UOP_BitNot, translate(UO->getSubExpr(), Ctx)); case UO_LNot: + return new (Arena) + til::UnaryOp(til::UOP_LogicNot, translate(UO->getSubExpr(), Ctx)); + + // Currently unsupported case UO_Real: case UO_Imag: case UO_Extension: - return new (Arena) - til::UnaryOp(UO->getOpcode(), translate(UO->getSubExpr(), Ctx)); + return new (Arena) til::Undefined(UO); } return new (Arena) til::Undefined(UO); } +til::SExpr *SExprBuilder::translateBinOp(til::TIL_BinaryOpcode Op, + const BinaryOperator *BO, + CallingContext *Ctx, bool Reverse) { + til::SExpr *E0 = translate(BO->getLHS(), Ctx); + til::SExpr *E1 = translate(BO->getRHS(), Ctx); + if (Reverse) + return new (Arena) til::BinaryOp(Op, E1, E0); + else + return new (Arena) til::BinaryOp(Op, E0, E1); +} + + til::SExpr *SExprBuilder::translateBinAssign(til::TIL_BinaryOpcode Op, const BinaryOperator *BO, - CallingContext *Ctx) { + CallingContext *Ctx, + bool Assign) { const Expr *LHS = BO->getLHS(); const Expr *RHS = BO->getRHS(); til::SExpr *E0 = translate(LHS, Ctx); @@ -308,7 +292,7 @@ til::SExpr *SExprBuilder::translateBinAssign(til::TIL_BinaryOpcode Op, CV = lookupVarDecl(VD); } - if (Op != BO_Assign) { + if (!Assign) { til::SExpr *Arg = CV ? CV : new (Arena) til::Load(E0); E1 = new (Arena) til::BinaryOp(Op, Arg, E1); E1 = addStatement(E1, nullptr, VD); @@ -326,39 +310,36 @@ til::SExpr *SExprBuilder::translateBinaryOperator(const BinaryOperator *BO, case BO_PtrMemI: return new (Arena) til::Undefined(BO); - case BO_Mul: - case BO_Div: - case BO_Rem: - case BO_Add: - case BO_Sub: - case BO_Shl: - case BO_Shr: - case BO_LT: - case BO_GT: - case BO_LE: - case BO_GE: - case BO_EQ: - case BO_NE: - case BO_And: - case BO_Xor: - case BO_Or: - case BO_LAnd: - case BO_LOr: - return new (Arena) - til::BinaryOp(BO->getOpcode(), translate(BO->getLHS(), Ctx), - translate(BO->getRHS(), Ctx)); - - case BO_Assign: return translateBinAssign(BO_Assign, BO, Ctx); - case BO_MulAssign: return translateBinAssign(BO_Mul, BO, Ctx); - case BO_DivAssign: return translateBinAssign(BO_Div, BO, Ctx); - case BO_RemAssign: return translateBinAssign(BO_Rem, BO, Ctx); - case BO_AddAssign: return translateBinAssign(BO_Add, BO, Ctx); - case BO_SubAssign: return translateBinAssign(BO_Sub, BO, Ctx); - case BO_ShlAssign: return translateBinAssign(BO_Shl, BO, Ctx); - case BO_ShrAssign: return translateBinAssign(BO_Shr, BO, Ctx); - case BO_AndAssign: return translateBinAssign(BO_And, BO, Ctx); - case BO_XorAssign: return translateBinAssign(BO_Xor, BO, Ctx); - case BO_OrAssign: return translateBinAssign(BO_Or, BO, Ctx); + case BO_Mul: return translateBinOp(til::BOP_Mul, BO, Ctx); + case BO_Div: return translateBinOp(til::BOP_Div, BO, Ctx); + case BO_Rem: return translateBinOp(til::BOP_Rem, BO, Ctx); + case BO_Add: return translateBinOp(til::BOP_Add, BO, Ctx); + case BO_Sub: return translateBinOp(til::BOP_Sub, BO, Ctx); + case BO_Shl: return translateBinOp(til::BOP_Shl, BO, Ctx); + case BO_Shr: return translateBinOp(til::BOP_Shr, BO, Ctx); + case BO_LT: return translateBinOp(til::BOP_Lt, BO, Ctx); + case BO_GT: return translateBinOp(til::BOP_Lt, BO, Ctx, true); + case BO_LE: return translateBinOp(til::BOP_Leq, BO, Ctx); + case BO_GE: return translateBinOp(til::BOP_Leq, BO, Ctx, true); + case BO_EQ: return translateBinOp(til::BOP_Eq, BO, Ctx); + case BO_NE: return translateBinOp(til::BOP_Neq, BO, Ctx); + case BO_And: return translateBinOp(til::BOP_BitAnd, BO, Ctx); + case BO_Xor: return translateBinOp(til::BOP_BitXor, BO, Ctx); + case BO_Or: return translateBinOp(til::BOP_BitOr, BO, Ctx); + case BO_LAnd: return translateBinOp(til::BOP_LogicAnd, BO, Ctx); + case BO_LOr: return translateBinOp(til::BOP_LogicOr, BO, Ctx); + + case BO_Assign: return translateBinAssign(til::BOP_Eq, BO, Ctx, true); + case BO_MulAssign: return translateBinAssign(til::BOP_Mul, BO, Ctx); + case BO_DivAssign: return translateBinAssign(til::BOP_Div, BO, Ctx); + case BO_RemAssign: return translateBinAssign(til::BOP_Rem, BO, Ctx); + case BO_AddAssign: return translateBinAssign(til::BOP_Add, BO, Ctx); + case BO_SubAssign: return translateBinAssign(til::BOP_Sub, BO, Ctx); + case BO_ShlAssign: return translateBinAssign(til::BOP_Shl, BO, Ctx); + case BO_ShrAssign: return translateBinAssign(til::BOP_Shr, BO, Ctx); + case BO_AndAssign: return translateBinAssign(til::BOP_BitAnd, BO, Ctx); + case BO_XorAssign: return translateBinAssign(til::BOP_BitXor, BO, Ctx); + case BO_OrAssign: return translateBinAssign(til::BOP_BitOr, BO, Ctx); case BO_Comma: // The clang CFG should have already processed both sides. @@ -390,8 +371,9 @@ til::SExpr *SExprBuilder::translateCastExpr(const CastExpr *CE, return E0; } default: { + // FIXME: handle different kinds of casts. til::SExpr *E0 = translate(CE->getSubExpr(), Ctx); - return new (Arena) til::Cast(K, E0); + return new (Arena) til::Cast(til::CAST_none, E0); } } } @@ -791,8 +773,7 @@ void SExprBuilder::exitCFG(const CFGBlock *Last) { -class LLVMPrinter : public til::PrettyPrinter { -}; +class TILPrinter : public til::PrettyPrinter {}; void printSCFG(CFGWalker &Walker) { @@ -800,7 +781,7 @@ void printSCFG(CFGWalker &Walker) { til::MemRegionRef Arena(&Bpa); SExprBuilder builder(Arena); til::SCFG *Cfg = builder.buildCFG(Walker); - LLVMPrinter::print(Cfg, llvm::errs()); + TILPrinter::print(Cfg, llvm::errs()); } diff --git a/lib/Analysis/ThreadSafetyTIL.cpp b/lib/Analysis/ThreadSafetyTIL.cpp new file mode 100644 index 0000000000..f4da8d46dc --- /dev/null +++ b/lib/Analysis/ThreadSafetyTIL.cpp @@ -0,0 +1,113 @@ +//===- ThreadSafetyTIL.cpp -------------------------------------*- C++ --*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT in the llvm repository for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/Analyses/ThreadSafetyTIL.h" +#include "clang/Analysis/Analyses/ThreadSafetyTraverse.h" + +namespace clang { +namespace threadSafety { +namespace til { + + +StringRef getUnaryOpcodeString(TIL_UnaryOpcode Op) { + switch (Op) { + case UOP_Minus: return "-"; + case UOP_BitNot: return "~"; + case UOP_LogicNot: return "!"; + } + return ""; +} + + +StringRef getBinaryOpcodeString(TIL_BinaryOpcode Op) { + switch (Op) { + case BOP_Mul: return "*"; + case BOP_Div: return "/"; + case BOP_Rem: return "%"; + case BOP_Add: return "+"; + case BOP_Sub: return "-"; + case BOP_Shl: return "<<"; + case BOP_Shr: return ">>"; + case BOP_BitAnd: return "&"; + case BOP_BitXor: return "^"; + case BOP_BitOr: return "|"; + case BOP_Eq: return "=="; + case BOP_Neq: return "!="; + case BOP_Lt: return "<"; + case BOP_Leq: return "<="; + case BOP_LogicAnd: return "&&"; + case BOP_LogicOr: return "||"; + } + return ""; +} + + + +// If E is a variable, then trace back through any aliases or redundant +// Phi nodes to find the canonical definition. +SExpr *getCanonicalVal(SExpr *E) { + while (auto *V = dyn_cast(E)) { + SExpr *D; + do { + if (V->kind() != Variable::VK_Let) + return V; + D = V->definition(); + auto *V2 = dyn_cast(D); + if (V2) + V = V2; + else + break; + } while (true); + + if (ThreadSafetyTIL::isTrivial(D)) + return D; + + if (Phi *Ph = dyn_cast(D)) { + if (Ph->status() == Phi::PH_Incomplete) + simplifyIncompleteArg(V, Ph); + + if (Ph->status() == Phi::PH_SingleVal) { + E = Ph->values()[0]; + continue; + } + } + return V; + } + return E; +} + + +// Trace the arguments of an incomplete Phi node to see if they have the same +// canonical definition. If so, mark the Phi node as redundant. +// getCanonicalVal() will recursively call simplifyIncompletePhi(). +void simplifyIncompleteArg(Variable *V, til::Phi *Ph) { + assert(Ph && Ph->status() == Phi::PH_Incomplete); + + // eliminate infinite recursion -- assume that this node is not redundant. + Ph->setStatus(Phi::PH_MultiVal); + + SExpr *E0 = getCanonicalVal(Ph->values()[0]); + for (unsigned i=1, n=Ph->values().size(); ivalues()[i]); + if (Ei == V) + continue; // Recursive reference to itself. Don't count. + if (Ei != E0) { + return; // Status is already set to MultiVal. + } + } + Ph->setStatus(Phi::PH_SingleVal); + // Eliminate Redundant Phi node. + V->setDefinition(Ph->values()[0]); +} + + +} // end namespace til +} // end namespace threadSafety +} // end namespace clang +