blob: 03e9e38206a201626fa905d75df33fd7bc518491 [file] [log] [blame]
Zhongxing Xu1ec4e972009-12-09 12:23:28 +00001//=== OSAtomicChecker.cpp - OSAtomic functions evaluator --------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This checker evaluates OSAtomic functions.
11//
12//===----------------------------------------------------------------------===//
13
14#include "GRExprEngineInternalChecks.h"
15#include "clang/Analysis/PathSensitive/Checker.h"
16#include "clang/Basic/Builtins.h"
17#include "llvm/ADT/StringSwitch.h"
18
19using namespace clang;
20
21namespace {
22
23class OSAtomicChecker : public Checker {
24public:
25 static void *getTag() { static int tag = 0; return &tag; }
26 virtual bool EvalCallExpr(CheckerContext &C, const CallExpr *CE);
27
28private:
29 bool EvalOSAtomicCompareAndSwap(CheckerContext &C, const CallExpr *CE);
30};
31
32}
33
34void clang::RegisterOSAtomicChecker(GRExprEngine &Eng) {
35 Eng.registerCheck(new OSAtomicChecker());
36}
37
38bool OSAtomicChecker::EvalCallExpr(CheckerContext &C,const CallExpr *CE) {
39 const GRState *state = C.getState();
40 const Expr *Callee = CE->getCallee();
41 SVal L = state->getSVal(Callee);
42
43 const FunctionDecl* FD = L.getAsFunctionDecl();
44 if (!FD)
45 return false;
46
47 const char *FName = FD->getNameAsCString();
48
49 // Check for compare and swap.
50 if (strncmp(FName, "OSAtomicCompareAndSwap", 22) == 0 ||
51 strncmp(FName, "objc_atomicCompareAndSwap", 25) == 0)
52 return EvalOSAtomicCompareAndSwap(C, CE);
53
54 // FIXME: Other atomics.
55 return false;
56}
57
58bool OSAtomicChecker::EvalOSAtomicCompareAndSwap(CheckerContext &C,
59 const CallExpr *CE) {
60 // Not enough arguments to match OSAtomicCompareAndSwap?
61 if (CE->getNumArgs() != 3)
62 return false;
63
64 ASTContext &Ctx = C.getASTContext();
65 const Expr *oldValueExpr = CE->getArg(0);
66 QualType oldValueType = Ctx.getCanonicalType(oldValueExpr->getType());
67
68 const Expr *newValueExpr = CE->getArg(1);
69 QualType newValueType = Ctx.getCanonicalType(newValueExpr->getType());
70
71 // Do the types of 'oldValue' and 'newValue' match?
72 if (oldValueType != newValueType)
73 return false;
74
75 const Expr *theValueExpr = CE->getArg(2);
76 const PointerType *theValueType=theValueExpr->getType()->getAs<PointerType>();
77
78 // theValueType not a pointer?
79 if (!theValueType)
80 return false;
81
82 QualType theValueTypePointee =
83 Ctx.getCanonicalType(theValueType->getPointeeType()).getUnqualifiedType();
84
85 // The pointee must match newValueType and oldValueType.
86 if (theValueTypePointee != newValueType)
87 return false;
88
89 static unsigned magic_load = 0;
90 static unsigned magic_store = 0;
91
92 const void *OSAtomicLoadTag = &magic_load;
93 const void *OSAtomicStoreTag = &magic_store;
94
95 // Load 'theValue'.
96 GRExprEngine &Engine = C.getEngine();
97 const GRState *state = C.getState();
98 ExplodedNodeSet Tmp;
99 SVal location = state->getSVal(theValueExpr);
100 // Here we should use the value type of the region as the load type.
101 const MemRegion *R = location.getAsRegion()->StripCasts();
102 QualType LoadTy;
103 if (R) {
104 LoadTy = cast<TypedRegion>(R)->getValueType(Ctx);
105 location = loc::MemRegionVal(R);
106 }
107 Engine.EvalLoad(Tmp, const_cast<Expr *>(theValueExpr), C.getPredecessor(),
108 state, location, OSAtomicLoadTag, LoadTy);
109
110 if (Tmp.empty()) {
111 // If no nodes were generated, other checkers must generated sinks. But
112 // since the builder state was restored, we set it manually to prevent
113 // auto transition.
114 // FIXME: there should be a better approach.
115 C.getNodeBuilder().BuildSinks = true;
116 return true;
117 }
118
119 for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end();
120 I != E; ++I) {
121
122 ExplodedNode *N = *I;
123 const GRState *stateLoad = N->getState();
124 SVal theValueVal_untested = stateLoad->getSVal(theValueExpr);
125 SVal oldValueVal_untested = stateLoad->getSVal(oldValueExpr);
126
127 // FIXME: Issue an error.
128 if (theValueVal_untested.isUndef() || oldValueVal_untested.isUndef()) {
129 return false;
130 }
131
132 DefinedOrUnknownSVal theValueVal =
133 cast<DefinedOrUnknownSVal>(theValueVal_untested);
134 DefinedOrUnknownSVal oldValueVal =
135 cast<DefinedOrUnknownSVal>(oldValueVal_untested);
136
137 SValuator &SVator = Engine.getSValuator();
138
139 // Perform the comparison.
140 DefinedOrUnknownSVal Cmp = SVator.EvalEQ(stateLoad,theValueVal,oldValueVal);
141
142 const GRState *stateEqual = stateLoad->Assume(Cmp, true);
143
144 // Were they equal?
145 if (stateEqual) {
146 // Perform the store.
147 ExplodedNodeSet TmpStore;
148 SVal val = stateEqual->getSVal(newValueExpr);
149
150 // Handle implicit value casts.
151 if (const TypedRegion *R =
152 dyn_cast_or_null<TypedRegion>(location.getAsRegion())) {
153 llvm::tie(state, val) = SVator.EvalCast(val, state,R->getValueType(Ctx),
154 newValueExpr->getType());
155 }
156
157 Engine.EvalStore(TmpStore, NULL, const_cast<Expr *>(theValueExpr), N,
158 stateEqual, location, val, OSAtomicStoreTag);
159
160 if (TmpStore.empty()) {
161 // If no nodes were generated, other checkers must generated sinks. But
162 // since the builder state was restored, we set it manually to prevent
163 // auto transition.
164 // FIXME: there should be a better approach.
165 C.getNodeBuilder().BuildSinks = true;
166 return true;
167 }
168
169 // Now bind the result of the comparison.
170 for (ExplodedNodeSet::iterator I2 = TmpStore.begin(),
171 E2 = TmpStore.end(); I2 != E2; ++I2) {
172 ExplodedNode *predNew = *I2;
173 const GRState *stateNew = predNew->getState();
174 SVal Res = Engine.getValueManager().makeTruthVal(true, CE->getType());
175 C.GenerateNode(stateNew->BindExpr(CE, Res), predNew);
176 }
177 }
178
179 // Were they not equal?
180 if (const GRState *stateNotEqual = stateLoad->Assume(Cmp, false)) {
181 SVal Res = Engine.getValueManager().makeTruthVal(false, CE->getType());
182 C.GenerateNode(stateNotEqual->BindExpr(CE, Res), N);
183 }
184 }
185
186 return true;
187}