1 //=== OSAtomicChecker.cpp - OSAtomic functions evaluator --------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This checker evaluates OSAtomic functions.
12 //===----------------------------------------------------------------------===//
14 #include "GRExprEngineInternalChecks.h"
15 #include "clang/GR/PathSensitive/Checker.h"
16 #include "clang/Basic/Builtins.h"
18 using namespace clang
;
22 class OSAtomicChecker
: public Checker
{
24 static void *getTag() { static int tag
= 0; return &tag
; }
25 virtual bool evalCallExpr(CheckerContext
&C
, const CallExpr
*CE
);
28 bool evalOSAtomicCompareAndSwap(CheckerContext
&C
, const CallExpr
*CE
);
33 void clang::RegisterOSAtomicChecker(GRExprEngine
&Eng
) {
34 Eng
.registerCheck(new OSAtomicChecker());
37 bool OSAtomicChecker::evalCallExpr(CheckerContext
&C
,const CallExpr
*CE
) {
38 const GRState
*state
= C
.getState();
39 const Expr
*Callee
= CE
->getCallee();
40 SVal L
= state
->getSVal(Callee
);
42 const FunctionDecl
* FD
= L
.getAsFunctionDecl();
46 const IdentifierInfo
*II
= FD
->getIdentifier();
50 llvm::StringRef
FName(II
->getName());
52 // Check for compare and swap.
53 if (FName
.startswith("OSAtomicCompareAndSwap") ||
54 FName
.startswith("objc_atomicCompareAndSwap"))
55 return evalOSAtomicCompareAndSwap(C
, CE
);
57 // FIXME: Other atomics.
61 bool OSAtomicChecker::evalOSAtomicCompareAndSwap(CheckerContext
&C
,
63 // Not enough arguments to match OSAtomicCompareAndSwap?
64 if (CE
->getNumArgs() != 3)
67 ASTContext
&Ctx
= C
.getASTContext();
68 const Expr
*oldValueExpr
= CE
->getArg(0);
69 QualType oldValueType
= Ctx
.getCanonicalType(oldValueExpr
->getType());
71 const Expr
*newValueExpr
= CE
->getArg(1);
72 QualType newValueType
= Ctx
.getCanonicalType(newValueExpr
->getType());
74 // Do the types of 'oldValue' and 'newValue' match?
75 if (oldValueType
!= newValueType
)
78 const Expr
*theValueExpr
= CE
->getArg(2);
79 const PointerType
*theValueType
=theValueExpr
->getType()->getAs
<PointerType
>();
81 // theValueType not a pointer?
85 QualType theValueTypePointee
=
86 Ctx
.getCanonicalType(theValueType
->getPointeeType()).getUnqualifiedType();
88 // The pointee must match newValueType and oldValueType.
89 if (theValueTypePointee
!= newValueType
)
92 static unsigned magic_load
= 0;
93 static unsigned magic_store
= 0;
95 const void *OSAtomicLoadTag
= &magic_load
;
96 const void *OSAtomicStoreTag
= &magic_store
;
99 GRExprEngine
&Engine
= C
.getEngine();
100 const GRState
*state
= C
.getState();
102 SVal location
= state
->getSVal(theValueExpr
);
103 // Here we should use the value type of the region as the load type, because
104 // we are simulating the semantics of the function, not the semantics of
105 // passing argument. So the type of theValue expr is not we are loading.
106 // But usually the type of the varregion is not the type we want either,
107 // we still need to do a CastRetrievedVal in store manager. So actually this
108 // LoadTy specifying can be omitted. But we put it here to emphasize the
111 if (const TypedRegion
*TR
=
112 dyn_cast_or_null
<TypedRegion
>(location
.getAsRegion())) {
113 LoadTy
= TR
->getValueType();
115 Engine
.evalLoad(Tmp
, theValueExpr
, C
.getPredecessor(),
116 state
, location
, OSAtomicLoadTag
, LoadTy
);
119 // If no nodes were generated, other checkers must generated sinks. But
120 // since the builder state was restored, we set it manually to prevent
122 // FIXME: there should be a better approach.
123 C
.getNodeBuilder().BuildSinks
= true;
127 for (ExplodedNodeSet::iterator I
= Tmp
.begin(), E
= Tmp
.end();
130 ExplodedNode
*N
= *I
;
131 const GRState
*stateLoad
= N
->getState();
132 SVal theValueVal_untested
= stateLoad
->getSVal(theValueExpr
);
133 SVal oldValueVal_untested
= stateLoad
->getSVal(oldValueExpr
);
135 // FIXME: Issue an error.
136 if (theValueVal_untested
.isUndef() || oldValueVal_untested
.isUndef()) {
140 DefinedOrUnknownSVal theValueVal
=
141 cast
<DefinedOrUnknownSVal
>(theValueVal_untested
);
142 DefinedOrUnknownSVal oldValueVal
=
143 cast
<DefinedOrUnknownSVal
>(oldValueVal_untested
);
145 SValBuilder
&svalBuilder
= Engine
.getSValBuilder();
147 // Perform the comparison.
148 DefinedOrUnknownSVal Cmp
= svalBuilder
.evalEQ(stateLoad
,theValueVal
,oldValueVal
);
150 const GRState
*stateEqual
= stateLoad
->assume(Cmp
, true);
154 // Perform the store.
155 ExplodedNodeSet TmpStore
;
156 SVal val
= stateEqual
->getSVal(newValueExpr
);
158 // Handle implicit value casts.
159 if (const TypedRegion
*R
=
160 dyn_cast_or_null
<TypedRegion
>(location
.getAsRegion())) {
161 val
= svalBuilder
.evalCast(val
,R
->getValueType(), newValueExpr
->getType());
164 Engine
.evalStore(TmpStore
, NULL
, theValueExpr
, N
,
165 stateEqual
, location
, val
, OSAtomicStoreTag
);
167 if (TmpStore
.empty()) {
168 // If no nodes were generated, other checkers must generated sinks. But
169 // since the builder state was restored, we set it manually to prevent
171 // FIXME: there should be a better approach.
172 C
.getNodeBuilder().BuildSinks
= true;
176 // Now bind the result of the comparison.
177 for (ExplodedNodeSet::iterator I2
= TmpStore
.begin(),
178 E2
= TmpStore
.end(); I2
!= E2
; ++I2
) {
179 ExplodedNode
*predNew
= *I2
;
180 const GRState
*stateNew
= predNew
->getState();
181 // Check for 'void' return type if we have a bogus function prototype.
182 SVal Res
= UnknownVal();
183 QualType T
= CE
->getType();
184 if (!T
->isVoidType())
185 Res
= Engine
.getSValBuilder().makeTruthVal(true, T
);
186 C
.generateNode(stateNew
->BindExpr(CE
, Res
), predNew
);
190 // Were they not equal?
191 if (const GRState
*stateNotEqual
= stateLoad
->assume(Cmp
, false)) {
192 // Check for 'void' return type if we have a bogus function prototype.
193 SVal Res
= UnknownVal();
194 QualType T
= CE
->getType();
195 if (!T
->isVoidType())
196 Res
= Engine
.getSValBuilder().makeTruthVal(false, CE
->getType());
197 C
.generateNode(stateNotEqual
->BindExpr(CE
, Res
), N
);