There was a suggestion made before to have each operation use predictable semantics that do not depend on profile, for example. MOVC will be used for TERNARY when it's available (sm4+), another instruction will be used for older profiles. It does not handle int/uint because it's not necessary, these types are passed directly to movc. It can translate TERNARY directly, but it has to handle float case somewhere.
I'm not understanding why the semantics of TERNARY would depend on profile. If it means "select first argument if the condition is a nonzero expression of any type", why would the semantics be any different for sm1?
I'm not saying we should get rid of CMP, but I don't see why we need *three* different instructions.