[clang] [analyzer] Simplify SVal for simple NonLoc->Loc casts (PR #66463)

Balazs Benics via cfe-commits cfe-commits at lists.llvm.org
Tue Oct 31 02:54:38 PDT 2023


steakhal wrote:

This is a brief summary of my recent investigation, no direct action is required.

I had a quick look at the issue differences between clang-17 and llvm/main as a preparation for the clang-18 release in early January and noticed that because of this patch, we have some unexpected improvements.
FYI this FP was eliminated by Z3 refutation anyway if you enabled that, but now even the engine can avoid exploring that dead-code.

---

Now that we simplify more often (see the patch), we can exclude more execution paths.
Here is an [example](https://godbolt.org/z/x35MT33xT):

```C++
int fixedFP(int x) {
  int* z = 0;
  if ((x & 1) && ((x & 1) ^ 1)) {
    return *z; // Nullptr deref FP? Now it's eliminated.
  }
  return 0;
}
```

Notice, that the FP is only eliminated in C++ mode, and still present in C mode for some reason.
My theory is that we use `int` as a `bool` type, thus simplification can't exploit that a boolean expression can only be `1` or `0`; and fails to simplify the expression.

The reason for having a wider impact than originally anticipated was that the function we modified `handleLValueBitCast()`
suggested that it should be invoked only for lvalue bitcasts, however, actually, it gets invoked from a few more places including ExprEngineC.cpp ExprEngine::VisitCast:
```C++
      case CK_IntegralToBoolean:
      case CK_IntegralToFloating:
      case CK_FloatingToIntegral:
      case CK_FloatingToBoolean:
      case CK_FloatingCast:
      case CK_FloatingRealToComplex:
      case CK_FloatingComplexToReal:
      case CK_FloatingComplexToBoolean:
      case CK_FloatingComplexCast:
      case CK_FloatingComplexToIntegralComplex:
      case CK_IntegralRealToComplex:
      case CK_IntegralComplexToReal:
      case CK_IntegralComplexToBoolean:
      case CK_IntegralComplexCast:
      case CK_IntegralComplexToFloatingComplex:
      case CK_CPointerToObjCPointerCast:
      case CK_BlockPointerToObjCPointerCast:
      case CK_AnyPointerToBlockPointerCast:
      case CK_ObjCObjectLValueCast:
      case CK_ZeroToOCLOpaqueType:
      case CK_IntToOCLSampler:
      case CK_LValueBitCast:
      case CK_FloatingToFixedPoint:
      case CK_FixedPointToFloating:
      case CK_FixedPointCast:
      case CK_FixedPointToBoolean:
      case CK_FixedPointToIntegral:
      case CK_IntegralToFixedPoint: {
        state =
            handleLValueBitCast(state, Ex, LCtx, T, ExTy, CastE, Bldr, Pred);
        continue;
      }
```

And this could have caused this "improvement".

---

The exploded node of the `PostStmt` `((x & 1) ^ 1)` was this prior the patch:
![image](https://github.com/llvm/llvm-project/assets/6280485/9fd6c577-c92d-48bc-a348-43014f7f3d41)
And now it looks like this (notice that the expression value is now simplified to `0 U1b`):
![image](https://github.com/llvm/llvm-project/assets/6280485/48c4cd9a-dc36-46ac-a871-67ebc5fd52f7)

Here is how a child node looks like for C now (notice that the constraint is `((reg_$0<int x>) & 1) ^ 1:  { [-2147483648, -1], [1, 2147483647] }`. This is the same state we had before this patch for C and C++ as well.
![image](https://github.com/llvm/llvm-project/assets/6280485/4335f96b-6c94-4bb0-96db-75e1f0474307)


https://github.com/llvm/llvm-project/pull/66463


More information about the cfe-commits mailing list