[analyzer] Produce SymbolCast symbols for integral types in SValBuilder::evalCast

Summary: Produce SymbolCast for integral types in `evalCast` function. Apply several simplification techniques while producing the symbols. Added a boolean option `handle-integral-cast-for-ranges` under `-analyzer-config` flag. Disabled the feature by default.

Differential Revision: https://reviews.llvm.org/D105340
This commit is contained in:
Denys Petrov 2021-07-02 16:04:07 +03:00
parent 7f4d66f23e
commit d835dd4cf5
7 changed files with 3386 additions and 22 deletions

View File

@ -320,6 +320,11 @@ ANALYZER_OPTION(bool, ShouldDisplayCheckerNameForText, "display-checker-name",
"Display the checker name for textual outputs",
true)
ANALYZER_OPTION(bool, ShouldSupportSymbolicIntegerCasts,
"support-symbolic-integer-casts",
"Produce cast symbols for integral types.",
false)
ANALYZER_OPTION(
bool, ShouldConsiderSingleElementArraysAsFlexibleArrayMembers,
"consider-single-element-arrays-as-flexible-array-members",

View File

@ -96,6 +96,17 @@ protected:
QualType OriginalTy);
SVal evalCastSubKind(nonloc::PointerToMember V, QualType CastTy,
QualType OriginalTy);
/// Reduce cast expression by removing redundant intermediate casts.
/// E.g.
/// - (char)(short)(int x) -> (char)(int x)
/// - (int)(int x) -> int x
///
/// \param V -- SymbolVal, which pressumably contains SymbolCast or any symbol
/// that is applicable for cast operation.
/// \param CastTy -- QualType, which `V` shall be cast to.
/// \return SVal with simplified cast expression.
/// \note: Currently only support integral casts.
SVal simplifySymbolCast(nonloc::SymbolVal V, QualType CastTy);
public:
SValBuilder(llvm::BumpPtrAllocator &alloc, ASTContext &context,
@ -103,18 +114,6 @@ public:
virtual ~SValBuilder() = default;
bool haveSameType(const SymExpr *Sym1, const SymExpr *Sym2) {
return haveSameType(Sym1->getType(), Sym2->getType());
}
bool haveSameType(QualType Ty1, QualType Ty2) {
// FIXME: Remove the second disjunct when we support symbolic
// truncation/extension.
return (Context.getCanonicalType(Ty1) == Context.getCanonicalType(Ty2) ||
(Ty1->isIntegralOrEnumerationType() &&
Ty2->isIntegralOrEnumerationType()));
}
SVal evalCast(SVal V, QualType CastTy, QualType OriginalTy);
// Handles casts of type CK_IntegralCast.

View File

@ -416,7 +416,10 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
case CK_IntegralCast: {
// Delegate to SValBuilder to process.
SVal V = state->getSVal(Ex, LCtx);
V = svalBuilder.evalIntegralCast(state, V, T, ExTy);
if (AMgr.options.ShouldSupportSymbolicIntegerCasts)
V = svalBuilder.evalCast(V, T, ExTy);
else
V = svalBuilder.evalIntegralCast(state, V, T, ExTy);
state = state->BindExpr(CastE, LCtx, V);
Bldr.generateNode(CastE, Pred, state);
continue;

View File

@ -980,15 +980,19 @@ SVal SValBuilder::evalCastSubKind(nonloc::SymbolVal V, QualType CastTy,
} else {
// Symbol to integer, float.
QualType T = Context.getCanonicalType(SE->getType());
// If types are the same or both are integers, ignore the cast.
// FIXME: Remove this hack when we support symbolic truncation/extension.
// HACK: If both castTy and T are integers, ignore the cast. This is
// not a permanent solution. Eventually we want to precisely handle
// extension/truncation of symbolic integers. This prevents us from losing
// precision when we assign 'x = y' and 'y' is symbolic and x and y are
// different integer types.
if (haveSameType(T, CastTy))
return V;
// Produce SymbolCast if CastTy and T are different integers.
// NOTE: In the end the type of SymbolCast shall be equal to CastTy.
if (T->isIntegralOrEnumerationType() &&
CastTy->isIntegralOrEnumerationType()) {
AnalyzerOptions &Opts =
StateMgr.getOwningEngine().getAnalysisManager().getAnalyzerOptions();
// If appropriate option is disabled, ignore the cast.
// NOTE: ShouldSupportSymbolicIntegerCasts is `false` by default.
if (!Opts.ShouldSupportSymbolicIntegerCasts)
return V;
return simplifySymbolCast(V, CastTy);
}
if (!Loc::isLocType(CastTy))
if (!IsUnknownOriginalType || !CastTy->isFloatingType() ||
T->isFloatingType())
@ -1004,3 +1008,75 @@ SVal SValBuilder::evalCastSubKind(nonloc::PointerToMember V, QualType CastTy,
// Member pointer to whatever.
return V;
}
SVal clang::ento::SValBuilder::simplifySymbolCast(nonloc::SymbolVal V,
QualType CastTy) {
// We use seven conditions to recognize a simplification case.
// For the clarity let `CastTy` be `C`, SE->getType() - `T`, root type - `R`,
// prefix `u` for unsigned, `s` for signed, no prefix - any sign:
// E.g. (char)(short)(uint x)
// ( sC )( sT )( uR x)
//
// C === R (the same type)
// (char)(char x) -> (char x)
// (long)(long x) -> (long x)
// Note: Comparisons operators below are for bit width.
// C == T
// (short)(short)(int x) -> (short)(int x)
// (int)(long)(char x) -> (int)(char x) (sizeof(long) == sizeof(int))
// (long)(ullong)(char x) -> (long)(char x) (sizeof(long) == sizeof(ullong))
// C < T
// (short)(int)(char x) -> (short)(char x)
// (char)(int)(short x) -> (char)(short x)
// (short)(int)(short x) -> (short x)
// C > T > uR
// (int)(short)(uchar x) -> (int)(uchar x)
// (uint)(short)(uchar x) -> (uint)(uchar x)
// (int)(ushort)(uchar x) -> (int)(uchar x)
// C > sT > sR
// (int)(short)(char x) -> (int)(char x)
// (uint)(short)(char x) -> (uint)(char x)
// C > sT == sR
// (int)(char)(char x) -> (int)(char x)
// (uint)(short)(short x) -> (uint)(short x)
// C > uT == uR
// (int)(uchar)(uchar x) -> (int)(uchar x)
// (uint)(ushort)(ushort x) -> (uint)(ushort x)
// (llong)(ulong)(uint x) -> (llong)(uint x) (sizeof(ulong) == sizeof(uint))
SymbolRef SE = V.getSymbol();
QualType T = Context.getCanonicalType(SE->getType());
if (T == CastTy)
return V;
if (!isa<SymbolCast>(SE))
return makeNonLoc(SE, T, CastTy);
SymbolRef RootSym = cast<SymbolCast>(SE)->getOperand();
QualType RT = RootSym->getType().getCanonicalType();
BasicValueFactory &BVF = getBasicValueFactory();
APSIntType CTy = BVF.getAPSIntType(CastTy);
APSIntType TTy = BVF.getAPSIntType(T);
const auto WC = CTy.getBitWidth();
const auto WT = TTy.getBitWidth();
if (WC <= WT) {
const bool isSameType = (RT == CastTy);
if (isSameType)
return nonloc::SymbolVal(RootSym);
return makeNonLoc(RootSym, RT, CastTy);
}
APSIntType RTy = BVF.getAPSIntType(RT);
const auto WR = RTy.getBitWidth();
const bool UT = TTy.isUnsigned();
const bool UR = RTy.isUnsigned();
if (((WT > WR) && (UR || !UT)) || ((WT == WR) && (UT == UR)))
return makeNonLoc(RootSym, RT, CastTy);
return makeNonLoc(SE, T, CastTy);
}

View File

@ -115,6 +115,7 @@
// CHECK-NEXT: serialize-stats = false
// CHECK-NEXT: silence-checkers = ""
// CHECK-NEXT: stable-report-filename = false
// CHECK-NEXT: support-symbolic-integer-casts = false
// CHECK-NEXT: suppress-c++-stdlib = true
// CHECK-NEXT: suppress-inlined-defensive-checks = true
// CHECK-NEXT: suppress-null-return-paths = true

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff