Support integral cast for ranges of symbolic integers. Previously we only support integral cast for concrete integers.

Reason about the ranges of SymbolCast expressions. Apply truncations, promotions and conversions to get a correct range set using nested types of a SymbolCast.

Fixes: https://github.com/llvm/llvm-project/issues/50380

### The solution

Create a map which contains **a bitwidth** as a key and **a range set** as a data. Call it ** CastMap**.

`CastMap = Map<uint32_t, RangeSet>`

`RangeSet`stored in

`CastMap`. But the signedness of all stored

`RangeSet`in the map shall be the same.

Create a map which contains **a symbol** as a key and **CastMap** as a data. Call it ** SymCastMap**.

`SymCastMap = Map<SymbolRef, CastMap>`

Store and update `SymCastMap` for every `SymbolCast` and every `SymExpr` which represents an integer.

Use a root symbol of `SymbolCast` as a key of the map. E.g. for `(int16)(uint8)(int32 x)` root symbol is `(int32 x)`.

For `SymExpr` use the symbol itself as a key of the map.

##### Getting a constraint

- Get a key symbol from
`SymbolCast/SymExpr`. - Get a
`CastMap`of constraints from`SymCastMap`using a key symbol. - Find the smallest type of the given cast symbolic expression.
- Find a
`RangeSet`in the`CastMap`for*equal or the first bigger than*the bitwidth of the smallest type. - If no
`RangeSet`was found, create a new full`RangeSet`for the smallest type. - Sequentially cast the
`RangeSet`across the chain of types starting from the most inner one.

GivenSymbol = (int16)(uint8)(int32 x) RootSymbol = GetRoot(GivenSymbol) // (int32 x) CastMap = GetCastMap(RootSymbol) // CastMap for (int32 x) MinType = FindMinType(GivenSymbol) // uint8 MinBitwidth = BitwidthOf(MinType) // uint8 RangeSet = FindRange(CastMap , MinBitwidth) // range for bitwidth of 8 if(!RangeSet) RangeSet = FindNextRange(CastMap, MinBitwidth) // range for bitwidth of 9+ if(!RangeSet) RangeSet = CreateRangeForType(MinType) // full range for uint8 CastChain = GetCastChain(GivenSymbol) // int32 -> uint8 -> int16 ResultRangeSet = RangeThroughCastChain(RangeSet, CastChain) return ResultRangeSet

##### Setting a constraint

- Get a key symbol from
`SymbolCast/SymExpr`. - Get a map of constraints from
`SymCastMap`using a key symbol. - Find the smallest type of the given cast symbolic expression.
- Find and update all
`RangeSet`'s in the`CastMap`for bitwidths which are*equal or lower than*the bitwidth of the smallest type. - If there is no constraint for the bitwidth of the smallest type in the map, add a new entry with the given
`RangeSet`.

GivenRangeSet = [N, M] GivenSymbol = (int16)(uint8)(int32 x) RootSymbol = GetRoot(GivenSymbol) // (int32 x) CastMap = GetCastMap(RootSymbol) // CastMap for (int32 x) MinType = FindMinType(GivenSymbol) // uint8 MinBitwidth = BitwidthOf(MinType) // uint8 Bitwidth = MinBitwidth while (Bitwidth > 0) // update all constraints which bitwidth is equal or lower then the minimal one RangeSet = FindRange(CastMap, Bitwidth) // range for bitwidth of 8 and lower UpdateRange(CastMap, Bitwidth, RangeSet ∩ GivenRangeSet) // intersect ranges and store the result back to the map Bitwidth-- if(!RangeExistsInMap(CastMap, MinBitwidth)) AddRange(CastMap, MinBitwidth, GivenRangeSet) // store the given range to the map

See tests in *symbol-integral-cast.cpp* for examples.