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>
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.
These maps will need to be cleaned up when symbols become dead (as in RangeConstraintManager::removeDeadBindings()).