diff --git a/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def b/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def index d7075fb39fc89..9974ea9392acb 100644 --- a/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def +++ b/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def @@ -440,11 +440,19 @@ ANALYZER_OPTION( ANALYZER_OPTION( unsigned, RegionStoreSmallStructLimit, "region-store-small-struct-limit", "The largest number of fields a struct can have and still be considered " - "small This is currently used to decide whether or not it is worth forcing " + "small. This is currently used to decide whether or not it is worth forcing " "a LazyCompoundVal on bind. To disable all small-struct-dependent " "behavior, set the option to 0.", 2) +ANALYZER_OPTION( + unsigned, RegionStoreSmallArrayLimit, "region-store-small-array-limit", + "The largest number of elements an array can have and still be considered " + "small. This is currently used to decide whether or not it is worth forcing " + "a LazyCompoundVal on bind. To disable all small-array-dependent " + "behavior, set the option to 0.", + 5) + //===----------------------------------------------------------------------===// // String analyzer options. //===----------------------------------------------------------------------===// diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp index 8a50cb27c8d3a..b432247bad7d8 100644 --- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -345,6 +345,16 @@ class RegionStoreManager : public StoreManager { /// To disable all small-struct-dependent behavior, set the option to "0". unsigned SmallStructLimit; + /// The largest number of element an array can have and still be + /// considered "small". + /// + /// This is currently used to decide whether or not it is worth "forcing" a + /// LazyCompoundVal on bind. + /// + /// This is controlled by 'region-store-small-struct-limit' option. + /// To disable all small-struct-dependent behavior, set the option to "0". + unsigned SmallArrayLimit; + /// A helper used to populate the work list with the given set of /// regions. void populateWorkList(InvalidateRegionsWorker &W, @@ -354,10 +364,11 @@ class RegionStoreManager : public StoreManager { public: RegionStoreManager(ProgramStateManager &mgr) : StoreManager(mgr), RBFactory(mgr.getAllocator()), - CBFactory(mgr.getAllocator()), SmallStructLimit(0) { + CBFactory(mgr.getAllocator()), SmallStructLimit(0), SmallArrayLimit(0) { ExprEngine &Eng = StateMgr.getOwningEngine(); AnalyzerOptions &Options = Eng.getAnalysisManager().options; SmallStructLimit = Options.RegionStoreSmallStructLimit; + SmallArrayLimit = Options.RegionStoreSmallArrayLimit; } /// setImplicitDefaultValue - Set the default binding for the provided @@ -487,6 +498,11 @@ class RegionStoreManager : public StoreManager { RegionBindingsRef bindVector(RegionBindingsConstRef B, const TypedValueRegion* R, SVal V); + Optional tryBindSmallArray(RegionBindingsConstRef B, + const TypedValueRegion *R, + const ArrayType *AT, + nonloc::LazyCompoundVal LCV); + RegionBindingsRef bindArray(RegionBindingsConstRef B, const TypedValueRegion* R, SVal V); @@ -2392,6 +2408,40 @@ RegionStoreManager::setImplicitDefaultValue(RegionBindingsConstRef B, return B.addBinding(R, BindingKey::Default, V); } +Optional RegionStoreManager::tryBindSmallArray( + RegionBindingsConstRef B, const TypedValueRegion *R, const ArrayType *AT, + nonloc::LazyCompoundVal LCV) { + + auto CAT = dyn_cast(AT); + + // If we don't know the size, create a lazyCompoundVal instead. + if (!CAT) + return None; + + QualType Ty = CAT->getElementType(); + if (!(Ty->isScalarType() || Ty->isReferenceType())) + return None; + + // If the array is too big, create a LCV instead. + uint64_t ArrSize = CAT->getSize().getLimitedValue(); + if (ArrSize > SmallArrayLimit) + return None; + + RegionBindingsRef NewB = B; + + for (uint64_t i = 0; i < ArrSize; ++i) { + auto Idx = svalBuilder.makeArrayIndex(i); + const ElementRegion *SrcER = + MRMgr.getElementRegion(Ty, Idx, LCV.getRegion(), Ctx); + SVal V = getBindingForElement(getRegionBindings(LCV.getStore()), SrcER); + + const ElementRegion *DstER = MRMgr.getElementRegion(Ty, Idx, R, Ctx); + NewB = bind(NewB, loc::MemRegionVal(DstER), V); + } + + return NewB; +} + RegionBindingsRef RegionStoreManager::bindArray(RegionBindingsConstRef B, const TypedValueRegion* R, @@ -2413,8 +2463,13 @@ RegionStoreManager::bindArray(RegionBindingsConstRef B, } // Handle lazy compound values. - if (isa(Init)) + if (Optional LCV = + Init.getAs()) { + if (Optional NewB = tryBindSmallArray(B, R, AT, *LCV)) + return *NewB; + return bindAggregate(B, R, Init); + } if (Init.isUnknown()) return bindAggregate(B, R, UnknownVal()); diff --git a/clang/test/Analysis/analyzer-config.c b/clang/test/Analysis/analyzer-config.c index 440547b62345a..9f22be6742c1f 100644 --- a/clang/test/Analysis/analyzer-config.c +++ b/clang/test/Analysis/analyzer-config.c @@ -114,6 +114,7 @@ // CHECK-NEXT: osx.NumberObjectConversion:Pedantic = false // CHECK-NEXT: osx.cocoa.RetainCount:TrackNSCFStartParam = false // CHECK-NEXT: prune-paths = true +// CHECK-NEXT: region-store-small-array-limit = 5 // CHECK-NEXT: region-store-small-struct-limit = 2 // CHECK-NEXT: report-in-main-source-file = false // CHECK-NEXT: serialize-stats = false