From: Zhongxing Xu Date: Mon, 2 Aug 2010 04:56:14 +0000 (+0000) Subject: Improve flat store: MemRegion::getAsOffset() computes a region's offset within X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=7caf9b369cba6edaf6eac25121cbc65ee938f14d;p=clang Improve flat store: MemRegion::getAsOffset() computes a region's offset within the top-level object. FlatStore now can bind and retrieve element and field regions. PR7297 is fixed by flat store. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@110020 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Checker/PathSensitive/MemRegion.h b/include/clang/Checker/PathSensitive/MemRegion.h index feb4b7218a..7b4eac254b 100644 --- a/include/clang/Checker/PathSensitive/MemRegion.h +++ b/include/clang/Checker/PathSensitive/MemRegion.h @@ -261,6 +261,22 @@ public: } }; +/// Represent a region's offset within the top level base region. +class RegionOffset { + /// The base region. + const MemRegion *R; + + /// The bit offset within the base region. It shouldn't be negative. + uint64_t Offset; + +public: + RegionOffset(const MemRegion *r) : R(r), Offset(0) {} + RegionOffset(const MemRegion *r, uint64_t off) : R(r), Offset(off) {} + + const MemRegion *getRegion() const { return R; } + uint64_t getOffset() const { return Offset; } +}; + /// SubRegion - A region that subsets another larger region. Most regions /// are subclasses of SubRegion. class SubRegion : public MemRegion { @@ -277,6 +293,11 @@ public: return UnknownVal(); } + /// Compute the offset within the top level memory object. + virtual RegionOffset getAsOffset() const { + assert(0 && "unimplemented"); + } + MemRegionManager* getMemRegionManager() const; bool isSubRegionOf(const MemRegion* R) const; @@ -286,31 +307,6 @@ public: } }; -//===----------------------------------------------------------------------===// -// Auxillary data classes for use with MemRegions. -//===----------------------------------------------------------------------===// - -class ElementRegion; - -class RegionRawOffset { -private: - friend class ElementRegion; - - const MemRegion *Region; - int64_t Offset; - - RegionRawOffset(const MemRegion* reg, int64_t offset = 0) - : Region(reg), Offset(offset) {} - -public: - // FIXME: Eventually support symbolic offsets. - int64_t getByteOffset() const { return Offset; } - const MemRegion *getRegion() const { return Region; } - - void dumpToStream(llvm::raw_ostream& os) const; - void dump() const; -}; - //===----------------------------------------------------------------------===// // MemRegion subclasses. //===----------------------------------------------------------------------===// @@ -335,6 +331,10 @@ public: DefinedOrUnknownSVal getExtent(ValueManager& ValMgr) const; + virtual RegionOffset getAsOffset() const { + return RegionOffset(this, 0); + } + void Profile(llvm::FoldingSetNodeID& ID) const; static void ProfileRegion(llvm::FoldingSetNodeID& ID, const Expr* Ex, @@ -551,6 +551,10 @@ public: DefinedOrUnknownSVal getExtent(ValueManager& ValMgr) const; + virtual RegionOffset getAsOffset() const { + return RegionOffset(this, 0); + } + void Profile(llvm::FoldingSetNodeID& ID) const; static void ProfileRegion(llvm::FoldingSetNodeID& ID, @@ -587,6 +591,10 @@ public: DefinedOrUnknownSVal getExtent(ValueManager& ValMgr) const; + virtual RegionOffset getAsOffset() const { + return RegionOffset(this, 0); + } + bool isBoundable() const { return false; } void Profile(llvm::FoldingSetNodeID& ID) const { @@ -619,6 +627,10 @@ public: return C.getCanonicalType(CL->getType()); } + virtual RegionOffset getAsOffset() const { + return RegionOffset(this, 0); + } + bool isBoundable() const { return !CL->isFileScope(); } void Profile(llvm::FoldingSetNodeID& ID) const; @@ -661,6 +673,10 @@ class VarRegion : public DeclRegion { VarRegion(const VarDecl* vd, const MemRegion* sReg) : DeclRegion(vd, sReg, VarRegionKind) {} + virtual RegionOffset getAsOffset() const { + return RegionOffset(this, 0); + } + static void ProfileRegion(llvm::FoldingSetNodeID& ID, const VarDecl* VD, const MemRegion *superRegion) { DeclRegion::ProfileRegion(ID, VD, superRegion, VarRegionKind); @@ -704,7 +720,11 @@ public: QualType getValueType(ASTContext &C) const { return QualType(ThisPointerTy, 0); } - + + virtual RegionOffset getAsOffset() const { + return RegionOffset(this, 0); + } + void dumpToStream(llvm::raw_ostream& os) const; static bool classof(const MemRegion* R) { @@ -734,6 +754,8 @@ public: DefinedOrUnknownSVal getExtent(ValueManager& ValMgr) const; + virtual RegionOffset getAsOffset() const; + static void ProfileRegion(llvm::FoldingSetNodeID& ID, const FieldDecl* FD, const MemRegion* superRegion) { DeclRegion::ProfileRegion(ID, FD, superRegion, FieldRegionKind); @@ -766,6 +788,30 @@ public: return R->getKind() == ObjCIvarRegionKind; } }; +//===----------------------------------------------------------------------===// +// Auxillary data classes for use with MemRegions. +//===----------------------------------------------------------------------===// + +class ElementRegion; + +class RegionRawOffset { +private: + friend class ElementRegion; + + const MemRegion *Region; + int64_t Offset; + + RegionRawOffset(const MemRegion* reg, int64_t offset = 0) + : Region(reg), Offset(offset) {} + +public: + // FIXME: Eventually support symbolic offsets. + int64_t getByteOffset() const { return Offset; } + const MemRegion *getRegion() const { return Region; } + + void dumpToStream(llvm::raw_ostream& os) const; + void dump() const; +}; class ElementRegion : public TypedRegion { friend class MemRegionManager; @@ -795,8 +841,10 @@ public: QualType getElementType() const { return ElementType; } + /// Compute the offset within the array. The array might also be a subobject. + RegionRawOffset getAsArrayOffset() const; - RegionRawOffset getAsRawOffset() const; + virtual RegionOffset getAsOffset() const; void dumpToStream(llvm::raw_ostream& os) const; @@ -824,6 +872,10 @@ public: return Ex->getType(); } + virtual RegionOffset getAsOffset() const { + return RegionOffset(this, 0); + } + void Profile(llvm::FoldingSetNodeID &ID) const; static bool classof(const MemRegion* R) { diff --git a/lib/Checker/FlatStore.cpp b/lib/Checker/FlatStore.cpp index 64575b3c97..51c537ecfa 100644 --- a/lib/Checker/FlatStore.cpp +++ b/lib/Checker/FlatStore.cpp @@ -74,7 +74,14 @@ private: return RegionBindings(static_cast(store)); } - Interval RegionToInterval(const MemRegion *R); + class RegionInterval { + public: + const MemRegion *R; + Interval I; + RegionInterval(const MemRegion *r, uint64_t s, uint64_t e) : R(r), I(s, e){} + }; + + RegionInterval RegionToInterval(const MemRegion *R); SVal RetrieveRegionWithNoBinding(const MemRegion *R, QualType T); }; @@ -86,11 +93,14 @@ StoreManager *clang::CreateFlatStoreManager(GRStateManager &StMgr) { SVal FlatStoreManager::Retrieve(Store store, Loc L, QualType T) { const MemRegion *R = cast(L).getRegion(); - Interval I = RegionToInterval(R); + RegionInterval RI = RegionToInterval(R); + + assert(RI.R && "should handle regions with unknown interval"); + RegionBindings B = getRegionBindings(store); - const BindingVal *BV = B.lookup(R); + const BindingVal *BV = B.lookup(RI.R); if (BV) { - const SVal *V = BVFactory.Lookup(*BV, I); + const SVal *V = BVFactory.Lookup(*BV, RI.I); if (V) return *V; else @@ -116,9 +126,10 @@ Store FlatStoreManager::Bind(Store store, Loc L, SVal val) { if (V) BV = *V; - Interval I = RegionToInterval(R); - BV = BVFactory.Add(BV, I, val); - B = RBFactory.Add(B, R, BV); + RegionInterval RI = RegionToInterval(R); + assert(RI.R && "should handle regions with unknown interval"); + BV = BVFactory.Add(BV, RI.I, val); + B = RBFactory.Add(B, RI.R, BV); return B.getRoot(); } @@ -139,7 +150,7 @@ SVal FlatStoreManager::ArrayToPointer(Loc Array) { Store FlatStoreManager::BindDecl(Store store, const VarRegion *VR, SVal initVal) { - return store; + return Bind(store, ValMgr.makeLoc(VR), initVal); } Store FlatStoreManager::BindDeclWithNoInit(Store store, const VarRegion *VR) { @@ -170,15 +181,30 @@ void FlatStoreManager::print(Store store, llvm::raw_ostream& Out, void FlatStoreManager::iterBindings(Store store, BindingsHandler& f) { } -Interval FlatStoreManager::RegionToInterval(const MemRegion *R) { +FlatStoreManager::RegionInterval +FlatStoreManager::RegionToInterval(const MemRegion *R) { switch (R->getKind()) { case MemRegion::VarRegionKind: { QualType T = cast(R)->getValueType(Ctx); uint64_t Size = Ctx.getTypeSize(T); - return Interval(0, Size-1); + return RegionInterval(R, 0, Size-1); } + + case MemRegion::ElementRegionKind: + case MemRegion::FieldRegionKind: { + const TypedRegion *TR = cast(R); + RegionOffset Offset = TR->getAsOffset(); + // We cannot compute offset for all ElementRegions, for example, elements + // with symbolic offsets. + if (!Offset.getRegion()) + return RegionInterval(0, 0, 0); + uint64_t Start = Offset.getOffset(); + uint64_t Size = Ctx.getTypeSize(TR->getValueType(Ctx)); + return RegionInterval(Offset.getRegion(), Start, Start+Size); + } + default: llvm_unreachable("Region kind unhandled."); - return Interval(0, 0); + return RegionInterval(0, 0, 0); } } diff --git a/lib/Checker/MemRegion.cpp b/lib/Checker/MemRegion.cpp index 9cfeb7ae2b..d9c559e847 100644 --- a/lib/Checker/MemRegion.cpp +++ b/lib/Checker/MemRegion.cpp @@ -18,6 +18,7 @@ #include "clang/Analysis/AnalysisContext.h" #include "clang/Analysis/Support/BumpVector.h" #include "clang/AST/CharUnits.h" +#include "clang/AST/RecordLayout.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -785,7 +786,7 @@ static bool IsCompleteType(ASTContext &Ctx, QualType Ty) { return true; } -RegionRawOffset ElementRegion::getAsRawOffset() const { +RegionRawOffset ElementRegion::getAsArrayOffset() const { CharUnits offset = CharUnits::Zero(); const ElementRegion *ER = this; const MemRegion *superR = NULL; @@ -827,6 +828,50 @@ RegionRawOffset ElementRegion::getAsRawOffset() const { return RegionRawOffset(superR, offset.getQuantity()); } +RegionOffset ElementRegion::getAsOffset() const { + uint64_t Offset; + if (const nonloc::ConcreteInt *CI = dyn_cast(&Index)) { + int64_t i = CI->getValue().getSExtValue(); + assert(i >= 0); + // We cannot compute offset for incomplete types. + if (!IsCompleteType(getContext(), ElementType)) + return RegionOffset(0); + + CharUnits Size = getContext().getTypeSizeInChars(ElementType); + Offset = i * Size.getQuantity() * 8; + } else + // We cannot compute offset for symbolic index. + return RegionOffset(0); + + // Get the offset of the super region. + RegionOffset SOffset = cast(superRegion)->getAsOffset(); + if (!SOffset.getRegion()) + return RegionOffset(0); + else + return RegionOffset(SOffset.getRegion(), SOffset.getOffset() + Offset); +} + +RegionOffset FieldRegion::getAsOffset() const { + const RecordDecl *RD = getDecl()->getParent(); + assert(RD->isDefinition()); + // Get the field number. + unsigned idx = 0; + for (RecordDecl::field_iterator FI = RD->field_begin(), FE = RD->field_end(); + FI != FE; ++FI, ++idx) + if (getDecl() == *FI) + break; + + const ASTRecordLayout &Layout = getContext().getASTRecordLayout(RD); + // This is offset in bits. + uint64_t Offset = Layout.getFieldOffset(idx); + + RegionOffset SOffset = cast(superRegion)->getAsOffset(); + if (!SOffset.getRegion()) + return RegionOffset(0); + else + return RegionOffset(SOffset.getRegion(), SOffset.getOffset() + Offset); +} + //===----------------------------------------------------------------------===// // BlockDataRegion //===----------------------------------------------------------------------===// diff --git a/lib/Checker/RegionStore.cpp b/lib/Checker/RegionStore.cpp index 5dd824e772..f7e19f6f51 100644 --- a/lib/Checker/RegionStore.cpp +++ b/lib/Checker/RegionStore.cpp @@ -1146,7 +1146,7 @@ SVal RegionStoreManager::RetrieveElement(Store store, // char *y = &x; // return *y; // FIXME: This is a hack, and doesn't do anything really intelligent yet. - const RegionRawOffset &O = R->getAsRawOffset(); + const RegionRawOffset &O = R->getAsArrayOffset(); if (const TypedRegion *baseR = dyn_cast_or_null(O.getRegion())) { QualType baseT = baseR->getValueType(Ctx); if (baseT->isScalarType()) { @@ -1608,7 +1608,7 @@ Store RegionStoreManager::CopyLazyBindings(nonloc::LazyCompoundVal V, BindingKey BindingKey::Make(const MemRegion *R, Kind k) { if (const ElementRegion *ER = dyn_cast(R)) { - const RegionRawOffset &O = ER->getAsRawOffset(); + const RegionRawOffset &O = ER->getAsArrayOffset(); if (O.getRegion()) return BindingKey(O.getRegion(), O.getByteOffset(), k); diff --git a/lib/Checker/SimpleSValuator.cpp b/lib/Checker/SimpleSValuator.cpp index 3bc4ee7d06..48c8b9a81b 100644 --- a/lib/Checker/SimpleSValuator.cpp +++ b/lib/Checker/SimpleSValuator.cpp @@ -711,8 +711,8 @@ SVal SimpleSValuator::EvalBinOpLL(const GRState *state, } // If the element indexes aren't comparable, see if the raw offsets are. - RegionRawOffset LeftOffset = LeftER->getAsRawOffset(); - RegionRawOffset RightOffset = RightER->getAsRawOffset(); + RegionRawOffset LeftOffset = LeftER->getAsArrayOffset(); + RegionRawOffset RightOffset = RightER->getAsArrayOffset(); if (LeftOffset.getRegion() != NULL && LeftOffset.getRegion() == RightOffset.getRegion()) { diff --git a/lib/Checker/Store.cpp b/lib/Checker/Store.cpp index b128331705..b3aab66512 100644 --- a/lib/Checker/Store.cpp +++ b/lib/Checker/Store.cpp @@ -139,7 +139,7 @@ const MemRegion *StoreManager::CastRegion(const MemRegion *R, QualType CastToTy) // FIXME: Handle symbolic raw offsets. const ElementRegion *elementR = cast(R); - const RegionRawOffset &rawOff = elementR->getAsRawOffset(); + const RegionRawOffset &rawOff = elementR->getAsArrayOffset(); const MemRegion *baseR = rawOff.getRegion(); // If we cannot compute a raw offset, throw up our hands and return diff --git a/test/Analysis/flat-store.c b/test/Analysis/flat-store.c new file mode 100644 index 0000000000..bb274b0d5d --- /dev/null +++ b/test/Analysis/flat-store.c @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -analyze -analyzer-check-objc-mem -analyzer-store=flat -verify %s +#define FAIL ((void)*(char*)0) +struct simple { int x; }; + +void PR7297 () { + struct simple a; + struct simple *p = &a; + p->x = 5; + if (!p[0].x) FAIL; // no-warning + if (p[0].x) FAIL; // expected-warning {{null}} +}