Add selection support for ext via a new opcode, G_EXT and a post-legalizer combine which matches it.
Add an applyEXT function, because the AArch64ext patterns require a register for the immediate. So, we have to create a G_CONSTANT to get these without writing new patterns or modifying the existing ones.
Tests are the same as arm64-ext.ll.
For reference, here are the patterns we get with G_EXT:
(AArch64ext:{ *:[v1i64] } V64:{ *:[v1i64] }:$Rn, V64:{ *:[v1i64] }:$Rm, (imm:{ *:[i32] }):$imm) => (EXTv8i8:{ *:[v1i64] } V64:{ *:[v1i64] }:$Rn, V64:{ *:[v1i64] }:$Rm, (imm:{ *:[i32] }):$imm) (AArch64ext:{ *:[v1f64] } V64:{ *:[v1f64] }:$Rn, V64:{ *:[v1f64] }:$Rm, (imm:{ *:[i32] }):$imm) => (EXTv8i8:{ *:[v1f64] } V64:{ *:[v1f64] }:$Rn, V64:{ *:[v1f64] }:$Rm, (imm:{ *:[i32] }):$imm) (AArch64ext:{ *:[v2i32] } V64:{ *:[v2i32] }:$Rn, V64:{ *:[v2i32] }:$Rm, (imm:{ *:[i32] }):$imm) => (EXTv8i8:{ *:[v2i32] } V64:{ *:[v2i32] }:$Rn, V64:{ *:[v2i32] }:$Rm, (imm:{ *:[i32] }):$imm) (AArch64ext:{ *:[v2f32] } V64:{ *:[v2f32] }:$Rn, V64:{ *:[v2f32] }:$Rm, (imm:{ *:[i32] }):$imm) => (EXTv8i8:{ *:[v2f32] } V64:{ *:[v2f32] }:$Rn, V64:{ *:[v2f32] }:$Rm, (imm:{ *:[i32] }):$imm) (AArch64ext:{ *:[v2i64] } V128:{ *:[v2i64] }:$Rn, V128:{ *:[v2i64] }:$Rm, (imm:{ *:[i32] }):$imm) => (EXTv16i8:{ *:[v2i64] } V128:{ *:[v2i64] }:$Rn, V128:{ *:[v2i64] }:$Rm, (imm:{ *:[i32] }):$imm) (AArch64ext:{ *:[v2f64] } V128:{ *:[v2f64] }:$Rn, V128:{ *:[v2f64] }:$Rm, (imm:{ *:[i32] }):$imm) => (EXTv16i8:{ *:[v2f64] } V128:{ *:[v2f64] }:$Rn, V128:{ *:[v2f64] }:$Rm, (imm:{ *:[i32] }):$imm) (AArch64ext:{ *:[v4i16] } V64:{ *:[v4i16] }:$Rn, V64:{ *:[v4i16] }:$Rm, (imm:{ *:[i32] }):$imm) => (EXTv8i8:{ *:[v4i16] } V64:{ *:[v4i16] }:$Rn, V64:{ *:[v4i16] }:$Rm, (imm:{ *:[i32] }):$imm) (AArch64ext:{ *:[v4f16] } V64:{ *:[v4f16] }:$Rn, V64:{ *:[v4f16] }:$Rm, (imm:{ *:[i32] }):$imm) => (EXTv8i8:{ *:[v4f16] } V64:{ *:[v4f16] }:$Rn, V64:{ *:[v4f16] }:$Rm, (imm:{ *:[i32] }):$imm) (AArch64ext:{ *:[v4bf16] } V64:{ *:[v4bf16] }:$Rn, V64:{ *:[v4bf16] }:$Rm, (imm:{ *:[i32] }):$imm) => (EXTv8i8:{ *:[v4bf16] } V64:{ *:[v4bf16] }:$Rn, V64:{ *:[v4bf16] }:$Rm, (imm:{ *:[i32] }):$imm) (AArch64ext:{ *:[v4i32] } V128:{ *:[v4i32] }:$Rn, V128:{ *:[v4i32] }:$Rm, (imm:{ *:[i32] }):$imm) => (EXTv16i8:{ *:[v4i32] } V128:{ *:[v4i32] }:$Rn, V128:{ *:[v4i32] }:$Rm, (imm:{ *:[i32] }):$imm) (AArch64ext:{ *:[v4f32] } V128:{ *:[v4f32] }:$Rn, V128:{ *:[v4f32] }:$Rm, (imm:{ *:[i32] }):$imm) => (EXTv16i8:{ *:[v4f32] } V128:{ *:[v4f32] }:$Rn, V128:{ *:[v4f32] }:$Rm, (imm:{ *:[i32] }):$imm) (AArch64ext:{ *:[v8i8] } V64:{ *:[v8i8] }:$Rn, V64:{ *:[v8i8] }:$Rm, (imm:{ *:[i32] }):$imm) => (EXTv8i8:{ *:[v8i8] } V64:{ *:[v8i8] }:$Rn, V64:{ *:[v8i8] }:$Rm, (imm:{ *:[i32] }):$imm) (AArch64ext:{ *:[v8i8] } V64:{ *:[v8i8] }:$Rn, V64:{ *:[v8i8] }:$Rm, (imm:{ *:[i32] }):$imm) => (EXTv8i8:{ *:[v8i8] } V64:{ *:[v8i8] }:$Rn, V64:{ *:[v8i8] }:$Rm, (imm:{ *:[i32] }):$imm) (AArch64ext:{ *:[v8i16] } V128:{ *:[v8i16] }:$Rn, V128:{ *:[v8i16] }:$Rm, (imm:{ *:[i32] }):$imm) => (EXTv16i8:{ *:[v8i16] } V128:{ *:[v8i16] }:$Rn, V128:{ *:[v8i16] }:$Rm, (imm:{ *:[i32] }):$imm) (AArch64ext:{ *:[v8f16] } V128:{ *:[v8f16] }:$Rn, V128:{ *:[v8f16] }:$Rm, (imm:{ *:[i32] }):$imm) => (EXTv16i8:{ *:[v8f16] } V128:{ *:[v8f16] }:$Rn, V128:{ *:[v8f16] }:$Rm, (imm:{ *:[i32] }):$imm) (AArch64ext:{ *:[v8bf16] } V128:{ *:[v8bf16] }:$Rn, V128:{ *:[v8bf16] }:$Rm, (imm:{ *:[i32] }):$imm) => (EXTv16i8:{ *:[v8bf16] } V128:{ *:[v8bf16] }:$Rn, V128:{ *:[v8bf16] }:$Rm, (imm:{ *:[i32] }):$imm) (AArch64ext:{ *:[v16i8] } V128:{ *:[v16i8] }:$Rn, V128:{ *:[v16i8] }:$Rm, (imm:{ *:[i32] }):$imm) => (EXTv16i8:{ *:[v16i8] } V128:{ *:[v16i8] }:$Rn, V128:{ *:[v16i8] }:$Rm, (imm:{ *:[i32] }):$imm) (AArch64ext:{ *:[v16i8] } V128:{ *:[v16i8] }:$Rn, V128:{ *:[v16i8] }:$Rm, (imm:{ *:[i32] }):$imm) => (EXTv16i8:{ *:[v16i8] } V128:{ *:[v16i8] }:$Rn, V128:{ *:[v16i8] }:$Rm, (imm:{ *:[i32] }):$imm)
Tiny nit: if you're returning optional instead of bool unlike the DAG version, I think its better to name this getExtMask rather than isExtMask.