Vector zext tends to get legalized into a vector anyext (represented as a vector shuffle with an undef vector + a bitcast) that gets ANDed with a mask that zeroes the undef elements.
Combine this into an explicit shuffle with a zero vector instead. This allows shuffle lowering to match it as a zext, instead of matching it as an anyext and emitting an explicit and like it does now.
This doesn't cover all the cases, as you can see in the test, but it's a start.
I think you can remove these two checks.
Your algorithm already checks (between line 24682 and 24686) that N1 is a build_vector or a bitcast of a build_vector.