We can simplify (and (icmp X, C1), (icmp X, C2)) to one of the icmps in many cases. I had to check some of these with Alive to prove to myself it's right, but everything seems to check out. Eg, the code in instcombine was completely ignoring predicates with mismatched signedness.
Handling or-of-icmps would be a follow-up step.
I initially thought I could use equivalence of the intersection with one of the input ranges rather than 'contains', but ConstantRange intersection doesn't work like I expected (not sure if it's wrong or if my expectations are off).