blob: cf16796b1b146cdd1990aea3f9ca8765fec0ccd9 [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
Ted Kremeneka0f77272009-12-16 06:03:24 +000047 const IdentifierInfo *II = FD->getIdentifier();
48 if (!II)
49 return false;
50
51 llvm::StringRef FName(II->getName());
Zhongxing Xu1ec4e972009-12-09 12:23:28 +000052
53 // Check for compare and swap.
Ted Kremeneka0f77272009-12-16 06:03:24 +000054 if (FName.startswith("OSAtomicCompareAndSwap") ||
55 FName.startswith("objc_atomicCompareAndSwap"))
Zhongxing Xu1ec4e972009-12-09 12:23:28 +000056 return EvalOSAtomicCompareAndSwap(C, CE);
57
58 // FIXME: Other atomics.
59 return false;
60}
61
62bool OSAtomicChecker::EvalOSAtomicCompareAndSwap(CheckerContext &C,
63 const CallExpr *CE) {
64 // Not enough arguments to match OSAtomicCompareAndSwap?
65 if (CE->getNumArgs() != 3)
66 return false;
67
68 ASTContext &Ctx = C.getASTContext();
69 const Expr *oldValueExpr = CE->getArg(0);
70 QualType oldValueType = Ctx.getCanonicalType(oldValueExpr->getType());
71
72 const Expr *newValueExpr = CE->getArg(1);
73 QualType newValueType = Ctx.getCanonicalType(newValueExpr->getType());
74
75 // Do the types of 'oldValue' and 'newValue' match?
76 if (oldValueType != newValueType)
77 return false;
78
79 const Expr *theValueExpr = CE->getArg(2);
80 const PointerType *theValueType=theValueExpr->getType()->getAs<PointerType>();
81
82 // theValueType not a pointer?
83 if (!theValueType)
84 return false;
85
86 QualType theValueTypePointee =
87 Ctx.getCanonicalType(theValueType->getPointeeType()).getUnqualifiedType();
88
89 // The pointee must match newValueType and oldValueType.
90 if (theValueTypePointee != newValueType)
91 return false;
92
93 static unsigned magic_load = 0;
94 static unsigned magic_store = 0;
95
96 const void *OSAtomicLoadTag = &magic_load;
97 const void *OSAtomicStoreTag = &magic_store;
98
99 // Load 'theValue'.
100 GRExprEngine &Engine = C.getEngine();
101 const GRState *state = C.getState();
102 ExplodedNodeSet Tmp;
103 SVal location = state->getSVal(theValueExpr);
104 // Here we should use the value type of the region as the load type.
Zhongxing Xu1ec4e972009-12-09 12:23:28 +0000105 QualType LoadTy;
Ted Kremenek6bcd5a02009-12-09 23:29:55 +0000106 if (const MemRegion *R = location.getAsRegion()) {
107 // We must be careful, as SymbolicRegions aren't typed.
108 const MemRegion *strippedR = R->StripCasts();
109 // FIXME: This isn't quite the right solution. One test case in 'test/Analysis/NSString.m'
110 // is giving the wrong result.
111 const TypedRegion *typedR =
112 isa<SymbolicRegion>(strippedR) ? cast<TypedRegion>(R) :
113 dyn_cast<TypedRegion>(strippedR);
114
115 if (typedR) {
116 LoadTy = typedR->getValueType(Ctx);
117 location = loc::MemRegionVal(typedR);
118 }
Zhongxing Xu1ec4e972009-12-09 12:23:28 +0000119 }
120 Engine.EvalLoad(Tmp, const_cast<Expr *>(theValueExpr), C.getPredecessor(),
121 state, location, OSAtomicLoadTag, LoadTy);
122
123 if (Tmp.empty()) {
124 // If no nodes were generated, other checkers must generated sinks. But
125 // since the builder state was restored, we set it manually to prevent
126 // auto transition.
127 // FIXME: there should be a better approach.
128 C.getNodeBuilder().BuildSinks = true;
129 return true;
130 }
131
132 for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end();
133 I != E; ++I) {
134
135 ExplodedNode *N = *I;
136 const GRState *stateLoad = N->getState();
137 SVal theValueVal_untested = stateLoad->getSVal(theValueExpr);
138 SVal oldValueVal_untested = stateLoad->getSVal(oldValueExpr);
139
140 // FIXME: Issue an error.
141 if (theValueVal_untested.isUndef() || oldValueVal_untested.isUndef()) {
142 return false;
143 }
144
145 DefinedOrUnknownSVal theValueVal =
146 cast<DefinedOrUnknownSVal>(theValueVal_untested);
147 DefinedOrUnknownSVal oldValueVal =
148 cast<DefinedOrUnknownSVal>(oldValueVal_untested);
149
150 SValuator &SVator = Engine.getSValuator();
151
152 // Perform the comparison.
153 DefinedOrUnknownSVal Cmp = SVator.EvalEQ(stateLoad,theValueVal,oldValueVal);
154
155 const GRState *stateEqual = stateLoad->Assume(Cmp, true);
156
157 // Were they equal?
158 if (stateEqual) {
159 // Perform the store.
160 ExplodedNodeSet TmpStore;
161 SVal val = stateEqual->getSVal(newValueExpr);
162
163 // Handle implicit value casts.
164 if (const TypedRegion *R =
165 dyn_cast_or_null<TypedRegion>(location.getAsRegion())) {
166 llvm::tie(state, val) = SVator.EvalCast(val, state,R->getValueType(Ctx),
167 newValueExpr->getType());
168 }
169
170 Engine.EvalStore(TmpStore, NULL, const_cast<Expr *>(theValueExpr), N,
171 stateEqual, location, val, OSAtomicStoreTag);
172
173 if (TmpStore.empty()) {
174 // If no nodes were generated, other checkers must generated sinks. But
175 // since the builder state was restored, we set it manually to prevent
176 // auto transition.
177 // FIXME: there should be a better approach.
178 C.getNodeBuilder().BuildSinks = true;
179 return true;
180 }
181
182 // Now bind the result of the comparison.
183 for (ExplodedNodeSet::iterator I2 = TmpStore.begin(),
184 E2 = TmpStore.end(); I2 != E2; ++I2) {
185 ExplodedNode *predNew = *I2;
186 const GRState *stateNew = predNew->getState();
187 SVal Res = Engine.getValueManager().makeTruthVal(true, CE->getType());
188 C.GenerateNode(stateNew->BindExpr(CE, Res), predNew);
189 }
190 }
191
192 // Were they not equal?
193 if (const GRState *stateNotEqual = stateLoad->Assume(Cmp, false)) {
194 SVal Res = Engine.getValueManager().makeTruthVal(false, CE->getType());
195 C.GenerateNode(stateNotEqual->BindExpr(CE, Res), N);
196 }
197 }
198
199 return true;
200}