MVE only has 128bit legal vectors, no 64bit vectors. There are a number of combines for nodes (MULH/AVG/ABD) that are beneficial for these smaller-than-legal vectors, and often created by the vectorizers, but are not currently transformed. There is no way to tell the target independent dag-combiner that it should, allowing the ARM backend to legalize them.
This changes the legality check in those nodes from TLI.isOperationLegalOrCustom(Opc, VT) (which inherently checks isTypeLegal(VT)) to TLI.isOperationLegal(Opc, VT) || TLI.isOperationCustom(Opc, VT), which allows the backend to mark the nodes as Custom for illegal types, legalising the nodes as it requires. The actual legalisation on MVE uses any_extends and vector casts to perform the operation on a legal vector, truncating the result back to the original type.
There may be other ways to do this - let me know if there is a better way. We can do the same for AArch64 for small types but I've not done that in this same patch.
I think the predicate you actually need here is something like !TLI.isOperationLegalOrCustom(MulhOpcode, NarrowVT) && (LegalTypes || !TLI.isOperationCustom(MulhOpcode, NarrowVT)). i.e. before type legalization, allow custom lowering. After type legalization, only allow custom lowering if the type is legal.
Constructing an operator with an illegal type after type legalization is likely to crash the compiler.